@agimon-ai/doompi-workflow 0.0.1-alpha.67 → 0.0.1-alpha.68

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-workflow",
3
- "version": "0.0.1-alpha.67",
3
+ "version": "0.0.1-alpha.68",
4
4
  "description": "GitHub Actions-style workflow graphs, artifacts, recovery, and asynchronous runs for DoomPi.",
5
5
  "keywords": [
6
6
  "ai",
@@ -34,7 +34,8 @@
34
34
  "src/web",
35
35
  "src/exports/webClient.ts",
36
36
  "src/types/webWorkflows.ts",
37
- "src/types/webWorkflowTerminal.ts"
37
+ "src/types/webWorkflowTerminal.ts",
38
+ "!src/web/**/*.stories.tsx"
38
39
  ],
39
40
  "type": "module",
40
41
  "main": "./dist/index.cjs",
@@ -68,12 +69,12 @@
68
69
  "@modelcontextprotocol/sdk": "1.30.0",
69
70
  "hono": "4.13.5",
70
71
  "zod": "4.5.4",
71
- "@agimon-ai/doompi-extension-contracts": "0.0.1-alpha.66",
72
- "@agimon-ai/doompi-ui": "0.0.1-alpha.67",
73
- "@agimon-ai/doompi-web-security": "0.0.1-alpha.28",
74
- "@agimon-ai/doompi-telemetry": "0.0.1-alpha.65",
75
- "@agimon-ai/doompi-web-contracts": "0.0.1-alpha.29",
76
- "@agimon-ai/doompi-web-components": "0.0.1-alpha.26"
72
+ "@agimon-ai/doompi-extension-contracts": "0.0.1-alpha.67",
73
+ "@agimon-ai/doompi-telemetry": "0.0.1-alpha.66",
74
+ "@agimon-ai/doompi-web-components": "0.0.1-alpha.27",
75
+ "@agimon-ai/doompi-web-contracts": "0.0.1-alpha.30",
76
+ "@agimon-ai/doompi-web-security": "0.0.1-alpha.29",
77
+ "@agimon-ai/doompi-ui": "0.0.1-alpha.68"
77
78
  },
