@agimon-ai/doompi-team 0.0.1-alpha.42 → 0.0.1-alpha.44
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +11 -11
- package/web/SubagentsPanel.tsx +231 -118
- package/web/index.ts +1 -1
- package/web/runActionsSlot.ts +1 -1
- package/web/subagentsStore.ts +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agimon-ai/doompi-team",
|
|
3
|
-
"version": "0.0.1-alpha.
|
|
3
|
+
"version": "0.0.1-alpha.44",
|
|
4
4
|
"description": "Asynchronous named subagents, team runs, intercom, and model policy for Pi coding agents.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"agent-team",
|
|
@@ -53,15 +53,15 @@
|
|
|
53
53
|
},
|
|
54
54
|
"dependencies": {
|
|
55
55
|
"@deepseek-ai/cordis": "4.0.1",
|
|
56
|
-
"typebox": "1.3.
|
|
56
|
+
"typebox": "1.3.19",
|
|
57
57
|
"yaml": "2.9.0",
|
|
58
|
-
"@agimon-ai/doompi-cache": "0.0.1-alpha.
|
|
59
|
-
"@agimon-ai/doompi-config": "0.0.1-alpha.
|
|
60
|
-
"@agimon-ai/doompi-extension-contracts": "0.0.1-alpha.
|
|
61
|
-
"@agimon-ai/doompi-telemetry": "0.0.1-alpha.
|
|
62
|
-
"@agimon-ai/doompi-ui": "0.0.1-alpha.
|
|
63
|
-
"@agimon-ai/doompi-web-contracts": "0.0.1-alpha.
|
|
64
|
-
"@agimon-ai/doompi-web-components": "0.0.1-alpha.
|
|
58
|
+
"@agimon-ai/doompi-cache": "0.0.1-alpha.11",
|
|
59
|
+
"@agimon-ai/doompi-config": "0.0.1-alpha.44",
|
|
60
|
+
"@agimon-ai/doompi-extension-contracts": "0.0.1-alpha.44",
|
|
61
|
+
"@agimon-ai/doompi-telemetry": "0.0.1-alpha.44",
|
|
62
|
+
"@agimon-ai/doompi-ui": "0.0.1-alpha.44",
|
|
63
|
+
"@agimon-ai/doompi-web-contracts": "0.0.1-alpha.7",
|
|
64
|
+
"@agimon-ai/doompi-web-components": "0.0.1-alpha.5"
|
|
65
65
|
},
|
|
66
66
|
"devDependencies": {
|
|
67
67
|
"@earendil-works/pi-agent-core": "0.84.4",
|
|
@@ -70,8 +70,8 @@
|
|
|
70
70
|
"@earendil-works/pi-tui": "0.84.4",
|
|
71
71
|
"@tanstack/react-store": "0.11.1",
|
|
72
72
|
"@tanstack/store": "0.11.1",
|
|
73
|
-
"@types/node": "26.
|
|
74
|
-
"@types/react": "19.2.
|
|
73
|
+
"@types/node": "26.4.0",
|
|
74
|
+
"@types/react": "19.2.18",
|
|
75
75
|
"@vitest/coverage-v8": "4.1.11",
|
|
76
76
|
"react": "19.2.8",
|
|
77
77
|
"typescript": "7.0.2",
|
package/web/SubagentsPanel.tsx
CHANGED
|
@@ -2,13 +2,22 @@ import {
|
|
|
2
2
|
Badge,
|
|
3
3
|
Button,
|
|
4
4
|
cn,
|
|
5
|
-
|
|
5
|
+
DropdownMenu,
|
|
6
|
+
DropdownMenuContent,
|
|
7
|
+
DropdownMenuItem,
|
|
8
|
+
DropdownMenuSeparator,
|
|
9
|
+
DropdownMenuTrigger,
|
|
6
10
|
EmptyState,
|
|
11
|
+
KebabIcon,
|
|
7
12
|
Kbd,
|
|
8
13
|
Panel,
|
|
14
|
+
Sheet,
|
|
15
|
+
SheetBody,
|
|
16
|
+
SheetContent,
|
|
17
|
+
SheetHeader,
|
|
18
|
+
SheetTitle,
|
|
9
19
|
STATUS_EDGE,
|
|
10
20
|
StatusBadge,
|
|
11
|
-
StreamCursor,
|
|
12
21
|
} from '@agimon-ai/doompi-web-components';
|
|
13
22
|
import type { SessionFrameSender, WebPluginSlotProps } from '@agimon-ai/doompi-web-contracts';
|
|
14
23
|
import { useStore } from '@tanstack/react-store';
|
|
@@ -21,10 +30,25 @@ import { abbreviateCwd } from './format.ts';
|
|
|
21
30
|
import { LaunchAgentDialog } from './LaunchAgentDialog.tsx';
|
|
22
31
|
import { RUN_ACTIONS_SLOT } from './runActionsSlot.ts';
|
|
23
32
|
import { elapsedRun, RUN_BADGE, RunControl } from './RunControl.tsx';
|
|
24
|
-
import {
|
|
33
|
+
import {
|
|
34
|
+
clearAutoOpen,
|
|
35
|
+
dismissRun,
|
|
36
|
+
isTerminalRun,
|
|
37
|
+
openRun,
|
|
38
|
+
requestRunStop,
|
|
39
|
+
subagents,
|
|
40
|
+
visibleRuns,
|
|
41
|
+
} from './subagentsStore.ts';
|
|
25
42
|
|
|
26
43
|
const TICK_MS = 10_000;
|
|
27
44
|
|
|
45
|
+
/**
|
|
46
|
+
* How much of a run's own conversation a card carries: enough to read what it
|
|
47
|
+
* is doing right now, not enough to read the run. The whole thread is one
|
|
48
|
+
* click away in the run's tab.
|
|
49
|
+
*/
|
|
50
|
+
const CARD_THREAD_ENTRIES = 8;
|
|
51
|
+
|
|
28
52
|
/** The card's work line: a task binding wins; otherwise the delegation prompt. */
|
|
29
53
|
function WorkLine({ run }: { run: SubagentRun }) {
|
|
30
54
|
const firstLine = run.task.split('\n').find((line) => line.trim() !== '') ?? '';
|
|
@@ -50,111 +74,202 @@ function WorkLine({ run }: { run: SubagentRun }) {
|
|
|
50
74
|
);
|
|
51
75
|
}
|
|
52
76
|
|
|
53
|
-
/**
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
77
|
+
/**
|
|
78
|
+
* Everything a card can do, behind one control.
|
|
79
|
+
*
|
|
80
|
+
* A card already spends its width on the run's own stream, and stop, clear,
|
|
81
|
+
* detail and the run's tab are four verbs: as buttons they crowd the footer
|
|
82
|
+
* and each one has to defend itself from the card's own click.
|
|
83
|
+
*/
|
|
84
|
+
function RunMenu({
|
|
85
|
+
sessionId,
|
|
86
|
+
run,
|
|
87
|
+
stopping,
|
|
88
|
+
send,
|
|
89
|
+
onOpenThread,
|
|
90
|
+
onOpenDetail,
|
|
91
|
+
}: {
|
|
92
|
+
sessionId: string;
|
|
93
|
+
run: SubagentRun;
|
|
94
|
+
stopping: boolean;
|
|
95
|
+
send: SessionFrameSender;
|
|
96
|
+
onOpenThread: () => void;
|
|
97
|
+
onOpenDetail: () => void;
|
|
98
|
+
}) {
|
|
99
|
+
return (
|
|
100
|
+
<DropdownMenu>
|
|
101
|
+
<DropdownMenuTrigger asChild>
|
|
102
|
+
<Button
|
|
103
|
+
variant="ghost"
|
|
104
|
+
size="icon"
|
|
105
|
+
data-testid={`run-menu-${run.runId}`}
|
|
106
|
+
title="what this run can do"
|
|
107
|
+
aria-label="run actions"
|
|
108
|
+
onClick={(event) => event.stopPropagation()}
|
|
109
|
+
>
|
|
110
|
+
<KebabIcon className="h-3 w-3" />
|
|
111
|
+
</Button>
|
|
112
|
+
</DropdownMenuTrigger>
|
|
113
|
+
<DropdownMenuContent data-testid={`run-menu-content-${run.runId}`}>
|
|
114
|
+
<DropdownMenuItem data-testid={`run-open-${run.runId}`} onSelect={onOpenThread}>
|
|
115
|
+
open thread
|
|
116
|
+
</DropdownMenuItem>
|
|
117
|
+
<DropdownMenuItem data-testid={`run-detail-${run.runId}`} onSelect={onOpenDetail}>
|
|
118
|
+
details
|
|
119
|
+
</DropdownMenuItem>
|
|
120
|
+
<DropdownMenuSeparator />
|
|
121
|
+
{isTerminalRun(run) ? (
|
|
122
|
+
<DropdownMenuItem data-testid={`run-clear-${run.runId}`} onSelect={() => dismissRun(sessionId, run.runId)}>
|
|
123
|
+
clear from the grid
|
|
124
|
+
</DropdownMenuItem>
|
|
125
|
+
) : (
|
|
126
|
+
<DropdownMenuItem
|
|
127
|
+
variant="destructive"
|
|
128
|
+
data-testid={`run-stop-${run.runId}`}
|
|
129
|
+
data-stopping={stopping}
|
|
130
|
+
disabled={stopping}
|
|
131
|
+
onSelect={() => requestRunStop(send, sessionId, run.runId)}
|
|
132
|
+
>
|
|
133
|
+
{stopping ? 'stopping…' : 'stop'}
|
|
134
|
+
</DropdownMenuItem>
|
|
135
|
+
)}
|
|
136
|
+
</DropdownMenuContent>
|
|
137
|
+
</DropdownMenu>
|
|
138
|
+
);
|
|
71
139
|
}
|
|
72
140
|
|
|
141
|
+
/**
|
|
142
|
+
* One run in the grid: its live conversation under a header, on the host's own
|
|
143
|
+
* transcript so a tool call reads the same here as in the session.
|
|
144
|
+
*
|
|
145
|
+
* The body is a glance, so it takes the newest entries only and does not take
|
|
146
|
+
* the pointer: the whole card is the way into the run's tab.
|
|
147
|
+
*/
|
|
73
148
|
function RunCard({
|
|
74
149
|
sessionId,
|
|
75
150
|
run,
|
|
76
151
|
now,
|
|
77
152
|
stopping,
|
|
78
153
|
send,
|
|
79
|
-
|
|
154
|
+
renderThread,
|
|
155
|
+
onOpenThread,
|
|
156
|
+
onOpenDetail,
|
|
80
157
|
}: {
|
|
81
158
|
sessionId: string;
|
|
82
159
|
run: SubagentRun;
|
|
83
160
|
now: number;
|
|
84
161
|
stopping: boolean;
|
|
85
162
|
send: SessionFrameSender;
|
|
86
|
-
|
|
163
|
+
renderThread: WebPluginSlotProps['renderThread'];
|
|
164
|
+
onOpenThread: () => void;
|
|
165
|
+
onOpenDetail: () => void;
|
|
87
166
|
}) {
|
|
88
167
|
const badge = RUN_BADGE[run.state];
|
|
89
168
|
const onKeyDown = (event: ReactKeyboardEvent<HTMLDivElement>): void => {
|
|
90
169
|
if (event.target !== event.currentTarget) return;
|
|
91
170
|
if (event.key === 'Enter' || event.key === ' ') {
|
|
92
171
|
event.preventDefault();
|
|
93
|
-
|
|
172
|
+
onOpenThread();
|
|
94
173
|
}
|
|
95
174
|
};
|
|
96
|
-
// A div rather than a button: the card carries
|
|
175
|
+
// A div rather than a button: the card carries a menu of its own, and a
|
|
97
176
|
// button cannot contain another.
|
|
98
177
|
return (
|
|
99
178
|
<Panel
|
|
100
179
|
asChild
|
|
101
180
|
className={cn(
|
|
102
|
-
'flex
|
|
181
|
+
'flex h-[280px] min-w-0 flex-col text-left transition-colors outline-none hover:border-doom-blue/50 focus-within:border-doom-blue',
|
|
103
182
|
STATUS_EDGE[badge.tone],
|
|
104
183
|
)}
|
|
105
184
|
>
|
|
106
|
-
<div
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
<span className="shrink-0 text-[9px] text-doom-faint">{elapsedRun(run, now)}</span>
|
|
120
|
-
<StatusBadge tone={badge.tone} data-testid="run-state">
|
|
121
|
-
{badge.label}
|
|
122
|
-
</StatusBadge>
|
|
123
|
-
</div>
|
|
124
|
-
<WorkLine run={run} />
|
|
125
|
-
<div className="flex min-w-0 flex-1 flex-col gap-1 overflow-hidden border-t border-doom-border-soft bg-doom-deep px-3 py-2.5">
|
|
126
|
-
{tailLines(run).map((line, index) => (
|
|
127
|
-
<span key={index} className={`truncate text-[10px] ${line.className}`}>
|
|
128
|
-
{line.text}
|
|
185
|
+
<div data-testid={`run-card-${run.runId}`} data-run-state={run.state}>
|
|
186
|
+
<div
|
|
187
|
+
role="button"
|
|
188
|
+
tabIndex={0}
|
|
189
|
+
data-testid={`run-open-card-${run.runId}`}
|
|
190
|
+
className="flex min-h-0 flex-1 cursor-pointer flex-col outline-none"
|
|
191
|
+
title="open this run's own thread in a tab"
|
|
192
|
+
onClick={onOpenThread}
|
|
193
|
+
onKeyDown={onKeyDown}
|
|
194
|
+
>
|
|
195
|
+
<div className="flex items-center gap-2 px-3 pt-2.5 pb-1.5">
|
|
196
|
+
<span data-testid="run-agent" className="truncate text-[12px] font-bold text-doom-hi">
|
|
197
|
+
{run.agent}
|
|
129
198
|
</span>
|
|
130
|
-
|
|
131
|
-
|
|
199
|
+
<span className="min-w-0 flex-1" />
|
|
200
|
+
<span className="shrink-0 text-[9px] text-doom-faint">{elapsedRun(run, now)}</span>
|
|
201
|
+
<StatusBadge tone={badge.tone} data-testid="run-state">
|
|
202
|
+
{badge.label}
|
|
203
|
+
</StatusBadge>
|
|
204
|
+
</div>
|
|
205
|
+
<WorkLine run={run} />
|
|
206
|
+
{/* The run's own journal, drawn by the host's timeline: the newest
|
|
207
|
+
entries only, and inert, so the card stays one click. */}
|
|
208
|
+
<div
|
|
209
|
+
data-testid="run-stream"
|
|
210
|
+
className="pointer-events-none flex min-h-0 flex-1 flex-col overflow-hidden border-t border-doom-border-soft bg-doom-deep"
|
|
211
|
+
>
|
|
212
|
+
{renderThread(run.runId, { limit: CARD_THREAD_ENTRIES, compact: true })}
|
|
213
|
+
</div>
|
|
214
|
+
{run.state === 'failed' && run.error ? (
|
|
215
|
+
<span
|
|
216
|
+
data-testid="run-error"
|
|
217
|
+
className="truncate border-t border-doom-border-soft px-3 py-1.5 text-[10px] text-doom-red"
|
|
218
|
+
>
|
|
219
|
+
{run.error}
|
|
220
|
+
</span>
|
|
221
|
+
) : null}
|
|
132
222
|
</div>
|
|
133
|
-
<div className="flex items-center gap-3 px-3 py-
|
|
223
|
+
<div className="flex shrink-0 items-center gap-3 border-t border-doom-border-soft px-3 py-1.5">
|
|
134
224
|
<span className="truncate text-[9px] text-doom-faint">
|
|
135
225
|
{run.toolCount !== undefined ? `${run.toolCount} tools` : '—'}
|
|
136
226
|
{run.tokens !== undefined ? ` · ${run.tokens.toLocaleString()} tk` : ''}
|
|
137
227
|
{run.model ? ` · ${run.model.split('/').pop() ?? run.model}` : ''}
|
|
138
228
|
</span>
|
|
139
229
|
<span className="min-w-0 flex-1" />
|
|
140
|
-
<
|
|
141
|
-
|
|
230
|
+
<RunMenu
|
|
231
|
+
sessionId={sessionId}
|
|
232
|
+
run={run}
|
|
233
|
+
stopping={stopping}
|
|
234
|
+
send={send}
|
|
235
|
+
onOpenThread={onOpenThread}
|
|
236
|
+
onOpenDetail={onOpenDetail}
|
|
237
|
+
/>
|
|
142
238
|
</div>
|
|
143
239
|
</div>
|
|
144
240
|
</Panel>
|
|
145
241
|
);
|
|
146
242
|
}
|
|
147
243
|
|
|
148
|
-
function
|
|
244
|
+
function SheetRow({ label, value, tone = 'text-doom-dim' }: { label: string; value: string; tone?: string }) {
|
|
149
245
|
return (
|
|
150
246
|
<div className="flex gap-2.5">
|
|
151
247
|
<span className="w-11 shrink-0 text-[9px] text-doom-faint">{label}</span>
|
|
152
|
-
<span className={`min-w-0
|
|
248
|
+
<span className={`min-w-0 break-words text-[10px] ${tone}`}>{value}</span>
|
|
153
249
|
</div>
|
|
154
250
|
);
|
|
155
251
|
}
|
|
156
252
|
|
|
157
|
-
function
|
|
253
|
+
function SheetBlock({ label, text, testId, tone }: { label: string; text: string; testId: string; tone?: string }) {
|
|
254
|
+
return (
|
|
255
|
+
<div className="flex flex-col gap-1 rounded-md border border-doom-border bg-doom-deep px-3 py-2.5">
|
|
256
|
+
<span className="text-[8px] font-bold tracking-[0.14em] text-doom-faint">{label}</span>
|
|
257
|
+
<pre
|
|
258
|
+
data-testid={testId}
|
|
259
|
+
className={`overflow-x-auto whitespace-pre-wrap break-words text-[10px] leading-relaxed ${tone ?? 'text-doom-text'}`}
|
|
260
|
+
>
|
|
261
|
+
{text}
|
|
262
|
+
</pre>
|
|
263
|
+
</div>
|
|
264
|
+
);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* What the run was asked to do and what it was given to do it with, over the
|
|
269
|
+
* grid rather than beside it: the conversation is on the card and in the run's
|
|
270
|
+
* tab, so this reads as a definition and takes no width from the fleet.
|
|
271
|
+
*/
|
|
272
|
+
function RunDetailSheet({
|
|
158
273
|
sessionId,
|
|
159
274
|
run,
|
|
160
275
|
now,
|
|
@@ -172,60 +287,45 @@ function RunDrawer({
|
|
|
172
287
|
onClose: () => void;
|
|
173
288
|
}) {
|
|
174
289
|
const badge = RUN_BADGE[run.state];
|
|
175
|
-
const output = run.summary ?? run.tail.join('\n');
|
|
176
290
|
return (
|
|
177
|
-
<
|
|
178
|
-
|
|
179
|
-
|
|
291
|
+
<Sheet
|
|
292
|
+
open
|
|
293
|
+
onOpenChange={(open) => {
|
|
294
|
+
if (!open) onClose();
|
|
295
|
+
}}
|
|
180
296
|
>
|
|
181
|
-
<
|
|
182
|
-
<
|
|
183
|
-
{run.agent}
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
<
|
|
191
|
-
<
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
<
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
{run.summary ? 'REPORT' : run.state === 'failed' ? 'FAILURE' : 'OUTPUT · live'}
|
|
214
|
-
</span>
|
|
215
|
-
<span className="text-[9px] text-doom-faint">
|
|
216
|
-
{run.toolCount !== undefined ? `${run.toolCount} tools` : ''}
|
|
217
|
-
</span>
|
|
218
|
-
</div>
|
|
219
|
-
<div className="flex-1 overflow-y-auto border-t border-doom-border-soft bg-doom-deep px-4 py-3">
|
|
220
|
-
<pre
|
|
221
|
-
data-testid="drawer-output"
|
|
222
|
-
className="whitespace-pre-wrap break-words text-[10px] leading-relaxed text-doom-text"
|
|
223
|
-
>
|
|
224
|
-
{run.state === 'failed' && run.error ? run.error : output || run.currentTool || '(nothing reported yet)'}
|
|
225
|
-
</pre>
|
|
226
|
-
{run.state === 'running' ? <StreamCursor className="mt-1 ml-0 h-[11px] w-1.5 translate-y-0" /> : null}
|
|
227
|
-
</div>
|
|
228
|
-
</aside>
|
|
297
|
+
<SheetContent data-testid="run-sheet" aria-describedby={undefined}>
|
|
298
|
+
<SheetHeader closeLabel="close the run detail">
|
|
299
|
+
<SheetTitle data-testid="sheet-agent">{run.agent}</SheetTitle>
|
|
300
|
+
<StatusBadge tone={badge.tone}>{badge.label}</StatusBadge>
|
|
301
|
+
<span className="min-w-0 flex-1" />
|
|
302
|
+
<span className="text-[10px] text-doom-faint">{elapsedRun(run, now)}</span>
|
|
303
|
+
{renderSlot(RUN_ACTIONS_SLOT.slot)}
|
|
304
|
+
<RunControl sessionId={sessionId} run={run} stopping={stopping} send={send} />
|
|
305
|
+
</SheetHeader>
|
|
306
|
+
<SheetBody>
|
|
307
|
+
<div className="flex flex-col gap-1.5">
|
|
308
|
+
<SheetRow label="run" value={run.runId} />
|
|
309
|
+
{run.taskRef ? <SheetRow label="task" value={run.taskRef} tone="text-doom-violet" /> : null}
|
|
310
|
+
<SheetRow label="model" value={run.model ?? 'the runtime default'} />
|
|
311
|
+
<SheetRow label="cwd" value={abbreviateCwd(run.cwd)} />
|
|
312
|
+
<SheetRow
|
|
313
|
+
label="work"
|
|
314
|
+
value={`${run.toolCount === undefined ? 'no' : String(run.toolCount)} tools${
|
|
315
|
+
run.tokens === undefined ? '' : ` · ${run.tokens.toLocaleString()} tk`
|
|
316
|
+
}`}
|
|
317
|
+
/>
|
|
318
|
+
</div>
|
|
319
|
+
<SheetBlock
|
|
320
|
+
label="PROMPT FROM THE MAIN AGENT"
|
|
321
|
+
testId="sheet-task"
|
|
322
|
+
text={run.task || '(no prompt recorded)'}
|
|
323
|
+
/>
|
|
324
|
+
{run.summary ? <SheetBlock label="REPORT" testId="sheet-summary" text={run.summary} /> : null}
|
|
325
|
+
{run.error ? <SheetBlock label="FAILURE" testId="sheet-error" text={run.error} tone="text-doom-red" /> : null}
|
|
326
|
+
</SheetBody>
|
|
327
|
+
</SheetContent>
|
|
328
|
+
</Sheet>
|
|
229
329
|
);
|
|
230
330
|
}
|
|
231
331
|
|
|
@@ -233,12 +333,19 @@ function RunDrawer({
|
|
|
233
333
|
* The subagents tab: the focused session's fleet as a computed grid.
|
|
234
334
|
*
|
|
235
335
|
* Columns come from the available width (auto-fill over a 420px minimum), and
|
|
236
|
-
* every card
|
|
237
|
-
*
|
|
238
|
-
*
|
|
239
|
-
*
|
|
336
|
+
* every card is the same height with the run's own live conversation
|
|
337
|
+
* absorbing the slack, so the grid reads as a fleet rather than a ragged list.
|
|
338
|
+
* A card opens the run's tab; its menu opens the detail sheet over the grid.
|
|
339
|
+
* The catalog to launch from still takes the right rail, because picking an
|
|
340
|
+
* agent is a list beside the fleet rather than a thing about one run.
|
|
240
341
|
*/
|
|
241
|
-
export function SubagentsPanel({
|
|
342
|
+
export function SubagentsPanel({
|
|
343
|
+
sessionId,
|
|
344
|
+
sendSessionFrame,
|
|
345
|
+
renderSlot,
|
|
346
|
+
renderThread,
|
|
347
|
+
openTransientTab,
|
|
348
|
+
}: WebPluginSlotProps) {
|
|
242
349
|
const runs = useStore(subagents.store, (state) => visibleRuns(subagents.select(state, sessionId)));
|
|
243
350
|
const { openRunId, stopRequested, autoOpenRunId } = useStore(subagents.store, (state) =>
|
|
244
351
|
subagents.select(state, sessionId),
|
|
@@ -317,7 +424,12 @@ export function SubagentsPanel({ sessionId, sendSessionFrame, renderSlot, openTr
|
|
|
317
424
|
now={now}
|
|
318
425
|
stopping={stopping(run)}
|
|
319
426
|
send={sendSessionFrame}
|
|
320
|
-
|
|
427
|
+
renderThread={renderThread}
|
|
428
|
+
onOpenThread={() => {
|
|
429
|
+
closeCatalog(sessionId);
|
|
430
|
+
openTransientTab(agentThreadTab(run));
|
|
431
|
+
}}
|
|
432
|
+
onOpenDetail={() => {
|
|
321
433
|
closeCatalog(sessionId);
|
|
322
434
|
openRun(sessionId, run.runId);
|
|
323
435
|
}}
|
|
@@ -325,7 +437,7 @@ export function SubagentsPanel({ sessionId, sendSessionFrame, renderSlot, openTr
|
|
|
325
437
|
))}
|
|
326
438
|
</div>
|
|
327
439
|
<span className="pt-3 text-[9px] text-doom-faint">
|
|
328
|
-
click a card
|
|
440
|
+
click a card to open the run's thread · the card menu holds details, stop and clear · finished runs leave
|
|
329
441
|
the grid after 10m
|
|
330
442
|
</span>
|
|
331
443
|
</>
|
|
@@ -337,8 +449,9 @@ export function SubagentsPanel({ sessionId, sendSessionFrame, renderSlot, openTr
|
|
|
337
449
|
onClose={() => closeCatalog(sessionId)}
|
|
338
450
|
onLaunch={(agent, fork) => openLaunch(sessionId, agent.name, fork)}
|
|
339
451
|
/>
|
|
340
|
-
) :
|
|
341
|
-
|
|
452
|
+
) : null}
|
|
453
|
+
{shownRun && sessionId !== null ? (
|
|
454
|
+
<RunDetailSheet
|
|
342
455
|
sessionId={sessionId}
|
|
343
456
|
run={shownRun}
|
|
344
457
|
now={now}
|
package/web/index.ts
CHANGED
|
@@ -40,6 +40,6 @@ export const webPlugin = defineWebPlugin({
|
|
|
40
40
|
// footer's one-line summary.
|
|
41
41
|
activitySections: [{ id: 'agents', component: AgentsActivitySection }],
|
|
42
42
|
toolRenderers: teamToolRenderers,
|
|
43
|
-
// The run
|
|
43
|
+
// The run detail sheet's action row is open to independent plugins.
|
|
44
44
|
slots: [RUN_ACTIONS_SLOT],
|
|
45
45
|
});
|
package/web/runActionsSlot.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { defineSlot } from '@agimon-ai/doompi-web-contracts';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* The slot the run
|
|
4
|
+
* The slot the run detail sheet opens beside its stop control: an independent
|
|
5
5
|
* plugin may put its own action for the shown run there, without this
|
|
6
6
|
* package knowing it exists. Fills receive the panel's own props.
|
|
7
7
|
*/
|
package/web/subagentsStore.ts
CHANGED
|
@@ -19,7 +19,7 @@ export interface SubagentsSession {
|
|
|
19
19
|
dismissed: string[];
|
|
20
20
|
/** Runs a stop was asked for and that have not yet reported a final state. */
|
|
21
21
|
stopRequested: string[];
|
|
22
|
-
/** The run whose
|
|
22
|
+
/** The run whose detail sheet the subagents tab shows. */
|
|
23
23
|
openRunId: string | undefined;
|
|
24
24
|
/** A launch this page asked for, until a run of that agent it had not seen shows up. */
|
|
25
25
|
pendingLaunch: { agent: string; knownRunIds: string[] } | undefined;
|