@bastani/atomic 0.9.12 → 0.9.13-alpha.1
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/CHANGELOG.md +20 -0
- package/dist/builtin/intercom/CHANGELOG.md +6 -0
- package/dist/builtin/intercom/README.md +5 -12
- package/dist/builtin/intercom/broker/client.ts +11 -2
- package/dist/builtin/intercom/contact-supervisor-tool.ts +13 -3
- package/dist/builtin/intercom/index-heavy.ts +13 -3
- package/dist/builtin/intercom/index.ts +43 -19
- package/dist/builtin/intercom/intercom-utils.ts +15 -29
- package/dist/builtin/intercom/package.json +1 -1
- package/dist/builtin/intercom/source-ownership.ts +9 -12
- package/dist/builtin/mcp/CHANGELOG.md +6 -0
- package/dist/builtin/mcp/README.md +3 -1
- package/dist/builtin/mcp/index.ts +10 -16
- package/dist/builtin/mcp/package.json +1 -1
- package/dist/builtin/mcp/startup-warmup.ts +20 -8
- package/dist/builtin/subagents/CHANGELOG.md +30 -0
- package/dist/builtin/subagents/README.md +22 -30
- package/dist/builtin/subagents/package.json +1 -1
- package/dist/builtin/subagents/src/extension/doctor.ts +1 -1
- package/dist/builtin/subagents/src/extension/fanout-child.ts +76 -56
- package/dist/builtin/subagents/src/extension/index.ts +23 -32
- package/dist/builtin/subagents/src/extension/startup-maintenance.ts +25 -44
- package/dist/builtin/subagents/src/intercom/result-intercom.ts +7 -7
- package/dist/builtin/subagents/src/runs/background/async-job-tracker.ts +166 -434
- package/dist/builtin/subagents/src/runs/background/notify.ts +29 -8
- package/dist/builtin/subagents/src/runs/foreground/chain-execution-dynamic-step.ts +2 -2
- package/dist/builtin/subagents/src/runs/foreground/chain-execution-parallel-runner.ts +4 -4
- package/dist/builtin/subagents/src/runs/foreground/chain-execution-parallel-step.ts +2 -2
- package/dist/builtin/subagents/src/runs/foreground/chain-execution-sequential-step.ts +2 -2
- package/dist/builtin/subagents/src/runs/foreground/execution-run-sync.ts +1 -281
- package/dist/builtin/subagents/src/runs/foreground/execution-structured-retries.ts +11 -75
- package/dist/builtin/subagents/src/runs/foreground/inprocess-run-sync.ts +457 -0
- package/dist/builtin/subagents/src/runs/foreground/subagent-executor-async.ts +119 -246
- package/dist/builtin/subagents/src/runs/foreground/subagent-executor-chain.ts +1 -1
- package/dist/builtin/subagents/src/runs/foreground/subagent-executor-context.ts +18 -9
- package/dist/builtin/subagents/src/runs/foreground/subagent-executor-parallel-task.ts +1 -1
- package/dist/builtin/subagents/src/runs/foreground/subagent-executor-parallel.ts +4 -4
- package/dist/builtin/subagents/src/runs/foreground/subagent-executor-resume.ts +1 -646
- package/dist/builtin/subagents/src/runs/foreground/subagent-executor-runtime.ts +2 -7
- package/dist/builtin/subagents/src/runs/foreground/subagent-executor-single.ts +5 -5
- package/dist/builtin/subagents/src/runs/foreground/subagent-executor-status.ts +7 -8
- package/dist/builtin/subagents/src/runs/foreground/subagent-executor-types.ts +14 -5
- package/dist/builtin/subagents/src/runs/foreground/subagent-executor.ts +158 -93
- package/dist/builtin/subagents/src/runs/inprocess/background-single.ts +179 -0
- package/dist/builtin/subagents/src/runs/inprocess/background.ts +89 -0
- package/dist/builtin/subagents/src/runs/inprocess/child-policy.ts +27 -0
- package/dist/builtin/subagents/src/runs/inprocess/control-registry.ts +55 -0
- package/dist/builtin/subagents/src/runs/inprocess/control-status.ts +106 -0
- package/dist/builtin/subagents/src/runs/inprocess/index.ts +50 -0
- package/dist/builtin/subagents/src/runs/inprocess/nested-routing.ts +218 -0
- package/dist/builtin/subagents/src/runs/{shared/subagent-prompt-runtime.ts → inprocess/prompt-behavior.ts} +37 -75
- package/dist/builtin/subagents/src/runs/inprocess/runner.ts +1138 -0
- package/dist/builtin/subagents/src/runs/{shared/nested-events.ts → inprocess/runtime-support/nested-api.ts} +13 -7
- package/dist/builtin/subagents/src/runs/{shared/nested-events-control.ts → inprocess/runtime-support/nested-control.ts} +18 -17
- package/dist/builtin/subagents/src/runs/{shared/nested-events-core.ts → inprocess/runtime-support/nested-core.ts} +14 -61
- package/dist/builtin/subagents/src/runs/{shared/nested-events-projection.ts → inprocess/runtime-support/nested-projection.ts} +4 -10
- package/dist/builtin/subagents/src/runs/{shared/nested-events-registry.ts → inprocess/runtime-support/nested-registry.ts} +19 -4
- package/dist/builtin/subagents/src/runs/{shared/nested-render.ts → inprocess/runtime-support/nested-rendering.ts} +3 -3
- package/dist/builtin/subagents/src/runs/{shared/nested-events-sanitize.ts → inprocess/runtime-support/nested-sanitize.ts} +3 -3
- package/dist/builtin/subagents/src/runs/inprocess.ts +1 -0
- package/dist/builtin/subagents/src/runs/shared/dynamic-fanout.ts +10 -4
- package/dist/builtin/subagents/src/runs/shared/model-candidate-filter.ts +0 -1
- package/dist/builtin/subagents/src/runs/shared/model-fallback.ts +15 -492
- package/dist/builtin/subagents/src/runs/shared/parallel-utils.ts +13 -11
- package/dist/builtin/subagents/src/runs/shared/run-history.ts +3 -5
- package/dist/builtin/subagents/src/runs/shared/single-output.ts +4 -4
- package/dist/builtin/subagents/src/runs/shared/workflow-graph.ts +5 -5
- package/dist/builtin/subagents/src/shared/types-async.ts +0 -1
- package/dist/builtin/subagents/src/shared/types-config.ts +18 -0
- package/dist/builtin/subagents/src/shared/types-results.ts +9 -2
- package/dist/builtin/subagents/src/slash/prompt-template-bridge.ts +2 -3
- package/dist/builtin/subagents/src/slash/slash-live-state.ts +2 -2
- package/dist/builtin/subagents/src/tui/render-chain-graph.ts +10 -4
- package/dist/builtin/subagents/src/tui/render-event-formatting.ts +12 -11
- package/dist/builtin/subagents/src/tui/render-result-compact.ts +49 -12
- package/dist/builtin/subagents/src/tui/render-result.ts +46 -17
- package/dist/builtin/subagents/src/tui/render-stable-output.ts +1 -1
- package/dist/builtin/subagents/src/tui/render-status-progress.ts +8 -5
- package/dist/builtin/subagents/src/tui/render-widget-graph.ts +0 -8
- package/dist/builtin/subagents/src/tui/render-widget.ts +0 -24
- package/dist/builtin/web-access/package.json +1 -1
- package/dist/builtin/workflows/CHANGELOG.md +25 -0
- package/dist/builtin/workflows/README.md +13 -3
- package/dist/builtin/workflows/package.json +1 -1
- package/dist/builtin/workflows/src/durable/backend.ts +7 -0
- package/dist/builtin/workflows/src/durable/dbos-metadata.ts +11 -1
- package/dist/builtin/workflows/src/durable/resume-runtime.ts +1 -0
- package/dist/builtin/workflows/src/durable/tool-primitive.ts +4 -28
- package/dist/builtin/workflows/src/durable/types.ts +5 -0
- package/dist/builtin/workflows/src/engine/run.ts +17 -1
- package/dist/builtin/workflows/src/extension/atomic-stage-session.ts +12 -39
- package/dist/builtin/workflows/src/extension/config-file-loader.ts +10 -1
- package/dist/builtin/workflows/src/extension/config-loader.ts +1 -1
- package/dist/builtin/workflows/src/extension/dispatcher.ts +4 -0
- package/dist/builtin/workflows/src/extension/index.bundle.mjs +1151 -737
- package/dist/builtin/workflows/src/extension/lifecycle-notifications.ts +303 -38
- package/dist/builtin/workflows/src/extension/render-result.ts +4 -0
- package/dist/builtin/workflows/src/extension/runtime-durable-resume.ts +6 -3
- package/dist/builtin/workflows/src/extension/runtime.ts +7 -1
- package/dist/builtin/workflows/src/extension/wiring.ts +53 -11
- package/dist/builtin/workflows/src/extension/workflow-command-registration.ts +12 -3
- package/dist/builtin/workflows/src/extension/workflow-durable-resume-command.ts +5 -2
- package/dist/builtin/workflows/src/extension/workflow-run-control-command.ts +9 -7
- package/dist/builtin/workflows/src/extension/workflow-status-summary.ts +11 -0
- package/dist/builtin/workflows/src/extension/workflow-tool-content.ts +18 -1
- package/dist/builtin/workflows/src/extension/workflow-tool-control.ts +10 -8
- package/dist/builtin/workflows/src/extension/workflow-tool.ts +9 -4
- package/dist/builtin/workflows/src/runs/background/quit.ts +28 -4
- package/dist/builtin/workflows/src/runs/background/resume-acknowledgements.ts +2 -1
- package/dist/builtin/workflows/src/runs/background/status.ts +55 -8
- package/dist/builtin/workflows/src/runs/foreground/executor-direct-helpers.ts +4 -0
- package/dist/builtin/workflows/src/runs/foreground/executor-prompt-nodes.ts +3 -1
- package/dist/builtin/workflows/src/runs/foreground/executor-stage-control.ts +1 -1
- package/dist/builtin/workflows/src/runs/foreground/stage-runner-context.ts +1 -1
- package/dist/builtin/workflows/src/runs/foreground/stage-runner-controller.ts +688 -38
- package/dist/builtin/workflows/src/runs/foreground/stage-runner-types.ts +3 -1
- package/dist/builtin/workflows/src/runs/shared/model-fallback-failures.ts +19 -514
- package/dist/builtin/workflows/src/runs/shared/retry.ts +44 -0
- package/dist/builtin/workflows/src/shared/authoring-contract-stage.d.ts +5 -0
- package/dist/builtin/workflows/src/shared/authoring-contract-stage.ts +6 -0
- package/dist/builtin/workflows/src/shared/authoring-contract-ui.d.ts +5 -1
- package/dist/builtin/workflows/src/shared/authoring-contract-ui.ts +5 -0
- package/dist/builtin/workflows/src/shared/persistence-restore-helpers.ts +5 -0
- package/dist/builtin/workflows/src/shared/persistence-restore.ts +3 -0
- package/dist/builtin/workflows/src/shared/persistence-session-entries.ts +4 -0
- package/dist/builtin/workflows/src/shared/run-indicator-status.ts +75 -0
- package/dist/builtin/workflows/src/shared/store-public-types.ts +43 -7
- package/dist/builtin/workflows/src/shared/store-run-methods.ts +42 -8
- package/dist/builtin/workflows/src/shared/store-stage-methods.ts +31 -8
- package/dist/builtin/workflows/src/shared/store-types.ts +34 -0
- package/dist/builtin/workflows/src/shared/timing.ts +10 -0
- package/dist/builtin/workflows/src/tui/chat-surface-message.ts +22 -2
- package/dist/builtin/workflows/src/tui/session-list.ts +1 -1
- package/dist/builtin/workflows/src/tui/session-overlays.ts +14 -10
- package/dist/builtin/workflows/src/tui/session-picker.ts +20 -4
- package/dist/builtin/workflows/src/tui/status-list.ts +31 -47
- package/dist/builtin/workflows/src/tui/store-widget-installer.ts +9 -1
- package/dist/builtin/workflows/src/tui/widget.ts +16 -46
- package/dist/core/agent-session-auto-compaction.d.ts.map +1 -1
- package/dist/core/agent-session-auto-compaction.js +54 -11
- package/dist/core/agent-session-auto-compaction.js.map +1 -1
- package/dist/core/agent-session-events.d.ts.map +1 -1
- package/dist/core/agent-session-events.js +49 -14
- package/dist/core/agent-session-events.js.map +1 -1
- package/dist/core/agent-session-methods.d.ts +13 -0
- package/dist/core/agent-session-methods.d.ts.map +1 -1
- package/dist/core/agent-session-methods.js.map +1 -1
- package/dist/core/agent-session-models.d.ts.map +1 -1
- package/dist/core/agent-session-models.js +12 -1
- package/dist/core/agent-session-models.js.map +1 -1
- package/dist/core/agent-session-prompt.d.ts.map +1 -1
- package/dist/core/agent-session-prompt.js +4 -0
- package/dist/core/agent-session-prompt.js.map +1 -1
- package/dist/core/agent-session-retry.d.ts +21 -4
- package/dist/core/agent-session-retry.d.ts.map +1 -1
- package/dist/core/agent-session-retry.js +232 -106
- package/dist/core/agent-session-retry.js.map +1 -1
- package/dist/core/agent-session-state.d.ts.map +1 -1
- package/dist/core/agent-session-state.js +2 -1
- package/dist/core/agent-session-state.js.map +1 -1
- package/dist/core/agent-session-tool-hooks.d.ts.map +1 -1
- package/dist/core/agent-session-tool-hooks.js +12 -0
- package/dist/core/agent-session-tool-hooks.js.map +1 -1
- package/dist/core/agent-session-tool-registry.d.ts.map +1 -1
- package/dist/core/agent-session-tool-registry.js +1 -1
- package/dist/core/agent-session-tool-registry.js.map +1 -1
- package/dist/core/agent-session-types.d.ts +3 -1
- package/dist/core/agent-session-types.d.ts.map +1 -1
- package/dist/core/agent-session-types.js.map +1 -1
- package/dist/core/agent-session.d.ts +12 -1
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +7 -0
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/extensions/context-types.d.ts +25 -0
- package/dist/core/extensions/context-types.d.ts.map +1 -1
- package/dist/core/extensions/context-types.js.map +1 -1
- package/dist/core/extensions/index.d.ts +1 -1
- package/dist/core/extensions/index.d.ts.map +1 -1
- package/dist/core/extensions/index.js.map +1 -1
- package/dist/core/extensions/runner-context.d.ts +2 -1
- package/dist/core/extensions/runner-context.d.ts.map +1 -1
- package/dist/core/extensions/runner-context.js +4 -0
- package/dist/core/extensions/runner-context.js.map +1 -1
- package/dist/core/extensions/runner.d.ts +3 -2
- package/dist/core/extensions/runner.d.ts.map +1 -1
- package/dist/core/extensions/runner.js +3 -1
- package/dist/core/extensions/runner.js.map +1 -1
- package/dist/core/model-fallback-failures.d.ts +30 -0
- package/dist/core/model-fallback-failures.d.ts.map +1 -0
- package/dist/core/model-fallback-failures.js +610 -0
- package/dist/core/model-fallback-failures.js.map +1 -0
- package/dist/core/retry-policy.d.ts +30 -0
- package/dist/core/retry-policy.d.ts.map +1 -0
- package/dist/core/retry-policy.js +25 -0
- package/dist/core/retry-policy.js.map +1 -0
- package/dist/core/sdk-types.d.ts +8 -2
- package/dist/core/sdk-types.d.ts.map +1 -1
- package/dist/core/sdk-types.js.map +1 -1
- package/dist/core/sdk.d.ts.map +1 -1
- package/dist/core/sdk.js +6 -1
- package/dist/core/sdk.js.map +1 -1
- package/dist/core/session-manager-core.d.ts +2 -0
- package/dist/core/session-manager-core.d.ts.map +1 -1
- package/dist/core/session-manager-core.js +7 -0
- package/dist/core/session-manager-core.js.map +1 -1
- package/dist/index-extensions.d.ts +1 -1
- package/dist/index-extensions.d.ts.map +1 -1
- package/dist/index-extensions.js.map +1 -1
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/docs/intercom.md +5 -10
- package/docs/settings.md +7 -1
- package/docs/subagents.md +19 -19
- package/docs/workflows.md +21 -5
- package/examples/extensions/gondolin/package-lock.json +3 -3
- package/npm-shrinkwrap.json +29 -29
- package/package.json +2 -2
- package/dist/builtin/subagents/src/runs/background/async-event-journal.ts +0 -112
- package/dist/builtin/subagents/src/runs/background/async-execution-chain.ts +0 -543
- package/dist/builtin/subagents/src/runs/background/async-execution-common.ts +0 -140
- package/dist/builtin/subagents/src/runs/background/async-execution-single.ts +0 -279
- package/dist/builtin/subagents/src/runs/background/async-execution-types.ts +0 -99
- package/dist/builtin/subagents/src/runs/background/async-execution.ts +0 -17
- package/dist/builtin/subagents/src/runs/background/async-resume.ts +0 -402
- package/dist/builtin/subagents/src/runs/background/async-status.ts +0 -411
- package/dist/builtin/subagents/src/runs/background/completion-claims.ts +0 -196
- package/dist/builtin/subagents/src/runs/background/completion-dedupe.ts +0 -102
- package/dist/builtin/subagents/src/runs/background/parallel-groups.ts +0 -59
- package/dist/builtin/subagents/src/runs/background/result-delivery-processor.ts +0 -300
- package/dist/builtin/subagents/src/runs/background/result-file-claims.ts +0 -166
- package/dist/builtin/subagents/src/runs/background/result-quarantine.ts +0 -77
- package/dist/builtin/subagents/src/runs/background/result-retry-scheduler.ts +0 -48
- package/dist/builtin/subagents/src/runs/background/result-status.ts +0 -87
- package/dist/builtin/subagents/src/runs/background/result-watcher-data.ts +0 -72
- package/dist/builtin/subagents/src/runs/background/result-watcher.ts +0 -320
- package/dist/builtin/subagents/src/runs/background/run-id-resolver.ts +0 -105
- package/dist/builtin/subagents/src/runs/background/run-status.ts +0 -358
- package/dist/builtin/subagents/src/runs/background/stale-run-reconciler.ts +0 -457
- package/dist/builtin/subagents/src/runs/background/subagent-runner-dynamic.ts +0 -376
- package/dist/builtin/subagents/src/runs/background/subagent-runner-finalize.ts +0 -150
- package/dist/builtin/subagents/src/runs/background/subagent-runner-output.ts +0 -109
- package/dist/builtin/subagents/src/runs/background/subagent-runner-parallel-helpers.ts +0 -128
- package/dist/builtin/subagents/src/runs/background/subagent-runner-parallel.ts +0 -309
- package/dist/builtin/subagents/src/runs/background/subagent-runner-sequential.ts +0 -173
- package/dist/builtin/subagents/src/runs/background/subagent-runner-state.ts +0 -578
- package/dist/builtin/subagents/src/runs/background/subagent-runner-step.ts +0 -330
- package/dist/builtin/subagents/src/runs/background/subagent-runner-streaming.ts +0 -330
- package/dist/builtin/subagents/src/runs/background/subagent-runner-types.ts +0 -210
- package/dist/builtin/subagents/src/runs/background/subagent-runner-utils.ts +0 -69
- package/dist/builtin/subagents/src/runs/background/subagent-runner.ts +0 -90
- package/dist/builtin/subagents/src/runs/background/top-level-async.ts +0 -12
- package/dist/builtin/subagents/src/runs/foreground/execution-attempt-control.ts +0 -140
- package/dist/builtin/subagents/src/runs/foreground/execution-attempt-finalize.ts +0 -108
- package/dist/builtin/subagents/src/runs/foreground/execution-attempt-types.ts +0 -17
- package/dist/builtin/subagents/src/runs/foreground/execution-attempt.ts +0 -558
- package/dist/builtin/subagents/src/runs/shared/attempt-watchdog.ts +0 -126
- package/dist/builtin/subagents/src/runs/shared/final-drain.ts +0 -39
- package/dist/builtin/subagents/src/runs/shared/pi-args.ts +0 -339
- package/dist/builtin/subagents/src/runs/shared/pi-spawn.ts +0 -165
- package/dist/builtin/subagents/src/runs/shared/spawn-env.ts +0 -21
- package/dist/builtin/subagents/src/shared/post-exit-stdio-guard.ts +0 -86
- /package/dist/builtin/subagents/src/runs/{shared/nested-path.ts → inprocess/runtime-support/nested-paths.ts} +0 -0
|
@@ -17,6 +17,7 @@ import type {
|
|
|
17
17
|
StageSnapshot,
|
|
18
18
|
StageStatus,
|
|
19
19
|
StoreSnapshot,
|
|
20
|
+
WorkflowActor,
|
|
20
21
|
} from "../shared/store-types.js";
|
|
21
22
|
import { deriveGraphThemeFromPiTheme, type GraphTheme } from "../tui/graph-theme.js";
|
|
22
23
|
import { renderWorkflowNoticeCard, type WorkflowNoticeTone } from "../tui/workflow-notice-card.js";
|
|
@@ -26,13 +27,33 @@ import { createLifecycleNoticeDelivery } from "./lifecycle-notification-delivery
|
|
|
26
27
|
export const LIFECYCLE_NOTICE_CUSTOM_TYPE = "workflows:lifecycle-notice";
|
|
27
28
|
export const LIFECYCLE_NOTICE_SNIPPET_LIMIT = 240;
|
|
28
29
|
|
|
29
|
-
export type WorkflowLifecycleNoticeKind =
|
|
30
|
+
export type WorkflowLifecycleNoticeKind =
|
|
31
|
+
| "started"
|
|
32
|
+
| "completed"
|
|
33
|
+
| "failed"
|
|
34
|
+
| "blocked"
|
|
35
|
+
| "awaiting_input"
|
|
36
|
+
| "paused"
|
|
37
|
+
| "quit"
|
|
38
|
+
| "resumed";
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Control-action kinds. Unlike terminal outcomes these are reversible and can
|
|
42
|
+
* repeat, so their dedupe keys carry the occurrence timestamp rather than the
|
|
43
|
+
* run id alone, and they are reported only when a control path attributed the
|
|
44
|
+
* action to a user.
|
|
45
|
+
*/
|
|
46
|
+
type WorkflowControlNoticeKind = "started" | "paused" | "quit" | "resumed";
|
|
30
47
|
|
|
31
48
|
export const WORKFLOW_LIFECYCLE_NOTICE_KINDS = [
|
|
49
|
+
"started",
|
|
32
50
|
"completed",
|
|
33
51
|
"failed",
|
|
34
52
|
"blocked",
|
|
35
53
|
"awaiting_input",
|
|
54
|
+
"paused",
|
|
55
|
+
"quit",
|
|
56
|
+
"resumed",
|
|
36
57
|
] as const satisfies readonly WorkflowLifecycleNoticeKind[];
|
|
37
58
|
|
|
38
59
|
export interface WorkflowLifecycleNotificationConfig {
|
|
@@ -57,6 +78,14 @@ export interface WorkflowLifecycleNoticeDetails {
|
|
|
57
78
|
readonly toolName?: string;
|
|
58
79
|
readonly durationMs?: number;
|
|
59
80
|
readonly active?: boolean;
|
|
81
|
+
/** Whether a quit run advertises itself as resumable. */
|
|
82
|
+
readonly resumable?: boolean;
|
|
83
|
+
/** Who performed this lifecycle action, when a control path attributed it. */
|
|
84
|
+
readonly actor?: WorkflowActor;
|
|
85
|
+
/** Who launched the run, when the launch was attributed. */
|
|
86
|
+
readonly origin?: WorkflowActor;
|
|
87
|
+
/** Source run this run continues, for a resumed continuation with a fresh id. */
|
|
88
|
+
readonly continuedFromRunId?: string;
|
|
60
89
|
readonly createdAt: number;
|
|
61
90
|
}
|
|
62
91
|
|
|
@@ -118,6 +147,12 @@ export function seedWorkflowLifecycleNotificationState(
|
|
|
118
147
|
state.deliveredTerminalRuns.add(key);
|
|
119
148
|
}
|
|
120
149
|
}
|
|
150
|
+
for (const occurrence of controlOccurrences(run)) {
|
|
151
|
+
const controlKey = controlOccurrenceKey(run, occurrence);
|
|
152
|
+
if (!state.pendingTerminalRuns.has(controlKey) && !state.retryableTerminalRuns.has(controlKey)) {
|
|
153
|
+
state.deliveredTerminalRuns.add(controlKey);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
121
156
|
if (run.pendingPrompt !== undefined) {
|
|
122
157
|
state.deliveredInputPrompts.add(runAwaitingInputKey(run.id, run.pendingPrompt));
|
|
123
158
|
}
|
|
@@ -227,6 +262,26 @@ export function installWorkflowLifecycleNotifications(options: WorkflowLifecycle
|
|
|
227
262
|
delivery.deliver(key, makeTerminalNotice(run, kind));
|
|
228
263
|
};
|
|
229
264
|
|
|
265
|
+
const emitControlNoticesOnce = (run: RunSnapshot): void => {
|
|
266
|
+
for (const occurrence of controlOccurrences(run)) {
|
|
267
|
+
if (!notifyOn.has(occurrence.kind)) continue;
|
|
268
|
+
const key = controlOccurrenceKey(run, occurrence);
|
|
269
|
+
if (
|
|
270
|
+
state.deliveredTerminalRuns.has(key) ||
|
|
271
|
+
state.pendingTerminalRuns.has(key) ||
|
|
272
|
+
state.retryableTerminalRuns.has(key)
|
|
273
|
+
)
|
|
274
|
+
continue;
|
|
275
|
+
if (state.suppressionDepth > 0) {
|
|
276
|
+
state.deliveredTerminalRuns.add(key);
|
|
277
|
+
state.retryableTerminalRuns.delete(key);
|
|
278
|
+
state.retryableTerminalNotices.delete(key);
|
|
279
|
+
continue;
|
|
280
|
+
}
|
|
281
|
+
delivery.deliver(key, makeControlNotice(run, occurrence));
|
|
282
|
+
}
|
|
283
|
+
};
|
|
284
|
+
|
|
230
285
|
const emitStageAwaitingInputNoticeOnce = (run: RunSnapshot, stage: StageSnapshot): void => {
|
|
231
286
|
if (stage.status !== "awaiting_input") return;
|
|
232
287
|
|
|
@@ -256,6 +311,7 @@ export function installWorkflowLifecycleNotifications(options: WorkflowLifecycle
|
|
|
256
311
|
emitTerminalNoticeOnce(run, "completed");
|
|
257
312
|
emitTerminalNoticeOnce(run, "failed");
|
|
258
313
|
emitTerminalNoticeOnce(run, "blocked");
|
|
314
|
+
emitControlNoticesOnce(run);
|
|
259
315
|
|
|
260
316
|
if (!notifyOn.has("awaiting_input")) continue;
|
|
261
317
|
emitRunAwaitingInputNoticeOnce(run);
|
|
@@ -298,20 +354,51 @@ export function registerLifecycleNoticeRenderer(
|
|
|
298
354
|
|
|
299
355
|
export function formatWorkflowLifecycleNoticeText(details: WorkflowLifecycleNoticeDetails): string {
|
|
300
356
|
const workflowName = escapeQuotedText(details.workflowName);
|
|
357
|
+
// "which you started" / "which the user started". Omitted entirely when the
|
|
358
|
+
// run carries no recorded origin, rather than guessing one.
|
|
359
|
+
const origin =
|
|
360
|
+
details.origin === undefined
|
|
361
|
+
? ""
|
|
362
|
+
: details.origin === "agent"
|
|
363
|
+
? ", which you started"
|
|
364
|
+
: ", which the user started";
|
|
365
|
+
const actor = details.actor === "agent" ? "You" : "The user";
|
|
366
|
+
if (details.kind === "started") {
|
|
367
|
+
return `▶ ${actor} started workflow "${workflowName}" (run ${details.runId}). It is running in the background; you will be notified when it completes.`;
|
|
368
|
+
}
|
|
301
369
|
if (details.kind === "completed") {
|
|
302
|
-
return `✓ Workflow "${workflowName}" completed (run ${details.runId}). Inspect: /workflow status ${details.runId}`;
|
|
370
|
+
return `✓ Workflow "${workflowName}" completed (run ${details.runId})${origin}. Inspect: /workflow status ${details.runId}`;
|
|
303
371
|
}
|
|
304
372
|
if (details.kind === "failed") {
|
|
305
373
|
const stage = details.stageName ?? details.failedStageId ?? details.stageId;
|
|
306
374
|
const tool = lifecycleToolOrigin(details);
|
|
307
|
-
const
|
|
375
|
+
const failureSite = stage ? `, stage ${stage}` : tool !== undefined ? `, tool ${tool}` : "";
|
|
308
376
|
const errorText = details.error ? `: ${details.error}` : "";
|
|
309
|
-
return `✗ Workflow "${workflowName}" failed (run ${details.runId}${
|
|
377
|
+
return `✗ Workflow "${workflowName}" failed (run ${details.runId}${failureSite})${origin}${errorText}. Inspect: /workflow status ${details.runId}`;
|
|
310
378
|
}
|
|
311
379
|
if (details.kind === "blocked") {
|
|
312
380
|
const errorText = details.error ? `: ${details.error}` : "";
|
|
313
381
|
const stateText = details.active === true ? "is blocked" : "ended blocked";
|
|
314
|
-
return `! Workflow "${workflowName}" ${stateText} (run ${details.runId})${errorText}. Inspect: /workflow status ${details.runId}`;
|
|
382
|
+
return `! Workflow "${workflowName}" ${stateText} (run ${details.runId})${origin}${errorText}. Inspect: /workflow status ${details.runId}`;
|
|
383
|
+
}
|
|
384
|
+
if (details.kind === "paused" || details.kind === "quit") {
|
|
385
|
+
const stopStage = details.stageName ?? details.stageId;
|
|
386
|
+
// "(run 8c31), which you started, at stage review" — the stage clause takes a
|
|
387
|
+
// separating comma only when an origin clause precedes it.
|
|
388
|
+
const stageText = stopStage ? `${origin === "" ? "" : ","} at stage ${stopStage}` : "";
|
|
389
|
+
const scopeText = details.scope === "stage" ? "stage of workflow" : "workflow";
|
|
390
|
+
const verb = details.kind === "paused" ? "paused" : "quit";
|
|
391
|
+
const noun = details.kind === "paused" ? "pause" : "stop";
|
|
392
|
+
const glyph = details.kind === "paused" ? "⏸" : "⏹";
|
|
393
|
+
return `${glyph} ${actor} ${verb} the ${scopeText} "${workflowName}" (run ${details.runId})${origin}${stageText}. This ${noun} was deliberate and user-requested; do not resume it or take over the work unless asked. Resume: /workflow resume ${details.runId}`;
|
|
394
|
+
}
|
|
395
|
+
if (details.kind === "resumed") {
|
|
396
|
+
const resumedStage = details.stageName ?? details.stageId;
|
|
397
|
+
const stageText = resumedStage ? `${origin === "" ? "" : ","} at stage ${resumedStage}` : "";
|
|
398
|
+
const scopeText = details.scope === "stage" ? "stage of workflow" : "workflow";
|
|
399
|
+
const continues =
|
|
400
|
+
details.continuedFromRunId === undefined ? "" : `, continuing run ${details.continuedFromRunId}`;
|
|
401
|
+
return `▶ ${actor} resumed the ${scopeText} "${workflowName}" (run ${details.runId}${continues})${origin}${stageText}. It is running again in the background.`;
|
|
315
402
|
}
|
|
316
403
|
const prompt = details.promptMessage ? ` Prompt: ${details.promptMessage}` : "";
|
|
317
404
|
if (details.scope === "run") {
|
|
@@ -349,10 +436,42 @@ function makeTerminalNotice(
|
|
|
349
436
|
...(failedToolNodeId !== undefined ? { toolNodeId: failedToolNodeId } : {}),
|
|
350
437
|
...(failedTool !== undefined ? { toolName: failedTool.name } : {}),
|
|
351
438
|
...(run.durationMs !== undefined ? { durationMs: run.durationMs } : {}),
|
|
439
|
+
// Attribution renders on every kind, and is omitted for a run that never
|
|
440
|
+
// recorded an origin rather than guessed at.
|
|
441
|
+
...(run.origin !== undefined ? { origin: run.origin } : {}),
|
|
352
442
|
createdAt: lifecycleOccurrenceAt(run, kind) ?? Date.now(),
|
|
353
443
|
};
|
|
354
444
|
}
|
|
355
445
|
|
|
446
|
+
function makeControlNotice(run: RunSnapshot, occurrence: ControlOccurrence): WorkflowLifecycleNoticeDetails {
|
|
447
|
+
const stage = occurrence.stage ?? restingStage(run);
|
|
448
|
+
const elapsedMs = run.durationMs ?? (occurrence.at >= run.startedAt ? occurrence.at - run.startedAt : undefined);
|
|
449
|
+
return {
|
|
450
|
+
kind: occurrence.kind,
|
|
451
|
+
scope: occurrence.scope,
|
|
452
|
+
runId: run.id,
|
|
453
|
+
workflowName: run.name,
|
|
454
|
+
status: occurrence.stage !== undefined ? occurrence.stage.status : run.status,
|
|
455
|
+
...(stage !== undefined ? { stageId: stage.id, stageName: stage.name } : {}),
|
|
456
|
+
...(elapsedMs !== undefined ? { durationMs: elapsedMs } : {}),
|
|
457
|
+
...(occurrence.kind === "quit" && run.resumable !== undefined ? { resumable: run.resumable } : {}),
|
|
458
|
+
...(occurrence.actor !== undefined ? { actor: occurrence.actor } : {}),
|
|
459
|
+
// The origin clause is omitted entirely for a run that never recorded one.
|
|
460
|
+
...(occurrence.kind !== "started" && run.origin !== undefined ? { origin: run.origin } : {}),
|
|
461
|
+
...(occurrence.kind === "resumed" && run.resumedFromRunId !== undefined
|
|
462
|
+
? { continuedFromRunId: run.resumedFromRunId }
|
|
463
|
+
: {}),
|
|
464
|
+
createdAt: occurrence.at,
|
|
465
|
+
};
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
/** The stage a run currently rests on, preferring an explicitly paused one. */
|
|
469
|
+
function restingStage(run: RunSnapshot): StageSnapshot | undefined {
|
|
470
|
+
return (
|
|
471
|
+
run.stages.find((stage) => stage.status === "paused") ?? run.stages.find((stage) => stage.status === "running")
|
|
472
|
+
);
|
|
473
|
+
}
|
|
474
|
+
|
|
356
475
|
function warnLifecycleSendFailure(error: unknown): void {
|
|
357
476
|
if (process.env.ATOMIC_WORKFLOW_DEBUG !== "1") return;
|
|
358
477
|
const message = error instanceof Error ? error.message : String(error);
|
|
@@ -400,6 +519,87 @@ function terminalRunKey(kind: "completed" | "failed" | "blocked", run: RunSnapsh
|
|
|
400
519
|
return occurrence === undefined ? `${kind}:${run.id}` : `${kind}:${run.id}:${occurrence}`;
|
|
401
520
|
}
|
|
402
521
|
|
|
522
|
+
/** One deliberate control action a run currently carries. */
|
|
523
|
+
interface ControlOccurrence {
|
|
524
|
+
readonly kind: WorkflowControlNoticeKind;
|
|
525
|
+
readonly scope: "run" | "stage";
|
|
526
|
+
readonly at: number;
|
|
527
|
+
readonly actor: WorkflowActor;
|
|
528
|
+
readonly stage?: StageSnapshot;
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
/**
|
|
532
|
+
* Every control action this run currently carries that a user actually asked
|
|
533
|
+
* for.
|
|
534
|
+
*
|
|
535
|
+
* Attribution, not the status transition, is the trigger. A run passes through
|
|
536
|
+
* paused on its way to a quit, and the engine resumes a run whenever a
|
|
537
|
+
* human-in-the-loop prompt is answered or a stage control acknowledges; none of
|
|
538
|
+
* those record an actor, so none of them reach the chat. Only a control path
|
|
539
|
+
* that names its caller does, and it names exactly one scope per request, which
|
|
540
|
+
* is what keeps a stage card and a run card from reporting the same event.
|
|
541
|
+
*/
|
|
542
|
+
function controlOccurrences(run: RunSnapshot): ControlOccurrence[] {
|
|
543
|
+
const occurrences: ControlOccurrence[] = [];
|
|
544
|
+
// Whether this run exists because someone resumed work, whoever asked for it.
|
|
545
|
+
// A durable resume reuses the original run id (issue #1498) and a failed-run
|
|
546
|
+
// continuation takes a fresh one; both re-enter `recordRunStart`, and neither
|
|
547
|
+
// is a new run to the user. Keying the start suppression on the resume itself
|
|
548
|
+
// rather than on who requested it is what keeps an agent-requested resume of a
|
|
549
|
+
// user-started run from being announced as a fresh launch.
|
|
550
|
+
const resumedRun = run.resumeSource === "run_control" || run.resumedFromRunId !== undefined;
|
|
551
|
+
const userResumed = run.resumeSource === "run_control" && run.resumeActor === "user";
|
|
552
|
+
// A resumed run that never paused in this session reports its start timestamp:
|
|
553
|
+
// the re-dispatch is the occurrence.
|
|
554
|
+
const continuation = userResumed && run.resumedAt === undefined;
|
|
555
|
+
if (run.origin === "user" && !resumedRun) {
|
|
556
|
+
occurrences.push({ kind: "started", scope: "run", at: run.startedAt, actor: "user" });
|
|
557
|
+
}
|
|
558
|
+
if (userResumed && run.status === "running") {
|
|
559
|
+
occurrences.push({
|
|
560
|
+
kind: "resumed",
|
|
561
|
+
scope: "run",
|
|
562
|
+
at: continuation ? run.startedAt : (run.resumedAt ?? run.startedAt),
|
|
563
|
+
actor: "user",
|
|
564
|
+
});
|
|
565
|
+
}
|
|
566
|
+
if (run.endedAt === undefined && run.status === "paused" && run.pauseActor === "user") {
|
|
567
|
+
if (run.exitReason === "quit") {
|
|
568
|
+
const quitAt = run.quitAt ?? run.pausedAt;
|
|
569
|
+
if (quitAt !== undefined) occurrences.push({ kind: "quit", scope: "run", at: quitAt, actor: "user" });
|
|
570
|
+
} else if (run.pausedAt !== undefined) {
|
|
571
|
+
occurrences.push({ kind: "paused", scope: "run", at: run.pausedAt, actor: "user" });
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
const runScoped = new Set(occurrences.map((occurrence) => occurrence.kind));
|
|
575
|
+
if (run.endedAt !== undefined) return occurrences;
|
|
576
|
+
for (const stage of run.stages) {
|
|
577
|
+
if (stage.status === "paused" && stage.pauseActor === "user" && stage.pausedAt !== undefined) {
|
|
578
|
+
if (!runScoped.has("paused") && !runScoped.has("quit")) {
|
|
579
|
+
occurrences.push({ kind: "paused", scope: "stage", at: stage.pausedAt, actor: "user", stage });
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
if (stage.status === "running" && stage.resumeActor === "user" && stage.resumedAt !== undefined) {
|
|
583
|
+
if (!runScoped.has("resumed")) {
|
|
584
|
+
occurrences.push({ kind: "resumed", scope: "stage", at: stage.resumedAt, actor: "user", stage });
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
return occurrences;
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
/**
|
|
592
|
+
* Control actions are repeatable, so the occurrence timestamp is part of the
|
|
593
|
+
* key: resuming clears `pausedAt`/`quitAt`, and the next pause mints a new key
|
|
594
|
+
* while repeated invalidations at one unchanged state reuse the delivered one.
|
|
595
|
+
*/
|
|
596
|
+
function controlOccurrenceKey(run: RunSnapshot, occurrence: ControlOccurrence): string {
|
|
597
|
+
if (occurrence.scope === "stage" && occurrence.stage !== undefined) {
|
|
598
|
+
return `${occurrence.kind}:${run.id}:stage:${occurrence.stage.id}:${occurrence.at}`;
|
|
599
|
+
}
|
|
600
|
+
return `${occurrence.kind}:${run.id}:${occurrence.at}`;
|
|
601
|
+
}
|
|
602
|
+
|
|
403
603
|
function awaitingInputKey(runId: string, stage: StageSnapshot): string {
|
|
404
604
|
const promptId = stage.pendingPrompt?.id ?? stage.inputRequest?.id;
|
|
405
605
|
if (promptId) return `awaiting_input:${runId}:stage:${stage.id}:${promptId}`;
|
|
@@ -439,38 +639,12 @@ function renderLifecycleNoticeCard(
|
|
|
439
639
|
details: WorkflowLifecycleNoticeDetails,
|
|
440
640
|
opts: { width: number; theme?: GraphTheme; fallbackText: string },
|
|
441
641
|
): string[] {
|
|
442
|
-
const tone: WorkflowNoticeTone =
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
: details.kind === "awaiting_input" || details.kind === "blocked"
|
|
446
|
-
? "warning"
|
|
447
|
-
: "success";
|
|
448
|
-
const title =
|
|
449
|
-
details.kind === "failed"
|
|
450
|
-
? "WORKFLOW FAILED"
|
|
451
|
-
: details.kind === "awaiting_input"
|
|
452
|
-
? "WORKFLOW INPUT"
|
|
453
|
-
: details.kind === "blocked"
|
|
454
|
-
? "WORKFLOW BLOCKED"
|
|
455
|
-
: "WORKFLOW COMPLETE";
|
|
456
|
-
const glyph =
|
|
457
|
-
details.kind === "failed"
|
|
458
|
-
? "✗"
|
|
459
|
-
: details.kind === "awaiting_input"
|
|
460
|
-
? "?"
|
|
461
|
-
: details.kind === "blocked"
|
|
462
|
-
? "!"
|
|
463
|
-
: "✓";
|
|
642
|
+
const tone: WorkflowNoticeTone = lifecycleNoticeTone(details.kind);
|
|
643
|
+
const title = lifecycleNoticeTitle(details.kind);
|
|
644
|
+
const glyph = lifecycleNoticeGlyph(details.kind);
|
|
464
645
|
const stage = details.stageName ?? details.failedStageId ?? details.stageId;
|
|
465
646
|
const tool = stage === undefined ? lifecycleToolOrigin(details) : undefined;
|
|
466
|
-
const headline =
|
|
467
|
-
details.kind === "failed"
|
|
468
|
-
? `Workflow "${details.workflowName}" failed`
|
|
469
|
-
: details.kind === "awaiting_input"
|
|
470
|
-
? `Workflow "${details.workflowName}" needs input`
|
|
471
|
-
: details.kind === "blocked"
|
|
472
|
-
? `Workflow "${details.workflowName}" ${details.active === true ? "is blocked" : "ended blocked"}`
|
|
473
|
-
: `Workflow "${details.workflowName}" completed`;
|
|
647
|
+
const headline = lifecycleNoticeHeadline(details);
|
|
474
648
|
return renderWorkflowNoticeCard({
|
|
475
649
|
title,
|
|
476
650
|
glyph,
|
|
@@ -479,21 +653,112 @@ function renderLifecycleNoticeCard(
|
|
|
479
653
|
fields: [
|
|
480
654
|
{ label: "workflow", value: details.workflowName },
|
|
481
655
|
{ label: "run", value: details.runId },
|
|
656
|
+
{ label: "continues", value: details.continuedFromRunId },
|
|
482
657
|
{ label: "stage", value: stage },
|
|
483
658
|
{ label: "tool", value: tool },
|
|
659
|
+
{ label: "actor", value: details.actor, tone: "muted" },
|
|
660
|
+
{ label: "launched", value: details.origin, tone: "muted" },
|
|
484
661
|
{ label: "prompt", value: details.promptMessage, tone: "muted" },
|
|
485
662
|
{ label: "error", value: details.error, tone: "error" },
|
|
663
|
+
{ label: "resumable", value: resumableFieldValue(details), tone: "muted" },
|
|
486
664
|
{ label: "duration", value: formatDurationMs(details.durationMs), tone: "muted" },
|
|
487
665
|
],
|
|
488
|
-
hints: [
|
|
489
|
-
details.kind === "awaiting_input" ? `/workflow connect ${details.runId}` : `/workflow status ${details.runId}`,
|
|
490
|
-
],
|
|
666
|
+
hints: [lifecycleNoticeHint(details)],
|
|
491
667
|
fallbackText: opts.fallbackText,
|
|
492
668
|
width: opts.width,
|
|
493
669
|
...(opts.theme ? { theme: opts.theme } : {}),
|
|
494
670
|
});
|
|
495
671
|
}
|
|
496
672
|
|
|
673
|
+
function lifecycleNoticeTone(kind: WorkflowLifecycleNoticeKind): WorkflowNoticeTone {
|
|
674
|
+
switch (kind) {
|
|
675
|
+
case "failed":
|
|
676
|
+
return "error";
|
|
677
|
+
case "blocked":
|
|
678
|
+
case "awaiting_input":
|
|
679
|
+
case "paused":
|
|
680
|
+
case "quit":
|
|
681
|
+
return "warning";
|
|
682
|
+
default:
|
|
683
|
+
return "success";
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
function lifecycleNoticeTitle(kind: WorkflowLifecycleNoticeKind): string {
|
|
688
|
+
switch (kind) {
|
|
689
|
+
case "started":
|
|
690
|
+
return "WORKFLOW STARTED";
|
|
691
|
+
case "failed":
|
|
692
|
+
return "WORKFLOW FAILED";
|
|
693
|
+
case "awaiting_input":
|
|
694
|
+
return "WORKFLOW INPUT";
|
|
695
|
+
case "blocked":
|
|
696
|
+
return "WORKFLOW BLOCKED";
|
|
697
|
+
case "paused":
|
|
698
|
+
return "WORKFLOW PAUSED";
|
|
699
|
+
case "quit":
|
|
700
|
+
return "WORKFLOW QUIT";
|
|
701
|
+
case "resumed":
|
|
702
|
+
return "WORKFLOW RESUMED";
|
|
703
|
+
default:
|
|
704
|
+
return "WORKFLOW COMPLETE";
|
|
705
|
+
}
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
function lifecycleNoticeGlyph(kind: WorkflowLifecycleNoticeKind): string {
|
|
709
|
+
switch (kind) {
|
|
710
|
+
case "failed":
|
|
711
|
+
return "✗";
|
|
712
|
+
case "awaiting_input":
|
|
713
|
+
return "?";
|
|
714
|
+
case "blocked":
|
|
715
|
+
return "!";
|
|
716
|
+
case "paused":
|
|
717
|
+
return "⏸";
|
|
718
|
+
case "quit":
|
|
719
|
+
return "⏹";
|
|
720
|
+
case "started":
|
|
721
|
+
case "resumed":
|
|
722
|
+
return "▶";
|
|
723
|
+
default:
|
|
724
|
+
return "✓";
|
|
725
|
+
}
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
function lifecycleNoticeHeadline(details: WorkflowLifecycleNoticeDetails): string {
|
|
729
|
+
const name = details.workflowName;
|
|
730
|
+
const scope = details.scope === "stage" ? "Stage of workflow" : "Workflow";
|
|
731
|
+
switch (details.kind) {
|
|
732
|
+
case "started":
|
|
733
|
+
return `Workflow "${name}" started`;
|
|
734
|
+
case "failed":
|
|
735
|
+
return `Workflow "${name}" failed`;
|
|
736
|
+
case "awaiting_input":
|
|
737
|
+
return `Workflow "${name}" needs input`;
|
|
738
|
+
case "blocked":
|
|
739
|
+
return `Workflow "${name}" ${details.active === true ? "is blocked" : "ended blocked"}`;
|
|
740
|
+
case "paused":
|
|
741
|
+
return `${scope} "${name}" paused`;
|
|
742
|
+
case "quit":
|
|
743
|
+
return `Workflow "${name}" quit`;
|
|
744
|
+
case "resumed":
|
|
745
|
+
return `${scope} "${name}" resumed`;
|
|
746
|
+
default:
|
|
747
|
+
return `Workflow "${name}" completed`;
|
|
748
|
+
}
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
function lifecycleNoticeHint(details: WorkflowLifecycleNoticeDetails): string {
|
|
752
|
+
if (details.kind === "awaiting_input") return `/workflow connect ${details.runId}`;
|
|
753
|
+
if (details.kind === "paused" || details.kind === "quit") return `/workflow resume ${details.runId}`;
|
|
754
|
+
return `/workflow status ${details.runId}`;
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
function resumableFieldValue(details: WorkflowLifecycleNoticeDetails): string | undefined {
|
|
758
|
+
if (details.resumable === undefined) return undefined;
|
|
759
|
+
return details.resumable ? "yes" : "no";
|
|
760
|
+
}
|
|
761
|
+
|
|
497
762
|
function formatDurationMs(durationMs: number | undefined): string | undefined {
|
|
498
763
|
if (durationMs === undefined) return undefined;
|
|
499
764
|
if (durationMs < 1000) return `${durationMs}ms`;
|
|
@@ -32,6 +32,7 @@ import { truncateToWidth } from "../tui/text-helpers.js";
|
|
|
32
32
|
import { renderWorkflowList } from "../tui/workflow-list.js";
|
|
33
33
|
import type { WorkflowReloadReport } from "./workflow-reload-report.js";
|
|
34
34
|
import type { WorkflowRunStatusFilter, WorkflowRunStatusSummary } from "./workflow-status-summary.js";
|
|
35
|
+
import { getWorkflowStatusRenderRuns } from "./workflow-status-summary.js";
|
|
35
36
|
|
|
36
37
|
// ---------------------------------------------------------------------------
|
|
37
38
|
// Result variants
|
|
@@ -240,6 +241,8 @@ export interface RenderResultOpts {
|
|
|
240
241
|
* not implement synchronized output (e.g. mosh).
|
|
241
242
|
*/
|
|
242
243
|
now?: number;
|
|
244
|
+
/** Point-in-time full run collection used only to resolve hidden nested indicators. */
|
|
245
|
+
allRuns?: readonly RunSnapshot[];
|
|
243
246
|
}
|
|
244
247
|
|
|
245
248
|
/**
|
|
@@ -335,6 +338,7 @@ export function renderResult(result: WorkflowToolResult, opts?: RenderResultOpts
|
|
|
335
338
|
theme: themed ? deriveGraphTheme({}) : undefined,
|
|
336
339
|
width: opts?.width,
|
|
337
340
|
now: opts?.now,
|
|
341
|
+
allRuns: opts?.allRuns ?? getWorkflowStatusRenderRuns(r) ?? r.snapshots,
|
|
338
342
|
});
|
|
339
343
|
}
|
|
340
344
|
|
|
@@ -18,7 +18,7 @@ import type { JobTracker } from "../runs/background/job-tracker.js";
|
|
|
18
18
|
import type { RunOpts } from "../runs/foreground/executor.js";
|
|
19
19
|
import type { StageAdapters } from "../runs/foreground/stage-runner.js";
|
|
20
20
|
import type { Store } from "../shared/store.js";
|
|
21
|
-
import type { RunSnapshot } from "../shared/store-types.js";
|
|
21
|
+
import type { RunSnapshot, WorkflowActor } from "../shared/store-types.js";
|
|
22
22
|
import type { WorkflowExecutionPolicy } from "../shared/types.js";
|
|
23
23
|
import type { WorkflowRegistry } from "../workflows/registry.js";
|
|
24
24
|
import { discoverWorkflows } from "./discovery.js";
|
|
@@ -26,7 +26,7 @@ import { discoverWorkflows } from "./discovery.js";
|
|
|
26
26
|
export interface DurableResumeRuntime {
|
|
27
27
|
resumeDurableWorkflow(
|
|
28
28
|
workflowId: string,
|
|
29
|
-
options?: { readonly policy?: WorkflowExecutionPolicy },
|
|
29
|
+
options?: { readonly policy?: WorkflowExecutionPolicy; readonly actor?: WorkflowActor },
|
|
30
30
|
): Promise<ResumeDurableResult>;
|
|
31
31
|
listDurableResumable(): readonly ResumableWorkflowEntry[];
|
|
32
32
|
prepareDurableResumable(workflowId?: string): Promise<readonly ResumableWorkflowEntry[]>;
|
|
@@ -75,7 +75,10 @@ export function createDurableResumeRuntime(deps: DurableResumeRuntimeDeps): Dura
|
|
|
75
75
|
if (resolved !== undefined) await backend.hydrateWorkflow(resolved.workflowId);
|
|
76
76
|
const adapterDeps: ResumeDurableDeps = {
|
|
77
77
|
registry: deps.registry,
|
|
78
|
-
baseRunOpts:
|
|
78
|
+
baseRunOpts: {
|
|
79
|
+
...deps.baseRunOpts(options?.policy),
|
|
80
|
+
...(options?.actor === undefined ? {} : { resumeActor: options.actor }),
|
|
81
|
+
},
|
|
79
82
|
durableBackend: backend,
|
|
80
83
|
resolveDefinition: async (name, cwd) =>
|
|
81
84
|
(await discoverWorkflows({ cwd: cwd ?? deps.runtimeCwd })).registry.get(name),
|
|
@@ -18,7 +18,7 @@ import { type RunOpts, resolveAndValidateInputs } from "../runs/foreground/execu
|
|
|
18
18
|
import type { StageAdapters } from "../runs/foreground/stage-runner.js";
|
|
19
19
|
import type { Store } from "../shared/store.js";
|
|
20
20
|
import { store as defaultStore } from "../shared/store.js";
|
|
21
|
-
import type { RunSnapshot } from "../shared/store-types.js";
|
|
21
|
+
import type { RunSnapshot, WorkflowActor } from "../shared/store-types.js";
|
|
22
22
|
import type {
|
|
23
23
|
WorkflowDefinition,
|
|
24
24
|
WorkflowExecutionPolicy,
|
|
@@ -115,6 +115,10 @@ export interface ExtensionRuntime extends DurableResumeRuntime {
|
|
|
115
115
|
}
|
|
116
116
|
export interface RuntimeDispatchOptions {
|
|
117
117
|
readonly policy?: WorkflowExecutionPolicy;
|
|
118
|
+
/** Who launched this run. Only an attributable launcher supplies it. */
|
|
119
|
+
readonly origin?: WorkflowActor;
|
|
120
|
+
/** Who requested this resume. Only an attributable requester supplies it. */
|
|
121
|
+
readonly actor?: WorkflowActor;
|
|
118
122
|
}
|
|
119
123
|
// ---------------------------------------------------------------------------
|
|
120
124
|
// Factory
|
|
@@ -266,6 +270,7 @@ export function createExtensionRuntime(opts: ExtensionRuntimeOpts = {}): Extensi
|
|
|
266
270
|
launchDetachedUntilStartup(def, sourceInputs, {
|
|
267
271
|
...runOptions(options?.policy),
|
|
268
272
|
continuation: { source, resumeFromStageId: resolvedStage.stageId },
|
|
273
|
+
...(options?.actor === undefined ? {} : { resumeActor: options.actor }),
|
|
269
274
|
...(jobs !== undefined ? { jobs } : {}),
|
|
270
275
|
});
|
|
271
276
|
if (isActiveBlockedResumable) {
|
|
@@ -393,6 +398,7 @@ export function createExtensionRuntime(opts: ExtensionRuntimeOpts = {}): Extensi
|
|
|
393
398
|
config,
|
|
394
399
|
models,
|
|
395
400
|
policy: options?.policy,
|
|
401
|
+
...(options?.origin === undefined ? {} : { origin: options.origin }),
|
|
396
402
|
cwd: runtimeCwd,
|
|
397
403
|
...(defaultSessionDir !== undefined ? { defaultSessionDir } : {}),
|
|
398
404
|
});
|
|
@@ -134,22 +134,64 @@ function isTestContext(): boolean {
|
|
|
134
134
|
return process.env.NODE_TEST_CONTEXT !== undefined || process.env.NODE_ENV === "test";
|
|
135
135
|
}
|
|
136
136
|
|
|
137
|
+
type StageSettingsManager = ReturnType<PiCodingAgentSdk["SettingsManager"]["create"]>;
|
|
138
|
+
|
|
139
|
+
function attachSettingsManager(error: unknown, settingsManager: StageSettingsManager): unknown {
|
|
140
|
+
if (error !== null && (typeof error === "object" || typeof error === "function")) {
|
|
141
|
+
try {
|
|
142
|
+
if (Object.isExtensible(error)) {
|
|
143
|
+
Object.defineProperty(error, "settingsManager", {
|
|
144
|
+
configurable: true,
|
|
145
|
+
enumerable: false,
|
|
146
|
+
value: settingsManager,
|
|
147
|
+
writable: false,
|
|
148
|
+
});
|
|
149
|
+
return error;
|
|
150
|
+
}
|
|
151
|
+
} catch {
|
|
152
|
+
// Frozen or proxy errors cannot carry the private retry hint. Wrap them
|
|
153
|
+
// without replacing the original failure as the cause.
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
const wrapped = new Error(error instanceof Error ? error.message : String(error), { cause: error });
|
|
157
|
+
Object.defineProperty(wrapped, "settingsManager", {
|
|
158
|
+
configurable: true,
|
|
159
|
+
enumerable: false,
|
|
160
|
+
value: settingsManager,
|
|
161
|
+
writable: false,
|
|
162
|
+
});
|
|
163
|
+
return wrapped;
|
|
164
|
+
}
|
|
165
|
+
|
|
137
166
|
async function createPiSdkAgentSession(
|
|
138
167
|
options?: CreateAgentSessionOptions,
|
|
139
168
|
prepareOptions?: PrepareAtomicStageSessionOptions,
|
|
140
169
|
): Promise<StageSessionCreateResult> {
|
|
141
170
|
const sdk = (await import("@bastani/atomic")) as PiCodingAgentSdk;
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
171
|
+
let settingsManager: ReturnType<PiCodingAgentSdk["SettingsManager"]["create"]> | undefined;
|
|
172
|
+
try {
|
|
173
|
+
const sessionOptions = await prepareAtomicStageSessionOptions(options, sdk, {
|
|
174
|
+
...prepareOptions,
|
|
175
|
+
onSettingsManager: (manager) => {
|
|
176
|
+
settingsManager = manager;
|
|
177
|
+
prepareOptions?.onSettingsManager?.(manager);
|
|
178
|
+
},
|
|
179
|
+
});
|
|
180
|
+
settingsManager = sessionOptions?.settingsManager ?? settingsManager;
|
|
181
|
+
const result = await sdk.createAgentSession(sessionOptions);
|
|
182
|
+
// `CreateAgentSessionResult` is `{ session, extensionsResult, modelFallbackMessage? }`;
|
|
183
|
+
// workflow stages only consume `.session` (structurally an `AgentSession`,
|
|
184
|
+
// which is a superset of our `StageSessionRuntime` projection).
|
|
185
|
+
const resultSettingsManager = result.session.settingsManager;
|
|
186
|
+
settingsManager = sessionOptions?.settingsManager ?? resultSettingsManager ?? settingsManager;
|
|
187
|
+
return {
|
|
188
|
+
session: result.session,
|
|
189
|
+
...(settingsManager?.getCodexFastModeSettings !== undefined ? { settingsManager } : {}),
|
|
190
|
+
};
|
|
191
|
+
} catch (error) {
|
|
192
|
+
if (settingsManager !== undefined) throw attachSettingsManager(error, settingsManager);
|
|
193
|
+
throw error;
|
|
194
|
+
}
|
|
153
195
|
}
|
|
154
196
|
|
|
155
197
|
async function createTestAgentSession(_options?: CreateAgentSessionOptions): Promise<StageSessionCreateResult> {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { inspectRun } from "../runs/background/status.js";
|
|
2
2
|
import { renderInputsSchema } from "../shared/render-inputs-schema.js";
|
|
3
|
+
import { resolveRunIndicatorStatuses } from "../shared/run-indicator-status.js";
|
|
3
4
|
import { schemaIsRequired } from "../shared/schema-introspection.js";
|
|
4
5
|
import { store } from "../shared/store.js";
|
|
5
6
|
import type { WorkflowExecutionPolicy } from "../shared/types.js";
|
|
@@ -158,8 +159,14 @@ async function workflowSlashHandler(
|
|
|
158
159
|
emitChatSurface(pi, { kind: "detail", detail: inspected.detail });
|
|
159
160
|
return;
|
|
160
161
|
}
|
|
161
|
-
const
|
|
162
|
-
|
|
162
|
+
const capturedRuns = store.graphSnapshot().runs;
|
|
163
|
+
const rows = selectRunsForPicker(capturedRuns, "", true, Date.now());
|
|
164
|
+
const visibleRuns = rows.map((r) => r.run);
|
|
165
|
+
emitChatSurface(pi, {
|
|
166
|
+
kind: "status",
|
|
167
|
+
runs: visibleRuns,
|
|
168
|
+
indicatorStatuses: resolveRunIndicatorStatuses(visibleRuns, capturedRuns),
|
|
169
|
+
});
|
|
163
170
|
return;
|
|
164
171
|
}
|
|
165
172
|
if (subcommand === "reload") {
|
|
@@ -250,7 +257,9 @@ async function workflowSlashHandler(
|
|
|
250
257
|
|
|
251
258
|
await ensureWorkflowResourcesVisible();
|
|
252
259
|
const result = await deps.runWithLifecycleSuppressedForPolicy(policy, () =>
|
|
253
|
-
deps
|
|
260
|
+
deps
|
|
261
|
+
.runtimeForContext(ctx)
|
|
262
|
+
.dispatch({ workflow: workflowName, inputs: mergedInputs, action: "run" }, { policy, origin: "user" }),
|
|
254
263
|
);
|
|
255
264
|
if (result.action !== "run" || !("runId" in result)) return;
|
|
256
265
|
const runResult = result as Extract<WorkflowToolResult, { action: "run"; runId: string }>;
|
|
@@ -121,7 +121,7 @@ export async function handleDurableResume(
|
|
|
121
121
|
fail(completedAttempt.message);
|
|
122
122
|
return true;
|
|
123
123
|
}
|
|
124
|
-
const result = await runtime.resumeDurableWorkflow(target, { policy });
|
|
124
|
+
const result = await runtime.resumeDurableWorkflow(target, { policy, actor: "user" });
|
|
125
125
|
fail(
|
|
126
126
|
allOpenable.length === 0 ? result.message : `${result.message}\n\n${formatResumableWorkflowList(allOpenable)}`,
|
|
127
127
|
);
|
|
@@ -216,7 +216,10 @@ async function resumeDurableTarget(
|
|
|
216
216
|
deps: WorkflowRunControlDeps,
|
|
217
217
|
runtime: ExtensionRuntime,
|
|
218
218
|
): Promise<boolean> {
|
|
219
|
-
const result = await runtime.resumeDurableWorkflow(workflowId, {
|
|
219
|
+
const result = await runtime.resumeDurableWorkflow(workflowId, {
|
|
220
|
+
policy: workflowPolicyFromContext(ctx),
|
|
221
|
+
actor: "user",
|
|
222
|
+
});
|
|
220
223
|
if (!result.ok) reporter.error(result.message);
|
|
221
224
|
else {
|
|
222
225
|
reporter.info(result.message);
|