@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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agimon-ai/doompi-team",
3
- "version": "0.0.1-alpha.42",
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.16",
56
+ "typebox": "1.3.19",
57
57
  "yaml": "2.9.0",
58
- "@agimon-ai/doompi-cache": "0.0.1-alpha.9",
59
- "@agimon-ai/doompi-config": "0.0.1-alpha.42",
60
- "@agimon-ai/doompi-extension-contracts": "0.0.1-alpha.42",
61
- "@agimon-ai/doompi-telemetry": "0.0.1-alpha.42",
62
- "@agimon-ai/doompi-ui": "0.0.1-alpha.42",
63
- "@agimon-ai/doompi-web-contracts": "0.0.1-alpha.5",
64
- "@agimon-ai/doompi-web-components": "0.0.1-alpha.3"
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.2.0",
74
- "@types/react": "19.2.7",
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",
@@ -2,13 +2,22 @@ import {
2
2
  Badge,
3
3
  Button,
4
4
  cn,
5
- CloseIcon,
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 { clearAutoOpen, openRun, subagents, visibleRuns } from './subagentsStore.ts';
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
- /** What fills the tail: live output while running, the report when done, the error when failed. */
54
- function tailLines(run: SubagentRun): { text: string; className: string }[] {
55
- if (run.state === 'failed' && run.error) {
56
- return [
57
- ...run.tail.map((line) => ({ text: line, className: 'text-doom-dim' })),
58
- { text: run.error, className: 'text-doom-red' },
59
- ];
60
- }
61
- if (run.summary) {
62
- return run.summary
63
- .split('\n')
64
- .filter((line) => line.trim() !== '')
65
- .slice(0, 6)
66
- .map((line) => ({ text: line, className: 'text-doom-text' }));
67
- }
68
- const lines = run.tail.map((line) => ({ text: line, className: 'text-doom-dim' }));
69
- if (run.currentTool) lines.push({ text: run.currentTool, className: 'text-doom-dim' });
70
- return lines;
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
- onOpen,
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
- onOpen: () => void;
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
- onOpen();
172
+ onOpenThread();
94
173
  }
95
174
  };
96
- // A div rather than a button: the card carries buttons of its own, and a
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 min-h-[210px] min-w-0 cursor-pointer flex-col text-left transition-colors outline-none hover:border-doom-blue/50 focus-visible:border-doom-blue',
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
- role="button"
108
- tabIndex={0}
109
- data-testid={`run-card-${run.runId}`}
110
- data-run-state={run.state}
111
- onClick={onOpen}
112
- onKeyDown={onKeyDown}
113
- >
114
- <div className="flex items-center gap-2 px-3 pt-2.5 pb-1.5">
115
- <span data-testid="run-agent" className="truncate text-[12px] font-bold text-doom-hi">
116
- {run.agent}
117
- </span>
118
- <span className="min-w-0 flex-1" />
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
- {run.state === 'running' ? <StreamCursor className="mt-0.5 ml-0 h-[11px] w-1.5 translate-y-0" /> : null}
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-2">
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
- <RunControl sessionId={sessionId} run={run} stopping={stopping} send={send} />
141
- <span className="text-[9px] font-bold text-doom-blue">detail</span>
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 DrawerRow({ label, value, tone = 'text-doom-dim' }: { label: string; value: string; tone?: string }) {
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 truncate text-[10px] ${tone}`}>{value}</span>
248
+ <span className={`min-w-0 break-words text-[10px] ${tone}`}>{value}</span>
153
249
  </div>
154
250
  );
155
251
  }
156
252
 
157
- function RunDrawer({
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
- <aside
178
- data-testid="run-drawer"
179
- className="flex w-[min(440px,calc(100vw-24px))] shrink-0 flex-col overflow-hidden border-l border-doom-border bg-doom-rail"
291
+ <Sheet
292
+ open
293
+ onOpenChange={(open) => {
294
+ if (!open) onClose();
295
+ }}
180
296
  >
181
- <div className="flex h-11 shrink-0 items-center gap-2.5 border-b border-doom-border px-4">
182
- <span data-testid="drawer-agent" className="truncate text-[13px] font-bold text-doom-hi">
183
- {run.agent}
184
- </span>
185
- <StatusBadge tone={badge.tone}>{badge.label}</StatusBadge>
186
- <span className="min-w-0 flex-1" />
187
- <span className="text-[10px] text-doom-faint">{elapsedRun(run, now)}</span>
188
- {renderSlot(RUN_ACTIONS_SLOT.slot)}
189
- <RunControl sessionId={sessionId} run={run} stopping={stopping} send={send} />
190
- <Button variant="ghost" size="icon" data-testid="drawer-close" title="close the run detail" onClick={onClose}>
191
- <CloseIcon className="h-3 w-3" />
192
- </Button>
193
- </div>
194
- <div className="flex flex-col gap-1.5 px-4 py-2.5">
195
- <DrawerRow label="run" value={run.runId.slice(0, 8)} />
196
- {run.taskRef ? <DrawerRow label="task" value={run.taskRef} tone="text-doom-violet" /> : null}
197
- {run.model ? <DrawerRow label="model" value={run.model} /> : null}
198
- <DrawerRow label="cwd" value={abbreviateCwd(run.cwd)} />
199
- </div>
200
- <div className="px-4 pb-2.5">
201
- <div className="flex flex-col gap-1 rounded-md border border-doom-border bg-doom-deep px-3 py-2.5">
202
- <span className="text-[8px] font-bold tracking-[0.14em] text-doom-faint">PROMPT FROM THE MAIN AGENT</span>
203
- <pre
204
- data-testid="drawer-task"
205
- className="max-h-40 overflow-y-auto whitespace-pre-wrap break-words text-[10px] leading-relaxed text-doom-text"
206
- >
207
- {run.task || '(no prompt recorded)'}
208
- </pre>
209
- </div>
210
- </div>
211
- <div className="flex items-center justify-between px-4 pb-1.5">
212
- <span className="text-[9px] font-bold tracking-[0.14em] text-doom-faint">
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 keeps a 210px minimum height with the tail absorbing the slack,
237
- * so short runs read as calmly as busy ones. The drawer on the right shows one
238
- * thing at a time: a card's run detail, or the agent catalog to launch from.
239
- * A launch that produced a run opens that run's own tab once it appears.
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({ sessionId, sendSessionFrame, renderSlot, openTransientTab }: WebPluginSlotProps) {
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
- onOpen={() => {
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 for the run detail · stop asks the runtime · clear hides a finished run · finished runs leave
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
- ) : shownRun && sessionId !== null ? (
341
- <RunDrawer
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 drawer's action row is open to independent plugins.
43
+ // The run detail sheet's action row is open to independent plugins.
44
44
  slots: [RUN_ACTIONS_SLOT],
45
45
  });
@@ -1,7 +1,7 @@
1
1
  import { defineSlot } from '@agimon-ai/doompi-web-contracts';
2
2
 
3
3
  /**
4
- * The slot the run drawer opens beside its stop control: an independent
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
  */
@@ -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 drawer the subagents tab shows. */
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;