78
79
  "devDependencies": {
79
80
  "@earendil-works/pi-coding-agent": "0.85.1",
@@ -76,7 +76,7 @@ function DelimitedPreview({ text, delimiter }: { text: string; delimiter: ',' |
76
76
  const [head = [], ...body] = rows;
77
77
  return (
78
78
  <div className="overflow-auto">
79
- <table data-testid="artifact-table" className="min-w-full border-collapse font-mono text-[11px]">
79
+ <table data-testid="artifact-table" className="min-w-full border-collapse font-mono text-sm">
80
80
  <thead className="sticky top-0 bg-doom-panel text-doom-hi">
81
81
  <tr>
82
82
  {head.map((cell, index) => (
@@ -142,12 +142,12 @@ function ArtifactPreview({
142
142
  return raw ? (
143
143
  <pre
144
144
  data-testid="artifact-raw"
145
- className="whitespace-pre-wrap break-words font-mono text-[11px] leading-[17px] text-doom-text"
145
+ className="whitespace-pre-wrap break-words font-mono text-sm leading-snug text-doom-text"
146
146
  >
147
147
  {text}
148
148
  </pre>
149
149
  ) : (
150
- <div data-testid="artifact-markdown" className="max-w-5xl text-[13px] text-doom-text">
150
+ <div data-testid="artifact-markdown" className="max-w-5xl text-base text-doom-text">
151
151
  <Markdown text={text} />
152
152
  </div>
153
153
  );
@@ -160,9 +160,7 @@ function ArtifactPreview({
160
160
  // An incomplete file remains useful as literal text while a workflow writes it.
161
161
  }
162
162
  return (
163
- <pre className="whitespace-pre-wrap break-words font-mono text-[11px] leading-[17px] text-doom-text">
164
- {formatted}
165
- </pre>
163
+ <pre className="whitespace-pre-wrap break-words font-mono text-sm leading-snug text-doom-text">{formatted}</pre>
166
164
  );
167
165
  }
168
166
  if (mimeType === 'text/csv' || mimeType === 'text/tab-separated-values') {
@@ -216,9 +214,7 @@ function ArtifactPreview({
216
214
  );
217
215
  }
218
216
  if (content.text !== undefined) {
219
- return (
220
- <pre className="whitespace-pre-wrap break-words font-mono text-[11px] leading-[17px] text-doom-text">{text}</pre>
221
- );
217
+ return <pre className="whitespace-pre-wrap break-words font-mono text-sm leading-snug text-doom-text">{text}</pre>;
222
218
  }
223
219
  return (
224
220
  <EmptyState title="preview unavailable" description="Download this artifact to open it with a local application." />
@@ -278,9 +274,9 @@ export function ArtifactViewerPanel({
278
274
  return (
279
275
  <div data-testid="artifact-viewer" className="flex min-h-0 flex-1 flex-col px-[26px] py-[18px]">
280
276
  <div className="flex items-center gap-2.5 pb-3">
281
- <span className="truncate text-[12px] font-bold text-doom-hi">{path}</span>
277
+ <span className="truncate text-sm font-bold text-doom-hi">{path}</span>
282
278
  <Badge size="xs">{mimeType ?? 'artifact'}</Badge>
283
- <span className="text-[9px] text-doom-faint">
279
+ <span className="text-2xs text-doom-faint">
284
280
  {content === undefined ? '' : `${formatSize(content.size)} · written ${formatWhen(content.modifiedAt)}`}
285
281
  </span>
286
282
  <span className="min-w-0 flex-1" />
@@ -320,17 +316,17 @@ export function ArtifactViewerPanel({
320
316
  </div>
321
317
  <div className="min-h-0 flex-1 overflow-auto rounded-md border border-doom-border bg-doom-deep p-4">
322
318
  {error !== undefined ? (
323
- <span data-testid="artifact-error" className="text-[10px] text-doom-yellow">
319
+ <span data-testid="artifact-error" className="text-xs text-doom-yellow">
324
320
  {error}
325
321
  </span>
326
322
  ) : content === undefined ? (
327
- <span className="text-[10px] text-doom-faint">loading preview</span>
323
+ <span className="text-xs text-doom-faint">loading preview</span>
328
324
  ) : (
329
325
  <ArtifactPreview content={content} rawUrl={rawUrl} raw={raw} />
330
326
  )}
331
327
  </div>
332
328
  {content?.truncated === true ? (
333
- <span className="pt-2 text-[9px] text-doom-faint">
329
+ <span className="pt-2 text-2xs text-doom-faint">
334
330
  this preview shows the first {formatSize(content.text?.length)}; download the artifact for the complete file
335
331
  </span>
336
332
  ) : null}
@@ -397,8 +393,8 @@ export function ArtifactsPane({
397
393
  className={`flex flex-col gap-0.5 px-3 py-1.5 text-left ${openable ? 'cursor-pointer hover:bg-doom-panel' : 'cursor-default'}`}
398
394
  >
399
395
  <span className="flex min-w-0 items-center gap-2">
400
- <span className={`w-3 shrink-0 text-[10px] ${glyph.className}`}>{glyph.glyph}</span>
401
- <span className={`truncate text-[11px] ${entry.state === 'pending' ? 'text-doom-dim' : 'text-doom-hi'}`}>
396
+ <span className={`w-3 shrink-0 text-xs ${glyph.className}`}>{glyph.glyph}</span>
397
+ <span className={`truncate text-sm ${entry.state === 'pending' ? 'text-doom-dim' : 'text-doom-hi'}`}>
402
398
  {entry.path}
403
399
  </span>
404
400
  {entry.producedBy.length === 0 ? null : (
@@ -407,14 +403,14 @@ export function ArtifactsPane({
407
403
  </Badge>
408
404
  )}
409
405
  <span className="min-w-0 flex-1" />
410
- <span className="shrink-0 text-[9px] text-doom-faint">
406
+ <span className="shrink-0 text-2xs text-doom-faint">
411
407
  {entry.state === 'pending'
412
408
  ? 'not written yet'
413
409
  : `${formatSize(entry.size)} · ${formatWhen(entry.modifiedAt)}`}
414
410
  </span>
415
411
  </span>
416
412
  {entry.description === '' ? null : (
417
- <span className="truncate pl-5 text-[9px] text-doom-faint">{entry.description}</span>
413
+ <span className="truncate pl-5 text-2xs text-doom-faint">{entry.description}</span>
418
414
  )}
419
415
  </button>
420
416
  );
@@ -430,7 +426,7 @@ export function ArtifactsPane({
430
426
  ) : null}
431
427
  {declared.length === 0 ? null : (
432
428
  <>
433
- <span className="px-3 pb-1 pt-2 text-[9px] font-bold tracking-[0.14em] text-doom-faint">
429
+ <span className="px-3 pb-1 pt-2 text-2xs font-bold tracking-wider text-doom-faint">
434
430
  DECLARED · {listing?.description || 'run-directory'}
435
431
  </span>
436
432
  {declared.map((entry) => (
@@ -440,16 +436,14 @@ export function ArtifactsPane({
440
436
  )}
441
437
  {found.length === 0 ? null : (
442
438
  <>
443
- <span className="px-3 pb-1 pt-3 text-[9px] font-bold tracking-[0.14em] text-doom-faint">
444
- ALSO IN THE FOLDER
445
- </span>
439
+ <span className="px-3 pb-1 pt-3 text-2xs font-bold tracking-wider text-doom-faint">ALSO IN THE FOLDER</span>
446
440
  {found.map((entry) => (
447
441
  <Row key={entry.path} entry={entry} />
448
442
  ))}
449
443
  </>
450
444
  )}
451
445
  {listing === undefined ? null : (
452
- <span className="truncate px-3 py-2 text-[9px] text-doom-faint">{listing.runDir}</span>
446
+ <span className="truncate px-3 py-2 text-2xs text-doom-faint">{listing.runDir}</span>
453
447
  )}
454
448
  </div>
455
449
  );
@@ -32,7 +32,7 @@ const BOOLEAN_TYPE = 'boolean';
32
32
  const BOOLEAN_VALUES = ['true', 'false'] as const;
33
33
 
34
34
  function FieldLabel({ children }: { children: string }) {
35
- return <span className="text-[9px] font-bold tracking-[0.18em] text-doom-faint">{children}</span>;
35
+ return <span className="text-2xs font-bold tracking-widest text-doom-faint">{children}</span>;
36
36
  }
37
37
 
38
38
  /** One `workflow_dispatch` input: a picker when the workflow constrains it, a field otherwise. */
@@ -58,7 +58,7 @@ function InputField({
58
58
  className="flex flex-col gap-1.5 sm:flex-row sm:items-center sm:gap-2.5"
59
59
  data-testid={`launch-input-${input.name}`}
60
60
  >
61
- <span className="min-w-0 truncate text-[10px] font-bold text-doom-text sm:w-28 sm:shrink-0">
61
+ <span className="min-w-0 truncate text-xs font-bold text-doom-text sm:w-28 sm:shrink-0">
62
62
  {input.name}
63
63
  {input.required === true ? <span className="text-doom-yellow"> *</span> : null}
64
64
  </span>
@@ -71,7 +71,7 @@ function InputField({
71
71
  />
72
72
  ) : (
73
73
  <Select value={value === '' ? undefined : value} onValueChange={onChange}>
74
- <SelectTrigger className="h-7 w-full min-w-0 text-[10px] sm:min-w-[160px]">
74
+ <SelectTrigger className="h-7 w-full min-w-0 text-xs sm:min-w-[160px]">
75
75
  <SelectValue placeholder="choose…" />
76
76
  </SelectTrigger>
77
77
  <SelectContent>
@@ -83,7 +83,7 @@ function InputField({
83
83
  </SelectContent>
84
84
  </Select>
85
85
  )}
86
- <span className="min-w-0 truncate text-[9px] text-doom-faint sm:w-40 sm:shrink-0">{hint}</span>
86
+ <span className="min-w-0 truncate text-2xs text-doom-faint sm:w-40 sm:shrink-0">{hint}</span>
87
87
  </div>
88
88
  );
89
89
  }
@@ -144,7 +144,7 @@ export function LaunchWorkflowDialog({
144
144
  <DialogContent width="lg" data-testid="launch-workflow-dialog" aria-describedby={undefined}>
145
145
  <DialogHeader>
146
146
  <div className="flex min-w-0 flex-col items-start gap-1 sm:flex-row sm:items-center sm:gap-2.5">
147
- <span className="text-[9px] text-doom-faint">launch workflow</span>
147
+ <span className="text-2xs text-doom-faint">launch workflow</span>
148
148
  <DialogTitle data-testid="launch-workflow-name" className="max-w-full break-words">
149
149
  {workflow.name}
150
150
  </DialogTitle>
@@ -154,7 +154,7 @@ export function LaunchWorkflowDialog({
154
154
  </div>
155
155
  </DialogHeader>
156
156
  <DialogBody>
157
- <p className="text-[11px] leading-relaxed text-doom-dim">
157
+ <p className="text-sm leading-relaxed text-doom-dim">
158
158
  {workflow.description || 'This workflow declares no description.'}
159
159
  </p>
160
160
  <div className="flex flex-col gap-1.5">
@@ -168,15 +168,13 @@ export function LaunchWorkflowDialog({
168
168
  onChange={(event) => setPrompt(event.target.value)}
169
169
  onKeyDown={onPromptKeyDown}
170
170
  />
171
- <span className="text-[9px] text-doom-faint">enter launches · shift+enter for a new line</span>
171
+ <span className="text-2xs text-doom-faint">enter launches · shift+enter for a new line</span>
172
172
  </div>
173
173
  {workflow.inputs.length === 0 ? null : (
174
174
  <div className="flex flex-col gap-2">
175
175
  <div className="flex items-center gap-2">
176
176
  <FieldLabel>INPUTS</FieldLabel>
177
- <span className="text-[9px] text-doom-faint">
178
- workflow_dispatch · {workflow.inputs.length} declared
179
- </span>
177
+ <span className="text-2xs text-doom-faint">workflow_dispatch · {workflow.inputs.length} declared</span>
180
178
  </div>
181
179
  {workflow.inputs.map((input) => (
182
180
  <InputField
@@ -192,13 +190,10 @@ export function LaunchWorkflowDialog({
192
190
  <div className="flex flex-col gap-1.5">
193
191
  <FieldLabel>RUNNER</FieldLabel>
194
192
  {workflow.runners === undefined || workflow.runners.length === 0 ? (
195
- <span className="flex h-7 items-center text-[10px] text-doom-dim">whatever the workflow resolves</span>
193
+ <span className="flex h-7 items-center text-xs text-doom-dim">whatever the workflow resolves</span>
196
194
  ) : (
197
195
  <Select value={runner} onValueChange={setRunner}>
198
- <SelectTrigger
199
- data-testid="launch-runner"
200
- className="h-7 w-full min-w-0 text-[10px] sm:min-w-[160px]"
201
- >
196
+ <SelectTrigger data-testid="launch-runner" className="h-7 w-full min-w-0 text-xs sm:min-w-[160px]">
202
197
  <SelectValue />
203
198
  </SelectTrigger>
204
199
  <SelectContent>
@@ -210,36 +205,36 @@ export function LaunchWorkflowDialog({
210
205
  </SelectContent>
211
206
  </Select>
212
207
  )}
213
- <span className="text-[9px] text-doom-faint">
208
+ <span className="text-2xs text-doom-faint">
214
209
  {workflow.runners === undefined ? 'no runner map declared' : 'the runners every step agrees on'}
215
210
  </span>
216
211
  </div>
217
212
  <div className="flex flex-col gap-1.5">
218
213
  <FieldLabel>JOBS</FieldLabel>
219
- <span className="flex h-7 items-center text-[10px] text-doom-dim">
214
+ <span className="flex h-7 items-center text-xs text-doom-dim">
220
215
  {workflow.jobs.map((job) => job.name).join(' → ') || 'none declared'}
221
216
  </span>
222
- <span className="text-[9px] text-doom-faint">in the order the file declares them</span>
217
+ <span className="text-2xs text-doom-faint">in the order the file declares them</span>
223
218
  </div>
224
219
  <div className="flex flex-col gap-1.5">
225
220
  <FieldLabel>CWD</FieldLabel>
226
- <span className="flex h-7 items-center text-[10px] text-doom-dim">{cwd}</span>
227
- <span className="text-[9px] text-doom-faint">the session's directory</span>
221
+ <span className="flex h-7 items-center text-xs text-doom-dim">{cwd}</span>
222
+ <span className="text-2xs text-doom-faint">the session's directory</span>
228
223
  </div>
229
224
  </div>
230
225
  <div className="flex flex-col gap-1 rounded-md border border-doom-border bg-doom-deep px-3 py-2">
231
226
  <FieldLabel>SENT TO THE SESSION</FieldLabel>
232
- <pre data-testid="launch-line" className="whitespace-pre-wrap break-words text-[10px] text-doom-green">
227
+ <pre data-testid="launch-line" className="whitespace-pre-wrap break-words text-xs text-doom-green">
233
228
  {line}
234
229
  </pre>
235
230
  </div>
236
231
  {problems.length === 0 ? null : (
237
- <p data-testid="launch-problems" className="text-[10px] text-doom-yellow">
232
+ <p data-testid="launch-problems" className="text-xs text-doom-yellow">
238
233
  {problems.join(' ')}
239
234
  </p>
240
235
  )}
241
236
  <DialogFooter className="flex-wrap sm:flex-nowrap">
242
- <span className="w-full text-[9px] text-doom-faint sm:w-auto">
237
+ <span className="w-full text-2xs text-doom-faint sm:w-auto">
243
238
  the run appears on the board as soon as it starts
244
239
  </span>
245
240
  <span className="min-w-0 flex-1" />
@@ -160,15 +160,13 @@ export function StepTerminalPanel({ sessionId, target }: WebPluginSlotProps & {
160
160
  return (
161
161
  <div data-testid="step-terminal-panel" className="flex min-h-0 flex-1 flex-col px-[26px] py-[18px]">
162
162
  <div data-testid="step-terminal-head" className="flex items-center gap-2.5 pb-3">
163
- <span className="shrink-0 truncate text-[12px] font-bold text-doom-hi">
164
- {run?.displayName ?? target.runKey}
165
- </span>
166
- <span className="text-[10px] text-doom-faint">›</span>
167
- <span className="shrink-0 truncate text-[11px] font-bold text-doom-blue">{target.job}</span>
163
+ <span className="shrink-0 truncate text-sm font-bold text-doom-hi">{run?.displayName ?? target.runKey}</span>
164
+ <span className="text-xs text-doom-faint">›</span>
165
+ <span className="shrink-0 truncate text-sm font-bold text-doom-blue">{target.job}</span>
168
166
  {target.step === undefined ? null : (
169
167
  <>
170
- <span className="text-[10px] text-doom-faint">›</span>
171
- <span className="min-w-0 truncate text-[11px] text-doom-text">{target.step}</span>
168
+ <span className="text-xs text-doom-faint">›</span>
169
+ <span className="min-w-0 truncate text-sm text-doom-text">{target.step}</span>
172
170
  </>
173
171
  )}
174
172
  {run === undefined ? null : (
@@ -204,12 +202,12 @@ export function StepTerminalPanel({ sessionId, target }: WebPluginSlotProps & {
204
202
  data-testid="step-terminal-screen"
205
203
  tabIndex={0}
206
204
  onKeyDown={onKeyDown}
207
- className={`min-h-0 flex-1 overflow-y-auto rounded-md border bg-doom-deep px-4 py-3 font-mono text-[11px] leading-[15px] text-doom-text outline-none ${
205
+ className={`min-h-0 flex-1 overflow-y-auto rounded-md border bg-doom-deep px-4 py-3 font-mono text-sm leading-tight text-doom-text outline-none ${
208
206
  held ? 'border-doom-blue/60' : 'border-doom-border'
209
207
  }`}
210
208
  >
211
209
  {lines.length === 0 ? (
212
- <span className="text-[10px] text-doom-faint">
210
+ <span className="text-xs text-doom-faint">
213
211
  {capabilities?.readable === false
214
212
  ? (capabilities.reason ?? 'This run has no terminal to read.')
215
213
  : 'waiting for the run to paint…'}
@@ -220,7 +218,7 @@ export function StepTerminalPanel({ sessionId, target }: WebPluginSlotProps & {
220
218
  {held && !ended ? <StreamCursor className="mt-0.5 h-[12px] w-1.5" /> : null}
221
219
  </div>
222
220
  <div className="flex items-center gap-3 pt-2.5">
223
- <span data-testid="step-terminal-hint" className="text-[9px] text-doom-faint">
221
+ <span data-testid="step-terminal-hint" className="text-2xs text-doom-faint">
224
222
  {held
225
223
  ? 'keys go to the run · ctrl-c and ctrl-d pass through · esc releases the keyboard'
226
224
  : capabilities?.writable === false
@@ -229,12 +227,12 @@ export function StepTerminalPanel({ sessionId, target }: WebPluginSlotProps & {
229
227
  </span>
230
228
  <span className="min-w-0 flex-1" />
231
229
  {ended ? (
232
- <span data-testid="step-terminal-ended" className="text-[9px] text-doom-faint">
230
+ <span data-testid="step-terminal-ended" className="text-2xs text-doom-faint">
233
231
  the run has settled; this is its last screen
234
232
  </span>
235
233
  ) : null}
236
234
  {notice === undefined ? null : (
237
- <span data-testid="step-terminal-notice" className="text-[9px] text-doom-yellow">
235
+ <span data-testid="step-terminal-notice" className="text-2xs text-doom-yellow">
238
236
  {notice}
239
237
  </span>
240
238
  )}
@@ -27,7 +27,7 @@ function Detail({ workflow }: { workflow: WorkflowCatalogEntryView }) {
27
27
  ['file', workflow.relativePath],
28
28
  ];
29
29
  return (
30
- <dl data-testid={`catalog-detail-${workflow.name}`} className="flex flex-col gap-1 pt-1 text-[9px]">
30
+ <dl data-testid={`catalog-detail-${workflow.name}`} className="flex flex-col gap-1 pt-1 text-2xs">
31
31
  {rows.map(([label, value]) => (
32
32
  <div key={label} className="flex gap-2">
33
33
  <dt className="w-16 shrink-0 text-doom-faint">{label}</dt>
@@ -69,26 +69,26 @@ function WorkflowRow({
69
69
  }`}
70
70
  >
71
71
  <div className="flex min-w-0 items-center gap-2">
72
- <span className="truncate text-[12px] font-bold text-doom-hi">{workflow.name}</span>
72
+ <span className="truncate text-sm font-bold text-doom-hi">{workflow.name}</span>
73
73
  {workflow.tags.map((tag) => (
74
74
  <Badge key={tag} size="xs" className="shrink-0">
75
75
  {tag}
76
76
  </Badge>
77
77
  ))}
78
78
  <span className="min-w-0 flex-1" />
79
- <span className={`shrink-0 text-[9px] ${workflow.error === undefined ? 'text-doom-cyan' : 'text-doom-red'}`}>
79
+ <span className={`shrink-0 text-2xs ${workflow.error === undefined ? 'text-doom-cyan' : 'text-doom-red'}`}>
80
80
  {workflow.error === undefined ? `${jobs} job${jobs === 1 ? '' : 's'}` : 'unreadable'}
81
81
  </span>
82
82
  </div>
83
- <span className="truncate text-[10px] text-doom-dim">{workflow.description || workflow.relativePath}</span>
83
+ <span className="truncate text-xs text-doom-dim">{workflow.description || workflow.relativePath}</span>
84
84
  {workflow.error === undefined ? (
85
- <span className="truncate text-[9px] text-doom-faint">
85
+ <span className="truncate text-2xs text-doom-faint">
86
86
  {workflow.inputs.length} input{workflow.inputs.length === 1 ? '' : 's'} ·{' '}
87
87
  {workflow.runners === undefined ? 'any runner' : workflow.runners.join(', ') || 'no runner in common'}
88
88
  {workflow.artifacts.length > 0 ? ` · ${workflow.artifacts.length} artifacts` : ''}
89
89
  </span>
90
90
  ) : (
91
- <span className="truncate text-[9px] text-doom-red">{workflow.error}</span>
91
+ <span className="truncate text-2xs text-doom-red">{workflow.error}</span>
92
92
  )}
93
93
  {selected ? (
94
94
  <div className="flex items-center gap-3 pt-0.5">
@@ -179,7 +179,7 @@ export function WorkflowCatalogDrawer({
179
179
  className="flex w-[min(440px,calc(100vw-24px))] shrink-0 flex-col overflow-hidden border-l border-doom-border bg-doom-rail outline-none"
180
180
  >
181
181
  <div className="flex h-11 shrink-0 items-center gap-2.5 border-b border-doom-border px-4">
182
- <span className="text-[13px] font-bold text-doom-hi">workflow catalog</span>
182
+ <span className="text-base font-bold text-doom-hi">workflow catalog</span>
183
183
  <Badge size="xs" data-testid="catalog-count">
184
184
  {state.workflows.length} workflow{state.workflows.length === 1 ? '' : 's'}
185
185
  </Badge>
@@ -200,7 +200,7 @@ export function WorkflowCatalogDrawer({
200
200
  </div>
201
201
  <div role="listbox" className="flex min-h-0 flex-1 flex-col overflow-y-auto pb-2">
202
202
  {shown.length === 0 ? (
203
- <p data-testid="catalog-empty" className="px-4 py-3 text-[10px] text-doom-faint">
203
+ <p data-testid="catalog-empty" className="px-4 py-3 text-xs text-doom-faint">
204
204
  {state.warning ??
205
205
  (state.workflows.length === 0
206
206
  ? 'no workflows found for this session; add one under automations/workflows'
@@ -220,8 +220,8 @@ export function WorkflowCatalogDrawer({
220
220
  ))}
221
221
  </div>
222
222
  <div className="flex h-8 shrink-0 items-center justify-between border-t border-doom-border-soft bg-doom-deep px-4">
223
- <span className="text-[9px] text-doom-faint">↑↓ choose · enter launch · i inspect · esc close</span>
224
- <span className="text-[9px] text-doom-faint">{state.cwd}</span>
223
+ <span className="text-2xs text-doom-faint">↑↓ choose · enter launch · i inspect · esc close</span>
224
+ <span className="text-2xs text-doom-faint">{state.cwd}</span>
225
225
  </div>
226
226
  </aside>
227
227
  );
@@ -59,7 +59,7 @@ export function WorkflowsActivitySection({ sessionId, openTransientTab }: WebPlu
59
59
  if (groups.length === 0) {
60
60
  return (
61
61
  <div className="flex items-center gap-2 px-1">
62
- <p data-testid="activity-summary-workflows" className="text-[10px] text-doom-faint">
62
+ <p data-testid="activity-summary-workflows" className="text-xs text-doom-faint">
63
63
  idle
64
64
  </p>
65
65
  <Button
@@ -91,20 +91,20 @@ export function WorkflowsActivitySection({ sessionId, openTransientTab }: WebPlu
91
91
  focusRun(sessionId, row.identity);
92
92
  openTransientTab(workflowsTab());
93
93
  }}
94
- className="min-w-0 gap-0.5 rounded-[5px] px-1 py-1 hover:bg-doom-panel"
94
+ className="min-w-0 gap-0.5 rounded-md px-1 py-1 hover:bg-doom-panel"
95
95
  >
96
96
  <span className="flex min-w-0 items-center gap-1.5">
97
97
  <Dot tone={TONE_DOT[row.tone]} pulse={row.tone === 'running'} />
98
98
  <span
99
- className={`min-w-0 flex-1 truncate text-[10px] font-bold ${
99
+ className={`min-w-0 flex-1 truncate text-xs font-bold ${
100
100
  row.tone === 'running' || row.tone === 'paused' ? 'text-doom-hi' : 'text-doom-dim'
101
101
  }`}
102
102
  >
103
103
  {row.name}
104
104
  </span>
105
- <span className="shrink-0 text-[9px] text-doom-faint">{row.elapsed}</span>
105
+ <span className="shrink-0 text-2xs text-doom-faint">{row.elapsed}</span>
106
106
  </span>
107
- <span className={`truncate pl-3 text-[9px] ${TONE_DETAIL[row.tone]}`}>{row.detail}</span>
107
+ <span className={`truncate pl-3 text-2xs ${TONE_DETAIL[row.tone]}`}>{row.detail}</span>
108
108
  </Button>
109
109
  );
110
110
 
@@ -121,15 +121,15 @@ export function WorkflowsActivitySection({ sessionId, openTransientTab }: WebPlu
121
121
  data-open={open}
122
122
  aria-expanded={open}
123
123
  onClick={() => setFolded((current) => ({ ...current, [group.name]: !open }))}
124
- className="justify-start gap-1.5 rounded-[5px] px-1 py-0.5 hover:bg-doom-panel"
124
+ className="justify-start gap-1.5 rounded-md px-1 py-0.5 hover:bg-doom-panel"
125
125
  >
126
126
  {open ? (
127
127
  <ChevronDownIcon className="h-2.5 w-2.5 text-doom-faint" />
128
128
  ) : (
129
129
  <ChevronRightIcon className="h-2.5 w-2.5 text-doom-faint" />
130
130
  )}
131
- <span className={`text-[10px] font-bold ${GROUP_LABEL[group.name]}`}>{group.name}</span>
132
- <span className="text-[9px] text-doom-faint">{group.rows.length}</span>
131
+ <span className={`text-xs font-bold ${GROUP_LABEL[group.name]}`}>{group.name}</span>
132
+ <span className="text-2xs text-doom-faint">{group.rows.length}</span>
133
133
  </Button>
134
134
  {open ? (
135
135
  <div className="flex flex-col gap-0.5 pl-2">
@@ -138,7 +138,7 @@ export function WorkflowsActivitySection({ sessionId, openTransientTab }: WebPlu
138
138
  ))}
139
139
  </div>
140
140
  ) : (
141
- <span className="truncate pl-5 text-[9px] text-doom-faint">
141
+ <span className="truncate pl-5 text-2xs text-doom-faint">
142
142
  {group.rows
143
143
  .slice(0, 2)
144
144
  .map((row) => row.name)
@@ -154,7 +154,7 @@ function WorkflowPicker({
154
154
  >
155
155
  <SearchIcon className="h-3 w-3 shrink-0 text-doom-faint" />
156
156
  <span className="min-w-0 flex-1 truncate text-left font-bold text-doom-hi">{selected.displayName}</span>
157
- <span className="shrink-0 text-[9px] font-normal text-doom-faint">{runs.length} workflows</span>
157
+ <span className="shrink-0 text-2xs font-normal text-doom-faint">{runs.length} workflows</span>
158
158
  <ChevronDownIcon className="h-3 w-3 shrink-0 text-doom-dim" />
159
159
  </Button>
160
160
  </PopoverTrigger>
@@ -175,13 +175,11 @@ function WorkflowPicker({
175
175
  </PopoverHeader>
176
176
  <div className="grid max-h-[360px] grid-cols-1 gap-2 overflow-y-auto p-2 sm:grid-cols-2">
177
177
  {sections.length === 0 ? (
178
- <span className="px-2 py-5 text-center text-[10px] text-doom-faint sm:col-span-2">
179
- no matching workflows
180
- </span>
178
+ <span className="px-2 py-5 text-center text-xs text-doom-faint sm:col-span-2">no matching workflows</span>
181
179
  ) : (
182
180
  sections.map((section) => (
183
181
  <div key={section.label} className="flex min-w-0 flex-col gap-0.5">
184
- <span className="px-2 py-1 text-[8px] font-bold uppercase tracking-[0.14em] text-doom-faint">
182
+ <span className="px-2 py-1 text-2xs font-bold uppercase tracking-wider text-doom-faint">
185
183
  {section.label} · {section.runs.length}
186
184
  </span>
187
185
  {section.runs.map((run) => {
@@ -204,12 +202,12 @@ function WorkflowPicker({
204
202
  >
205
203
  <span className="flex min-w-0 items-center gap-2">
206
204
  <Dot tone={runDot(run)} pulse={run.stage === 'running'} />
207
- <span className={cn('min-w-0 flex-1 truncate text-[10px]', active && 'font-bold text-doom-hi')}>
205
+ <span className={cn('min-w-0 flex-1 truncate text-xs', active && 'font-bold text-doom-hi')}>
208
206
  {run.displayName}
209
207
  </span>
210
- <span className="shrink-0 text-[8px] text-doom-faint">{runState(run)}</span>
208
+ <span className="shrink-0 text-2xs text-doom-faint">{runState(run)}</span>
211
209
  </span>
212
- <span className="truncate pl-3.5 text-[8px] text-doom-dim">
210
+ <span className="truncate pl-3.5 text-2xs text-doom-dim">
213
211
  {[run.position?.job, run.position?.step].filter(Boolean).join(' · ') || 'settled'}
214
212
  </span>
215
213
  </button>
@@ -244,10 +242,10 @@ function SelectedAttention({ run }: { run: WorkflowRunView }) {
244
242
  <StatusBadge size="xs" tone={attention.kind === 'error' ? 'error' : 'running'}>
245
243
  {attention.kind === 'error' ? 'ERROR' : 'PAUSED'}
246
244
  </StatusBadge>
247
- <span data-testid={`needs-card-${run.runKey}`} className="min-w-0 flex-1 truncate text-[10px] text-doom-text">
245
+ <span data-testid={`needs-card-${run.runKey}`} className="min-w-0 flex-1 truncate text-xs text-doom-text">
248
246
  {run.displayName}: {attention.text}
249
247
  </span>
250
- <span className="shrink-0 text-[9px] text-doom-faint">
248
+ <span className="shrink-0 text-2xs text-doom-faint">
251
249
  {attention.kind === 'error' ? 'recover from the owning session' : 'resume from the owning session'}
252
250
  </span>
253
251
  </div>
@@ -275,11 +273,11 @@ function JobRow({
275
273
  onClick={onSelect}
276
274
  className={cn('gap-2 rounded px-2 py-1.5', !selected && 'hover:bg-doom-deep')}
277
275
  >
278
- <span className={`w-3 shrink-0 text-[10px] ${icon.className}`}>{icon.glyph}</span>
279
- <OptionLabel density="compact" className={cn('text-[10px]', selected ? 'text-doom-hi' : 'text-doom-text')}>
276
+ <span className={`w-3 shrink-0 text-xs ${icon.className}`}>{icon.glyph}</span>
277
+ <OptionLabel density="compact" className={cn('text-xs', selected ? 'text-doom-hi' : 'text-doom-text')}>
280
278
  {job.name}
281
279
  </OptionLabel>
282
- <span className="shrink-0 text-[8px] text-doom-faint">{spanDuration(job.startedAt, job.endedAt, now) ?? ''}</span>
280
+ <span className="shrink-0 text-2xs text-doom-faint">{spanDuration(job.startedAt, job.endedAt, now) ?? ''}</span>
283
281
  </OptionRow>
284
282
  );
285
283
  }
@@ -309,23 +307,21 @@ function StepRow({
309
307
  )}
310
308
  >
311
309
  <span className="flex items-center gap-2">
312
- <span className={`w-3 shrink-0 text-[10px] ${icon.className}`}>{icon.glyph}</span>
310
+ <span className={`w-3 shrink-0 text-xs ${icon.className}`}>{icon.glyph}</span>
313
311
  <span
314
312
  className={cn(
315
- 'min-w-0 flex-1 truncate text-[9px]',
313
+ 'min-w-0 flex-1 truncate text-2xs',
316
314
  step.status === 'failed' ? 'text-doom-red' : 'text-doom-text',
317
315
  )}
318
316
  >
319
317
  {step.name}
320
318
  </span>
321
- <span className="shrink-0 text-[8px] text-doom-faint">
319
+ <span className="shrink-0 text-2xs text-doom-faint">
322
320
  {spanDuration(step.startedAt, step.endedAt, now) ?? ''}
323
321
  </span>
324
322
  </span>
325
323
  {step.reason === undefined ? null : (
326
- <span
327
- className={cn('truncate pl-5 text-[8px]', step.status === 'failed' ? 'text-doom-red' : 'text-doom-faint')}
328
- >
324
+ <span className={cn('truncate pl-5 text-2xs', step.status === 'failed' ? 'text-doom-red' : 'text-doom-faint')}>
329
325
  {step.reason}
330
326
  </span>
331
327
  )}
@@ -385,12 +381,12 @@ function InlineStepOutput({
385
381
  <div data-testid="workflow-inline-output" className="flex min-h-0 flex-1 flex-col bg-doom-deep">
386
382
  <div className="flex h-9 shrink-0 items-center gap-2 border-b border-doom-border-soft px-3">
387
383
  <Dot tone={ended ? 'neutral' : 'blue'} pulse={!ended && run.stage === 'running'} />
388
- <span className={cn('text-[9px] font-bold', ended ? 'text-doom-faint' : 'text-doom-blue')}>
384
+ <span className={cn('text-2xs font-bold', ended ? 'text-doom-faint' : 'text-doom-blue')}>
389
385
  {ended ? 'FINAL OUTPUT' : 'LIVE OUTPUT'}
390
386
  </span>
391
- <span className="min-w-0 truncate text-[10px] font-bold text-doom-hi">{step?.name ?? job.name}</span>
387
+ <span className="min-w-0 truncate text-xs font-bold text-doom-hi">{step?.name ?? job.name}</span>
392
388
  <span className="min-w-0 flex-1" />
393
- <span className="text-[8px] text-doom-faint">{ended ? 'settled' : 'following · 500ms'}</span>
389
+ <span className="text-2xs text-doom-faint">{ended ? 'settled' : 'following · 500ms'}</span>
394
390
  <Button variant="outline" size="xs" data-testid="workflow-open-terminal" onClick={onOpenTerminal}>
395
391
  open terminal
396
392
  </Button>
@@ -398,10 +394,10 @@ function InlineStepOutput({
398
394
  <div
399
395
  ref={screenRef}
400
396
  data-testid="workflow-inline-screen"
401
- className="min-h-0 flex-1 overflow-y-auto px-4 py-3 font-mono text-[11px] leading-[15px] text-doom-text"
397
+ className="min-h-0 flex-1 overflow-y-auto px-4 py-3 font-mono text-sm leading-tight text-doom-text"
402
398
  >
403
399
  {lines.length === 0 ? (
404
- <span className="text-[10px] text-doom-faint">
400
+ <span className="text-xs text-doom-faint">
405
401
  {capabilities?.readable === false
406
402
  ? (capabilities.reason ?? 'This run has no terminal to read.')
407
403
  : 'waiting for the run to paint…'}
@@ -411,7 +407,7 @@ function InlineStepOutput({
411
407
  )}
412
408
  {!ended && run.stage === 'running' ? <StreamCursor className="mt-0.5 h-[12px] w-1.5" /> : null}
413
409
  </div>
414
- <div className="flex h-7 shrink-0 items-center gap-2 border-t border-doom-border-soft px-3 text-[8px] text-doom-faint">
410
+ <div className="flex h-7 shrink-0 items-center gap-2 border-t border-doom-border-soft px-3 text-2xs text-doom-faint">
415
411
  <span>output follows automatically · select another step to inspect its current screen</span>
416
412
  <span className="min-w-0 flex-1" />
417
413
  <span>last 48 lines</span>
@@ -459,11 +455,11 @@ function DeleteWorkflowDialog({
459
455
  <DialogTitle>Delete {run.displayName}?</DialogTitle>
460
456
  </DialogHeader>
461
457
  <DialogBody>
462
- <p className="text-[11px] leading-relaxed text-doom-dim">
458
+ <p className="text-sm leading-relaxed text-doom-dim">
463
459
  This permanently removes the workflow run, including its logs and artifacts. This action cannot be undone.
464
460
  </p>
465
461
  {error === undefined ? null : (
466
- <p data-testid="delete-workflow-error" className="text-[10px] text-doom-red">
462
+ <p data-testid="delete-workflow-error" className="text-xs text-doom-red">
467
463
  {error}
468
464
  </p>
469
465
  )}
@@ -569,13 +565,13 @@ export function WorkflowsPanel({ sessionId, openTransientTab, sendSessionFrame }
569
565
  <StatusBadge tone={run.stage === 'error' ? 'error' : run.stage === 'running' ? 'info' : 'ok'}>
570
566
  {runState(run)}
571
567
  </StatusBadge>
572
- <span className="truncate text-[9px] text-doom-dim">
568
+ <span className="truncate text-2xs text-doom-dim">
573
569
  {[run.position?.job, run.position?.step].filter(Boolean).join(' · ') ||
574
570
  run.workflowName ||
575
571
  run.displayName}
576
572
  </span>
577
573
  <span className="min-w-0 flex-1" />
578
- <span data-testid="workflows-tally" className="text-[9px] text-doom-faint">
574
+ <span data-testid="workflows-tally" className="text-2xs text-doom-faint">
579
575
  {runs.filter((candidate) => candidate.stage === 'running').length} running · {runs.length} total
580
576
  </span>
581
577
  {run.stage === 'running' ? null : (
@@ -606,9 +602,9 @@ export function WorkflowsPanel({ sessionId, openTransientTab, sendSessionFrame }
606
602
  className="flex max-h-44 w-full shrink-0 flex-col overflow-y-auto rounded-md border border-doom-border bg-doom-panel p-1.5 sm:max-h-none sm:w-[214px]"
607
603
  >
608
604
  <div className="flex items-center px-2 pb-1 pt-1">
609
- <span className="text-[8px] font-bold tracking-[0.14em] text-doom-faint">JOBS</span>
605
+ <span className="text-2xs font-bold tracking-wider text-doom-faint">JOBS</span>
610
606
  <span className="min-w-0 flex-1" />
611
- <span className="text-[8px] text-doom-faint">
607
+ <span className="text-2xs text-doom-faint">
612
608
  {jobs.filter((candidate) => candidate.status === 'completed').length}/{jobs.length}
613
609
  </span>
614
610
  </div>
@@ -635,11 +631,11 @@ export function WorkflowsPanel({ sessionId, openTransientTab, sendSessionFrame }
635
631
  <div className="flex items-center px-2 pb-1">
636
632
  <span
637
633
  data-testid="job-pane-name"
638
- className="min-w-0 flex-1 truncate text-[9px] font-bold text-doom-hi"
634
+ className="min-w-0 flex-1 truncate text-2xs font-bold text-doom-hi"
639
635
  >
640
636
  {job.name}
641
637
  </span>
642
- <span className="text-[8px] text-doom-faint">
638
+ <span className="text-2xs text-doom-faint">
643
639
  {spanDuration(job.startedAt, job.endedAt, now) ?? ''}
644
640
  </span>
645
641
  </div>
@@ -662,7 +658,7 @@ export function WorkflowsPanel({ sessionId, openTransientTab, sendSessionFrame }
662
658
  </div>
663
659
  <DetailPane>
664
660
  <div className="flex h-9 shrink-0 items-center gap-2 border-b border-doom-border-soft bg-doom-panel px-3">
665
- <span className="min-w-0 truncate text-[10px] font-bold text-doom-hi">
661
+ <span className="min-w-0 truncate text-xs font-bold text-doom-hi">
666
662
  {job?.name ?? run.displayName}
667
663
  </span>
668
664
  {job === undefined ? null : (