stagent 0.9.6 → 0.11.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 +20 -44
- package/dist/cli.js +66 -18
- package/docs/.coverage-gaps.json +144 -56
- package/docs/.last-generated +1 -1
- package/docs/features/agent-intelligence.md +12 -2
- package/docs/features/chat.md +40 -5
- package/docs/features/cost-usage.md +1 -1
- package/docs/features/documents.md +5 -2
- package/docs/features/inbox-notifications.md +10 -2
- package/docs/features/keyboard-navigation.md +12 -3
- package/docs/features/provider-runtimes.md +20 -2
- package/docs/features/schedules.md +32 -4
- package/docs/features/settings.md +28 -5
- package/docs/features/shared-components.md +7 -3
- package/docs/features/tables.md +11 -2
- package/docs/features/tool-permissions.md +6 -2
- package/docs/features/workflows.md +14 -4
- package/docs/index.md +1 -1
- package/docs/journeys/developer.md +39 -2
- package/docs/journeys/personal-use.md +32 -8
- package/docs/journeys/power-user.md +45 -14
- package/docs/journeys/work-use.md +17 -8
- package/docs/manifest.json +15 -15
- 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/plans/2026-04-14-chat-command-namespace-refactor.md +1390 -0
- package/docs/superpowers/plans/2026-04-14-chat-environment-integration.md +1561 -0
- package/docs/superpowers/plans/2026-04-14-chat-polish-bundle-v1.md +1219 -0
- package/docs/superpowers/plans/2026-04-14-chat-session-persistence-provider-closeout.md +399 -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/next.config.mjs +1 -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/chat/conversations/[id]/skills/__tests__/activate.test.ts +141 -0
- package/src/app/api/chat/conversations/[id]/skills/activate/route.ts +74 -0
- package/src/app/api/chat/conversations/[id]/skills/deactivate/route.ts +33 -0
- package/src/app/api/chat/export/route.ts +52 -0
- package/src/app/api/chat/files/search/route.ts +50 -0
- package/src/app/api/diagnostics/chat-streams/route.ts +65 -0
- package/src/app/api/environment/rescan-if-stale/__tests__/route.test.ts +45 -0
- package/src/app/api/environment/rescan-if-stale/route.ts +23 -0
- package/src/app/api/environment/skills/route.ts +13 -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/chat/pins/route.ts +94 -0
- package/src/app/api/settings/chat/saved-searches/__tests__/route.test.ts +119 -0
- package/src/app/api/settings/chat/saved-searches/route.ts +79 -0
- package/src/app/api/settings/environment/route.ts +26 -0
- 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 +52 -33
- package/src/app/api/tasks/[id]/respond/route.ts +31 -15
- package/src/app/api/tasks/[id]/resume/route.ts +24 -3
- 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/documents/page.tsx +4 -1
- 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 +8 -9
- package/src/components/chat/__tests__/capability-banner.test.tsx +38 -0
- package/src/components/chat/__tests__/chat-session-provider.test.tsx +573 -0
- package/src/components/chat/__tests__/skill-row.test.tsx +91 -0
- package/src/components/chat/capability-banner.tsx +68 -0
- package/src/components/chat/chat-command-popover.tsx +670 -49
- package/src/components/chat/chat-input.tsx +104 -10
- package/src/components/chat/chat-message.tsx +12 -3
- package/src/components/chat/chat-session-provider.tsx +790 -0
- package/src/components/chat/chat-shell.tsx +151 -401
- package/src/components/chat/command-tab-bar.tsx +68 -0
- package/src/components/chat/conversation-template-picker.tsx +421 -0
- package/src/components/chat/help-dialog.tsx +39 -0
- package/src/components/chat/skill-composition-conflict-dialog.tsx +96 -0
- package/src/components/chat/skill-row.tsx +147 -0
- package/src/components/documents/document-browser.tsx +37 -19
- 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/__tests__/permission-response-actions.test.tsx +70 -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/notifications/permission-response-actions.tsx +155 -1
- 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/environment-section.tsx +102 -0
- 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/__tests__/filter-hint.test.tsx +40 -0
- package/src/components/shared/__tests__/saved-searches-manager.test.tsx +147 -0
- package/src/components/shared/app-sidebar.tsx +4 -3
- package/src/components/shared/command-palette.tsx +266 -7
- package/src/components/shared/filter-hint.tsx +70 -0
- package/src/components/shared/filter-input.tsx +59 -0
- package/src/components/shared/saved-searches-manager.tsx +199 -0
- 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 +31 -2
- package/src/components/tasks/task-card.tsx +29 -3
- package/src/components/tasks/task-chip-bar.tsx +54 -1
- 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/hooks/__tests__/use-chat-autocomplete-tabs.test.ts +47 -0
- package/src/hooks/__tests__/use-saved-searches.test.ts +70 -0
- package/src/hooks/use-active-skills.ts +110 -0
- package/src/hooks/use-chat-autocomplete.ts +120 -7
- package/src/hooks/use-enriched-skills.ts +19 -0
- package/src/hooks/use-pinned-entries.ts +104 -0
- package/src/hooks/use-recent-user-messages.ts +19 -0
- package/src/hooks/use-saved-searches.ts +142 -0
- package/src/instrumentation-node.ts +94 -0
- package/src/instrumentation.ts +4 -48
- package/src/lib/agents/__tests__/claude-agent-sdk-options.test.ts +56 -0
- package/src/lib/agents/__tests__/claude-agent.test.ts +212 -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/__tests__/task-dispatch.test.ts +166 -0
- package/src/lib/agents/__tests__/tool-permissions.test.ts +60 -0
- package/src/lib/agents/claude-agent.ts +217 -21
- package/src/lib/agents/execution-manager.ts +0 -35
- package/src/lib/agents/handoff/bus.ts +2 -2
- package/src/lib/agents/learned-context.ts +0 -12
- package/src/lib/agents/learning-session.ts +18 -5
- package/src/lib/agents/profiles/__tests__/list-fused-profiles.test.ts +110 -0
- package/src/lib/agents/profiles/__tests__/registry.test.ts +53 -4
- package/src/lib/agents/profiles/builtins/upgrade-assistant/SKILL.md +97 -0
- package/src/lib/agents/profiles/builtins/upgrade-assistant/profile.yaml +36 -0
- package/src/lib/agents/profiles/list-fused-profiles.ts +104 -0
- package/src/lib/agents/profiles/registry.ts +18 -0
- package/src/lib/agents/profiles/types.ts +7 -1
- package/src/lib/agents/router.ts +3 -6
- package/src/lib/agents/runtime/__tests__/catalog.test.ts +130 -0
- package/src/lib/agents/runtime/__tests__/execution-target.test.ts +183 -0
- package/src/lib/agents/runtime/__tests__/openai-codex-auth.test.ts +118 -0
- package/src/lib/agents/runtime/anthropic-direct.ts +8 -0
- package/src/lib/agents/runtime/catalog.ts +121 -0
- package/src/lib/agents/runtime/claude-sdk.ts +32 -0
- package/src/lib/agents/runtime/codex-app-server-client.ts +11 -5
- package/src/lib/agents/runtime/execution-target.ts +456 -0
- package/src/lib/agents/runtime/index.ts +4 -0
- package/src/lib/agents/runtime/launch-failure.ts +101 -0
- package/src/lib/agents/runtime/openai-codex-auth.ts +389 -0
- package/src/lib/agents/runtime/openai-codex.ts +64 -60
- package/src/lib/agents/runtime/openai-direct.ts +8 -0
- package/src/lib/agents/runtime/types.ts +8 -0
- package/src/lib/agents/task-dispatch.ts +220 -0
- package/src/lib/agents/tool-permissions.ts +16 -1
- package/src/lib/book/chapter-mapping.ts +11 -0
- package/src/lib/book/content.ts +10 -0
- package/src/lib/chat/__tests__/active-skill-injection.test.ts +261 -0
- package/src/lib/chat/__tests__/active-streams.test.ts +49 -0
- package/src/lib/chat/__tests__/clean-filter-input.test.ts +68 -0
- package/src/lib/chat/__tests__/command-tabs.test.ts +68 -0
- package/src/lib/chat/__tests__/context-builder-files.test.ts +112 -0
- package/src/lib/chat/__tests__/dismissals.test.ts +65 -0
- package/src/lib/chat/__tests__/engine-sdk-options.test.ts +117 -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__/skill-conflict.test.ts +35 -0
- package/src/lib/chat/__tests__/stream-telemetry.test.ts +151 -0
- package/src/lib/chat/__tests__/types.test.ts +28 -0
- package/src/lib/chat/active-skills.ts +31 -0
- package/src/lib/chat/active-streams.ts +27 -0
- package/src/lib/chat/clean-filter-input.ts +30 -0
- package/src/lib/chat/codex-engine.ts +46 -24
- package/src/lib/chat/command-tabs.ts +61 -0
- package/src/lib/chat/context-builder.ts +146 -4
- package/src/lib/chat/dismissals.ts +73 -0
- package/src/lib/chat/engine.ts +159 -18
- package/src/lib/chat/files/__tests__/search.test.ts +135 -0
- package/src/lib/chat/files/expand-mention.ts +76 -0
- package/src/lib/chat/files/search.ts +99 -0
- package/src/lib/chat/reconcile.ts +117 -0
- package/src/lib/chat/skill-composition.ts +210 -0
- package/src/lib/chat/skill-conflict.ts +105 -0
- package/src/lib/chat/stagent-tools.ts +7 -19
- package/src/lib/chat/stream-telemetry.ts +137 -0
- package/src/lib/chat/suggested-prompts.ts +28 -1
- package/src/lib/chat/system-prompt.ts +48 -1
- package/src/lib/chat/tool-catalog.ts +35 -4
- package/src/lib/chat/tools/__tests__/enrich-table-tool.test.ts +127 -0
- package/src/lib/chat/tools/__tests__/profile-tools.test.ts +51 -0
- package/src/lib/chat/tools/__tests__/schedule-tools.test.ts +261 -0
- package/src/lib/chat/tools/__tests__/settings-tools.test.ts +294 -0
- package/src/lib/chat/tools/__tests__/skill-tools.test.ts +474 -0
- package/src/lib/chat/tools/__tests__/task-tools.test.ts +399 -0
- package/src/lib/chat/tools/__tests__/workflow-tools-dedup.test.ts +351 -0
- package/src/lib/chat/tools/blueprint-tools.ts +190 -0
- package/src/lib/chat/tools/document-tools.ts +29 -13
- package/src/lib/chat/tools/helpers.ts +41 -0
- package/src/lib/chat/tools/notification-tools.ts +9 -5
- package/src/lib/chat/tools/profile-tools.ts +120 -23
- 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/skill-tools.ts +183 -0
- package/src/lib/chat/tools/table-tools.ts +71 -0
- package/src/lib/chat/tools/task-tools.ts +89 -21
- package/src/lib/chat/tools/workflow-tools.ts +275 -32
- package/src/lib/chat/types.ts +15 -0
- package/src/lib/constants/settings.ts +10 -18
- package/src/lib/data/__tests__/clear.test.ts +56 -2
- package/src/lib/data/clear.ts +17 -16
- 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 +62 -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 +94 -23
- package/src/lib/environment/__tests__/auto-promote.test.ts +132 -0
- package/src/lib/environment/__tests__/list-skills-enriched.test.ts +55 -0
- package/src/lib/environment/__tests__/skill-enrichment.test.ts +129 -0
- package/src/lib/environment/__tests__/skill-recommendations.test.ts +87 -0
- package/src/lib/environment/data.ts +9 -0
- package/src/lib/environment/list-skills.ts +176 -0
- package/src/lib/environment/parsers/__tests__/skill.test.ts +54 -0
- package/src/lib/environment/parsers/skill.ts +26 -5
- package/src/lib/environment/profile-generator.ts +54 -0
- package/src/lib/environment/skill-enrichment.ts +106 -0
- package/src/lib/environment/skill-recommendations.ts +66 -0
- package/src/lib/environment/workspace-context.ts +13 -1
- package/src/lib/filters/__tests__/parse.quoted.test.ts +40 -0
- package/src/lib/filters/__tests__/parse.test.ts +135 -0
- package/src/lib/filters/parse.ts +86 -0
- 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 +181 -0
- package/src/lib/instance/bootstrap.ts +270 -0
- package/src/lib/instance/detect.ts +49 -0
- package/src/lib/instance/fingerprint.ts +76 -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 +205 -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 +236 -17
- 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/__tests__/render-prompt.test.ts +124 -0
- package/src/lib/workflows/blueprints/instantiator.ts +22 -1
- package/src/lib/workflows/blueprints/render-prompt.ts +71 -0
- package/src/lib/workflows/blueprints/types.ts +16 -2
- package/src/lib/workflows/delay.ts +106 -0
- package/src/lib/workflows/engine.ts +212 -7
- 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/test/setup.ts +10 -0
- 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
|
@@ -9,6 +9,47 @@ import { Inbox, Plus, CheckSquare, Square, ArrowRight, Play, Trash2 } from "luci
|
|
|
9
9
|
import { TaskCard, type TaskItem } from "./task-card";
|
|
10
10
|
import { WorkflowKanbanCard, type WorkflowKanbanItem } from "@/components/workflows/workflow-kanban-card";
|
|
11
11
|
import type { TaskStatus } from "@/lib/constants/task-status";
|
|
12
|
+
import type { SortOrder } from "./kanban-board";
|
|
13
|
+
|
|
14
|
+
type KanbanItem =
|
|
15
|
+
| { kind: "task"; data: TaskItem }
|
|
16
|
+
| { kind: "workflow"; data: WorkflowKanbanItem };
|
|
17
|
+
|
|
18
|
+
function itemName(item: KanbanItem): string {
|
|
19
|
+
return item.kind === "task" ? item.data.title : item.data.name;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function mergedItems(
|
|
23
|
+
tasks: TaskItem[],
|
|
24
|
+
workflows: WorkflowKanbanItem[],
|
|
25
|
+
sortOrder: SortOrder
|
|
26
|
+
): KanbanItem[] {
|
|
27
|
+
const items: KanbanItem[] = [
|
|
28
|
+
...workflows.map((w) => ({ kind: "workflow" as const, data: w })),
|
|
29
|
+
...tasks.map((t) => ({ kind: "task" as const, data: t })),
|
|
30
|
+
];
|
|
31
|
+
|
|
32
|
+
switch (sortOrder) {
|
|
33
|
+
case "created-desc":
|
|
34
|
+
return items.sort(
|
|
35
|
+
(a, b) => new Date(b.data.createdAt).getTime() - new Date(a.data.createdAt).getTime()
|
|
36
|
+
);
|
|
37
|
+
case "created-asc":
|
|
38
|
+
return items.sort(
|
|
39
|
+
(a, b) => new Date(a.data.createdAt).getTime() - new Date(b.data.createdAt).getTime()
|
|
40
|
+
);
|
|
41
|
+
case "title-asc":
|
|
42
|
+
return items.sort((a, b) => itemName(a).localeCompare(itemName(b)));
|
|
43
|
+
case "priority":
|
|
44
|
+
// Workflows lack priority — keep them at top, then sort tasks by priority
|
|
45
|
+
return items.sort((a, b) => {
|
|
46
|
+
if (a.kind === "workflow" && b.kind === "workflow") return 0;
|
|
47
|
+
if (a.kind === "workflow") return -1;
|
|
48
|
+
if (b.kind === "workflow") return 1;
|
|
49
|
+
return a.data.priority - b.data.priority;
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
}
|
|
12
53
|
|
|
13
54
|
const columnLabels: Record<string, string> = {
|
|
14
55
|
planned: "Planned",
|
|
@@ -22,6 +63,7 @@ export function KanbanColumn({
|
|
|
22
63
|
status,
|
|
23
64
|
tasks,
|
|
24
65
|
workflows = [],
|
|
66
|
+
sortOrder = "priority",
|
|
25
67
|
exitingIds,
|
|
26
68
|
onTaskClick,
|
|
27
69
|
onAddTask,
|
|
@@ -34,6 +76,7 @@ export function KanbanColumn({
|
|
|
34
76
|
status: TaskStatus;
|
|
35
77
|
tasks: TaskItem[];
|
|
36
78
|
workflows?: WorkflowKanbanItem[];
|
|
79
|
+
sortOrder?: SortOrder;
|
|
37
80
|
exitingIds?: Set<string>;
|
|
38
81
|
onTaskClick: (task: TaskItem) => void;
|
|
39
82
|
onAddTask?: () => void;
|
|
@@ -195,30 +238,26 @@ export function KanbanColumn({
|
|
|
195
238
|
</div>
|
|
196
239
|
) : (
|
|
197
240
|
<>
|
|
198
|
-
{
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
{/* Task cards (draggable) */}
|
|
203
|
-
{tasks.map((task) => {
|
|
204
|
-
const isExiting = exitingIds?.has(task.id);
|
|
205
|
-
return (
|
|
241
|
+
{mergedItems(tasks, workflows, sortOrder).map((item) =>
|
|
242
|
+
item.kind === "workflow" ? (
|
|
243
|
+
<WorkflowKanbanCard key={item.data.id} workflow={item.data} />
|
|
244
|
+
) : (
|
|
206
245
|
<div
|
|
207
|
-
key={
|
|
208
|
-
className={
|
|
246
|
+
key={item.data.id}
|
|
247
|
+
className={exitingIds?.has(item.data.id) ? "animate-card-exit pointer-events-none" : ""}
|
|
209
248
|
>
|
|
210
249
|
<TaskCard
|
|
211
|
-
task={
|
|
250
|
+
task={item.data}
|
|
212
251
|
onClick={onTaskClick}
|
|
213
252
|
selectionMode={selectMode}
|
|
214
|
-
selected={selectedIds.has(
|
|
253
|
+
selected={selectedIds.has(item.data.id)}
|
|
215
254
|
onSelect={handleSelect}
|
|
216
255
|
onDelete={onDeleteTask}
|
|
217
256
|
onEdit={onEditTask}
|
|
218
257
|
/>
|
|
219
258
|
</div>
|
|
220
|
-
)
|
|
221
|
-
|
|
259
|
+
)
|
|
260
|
+
)}
|
|
222
261
|
</>
|
|
223
262
|
)}
|
|
224
263
|
</div>
|
|
@@ -10,11 +10,15 @@ import {
|
|
|
10
10
|
Timer,
|
|
11
11
|
Cpu,
|
|
12
12
|
Paperclip,
|
|
13
|
+
CalendarClock,
|
|
14
|
+
CalendarCheck,
|
|
13
15
|
} from "lucide-react";
|
|
14
16
|
import { taskStatusVariant } from "@/lib/constants/status-colors";
|
|
17
|
+
import { formatCompactDateTime } from "@/lib/utils/format-timestamp";
|
|
15
18
|
import { TaskBentoCell } from "./task-bento-cell";
|
|
16
19
|
import type { TaskItem } from "./task-card";
|
|
17
20
|
import type { DocumentRow } from "@/lib/db/schema";
|
|
21
|
+
import { getRuntimeCatalogEntry } from "@/lib/agents/runtime/catalog";
|
|
18
22
|
|
|
19
23
|
const priorityConfig: Record<number, { icon: typeof ArrowUp; label: string }> = {
|
|
20
24
|
0: { icon: ArrowUp, label: "P0 Critical" },
|
|
@@ -75,6 +79,7 @@ export function TaskBentoGrid({ task, docs }: TaskBentoGridProps) {
|
|
|
75
79
|
|
|
76
80
|
const inputDocs = docs.filter((d) => d.direction === "input");
|
|
77
81
|
const outputDocs = docs.filter((d) => d.direction === "output");
|
|
82
|
+
const modelId = task.effectiveModelId ?? usage?.modelId ?? null;
|
|
78
83
|
const docSummary =
|
|
79
84
|
inputDocs.length > 0 && outputDocs.length > 0
|
|
80
85
|
? `${inputDocs.length} in / ${outputDocs.length} out`
|
|
@@ -127,6 +132,22 @@ export function TaskBentoGrid({ task, docs }: TaskBentoGridProps) {
|
|
|
127
132
|
/>
|
|
128
133
|
)}
|
|
129
134
|
|
|
135
|
+
{usage?.startedAt && (
|
|
136
|
+
<TaskBentoCell
|
|
137
|
+
icon={CalendarClock}
|
|
138
|
+
label="Started At"
|
|
139
|
+
value={formatCompactDateTime(usage.startedAt)}
|
|
140
|
+
/>
|
|
141
|
+
)}
|
|
142
|
+
|
|
143
|
+
{usage?.finishedAt && (
|
|
144
|
+
<TaskBentoCell
|
|
145
|
+
icon={CalendarCheck}
|
|
146
|
+
label="Finished At"
|
|
147
|
+
value={formatCompactDateTime(usage.finishedAt)}
|
|
148
|
+
/>
|
|
149
|
+
)}
|
|
150
|
+
|
|
130
151
|
{usage?.startedAt && usage?.finishedAt && (
|
|
131
152
|
<TaskBentoCell
|
|
132
153
|
icon={Timer}
|
|
@@ -135,11 +156,19 @@ export function TaskBentoGrid({ task, docs }: TaskBentoGridProps) {
|
|
|
135
156
|
/>
|
|
136
157
|
)}
|
|
137
158
|
|
|
138
|
-
{
|
|
159
|
+
{task.effectiveRuntimeId && (
|
|
160
|
+
<TaskBentoCell
|
|
161
|
+
icon={Cpu}
|
|
162
|
+
label="Runtime Used"
|
|
163
|
+
value={getRuntimeCatalogEntry(task.effectiveRuntimeId as never).label}
|
|
164
|
+
/>
|
|
165
|
+
)}
|
|
166
|
+
|
|
167
|
+
{modelId && (
|
|
139
168
|
<TaskBentoCell
|
|
140
169
|
icon={Cpu}
|
|
141
170
|
label="Model"
|
|
142
|
-
value={<span className="text-sm font-semibold">{truncateModel(
|
|
171
|
+
value={<span className="text-sm font-semibold">{truncateModel(modelId)}</span>}
|
|
143
172
|
/>
|
|
144
173
|
)}
|
|
145
174
|
|
|
@@ -5,7 +5,8 @@ import { useSortable } from "@dnd-kit/sortable";
|
|
|
5
5
|
import { CSS } from "@dnd-kit/utilities";
|
|
6
6
|
import { Card } from "@/components/ui/card";
|
|
7
7
|
import { Badge } from "@/components/ui/badge";
|
|
8
|
-
import { AlertCircle, Bot, ArrowUp, ArrowDown, Minus, Trash2, Check, X, Loader2, Square, CheckSquare, Pencil, FileText } from "lucide-react";
|
|
8
|
+
import { AlertCircle, Bot, ArrowUp, ArrowDown, Minus, Trash2, Check, X, Loader2, Square, CheckSquare, Pencil, FileText, Clock } from "lucide-react";
|
|
9
|
+
import { formatCompactDateTime } from "@/lib/utils/format-timestamp";
|
|
9
10
|
import type { TaskStatus } from "@/lib/constants/task-status";
|
|
10
11
|
|
|
11
12
|
export interface TaskItem {
|
|
@@ -16,6 +17,9 @@ export interface TaskItem {
|
|
|
16
17
|
priority: number;
|
|
17
18
|
assignedAgent: string | null;
|
|
18
19
|
agentProfile: string | null;
|
|
20
|
+
effectiveRuntimeId?: string | null;
|
|
21
|
+
effectiveModelId?: string | null;
|
|
22
|
+
runtimeFallbackReason?: string | null;
|
|
19
23
|
projectId: string | null;
|
|
20
24
|
projectName?: string;
|
|
21
25
|
workflowId: string | null;
|
|
@@ -122,6 +126,14 @@ export function TaskCard({
|
|
|
122
126
|
const showDeleteButton = onDelete && !isRunning;
|
|
123
127
|
const isEditable = (task.status === "planned" || task.status === "queued") && !!onEdit;
|
|
124
128
|
|
|
129
|
+
// Status-contextual date: planned→createdAt, running→startedAt, completed/failed→finishedAt
|
|
130
|
+
const relevantDate =
|
|
131
|
+
task.status === "planned" || task.status === "queued"
|
|
132
|
+
? task.createdAt
|
|
133
|
+
: task.status === "running"
|
|
134
|
+
? task.usage?.startedAt ?? task.updatedAt
|
|
135
|
+
: task.usage?.finishedAt ?? task.updatedAt;
|
|
136
|
+
|
|
125
137
|
return (
|
|
126
138
|
<Card
|
|
127
139
|
ref={setNodeRef}
|
|
@@ -244,7 +256,13 @@ export function TaskCard({
|
|
|
244
256
|
<Pencil className="h-3.5 w-3.5" />
|
|
245
257
|
</button>
|
|
246
258
|
)}
|
|
247
|
-
<
|
|
259
|
+
<span
|
|
260
|
+
className="flex items-center gap-1 text-xs text-muted-foreground tabular-nums truncate min-w-0 flex-1"
|
|
261
|
+
title={new Date(relevantDate).toLocaleString()}
|
|
262
|
+
>
|
|
263
|
+
<Clock className="h-3 w-3 shrink-0" />
|
|
264
|
+
{formatCompactDateTime(relevantDate)}
|
|
265
|
+
</span>
|
|
248
266
|
{showDeleteButton && (
|
|
249
267
|
<button
|
|
250
268
|
type="button"
|
|
@@ -257,7 +275,15 @@ export function TaskCard({
|
|
|
257
275
|
)}
|
|
258
276
|
</>
|
|
259
277
|
)
|
|
260
|
-
) :
|
|
278
|
+
) : (
|
|
279
|
+
<span
|
|
280
|
+
className="flex items-center gap-1 text-xs text-muted-foreground tabular-nums truncate min-w-0 flex-1"
|
|
281
|
+
title={new Date(relevantDate).toLocaleString()}
|
|
282
|
+
>
|
|
283
|
+
<Clock className="h-3 w-3 shrink-0" />
|
|
284
|
+
{formatCompactDateTime(relevantDate)}
|
|
285
|
+
</span>
|
|
286
|
+
)}
|
|
261
287
|
</div>
|
|
262
288
|
</Card>
|
|
263
289
|
);
|
|
@@ -19,11 +19,14 @@ import {
|
|
|
19
19
|
ArrowUp,
|
|
20
20
|
ArrowDown,
|
|
21
21
|
Minus,
|
|
22
|
+
CalendarClock,
|
|
23
|
+
CalendarCheck,
|
|
22
24
|
} from "lucide-react";
|
|
23
25
|
import { taskStatusVariant } from "@/lib/constants/status-colors";
|
|
24
26
|
import { MAX_RESUME_COUNT } from "@/lib/constants/task-status";
|
|
25
27
|
import { formatTimestamp } from "@/lib/utils/format-timestamp";
|
|
26
28
|
import type { TaskItem } from "./task-card";
|
|
29
|
+
import { getRuntimeCatalogEntry } from "@/lib/agents/runtime/catalog";
|
|
27
30
|
|
|
28
31
|
const priorityConfig: Record<number, { icon: typeof ArrowUp; label: string }> = {
|
|
29
32
|
0: { icon: ArrowUp, label: "P0 Critical" },
|
|
@@ -57,6 +60,12 @@ export function TaskChipBar({
|
|
|
57
60
|
}: TaskChipBarProps) {
|
|
58
61
|
const priority = priorityConfig[task.priority] ?? priorityConfig[2];
|
|
59
62
|
const PriorityIcon = priority.icon;
|
|
63
|
+
const requestedRuntimeLabel = task.assignedAgent
|
|
64
|
+
? getRuntimeCatalogEntry(task.assignedAgent as never).label
|
|
65
|
+
: null;
|
|
66
|
+
const effectiveRuntimeLabel = task.effectiveRuntimeId
|
|
67
|
+
? getRuntimeCatalogEntry(task.effectiveRuntimeId as never).label
|
|
68
|
+
: null;
|
|
60
69
|
|
|
61
70
|
const hasRelationships = task.projectId || task.workflowId || task.scheduleId;
|
|
62
71
|
|
|
@@ -147,7 +156,29 @@ export function TaskChipBar({
|
|
|
147
156
|
{task.assignedAgent && (
|
|
148
157
|
<Badge variant="secondary" className="text-xs gap-1">
|
|
149
158
|
<Bot className="h-3 w-3" />
|
|
150
|
-
{task.assignedAgent}
|
|
159
|
+
{requestedRuntimeLabel ?? task.assignedAgent}
|
|
160
|
+
</Badge>
|
|
161
|
+
)}
|
|
162
|
+
|
|
163
|
+
{task.effectiveRuntimeId &&
|
|
164
|
+
task.effectiveRuntimeId !== task.assignedAgent && (
|
|
165
|
+
<Badge
|
|
166
|
+
variant="outline"
|
|
167
|
+
className="text-xs gap-1 border-amber-500/40 bg-amber-500/10 text-amber-700 dark:text-amber-300"
|
|
168
|
+
title={task.runtimeFallbackReason ?? undefined}
|
|
169
|
+
>
|
|
170
|
+
<Bot className="h-3 w-3" />
|
|
171
|
+
Ran on {effectiveRuntimeLabel ?? task.effectiveRuntimeId}
|
|
172
|
+
</Badge>
|
|
173
|
+
)}
|
|
174
|
+
|
|
175
|
+
{task.runtimeFallbackReason && (
|
|
176
|
+
<Badge
|
|
177
|
+
variant="outline"
|
|
178
|
+
className="text-xs border-amber-500/40 bg-amber-500/10 text-amber-700 dark:text-amber-300"
|
|
179
|
+
title={task.runtimeFallbackReason}
|
|
180
|
+
>
|
|
181
|
+
Fallback applied
|
|
151
182
|
</Badge>
|
|
152
183
|
)}
|
|
153
184
|
|
|
@@ -172,6 +203,28 @@ export function TaskChipBar({
|
|
|
172
203
|
>
|
|
173
204
|
Updated {formatTimestamp(task.updatedAt)}
|
|
174
205
|
</Badge>
|
|
206
|
+
|
|
207
|
+
{task.usage?.startedAt && (
|
|
208
|
+
<Badge
|
|
209
|
+
variant="outline"
|
|
210
|
+
className="text-xs font-normal gap-1"
|
|
211
|
+
title={new Date(task.usage.startedAt).toLocaleString()}
|
|
212
|
+
>
|
|
213
|
+
<CalendarClock className="h-3 w-3" />
|
|
214
|
+
Started {formatTimestamp(task.usage.startedAt)}
|
|
215
|
+
</Badge>
|
|
216
|
+
)}
|
|
217
|
+
|
|
218
|
+
{task.usage?.finishedAt && (
|
|
219
|
+
<Badge
|
|
220
|
+
variant="outline"
|
|
221
|
+
className="text-xs font-normal gap-1"
|
|
222
|
+
title={new Date(task.usage.finishedAt).toLocaleString()}
|
|
223
|
+
>
|
|
224
|
+
<CalendarCheck className="h-3 w-3" />
|
|
225
|
+
Finished {formatTimestamp(task.usage.finishedAt)}
|
|
226
|
+
</Badge>
|
|
227
|
+
)}
|
|
175
228
|
</div>
|
|
176
229
|
|
|
177
230
|
{/* Row 3: Relationship Links (only if any FK exists) */}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Separator } from "@/components/ui/separator";
|
|
2
2
|
import { LightMarkdown } from "@/components/shared/light-markdown";
|
|
3
|
-
import { ExpandableResult } from "@/components/workflows/
|
|
3
|
+
import { ExpandableResult } from "@/components/workflows/shared/step-result";
|
|
4
4
|
|
|
5
5
|
interface TaskResultRendererProps {
|
|
6
6
|
description: string | null;
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { useCallback, useState } from "react";
|
|
4
|
+
import { Button } from "@/components/ui/button";
|
|
5
|
+
import { Play } from "lucide-react";
|
|
6
|
+
import { toast } from "sonner";
|
|
7
|
+
import { formatDuration } from "@/lib/workflows/delay";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Body content for a delay step row in the workflow status view. Renders
|
|
11
|
+
* three visual states keyed off the step's status:
|
|
12
|
+
*
|
|
13
|
+
* - Pending (workflow hasn't reached this step yet): "Will wait 3d"
|
|
14
|
+
* - Active delay (workflow paused, waiting): absolute resume time +
|
|
15
|
+
* remaining duration + Resume Now button
|
|
16
|
+
* - Completed (workflow has already passed this step): "Delayed 3d — completed"
|
|
17
|
+
*
|
|
18
|
+
* Countdown is static on mount/focus — no per-second ticking, because live
|
|
19
|
+
* aria-live updates would flood assistive tech users. Users needing a refresh
|
|
20
|
+
* can reload or refocus the page.
|
|
21
|
+
*
|
|
22
|
+
* Extracted from workflow-status-view.tsx during the TDR-031 router refactor
|
|
23
|
+
* so the non-loop subview can import it without a circular dependency on the
|
|
24
|
+
* thin router file.
|
|
25
|
+
*/
|
|
26
|
+
export function DelayStepBody({
|
|
27
|
+
workflowId,
|
|
28
|
+
delayDuration,
|
|
29
|
+
stepStatus,
|
|
30
|
+
resumeAt,
|
|
31
|
+
}: {
|
|
32
|
+
workflowId: string;
|
|
33
|
+
delayDuration: string;
|
|
34
|
+
stepStatus: string;
|
|
35
|
+
resumeAt: number | null;
|
|
36
|
+
}) {
|
|
37
|
+
const [resuming, setResuming] = useState(false);
|
|
38
|
+
|
|
39
|
+
const handleResumeNow = useCallback(async () => {
|
|
40
|
+
setResuming(true);
|
|
41
|
+
try {
|
|
42
|
+
const res = await fetch(`/api/workflows/${workflowId}/resume`, { method: "POST" });
|
|
43
|
+
if (res.status === 202) {
|
|
44
|
+
toast.success("Resume dispatched");
|
|
45
|
+
} else if (res.status === 409) {
|
|
46
|
+
toast.info("Workflow already resumed by scheduler");
|
|
47
|
+
} else {
|
|
48
|
+
const body = await res.json().catch(() => ({}));
|
|
49
|
+
toast.error(body.error ?? "Failed to resume workflow");
|
|
50
|
+
}
|
|
51
|
+
} catch (err) {
|
|
52
|
+
toast.error(err instanceof Error ? err.message : "Failed to resume workflow");
|
|
53
|
+
} finally {
|
|
54
|
+
setResuming(false);
|
|
55
|
+
}
|
|
56
|
+
}, [workflowId]);
|
|
57
|
+
|
|
58
|
+
if (stepStatus === "completed") {
|
|
59
|
+
return (
|
|
60
|
+
<p className="text-xs text-muted-foreground mt-0.5">
|
|
61
|
+
Delayed {delayDuration} — completed
|
|
62
|
+
</p>
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (stepStatus === "delayed" && resumeAt) {
|
|
67
|
+
const resumeDate = new Date(resumeAt);
|
|
68
|
+
const remainingMs = Math.max(0, resumeAt - Date.now());
|
|
69
|
+
const remainingLabel =
|
|
70
|
+
remainingMs < 60_000
|
|
71
|
+
? "less than a minute"
|
|
72
|
+
: formatDuration(Math.round(remainingMs / 60_000) * 60_000);
|
|
73
|
+
return (
|
|
74
|
+
<div className="mt-1 space-y-2">
|
|
75
|
+
<p className="text-xs text-status-warning">
|
|
76
|
+
Resumes{" "}
|
|
77
|
+
<time dateTime={resumeDate.toISOString()}>
|
|
78
|
+
{resumeDate.toLocaleString(undefined, {
|
|
79
|
+
weekday: "short",
|
|
80
|
+
month: "short",
|
|
81
|
+
day: "numeric",
|
|
82
|
+
hour: "numeric",
|
|
83
|
+
minute: "2-digit",
|
|
84
|
+
timeZoneName: "short",
|
|
85
|
+
})}
|
|
86
|
+
</time>{" "}
|
|
87
|
+
<span className="text-muted-foreground">({remainingLabel} remaining)</span>
|
|
88
|
+
</p>
|
|
89
|
+
<Button
|
|
90
|
+
size="sm"
|
|
91
|
+
variant="outline"
|
|
92
|
+
onClick={handleResumeNow}
|
|
93
|
+
disabled={resuming}
|
|
94
|
+
aria-label="Resume workflow now"
|
|
95
|
+
>
|
|
96
|
+
<Play className="h-3 w-3 mr-1" />
|
|
97
|
+
{resuming ? "Resuming..." : "Resume Now"}
|
|
98
|
+
</Button>
|
|
99
|
+
</div>
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Pending / upcoming delay — workflow hasn't reached this step yet
|
|
104
|
+
return (
|
|
105
|
+
<p className="text-xs text-muted-foreground mt-0.5">
|
|
106
|
+
Will wait {delayDuration}
|
|
107
|
+
</p>
|
|
108
|
+
);
|
|
109
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { useCallback, useEffect, useState } from "react";
|
|
4
|
+
import type { WorkflowStatusResponse } from "@/lib/workflows/types";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Polling hook for `GET /api/workflows/[id]/status`.
|
|
8
|
+
*
|
|
9
|
+
* Owns the fetch, the 3-second interval, cancellation on unmount, and
|
|
10
|
+
* re-subscription when `workflowId` changes. Exposes `setData` so callers can
|
|
11
|
+
* apply optimistic updates (e.g. flipping a step to "running" immediately when
|
|
12
|
+
* Execute is clicked) without racing the next poll tick.
|
|
13
|
+
*
|
|
14
|
+
* Returns `WorkflowStatusResponse | null` — narrowing on `data.pattern` is the
|
|
15
|
+
* caller's job per TDR-031. This hook intentionally does not narrow for
|
|
16
|
+
* consumers; each pattern-specific subview handles its own arm.
|
|
17
|
+
*/
|
|
18
|
+
export function useWorkflowStatus(workflowId: string): {
|
|
19
|
+
data: WorkflowStatusResponse | null;
|
|
20
|
+
setData: (updater: (current: WorkflowStatusResponse | null) => WorkflowStatusResponse | null) => void;
|
|
21
|
+
refetch: () => Promise<void>;
|
|
22
|
+
} {
|
|
23
|
+
const [data, setDataInternal] = useState<WorkflowStatusResponse | null>(null);
|
|
24
|
+
|
|
25
|
+
const refetch = useCallback(async () => {
|
|
26
|
+
const res = await fetch(`/api/workflows/${workflowId}/status`);
|
|
27
|
+
if (res.ok) {
|
|
28
|
+
const json = (await res.json()) as WorkflowStatusResponse;
|
|
29
|
+
setDataInternal(json);
|
|
30
|
+
}
|
|
31
|
+
}, [workflowId]);
|
|
32
|
+
|
|
33
|
+
useEffect(() => {
|
|
34
|
+
// Reset data when the workflowId changes so the old workflow's state
|
|
35
|
+
// never briefly shows while the new one is fetching.
|
|
36
|
+
setDataInternal(null);
|
|
37
|
+
refetch();
|
|
38
|
+
const interval = setInterval(refetch, 3000);
|
|
39
|
+
return () => clearInterval(interval);
|
|
40
|
+
}, [refetch]);
|
|
41
|
+
|
|
42
|
+
const setData = useCallback(
|
|
43
|
+
(updater: (current: WorkflowStatusResponse | null) => WorkflowStatusResponse | null) => {
|
|
44
|
+
setDataInternal((prev) => updater(prev));
|
|
45
|
+
},
|
|
46
|
+
[]
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
return { data, setData, refetch };
|
|
50
|
+
}
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
import { useState } from "react";
|
|
4
4
|
import { Badge } from "@/components/ui/badge";
|
|
5
5
|
import { Button } from "@/components/ui/button";
|
|
6
|
-
import { ExpandableResult } from "./
|
|
6
|
+
import { ExpandableResult } from "./shared/step-result";
|
|
7
7
|
import {
|
|
8
8
|
CheckCircle,
|
|
9
9
|
Circle,
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { useState } from "react";
|
|
4
|
+
import Link from "next/link";
|
|
5
|
+
import ReactMarkdown from "react-markdown";
|
|
6
|
+
import remarkGfm from "remark-gfm";
|
|
7
|
+
import { FileText } from "lucide-react";
|
|
8
|
+
import { PROSE_NOTIFICATION } from "@/lib/constants/prose-styles";
|
|
9
|
+
import type { WorkflowStatusDocument } from "@/lib/workflows/types";
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Expandable step result with gradient fade progressive disclosure.
|
|
13
|
+
* Extracted from the legacy workflow-status-view god component so it can be
|
|
14
|
+
* reused by pattern-specific subviews and LoopStatusView without a circular
|
|
15
|
+
* import.
|
|
16
|
+
*/
|
|
17
|
+
export function ExpandableResult({ result }: { result: string }) {
|
|
18
|
+
const [expanded, setExpanded] = useState(false);
|
|
19
|
+
|
|
20
|
+
if (!result) return null;
|
|
21
|
+
|
|
22
|
+
return (
|
|
23
|
+
<div className="mt-2">
|
|
24
|
+
<div
|
|
25
|
+
className={`${PROSE_NOTIFICATION} ${
|
|
26
|
+
expanded
|
|
27
|
+
? "max-h-96 overflow-auto"
|
|
28
|
+
: result.length > 200
|
|
29
|
+
? "max-h-20 overflow-hidden mask-fade-bottom"
|
|
30
|
+
: ""
|
|
31
|
+
}`}
|
|
32
|
+
>
|
|
33
|
+
<ReactMarkdown remarkPlugins={[remarkGfm]}>{result}</ReactMarkdown>
|
|
34
|
+
</div>
|
|
35
|
+
{result.length > 200 && (
|
|
36
|
+
<button
|
|
37
|
+
type="button"
|
|
38
|
+
className="mt-1 text-xs text-muted-foreground hover:text-foreground transition-colors"
|
|
39
|
+
onClick={() => setExpanded(!expanded)}
|
|
40
|
+
>
|
|
41
|
+
{expanded ? "Show less" : "Show more"}
|
|
42
|
+
</button>
|
|
43
|
+
)}
|
|
44
|
+
</div>
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Document list for a single step or parent task — renders a labeled cluster
|
|
50
|
+
* of links to document detail pages.
|
|
51
|
+
*/
|
|
52
|
+
export function DocumentList({
|
|
53
|
+
docs,
|
|
54
|
+
label,
|
|
55
|
+
}: {
|
|
56
|
+
docs: WorkflowStatusDocument[];
|
|
57
|
+
label: string;
|
|
58
|
+
}) {
|
|
59
|
+
if (docs.length === 0) return null;
|
|
60
|
+
|
|
61
|
+
return (
|
|
62
|
+
<div className="mt-3">
|
|
63
|
+
<p className="text-xs font-medium text-muted-foreground mb-1.5">{label}</p>
|
|
64
|
+
<div className="space-y-1">
|
|
65
|
+
{docs.map((doc) => (
|
|
66
|
+
<Link
|
|
67
|
+
key={doc.id}
|
|
68
|
+
href={`/documents/${doc.id}`}
|
|
69
|
+
className="flex items-center gap-2 text-xs text-brand-blue hover:underline"
|
|
70
|
+
>
|
|
71
|
+
<FileText className="h-3 w-3 shrink-0" />
|
|
72
|
+
{doc.originalName}
|
|
73
|
+
</Link>
|
|
74
|
+
))}
|
|
75
|
+
</div>
|
|
76
|
+
</div>
|
|
77
|
+
);
|
|
78
|
+
}
|