@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
|
@@ -112,6 +112,8 @@ export interface InternalStageContext extends StageContext {
|
|
|
112
112
|
* `undefined` keys when the session has not yet been created.
|
|
113
113
|
*/
|
|
114
114
|
__sessionMeta(): { sessionId: string | undefined; sessionFile: string | undefined };
|
|
115
|
+
/** Internal: live coding-agent session when the adapter returned one. */
|
|
116
|
+
__agentSession(): AgentSession | undefined;
|
|
115
117
|
/** Internal: selected/effective model and fallback attempt metadata. */
|
|
116
118
|
__modelFallbackMeta(): StageModelFallbackMeta;
|
|
117
119
|
/**
|
|
@@ -194,6 +196,20 @@ function lastAssistantTextFromMessages(messages: AgentSession["messages"]): stri
|
|
|
194
196
|
return undefined;
|
|
195
197
|
}
|
|
196
198
|
|
|
199
|
+
function asAgentSession(activeSession: StageSessionRuntime | undefined): AgentSession | undefined {
|
|
200
|
+
if (!activeSession) return undefined;
|
|
201
|
+
const candidate = activeSession as StageSessionRuntime & Partial<Pick<AgentSession, "state" | "sessionManager" | "modelRegistry" | "getContextUsage">>;
|
|
202
|
+
if (
|
|
203
|
+
candidate.state !== undefined &&
|
|
204
|
+
candidate.sessionManager !== undefined &&
|
|
205
|
+
candidate.modelRegistry !== undefined &&
|
|
206
|
+
typeof candidate.getContextUsage === "function"
|
|
207
|
+
) {
|
|
208
|
+
return candidate as AgentSession;
|
|
209
|
+
}
|
|
210
|
+
return undefined;
|
|
211
|
+
}
|
|
212
|
+
|
|
197
213
|
function lastAssistantTextFromSession(
|
|
198
214
|
activeSession: StageSessionRuntime | undefined,
|
|
199
215
|
fallback: string | undefined,
|
|
@@ -369,17 +385,25 @@ export function createStageContext(opts: StageRunnerOpts): InternalStageContext
|
|
|
369
385
|
deferred: PromiseWithResolvers<{ message?: string }>;
|
|
370
386
|
} | null = null;
|
|
371
387
|
|
|
372
|
-
// Wire the executor's abort signal to the
|
|
373
|
-
// (or other forced abort) doesn't leave a paused stage
|
|
374
|
-
// resume signal that will never arrive.
|
|
388
|
+
// Wire the executor's abort signal to the live SDK session and pause
|
|
389
|
+
// deferred so a kill (or other forced abort) doesn't leave a paused stage
|
|
390
|
+
// hanging on a resume signal that will never arrive. Re-use the abort
|
|
391
|
+
// reason instead of manufacturing a stage-specific error; shutdown/kill is
|
|
392
|
+
// expected cancellation and should not surface as a noisy per-stage failure.
|
|
375
393
|
if (signal) {
|
|
394
|
+
const abortReason = (): Error | DOMException | string => {
|
|
395
|
+
const reason = signal.reason;
|
|
396
|
+
if (reason instanceof Error || reason instanceof DOMException || typeof reason === "string") {
|
|
397
|
+
return reason;
|
|
398
|
+
}
|
|
399
|
+
return new DOMException("workflow killed", "AbortError");
|
|
400
|
+
};
|
|
376
401
|
const onAbort = (): void => {
|
|
402
|
+
void session?.abort().catch(() => {});
|
|
377
403
|
if (!pauseRequest) return;
|
|
378
404
|
const req = pauseRequest;
|
|
379
405
|
pauseRequest = null;
|
|
380
|
-
req.deferred.reject(
|
|
381
|
-
new Error(`pi-workflows: stage "${stageName}" aborted while paused`),
|
|
382
|
-
);
|
|
406
|
+
req.deferred.reject(abortReason());
|
|
383
407
|
};
|
|
384
408
|
if (signal.aborted) onAbort();
|
|
385
409
|
else signal.addEventListener("abort", onAbort, { once: true });
|
|
@@ -475,8 +499,22 @@ export function createStageContext(opts: StageRunnerOpts): InternalStageContext
|
|
|
475
499
|
// accumulated assistant text when resume carries no message.
|
|
476
500
|
let nextText: string | undefined = initialText;
|
|
477
501
|
while (nextText !== undefined) {
|
|
502
|
+
const pendingPauseBeforePrompt = pauseRequest;
|
|
503
|
+
if (pendingPauseBeforePrompt) {
|
|
504
|
+
const { message } = await pendingPauseBeforePrompt.deferred.promise;
|
|
505
|
+
nextText = message;
|
|
506
|
+
if (nextText === undefined) return;
|
|
507
|
+
continue;
|
|
508
|
+
}
|
|
478
509
|
try {
|
|
479
510
|
await activeSession.prompt(nextText, sdkOptions);
|
|
511
|
+
const pendingPauseAfterPrompt = pauseRequest;
|
|
512
|
+
if (pendingPauseAfterPrompt) {
|
|
513
|
+
const { message } = await pendingPauseAfterPrompt.deferred.promise;
|
|
514
|
+
nextText = message;
|
|
515
|
+
if (nextText === undefined) return;
|
|
516
|
+
continue;
|
|
517
|
+
}
|
|
480
518
|
nextText = undefined;
|
|
481
519
|
} catch (err) {
|
|
482
520
|
if (pauseRequest) {
|
|
@@ -669,6 +707,10 @@ export function createStageContext(opts: StageRunnerOpts): InternalStageContext
|
|
|
669
707
|
};
|
|
670
708
|
},
|
|
671
709
|
|
|
710
|
+
__agentSession() {
|
|
711
|
+
return asAgentSession(session);
|
|
712
|
+
},
|
|
713
|
+
|
|
672
714
|
__modelFallbackMeta() {
|
|
673
715
|
const attemptedModels = modelAttempts.map((attempt) => attempt.model);
|
|
674
716
|
const model = selectedModel ?? workflowModelId(session?.model);
|
|
@@ -682,7 +724,13 @@ export function createStageContext(opts: StageRunnerOpts): InternalStageContext
|
|
|
682
724
|
|
|
683
725
|
async __requestPause() {
|
|
684
726
|
if (pauseRequest) return;
|
|
685
|
-
|
|
727
|
+
const deferred = Promise.withResolvers<{ message?: string }>();
|
|
728
|
+
// A shutdown may reject this deferred when no prompt awaiter is actively
|
|
729
|
+
// observing it (for example a paused pending live stream). Keep the
|
|
730
|
+
// original promise for real waiters, but mark expected cancellation as
|
|
731
|
+
// observed so app-exit cleanup stays quiet.
|
|
732
|
+
void deferred.promise.catch(() => {});
|
|
733
|
+
pauseRequest = { deferred };
|
|
686
734
|
// Best-effort: stop the current Pi op so the awaiter races abort.
|
|
687
735
|
// The executor's `runTrackedStageCall` re-issues the call once
|
|
688
736
|
// `__resume()` settles `pauseRequest.deferred`.
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Programmatic workflow runner.
|
|
3
3
|
*
|
|
4
|
-
* This helper executes
|
|
4
|
+
* This helper executes named workflows and direct workflow task definitions
|
|
5
|
+
* from an explicit object that mirrors the workflow tool surface.
|
|
5
6
|
*/
|
|
6
7
|
|
|
7
8
|
import { homedir } from "node:os";
|
|
8
|
-
import { run, type RunOpts as ExecutorRunOptions } from "../foreground/executor.js";
|
|
9
|
+
import { run, runChain, runParallel, runTask, type RunOpts as ExecutorRunOptions } from "../foreground/executor.js";
|
|
9
10
|
import { buildRuntimeAdapters, type RuntimeAdapterBuildOptions, type RuntimeWiringSurface } from "../../extension/wiring.js";
|
|
10
11
|
import { discoverWorkflows } from "../../extension/discovery.js";
|
|
11
12
|
import { createStore } from "../../shared/store.js";
|
|
@@ -14,15 +15,38 @@ import { validateInputs, type ValidationError } from "./validate-inputs.js";
|
|
|
14
15
|
import type { CreateAgentSessionOptions } from "@bastani/atomic";
|
|
15
16
|
import type { StageSessionRuntime } from "../foreground/stage-runner.js";
|
|
16
17
|
import type {
|
|
18
|
+
StageOptions,
|
|
19
|
+
WorkflowChainStep,
|
|
17
20
|
WorkflowDetails,
|
|
18
21
|
WorkflowDetailsStatus,
|
|
22
|
+
WorkflowDirectOptions,
|
|
23
|
+
WorkflowDirectTaskItem,
|
|
19
24
|
WorkflowInputSchema,
|
|
25
|
+
WorkflowMaxOutput,
|
|
26
|
+
WorkflowOutputMode,
|
|
20
27
|
} from "../../shared/types.js";
|
|
21
28
|
|
|
22
|
-
export interface WorkflowDefinition {
|
|
23
|
-
mode
|
|
24
|
-
workflow
|
|
29
|
+
export interface WorkflowDefinition extends StageOptions {
|
|
30
|
+
mode?: "workflow" | "named" | "single" | "parallel" | "chain";
|
|
31
|
+
workflow?: string;
|
|
25
32
|
inputs?: Record<string, unknown>;
|
|
33
|
+
/** Direct single-task mode, or root task text for direct chain/parallel execution. */
|
|
34
|
+
task?: WorkflowDirectTaskItem | string;
|
|
35
|
+
/** Direct top-level parallel mode. */
|
|
36
|
+
tasks?: readonly WorkflowDirectTaskItem[];
|
|
37
|
+
/** Direct sequential/parallel chain mode. */
|
|
38
|
+
chain?: readonly WorkflowChainStep[];
|
|
39
|
+
chainName?: string;
|
|
40
|
+
concurrency?: number;
|
|
41
|
+
failFast?: boolean;
|
|
42
|
+
/** Chain-only shared artifact directory for relative reads, outputs, and worktree diffs. */
|
|
43
|
+
chainDir?: string;
|
|
44
|
+
reads?: readonly string[] | false;
|
|
45
|
+
output?: string | false;
|
|
46
|
+
outputMode?: WorkflowOutputMode;
|
|
47
|
+
worktree?: boolean;
|
|
48
|
+
maxOutput?: WorkflowMaxOutput;
|
|
49
|
+
artifacts?: boolean;
|
|
26
50
|
}
|
|
27
51
|
|
|
28
52
|
export type WorkflowRunOptions = Omit<ExecutorRunOptions, "adapters" | "store">;
|
|
@@ -37,7 +61,10 @@ export interface WorkflowOptions {
|
|
|
37
61
|
stubAgent?: boolean;
|
|
38
62
|
}
|
|
39
63
|
|
|
40
|
-
function runOptionsWithAdapters(
|
|
64
|
+
function runOptionsWithAdapters(
|
|
65
|
+
options: WorkflowOptions,
|
|
66
|
+
definition?: WorkflowDefinition,
|
|
67
|
+
): ExecutorRunOptions {
|
|
41
68
|
const adapterOptions = options.stubAgent === true
|
|
42
69
|
? {
|
|
43
70
|
...options.adapterOptions,
|
|
@@ -45,8 +72,26 @@ function runOptionsWithAdapters(options: WorkflowOptions): ExecutorRunOptions {
|
|
|
45
72
|
}
|
|
46
73
|
: options.adapterOptions;
|
|
47
74
|
|
|
75
|
+
const argConcurrency =
|
|
76
|
+
typeof definition?.concurrency === "number" && Number.isFinite(definition.concurrency)
|
|
77
|
+
? Math.max(1, Math.floor(definition.concurrency))
|
|
78
|
+
: undefined;
|
|
79
|
+
const config = argConcurrency === undefined
|
|
80
|
+
? options.runOptions?.config
|
|
81
|
+
: {
|
|
82
|
+
maxDepth: options.runOptions?.config?.maxDepth ?? 4,
|
|
83
|
+
defaultConcurrency: argConcurrency,
|
|
84
|
+
persistRuns: options.runOptions?.config?.persistRuns ?? true,
|
|
85
|
+
statusFile: options.runOptions?.config?.statusFile ?? false,
|
|
86
|
+
...(options.runOptions?.config?.statusFilePath !== undefined
|
|
87
|
+
? { statusFilePath: options.runOptions.config.statusFilePath }
|
|
88
|
+
: {}),
|
|
89
|
+
resumeInFlight: options.runOptions?.config?.resumeInFlight ?? ("ask" as const),
|
|
90
|
+
};
|
|
91
|
+
|
|
48
92
|
return {
|
|
49
93
|
...options.runOptions,
|
|
94
|
+
...(config !== undefined ? { config } : {}),
|
|
50
95
|
adapters: buildRuntimeAdapters(options.pi ?? {}, adapterOptions),
|
|
51
96
|
store: createStore(),
|
|
52
97
|
};
|
|
@@ -97,19 +142,77 @@ async function createStubAgentSession(
|
|
|
97
142
|
return { session };
|
|
98
143
|
}
|
|
99
144
|
|
|
145
|
+
function withoutUndefinedProperties<T extends object>(value: T): Partial<T> {
|
|
146
|
+
return Object.fromEntries(
|
|
147
|
+
Object.entries(value).filter(([, field]) => field !== undefined),
|
|
148
|
+
) as Partial<T>;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function directOptions(definition: WorkflowDefinition): WorkflowDirectOptions {
|
|
152
|
+
const {
|
|
153
|
+
mode: _mode,
|
|
154
|
+
workflow: _workflow,
|
|
155
|
+
inputs: _inputs,
|
|
156
|
+
task,
|
|
157
|
+
tasks: _tasks,
|
|
158
|
+
chain: _chain,
|
|
159
|
+
chainName,
|
|
160
|
+
concurrency,
|
|
161
|
+
failFast,
|
|
162
|
+
chainDir,
|
|
163
|
+
reads,
|
|
164
|
+
output,
|
|
165
|
+
outputMode,
|
|
166
|
+
worktree,
|
|
167
|
+
maxOutput,
|
|
168
|
+
artifacts,
|
|
169
|
+
...stageOptions
|
|
170
|
+
} = definition;
|
|
171
|
+
|
|
172
|
+
return {
|
|
173
|
+
...withoutUndefinedProperties(stageOptions),
|
|
174
|
+
...(typeof task === "string" ? { task } : {}),
|
|
175
|
+
...(typeof chainName === "string" ? { chainName } : {}),
|
|
176
|
+
...(typeof concurrency === "number" ? { concurrency } : {}),
|
|
177
|
+
...(typeof failFast === "boolean" ? { failFast } : {}),
|
|
178
|
+
...(typeof chainDir === "string" ? { chainDir } : {}),
|
|
179
|
+
...(reads !== undefined ? { reads } : {}),
|
|
180
|
+
...(output !== undefined ? { output } : {}),
|
|
181
|
+
...(outputMode !== undefined ? { outputMode } : {}),
|
|
182
|
+
...(typeof worktree === "boolean" ? { worktree } : {}),
|
|
183
|
+
...(maxOutput !== undefined ? { maxOutput } : {}),
|
|
184
|
+
...(typeof artifacts === "boolean" ? { artifacts } : {}),
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
function hasDirectExecutionMode(definition: WorkflowDefinition): boolean {
|
|
189
|
+
return (
|
|
190
|
+
definition.mode === "single" ||
|
|
191
|
+
definition.mode === "parallel" ||
|
|
192
|
+
definition.mode === "chain" ||
|
|
193
|
+
(definition.task !== undefined && typeof definition.task === "object") ||
|
|
194
|
+
Array.isArray(definition.tasks) ||
|
|
195
|
+
Array.isArray(definition.chain)
|
|
196
|
+
);
|
|
197
|
+
}
|
|
198
|
+
|
|
100
199
|
async function runNamedWorkflow(
|
|
101
200
|
definition: WorkflowDefinition,
|
|
102
201
|
options: WorkflowOptions,
|
|
103
202
|
runOptions: ExecutorRunOptions,
|
|
104
203
|
): Promise<WorkflowDetails> {
|
|
204
|
+
const workflowName = definition.workflow;
|
|
205
|
+
if (typeof workflowName !== "string" || workflowName.length === 0) {
|
|
206
|
+
throw new Error('Workflow definition must include "workflow" for named workflow execution.');
|
|
207
|
+
}
|
|
105
208
|
const discovery = await discoverWorkflows({
|
|
106
209
|
cwd: options.cwd ?? process.cwd(),
|
|
107
210
|
homeDir: options.homeDir ?? homedir(),
|
|
108
211
|
});
|
|
109
|
-
const workflow = discovery.registry.get(
|
|
212
|
+
const workflow = discovery.registry.get(workflowName);
|
|
110
213
|
if (workflow === undefined) {
|
|
111
214
|
const available = discovery.registry.names();
|
|
112
|
-
throw new Error(`Workflow not found: "${
|
|
215
|
+
throw new Error(`Workflow not found: "${workflowName}". Available: ${available.length > 0 ? available.join(", ") : "(none)"}`);
|
|
113
216
|
}
|
|
114
217
|
const inputs = definition.inputs ?? {};
|
|
115
218
|
const errors = validateInputs(workflow.inputs, inputs);
|
|
@@ -161,10 +264,43 @@ function toWorkflowDetailsStatus(status: string): WorkflowDetailsStatus {
|
|
|
161
264
|
}
|
|
162
265
|
}
|
|
163
266
|
|
|
267
|
+
async function runDirectWorkflow(
|
|
268
|
+
definition: WorkflowDefinition,
|
|
269
|
+
runOptions: ExecutorRunOptions,
|
|
270
|
+
): Promise<WorkflowDetails> {
|
|
271
|
+
const options = directOptions(definition);
|
|
272
|
+
|
|
273
|
+
if (Array.isArray(definition.chain) || definition.mode === "chain") {
|
|
274
|
+
if (!Array.isArray(definition.chain)) {
|
|
275
|
+
throw new Error('Direct chain workflow definitions must include "chain".');
|
|
276
|
+
}
|
|
277
|
+
return runChain(definition.chain, options, runOptions);
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
if (Array.isArray(definition.tasks) || definition.mode === "parallel") {
|
|
281
|
+
if (!Array.isArray(definition.tasks)) {
|
|
282
|
+
throw new Error('Direct parallel workflow definitions must include "tasks".');
|
|
283
|
+
}
|
|
284
|
+
return runParallel(definition.tasks, options, runOptions);
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
if (typeof definition.task === "object") {
|
|
288
|
+
return runTask(definition.task, options, runOptions);
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
if (typeof definition.task === "string" && definition.mode === "single") {
|
|
292
|
+
return runTask({ name: "task", task: definition.task }, options, runOptions);
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
throw new Error('Direct workflow definitions must include "task", "tasks", or "chain".');
|
|
296
|
+
}
|
|
297
|
+
|
|
164
298
|
export async function runWorkflow(
|
|
165
299
|
definition: WorkflowDefinition,
|
|
166
300
|
options: WorkflowOptions = {},
|
|
167
301
|
): Promise<WorkflowDetails> {
|
|
168
|
-
const runOptions = runOptionsWithAdapters(options);
|
|
169
|
-
return
|
|
302
|
+
const runOptions = runOptionsWithAdapters(options, definition);
|
|
303
|
+
return hasDirectExecutionMode(definition)
|
|
304
|
+
? runDirectWorkflow(definition, runOptions)
|
|
305
|
+
: runNamedWorkflow(definition, options, runOptions);
|
|
170
306
|
}
|
|
@@ -39,6 +39,12 @@ export interface Store {
|
|
|
39
39
|
result?: Record<string, unknown>,
|
|
40
40
|
error?: string,
|
|
41
41
|
): boolean;
|
|
42
|
+
/**
|
|
43
|
+
* Remove a run from live workflow history/status. Any pending HIL prompt
|
|
44
|
+
* waiter is rejected because the workflow will not resume through that path.
|
|
45
|
+
* Returns `true` when a run was removed, `false` when the id is unknown.
|
|
46
|
+
*/
|
|
47
|
+
removeRun(runId: string): boolean;
|
|
42
48
|
recordNotice(notice: WorkflowNotice): void;
|
|
43
49
|
/**
|
|
44
50
|
* Acknowledges a notice by id.
|
|
@@ -303,6 +309,29 @@ export function createStore(): Store {
|
|
|
303
309
|
return true;
|
|
304
310
|
},
|
|
305
311
|
|
|
312
|
+
removeRun(runId: string): boolean {
|
|
313
|
+
const index = _runs.findIndex((r) => r.id === runId);
|
|
314
|
+
if (index < 0) return false;
|
|
315
|
+
const run = _runs[index]!;
|
|
316
|
+
const pending = run.pendingPrompt;
|
|
317
|
+
if (pending) {
|
|
318
|
+
const entry = _resolvers.get(pending.id);
|
|
319
|
+
if (entry) {
|
|
320
|
+
_resolvers.delete(pending.id);
|
|
321
|
+
entry.reject(
|
|
322
|
+
new Error(`pi-workflows: run ${runId} was removed before prompt resolved`),
|
|
323
|
+
);
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
_runs.splice(index, 1);
|
|
327
|
+
for (let i = _notices.length - 1; i >= 0; i--) {
|
|
328
|
+
if (_notices[i]?.runId === runId) _notices.splice(i, 1);
|
|
329
|
+
}
|
|
330
|
+
_version++;
|
|
331
|
+
notify();
|
|
332
|
+
return true;
|
|
333
|
+
},
|
|
334
|
+
|
|
306
335
|
recordNotice(notice: WorkflowNotice): void {
|
|
307
336
|
_notices.push(notice);
|
|
308
337
|
_version++;
|
|
@@ -264,14 +264,35 @@ export interface WorkflowTaskStep extends WorkflowTaskOptions {
|
|
|
264
264
|
name: string;
|
|
265
265
|
}
|
|
266
266
|
|
|
267
|
-
export interface
|
|
267
|
+
export interface WorkflowSharedTaskDefaults extends StageOptions {
|
|
268
|
+
/** Optional default output artifact path for steps that do not set one. */
|
|
269
|
+
output?: string | false;
|
|
270
|
+
/** Default output mode for steps that do not set one. */
|
|
271
|
+
outputMode?: WorkflowOutputMode;
|
|
272
|
+
/** Files the task should read before responding; relative paths resolve via chainDir for chains, otherwise cwd. */
|
|
273
|
+
reads?: readonly string[] | false;
|
|
274
|
+
/** Workflow-owned isolation flag; not forwarded to createAgentSession(). */
|
|
275
|
+
worktree?: boolean;
|
|
276
|
+
/** Default output truncation limits for steps that do not set one. */
|
|
277
|
+
maxOutput?: WorkflowMaxOutput;
|
|
278
|
+
/** Whether to include debug artifacts such as sessions and worktree diffs. */
|
|
279
|
+
artifacts?: boolean;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
export interface WorkflowChainOptions extends WorkflowSharedTaskDefaults {
|
|
268
283
|
/** Shared/root task used for `{task}` in chain steps. */
|
|
269
284
|
task?: string;
|
|
285
|
+
/** Shared artifact directory for relative reads, outputs, and worktree diffs. */
|
|
286
|
+
chainDir?: string;
|
|
270
287
|
}
|
|
271
288
|
|
|
272
|
-
export interface WorkflowParallelOptions {
|
|
289
|
+
export interface WorkflowParallelOptions extends WorkflowSharedTaskDefaults {
|
|
273
290
|
/** Shared fallback task for parallel steps without their own task. */
|
|
274
291
|
task?: string;
|
|
292
|
+
/** Maximum number of parallel steps to schedule concurrently. */
|
|
293
|
+
concurrency?: number;
|
|
294
|
+
/** Stop scheduling additional steps after the first failure. Default: true. */
|
|
295
|
+
failFast?: boolean;
|
|
275
296
|
}
|
|
276
297
|
|
|
277
298
|
export type WorkflowOutputMode = "inline" | "file-only";
|
|
@@ -379,7 +400,6 @@ export interface WorkflowTaskSessionFields {
|
|
|
379
400
|
output?: string | false;
|
|
380
401
|
outputMode?: WorkflowOutputMode;
|
|
381
402
|
reads?: readonly string[] | false;
|
|
382
|
-
progress?: boolean;
|
|
383
403
|
/** Workflow-owned isolation flag; not forwarded to createAgentSession(). */
|
|
384
404
|
worktree?: boolean;
|
|
385
405
|
maxOutput?: WorkflowMaxOutput;
|
|
@@ -412,10 +432,11 @@ export interface WorkflowDirectOptions extends StageOptions {
|
|
|
412
432
|
chainName?: string;
|
|
413
433
|
concurrency?: number;
|
|
414
434
|
failFast?: boolean;
|
|
435
|
+
/** Chain-only shared artifact directory for relative reads, outputs, and worktree diffs. */
|
|
415
436
|
chainDir?: string;
|
|
437
|
+
reads?: readonly string[] | false;
|
|
416
438
|
output?: string | false;
|
|
417
439
|
outputMode?: WorkflowOutputMode;
|
|
418
|
-
progress?: boolean;
|
|
419
440
|
worktree?: boolean;
|
|
420
441
|
maxOutput?: WorkflowMaxOutput;
|
|
421
442
|
artifacts?: boolean;
|
|
@@ -21,6 +21,59 @@ interface Cell {
|
|
|
21
21
|
fg: string | null;
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
+
type Dir = "u" | "d" | "l" | "r";
|
|
25
|
+
|
|
26
|
+
function dirsForGlyph(ch: string): Set<Dir> {
|
|
27
|
+
switch (ch) {
|
|
28
|
+
case "│":
|
|
29
|
+
return new Set(["u", "d"]);
|
|
30
|
+
case "─":
|
|
31
|
+
return new Set(["l", "r"]);
|
|
32
|
+
case "╭":
|
|
33
|
+
case "┌":
|
|
34
|
+
return new Set(["d", "r"]);
|
|
35
|
+
case "╮":
|
|
36
|
+
case "┐":
|
|
37
|
+
return new Set(["d", "l"]);
|
|
38
|
+
case "╰":
|
|
39
|
+
case "└":
|
|
40
|
+
return new Set(["u", "r"]);
|
|
41
|
+
case "╯":
|
|
42
|
+
case "┘":
|
|
43
|
+
return new Set(["u", "l"]);
|
|
44
|
+
case "├":
|
|
45
|
+
return new Set(["u", "d", "r"]);
|
|
46
|
+
case "┤":
|
|
47
|
+
return new Set(["u", "d", "l"]);
|
|
48
|
+
case "┬":
|
|
49
|
+
return new Set(["d", "l", "r"]);
|
|
50
|
+
case "┴":
|
|
51
|
+
return new Set(["u", "l", "r"]);
|
|
52
|
+
case "┼":
|
|
53
|
+
return new Set(["u", "d", "l", "r"]);
|
|
54
|
+
default:
|
|
55
|
+
return new Set();
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function glyphForDirs(dirs: Set<Dir>): string {
|
|
60
|
+
const has = (...want: Dir[]) => want.every((d) => dirs.has(d));
|
|
61
|
+
if (has("u", "d", "l", "r")) return "┼";
|
|
62
|
+
if (has("u", "d", "r")) return "├";
|
|
63
|
+
if (has("u", "d", "l")) return "┤";
|
|
64
|
+
if (has("d", "l", "r")) return "┬";
|
|
65
|
+
if (has("u", "l", "r")) return "┴";
|
|
66
|
+
if (has("u", "d")) return "│";
|
|
67
|
+
if (has("l", "r")) return "─";
|
|
68
|
+
if (has("d", "r")) return "┌";
|
|
69
|
+
if (has("d", "l")) return "┐";
|
|
70
|
+
if (has("u", "r")) return "└";
|
|
71
|
+
if (has("u", "l")) return "┘";
|
|
72
|
+
if (has("u") || has("d")) return "│";
|
|
73
|
+
if (has("l") || has("r")) return "─";
|
|
74
|
+
return " ";
|
|
75
|
+
}
|
|
76
|
+
|
|
24
77
|
export class GraphCanvas {
|
|
25
78
|
/** rowIdx → colIdx → Cell. Sparse — empty cells render as a single space. */
|
|
26
79
|
private rows: Map<number, Map<number, Cell>> = new Map();
|
|
@@ -39,6 +92,20 @@ export class GraphCanvas {
|
|
|
39
92
|
if (col > this.maxCol) this.maxCol = col;
|
|
40
93
|
}
|
|
41
94
|
|
|
95
|
+
getCell(row: number, col: number): string | null {
|
|
96
|
+
return this.rows.get(row)?.get(col)?.ch ?? null;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
mergeCell(row: number, col: number, dirs: readonly Dir[], fg: string | null): void {
|
|
100
|
+
if (row < 0 || col < 0) return;
|
|
101
|
+
const existing = this.getCell(row, col);
|
|
102
|
+
const merged = new Set<Dir>([
|
|
103
|
+
...(existing == null ? [] : dirsForGlyph(existing)),
|
|
104
|
+
...dirs,
|
|
105
|
+
]);
|
|
106
|
+
this.setCell(row, col, glyphForDirs(merged), fg);
|
|
107
|
+
}
|
|
108
|
+
|
|
42
109
|
/**
|
|
43
110
|
* Paint a single character cell, but only if the target cell is empty.
|
|
44
111
|
* Used by the edge plotter so a corner glyph never clobbers a node-card
|
|
@@ -58,13 +125,13 @@ export class GraphCanvas {
|
|
|
58
125
|
hline(row: number, fromCol: number, toCol: number, fg: string | null): void {
|
|
59
126
|
const lo = Math.min(fromCol, toCol);
|
|
60
127
|
const hi = Math.max(fromCol, toCol);
|
|
61
|
-
for (let c = lo; c <= hi; c++) this.
|
|
128
|
+
for (let c = lo; c <= hi; c++) this.mergeCell(row, c, ["l", "r"], fg);
|
|
62
129
|
}
|
|
63
130
|
|
|
64
131
|
vline(col: number, fromRow: number, toRow: number, fg: string | null): void {
|
|
65
132
|
const lo = Math.min(fromRow, toRow);
|
|
66
133
|
const hi = Math.max(fromRow, toRow);
|
|
67
|
-
for (let r = lo; r <= hi; r++) this.
|
|
134
|
+
for (let r = lo; r <= hi; r++) this.mergeCell(r, col, ["u", "d"], fg);
|
|
68
135
|
}
|
|
69
136
|
|
|
70
137
|
/** Materialise the canvas as one ANSI-styled string per row. */
|