stagent 0.1.11 → 0.1.13

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.
Files changed (145) hide show
  1. package/README.md +74 -49
  2. package/package.json +3 -2
  3. package/public/readme/cost-usage-list.png +0 -0
  4. package/public/readme/dashboard-bulk-select.png +0 -0
  5. package/public/readme/dashboard-card-edit.png +0 -0
  6. package/public/readme/dashboard-create-form-ai-applied.png +0 -0
  7. package/public/readme/dashboard-create-form-ai-assist.png +0 -0
  8. package/public/readme/dashboard-create-form-empty.png +0 -0
  9. package/public/readme/dashboard-create-form-filled.png +0 -0
  10. package/public/readme/dashboard-filtered.png +0 -0
  11. package/public/readme/dashboard-list.png +0 -0
  12. package/public/readme/dashboard-workflow-confirm.png +0 -0
  13. package/public/readme/home-below-fold.png +0 -0
  14. package/public/readme/home-list.png +0 -0
  15. package/public/readme/inbox-list.png +0 -0
  16. package/public/readme/playbook-list.png +0 -0
  17. package/public/readme/profiles-list.png +0 -0
  18. package/public/readme/settings-list.png +0 -0
  19. package/public/readme/workflows-list.png +0 -0
  20. package/src/__tests__/e2e/blueprint.test.ts +63 -0
  21. package/src/__tests__/e2e/cross-runtime.test.ts +77 -0
  22. package/src/__tests__/e2e/helpers.ts +286 -0
  23. package/src/__tests__/e2e/parallel-workflow.test.ts +120 -0
  24. package/src/__tests__/e2e/sequence-workflow.test.ts +109 -0
  25. package/src/__tests__/e2e/setup.ts +156 -0
  26. package/src/__tests__/e2e/single-task.test.ts +170 -0
  27. package/src/app/api/command-palette/recent/route.ts +41 -18
  28. package/src/app/api/context/batch/route.ts +44 -0
  29. package/src/app/api/permissions/presets/route.ts +80 -0
  30. package/src/app/api/playbook/status/route.ts +15 -0
  31. package/src/app/api/profiles/route.ts +23 -20
  32. package/src/app/api/settings/pricing/route.ts +15 -0
  33. package/src/app/api/tasks/[id]/route.ts +54 -3
  34. package/src/app/api/workflows/[id]/route.ts +43 -4
  35. package/src/app/api/workflows/[id]/status/route.ts +70 -2
  36. package/src/app/api/workflows/from-assist/route.ts +6 -32
  37. package/src/app/costs/page.tsx +53 -43
  38. package/src/app/dashboard/page.tsx +59 -21
  39. package/src/app/documents/[id]/page.tsx +10 -8
  40. package/src/app/globals.css +11 -0
  41. package/src/app/page.tsx +60 -3
  42. package/src/app/playbook/[slug]/page.tsx +76 -0
  43. package/src/app/playbook/page.tsx +54 -0
  44. package/src/app/profiles/page.tsx +7 -4
  45. package/src/app/settings/page.tsx +2 -2
  46. package/src/app/tasks/[id]/page.tsx +22 -2
  47. package/src/components/costs/cost-dashboard.tsx +226 -320
  48. package/src/components/dashboard/activity-feed.tsx +6 -2
  49. package/src/components/dashboard/greeting.tsx +3 -1
  50. package/src/components/dashboard/priority-queue.tsx +58 -9
  51. package/src/components/dashboard/stats-cards.tsx +16 -2
  52. package/src/components/documents/document-chip-bar.tsx +183 -0
  53. package/src/components/documents/document-content-renderer.tsx +146 -0
  54. package/src/components/documents/document-detail-view.tsx +16 -239
  55. package/src/components/documents/image-zoom-view.tsx +60 -0
  56. package/src/components/documents/smart-extracted-text.tsx +47 -0
  57. package/src/components/documents/utils.ts +70 -0
  58. package/src/components/notifications/batch-proposal-review.tsx +150 -0
  59. package/src/components/notifications/inbox-list.tsx +4 -5
  60. package/src/components/notifications/notification-item.tsx +73 -6
  61. package/src/components/notifications/pending-approval-host.tsx +63 -14
  62. package/src/components/playbook/adoption-heatmap.tsx +69 -0
  63. package/src/components/playbook/journey-card.tsx +110 -0
  64. package/src/components/playbook/playbook-action-button.tsx +22 -0
  65. package/src/components/playbook/playbook-browser.tsx +143 -0
  66. package/src/components/playbook/playbook-card.tsx +102 -0
  67. package/src/components/playbook/playbook-detail-view.tsx +225 -0
  68. package/src/components/playbook/playbook-homepage.tsx +142 -0
  69. package/src/components/playbook/playbook-toc.tsx +90 -0
  70. package/src/components/playbook/playbook-updated-badge.tsx +23 -0
  71. package/src/components/playbook/related-docs.tsx +30 -0
  72. package/src/components/profiles/__tests__/learned-context-panel.test.tsx +175 -0
  73. package/src/components/profiles/context-proposal-review.tsx +7 -3
  74. package/src/components/profiles/learned-context-panel.tsx +116 -8
  75. package/src/components/profiles/profile-browser.tsx +1 -0
  76. package/src/components/profiles/profile-card.tsx +16 -8
  77. package/src/components/profiles/profile-detail-view.tsx +12 -4
  78. package/src/components/settings/__tests__/auth-config-section.test.tsx +147 -0
  79. package/src/components/settings/api-key-form.tsx +5 -43
  80. package/src/components/settings/auth-config-section.tsx +10 -6
  81. package/src/components/settings/auth-status-badge.tsx +8 -0
  82. package/src/components/settings/budget-guardrails-section.tsx +403 -620
  83. package/src/components/settings/connection-test-control.tsx +63 -0
  84. package/src/components/settings/permissions-section.tsx +85 -75
  85. package/src/components/settings/permissions-sections.tsx +24 -0
  86. package/src/components/settings/presets-section.tsx +159 -0
  87. package/src/components/settings/pricing-registry-panel.tsx +164 -0
  88. package/src/components/shared/app-sidebar.tsx +4 -2
  89. package/src/components/shared/command-palette.tsx +30 -0
  90. package/src/components/shared/light-markdown.tsx +134 -0
  91. package/src/components/tasks/__tests__/kanban-board-accessibility.test.tsx +1 -1
  92. package/src/components/tasks/ai-assist-panel.tsx +108 -78
  93. package/src/components/tasks/content-preview.tsx +2 -1
  94. package/src/components/tasks/kanban-board.tsx +57 -5
  95. package/src/components/tasks/kanban-column.tsx +34 -23
  96. package/src/components/tasks/task-bento-cell.tsx +50 -0
  97. package/src/components/tasks/task-bento-grid.tsx +155 -0
  98. package/src/components/tasks/task-card.tsx +14 -16
  99. package/src/components/tasks/task-chip-bar.tsx +207 -0
  100. package/src/components/tasks/task-detail-view.tsx +42 -190
  101. package/src/components/tasks/task-result-renderer.tsx +33 -0
  102. package/src/components/workflows/blueprint-gallery.tsx +19 -12
  103. package/src/components/workflows/blueprint-preview.tsx +8 -1
  104. package/src/components/workflows/loop-status-view.tsx +2 -4
  105. package/src/components/workflows/swarm-dashboard.tsx +2 -3
  106. package/src/components/workflows/workflow-confirmation-view.tsx +2 -7
  107. package/src/components/workflows/workflow-full-output.tsx +80 -0
  108. package/src/components/workflows/workflow-kanban-card.tsx +121 -0
  109. package/src/components/workflows/workflow-list.tsx +47 -42
  110. package/src/components/workflows/workflow-status-view.tsx +163 -16
  111. package/src/lib/agents/learned-context.ts +27 -15
  112. package/src/lib/agents/learning-session.ts +354 -0
  113. package/src/lib/agents/pattern-extractor.ts +19 -0
  114. package/src/lib/agents/profiles/__tests__/sort.test.ts +42 -0
  115. package/src/lib/agents/profiles/sort.ts +7 -0
  116. package/src/lib/constants/card-icons.tsx +202 -0
  117. package/src/lib/constants/prose-styles.ts +7 -0
  118. package/src/lib/constants/settings.ts +1 -0
  119. package/src/lib/constants/task-status.ts +3 -0
  120. package/src/lib/db/schema.ts +3 -0
  121. package/src/lib/docs/adoption.ts +105 -0
  122. package/src/lib/docs/journey-tracker.ts +21 -0
  123. package/src/lib/docs/reader.ts +107 -0
  124. package/src/lib/docs/types.ts +54 -0
  125. package/src/lib/docs/usage-stage.ts +60 -0
  126. package/src/lib/documents/context-builder.ts +41 -0
  127. package/src/lib/notifications/actionable.ts +18 -10
  128. package/src/lib/queries/chart-data.ts +20 -1
  129. package/src/lib/settings/__tests__/budget-guardrails.test.ts +86 -24
  130. package/src/lib/settings/budget-guardrails.ts +213 -85
  131. package/src/lib/settings/permission-presets.ts +150 -0
  132. package/src/lib/settings/runtime-setup.ts +71 -0
  133. package/src/lib/usage/__tests__/ledger.test.ts +2 -2
  134. package/src/lib/usage/__tests__/pricing-registry.test.ts +78 -0
  135. package/src/lib/usage/ledger.ts +1 -1
  136. package/src/lib/usage/pricing-registry.ts +570 -0
  137. package/src/lib/usage/pricing.ts +15 -95
  138. package/src/lib/utils/__tests__/learned-context-history.test.ts +171 -0
  139. package/src/lib/utils/learned-context-history.ts +150 -0
  140. package/src/lib/validators/__tests__/settings.test.ts +23 -16
  141. package/src/lib/validators/settings.ts +3 -9
  142. package/src/lib/workflows/engine.ts +75 -61
  143. package/src/lib/workflows/types.ts +2 -0
  144. package/tsconfig.json +2 -1
  145. package/src/components/documents/document-preview.tsx +0 -68
