Skip to content
Source
Sign inSign up

NativeChildWorkSheet.tsx

116 lines4.2 KB

1import type {
2 EnvironmentId,
3 OrchestrationV2GetNativeChildWorkResult,
4 OrchestrationV2NativeChildWork,
5 ThreadId,
6} from "@t3tools/contracts";
7import { ScrollView, View } from "react-native";
8import { useSafeAreaInsets } from "react-native-safe-area-context";
9​
10import { AppText as Text } from "../../components/ui/AppText";
11import { Button } from "../../components/ui/Button";
12import { Sheet } from "../../components/ui/Sheet";
13import { orchestrationEnvironment } from "../../state/orchestration";
14import { useEnvironmentQuery } from "../../state/query";
15​
16const stepLabel = (type: string) => {
17 const words = type.toLowerCase().replaceAll("_", " ");
18 return words.charAt(0).toUpperCase() + words.slice(1);
19};
20​
21/** Read-only view of what a provider-native subagent's children did. Mount only while open. */
22export function NativeChildWorkSheet(props: {
23 readonly environmentId: EnvironmentId;
24 readonly threadId: ThreadId;
25 readonly onClose: () => void;
26}) {
27 const insets = useSafeAreaInsets();
28 const query = useEnvironmentQuery<OrchestrationV2GetNativeChildWorkResult, unknown>(
29 orchestrationEnvironment.nativeChildWork({
30 environmentId: props.environmentId,
31 input: { threadId: props.threadId },
32 }),
33 );
34 const children = query.data?.children;
35 return (
36 <Sheet title="Subagent work" onClose={props.onClose}>
37 <ScrollView
38 contentInsetAdjustmentBehavior="automatic"
39 contentContainerStyle={{
40 gap: 16,
41 paddingHorizontal: 20,
42 paddingTop: 12,
43 paddingBottom: Math.max(20, insets.bottom),
44 }}
45 >
46 <Text className="font-sans text-xs text-foreground-secondary">
47 Read only. These subagents cannot take messages.
48 </Text>
49 {query.error !== null ? (
50 <Text selectable className="font-sans text-sm text-danger-foreground">
51 {query.error}
52 </Text>
53 ) : children === undefined ? (
54 <Text className="font-sans text-sm text-foreground-secondary">Loading…</Text>
55 ) : children.length === 0 ? (
56 <Text className="font-sans text-sm text-foreground-secondary">
57 The provider has no record of which subagents this call started.
58 </Text>
59 ) : (
60 children.map((child) => <NativeChild key={child.conversationId} child={child} />)
61 )}
62 <View className="flex-row justify-end">
63 <Button
64 label="Refresh"
65 tone="secondary"
66 disabled={query.isPending}
67 onPress={query.refresh}
68 />
69 </View>
70 </ScrollView>
71 </Sheet>
72 );
74​
75function NativeChild(props: { readonly child: OrchestrationV2NativeChildWork }) {
76 const { child } = props;
77 return (
78 <View className="gap-2">
79 <Text className="font-t3-bold text-sm text-foreground">{child.role || "Subagent"}</Text>
80 <Text selectable className="font-sans text-sm text-foreground-secondary">
81 {child.prompt}
82 </Text>
83 {child.truncated ? (
84 <Text className="font-sans text-xs text-foreground-secondary">
85 Earlier steps are not shown.
86 </Text>
87 ) : null}
88 {child.steps === null ? (
89 <Text className="font-sans text-xs text-foreground-secondary">No transcript yet.</Text>
90 ) : (
91 <View className="gap-2 border-l border-border ps-3">
92 {child.steps.map((step) => (
93 <View key={step.index} className="gap-1">
94 <Text className="font-t3-bold text-xs text-foreground-secondary">
95 {stepLabel(step.type)}
96 {step.status !== null && step.status !== "DONE"
97 ? ` · ${stepLabel(step.status)}`
98 : ""}
99 </Text>
100 {step.toolCalls.length === 0 ? null : (
101 <Text selectable className="font-mono text-xs text-foreground">
102 {step.toolCalls.map((call) => `${call.name} ${call.input}`).join("\n")}
103 </Text>
104 )}
105 {step.text === null ? null : (
106 <Text selectable className="font-mono text-xs text-foreground">
107 {step.text}
108 </Text>
109 )}
110 </View>
111 ))}
112 </View>
113 )}
114 </View>
115 );