@bastani/atomic 0.8.4-0 → 0.8.5-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/CHANGELOG.md +16 -0
- package/README.md +24 -23
- package/dist/builtin/intercom/README.md +5 -5
- package/dist/builtin/intercom/index.ts +1 -1
- package/dist/builtin/intercom/package.json +1 -1
- package/dist/builtin/intercom/ui/compose.ts +19 -1
- package/dist/builtin/intercom/ui/session-list.ts +19 -1
- package/dist/builtin/mcp/README.md +3 -3
- package/dist/builtin/mcp/commands.ts +1 -1
- package/dist/builtin/mcp/host-html-template.ts +1 -1
- package/dist/builtin/mcp/mcp-panel.ts +14 -14
- package/dist/builtin/mcp/mcp-setup-panel.ts +4 -4
- package/dist/builtin/mcp/package.json +1 -1
- package/dist/builtin/mcp/tool-result-renderer.ts +1 -1
- package/dist/builtin/subagents/README.md +3 -3
- package/dist/builtin/subagents/package.json +1 -1
- package/dist/builtin/subagents/src/tui/render.ts +1844 -1062
- package/dist/builtin/web-access/README.md +1 -1
- package/dist/builtin/web-access/curator-page.ts +2 -2
- package/dist/builtin/web-access/index.ts +1 -1
- package/dist/builtin/web-access/package.json +1 -1
- package/dist/builtin/workflows/README.md +34 -7
- package/dist/builtin/workflows/builtin/deep-research-codebase.ts +23 -4
- package/dist/builtin/workflows/builtin/ralph.ts +1 -1
- package/dist/builtin/workflows/package.json +1 -1
- package/dist/builtin/workflows/skills/workflow/SKILL.md +75 -16
- package/dist/builtin/workflows/skills/workflow/references/running-workflows.md +34 -11
- package/dist/builtin/workflows/skills/workflow/references/sdk-authoring.md +111 -20
- package/dist/builtin/workflows/src/extension/discovery.ts +32 -4
- package/dist/builtin/workflows/src/extension/index.ts +347 -63
- package/dist/builtin/workflows/src/extension/render-call.ts +3 -1
- package/dist/builtin/workflows/src/extension/render-result.ts +7 -0
- package/dist/builtin/workflows/src/extension/runtime.ts +4 -2
- package/dist/builtin/workflows/src/extension/wiring.ts +32 -8
- package/dist/builtin/workflows/src/extension/workflow-schema.ts +36 -14
- package/dist/builtin/workflows/src/runs/background/runner.ts +2 -2
- package/dist/builtin/workflows/src/runs/background/status.ts +89 -0
- package/dist/builtin/workflows/src/runs/foreground/executor.ts +338 -78
- package/dist/builtin/workflows/src/runs/foreground/stage-control-registry.ts +2 -0
- package/dist/builtin/workflows/src/runs/foreground/stage-runner.ts +55 -7
- package/dist/builtin/workflows/src/runs/shared/workflow-runner.ts +146 -10
- package/dist/builtin/workflows/src/shared/store.ts +29 -0
- package/dist/builtin/workflows/src/shared/types.ts +25 -4
- package/dist/builtin/workflows/src/tui/graph-canvas.ts +69 -2
- package/dist/builtin/workflows/src/tui/graph-view.ts +97 -182
- package/dist/builtin/workflows/src/tui/header.ts +36 -20
- package/dist/builtin/workflows/src/tui/inline-form-card.ts +129 -46
- package/dist/builtin/workflows/src/tui/inline-form-editor.ts +111 -36
- package/dist/builtin/workflows/src/tui/inputs-picker.ts +311 -91
- package/dist/builtin/workflows/src/tui/layout.ts +1 -1
- package/dist/builtin/workflows/src/tui/node-card.ts +66 -37
- package/dist/builtin/workflows/src/tui/overlay-adapter.ts +20 -6
- package/dist/builtin/workflows/src/tui/prompt-card.ts +262 -85
- package/dist/builtin/workflows/src/tui/run-detail.ts +50 -31
- package/dist/builtin/workflows/src/tui/session-confirm.ts +21 -14
- package/dist/builtin/workflows/src/tui/session-picker.ts +35 -26
- package/dist/builtin/workflows/src/tui/stage-chat-view.ts +531 -960
- package/dist/builtin/workflows/src/tui/status-helpers.ts +6 -0
- package/dist/builtin/workflows/src/tui/status-list.ts +8 -4
- package/dist/builtin/workflows/src/tui/store-widget-installer.ts +7 -2
- package/dist/builtin/workflows/src/tui/switcher.ts +55 -25
- package/dist/builtin/workflows/src/tui/workflow-attach-pane.ts +33 -1
- package/dist/builtin/workflows/src/tui/workflow-list.ts +10 -6
- package/dist/cli/args.d.ts.map +1 -1
- package/dist/cli/args.js +1 -1
- package/dist/cli/args.js.map +1 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +20 -6
- package/dist/config.js.map +1 -1
- package/dist/core/agent-session-services.d.ts +3 -3
- package/dist/core/agent-session-services.d.ts.map +1 -1
- package/dist/core/agent-session-services.js.map +1 -1
- package/dist/core/agent-session.d.ts +7 -7
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/compaction/branch-summarization.d.ts +2 -2
- package/dist/core/compaction/branch-summarization.d.ts.map +1 -1
- package/dist/core/compaction/branch-summarization.js.map +1 -1
- package/dist/core/compaction/compaction.d.ts +3 -3
- package/dist/core/compaction/compaction.d.ts.map +1 -1
- package/dist/core/compaction/compaction.js.map +1 -1
- package/dist/core/export-html/tool-renderer.d.ts.map +1 -1
- package/dist/core/export-html/tool-renderer.js.map +1 -1
- package/dist/core/extensions/loader.d.ts +3 -2
- package/dist/core/extensions/loader.d.ts.map +1 -1
- package/dist/core/extensions/loader.js +24 -12
- package/dist/core/extensions/loader.js.map +1 -1
- package/dist/core/extensions/runner.d.ts.map +1 -1
- package/dist/core/extensions/runner.js +6 -0
- package/dist/core/extensions/runner.js.map +1 -1
- package/dist/core/extensions/types.d.ts +28 -17
- package/dist/core/extensions/types.d.ts.map +1 -1
- package/dist/core/extensions/types.js.map +1 -1
- package/dist/core/package-manager.d.ts +1 -0
- package/dist/core/package-manager.d.ts.map +1 -1
- package/dist/core/package-manager.js +65 -28
- package/dist/core/package-manager.js.map +1 -1
- package/dist/core/resource-loader.d.ts.map +1 -1
- package/dist/core/resource-loader.js +13 -5
- package/dist/core/resource-loader.js.map +1 -1
- package/dist/core/sdk.d.ts +3 -3
- package/dist/core/sdk.d.ts.map +1 -1
- package/dist/core/sdk.js.map +1 -1
- package/dist/core/session-manager.d.ts.map +1 -1
- package/dist/core/session-manager.js +1 -1
- package/dist/core/session-manager.js.map +1 -1
- package/dist/core/settings-manager.d.ts +2 -0
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/core/slash-commands.d.ts.map +1 -1
- package/dist/core/slash-commands.js +1 -1
- package/dist/core/slash-commands.js.map +1 -1
- package/dist/core/system-prompt.d.ts.map +1 -1
- package/dist/core/system-prompt.js +5 -3
- package/dist/core/system-prompt.js.map +1 -1
- package/dist/core/tools/ask-user-question/view/components/preview/preview-block-renderer.d.ts +1 -1
- package/dist/core/tools/ask-user-question/view/components/preview/preview-block-renderer.d.ts.map +1 -1
- package/dist/core/tools/ask-user-question/view/components/preview/preview-block-renderer.js +1 -1
- package/dist/core/tools/ask-user-question/view/components/preview/preview-block-renderer.js.map +1 -1
- package/dist/core/tools/ask-user-question/view/dialog-builder.d.ts +8 -8
- package/dist/core/tools/ask-user-question/view/dialog-builder.d.ts.map +1 -1
- package/dist/core/tools/ask-user-question/view/dialog-builder.js +6 -6
- package/dist/core/tools/ask-user-question/view/dialog-builder.js.map +1 -1
- package/dist/core/tools/bash.d.ts.map +1 -1
- package/dist/core/tools/bash.js +1 -1
- package/dist/core/tools/bash.js.map +1 -1
- package/dist/core/tools/find.d.ts.map +1 -1
- package/dist/core/tools/find.js +1 -1
- package/dist/core/tools/find.js.map +1 -1
- package/dist/core/tools/grep.d.ts.map +1 -1
- package/dist/core/tools/grep.js +7 -4
- package/dist/core/tools/grep.js.map +1 -1
- package/dist/core/tools/index.d.ts +3 -2
- package/dist/core/tools/index.d.ts.map +1 -1
- package/dist/core/tools/index.js.map +1 -1
- package/dist/core/tools/ls.d.ts.map +1 -1
- package/dist/core/tools/ls.js +3 -2
- package/dist/core/tools/ls.js.map +1 -1
- package/dist/core/tools/read.d.ts.map +1 -1
- package/dist/core/tools/read.js +2 -2
- package/dist/core/tools/read.js.map +1 -1
- package/dist/core/tools/render-utils.d.ts +2 -1
- package/dist/core/tools/render-utils.d.ts.map +1 -1
- package/dist/core/tools/render-utils.js.map +1 -1
- package/dist/core/tools/todos.d.ts.map +1 -1
- package/dist/core/tools/todos.js +1 -1
- package/dist/core/tools/todos.js.map +1 -1
- package/dist/core/tools/tool-definition-wrapper.d.ts +4 -3
- package/dist/core/tools/tool-definition-wrapper.d.ts.map +1 -1
- package/dist/core/tools/tool-definition-wrapper.js.map +1 -1
- package/dist/core/tools/write.d.ts.map +1 -1
- package/dist/core/tools/write.js +1 -1
- package/dist/core/tools/write.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +2 -2
- package/dist/main.js.map +1 -1
- package/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/assistant-message.js +3 -3
- package/dist/modes/interactive/components/assistant-message.js.map +1 -1
- package/dist/modes/interactive/components/bash-execution.d.ts.map +1 -1
- package/dist/modes/interactive/components/bash-execution.js +3 -3
- package/dist/modes/interactive/components/bash-execution.js.map +1 -1
- package/dist/modes/interactive/components/branch-summary-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/branch-summary-message.js +1 -1
- package/dist/modes/interactive/components/branch-summary-message.js.map +1 -1
- package/dist/modes/interactive/components/chat-message-renderer.d.ts +2 -1
- package/dist/modes/interactive/components/chat-message-renderer.d.ts.map +1 -1
- package/dist/modes/interactive/components/chat-message-renderer.js.map +1 -1
- package/dist/modes/interactive/components/compaction-summary-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/compaction-summary-message.js +1 -1
- package/dist/modes/interactive/components/compaction-summary-message.js.map +1 -1
- package/dist/modes/interactive/components/config-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/config-selector.js +1 -1
- package/dist/modes/interactive/components/config-selector.js.map +1 -1
- package/dist/modes/interactive/components/custom-editor.d.ts +3 -0
- package/dist/modes/interactive/components/custom-editor.d.ts.map +1 -1
- package/dist/modes/interactive/components/custom-editor.js +13 -3
- package/dist/modes/interactive/components/custom-editor.js.map +1 -1
- package/dist/modes/interactive/components/footer.d.ts.map +1 -1
- package/dist/modes/interactive/components/footer.js +1 -1
- package/dist/modes/interactive/components/footer.js.map +1 -1
- package/dist/modes/interactive/components/index.d.ts +2 -1
- package/dist/modes/interactive/components/index.d.ts.map +1 -1
- package/dist/modes/interactive/components/index.js +2 -1
- package/dist/modes/interactive/components/index.js.map +1 -1
- package/dist/modes/interactive/components/keybinding-hints.d.ts +1 -0
- package/dist/modes/interactive/components/keybinding-hints.d.ts.map +1 -1
- package/dist/modes/interactive/components/keybinding-hints.js +47 -5
- package/dist/modes/interactive/components/keybinding-hints.js.map +1 -1
- package/dist/modes/interactive/components/login-dialog.d.ts.map +1 -1
- package/dist/modes/interactive/components/login-dialog.js +5 -5
- package/dist/modes/interactive/components/login-dialog.js.map +1 -1
- package/dist/modes/interactive/components/model-selector.d.ts +3 -3
- package/dist/modes/interactive/components/model-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/model-selector.js.map +1 -1
- package/dist/modes/interactive/components/scoped-models-selector.d.ts +2 -2
- package/dist/modes/interactive/components/scoped-models-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/scoped-models-selector.js +7 -7
- package/dist/modes/interactive/components/scoped-models-selector.js.map +1 -1
- package/dist/modes/interactive/components/session-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/session-selector.js +8 -8
- package/dist/modes/interactive/components/session-selector.js.map +1 -1
- package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/settings-selector.js +3 -3
- package/dist/modes/interactive/components/settings-selector.js.map +1 -1
- package/dist/modes/interactive/components/skill-invocation-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/skill-invocation-message.js +2 -2
- package/dist/modes/interactive/components/skill-invocation-message.js.map +1 -1
- package/dist/modes/interactive/components/tool-execution.d.ts +10 -12
- package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
- package/dist/modes/interactive/components/tool-execution.js +3 -3
- package/dist/modes/interactive/components/tool-execution.js.map +1 -1
- package/dist/modes/interactive/components/working-status.d.ts +25 -0
- package/dist/modes/interactive/components/working-status.d.ts.map +1 -0
- package/dist/modes/interactive/components/working-status.js +28 -0
- package/dist/modes/interactive/components/working-status.js.map +1 -0
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +8 -7
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-mode.js +8 -0
- package/dist/modes/rpc/rpc-mode.js.map +1 -1
- package/dist/modes/rpc/rpc-types.d.ts +5 -5
- package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-types.js.map +1 -1
- package/dist/utils/tools-manager.d.ts.map +1 -1
- package/dist/utils/tools-manager.js.map +1 -1
- package/docs/development.md +2 -2
- package/docs/extensions.md +7 -7
- package/docs/packages.md +11 -8
- package/docs/quickstart.md +2 -2
- package/docs/rpc.md +1 -1
- package/docs/sdk.md +14 -11
- package/docs/session-format.md +1 -1
- package/docs/sessions.md +10 -10
- package/docs/settings.md +1 -1
- package/docs/terminal-setup.md +9 -9
- package/docs/tmux.md +10 -10
- package/docs/tui.md +2 -2
- package/docs/usage.md +9 -9
- package/package.json +6 -1
|
@@ -14,10 +14,13 @@ import { restoreOnSessionStart } from "../shared/persistence-restore.js";
|
|
|
14
14
|
import type { SessionManager } from "../shared/persistence-restore.js";
|
|
15
15
|
import { installCompactionHook } from "../shared/persistence-compaction-policy.js";
|
|
16
16
|
import {
|
|
17
|
-
killRun,
|
|
18
17
|
killAllRuns,
|
|
18
|
+
destroyRun,
|
|
19
|
+
destroyAllRuns,
|
|
19
20
|
resumeRun,
|
|
20
21
|
pauseRun,
|
|
22
|
+
interruptRun,
|
|
23
|
+
interruptAllRuns,
|
|
21
24
|
inspectRun,
|
|
22
25
|
} from "../runs/background/status.js";
|
|
23
26
|
import { cancellationRegistry } from "../runs/background/cancellation-registry.js";
|
|
@@ -242,6 +245,17 @@ export interface PiExecuteContext extends PiModelContext {
|
|
|
242
245
|
* mocks can stub a minimal surface; production runtime supplies all of
|
|
243
246
|
* them.
|
|
244
247
|
*/
|
|
248
|
+
export interface WorkflowResourceInfo {
|
|
249
|
+
readonly path: string;
|
|
250
|
+
readonly enabled: boolean;
|
|
251
|
+
readonly metadata?: {
|
|
252
|
+
readonly source?: string;
|
|
253
|
+
readonly scope?: string;
|
|
254
|
+
readonly origin?: string;
|
|
255
|
+
readonly baseDir?: string;
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
|
|
245
259
|
export interface ExtensionAPI {
|
|
246
260
|
registerTool?: <TArgs, TResult>(opts: PiToolOpts<TArgs, TResult>) => void;
|
|
247
261
|
/**
|
|
@@ -272,6 +286,8 @@ export interface ExtensionAPI {
|
|
|
272
286
|
},
|
|
273
287
|
) => void | Promise<void>;
|
|
274
288
|
registerFlag?: (name: string, opts: PiFlagNamedOpts) => void;
|
|
289
|
+
/** Return package-provided workflow files discovered by Atomic's package loader. */
|
|
290
|
+
getWorkflowResources?: () => readonly WorkflowResourceInfo[];
|
|
275
291
|
/**
|
|
276
292
|
* Register a keyboard shortcut.
|
|
277
293
|
* Present on pi >= 1.x; absent on older runtimes.
|
|
@@ -378,10 +394,17 @@ export interface WorkflowToolArgs extends StageOptions {
|
|
|
378
394
|
| "get"
|
|
379
395
|
| "status"
|
|
380
396
|
| "interrupt"
|
|
397
|
+
| "kill"
|
|
381
398
|
| "resume"
|
|
382
399
|
| "inputs";
|
|
383
|
-
/** Canonical run identifier for status/interrupt/resume. */
|
|
400
|
+
/** Canonical run identifier or unique prefix for status/interrupt/kill/resume. */
|
|
384
401
|
runId?: string;
|
|
402
|
+
/** Apply supported run-control actions to all in-flight runs. */
|
|
403
|
+
all?: boolean;
|
|
404
|
+
/** Stage id, unique prefix, or name for stage-scoped resume. */
|
|
405
|
+
stageId?: string;
|
|
406
|
+
/** Optional message forwarded when resuming paused work. */
|
|
407
|
+
message?: string;
|
|
385
408
|
/** Direct single-task mode, or root task string when chain is present. */
|
|
386
409
|
task?: WorkflowDirectTaskItem | string;
|
|
387
410
|
/** Direct top-level parallel mode. */
|
|
@@ -393,6 +416,7 @@ export interface WorkflowToolArgs extends StageOptions {
|
|
|
393
416
|
/** Internal host-derived parent session file for context:"fork". */
|
|
394
417
|
forkFromSessionFile?: string;
|
|
395
418
|
concurrency?: number;
|
|
419
|
+
failFast?: boolean;
|
|
396
420
|
async?: boolean;
|
|
397
421
|
intercom?: {
|
|
398
422
|
enabled?: boolean;
|
|
@@ -404,10 +428,10 @@ export interface WorkflowToolArgs extends StageOptions {
|
|
|
404
428
|
};
|
|
405
429
|
output?: string | false;
|
|
406
430
|
outputMode?: "inline" | "file-only";
|
|
431
|
+
reads?: readonly string[] | false;
|
|
407
432
|
chainDir?: string;
|
|
408
433
|
maxOutput?: WorkflowMaxOutput;
|
|
409
434
|
artifacts?: boolean;
|
|
410
|
-
progress?: boolean;
|
|
411
435
|
worktree?: boolean;
|
|
412
436
|
}
|
|
413
437
|
|
|
@@ -592,10 +616,10 @@ export function makeExecuteWorkflowTool(
|
|
|
592
616
|
};
|
|
593
617
|
}
|
|
594
618
|
|
|
595
|
-
case "
|
|
596
|
-
|
|
597
|
-
if (
|
|
598
|
-
const results =
|
|
619
|
+
case "kill": {
|
|
620
|
+
const target = resolveToolRunTarget(args, "No in-flight runs to kill.");
|
|
621
|
+
if (target.kind === "all") {
|
|
622
|
+
const results = destroyAllRuns({
|
|
599
623
|
cancellation: cancellationRegistry,
|
|
600
624
|
persistence: getPersistence(),
|
|
601
625
|
});
|
|
@@ -606,11 +630,17 @@ export function makeExecuteWorkflowTool(
|
|
|
606
630
|
status: killed > 0 ? "killed" : "noop",
|
|
607
631
|
message:
|
|
608
632
|
killed > 0
|
|
609
|
-
? `
|
|
610
|
-
: "No in-flight runs to
|
|
633
|
+
? `Killed and removed ${killed} run(s).`
|
|
634
|
+
: "No in-flight runs to kill.",
|
|
611
635
|
};
|
|
612
636
|
}
|
|
613
|
-
|
|
637
|
+
if (target.kind === "ambiguous") {
|
|
638
|
+
return { action, runId: target.target, status: "noop", message: ambiguousRunMessage(target.target, target.matches) };
|
|
639
|
+
}
|
|
640
|
+
if (target.kind === "not_found") {
|
|
641
|
+
return { action, runId: target.target, status: "noop", message: target.message };
|
|
642
|
+
}
|
|
643
|
+
const result = destroyRun(target.runId, {
|
|
614
644
|
cancellation: cancellationRegistry,
|
|
615
645
|
persistence: getPersistence(),
|
|
616
646
|
});
|
|
@@ -619,35 +649,101 @@ export function makeExecuteWorkflowTool(
|
|
|
619
649
|
action,
|
|
620
650
|
runId: result.runId,
|
|
621
651
|
status: "killed",
|
|
622
|
-
message: `Run ${result.runId}
|
|
652
|
+
message: `Run ${result.runId} killed and removed (was ${result.previousStatus}).`,
|
|
653
|
+
};
|
|
654
|
+
}
|
|
655
|
+
return {
|
|
656
|
+
action,
|
|
657
|
+
runId: target.runId,
|
|
658
|
+
status: "noop",
|
|
659
|
+
message: `Run not found: ${target.runId}`,
|
|
660
|
+
};
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
case "interrupt": {
|
|
664
|
+
// Interrupt is resumable: it pauses live work and keeps runs in history/status.
|
|
665
|
+
const target = resolveToolRunTarget(args, "No in-flight runs to interrupt.");
|
|
666
|
+
if (target.kind === "all") {
|
|
667
|
+
const results = interruptAllRuns();
|
|
668
|
+
const interrupted = results.filter((r) => r.ok).length;
|
|
669
|
+
return {
|
|
670
|
+
action,
|
|
671
|
+
runId: "--all",
|
|
672
|
+
status: interrupted > 0 ? "paused" : "noop",
|
|
673
|
+
message:
|
|
674
|
+
interrupted > 0
|
|
675
|
+
? `Interrupted ${interrupted} run(s).`
|
|
676
|
+
: "No in-flight runs to interrupt.",
|
|
677
|
+
};
|
|
678
|
+
}
|
|
679
|
+
if (target.kind === "ambiguous") {
|
|
680
|
+
return { action, runId: target.target, status: "noop", message: ambiguousRunMessage(target.target, target.matches) };
|
|
681
|
+
}
|
|
682
|
+
if (target.kind === "not_found") {
|
|
683
|
+
return { action, runId: target.target, status: "noop", message: target.message };
|
|
684
|
+
}
|
|
685
|
+
const result = interruptRun(target.runId);
|
|
686
|
+
if (result.ok) {
|
|
687
|
+
return {
|
|
688
|
+
action,
|
|
689
|
+
runId: result.runId,
|
|
690
|
+
status: "paused",
|
|
691
|
+
message: `Run ${result.runId} interrupted and can be resumed.`,
|
|
623
692
|
};
|
|
624
693
|
}
|
|
625
694
|
return {
|
|
626
695
|
action,
|
|
627
|
-
runId,
|
|
696
|
+
runId: target.runId,
|
|
628
697
|
status: "noop",
|
|
629
698
|
message:
|
|
630
699
|
result.reason === "not_found"
|
|
631
|
-
? `Run not found: ${runId}`
|
|
632
|
-
:
|
|
700
|
+
? `Run not found: ${target.runId}`
|
|
701
|
+
: result.reason === "already_ended"
|
|
702
|
+
? `Run already ended: ${target.runId}`
|
|
703
|
+
: result.reason === "stage_not_found"
|
|
704
|
+
? `Stage not found for run: ${target.runId}`
|
|
705
|
+
: `No active stages to interrupt for run: ${target.runId}`,
|
|
633
706
|
};
|
|
634
707
|
}
|
|
635
708
|
|
|
636
709
|
case "resume": {
|
|
637
|
-
const
|
|
710
|
+
const target = resolveToolRunTarget(args, "No active run to resume.");
|
|
711
|
+
if (target.kind === "all") {
|
|
712
|
+
return { action: "resume", runId: "--all", status: "noop", message: "Resume does not support --all." };
|
|
713
|
+
}
|
|
714
|
+
if (target.kind === "ambiguous") {
|
|
715
|
+
return { action: "resume", runId: target.target, status: "noop", message: ambiguousRunMessage(target.target, target.matches) };
|
|
716
|
+
}
|
|
717
|
+
if (target.kind === "not_found") {
|
|
718
|
+
return { action: "resume", runId: target.target, status: "noop", message: target.message };
|
|
719
|
+
}
|
|
720
|
+
const stage = resolveToolStageTarget(target.runId, args.stageId);
|
|
721
|
+
if (!stage.ok) {
|
|
722
|
+
return { action: "resume", runId: target.runId, status: "noop", message: stage.message };
|
|
723
|
+
}
|
|
724
|
+
const run = store.runs().find((r) => r.id === target.runId);
|
|
725
|
+
const isPaused =
|
|
726
|
+
run?.status === "paused" ||
|
|
727
|
+
(run?.stages.some((s) => s.status === "paused") ?? false);
|
|
728
|
+
const result = resumeRun(target.runId, { stageId: stage.stageId, message: args.message });
|
|
638
729
|
if (result.ok) {
|
|
730
|
+
const message = isPaused
|
|
731
|
+
? result.resumed.length === 0
|
|
732
|
+
? `No paused stages on run ${result.runId.slice(0, 8)}.`
|
|
733
|
+
: `Resumed ${result.resumed.length} stage(s) on run ${result.runId.slice(0, 8)}${args.message ? ` with message: "${args.message}"` : ""}.`
|
|
734
|
+
: `Snapshot available: run ${result.runId} (${result.snapshot.name}) — status: ${result.snapshot.status}, stages: ${result.snapshot.stages.length}`;
|
|
639
735
|
return {
|
|
640
736
|
action: "resume",
|
|
641
737
|
runId: result.runId,
|
|
642
738
|
status: "ok",
|
|
643
|
-
message
|
|
739
|
+
message,
|
|
644
740
|
};
|
|
645
741
|
}
|
|
646
742
|
return {
|
|
647
743
|
action: "resume",
|
|
648
|
-
runId,
|
|
744
|
+
runId: target.runId,
|
|
649
745
|
status: "noop",
|
|
650
|
-
message: `Run not found: ${runId}`,
|
|
746
|
+
message: `Run not found: ${target.runId}`,
|
|
651
747
|
};
|
|
652
748
|
}
|
|
653
749
|
|
|
@@ -669,7 +765,7 @@ export function makeExecuteWorkflowTool(
|
|
|
669
765
|
* `registerWorkflowCommand` alongside the host registration so the
|
|
670
766
|
* `on("input", …)` interceptor below can dispatch our commands directly
|
|
671
767
|
* — bypassing pi's optimistic `startPendingSubmission` flow which
|
|
672
|
-
* fires the `Working… (
|
|
768
|
+
* fires the `Working… (esc to interrupt)` loader before the host knows
|
|
673
769
|
* the input is a synchronous picker/connect UI, not a streaming turn.
|
|
674
770
|
*
|
|
675
771
|
* See `installInputInterceptor()` for the dispatch path and rationale.
|
|
@@ -710,7 +806,7 @@ function registerWorkflowCommand(
|
|
|
710
806
|
* pi's editor `onSubmit` handler unconditionally calls
|
|
711
807
|
* `startPendingSubmission` for any text that isn't a built-in slash /
|
|
712
808
|
* skill / bash / python command — this echoes the message into chat
|
|
713
|
-
* scrollback AND starts the `Working… (
|
|
809
|
+
* scrollback AND starts the `Working… (esc to interrupt)` loader in
|
|
714
810
|
* `statusContainer` before `session.prompt` even runs. The loader is
|
|
715
811
|
* an optimistic affordance for the agent-streaming case; for our
|
|
716
812
|
* synchronous picker/connect UIs (`/workflow connect`, `/workflow run`,
|
|
@@ -772,6 +868,30 @@ function installInputInterceptor(
|
|
|
772
868
|
});
|
|
773
869
|
}
|
|
774
870
|
|
|
871
|
+
function formatStartupDiagnostics(
|
|
872
|
+
configResult: ConfigLoadResult | null,
|
|
873
|
+
discoveryResult: DiscoveryResult | null,
|
|
874
|
+
): string | null {
|
|
875
|
+
const lines: string[] = [];
|
|
876
|
+
for (const diagnostic of configResult?.diagnostics ?? []) {
|
|
877
|
+
lines.push(`- [${diagnostic.level} ${diagnostic.code}] ${diagnostic.source ?? "workflow config"}: ${diagnostic.message}`);
|
|
878
|
+
}
|
|
879
|
+
for (const diagnostic of discoveryResult?.errors ?? []) {
|
|
880
|
+
lines.push(`- [${diagnostic.level} ${diagnostic.code}] ${diagnostic.source ?? "workflow discovery"}: ${diagnostic.message}`);
|
|
881
|
+
}
|
|
882
|
+
|
|
883
|
+
if (lines.length === 0) return null;
|
|
884
|
+
|
|
885
|
+
const maxVisible = 8;
|
|
886
|
+
const visible = lines.slice(0, maxVisible);
|
|
887
|
+
const remaining = lines.length - visible.length;
|
|
888
|
+
return [
|
|
889
|
+
`Workflow discovery diagnostics (${lines.length}): some workflow resources were skipped or need attention.`,
|
|
890
|
+
...visible,
|
|
891
|
+
...(remaining > 0 ? [`- … ${remaining} more`] : []),
|
|
892
|
+
].join("\n");
|
|
893
|
+
}
|
|
894
|
+
|
|
775
895
|
/**
|
|
776
896
|
* Resolve a user-supplied run identifier (full UUID or unique prefix) to
|
|
777
897
|
* a concrete runId. The widget surfaces an 8-char prefix to keep the
|
|
@@ -794,6 +914,52 @@ function resolveRunIdPrefix(target: string): RunIdResolution {
|
|
|
794
914
|
return { kind: "ambiguous", matches: prefixed.map((r) => r.id) };
|
|
795
915
|
}
|
|
796
916
|
|
|
917
|
+
type ToolRunTarget =
|
|
918
|
+
| { kind: "all" }
|
|
919
|
+
| { kind: "run"; runId: string }
|
|
920
|
+
| { kind: "ambiguous"; target: string; matches: string[] }
|
|
921
|
+
| { kind: "not_found"; target: string; message: string };
|
|
922
|
+
|
|
923
|
+
function resolveToolRunTarget(
|
|
924
|
+
args: WorkflowToolArgs,
|
|
925
|
+
emptyMessage: string,
|
|
926
|
+
): ToolRunTarget {
|
|
927
|
+
const rawTarget = args.runId?.trim() ?? "";
|
|
928
|
+
if (args.all === true || rawTarget === "--all") return { kind: "all" };
|
|
929
|
+
|
|
930
|
+
const target = rawTarget || store.activeRunId() || "";
|
|
931
|
+
if (!target) return { kind: "not_found", target: rawTarget, message: emptyMessage };
|
|
932
|
+
|
|
933
|
+
const resolved = resolveRunIdPrefix(target);
|
|
934
|
+
if (resolved.kind === "exact") return { kind: "run", runId: resolved.runId };
|
|
935
|
+
if (resolved.kind === "ambiguous") {
|
|
936
|
+
return { kind: "ambiguous", target, matches: resolved.matches };
|
|
937
|
+
}
|
|
938
|
+
return { kind: "not_found", target, message: `Run not found: ${target}` };
|
|
939
|
+
}
|
|
940
|
+
|
|
941
|
+
type ToolStageTarget =
|
|
942
|
+
| { ok: true; stageId?: string }
|
|
943
|
+
| { ok: false; message: string };
|
|
944
|
+
|
|
945
|
+
function resolveToolStageTarget(runId: string, stageTarget?: string): ToolStageTarget {
|
|
946
|
+
const target = stageTarget?.trim();
|
|
947
|
+
if (!target) return { ok: true };
|
|
948
|
+
|
|
949
|
+
const run = store.runs().find((r) => r.id === runId);
|
|
950
|
+
const stage = run?.stages.find(
|
|
951
|
+
(s) => s.id === target || s.id.startsWith(target) || s.name === target,
|
|
952
|
+
);
|
|
953
|
+
if (!stage) return { ok: false, message: `Stage not found in run ${runId.slice(0, 8)}: ${target}` };
|
|
954
|
+
return { ok: true, stageId: stage.id };
|
|
955
|
+
}
|
|
956
|
+
|
|
957
|
+
function ambiguousRunMessage(target: string, matches: readonly string[]): string {
|
|
958
|
+
return `Ambiguous run prefix "${target}" matches: ${matches
|
|
959
|
+
.map((id) => id.slice(0, 12))
|
|
960
|
+
.join(", ")}`;
|
|
961
|
+
}
|
|
962
|
+
|
|
797
963
|
function overlaySurfaceFromContext(ctx?: {
|
|
798
964
|
ui?: PiUISurface;
|
|
799
965
|
}): OverlayPiSurface | undefined {
|
|
@@ -806,7 +972,7 @@ function overlaySurfaceFromContext(ctx?: {
|
|
|
806
972
|
|
|
807
973
|
/**
|
|
808
974
|
* Strip the clack-style `--yes` / `-y` confirmation skip flag from a token
|
|
809
|
-
* list. Used by `/workflow interrupt` to skip the confirmation overlay.
|
|
975
|
+
* list. Used by `/workflow interrupt` and `/workflow kill` to skip the confirmation overlay.
|
|
810
976
|
*/
|
|
811
977
|
export function stripYesFlag(tokens: string[]): {
|
|
812
978
|
tokens: string[];
|
|
@@ -991,10 +1157,6 @@ function factory(pi: ExtensionAPI): void {
|
|
|
991
1157
|
// `installInputInterceptor` for the rationale.
|
|
992
1158
|
const workflowCommands = new Map<string, WorkflowCommandHandler>();
|
|
993
1159
|
|
|
994
|
-
// Build graph overlay adapter — wraps GraphView + pi.ui.custom.
|
|
995
|
-
// noopOverlay returned when pi.ui?.custom is absent (degraded runtime).
|
|
996
|
-
const overlay: GraphOverlayPort = buildGraphOverlayAdapter(pi, store);
|
|
997
|
-
|
|
998
1160
|
// -------------------------------------------------------------------------
|
|
999
1161
|
// 1. Create ExtensionRuntime — mutable ref seeded from startup discovery,
|
|
1000
1162
|
// upgraded to unified async discovery once discoverWorkflows() resolves.
|
|
@@ -1006,6 +1168,18 @@ function factory(pi: ExtensionAPI): void {
|
|
|
1006
1168
|
const persistenceRef: { current: WorkflowPersistencePort | undefined } = {
|
|
1007
1169
|
current: makePersistencePort(pi, WORKFLOW_CONFIG_DEFAULTS.persistRuns),
|
|
1008
1170
|
};
|
|
1171
|
+
|
|
1172
|
+
// Build graph overlay adapter — wraps GraphView + pi.ui.custom.
|
|
1173
|
+
// noopOverlay returned when pi.ui?.custom is absent (degraded runtime).
|
|
1174
|
+
const overlay: GraphOverlayPort = buildGraphOverlayAdapter(pi, store, {
|
|
1175
|
+
onKillRun: (runId) => {
|
|
1176
|
+
destroyRun(runId, {
|
|
1177
|
+
cancellation: cancellationRegistry,
|
|
1178
|
+
persistence: persistenceRef.current,
|
|
1179
|
+
});
|
|
1180
|
+
},
|
|
1181
|
+
});
|
|
1182
|
+
|
|
1009
1183
|
const mcpPort: WorkflowMcpPort | undefined = makeMcpPort(pi);
|
|
1010
1184
|
|
|
1011
1185
|
/**
|
|
@@ -1158,7 +1332,10 @@ function factory(pi: ExtensionAPI): void {
|
|
|
1158
1332
|
)
|
|
1159
1333
|
: undefined;
|
|
1160
1334
|
|
|
1161
|
-
const
|
|
1335
|
+
const packageWorkflowPaths = (pi.getWorkflowResources?.() ?? [])
|
|
1336
|
+
.filter((resource) => resource.enabled !== false)
|
|
1337
|
+
.map((resource) => resource.path);
|
|
1338
|
+
const result = await discoverWorkflows({ config: discoveryConfig, packageWorkflowPaths });
|
|
1162
1339
|
discoveryRef.current = result;
|
|
1163
1340
|
|
|
1164
1341
|
// Resolve effective config (fills in all defaults) and build WorkflowRuntimeConfig.
|
|
@@ -1230,11 +1407,12 @@ function factory(pi: ExtensionAPI): void {
|
|
|
1230
1407
|
* connect [runId|prefix] no arg → picker overlay; arg → attach to graph
|
|
1231
1408
|
* attach [runId|prefix [stageId]] open the in-place attach pane on a stage
|
|
1232
1409
|
* interrupt [runId|prefix|--all] [-y] confirmation overlay unless -y
|
|
1233
|
-
*
|
|
1410
|
+
* kill [runId|prefix|--all] [-y] kill and remove from history/status
|
|
1411
|
+
* pause [runId|prefix [stageId]] pause a run or specific stage
|
|
1234
1412
|
* resume [runId|prefix [stageId] …] resume paused work or reopen snapshot
|
|
1235
1413
|
*/
|
|
1236
1414
|
async function handleRunControlCommand(
|
|
1237
|
-
action: "connect" | "interrupt" | "attach" | "pause" | "resume",
|
|
1415
|
+
action: "connect" | "interrupt" | "kill" | "attach" | "pause" | "resume",
|
|
1238
1416
|
rest: string[],
|
|
1239
1417
|
ctx: PiCommandContext,
|
|
1240
1418
|
): Promise<boolean> {
|
|
@@ -1271,14 +1449,14 @@ function factory(pi: ExtensionAPI): void {
|
|
|
1271
1449
|
);
|
|
1272
1450
|
return true;
|
|
1273
1451
|
}
|
|
1274
|
-
const killed =
|
|
1452
|
+
const killed = destroyRun(result.runId, {
|
|
1275
1453
|
cancellation: cancellationRegistry,
|
|
1276
1454
|
persistence: persistenceRef.current,
|
|
1277
1455
|
});
|
|
1278
1456
|
print(
|
|
1279
1457
|
killed.ok
|
|
1280
|
-
? `Run ${killed.runId.slice(0, 8)}
|
|
1281
|
-
: `Run ${result.runId.slice(0, 8)}
|
|
1458
|
+
? `Run ${killed.runId.slice(0, 8)} killed and removed.`
|
|
1459
|
+
: `Run not found: ${result.runId.slice(0, 8)}.`,
|
|
1282
1460
|
);
|
|
1283
1461
|
return true;
|
|
1284
1462
|
}
|
|
@@ -1301,7 +1479,7 @@ function factory(pi: ExtensionAPI): void {
|
|
|
1301
1479
|
}
|
|
1302
1480
|
overlay.open(resolved.runId, overlaySurfaceFromContext(ctx));
|
|
1303
1481
|
print(
|
|
1304
|
-
`Attached to ${resolved.runId.slice(0, 8)}.
|
|
1482
|
+
`Attached to ${resolved.runId.slice(0, 8)}. h/ctrl+d hide · q kill · esc close.`,
|
|
1305
1483
|
);
|
|
1306
1484
|
return true;
|
|
1307
1485
|
}
|
|
@@ -1326,6 +1504,87 @@ function factory(pi: ExtensionAPI): void {
|
|
|
1326
1504
|
if (!yes && ctx.ui && typeof ctx.ui.confirm === "function") {
|
|
1327
1505
|
const ok = await ctx.ui.confirm(
|
|
1328
1506
|
`Interrupt all ${inFlight.length} in-flight workflow runs?`,
|
|
1507
|
+
`Pauses: ${inFlight.map((r) => `${r.name} (${r.id.slice(0, 8)})`).join(", ")}`,
|
|
1508
|
+
);
|
|
1509
|
+
if (!ok) {
|
|
1510
|
+
print("Cancelled.");
|
|
1511
|
+
return true;
|
|
1512
|
+
}
|
|
1513
|
+
}
|
|
1514
|
+
const results = interruptAllRuns();
|
|
1515
|
+
const interrupted = results.filter((r) => r.ok).length;
|
|
1516
|
+
print(
|
|
1517
|
+
interrupted > 0
|
|
1518
|
+
? `Interrupted ${interrupted} run(s).`
|
|
1519
|
+
: "No in-flight runs to interrupt.",
|
|
1520
|
+
);
|
|
1521
|
+
return true;
|
|
1522
|
+
}
|
|
1523
|
+
const resolved = resolveRunIdPrefix(target!);
|
|
1524
|
+
if (resolved.kind === "not_found") {
|
|
1525
|
+
print(`Run not found: ${target}`);
|
|
1526
|
+
return true;
|
|
1527
|
+
}
|
|
1528
|
+
if (resolved.kind === "ambiguous") {
|
|
1529
|
+
print(
|
|
1530
|
+
`Ambiguous run prefix "${target}" matches multiple runs: ${resolved.matches
|
|
1531
|
+
.map((id) => id.slice(0, 12))
|
|
1532
|
+
.join(", ")}`,
|
|
1533
|
+
);
|
|
1534
|
+
return true;
|
|
1535
|
+
}
|
|
1536
|
+
const run = store.runs().find((r) => r.id === resolved.runId);
|
|
1537
|
+
if (!yes && run && run.endedAt === undefined && typeof ctx.ui.confirm === "function") {
|
|
1538
|
+
const confirmed = await ctx.ui.confirm(
|
|
1539
|
+
`Interrupt workflow run ${run.name} (${run.id.slice(0, 8)})?`,
|
|
1540
|
+
"Pauses live work so it can be resumed later.",
|
|
1541
|
+
);
|
|
1542
|
+
if (!confirmed) {
|
|
1543
|
+
print(
|
|
1544
|
+
`Cancelled. Run ${resolved.runId.slice(0, 8)} is still active.`,
|
|
1545
|
+
);
|
|
1546
|
+
return true;
|
|
1547
|
+
}
|
|
1548
|
+
}
|
|
1549
|
+
const result = interruptRun(resolved.runId);
|
|
1550
|
+
if (result.ok) {
|
|
1551
|
+
print(
|
|
1552
|
+
`Run ${result.runId.slice(0, 8)} interrupted and can be resumed.`,
|
|
1553
|
+
);
|
|
1554
|
+
} else {
|
|
1555
|
+
print(
|
|
1556
|
+
result.reason === "not_found"
|
|
1557
|
+
? `Run not found: ${target}`
|
|
1558
|
+
: result.reason === "already_ended"
|
|
1559
|
+
? `Run already ended: ${target}`
|
|
1560
|
+
: result.reason === "stage_not_found"
|
|
1561
|
+
? `Stage not found for run ${resolved.runId.slice(0, 8)}.`
|
|
1562
|
+
: `No active stages to interrupt on run ${resolved.runId.slice(0, 8)}.`,
|
|
1563
|
+
);
|
|
1564
|
+
}
|
|
1565
|
+
return true;
|
|
1566
|
+
}
|
|
1567
|
+
|
|
1568
|
+
if (action === "kill") {
|
|
1569
|
+
const { tokens: killArgs, yes } = stripYesFlag(rest);
|
|
1570
|
+
let target = killArgs.find((t) => !t.startsWith("--"));
|
|
1571
|
+
const wantsAll = killArgs.includes("--all");
|
|
1572
|
+
if (!target && !wantsAll) {
|
|
1573
|
+
target = store.activeRunId() ?? undefined;
|
|
1574
|
+
if (!target) {
|
|
1575
|
+
print("No in-flight runs to kill.");
|
|
1576
|
+
return true;
|
|
1577
|
+
}
|
|
1578
|
+
}
|
|
1579
|
+
if (wantsAll) {
|
|
1580
|
+
const inFlight = store.runs().filter((r) => r.endedAt === undefined);
|
|
1581
|
+
if (inFlight.length === 0) {
|
|
1582
|
+
print("No in-flight runs to kill.");
|
|
1583
|
+
return true;
|
|
1584
|
+
}
|
|
1585
|
+
if (!yes && ctx.ui && typeof ctx.ui.confirm === "function") {
|
|
1586
|
+
const ok = await ctx.ui.confirm(
|
|
1587
|
+
`Kill and remove all ${inFlight.length} in-flight workflow runs?`,
|
|
1329
1588
|
`Aborts: ${inFlight.map((r) => `${r.name} (${r.id.slice(0, 8)})`).join(", ")}`,
|
|
1330
1589
|
);
|
|
1331
1590
|
if (!ok) {
|
|
@@ -1333,15 +1592,15 @@ function factory(pi: ExtensionAPI): void {
|
|
|
1333
1592
|
return true;
|
|
1334
1593
|
}
|
|
1335
1594
|
}
|
|
1336
|
-
const results =
|
|
1595
|
+
const results = destroyAllRuns({
|
|
1337
1596
|
cancellation: cancellationRegistry,
|
|
1338
1597
|
persistence: persistenceRef.current,
|
|
1339
1598
|
});
|
|
1340
1599
|
const killed = results.filter((r) => r.ok).length;
|
|
1341
1600
|
print(
|
|
1342
1601
|
killed > 0
|
|
1343
|
-
? `
|
|
1344
|
-
: "No in-flight runs to
|
|
1602
|
+
? `Killed and removed ${killed} run(s).`
|
|
1603
|
+
: "No in-flight runs to kill.",
|
|
1345
1604
|
);
|
|
1346
1605
|
return true;
|
|
1347
1606
|
}
|
|
@@ -1359,29 +1618,25 @@ function factory(pi: ExtensionAPI): void {
|
|
|
1359
1618
|
return true;
|
|
1360
1619
|
}
|
|
1361
1620
|
const run = store.runs().find((r) => r.id === resolved.runId);
|
|
1362
|
-
if (!yes && run &&
|
|
1621
|
+
if (!yes && run && ctx.ui) {
|
|
1363
1622
|
const confirmed = await openKillConfirm(ctx.ui, run, theme);
|
|
1364
1623
|
if (!confirmed) {
|
|
1365
1624
|
print(
|
|
1366
|
-
`Cancelled. Run ${resolved.runId.slice(0, 8)} is still
|
|
1625
|
+
`Cancelled. Run ${resolved.runId.slice(0, 8)} is still in history/status.`,
|
|
1367
1626
|
);
|
|
1368
1627
|
return true;
|
|
1369
1628
|
}
|
|
1370
1629
|
}
|
|
1371
|
-
const result =
|
|
1630
|
+
const result = destroyRun(resolved.runId, {
|
|
1372
1631
|
cancellation: cancellationRegistry,
|
|
1373
1632
|
persistence: persistenceRef.current,
|
|
1374
1633
|
});
|
|
1375
1634
|
if (result.ok) {
|
|
1376
1635
|
print(
|
|
1377
|
-
`Run ${result.runId.slice(0, 8)}
|
|
1636
|
+
`Run ${result.runId.slice(0, 8)} killed and removed (was ${result.previousStatus}).`,
|
|
1378
1637
|
);
|
|
1379
1638
|
} else {
|
|
1380
|
-
print(
|
|
1381
|
-
result.reason === "not_found"
|
|
1382
|
-
? `Run not found: ${target}`
|
|
1383
|
-
: `Run already ended: ${target}`,
|
|
1384
|
-
);
|
|
1639
|
+
print(`Run not found: ${target}`);
|
|
1385
1640
|
}
|
|
1386
1641
|
return true;
|
|
1387
1642
|
}
|
|
@@ -1405,7 +1660,7 @@ function factory(pi: ExtensionAPI): void {
|
|
|
1405
1660
|
// Forward through the existing interrupt flow for clarity.
|
|
1406
1661
|
if (picked.kind === "kill") {
|
|
1407
1662
|
return handleRunControlCommand(
|
|
1408
|
-
"
|
|
1663
|
+
"kill",
|
|
1409
1664
|
[picked.runId, "-y"],
|
|
1410
1665
|
ctx,
|
|
1411
1666
|
);
|
|
@@ -1443,8 +1698,8 @@ function factory(pi: ExtensionAPI): void {
|
|
|
1443
1698
|
overlay.open(runId, overlaySurfaceFromContext(ctx), stageId);
|
|
1444
1699
|
print(
|
|
1445
1700
|
stageId
|
|
1446
|
-
? `Attached to ${runId.slice(0, 8)} stage ${stageId.slice(0, 8)}.
|
|
1447
|
-
: `Attached to ${runId.slice(0, 8)}.
|
|
1701
|
+
? `Attached to ${runId.slice(0, 8)} stage ${stageId.slice(0, 8)}. ctrl+d return to graph · esc close.`
|
|
1702
|
+
: `Attached to ${runId.slice(0, 8)}. ↵ chat · ctrl+d detach.`,
|
|
1448
1703
|
);
|
|
1449
1704
|
return true;
|
|
1450
1705
|
}
|
|
@@ -1513,7 +1768,7 @@ function factory(pi: ExtensionAPI): void {
|
|
|
1513
1768
|
}
|
|
1514
1769
|
// Open the orchestrator overlay (graph for run-level pause, stage
|
|
1515
1770
|
// chat when a stage was named). This mirrors connect/attach/resume:
|
|
1516
|
-
// the full-screen overlay hides Pi's "Working… (
|
|
1771
|
+
// the full-screen overlay hides Pi's "Working… (esc to interrupt)"
|
|
1517
1772
|
// spinner, which otherwise stays visible because the host session
|
|
1518
1773
|
// is still streaming whatever was happening before the pause hit.
|
|
1519
1774
|
if (typeof ctx.ui?.custom === "function") {
|
|
@@ -1608,7 +1863,7 @@ function factory(pi: ExtensionAPI): void {
|
|
|
1608
1863
|
"workflow",
|
|
1609
1864
|
{
|
|
1610
1865
|
description:
|
|
1611
|
-
"Run or inspect pi workflows. Usage: /workflow <name> [key=value…] | /workflow [list|status|connect|attach|interrupt|pause|resume|inputs] [args]",
|
|
1866
|
+
"Run or inspect pi workflows. Usage: /workflow <name> [key=value…] | /workflow [list|status|connect|attach|interrupt|kill|pause|resume|inputs] [args]",
|
|
1612
1867
|
handler: async (args: string, ctx: PiCommandContext) => {
|
|
1613
1868
|
const print = (msg: string): void => ctx.ui.notify(msg, "info");
|
|
1614
1869
|
// Quote-aware split so `prompt="map the codebase"` stays a single
|
|
@@ -1702,7 +1957,7 @@ function factory(pi: ExtensionAPI): void {
|
|
|
1702
1957
|
if (subcommand === "interrupt") {
|
|
1703
1958
|
// The top-level chat command is the fast interrupt path surfaced by the
|
|
1704
1959
|
// widget hint (`/workflow interrupt <id>`). The user's explicit slash
|
|
1705
|
-
// command should
|
|
1960
|
+
// command should pause immediately, even when a confirm surface is
|
|
1706
1961
|
// unavailable or would steal focus from the running workflow.
|
|
1707
1962
|
const interruptArgs = parts.slice(1);
|
|
1708
1963
|
const hasYes = interruptArgs.some((t) => t === "--yes" || t === "-y");
|
|
@@ -1714,6 +1969,20 @@ function factory(pi: ExtensionAPI): void {
|
|
|
1714
1969
|
return;
|
|
1715
1970
|
}
|
|
1716
1971
|
|
|
1972
|
+
// -----------------------------------------------------------------------
|
|
1973
|
+
// kill — destructive fast path: abort and remove from history/status.
|
|
1974
|
+
// -----------------------------------------------------------------------
|
|
1975
|
+
if (subcommand === "kill") {
|
|
1976
|
+
const killArgs = parts.slice(1);
|
|
1977
|
+
const hasYes = killArgs.some((t) => t === "--yes" || t === "-y");
|
|
1978
|
+
await handleRunControlCommand(
|
|
1979
|
+
"kill",
|
|
1980
|
+
hasYes ? killArgs : [...killArgs, "-y"],
|
|
1981
|
+
ctx,
|
|
1982
|
+
);
|
|
1983
|
+
return;
|
|
1984
|
+
}
|
|
1985
|
+
|
|
1717
1986
|
// -----------------------------------------------------------------------
|
|
1718
1987
|
// resume — non-paused runs reopen the orchestrator pane (legacy
|
|
1719
1988
|
// behaviour); paused runs resume live work through the registry.
|
|
@@ -1814,7 +2083,7 @@ function factory(pi: ExtensionAPI): void {
|
|
|
1814
2083
|
// Track whether the inputs picker actually showed a UI to the user.
|
|
1815
2084
|
// We use this below to mount the orchestrator overlay on dispatch
|
|
1816
2085
|
// success — same UX as `/workflow connect|attach|pause|resume`,
|
|
1817
|
-
// which all cover Pi's `⠴ Working… (
|
|
2086
|
+
// which all cover Pi's `⠴ Working… (esc to interrupt)` spinner
|
|
1818
2087
|
// with the full-screen overlay instead of leaving it visible in
|
|
1819
2088
|
// the chat while the workflow runs in the background.
|
|
1820
2089
|
let pickerWasShown = false;
|
|
@@ -1988,6 +2257,11 @@ function factory(pi: ExtensionAPI): void {
|
|
|
1988
2257
|
label: "interrupt",
|
|
1989
2258
|
description: "Interrupt a run",
|
|
1990
2259
|
},
|
|
2260
|
+
{
|
|
2261
|
+
value: "kill ",
|
|
2262
|
+
label: "kill",
|
|
2263
|
+
description: "Kill and remove a run",
|
|
2264
|
+
},
|
|
1991
2265
|
{
|
|
1992
2266
|
value: "pause ",
|
|
1993
2267
|
label: "pause",
|
|
@@ -2041,12 +2315,13 @@ function factory(pi: ExtensionAPI): void {
|
|
|
2041
2315
|
return completeToken(partial, runIdItems());
|
|
2042
2316
|
}
|
|
2043
2317
|
|
|
2044
|
-
if (subcommand === "interrupt") {
|
|
2318
|
+
if (subcommand === "interrupt" || subcommand === "kill") {
|
|
2319
|
+
const verb = subcommand === "kill" ? "Kill and remove" : "Interrupt";
|
|
2045
2320
|
return completeToken(partial, [
|
|
2046
2321
|
{
|
|
2047
2322
|
value: "--all ",
|
|
2048
2323
|
label: "--all",
|
|
2049
|
-
description:
|
|
2324
|
+
description: `${verb} all in-flight runs`,
|
|
2050
2325
|
},
|
|
2051
2326
|
{
|
|
2052
2327
|
value: "--yes ",
|
|
@@ -2187,6 +2462,10 @@ function factory(pi: ExtensionAPI): void {
|
|
|
2187
2462
|
// tunables must be resolved first.
|
|
2188
2463
|
await discoveryPromise;
|
|
2189
2464
|
if (ctx?.ui) {
|
|
2465
|
+
const diagnostics = formatStartupDiagnostics(configLoadRef.current, discoveryRef.current);
|
|
2466
|
+
if (diagnostics !== null) {
|
|
2467
|
+
ctx.ui.notify?.(diagnostics, "warning");
|
|
2468
|
+
}
|
|
2190
2469
|
storeWidgetUnsubscribe?.();
|
|
2191
2470
|
storeWidgetUnsubscribe = installStoreWidget({ ui: ctx.ui }, store);
|
|
2192
2471
|
}
|
|
@@ -2206,17 +2485,22 @@ function factory(pi: ExtensionAPI): void {
|
|
|
2206
2485
|
});
|
|
2207
2486
|
|
|
2208
2487
|
installCompactionHook(pi, store);
|
|
2209
|
-
pi.on("session_shutdown", () => {
|
|
2210
|
-
//
|
|
2211
|
-
//
|
|
2212
|
-
//
|
|
2488
|
+
pi.on("session_shutdown", (event) => {
|
|
2489
|
+
// Only application exit owns workflow teardown. Session replacement
|
|
2490
|
+
// paths (reload/new/resume/fork) are handled by session_start restore
|
|
2491
|
+
// logic so they do not masquerade as app-exit kills.
|
|
2492
|
+
const reason = typeof event === "object" && event !== null && "reason" in event
|
|
2493
|
+
? (event as { readonly reason?: string }).reason
|
|
2494
|
+
: undefined;
|
|
2213
2495
|
intercomControlUnsubscribe?.();
|
|
2214
2496
|
intercomControlUnsubscribe = null;
|
|
2215
|
-
|
|
2216
|
-
|
|
2217
|
-
|
|
2218
|
-
|
|
2219
|
-
|
|
2497
|
+
if (reason === "quit") {
|
|
2498
|
+
killAllRuns({
|
|
2499
|
+
store,
|
|
2500
|
+
cancellation: cancellationRegistry,
|
|
2501
|
+
persistence: persistenceRef.current,
|
|
2502
|
+
});
|
|
2503
|
+
}
|
|
2220
2504
|
storeWidgetUnsubscribe?.();
|
|
2221
2505
|
storeWidgetUnsubscribe = null;
|
|
2222
2506
|
});
|
|
@@ -2279,7 +2563,7 @@ function factory(pi: ExtensionAPI): void {
|
|
|
2279
2563
|
);
|
|
2280
2564
|
|
|
2281
2565
|
// -------------------------------------------------------------------------
|
|
2282
|
-
// 7. Suppress pi's optimistic "Working… (
|
|
2566
|
+
// 7. Suppress pi's optimistic "Working… (esc to interrupt)" loader
|
|
2283
2567
|
// for our slash commands. Workflow commands are synchronous picker /
|
|
2284
2568
|
// connect / inspect UIs, not streaming turns — the loader is noise
|
|
2285
2569
|
// that pads chrome above the picker. The `on("input")` hook fires
|