stagent 0.9.5 → 0.10.0
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/README.md +5 -42
- package/dist/cli.js +42 -18
- package/docs/.coverage-gaps.json +13 -55
- package/docs/.last-generated +1 -1
- package/docs/features/provider-runtimes.md +4 -0
- package/docs/features/schedules.md +32 -4
- package/docs/features/settings.md +28 -5
- package/docs/features/tables.md +9 -2
- package/docs/features/workflows.md +10 -4
- package/docs/journeys/developer.md +15 -1
- package/docs/journeys/personal-use.md +21 -4
- package/docs/superpowers/plans/2026-04-07-instance-bootstrap.md +1691 -0
- package/docs/superpowers/plans/2026-04-08-schedule-orchestration.md +2983 -0
- package/docs/superpowers/plans/2026-04-11-schedule-maxturns-api-control.md +551 -0
- package/docs/superpowers/plans/2026-04-11-task-create-profile-validation.md +864 -0
- package/docs/superpowers/plans/2026-04-11-task-runtime-stagent-mcp-injection.md +739 -0
- package/docs/superpowers/specs/2026-04-08-chat-sse-resilience-hotfix-design.md +201 -0
- package/docs/superpowers/specs/2026-04-08-schedule-orchestration-design.md +371 -0
- package/docs/superpowers/specs/2026-04-08-swarm-visibility-design.md +213 -0
- package/package.json +3 -2
- package/src/__tests__/instrumentation-smoke.test.ts +15 -0
- package/src/app/analytics/page.tsx +1 -21
- package/src/app/api/chat/conversations/[id]/messages/route.ts +22 -1
- package/src/app/api/diagnostics/chat-streams/route.ts +65 -0
- package/src/app/api/instance/config/route.ts +41 -0
- package/src/app/api/instance/init/route.ts +34 -0
- package/src/app/api/instance/upgrade/check/route.ts +26 -0
- package/src/app/api/instance/upgrade/route.ts +96 -0
- package/src/app/api/instance/upgrade/status/route.ts +35 -0
- package/src/app/api/memory/route.ts +0 -11
- package/src/app/api/notifications/route.ts +4 -2
- package/src/app/api/projects/[id]/route.ts +5 -155
- package/src/app/api/projects/__tests__/delete-project.test.ts +10 -19
- package/src/app/api/schedules/[id]/execute/route.ts +111 -0
- package/src/app/api/schedules/[id]/route.ts +9 -1
- package/src/app/api/schedules/__tests__/execute-route.test.ts +118 -0
- package/src/app/api/schedules/route.ts +3 -12
- package/src/app/api/settings/openai/login/route.ts +22 -0
- package/src/app/api/settings/openai/logout/route.ts +7 -0
- package/src/app/api/settings/openai/route.ts +21 -1
- package/src/app/api/settings/providers/route.ts +35 -8
- package/src/app/api/tables/[id]/enrich/__tests__/route.test.ts +153 -0
- package/src/app/api/tables/[id]/enrich/plan/route.ts +98 -0
- package/src/app/api/tables/[id]/enrich/route.ts +147 -0
- package/src/app/api/tables/[id]/enrich/runs/route.ts +25 -0
- package/src/app/api/tasks/[id]/execute/route.ts +0 -21
- package/src/app/api/workflows/[id]/resume/route.ts +59 -0
- package/src/app/api/workflows/[id]/status/route.ts +22 -8
- package/src/app/api/workspace/context/route.ts +2 -0
- package/src/app/api/workspace/fix-data-dir/route.ts +81 -0
- package/src/app/chat/page.tsx +11 -0
- package/src/app/inbox/page.tsx +12 -5
- package/src/app/layout.tsx +42 -21
- package/src/app/page.tsx +0 -2
- package/src/app/settings/page.tsx +6 -9
- package/src/components/chat/__tests__/chat-session-provider.test.tsx +408 -0
- package/src/components/chat/chat-command-popover.tsx +2 -2
- package/src/components/chat/chat-input.tsx +2 -3
- package/src/components/chat/chat-session-provider.tsx +720 -0
- package/src/components/chat/chat-shell.tsx +92 -401
- package/src/components/instance/__tests__/instance-section.test.tsx +125 -0
- package/src/components/instance/instance-section.tsx +382 -0
- package/src/components/instance/upgrade-badge.tsx +219 -0
- package/src/components/notifications/__tests__/batch-proposal-review.test.tsx +95 -0
- package/src/components/notifications/__tests__/notification-item.test.tsx +106 -0
- package/src/components/notifications/batch-proposal-review.tsx +20 -5
- package/src/components/notifications/inbox-list.tsx +11 -2
- package/src/components/notifications/notification-item.tsx +56 -2
- package/src/components/notifications/pending-approval-host.tsx +56 -37
- package/src/components/schedules/schedule-create-sheet.tsx +19 -1
- package/src/components/schedules/schedule-edit-sheet.tsx +20 -1
- package/src/components/schedules/schedule-form.tsx +31 -0
- package/src/components/settings/__tests__/providers-runtimes-section.test.tsx +149 -0
- package/src/components/settings/auth-method-selector.tsx +19 -4
- package/src/components/settings/auth-status-badge.tsx +28 -3
- package/src/components/settings/openai-chatgpt-auth-control.tsx +278 -0
- package/src/components/settings/openai-runtime-section.tsx +7 -1
- package/src/components/settings/providers-runtimes-section.tsx +138 -19
- package/src/components/shared/app-sidebar.tsx +4 -3
- package/src/components/shared/command-palette.tsx +4 -5
- package/src/components/shared/theme-toggle.tsx +5 -24
- package/src/components/shared/workspace-indicator.tsx +61 -2
- package/src/components/tables/__tests__/table-enrichment-sheet.test.tsx +130 -0
- package/src/components/tables/table-create-sheet.tsx +4 -0
- package/src/components/tables/table-enrichment-runs.tsx +103 -0
- package/src/components/tables/table-enrichment-sheet.tsx +538 -0
- package/src/components/tables/table-spreadsheet.tsx +29 -5
- package/src/components/tables/table-toolbar.tsx +10 -1
- package/src/components/tasks/kanban-board.tsx +1 -0
- package/src/components/tasks/kanban-column.tsx +53 -14
- package/src/components/tasks/task-bento-grid.tsx +19 -0
- package/src/components/tasks/task-card.tsx +26 -3
- package/src/components/tasks/task-chip-bar.tsx +24 -0
- package/src/components/tasks/task-result-renderer.tsx +1 -1
- package/src/components/workflows/delay-step-body.tsx +109 -0
- package/src/components/workflows/hooks/use-workflow-status.ts +50 -0
- package/src/components/workflows/loop-status-view.tsx +1 -1
- package/src/components/workflows/shared/step-result.tsx +78 -0
- package/src/components/workflows/shared/workflow-header.tsx +141 -0
- package/src/components/workflows/shared/workflow-loading-skeleton.tsx +36 -0
- package/src/components/workflows/swarm-dashboard.tsx +2 -15
- package/src/components/workflows/views/loop-pattern-view.tsx +137 -0
- package/src/components/workflows/views/sequence-pattern-view.tsx +511 -0
- package/src/components/workflows/workflow-form-view.tsx +133 -16
- package/src/components/workflows/workflow-status-view.tsx +30 -740
- package/src/instrumentation-node.ts +94 -0
- package/src/instrumentation.ts +4 -48
- package/src/lib/agents/__tests__/claude-agent.test.ts +199 -0
- package/src/lib/agents/__tests__/execution-manager.test.ts +1 -27
- package/src/lib/agents/__tests__/failure-reason.test.ts +68 -0
- package/src/lib/agents/__tests__/learned-context.test.ts +0 -11
- package/src/lib/agents/__tests__/learning-session.test.ts +158 -0
- package/src/lib/agents/__tests__/pattern-extractor.test.ts +48 -0
- package/src/lib/agents/claude-agent.ts +155 -18
- package/src/lib/agents/execution-manager.ts +0 -35
- package/src/lib/agents/learned-context.ts +0 -12
- package/src/lib/agents/learning-session.ts +18 -5
- package/src/lib/agents/profiles/__tests__/registry.test.ts +6 -4
- package/src/lib/agents/profiles/builtins/upgrade-assistant/SKILL.md +70 -0
- package/src/lib/agents/profiles/builtins/upgrade-assistant/profile.yaml +32 -0
- package/src/lib/agents/runtime/__tests__/openai-codex-auth.test.ts +118 -0
- package/src/lib/agents/runtime/codex-app-server-client.ts +11 -5
- package/src/lib/agents/runtime/openai-codex-auth.ts +389 -0
- package/src/lib/agents/runtime/openai-codex.ts +29 -60
- package/src/lib/agents/runtime/types.ts +8 -0
- package/src/lib/book/chapter-mapping.ts +11 -0
- package/src/lib/book/content.ts +10 -0
- package/src/lib/chat/__tests__/active-streams.test.ts +49 -0
- package/src/lib/chat/__tests__/finalize-safety-net.test.ts +139 -0
- package/src/lib/chat/__tests__/reconcile.test.ts +137 -0
- package/src/lib/chat/__tests__/stream-telemetry.test.ts +151 -0
- package/src/lib/chat/active-streams.ts +27 -0
- package/src/lib/chat/codex-engine.ts +16 -17
- package/src/lib/chat/context-builder.ts +5 -3
- package/src/lib/chat/engine.ts +50 -3
- package/src/lib/chat/reconcile.ts +117 -0
- package/src/lib/chat/stagent-tools.ts +1 -0
- package/src/lib/chat/stream-telemetry.ts +132 -0
- package/src/lib/chat/suggested-prompts.ts +28 -1
- package/src/lib/chat/system-prompt.ts +26 -1
- package/src/lib/chat/tool-catalog.ts +2 -1
- package/src/lib/chat/tools/__tests__/enrich-table-tool.test.ts +127 -0
- package/src/lib/chat/tools/__tests__/schedule-tools.test.ts +261 -0
- package/src/lib/chat/tools/__tests__/task-tools.test.ts +352 -0
- package/src/lib/chat/tools/__tests__/workflow-tools-dedup.test.ts +217 -0
- package/src/lib/chat/tools/document-tools.ts +29 -13
- package/src/lib/chat/tools/helpers.ts +39 -0
- package/src/lib/chat/tools/notification-tools.ts +9 -5
- package/src/lib/chat/tools/project-tools.ts +33 -0
- package/src/lib/chat/tools/schedule-tools.ts +44 -11
- package/src/lib/chat/tools/table-tools.ts +71 -0
- package/src/lib/chat/tools/task-tools.ts +84 -20
- package/src/lib/chat/tools/workflow-tools.ts +234 -32
- package/src/lib/constants/settings.ts +8 -18
- package/src/lib/data/__tests__/clear.test.ts +56 -2
- package/src/lib/data/clear.ts +20 -15
- package/src/lib/data/delete-project.ts +171 -0
- package/src/lib/db/__tests__/bootstrap.test.ts +1 -1
- package/src/lib/db/bootstrap.ts +45 -16
- package/src/lib/db/index.ts +5 -0
- package/src/lib/db/migrations/0009_add_app_instances.sql +25 -0
- package/src/lib/db/migrations/0024_add_workflow_resume_at.sql +10 -0
- package/src/lib/db/migrations/0025_drop_app_instances.sql +3 -0
- package/src/lib/db/migrations/0026_drop_license.sql +3 -0
- package/src/lib/db/migrations/meta/_journal.json +21 -0
- package/src/lib/db/schema.ts +68 -23
- package/src/lib/environment/workspace-context.ts +13 -1
- package/src/lib/import/dedup.ts +4 -54
- package/src/lib/instance/__tests__/bootstrap.test.ts +362 -0
- package/src/lib/instance/__tests__/detect.test.ts +115 -0
- package/src/lib/instance/__tests__/fingerprint.test.ts +48 -0
- package/src/lib/instance/__tests__/git-ops.test.ts +95 -0
- package/src/lib/instance/__tests__/settings.test.ts +83 -0
- package/src/lib/instance/__tests__/upgrade-poller.test.ts +131 -0
- package/src/lib/instance/bootstrap.ts +270 -0
- package/src/lib/instance/detect.ts +49 -0
- package/src/lib/instance/fingerprint.ts +78 -0
- package/src/lib/instance/git-ops.ts +95 -0
- package/src/lib/instance/settings.ts +61 -0
- package/src/lib/instance/types.ts +77 -0
- package/src/lib/instance/upgrade-poller.ts +153 -0
- package/src/lib/notifications/__tests__/visibility.test.ts +51 -0
- package/src/lib/notifications/visibility.ts +33 -0
- package/src/lib/schedules/__tests__/collision-check.test.ts +93 -0
- package/src/lib/schedules/__tests__/config.test.ts +62 -0
- package/src/lib/schedules/__tests__/firing-metrics.test.ts +99 -0
- package/src/lib/schedules/__tests__/integration.test.ts +82 -0
- package/src/lib/schedules/__tests__/slot-claim.test.ts +242 -0
- package/src/lib/schedules/__tests__/tick-scheduler.test.ts +102 -0
- package/src/lib/schedules/__tests__/turn-budget.test.ts +228 -0
- package/src/lib/schedules/collision-check.ts +105 -0
- package/src/lib/schedules/config.ts +53 -0
- package/src/lib/schedules/scheduler.ts +232 -13
- package/src/lib/schedules/slot-claim.ts +105 -0
- package/src/lib/settings/__tests__/openai-auth.test.ts +101 -0
- package/src/lib/settings/__tests__/openai-login-manager.test.ts +64 -0
- package/src/lib/settings/__tests__/runtime-setup.test.ts +33 -0
- package/src/lib/settings/openai-auth.ts +105 -10
- package/src/lib/settings/openai-login-manager.ts +260 -0
- package/src/lib/settings/runtime-setup.ts +14 -4
- package/src/lib/tables/__tests__/enrichment-planner.test.ts +124 -0
- package/src/lib/tables/__tests__/enrichment.test.ts +147 -0
- package/src/lib/tables/enrichment-planner.ts +454 -0
- package/src/lib/tables/enrichment.ts +328 -0
- package/src/lib/tables/query-builder.ts +5 -2
- package/src/lib/tables/trigger-evaluator.ts +3 -2
- package/src/lib/theme.ts +71 -0
- package/src/lib/usage/ledger.ts +2 -18
- package/src/lib/util/__tests__/similarity.test.ts +106 -0
- package/src/lib/util/similarity.ts +77 -0
- package/src/lib/utils/format-timestamp.ts +24 -0
- package/src/lib/utils/stagent-paths.ts +12 -0
- package/src/lib/validators/__tests__/blueprint.test.ts +172 -0
- package/src/lib/validators/__tests__/settings.test.ts +10 -0
- package/src/lib/validators/blueprint.ts +70 -9
- package/src/lib/validators/profile.ts +2 -2
- package/src/lib/validators/settings.ts +3 -1
- package/src/lib/workflows/__tests__/delay.test.ts +196 -0
- package/src/lib/workflows/__tests__/engine.test.ts +8 -0
- package/src/lib/workflows/__tests__/loop-executor.test.ts +54 -0
- package/src/lib/workflows/__tests__/post-action.test.ts +108 -0
- package/src/lib/workflows/blueprints/instantiator.ts +22 -1
- package/src/lib/workflows/blueprints/types.ts +10 -2
- package/src/lib/workflows/delay.ts +106 -0
- package/src/lib/workflows/engine.ts +207 -4
- package/src/lib/workflows/loop-executor.ts +349 -24
- package/src/lib/workflows/post-action.ts +91 -0
- package/src/lib/workflows/types.ts +166 -1
- package/src/app/api/license/checkout/route.ts +0 -28
- package/src/app/api/license/portal/route.ts +0 -26
- package/src/app/api/license/route.ts +0 -89
- package/src/app/api/license/usage/route.ts +0 -63
- package/src/app/api/marketplace/browse/route.ts +0 -15
- package/src/app/api/marketplace/import/route.ts +0 -28
- package/src/app/api/marketplace/publish/route.ts +0 -40
- package/src/app/api/onboarding/email/route.ts +0 -53
- package/src/app/api/settings/telemetry/route.ts +0 -14
- package/src/app/api/sync/export/route.ts +0 -54
- package/src/app/api/sync/restore/route.ts +0 -37
- package/src/app/api/sync/sessions/route.ts +0 -24
- package/src/app/auth/callback/route.ts +0 -73
- package/src/app/marketplace/page.tsx +0 -19
- package/src/components/analytics/analytics-gate-card.tsx +0 -101
- package/src/components/marketplace/blueprint-card.tsx +0 -61
- package/src/components/marketplace/marketplace-browser.tsx +0 -131
- package/src/components/onboarding/email-capture-card.tsx +0 -104
- package/src/components/settings/activation-form.tsx +0 -95
- package/src/components/settings/cloud-account-section.tsx +0 -147
- package/src/components/settings/cloud-sync-section.tsx +0 -155
- package/src/components/settings/subscription-section.tsx +0 -410
- package/src/components/settings/telemetry-section.tsx +0 -80
- package/src/components/shared/premium-gate-overlay.tsx +0 -50
- package/src/components/shared/schedule-gate-dialog.tsx +0 -64
- package/src/components/shared/upgrade-banner.tsx +0 -112
- package/src/hooks/use-supabase-auth.ts +0 -79
- package/src/lib/billing/email.ts +0 -54
- package/src/lib/billing/products.ts +0 -80
- package/src/lib/billing/stripe.ts +0 -101
- package/src/lib/cloud/supabase-browser.ts +0 -32
- package/src/lib/cloud/supabase-client.ts +0 -56
- package/src/lib/license/__tests__/features.test.ts +0 -56
- package/src/lib/license/__tests__/key-format.test.ts +0 -88
- package/src/lib/license/__tests__/manager.test.ts +0 -64
- package/src/lib/license/__tests__/tier-limits.test.ts +0 -79
- package/src/lib/license/cloud-validation.ts +0 -60
- package/src/lib/license/features.ts +0 -44
- package/src/lib/license/key-format.ts +0 -101
- package/src/lib/license/limit-check.ts +0 -111
- package/src/lib/license/limit-queries.ts +0 -51
- package/src/lib/license/manager.ts +0 -345
- package/src/lib/license/notifications.ts +0 -59
- package/src/lib/license/tier-limits.ts +0 -71
- package/src/lib/marketplace/marketplace-client.ts +0 -107
- package/src/lib/sync/cloud-sync.ts +0 -235
- package/src/lib/telemetry/conversion-events.ts +0 -71
- package/src/lib/telemetry/queue.ts +0 -122
- package/src/lib/validators/license.ts +0 -33
|
@@ -1,256 +1,28 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import { useCallback, useState } from "react";
|
|
4
4
|
import { useRouter } from "next/navigation";
|
|
5
|
-
import Link from "next/link";
|
|
6
|
-
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
|
7
|
-
import { Badge } from "@/components/ui/badge";
|
|
8
|
-
import { Button } from "@/components/ui/button";
|
|
9
|
-
import { Skeleton } from "@/components/ui/skeleton";
|
|
10
|
-
import {
|
|
11
|
-
CheckCircle,
|
|
12
|
-
Circle,
|
|
13
|
-
Loader2,
|
|
14
|
-
XCircle,
|
|
15
|
-
ShieldQuestion,
|
|
16
|
-
Play,
|
|
17
|
-
Pencil,
|
|
18
|
-
Copy,
|
|
19
|
-
RotateCcw,
|
|
20
|
-
Trash2,
|
|
21
|
-
Clock3,
|
|
22
|
-
GitBranch,
|
|
23
|
-
MessageSquareMore,
|
|
24
|
-
FileText,
|
|
25
|
-
Paperclip,
|
|
26
|
-
ArrowRight,
|
|
27
|
-
FolderKanban,
|
|
28
|
-
} from "lucide-react";
|
|
29
|
-
import { Checkbox } from "@/components/ui/checkbox";
|
|
30
|
-
import ReactMarkdown from "react-markdown";
|
|
31
|
-
import remarkGfm from "remark-gfm";
|
|
32
5
|
import { toast } from "sonner";
|
|
33
|
-
import { workflowStatusVariant, patternLabels } from "@/lib/constants/status-colors";
|
|
34
|
-
import { IconCircle, getWorkflowIconFromName } from "@/lib/constants/card-icons";
|
|
35
|
-
import { LoopStatusView } from "./loop-status-view";
|
|
36
6
|
import { ConfirmDialog } from "@/components/shared/confirm-dialog";
|
|
37
|
-
import {
|
|
38
|
-
import {
|
|
39
|
-
import {
|
|
40
|
-
import
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
taskId?: string;
|
|
52
|
-
result?: string;
|
|
53
|
-
error?: string;
|
|
54
|
-
};
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
interface DocumentInfo {
|
|
58
|
-
id: string;
|
|
59
|
-
originalName: string;
|
|
60
|
-
mimeType: string;
|
|
61
|
-
storagePath: string;
|
|
62
|
-
direction: string;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
interface WorkflowStatusData {
|
|
66
|
-
id: string;
|
|
67
|
-
name: string;
|
|
68
|
-
status: string;
|
|
69
|
-
pattern: string;
|
|
70
|
-
projectId?: string | null;
|
|
71
|
-
definition?: string;
|
|
72
|
-
steps: StepWithState[];
|
|
73
|
-
loopConfig?: LoopConfig;
|
|
74
|
-
loopState?: LoopState;
|
|
75
|
-
swarmConfig?: SwarmConfig;
|
|
76
|
-
stepDocuments?: Record<string, DocumentInfo[]>;
|
|
77
|
-
parentDocuments?: DocumentInfo[];
|
|
78
|
-
runNumber?: number;
|
|
79
|
-
runHistory?: Array<{
|
|
80
|
-
runNumber: number | null;
|
|
81
|
-
taskCount: number;
|
|
82
|
-
completedCount: number;
|
|
83
|
-
failedCount: number;
|
|
84
|
-
}>;
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
interface WorkflowStatusViewProps {
|
|
88
|
-
workflowId: string;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
const stepStatusIcons: Record<string, React.ReactNode> = {
|
|
92
|
-
pending: <Circle className="h-4 w-4 text-muted-foreground" />,
|
|
93
|
-
running: <Loader2 className="h-4 w-4 text-status-running animate-spin" />,
|
|
94
|
-
completed: <CheckCircle className="h-4 w-4 text-status-completed" />,
|
|
95
|
-
failed: <XCircle className="h-4 w-4 text-destructive" />,
|
|
96
|
-
waiting_approval: <ShieldQuestion className="h-4 w-4 text-status-warning" />,
|
|
97
|
-
waiting_dependencies: <Clock3 className="h-4 w-4 text-status-warning" />,
|
|
98
|
-
};
|
|
99
|
-
|
|
100
|
-
/** Expandable step result with gradient fade progressive disclosure */
|
|
101
|
-
export function ExpandableResult({ result }: { result: string }) {
|
|
102
|
-
const [expanded, setExpanded] = useState(false);
|
|
103
|
-
|
|
104
|
-
if (!result) return null;
|
|
105
|
-
|
|
106
|
-
return (
|
|
107
|
-
<div className="mt-2">
|
|
108
|
-
<div
|
|
109
|
-
className={`${PROSE_NOTIFICATION} ${
|
|
110
|
-
expanded
|
|
111
|
-
? "max-h-96 overflow-auto"
|
|
112
|
-
: result.length > 200
|
|
113
|
-
? "max-h-20 overflow-hidden mask-fade-bottom"
|
|
114
|
-
: ""
|
|
115
|
-
}`}
|
|
116
|
-
>
|
|
117
|
-
<ReactMarkdown remarkPlugins={[remarkGfm]}>{result}</ReactMarkdown>
|
|
118
|
-
</div>
|
|
119
|
-
{result.length > 200 && (
|
|
120
|
-
<button
|
|
121
|
-
type="button"
|
|
122
|
-
className="mt-1 text-xs text-muted-foreground hover:text-foreground transition-colors"
|
|
123
|
-
onClick={() => setExpanded(!expanded)}
|
|
124
|
-
>
|
|
125
|
-
{expanded ? "Show less" : "Show more"}
|
|
126
|
-
</button>
|
|
127
|
-
)}
|
|
128
|
-
</div>
|
|
129
|
-
);
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
/** Document list for a single step or parent task */
|
|
133
|
-
function DocumentList({ docs, label }: { docs: DocumentInfo[]; label: string }) {
|
|
134
|
-
if (docs.length === 0) return null;
|
|
135
|
-
|
|
136
|
-
return (
|
|
137
|
-
<div className="mt-3">
|
|
138
|
-
<p className="text-xs font-medium text-muted-foreground mb-1.5">{label}</p>
|
|
139
|
-
<div className="space-y-1">
|
|
140
|
-
{docs.map((doc) => (
|
|
141
|
-
<Link
|
|
142
|
-
key={doc.id}
|
|
143
|
-
href={`/documents/${doc.id}`}
|
|
144
|
-
className="flex items-center gap-2 text-xs text-brand-blue hover:underline"
|
|
145
|
-
>
|
|
146
|
-
<FileText className="h-3 w-3 shrink-0" />
|
|
147
|
-
{doc.originalName}
|
|
148
|
-
</Link>
|
|
149
|
-
))}
|
|
150
|
-
</div>
|
|
151
|
-
</div>
|
|
152
|
-
);
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
export function WorkflowStatusView({ workflowId }: WorkflowStatusViewProps) {
|
|
7
|
+
import { useWorkflowStatus } from "./hooks/use-workflow-status";
|
|
8
|
+
import { SequencePatternView } from "./views/sequence-pattern-view";
|
|
9
|
+
import { LoopPatternView } from "./views/loop-pattern-view";
|
|
10
|
+
import { WorkflowLoadingSkeleton } from "./shared/workflow-loading-skeleton";
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Thin router for the workflow detail page (TDR-031). Owns polling + the
|
|
14
|
+
* delete confirm dialog and dispatches to a pattern-specific subview. The
|
|
15
|
+
* if/else on `data.pattern` uses TypeScript's discriminated-union narrowing
|
|
16
|
+
* so each subview receives its own arm, with `.state` guaranteed present on
|
|
17
|
+
* the non-loop arm — which is why PR #6's `s.state?.result` optional chain
|
|
18
|
+
* is no longer needed and has been removed.
|
|
19
|
+
*/
|
|
20
|
+
export function WorkflowStatusView({ workflowId }: { workflowId: string }) {
|
|
157
21
|
const router = useRouter();
|
|
158
|
-
const
|
|
159
|
-
const [executing, setExecuting] = useState(false);
|
|
22
|
+
const { data, setData, refetch } = useWorkflowStatus(workflowId);
|
|
160
23
|
const [confirmDelete, setConfirmDelete] = useState(false);
|
|
161
24
|
|
|
162
|
-
const
|
|
163
|
-
const res = await fetch(`/api/workflows/${workflowId}/status`);
|
|
164
|
-
if (res.ok) setData(await res.json());
|
|
165
|
-
}, [workflowId]);
|
|
166
|
-
|
|
167
|
-
useEffect(() => {
|
|
168
|
-
fetchStatus();
|
|
169
|
-
const interval = setInterval(fetchStatus, 3000);
|
|
170
|
-
return () => clearInterval(interval);
|
|
171
|
-
}, [fetchStatus]);
|
|
172
|
-
|
|
173
|
-
async function startExecution() {
|
|
174
|
-
setExecuting(true);
|
|
175
|
-
// Optimistic update — immediately show "active" status
|
|
176
|
-
if (data) {
|
|
177
|
-
setData({
|
|
178
|
-
...data,
|
|
179
|
-
status: "active",
|
|
180
|
-
steps: data.steps.map((step, index) => {
|
|
181
|
-
if (data.pattern === "swarm") {
|
|
182
|
-
const lastIndex = data.steps.length - 1;
|
|
183
|
-
return {
|
|
184
|
-
...step,
|
|
185
|
-
state: {
|
|
186
|
-
...step.state,
|
|
187
|
-
status:
|
|
188
|
-
index === 0
|
|
189
|
-
? "running"
|
|
190
|
-
: index === lastIndex
|
|
191
|
-
? "waiting_dependencies"
|
|
192
|
-
: "pending",
|
|
193
|
-
},
|
|
194
|
-
};
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
if (data.pattern === "parallel") {
|
|
198
|
-
const isJoin = !!step.dependsOn?.length;
|
|
199
|
-
return {
|
|
200
|
-
...step,
|
|
201
|
-
state: {
|
|
202
|
-
...step.state,
|
|
203
|
-
status: isJoin
|
|
204
|
-
? "waiting_dependencies"
|
|
205
|
-
: index === 0
|
|
206
|
-
? "running"
|
|
207
|
-
: "pending",
|
|
208
|
-
},
|
|
209
|
-
};
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
return index === 0
|
|
213
|
-
? { ...step, state: { ...step.state, status: "running" } }
|
|
214
|
-
: step;
|
|
215
|
-
}),
|
|
216
|
-
});
|
|
217
|
-
}
|
|
218
|
-
try {
|
|
219
|
-
const res = await fetch(`/api/workflows/${workflowId}/execute`, {
|
|
220
|
-
method: "POST",
|
|
221
|
-
});
|
|
222
|
-
if (res.ok) {
|
|
223
|
-
toast.success("Workflow started");
|
|
224
|
-
fetchStatus();
|
|
225
|
-
} else {
|
|
226
|
-
const err = await res.json().catch(() => null);
|
|
227
|
-
toast.error(err?.error ?? "Failed to start workflow");
|
|
228
|
-
fetchStatus(); // Revert optimistic update on failure
|
|
229
|
-
}
|
|
230
|
-
} finally {
|
|
231
|
-
setExecuting(false);
|
|
232
|
-
}
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
async function handleRerun() {
|
|
236
|
-
setExecuting(true);
|
|
237
|
-
try {
|
|
238
|
-
const res = await fetch(`/api/workflows/${workflowId}/execute`, {
|
|
239
|
-
method: "POST",
|
|
240
|
-
});
|
|
241
|
-
if (res.ok) {
|
|
242
|
-
toast.success("Workflow re-started");
|
|
243
|
-
fetchStatus();
|
|
244
|
-
} else {
|
|
245
|
-
const err = await res.json().catch(() => null);
|
|
246
|
-
toast.error(err?.error ?? "Failed to re-run workflow");
|
|
247
|
-
}
|
|
248
|
-
} finally {
|
|
249
|
-
setExecuting(false);
|
|
250
|
-
}
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
async function handleDelete() {
|
|
25
|
+
const handleDelete = useCallback(async () => {
|
|
254
26
|
setConfirmDelete(false);
|
|
255
27
|
const res = await fetch(`/api/workflows/${workflowId}`, { method: "DELETE" });
|
|
256
28
|
if (res.ok) {
|
|
@@ -260,390 +32,24 @@ export function WorkflowStatusView({ workflowId }: WorkflowStatusViewProps) {
|
|
|
260
32
|
const err = await res.json().catch(() => null);
|
|
261
33
|
toast.error(err?.error ?? "Failed to delete workflow");
|
|
262
34
|
}
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
if (!data) {
|
|
266
|
-
return (
|
|
267
|
-
<Card>
|
|
268
|
-
<CardHeader>
|
|
269
|
-
<div className="flex items-center justify-between">
|
|
270
|
-
<div>
|
|
271
|
-
<Skeleton className="h-6 w-48 mb-2" />
|
|
272
|
-
<Skeleton className="h-4 w-24" />
|
|
273
|
-
</div>
|
|
274
|
-
<Skeleton className="h-6 w-16 rounded-full" />
|
|
275
|
-
</div>
|
|
276
|
-
</CardHeader>
|
|
277
|
-
<CardContent>
|
|
278
|
-
<div className="space-y-3">
|
|
279
|
-
{[1, 2, 3].map((i) => (
|
|
280
|
-
<div key={i} className="flex items-start gap-3">
|
|
281
|
-
<Skeleton className="h-4 w-4 rounded-full mt-0.5" />
|
|
282
|
-
<div className="flex-1">
|
|
283
|
-
<Skeleton className="h-4 w-32 mb-1" />
|
|
284
|
-
<Skeleton className="h-3 w-full" />
|
|
285
|
-
</div>
|
|
286
|
-
</div>
|
|
287
|
-
))}
|
|
288
|
-
</div>
|
|
289
|
-
</CardContent>
|
|
290
|
-
</Card>
|
|
291
|
-
);
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
const hasDefinition = !!data.definition;
|
|
295
|
-
const parallelBranches =
|
|
296
|
-
data.pattern === "parallel"
|
|
297
|
-
? data.steps.filter((step) => !step.dependsOn?.length)
|
|
298
|
-
: [];
|
|
299
|
-
const synthesisStep =
|
|
300
|
-
data.pattern === "parallel"
|
|
301
|
-
? data.steps.find((step) => step.dependsOn?.length) ?? null
|
|
302
|
-
: null;
|
|
35
|
+
}, [router, workflowId]);
|
|
303
36
|
|
|
304
|
-
|
|
305
|
-
const completedStepOutputs = data.steps
|
|
306
|
-
.filter((s) => s.state.result && s.state.status === "completed")
|
|
307
|
-
.map((s) => ({ name: s.name, result: s.state.result! }));
|
|
37
|
+
const onRequestDelete = useCallback(() => setConfirmDelete(true), []);
|
|
308
38
|
|
|
309
|
-
|
|
310
|
-
const hasStepDocs = data.stepDocuments && Object.keys(data.stepDocuments).length > 0;
|
|
311
|
-
const hasParentDocs = data.parentDocuments && data.parentDocuments.length > 0;
|
|
39
|
+
if (!data) return <WorkflowLoadingSkeleton />;
|
|
312
40
|
|
|
313
41
|
return (
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
<
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
<div>
|
|
324
|
-
<CardTitle>{data.name}</CardTitle>
|
|
325
|
-
<p className="text-sm text-muted-foreground mt-1">
|
|
326
|
-
{patternLabels[data.pattern] ?? data.pattern}
|
|
327
|
-
</p>
|
|
328
|
-
<div className="flex items-center gap-2 mt-1">
|
|
329
|
-
{data.projectId && (
|
|
330
|
-
<Badge
|
|
331
|
-
variant="outline"
|
|
332
|
-
className="text-xs cursor-pointer hover:bg-accent gap-1"
|
|
333
|
-
onClick={() => router.push(`/projects/${data.projectId}`)}
|
|
334
|
-
>
|
|
335
|
-
<FolderKanban className="h-3 w-3" />
|
|
336
|
-
Project
|
|
337
|
-
</Badge>
|
|
338
|
-
)}
|
|
339
|
-
{data.runNumber != null && data.runNumber > 0 && (
|
|
340
|
-
<Badge variant="outline" className="text-xs font-normal">
|
|
341
|
-
Run #{data.runNumber}
|
|
342
|
-
</Badge>
|
|
343
|
-
)}
|
|
344
|
-
</div>
|
|
345
|
-
</div>
|
|
346
|
-
</div>
|
|
347
|
-
<div className="flex items-center gap-2">
|
|
348
|
-
<Badge variant={workflowStatusVariant[data.status] ?? "secondary"}>
|
|
349
|
-
{data.status}
|
|
350
|
-
</Badge>
|
|
351
|
-
|
|
352
|
-
{/* Execute button for draft/paused non-loop workflows */}
|
|
353
|
-
{data.pattern !== "loop" && (data.status === "draft" || data.status === "paused") && (
|
|
354
|
-
<Button size="sm" onClick={startExecution} disabled={executing}>
|
|
355
|
-
<Play className="h-3 w-3 mr-1" />
|
|
356
|
-
{executing ? "Starting..." : "Execute"}
|
|
357
|
-
</Button>
|
|
358
|
-
)}
|
|
359
|
-
|
|
360
|
-
{/* Edit — draft, completed, or failed */}
|
|
361
|
-
{["draft", "completed", "failed"].includes(data.status) && hasDefinition && (
|
|
362
|
-
<Button
|
|
363
|
-
variant="outline"
|
|
364
|
-
size="sm"
|
|
365
|
-
onClick={() => router.push(`/workflows/${workflowId}/edit`)}
|
|
366
|
-
>
|
|
367
|
-
<Pencil className="h-3.5 w-3.5 mr-1.5" />
|
|
368
|
-
Edit
|
|
369
|
-
</Button>
|
|
370
|
-
)}
|
|
371
|
-
|
|
372
|
-
{/* Clone — always available */}
|
|
373
|
-
{hasDefinition && (
|
|
374
|
-
<Button
|
|
375
|
-
variant="outline"
|
|
376
|
-
size="sm"
|
|
377
|
-
onClick={() => router.push(`/workflows/${workflowId}/edit?clone=true`)}
|
|
378
|
-
>
|
|
379
|
-
<Copy className="h-3.5 w-3.5 mr-1.5" />
|
|
380
|
-
Clone
|
|
381
|
-
</Button>
|
|
382
|
-
)}
|
|
383
|
-
|
|
384
|
-
{/* Re-run — completed/failed only */}
|
|
385
|
-
{(data.status === "completed" || data.status === "failed") && (
|
|
386
|
-
<Button
|
|
387
|
-
variant="outline"
|
|
388
|
-
size="sm"
|
|
389
|
-
onClick={handleRerun}
|
|
390
|
-
disabled={executing}
|
|
391
|
-
>
|
|
392
|
-
<RotateCcw className="h-3.5 w-3.5 mr-1.5" />
|
|
393
|
-
Re-run
|
|
394
|
-
</Button>
|
|
395
|
-
)}
|
|
396
|
-
|
|
397
|
-
{/* Delete — not active */}
|
|
398
|
-
{data.status !== "active" && (
|
|
399
|
-
<Button
|
|
400
|
-
variant="outline"
|
|
401
|
-
size="sm"
|
|
402
|
-
className="text-destructive hover:text-destructive"
|
|
403
|
-
onClick={() => setConfirmDelete(true)}
|
|
404
|
-
>
|
|
405
|
-
<Trash2 className="h-3.5 w-3.5 mr-1.5" />
|
|
406
|
-
Delete
|
|
407
|
-
</Button>
|
|
408
|
-
)}
|
|
409
|
-
</div>
|
|
410
|
-
</div>
|
|
411
|
-
</CardHeader>
|
|
412
|
-
<CardContent>
|
|
413
|
-
{/* Loop prompt display */}
|
|
414
|
-
{data.pattern === "loop" && data.steps[0]?.prompt && (
|
|
415
|
-
<div className="mb-4">
|
|
416
|
-
<p className="text-xs font-medium text-muted-foreground mb-1.5">Loop Prompt</p>
|
|
417
|
-
<div className="rounded-md border bg-muted/50 p-3">
|
|
418
|
-
<p className="text-sm whitespace-pre-wrap">{data.steps[0].prompt}</p>
|
|
419
|
-
</div>
|
|
420
|
-
</div>
|
|
421
|
-
)}
|
|
422
|
-
|
|
423
|
-
{data.pattern === "loop" && data.loopConfig ? (
|
|
424
|
-
<LoopStatusView
|
|
425
|
-
workflowId={workflowId}
|
|
426
|
-
workflowStatus={data.status}
|
|
427
|
-
loopConfig={data.loopConfig}
|
|
428
|
-
loopState={data.loopState ?? null}
|
|
429
|
-
onRefresh={fetchStatus}
|
|
430
|
-
/>
|
|
431
|
-
) : data.pattern === "swarm" ? (
|
|
432
|
-
<SwarmDashboard
|
|
433
|
-
workflowId={workflowId}
|
|
434
|
-
workflowStatus={data.status}
|
|
435
|
-
steps={data.steps}
|
|
436
|
-
swarmConfig={data.swarmConfig}
|
|
437
|
-
onRefresh={fetchStatus}
|
|
438
|
-
stepStatusIcons={stepStatusIcons}
|
|
439
|
-
/>
|
|
440
|
-
) : (
|
|
441
|
-
<div className="space-y-4" aria-live="polite">
|
|
442
|
-
{data.pattern === "parallel" && parallelBranches.length > 0 ? (
|
|
443
|
-
<>
|
|
444
|
-
<section className="space-y-3">
|
|
445
|
-
<div className="flex items-center gap-2">
|
|
446
|
-
<GitBranch className="h-4 w-4 text-muted-foreground" />
|
|
447
|
-
<p className="text-sm font-medium">Parallel Branches</p>
|
|
448
|
-
<Badge variant="secondary" className="text-xs">
|
|
449
|
-
{parallelBranches.length}
|
|
450
|
-
</Badge>
|
|
451
|
-
</div>
|
|
452
|
-
<div className="grid gap-3 md:grid-cols-2">
|
|
453
|
-
{parallelBranches.map((step, index) => (
|
|
454
|
-
<div
|
|
455
|
-
key={step.id}
|
|
456
|
-
className="surface-card-muted rounded-lg border border-border/50 p-4"
|
|
457
|
-
>
|
|
458
|
-
<div className="flex items-start gap-3">
|
|
459
|
-
<div className="mt-0.5">
|
|
460
|
-
{stepStatusIcons[step.state.status] ?? stepStatusIcons.pending}
|
|
461
|
-
</div>
|
|
462
|
-
<div className="min-w-0 flex-1">
|
|
463
|
-
<div className="flex items-center gap-2">
|
|
464
|
-
<Badge variant="secondary" className="text-[11px]">
|
|
465
|
-
Branch {index + 1}
|
|
466
|
-
</Badge>
|
|
467
|
-
<span className="text-sm font-medium">{step.name}</span>
|
|
468
|
-
</div>
|
|
469
|
-
<p className="mt-1 text-xs text-muted-foreground line-clamp-2">
|
|
470
|
-
{step.prompt}
|
|
471
|
-
</p>
|
|
472
|
-
{step.state.error && (
|
|
473
|
-
<p className="mt-2 text-xs text-destructive">
|
|
474
|
-
{step.state.error}
|
|
475
|
-
</p>
|
|
476
|
-
)}
|
|
477
|
-
{step.state.result && step.state.status === "completed" && (
|
|
478
|
-
<ExpandableResult result={step.state.result} />
|
|
479
|
-
)}
|
|
480
|
-
{/* Step output documents */}
|
|
481
|
-
{step.state.taskId && data.stepDocuments?.[step.state.taskId] && (
|
|
482
|
-
<DocumentList
|
|
483
|
-
docs={data.stepDocuments[step.state.taskId]}
|
|
484
|
-
label="Generated Files"
|
|
485
|
-
/>
|
|
486
|
-
)}
|
|
487
|
-
</div>
|
|
488
|
-
</div>
|
|
489
|
-
</div>
|
|
490
|
-
))}
|
|
491
|
-
</div>
|
|
492
|
-
</section>
|
|
493
|
-
|
|
494
|
-
{synthesisStep && (
|
|
495
|
-
<section className="space-y-3">
|
|
496
|
-
<div className="flex items-center gap-2">
|
|
497
|
-
<MessageSquareMore className="h-4 w-4 text-muted-foreground" />
|
|
498
|
-
<p className="text-sm font-medium">Synthesis Step</p>
|
|
499
|
-
</div>
|
|
500
|
-
<div className="surface-card-muted rounded-lg border border-border/50 p-4">
|
|
501
|
-
<div className="flex items-start gap-3">
|
|
502
|
-
<div className="mt-0.5">
|
|
503
|
-
{stepStatusIcons[synthesisStep.state.status] ??
|
|
504
|
-
stepStatusIcons.pending}
|
|
505
|
-
</div>
|
|
506
|
-
<div className="min-w-0 flex-1">
|
|
507
|
-
<div className="flex items-center gap-2">
|
|
508
|
-
<Badge variant="outline" className="text-[11px]">
|
|
509
|
-
join
|
|
510
|
-
</Badge>
|
|
511
|
-
<span className="text-sm font-medium">
|
|
512
|
-
{synthesisStep.name}
|
|
513
|
-
</span>
|
|
514
|
-
</div>
|
|
515
|
-
<p className="mt-1 text-xs text-muted-foreground line-clamp-2">
|
|
516
|
-
{synthesisStep.prompt}
|
|
517
|
-
</p>
|
|
518
|
-
<p className="mt-2 text-xs text-muted-foreground">
|
|
519
|
-
Waits for all {parallelBranches.length} branches before
|
|
520
|
-
running.
|
|
521
|
-
</p>
|
|
522
|
-
{synthesisStep.state.error && (
|
|
523
|
-
<p className="mt-2 text-xs text-destructive">
|
|
524
|
-
{synthesisStep.state.error}
|
|
525
|
-
</p>
|
|
526
|
-
)}
|
|
527
|
-
{synthesisStep.state.result &&
|
|
528
|
-
synthesisStep.state.status === "completed" && (
|
|
529
|
-
<ExpandableResult result={synthesisStep.state.result} />
|
|
530
|
-
)}
|
|
531
|
-
{/* Synthesis step documents */}
|
|
532
|
-
{synthesisStep.state.taskId && data.stepDocuments?.[synthesisStep.state.taskId] && (
|
|
533
|
-
<DocumentList
|
|
534
|
-
docs={data.stepDocuments[synthesisStep.state.taskId]}
|
|
535
|
-
label="Generated Files"
|
|
536
|
-
/>
|
|
537
|
-
)}
|
|
538
|
-
</div>
|
|
539
|
-
</div>
|
|
540
|
-
</div>
|
|
541
|
-
</section>
|
|
542
|
-
)}
|
|
543
|
-
</>
|
|
544
|
-
) : (
|
|
545
|
-
<div className="space-y-3">
|
|
546
|
-
{data.steps.map((step, index) => (
|
|
547
|
-
<div key={`${step.id}-${index}`} className="flex items-start gap-3">
|
|
548
|
-
<div className="mt-0.5 flex flex-col items-center">
|
|
549
|
-
{stepStatusIcons[step.state.status] ?? stepStatusIcons.pending}
|
|
550
|
-
{index < data.steps.length - 1 && (
|
|
551
|
-
<div className="mt-1 h-6 w-px bg-border" />
|
|
552
|
-
)}
|
|
553
|
-
</div>
|
|
554
|
-
<div className="flex-1 min-w-0">
|
|
555
|
-
<div className="flex items-center gap-2">
|
|
556
|
-
<span className="text-sm font-medium">{step.name}</span>
|
|
557
|
-
{step.requiresApproval && (
|
|
558
|
-
<Badge variant="outline" className="text-xs">
|
|
559
|
-
checkpoint
|
|
560
|
-
</Badge>
|
|
561
|
-
)}
|
|
562
|
-
</div>
|
|
563
|
-
<div className="flex items-center gap-1.5 mt-0.5">
|
|
564
|
-
<p className="text-xs text-muted-foreground truncate">
|
|
565
|
-
{step.prompt.slice(0, 100)}
|
|
566
|
-
{step.prompt.length > 100 ? "..." : ""}
|
|
567
|
-
</p>
|
|
568
|
-
{step.state.taskId && (
|
|
569
|
-
<a
|
|
570
|
-
href={`/tasks/${step.state.taskId}`}
|
|
571
|
-
className="text-[10px] text-primary hover:underline shrink-0"
|
|
572
|
-
>
|
|
573
|
-
view task
|
|
574
|
-
</a>
|
|
575
|
-
)}
|
|
576
|
-
</div>
|
|
577
|
-
{/* Parent document context indicator */}
|
|
578
|
-
{data.parentDocuments && data.parentDocuments.length > 0 && index === 0 && (
|
|
579
|
-
<div className="flex items-center gap-1 mt-1">
|
|
580
|
-
<Badge variant="outline" className="text-[10px] px-1.5 py-0">
|
|
581
|
-
{data.parentDocuments.length} doc{data.parentDocuments.length !== 1 ? "s" : ""} attached
|
|
582
|
-
</Badge>
|
|
583
|
-
</div>
|
|
584
|
-
)}
|
|
585
|
-
{step.state.error && (
|
|
586
|
-
<p className="text-xs text-destructive mt-1">
|
|
587
|
-
{step.state.error}
|
|
588
|
-
</p>
|
|
589
|
-
)}
|
|
590
|
-
{step.state.result && step.state.status === "completed" && (
|
|
591
|
-
<ExpandableResult result={step.state.result} />
|
|
592
|
-
)}
|
|
593
|
-
{/* Step output documents */}
|
|
594
|
-
{step.state.taskId && data.stepDocuments?.[step.state.taskId] && (
|
|
595
|
-
<DocumentList
|
|
596
|
-
docs={data.stepDocuments[step.state.taskId]}
|
|
597
|
-
label="Generated Files"
|
|
598
|
-
/>
|
|
599
|
-
)}
|
|
600
|
-
</div>
|
|
601
|
-
</div>
|
|
602
|
-
))}
|
|
603
|
-
</div>
|
|
604
|
-
)}
|
|
605
|
-
</div>
|
|
606
|
-
)}
|
|
607
|
-
|
|
608
|
-
{/* Documents section — parent inputs and step outputs */}
|
|
609
|
-
{(hasParentDocs || hasStepDocs) && (
|
|
610
|
-
<div className="mt-6 pt-4 border-t border-border/50">
|
|
611
|
-
<div className="flex items-center gap-2 mb-3">
|
|
612
|
-
<Paperclip className="h-4 w-4 text-muted-foreground" />
|
|
613
|
-
<p className="text-sm font-medium">Documents</p>
|
|
614
|
-
</div>
|
|
615
|
-
{hasParentDocs && (
|
|
616
|
-
<DocumentList docs={data.parentDocuments!} label="Input Files" />
|
|
617
|
-
)}
|
|
618
|
-
{hasStepDocs && Object.entries(data.stepDocuments!).map(([taskId, docs]) => {
|
|
619
|
-
const step = data.steps.find((s) => s.state.taskId === taskId);
|
|
620
|
-
return (
|
|
621
|
-
<DocumentList
|
|
622
|
-
key={taskId}
|
|
623
|
-
docs={docs}
|
|
624
|
-
label={step ? `Output: ${step.name}` : "Output Files"}
|
|
625
|
-
/>
|
|
626
|
-
);
|
|
627
|
-
})}
|
|
628
|
-
</div>
|
|
629
|
-
)}
|
|
630
|
-
</CardContent>
|
|
631
|
-
</Card>
|
|
632
|
-
|
|
633
|
-
{/* Full output — inline below workflow card when completed */}
|
|
634
|
-
{data.status === "completed" && completedStepOutputs.length > 0 && (
|
|
635
|
-
<WorkflowFullOutput
|
|
636
|
-
workflowName={data.name}
|
|
637
|
-
steps={completedStepOutputs}
|
|
42
|
+
<>
|
|
43
|
+
{data.pattern === "loop" ? (
|
|
44
|
+
<LoopPatternView data={data} onRefresh={refetch} onRequestDelete={onRequestDelete} />
|
|
45
|
+
) : (
|
|
46
|
+
<SequencePatternView
|
|
47
|
+
data={data}
|
|
48
|
+
setData={setData}
|
|
49
|
+
onRefresh={refetch}
|
|
50
|
+
onRequestDelete={onRequestDelete}
|
|
638
51
|
/>
|
|
639
52
|
)}
|
|
640
|
-
|
|
641
|
-
{/* Output Dock — chain into new workflow */}
|
|
642
|
-
{data.status === "completed" && hasStepDocs && (
|
|
643
|
-
<OutputDock stepDocuments={data.stepDocuments!} steps={data.steps} />
|
|
644
|
-
)}
|
|
645
|
-
|
|
646
|
-
{/* Delete confirmation */}
|
|
647
53
|
<ConfirmDialog
|
|
648
54
|
open={confirmDelete}
|
|
649
55
|
onOpenChange={setConfirmDelete}
|
|
@@ -653,122 +59,6 @@ export function WorkflowStatusView({ workflowId }: WorkflowStatusViewProps) {
|
|
|
653
59
|
onConfirm={handleDelete}
|
|
654
60
|
destructive
|
|
655
61
|
/>
|
|
656
|
-
|
|
657
|
-
);
|
|
658
|
-
}
|
|
659
|
-
|
|
660
|
-
/** Output Dock — selectable output documents for chaining into a new workflow */
|
|
661
|
-
function OutputDock({
|
|
662
|
-
stepDocuments,
|
|
663
|
-
steps,
|
|
664
|
-
}: {
|
|
665
|
-
stepDocuments: Record<string, DocumentInfo[]>;
|
|
666
|
-
steps: StepWithState[];
|
|
667
|
-
}) {
|
|
668
|
-
const router = useRouter();
|
|
669
|
-
const [selectedIds, setSelectedIds] = useState<Set<string>>(new Set());
|
|
670
|
-
|
|
671
|
-
// Flatten all output documents
|
|
672
|
-
const allOutputDocs = Object.entries(stepDocuments).flatMap(
|
|
673
|
-
([taskId, docs]) => {
|
|
674
|
-
const step = steps.find((s) => s.state.taskId === taskId);
|
|
675
|
-
return docs.map((doc) => ({
|
|
676
|
-
...doc,
|
|
677
|
-
stepName: step?.name ?? "Unknown Step",
|
|
678
|
-
}));
|
|
679
|
-
}
|
|
680
|
-
);
|
|
681
|
-
|
|
682
|
-
if (allOutputDocs.length === 0) return null;
|
|
683
|
-
|
|
684
|
-
function toggleDoc(id: string) {
|
|
685
|
-
setSelectedIds((prev) => {
|
|
686
|
-
const next = new Set(prev);
|
|
687
|
-
if (next.has(id)) next.delete(id);
|
|
688
|
-
else next.add(id);
|
|
689
|
-
return next;
|
|
690
|
-
});
|
|
691
|
-
}
|
|
692
|
-
|
|
693
|
-
function selectAll() {
|
|
694
|
-
setSelectedIds(new Set(allOutputDocs.map((d) => d.id)));
|
|
695
|
-
}
|
|
696
|
-
|
|
697
|
-
function chainIntoNewWorkflow() {
|
|
698
|
-
if (selectedIds.size === 0) return;
|
|
699
|
-
const params = new URLSearchParams({
|
|
700
|
-
inputDocs: [...selectedIds].join(","),
|
|
701
|
-
});
|
|
702
|
-
router.push(`/workflows/new?${params}`);
|
|
703
|
-
}
|
|
704
|
-
|
|
705
|
-
return (
|
|
706
|
-
<Card>
|
|
707
|
-
<CardHeader className="pb-3">
|
|
708
|
-
<div className="flex items-center justify-between">
|
|
709
|
-
<CardTitle className="text-sm font-medium flex items-center gap-2">
|
|
710
|
-
<ArrowRight className="h-4 w-4" />
|
|
711
|
-
Chain Output Documents
|
|
712
|
-
</CardTitle>
|
|
713
|
-
<Button
|
|
714
|
-
variant="ghost"
|
|
715
|
-
size="sm"
|
|
716
|
-
onClick={selectAll}
|
|
717
|
-
className="text-xs"
|
|
718
|
-
>
|
|
719
|
-
Select All
|
|
720
|
-
</Button>
|
|
721
|
-
</div>
|
|
722
|
-
<p className="text-xs text-muted-foreground">
|
|
723
|
-
Select output documents to use as inputs in a new workflow
|
|
724
|
-
</p>
|
|
725
|
-
</CardHeader>
|
|
726
|
-
<CardContent>
|
|
727
|
-
<div className="grid grid-cols-1 sm:grid-cols-2 gap-2 mb-4">
|
|
728
|
-
{allOutputDocs.map((doc) => {
|
|
729
|
-
const isChecked = selectedIds.has(doc.id);
|
|
730
|
-
return (
|
|
731
|
-
<div
|
|
732
|
-
key={doc.id}
|
|
733
|
-
role="button"
|
|
734
|
-
tabIndex={0}
|
|
735
|
-
onClick={() => toggleDoc(doc.id)}
|
|
736
|
-
onKeyDown={(e) => { if (e.key === "Enter" || e.key === " ") { e.preventDefault(); toggleDoc(doc.id); } }}
|
|
737
|
-
className={`flex items-center gap-3 p-3 rounded-lg text-left transition-colors border cursor-pointer ${
|
|
738
|
-
isChecked
|
|
739
|
-
? "bg-accent/50 border-accent"
|
|
740
|
-
: "hover:bg-muted/50 border-border/50"
|
|
741
|
-
}`}
|
|
742
|
-
>
|
|
743
|
-
<Checkbox
|
|
744
|
-
checked={isChecked}
|
|
745
|
-
onCheckedChange={() => toggleDoc(doc.id)}
|
|
746
|
-
/>
|
|
747
|
-
<FileText className="h-4 w-4 flex-shrink-0 text-muted-foreground" />
|
|
748
|
-
<div className="flex-1 min-w-0">
|
|
749
|
-
<p className="text-sm font-medium truncate">
|
|
750
|
-
{doc.originalName}
|
|
751
|
-
</p>
|
|
752
|
-
<p className="text-xs text-muted-foreground truncate">
|
|
753
|
-
{doc.stepName}
|
|
754
|
-
</p>
|
|
755
|
-
</div>
|
|
756
|
-
</div>
|
|
757
|
-
);
|
|
758
|
-
})}
|
|
759
|
-
</div>
|
|
760
|
-
|
|
761
|
-
{selectedIds.size > 0 && (
|
|
762
|
-
<Button
|
|
763
|
-
onClick={chainIntoNewWorkflow}
|
|
764
|
-
className="w-full gap-2"
|
|
765
|
-
size="sm"
|
|
766
|
-
>
|
|
767
|
-
<ArrowRight className="h-4 w-4" />
|
|
768
|
-
Chain {selectedIds.size} Document{selectedIds.size !== 1 ? "s" : ""} Into New Workflow
|
|
769
|
-
</Button>
|
|
770
|
-
)}
|
|
771
|
-
</CardContent>
|
|
772
|
-
</Card>
|
|
62
|
+
</>
|
|
773
63
|
);
|
|
774
64
|
}
|