@@ -11,6 +11,7 @@ import { EmptyState } from "@/components/shared/empty-state";
11
11
  import { GitBranch, Plus, Pencil, Copy, RotateCcw, Trash2, Layers } from "lucide-react";
12
12
  import { toast } from "sonner";
13
13
  import { workflowStatusVariant, patternLabels } from "@/lib/constants/status-colors";
14
+ import { IconCircle, getWorkflowIconFromName } from "@/lib/constants/card-icons";
14
15
 
15
16
  interface Workflow {
16
17
  id: string;
@@ -137,6 +138,7 @@ export function WorkflowList({ projects }: WorkflowListProps) {
137
138
  const pattern = getPattern(wf.definition);
138
139
  const stepCount = getStepCount(wf.definition);
139
140
  const promptPreview = getPromptPreview(wf.definition);
141
+ const wfIcon = getWorkflowIconFromName(wf.name, pattern);
140
142
  return (
141
143
  <Card
142
144
  key={wf.id}
@@ -146,11 +148,9 @@ export function WorkflowList({ projects }: WorkflowListProps) {
146
148
  onKeyDown={(e) => { if (e.key === "Enter" || e.key === " ") { e.preventDefault(); router.push(`/workflows/${wf.id}`); } }}
147
149
  >
148
150
  <CardHeader className="pb-2">
149
- <div className="flex items-center justify-between">
150
- <CardTitle className="text-base font-medium">{wf.name}</CardTitle>
151
- <Badge variant={workflowStatusVariant[wf.status] ?? "secondary"}>
152
- {wf.status}
153
- </Badge>
151
+ <div className="flex items-center gap-3">
152
+ <IconCircle icon={wfIcon.icon} colors={wfIcon.colors} />
153
+ <CardTitle className="min-w-0 truncate text-base font-medium">{wf.name}</CardTitle>
154
154
  </div>
155
155
  </CardHeader>
156
156
  <CardContent>
@@ -164,49 +164,54 @@ export function WorkflowList({ projects }: WorkflowListProps) {
164
164
  {promptPreview}
165
165
  </p>
166
166
  )}
167
- <div className="flex items-center gap-1 mt-3">
168
- {wf.status === "draft" && (
169
- <Button
170
- variant="ghost"
171
- size="icon"
172
- className="h-7 w-7"
173
- aria-label="Edit workflow"
174
- onClick={(e) => { e.stopPropagation(); router.push(`/workflows/${wf.id}/edit`); }}
175
- >
176
- <Pencil className="h-3.5 w-3.5" />
177
- </Button>
178
- )}
179
- <Button
180
- variant="ghost"
181
- size="icon"
182
- className="h-7 w-7"
183
- aria-label="Clone workflow"
184
- onClick={(e) => { e.stopPropagation(); router.push(`/workflows/${wf.id}/edit?clone=true`); }}
185
- >
186
- <Copy className="h-3.5 w-3.5" />
187
- </Button>
188
- {(wf.status === "completed" || wf.status === "failed") && (
167
+ <div className="flex items-center justify-between mt-3">
168
+ <Badge variant={workflowStatusVariant[wf.status] ?? "secondary"}>
169
+ {wf.status}
170
+ </Badge>
171
+ <div className="flex items-center gap-1">
172
+ {wf.status === "draft" && (
173
+ <Button
174
+ variant="ghost"
175
+ size="icon"
176
+ className="h-7 w-7"
177
+ aria-label="Edit workflow"
178
+ onClick={(e) => { e.stopPropagation(); router.push(`/workflows/${wf.id}/edit`); }}
179
+ >
180
+ <Pencil className="h-3.5 w-3.5" />
181
+ </Button>
182
+ )}
189
183
  <Button
190
184
  variant="ghost"
191
185
  size="icon"
192
186
  className="h-7 w-7"
193
- aria-label="Re-run workflow"
194
- onClick={(e) => { e.stopPropagation(); handleRerun(wf.id); }}
195
- >
196
- <RotateCcw className="h-3.5 w-3.5" />
197
- </Button>
198
- )}
199
- {wf.status !== "active" && (
200
- <Button
201
- variant="ghost"
202
- size="icon"
203
- className="h-7 w-7 text-destructive"
204
- aria-label="Delete workflow"
205
- onClick={(e) => { e.stopPropagation(); setConfirmDeleteId(wf.id); }}
187
+ aria-label="Clone workflow"
188
+ onClick={(e) => { e.stopPropagation(); router.push(`/workflows/${wf.id}/edit?clone=true`); }}
206
189
  >
207
- <Trash2 className="h-3.5 w-3.5" />
190
+ <Copy className="h-3.5 w-3.5" />
208
191
  </Button>
209
- )}
192
+ {(wf.status === "completed" || wf.status === "failed") && (
193
+ <Button
194
+ variant="ghost"
195
+ size="icon"
196
+ className="h-7 w-7"
197
+ aria-label="Re-run workflow"
198
+ onClick={(e) => { e.stopPropagation(); handleRerun(wf.id); }}
199
+ >
200
+ <RotateCcw className="h-3.5 w-3.5" />
201
+ </Button>
202
+ )}
203
+ {wf.status !== "active" && (
204
+ <Button
205
+ variant="ghost"
206
+ size="icon"
207
+ className="h-7 w-7 text-destructive"
208
+ aria-label="Delete workflow"
209
+ onClick={(e) => { e.stopPropagation(); setConfirmDeleteId(wf.id); }}
210
+ >
211
+ <Trash2 className="h-3.5 w-3.5" />
212
+ </Button>
213
+ )}
214
+ </div>
210
215
  </div>
211
216
  </CardContent>
212
217
  </Card>
@@ -2,6 +2,7 @@
2
2
 
3
3
  import { useEffect, useState, useCallback } from "react";
4
4
  import { useRouter } from "next/navigation";
5
+ import Link from "next/link";
5
6
  import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
6
7
  import { Badge } from "@/components/ui/badge";
7
8
  import { Button } from "@/components/ui/button";
@@ -20,12 +21,21 @@ import {
20
21
  Clock3,
21
22
  GitBranch,
22
23
  MessageSquareMore,
24
+ ChevronDown,
25
+ ChevronRight,
26
+ FileText,
27
+ Paperclip,
23
28
  } from "lucide-react";
29
+ import ReactMarkdown from "react-markdown";
30
+ import remarkGfm from "remark-gfm";
24
31
  import { toast } from "sonner";
25
32
  import { workflowStatusVariant, patternLabels } from "@/lib/constants/status-colors";
33
+ import { IconCircle, getWorkflowIconFromName } from "@/lib/constants/card-icons";
26
34
  import { LoopStatusView } from "./loop-status-view";
27
35
  import { ConfirmDialog } from "@/components/shared/confirm-dialog";
36
+ import { LightMarkdown } from "@/components/shared/light-markdown";
28
37
  import { SwarmDashboard } from "./swarm-dashboard";
38
+ import { WorkflowFullOutput } from "./workflow-full-output";
29
39
  import type { LoopState, LoopConfig, SwarmConfig } from "@/lib/workflows/types";
30
40
 
31
41
  interface StepWithState {
@@ -43,6 +53,14 @@ interface StepWithState {
43
53
  };
44
54
  }
45
55
 
56
+ interface DocumentInfo {
57
+ id: string;
58
+ originalName: string;
59
+ mimeType: string;
60
+ storagePath: string;
61
+ direction: string;
62
+ }
63
+
46
64
  interface WorkflowStatusData {
47
65
  id: string;
48
66
  name: string;
@@ -54,6 +72,8 @@ interface WorkflowStatusData {
54
72
  loopConfig?: LoopConfig;
55
73
  loopState?: LoopState;
56
74
  swarmConfig?: SwarmConfig;
75
+ stepDocuments?: Record<string, DocumentInfo[]>;
76
+ parentDocuments?: DocumentInfo[];
57
77
  }
58
78
 
59
79
  interface WorkflowStatusViewProps {
@@ -69,6 +89,72 @@ const stepStatusIcons: Record<string, React.ReactNode> = {
69
89
  waiting_dependencies: <Clock3 className="h-4 w-4 text-status-warning" />,
70
90
  };
71
91
 
92
+ /** Expandable step result: collapsed LightMarkdown preview + full markdown on expand */
93
+ export function ExpandableResult({ result }: { result: string }) {
94
+ const [expanded, setExpanded] = useState(false);
95
+
96
+ if (!result) return null;
97
+
98
+ return (
99
+ <div className="mt-2">
100
+ {expanded ? (
101
+ <>
102
+ <div className="prose prose-sm dark:prose-invert max-w-none max-h-96 overflow-auto rounded-md border bg-muted/30 p-3">
103
+ <ReactMarkdown remarkPlugins={[remarkGfm]}>{result}</ReactMarkdown>
104
+ </div>
105
+ <Button
106
+ variant="ghost"
107
+ size="sm"
108
+ className="mt-1 h-6 text-xs text-muted-foreground"
109
+ onClick={() => setExpanded(false)}
110
+ >
111
+ <ChevronDown className="h-3 w-3 mr-1" />
112
+ Collapse
113
+ </Button>
114
+ </>
115
+ ) : (
116
+ <>
117
+ <LightMarkdown content={result.slice(0, 500)} lineClamp={2} />
118
+ {result.length > 200 && (
119
+ <Button
120
+ variant="ghost"
121
+ size="sm"
122
+ className="mt-1 h-6 text-xs text-muted-foreground"
123
+ onClick={() => setExpanded(true)}
124
+ >
125
+ <ChevronRight className="h-3 w-3 mr-1" />
126
+ Expand
127
+ </Button>
128
+ )}
129
+ </>
130
+ )}
131
+ </div>
132
+ );
133
+ }
134
+
135
+ /** Document list for a single step or parent task */
136
+ function DocumentList({ docs, label }: { docs: DocumentInfo[]; label: string }) {
137
+ if (docs.length === 0) return null;
138
+
139
+ return (
140
+ <div className="mt-3">
141
+ <p className="text-xs font-medium text-muted-foreground mb-1.5">{label}</p>
142
+ <div className="space-y-1">
143
+ {docs.map((doc) => (
144
+ <Link
145
+ key={doc.id}
146
+ href={`/documents/${doc.id}`}
147
+ className="flex items-center gap-2 text-xs text-brand-blue hover:underline"
148
+ >
149
+ <FileText className="h-3 w-3 shrink-0" />
150
+ {doc.originalName}
151
+ </Link>
152
+ ))}
153
+ </div>
154
+ </div>
155
+ );
156
+ }
157
+
72
158
 
73
159
  export function WorkflowStatusView({ workflowId }: WorkflowStatusViewProps) {
74
160
  const router = useRouter();
@@ -218,16 +304,31 @@ export function WorkflowStatusView({ workflowId }: WorkflowStatusViewProps) {
218
304
  ? data.steps.find((step) => step.dependsOn?.length) ?? null
219
305
  : null;
220
306
 
307
+ // Collect all completed step outputs for full output sheet
308
+ const completedStepOutputs = data.steps
309
+ .filter((s) => s.state.result && s.state.status === "completed")
310
+ .map((s) => ({ name: s.name, result: s.state.result! }));
311
+
312
+ // Check for any documents
313
+ const hasStepDocs = data.stepDocuments && Object.keys(data.stepDocuments).length > 0;
314
+ const hasParentDocs = data.parentDocuments && data.parentDocuments.length > 0;
315
+
221
316
  return (
222
- <>
317
+ <div className="space-y-6">
223
318
  <Card>
224
319
  <CardHeader>
225
320
  <div className="flex items-center justify-between">
226
- <div>
227
- <CardTitle>{data.name}</CardTitle>
228
- <p className="text-sm text-muted-foreground mt-1">
229
- {patternLabels[data.pattern] ?? data.pattern}
230
- </p>
321
+ <div className="flex items-center gap-3">
322
+ <IconCircle
323
+ icon={getWorkflowIconFromName(data.name, data.pattern).icon}
324
+ colors={getWorkflowIconFromName(data.name, data.pattern).colors}
325
+ />
326
+ <div>
327
+ <CardTitle>{data.name}</CardTitle>
328
+ <p className="text-sm text-muted-foreground mt-1">
329
+ {patternLabels[data.pattern] ?? data.pattern}
330
+ </p>
331
+ </div>
231
332
  </div>
232
333
  <div className="flex items-center gap-2">
233
334
  <Badge variant={workflowStatusVariant[data.status] ?? "secondary"}>
@@ -363,9 +464,14 @@ export function WorkflowStatusView({ workflowId }: WorkflowStatusViewProps) {
363
464
  </p>
364
465
  )}
365
466
  {step.state.result && step.state.status === "completed" && (
366
- <p className="mt-2 text-xs text-muted-foreground line-clamp-4">
367
- {step.state.result.slice(0, 260)}
368
- </p>
467
+ <ExpandableResult result={step.state.result} />
468
+ )}
469
+ {/* Step output documents */}
470
+ {step.state.taskId && data.stepDocuments?.[step.state.taskId] && (
471
+ <DocumentList
472
+ docs={data.stepDocuments[step.state.taskId]}
473
+ label="Generated Files"
474
+ />
369
475
  )}
370
476
  </div>
371
477
  </div>
@@ -409,10 +515,15 @@ export function WorkflowStatusView({ workflowId }: WorkflowStatusViewProps) {
409
515
  )}
410
516
  {synthesisStep.state.result &&
411
517
  synthesisStep.state.status === "completed" && (
412
- <p className="mt-2 text-xs text-muted-foreground line-clamp-4">
413
- {synthesisStep.state.result.slice(0, 260)}
414
- </p>
518
+ <ExpandableResult result={synthesisStep.state.result} />
415
519
  )}
520
+ {/* Synthesis step documents */}
521
+ {synthesisStep.state.taskId && data.stepDocuments?.[synthesisStep.state.taskId] && (
522
+ <DocumentList
523
+ docs={data.stepDocuments[synthesisStep.state.taskId]}
524
+ label="Generated Files"
525
+ />
526
+ )}
416
527
  </div>
417
528
  </div>
418
529
  </div>
@@ -448,9 +559,14 @@ export function WorkflowStatusView({ workflowId }: WorkflowStatusViewProps) {
448
559
  </p>
449
560
  )}
450
561
  {step.state.result && step.state.status === "completed" && (
451
- <p className="text-xs text-muted-foreground mt-1 line-clamp-2">
452
- {step.state.result.slice(0, 200)}
453
- </p>
562
+ <ExpandableResult result={step.state.result} />
563
+ )}
564
+ {/* Step output documents */}
565
+ {step.state.taskId && data.stepDocuments?.[step.state.taskId] && (
566
+ <DocumentList
567
+ docs={data.stepDocuments[step.state.taskId]}
568
+ label="Generated Files"
569
+ />
454
570
  )}
455
571
  </div>
456
572
  </div>
@@ -459,9 +575,40 @@ export function WorkflowStatusView({ workflowId }: WorkflowStatusViewProps) {
459
575
  )}
460
576
  </div>
461
577
  )}
578
+
579
+ {/* Documents section — parent inputs and step outputs */}
580
+ {(hasParentDocs || hasStepDocs) && (
581
+ <div className="mt-6 pt-4 border-t border-border/50">
582
+ <div className="flex items-center gap-2 mb-3">
583
+ <Paperclip className="h-4 w-4 text-muted-foreground" />
584
+ <p className="text-sm font-medium">Documents</p>
585
+ </div>
586
+ {hasParentDocs && (
587
+ <DocumentList docs={data.parentDocuments!} label="Input Files" />
588
+ )}
589
+ {hasStepDocs && Object.entries(data.stepDocuments!).map(([taskId, docs]) => {
590
+ const step = data.steps.find((s) => s.state.taskId === taskId);
591
+ return (
592
+ <DocumentList
593
+ key={taskId}
594
+ docs={docs}
595
+ label={step ? `Output: ${step.name}` : "Output Files"}
596
+ />
597
+ );
598
+ })}
599
+ </div>
600
+ )}
462
601
  </CardContent>
463
602
  </Card>
464
603
 
604
+ {/* Full output — inline below workflow card when completed */}
605
+ {data.status === "completed" && completedStepOutputs.length > 0 && (
606
+ <WorkflowFullOutput
607
+ workflowName={data.name}
608
+ steps={completedStepOutputs}
609
+ />
610
+ )}
611
+
465
612
  {/* Delete confirmation */}
466
613
  <ConfirmDialog
467
614
  open={confirmDelete}
@@ -472,6 +619,6 @@ export function WorkflowStatusView({ workflowId }: WorkflowStatusViewProps) {
472
619
  onConfirm={handleDelete}
473
620
  destructive
474
621
  />
475
- </>
622
+ </div>
476
623
  );
477
624
  }
@@ -58,14 +58,24 @@ function getNextVersion(profileId: string): number {
58
58
  // Proposal flow
59
59
  // ---------------------------------------------------------------------------
60
60
 
61
- /** Insert a context proposal and create a notification for human review */
61
+ /**
62
+ * Insert a context proposal and optionally create a notification for human review.
63
+ *
64
+ * When `options.silent` is true, the proposal row is created but no notification
65
+ * is generated. This is used by the learning session system to buffer proposals
66
+ * during workflow execution — a batch notification is created when the session closes.
67
+ *
68
+ * Returns the learned_context row ID (not the notification ID) so callers can
69
+ * reference the proposal regardless of whether a notification was created.
70
+ */
62
71
  export async function proposeContextAddition(
63
72
  profileId: string,
64
73
  taskId: string,
65
- additions: string
74
+ additions: string,
75
+ options?: { silent?: boolean }
66
76
  ): Promise<string> {
67
77
  const version = getNextVersion(profileId);
68
- const notificationId = crypto.randomUUID();
78
+ const notificationId = options?.silent ? null : crypto.randomUUID();
69
79
  const rowId = crypto.randomUUID();
70
80
  const now = new Date();
71
81
 
@@ -83,19 +93,21 @@ export async function proposeContextAddition(
83
93
  createdAt: now,
84
94
  });
85
95
 
86
- // Create notification for human review
87
- await db.insert(notifications).values({
88
- id: notificationId,
89
- taskId,
90
- type: "context_proposal",
91
- title: `Context proposal for ${profileId}`,
92
- body: additions.slice(0, 500),
93
- toolName: profileId,
94
- toolInput: JSON.stringify({ profileId, additions, learnedContextId: rowId }),
95
- createdAt: now,
96
- });
96
+ // Create notification for human review (unless silent)
97
+ if (notificationId) {
98
+ await db.insert(notifications).values({
99
+ id: notificationId,
100
+ taskId,
101
+ type: "context_proposal",
102
+ title: `Context proposal for ${profileId}`,
103
+ body: additions.slice(0, 500),
104
+ toolName: profileId,
105
+ toolInput: JSON.stringify({ profileId, additions, learnedContextId: rowId }),
106
+ createdAt: now,
107
+ });
108
+ }
97
109
 
98
- return notificationId;
110
+ return rowId;
99
111
  }
100
112
 
101
113
  // ---------------------------------------------------------------------------