@bastani/atomic 0.9.15 → 0.9.16-alpha.2
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 +19 -0
- package/dist/builtin/intercom/CHANGELOG.md +10 -0
- package/dist/builtin/intercom/README.md +8 -8
- package/dist/builtin/intercom/contact-supervisor-tool.ts +68 -32
- package/dist/builtin/intercom/index-heavy.ts +1 -0
- package/dist/builtin/intercom/index.ts +21 -15
- package/dist/builtin/intercom/intercom-tool.ts +66 -10
- package/dist/builtin/intercom/package.json +1 -1
- package/dist/builtin/intercom/parent-ask-handoff.ts +72 -0
- package/dist/builtin/mcp/package.json +1 -1
- package/dist/builtin/subagents/CHANGELOG.md +26 -0
- package/dist/builtin/subagents/README.md +37 -114
- package/dist/builtin/subagents/agents/worker.md +2 -2
- package/dist/builtin/subagents/package.json +1 -5
- package/dist/builtin/subagents/skills/subagent/SKILL.md +26 -103
- package/dist/builtin/subagents/src/extension/index.ts +5 -30
- package/dist/builtin/subagents/src/extension/notification-content.ts +1 -1
- package/dist/builtin/subagents/src/extension/schemas.ts +2 -14
- package/dist/builtin/subagents/src/extension/tool-description.ts +1 -4
- package/dist/builtin/subagents/src/extension/tool-rendering.ts +53 -0
- package/dist/builtin/subagents/src/intercom/intercom-bridge.ts +1 -54
- package/dist/builtin/subagents/src/intercom/result-intercom.ts +4 -4
- package/dist/builtin/subagents/src/runs/foreground/execution-parent-ask-handoff.ts +78 -0
- package/dist/builtin/subagents/src/runs/foreground/inprocess-run-sync.ts +7 -5
- package/dist/builtin/subagents/src/runs/foreground/notify.ts +5 -6
- package/dist/builtin/subagents/src/runs/foreground/parent-ask-output.ts +48 -0
- package/dist/builtin/subagents/src/runs/foreground/subagent-executor-burst-display.ts +46 -0
- package/dist/builtin/subagents/src/runs/foreground/subagent-executor-burst.ts +403 -0
- package/dist/builtin/subagents/src/runs/foreground/subagent-executor-context.ts +48 -9
- package/dist/builtin/subagents/src/runs/foreground/subagent-executor-input.ts +3 -1
- package/dist/builtin/subagents/src/runs/foreground/subagent-executor-live-update.ts +11 -0
- package/dist/builtin/subagents/src/runs/foreground/subagent-executor-parallel-task.ts +164 -93
- package/dist/builtin/subagents/src/runs/foreground/subagent-executor-parallel.ts +36 -29
- package/dist/builtin/subagents/src/runs/foreground/subagent-executor-parent-ask-projection.ts +11 -0
- package/dist/builtin/subagents/src/runs/foreground/subagent-executor-single.ts +42 -17
- package/dist/builtin/subagents/src/runs/foreground/subagent-executor-status.ts +3 -121
- package/dist/builtin/subagents/src/runs/foreground/subagent-executor-types.ts +7 -2
- package/dist/builtin/subagents/src/runs/foreground/subagent-executor.ts +30 -257
- package/dist/builtin/subagents/src/runs/inprocess/control-status.ts +0 -34
- package/dist/builtin/subagents/src/runs/inprocess/index.ts +0 -12
- package/dist/builtin/subagents/src/runs/inprocess/prompt-behavior.ts +1 -1
- package/dist/builtin/subagents/src/runs/inprocess/runner.ts +2 -141
- package/dist/builtin/subagents/src/runs/shared/parallel-utils.ts +8 -0
- package/dist/builtin/subagents/src/shared/status-format.ts +3 -3
- package/dist/builtin/subagents/src/shared/types-config.ts +41 -0
- package/dist/builtin/subagents/src/shared/types-foreground-state.ts +7 -15
- package/dist/builtin/subagents/src/shared/types-results.ts +3 -1
- package/dist/builtin/subagents/src/shared/types-runtime.ts +1 -11
- package/dist/builtin/subagents/src/slash/slash-commands.ts +4 -468
- package/dist/builtin/subagents/src/tui/render-progress.ts +2 -2
- package/dist/builtin/subagents/src/tui/render-result-compact.ts +2 -2
- package/dist/builtin/subagents/src/tui/render-result.ts +38 -14
- package/dist/builtin/subagents/src/tui/render-status-progress.ts +1 -1
- package/dist/builtin/web-access/package.json +1 -1
- package/dist/builtin/workflows/CHANGELOG.md +16 -0
- package/dist/builtin/workflows/README.md +1 -1
- package/dist/builtin/workflows/package.json +1 -1
- package/dist/builtin/workflows/skills/bro/LICENSE.txt +21 -0
- package/dist/builtin/workflows/skills/bro/SKILL.md +14 -0
- package/dist/builtin/workflows/skills/how/LICENSE.txt +21 -0
- package/dist/builtin/workflows/skills/how/SKILL.md +162 -0
- package/dist/builtin/workflows/skills/how/references/critic-prompt.md +59 -0
- package/dist/builtin/workflows/skills/how/references/critique-rubric.md +58 -0
- package/dist/builtin/workflows/skills/how/references/explainer-prompt.md +55 -0
- package/dist/builtin/workflows/skills/how/references/explorer-prompt.md +52 -0
- package/dist/builtin/workflows/skills/teach/LICENSE.txt +21 -0
- package/dist/builtin/workflows/skills/teach/SKILL.md +28 -0
- package/dist/builtin/workflows/skills/unslop/LICENSE.txt +21 -0
- package/dist/builtin/workflows/skills/unslop/SKILL.md +87 -0
- package/dist/builtin/workflows/skills/why/LICENSE.txt +21 -0
- package/dist/builtin/workflows/skills/why/SKILL.md +267 -0
- package/dist/builtin/workflows/skills/why/references/epistemics.md +144 -0
- package/dist/builtin/workflows/skills/why/references/investigator-prompt.md +103 -0
- package/dist/builtin/workflows/skills/why/references/source-playbook.md +17 -0
- package/dist/builtin/workflows/skills/why/references/sources/code-archaeology.md +88 -0
- package/dist/builtin/workflows/skills/why/references/sources/databricks.md +70 -0
- package/dist/builtin/workflows/skills/why/references/sources/datadog.md +99 -0
- package/dist/builtin/workflows/skills/why/references/sources/incident-postmortem.md +15 -0
- package/dist/builtin/workflows/skills/why/references/sources/linear.md +48 -0
- package/dist/builtin/workflows/skills/why/references/sources/notion.md +55 -0
- package/dist/builtin/workflows/skills/why/references/sources/sentry.md +100 -0
- package/dist/builtin/workflows/skills/why/references/sources/slack.md +54 -0
- package/dist/builtin/workflows/skills/why/references/synthesizer-prompt.md +135 -0
- package/dist/builtin/workflows/src/durable/dbos-embedded-postgres-root.ts +548 -39
- package/dist/builtin/workflows/src/durable/dbos-embedded-postgres.ts +529 -65
- package/dist/builtin/workflows/src/durable/dbos-lifecycle.ts +30 -8
- package/dist/builtin/workflows/src/durable/dbos-local-postgres.ts +36 -3
- package/dist/builtin/workflows/src/durable/local-command.ts +68 -10
- package/dist/builtin/workflows/src/extension/companions.ts +1 -1
- package/dist/builtin/workflows/src/extension/dispatcher.ts +7 -1
- package/dist/builtin/workflows/src/extension/extension-factory.ts +2 -2
- package/dist/builtin/workflows/src/extension/index.bundle.mjs +1094 -298
- package/dist/builtin/workflows/src/extension/public-types.ts +2 -2
- package/dist/builtin/workflows/src/extension/render-result.ts +16 -2
- package/dist/builtin/workflows/src/extension/runtime.ts +8 -1
- package/dist/builtin/workflows/src/extension/workflow-request-abort.ts +15 -0
- package/dist/builtin/workflows/src/extension/workflow-tool-content.ts +4 -3
- package/dist/builtin/workflows/src/extension/workflow-tool-registration.ts +107 -13
- package/dist/builtin/workflows/src/extension/workflow-tool.ts +22 -12
- package/dist/builtin/workflows/src/tui/widget.ts +25 -6
- package/dist/core/agent-session-auto-compaction.js +10 -10
- 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 +11 -13
- package/dist/core/agent-session-events.js.map +1 -1
- package/dist/core/agent-session-methods.d.ts +1 -1
- 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 +5 -7
- package/dist/core/agent-session-models.js.map +1 -1
- package/dist/core/agent-session-prompt.js +4 -4
- package/dist/core/agent-session-prompt.js.map +1 -1
- package/dist/core/agent-session-retry.d.ts +4 -4
- package/dist/core/agent-session-retry.d.ts.map +1 -1
- package/dist/core/agent-session-retry.js +8 -34
- package/dist/core/agent-session-retry.js.map +1 -1
- package/dist/core/agent-session-tool-hooks.js +4 -4
- package/dist/core/agent-session-tool-hooks.js.map +1 -1
- package/dist/core/atomic-guide-command.d.ts.map +1 -1
- package/dist/core/atomic-guide-command.js +3 -10
- package/dist/core/atomic-guide-command.js.map +1 -1
- package/dist/core/event-bus.d.ts.map +1 -1
- package/dist/core/event-bus.js +19 -3
- package/dist/core/event-bus.js.map +1 -1
- package/dist/core/slash-commands.d.ts.map +1 -1
- package/dist/core/slash-commands.js +0 -3
- 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 +0 -12
- package/dist/core/system-prompt.js.map +1 -1
- package/dist/modes/interactive-engine/remote-command-catalog.d.ts +1 -1
- package/dist/modes/interactive-engine/remote-command-catalog.js +1 -1
- package/dist/modes/interactive-engine/remote-command-catalog.js.map +1 -1
- package/docs/intercom.md +29 -27
- package/docs/settings.md +2 -2
- package/docs/subagents.md +18 -15
- package/docs/usage.md +0 -9
- package/docs/workflows.md +27 -10
- package/npm-shrinkwrap.json +32 -32
- package/package.json +3 -3
- package/dist/builtin/subagents/prompts/gather-context-and-clarify.md +0 -27
- package/dist/builtin/subagents/prompts/parallel-cleanup.md +0 -61
- package/dist/builtin/subagents/prompts/parallel-context-build.md +0 -43
- package/dist/builtin/subagents/prompts/parallel-handoff-plan.md +0 -47
- package/dist/builtin/subagents/prompts/parallel-research.md +0 -34
- package/dist/builtin/subagents/prompts/parallel-review.md +0 -47
- package/dist/builtin/subagents/prompts/review-loop.md +0 -39
- package/dist/builtin/subagents/src/extension/doctor.ts +0 -188
- package/dist/builtin/subagents/src/runs/inprocess/attempt-handles.ts +0 -104
- /package/dist/builtin/subagents/src/runs/foreground/{subagent-executor-resume.ts → subagent-executor-cwd.ts} +0 -0
|
@@ -5,7 +5,7 @@ import type { SessionManager } from "../shared/persistence-restore.js";
|
|
|
5
5
|
import type { RunStatus, StageStatus } from "../shared/store-types.js";
|
|
6
6
|
import type { WorkflowBudget, WorkflowInputValues } from "../shared/types.js";
|
|
7
7
|
import type { WidgetFactory } from "../tui/store-widget-installer.js";
|
|
8
|
-
import type { RenderResultOpts,
|
|
8
|
+
import type { RenderResultOpts, WorkflowRegisteredToolResult } from "./render-result.js";
|
|
9
9
|
import type { PiUISurface } from "./wiring.js";
|
|
10
10
|
|
|
11
11
|
export type PiTheme = Record<string, string>;
|
|
@@ -231,4 +231,4 @@ export interface WorkflowToolArgs {
|
|
|
231
231
|
reason?: string;
|
|
232
232
|
}
|
|
233
233
|
|
|
234
|
-
export type WorkflowExecuteToolResult = PiAgentToolResult<
|
|
234
|
+
export type WorkflowExecuteToolResult = PiAgentToolResult<WorkflowRegisteredToolResult>;
|
|
@@ -197,6 +197,15 @@ export interface ModelCatalogEntry {
|
|
|
197
197
|
}
|
|
198
198
|
type ModelsResult = { action: "models"; models: ModelCatalogEntry[] };
|
|
199
199
|
|
|
200
|
+
export type WorkflowTimeoutResult = {
|
|
201
|
+
action: NonNullable<import("./public-types.js").WorkflowToolArgs["action"]>;
|
|
202
|
+
status: "failed";
|
|
203
|
+
code: "WORKFLOW_TIMEOUT";
|
|
204
|
+
timeoutMs: number;
|
|
205
|
+
runId?: string;
|
|
206
|
+
error: string;
|
|
207
|
+
};
|
|
208
|
+
|
|
200
209
|
export type WorkflowToolResult =
|
|
201
210
|
| ListResult
|
|
202
211
|
| StatusResult
|
|
@@ -216,6 +225,8 @@ export type WorkflowToolResult =
|
|
|
216
225
|
| ToolNodeControlResult
|
|
217
226
|
| ModelsResult;
|
|
218
227
|
|
|
228
|
+
export type WorkflowRegisteredToolResult = WorkflowToolResult | WorkflowTimeoutResult;
|
|
229
|
+
|
|
219
230
|
export interface RenderResultOpts {
|
|
220
231
|
isPartial?: boolean;
|
|
221
232
|
/**
|
|
@@ -310,7 +321,7 @@ function transcriptNoticeText(result: TranscriptResult): string {
|
|
|
310
321
|
return entriesText;
|
|
311
322
|
}
|
|
312
323
|
|
|
313
|
-
export function renderResult(result:
|
|
324
|
+
export function renderResult(result: WorkflowRegisteredToolResult | null | undefined, opts?: RenderResultOpts): string {
|
|
314
325
|
const partial = opts?.isPartial === true;
|
|
315
326
|
const themed = opts?.plain !== true;
|
|
316
327
|
|
|
@@ -319,9 +330,12 @@ export function renderResult(result: WorkflowToolResult, opts?: RenderResultOpts
|
|
|
319
330
|
// paths that return content without a structured payload. Dereferencing a
|
|
320
331
|
// missing `action` here previously threw and crashed the TUI render loop, so
|
|
321
332
|
// degrade gracefully instead.
|
|
322
|
-
if (result === null ||
|
|
333
|
+
if (result === null || result === undefined || typeof result.action !== "string") {
|
|
323
334
|
return partial ? "" : renderNotice("WORKFLOW", "no result", opts, themed);
|
|
324
335
|
}
|
|
336
|
+
if ("code" in result && result.code === "WORKFLOW_TIMEOUT") {
|
|
337
|
+
return renderNotice("WORKFLOW TIMEOUT", result.error, opts, themed);
|
|
338
|
+
}
|
|
325
339
|
|
|
326
340
|
switch (result.action) {
|
|
327
341
|
case "list": {
|
|
@@ -40,6 +40,7 @@ import {
|
|
|
40
40
|
releaseActiveBlockedClaim,
|
|
41
41
|
} from "./runtime-active-block-claim.js";
|
|
42
42
|
import { createDurableResumeRuntime, type DurableResumeRuntime } from "./runtime-durable-resume.js";
|
|
43
|
+
import { raceWorkflowRequestAbort } from "./workflow-request-abort.js";
|
|
43
44
|
|
|
44
45
|
// ---------------------------------------------------------------------------
|
|
45
46
|
// Options
|
|
@@ -122,6 +123,10 @@ export interface RuntimeDispatchOptions {
|
|
|
122
123
|
readonly actor?: WorkflowActor;
|
|
123
124
|
/** Run-level budget override used when a continuation is launched. */
|
|
124
125
|
readonly budget?: WorkflowBudget;
|
|
126
|
+
/** Cancels only the public request/admission wait, never detached execution after acknowledgement. */
|
|
127
|
+
readonly signal?: AbortSignal;
|
|
128
|
+
/** Reports the exact detached identity before startup admission is awaited. */
|
|
129
|
+
readonly onRunAccepted?: (runId: string) => void;
|
|
125
130
|
}
|
|
126
131
|
// ---------------------------------------------------------------------------
|
|
127
132
|
// Factory
|
|
@@ -397,7 +402,7 @@ export function createExtensionRuntime(opts: ExtensionRuntimeOpts = {}): Extensi
|
|
|
397
402
|
},
|
|
398
403
|
|
|
399
404
|
async dispatch(args: WorkflowToolArgs, options?: RuntimeDispatchOptions): Promise<WorkflowToolResult> {
|
|
400
|
-
await ensureDbosReady();
|
|
405
|
+
await raceWorkflowRequestAbort(ensureDbosReady(), options?.signal);
|
|
401
406
|
const defaultSessionDir = resolveDefaultStageSessionDir?.();
|
|
402
407
|
return dispatch(args, {
|
|
403
408
|
registry,
|
|
@@ -411,6 +416,8 @@ export function createExtensionRuntime(opts: ExtensionRuntimeOpts = {}): Extensi
|
|
|
411
416
|
models,
|
|
412
417
|
policy: options?.policy,
|
|
413
418
|
...(options?.origin === undefined ? {} : { origin: options.origin }),
|
|
419
|
+
...(options?.signal === undefined ? {} : { signal: options.signal }),
|
|
420
|
+
...(options?.onRunAccepted === undefined ? {} : { onRunAccepted: options.onRunAccepted }),
|
|
414
421
|
cwd: runtimeCwd,
|
|
415
422
|
...(defaultSessionDir !== undefined ? { defaultSessionDir } : {}),
|
|
416
423
|
});
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export function raceWorkflowRequestAbort<T>(operation: Promise<T>, signal: AbortSignal | undefined): Promise<T> {
|
|
2
|
+
if (signal === undefined) return operation;
|
|
3
|
+
if (signal.aborted) {
|
|
4
|
+
void operation.catch(() => {});
|
|
5
|
+
return Promise.reject(signal.reason ?? new DOMException("Workflow request aborted", "AbortError"));
|
|
6
|
+
}
|
|
7
|
+
const abort = Promise.withResolvers<never>();
|
|
8
|
+
const onAbort = (): void => {
|
|
9
|
+
abort.reject(signal.reason ?? new DOMException("Workflow request aborted", "AbortError"));
|
|
10
|
+
};
|
|
11
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
12
|
+
return Promise.race([operation, abort.promise]).finally(() => {
|
|
13
|
+
signal.removeEventListener("abort", onAbort);
|
|
14
|
+
});
|
|
15
|
+
}
|
|
@@ -4,12 +4,12 @@ import type { RunSnapshot } from "../shared/store-types.js";
|
|
|
4
4
|
import type { WorkflowSerializableValue } from "../shared/types.js";
|
|
5
5
|
import { fmtDuration, statusIcon } from "../tui/status-helpers.js";
|
|
6
6
|
import type { WorkflowToolArgs } from "./public-types.js";
|
|
7
|
-
import type { WorkflowToolResult } from "./render-result.js";
|
|
7
|
+
import type { WorkflowRegisteredToolResult, WorkflowToolResult } from "./render-result.js";
|
|
8
8
|
import type { ExtensionRuntime } from "./runtime.js";
|
|
9
9
|
import type { WorkflowRunStatusSummary, WorkflowStatusAwaitingInput } from "./workflow-status-summary.js";
|
|
10
10
|
import { getWorkflowStatusRenderRuns } from "./workflow-status-summary.js";
|
|
11
11
|
|
|
12
|
-
function stringifyWorkflowToolResult(result:
|
|
12
|
+
function stringifyWorkflowToolResult(result: WorkflowRegisteredToolResult): string {
|
|
13
13
|
return JSON.stringify(result, null, 2);
|
|
14
14
|
}
|
|
15
15
|
|
|
@@ -212,8 +212,9 @@ function renderStageToolContent(result: Extract<WorkflowToolResult, { action: "s
|
|
|
212
212
|
return lines.join("\n");
|
|
213
213
|
}
|
|
214
214
|
|
|
215
|
-
export function renderWorkflowToolContent(result:
|
|
215
|
+
export function renderWorkflowToolContent(result: WorkflowRegisteredToolResult, args: WorkflowToolArgs): string {
|
|
216
216
|
if (args.format === "json") return stringifyWorkflowToolResult(result);
|
|
217
|
+
if ("code" in result && result.code === "WORKFLOW_TIMEOUT") return stringifyWorkflowToolResult(result);
|
|
217
218
|
switch (result.action) {
|
|
218
219
|
case "transcript":
|
|
219
220
|
return renderTranscriptToolContent(result);
|
|
@@ -1,35 +1,127 @@
|
|
|
1
|
-
import type { ExtensionAPI, PiExecuteContext, WorkflowToolArgs } from "./public-types.js";
|
|
1
|
+
import type { ExtensionAPI, PiExecuteContext, PiToolOpts, WorkflowToolArgs } from "./public-types.js";
|
|
2
2
|
import { renderCall } from "./render-call.js";
|
|
3
3
|
import { dynamicTextRenderComponent } from "./render-component.js";
|
|
4
|
-
import type { WorkflowToolResult } from "./render-result.js";
|
|
4
|
+
import type { WorkflowRegisteredToolResult, WorkflowTimeoutResult, WorkflowToolResult } from "./render-result.js";
|
|
5
5
|
import { renderResult } from "./render-result.js";
|
|
6
6
|
import { workflowPolicyFromContext } from "./workflow-policy.js";
|
|
7
7
|
import { DEFAULT_PROMPT_GUIDANCE, WORKFLOW_TOOL_DESCRIPTION } from "./workflow-prompts.js";
|
|
8
|
+
import { raceWorkflowRequestAbort } from "./workflow-request-abort.js";
|
|
8
9
|
import { WorkflowParametersSchema } from "./workflow-schema.js";
|
|
9
10
|
import { renderWorkflowToolContent } from "./workflow-tool-content.js";
|
|
10
11
|
|
|
12
|
+
interface WorkflowToolRegistrationOptions {
|
|
13
|
+
/** Internal fixture seam; production registrations always use the fixed default. */
|
|
14
|
+
readonly requestTimeoutMs?: number;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export type WorkflowToolRegistrar = Pick<ExtensionAPI, "registerTool">;
|
|
18
|
+
|
|
19
|
+
export const WORKFLOW_TOOL_REQUEST_TIMEOUT_MS = 30_000;
|
|
20
|
+
|
|
21
|
+
type WorkflowToolExecutor = (
|
|
22
|
+
args: WorkflowToolArgs,
|
|
23
|
+
ctx: PiExecuteContext,
|
|
24
|
+
signal?: AbortSignal,
|
|
25
|
+
onRunAccepted?: (runId: string) => void,
|
|
26
|
+
) => Promise<WorkflowToolResult>;
|
|
27
|
+
|
|
28
|
+
const MUTATING_WORKFLOW_ACTIONS = new Set<NonNullable<WorkflowToolArgs["action"]>>([
|
|
29
|
+
"reload",
|
|
30
|
+
"run",
|
|
31
|
+
"send",
|
|
32
|
+
"pause",
|
|
33
|
+
"resume",
|
|
34
|
+
"interrupt",
|
|
35
|
+
"quit",
|
|
36
|
+
]);
|
|
37
|
+
|
|
38
|
+
function workflowToolTimeoutResult(
|
|
39
|
+
args: WorkflowToolArgs,
|
|
40
|
+
timeoutMs: number,
|
|
41
|
+
runId: string | undefined,
|
|
42
|
+
): WorkflowTimeoutResult {
|
|
43
|
+
const action = args.action ?? "run";
|
|
44
|
+
const prefix = `Workflow ${action} request timed out after ${timeoutMs}ms.`;
|
|
45
|
+
return {
|
|
46
|
+
action,
|
|
47
|
+
status: "failed",
|
|
48
|
+
code: "WORKFLOW_TIMEOUT",
|
|
49
|
+
timeoutMs,
|
|
50
|
+
...(runId === undefined ? {} : { runId }),
|
|
51
|
+
error: MUTATING_WORKFLOW_ACTIONS.has(action)
|
|
52
|
+
? `${prefix} The outcome is unknown. Inspect workflow status before retrying.`
|
|
53
|
+
: prefix,
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
async function executeWithWorkflowToolDeadline(
|
|
58
|
+
executeWorkflowTool: WorkflowToolExecutor,
|
|
59
|
+
params: WorkflowToolArgs,
|
|
60
|
+
ctx: PiExecuteContext,
|
|
61
|
+
callerSignal: AbortSignal | undefined,
|
|
62
|
+
timeoutMs: number,
|
|
63
|
+
): Promise<WorkflowRegisteredToolResult> {
|
|
64
|
+
callerSignal?.throwIfAborted();
|
|
65
|
+
const deadlineController = new AbortController();
|
|
66
|
+
const operationSignal =
|
|
67
|
+
callerSignal === undefined
|
|
68
|
+
? deadlineController.signal
|
|
69
|
+
: AbortSignal.any([callerSignal, deadlineController.signal]);
|
|
70
|
+
let timer: ReturnType<typeof setTimeout> | undefined;
|
|
71
|
+
let acceptedRunId: string | undefined;
|
|
72
|
+
const timeout = new Promise<WorkflowRegisteredToolResult>((resolve) => {
|
|
73
|
+
timer = setTimeout(() => {
|
|
74
|
+
const result = workflowToolTimeoutResult(params, timeoutMs, acceptedRunId);
|
|
75
|
+
resolve(result);
|
|
76
|
+
deadlineController.abort(new Error(result.error));
|
|
77
|
+
}, timeoutMs);
|
|
78
|
+
});
|
|
79
|
+
try {
|
|
80
|
+
return await raceWorkflowRequestAbort(
|
|
81
|
+
Promise.race([
|
|
82
|
+
executeWorkflowTool(params, ctx, operationSignal, (runId) => {
|
|
83
|
+
acceptedRunId = runId;
|
|
84
|
+
}),
|
|
85
|
+
timeout,
|
|
86
|
+
]),
|
|
87
|
+
callerSignal,
|
|
88
|
+
);
|
|
89
|
+
} finally {
|
|
90
|
+
if (timer !== undefined) clearTimeout(timer);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
11
94
|
export function registerWorkflowTool(
|
|
12
|
-
pi:
|
|
13
|
-
executeWorkflowTool:
|
|
95
|
+
pi: WorkflowToolRegistrar,
|
|
96
|
+
executeWorkflowTool: WorkflowToolExecutor,
|
|
14
97
|
runWithLifecycleSuppressedForPolicy: <T>(
|
|
15
98
|
policy: ReturnType<typeof workflowPolicyFromContext>,
|
|
16
99
|
fn: () => Promise<T>,
|
|
17
100
|
) => Promise<T>,
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
pi.registerTool
|
|
101
|
+
options: WorkflowToolRegistrationOptions = {},
|
|
102
|
+
): PiToolOpts<WorkflowToolArgs, WorkflowRegisteredToolResult> | undefined {
|
|
103
|
+
if (typeof pi.registerTool !== "function") return undefined;
|
|
104
|
+
const tool: PiToolOpts<WorkflowToolArgs, WorkflowRegisteredToolResult> = {
|
|
21
105
|
name: "workflow",
|
|
22
106
|
label: "workflow",
|
|
23
107
|
description: WORKFLOW_TOOL_DESCRIPTION,
|
|
24
108
|
parameters: WorkflowParametersSchema,
|
|
25
109
|
promptGuidelines: DEFAULT_PROMPT_GUIDANCE,
|
|
26
110
|
renderShell: "self",
|
|
27
|
-
execute: async (_toolCallId, params,
|
|
111
|
+
execute: async (_toolCallId, params, signal, _onUpdate, ctx) => {
|
|
28
112
|
const policy = workflowPolicyFromContext(ctx);
|
|
29
|
-
const details =
|
|
30
|
-
(
|
|
31
|
-
|
|
32
|
-
|
|
113
|
+
const details = await executeWithWorkflowToolDeadline(
|
|
114
|
+
(actionParams, actionContext, operationSignal, onRunAccepted) =>
|
|
115
|
+
(actionParams.action ?? "run") === "run"
|
|
116
|
+
? runWithLifecycleSuppressedForPolicy(policy, () =>
|
|
117
|
+
executeWorkflowTool(actionParams, actionContext, operationSignal, onRunAccepted),
|
|
118
|
+
)
|
|
119
|
+
: executeWorkflowTool(actionParams, actionContext, operationSignal, onRunAccepted),
|
|
120
|
+
params,
|
|
121
|
+
ctx,
|
|
122
|
+
signal,
|
|
123
|
+
options.requestTimeoutMs ?? WORKFLOW_TOOL_REQUEST_TIMEOUT_MS,
|
|
124
|
+
);
|
|
33
125
|
return {
|
|
34
126
|
content: [{ type: "text", text: renderWorkflowToolContent(details, params) }],
|
|
35
127
|
details,
|
|
@@ -47,5 +139,7 @@ export function registerWorkflowTool(
|
|
|
47
139
|
}),
|
|
48
140
|
);
|
|
49
141
|
},
|
|
50
|
-
}
|
|
142
|
+
};
|
|
143
|
+
pi.registerTool(tool);
|
|
144
|
+
return tool;
|
|
51
145
|
}
|
|
@@ -8,6 +8,7 @@ import type { ExtensionRuntime } from "./runtime.js";
|
|
|
8
8
|
import { formatWorkflowResourceLoadWarning } from "./workflow-command-surfaces.js";
|
|
9
9
|
import { workflowPolicyFromContext } from "./workflow-policy.js";
|
|
10
10
|
import type { WorkflowReloadReport } from "./workflow-reload-report.js";
|
|
11
|
+
import { raceWorkflowRequestAbort } from "./workflow-request-abort.js";
|
|
11
12
|
import { buildWorkflowStatusListing, setWorkflowStatusRenderRuns } from "./workflow-status-summary.js";
|
|
12
13
|
import { isWorkflowStageToolContext, resolveRunId, topLevelExpandedSnapshots } from "./workflow-targets.js";
|
|
13
14
|
import { workflowGetResult } from "./workflow-tool-content.js";
|
|
@@ -66,10 +67,17 @@ export function makeExecuteWorkflowTool(
|
|
|
66
67
|
reloadWorkflowResources: () => Promise<WorkflowReloadReport | undefined> | undefined,
|
|
67
68
|
ensureWorkflowResourcesLoaded: () => Promise<void> | void = () => {},
|
|
68
69
|
sendDeps: WorkflowSendDeps = {},
|
|
69
|
-
): (
|
|
70
|
+
): (
|
|
71
|
+
args: WorkflowToolArgs,
|
|
72
|
+
ctx: PiExecuteContext,
|
|
73
|
+
signal?: AbortSignal,
|
|
74
|
+
onRunAccepted?: (runId: string) => void,
|
|
75
|
+
) => Promise<WorkflowToolResult> {
|
|
70
76
|
return async function executeWorkflowTool(
|
|
71
77
|
args: WorkflowToolArgs,
|
|
72
78
|
ctx: PiExecuteContext,
|
|
79
|
+
signal?: AbortSignal,
|
|
80
|
+
onRunAccepted?: (runId: string) => void,
|
|
73
81
|
): Promise<WorkflowToolResult> {
|
|
74
82
|
const action = args.action ?? "run";
|
|
75
83
|
const runId = args.runId ?? "";
|
|
@@ -84,10 +92,12 @@ export function makeExecuteWorkflowTool(
|
|
|
84
92
|
}
|
|
85
93
|
const policy: WorkflowExecutionPolicy = workflowPolicyFromContext(ctx);
|
|
86
94
|
const getRuntime = (): ExtensionRuntime => (typeof runtime === "function" ? runtime(ctx) : runtime);
|
|
95
|
+
const awaitRequest = <T>(operation: Promise<T>): Promise<T> => raceWorkflowRequestAbort(operation, signal);
|
|
87
96
|
const ensureWorkflowResourcesVisible = async (): Promise<void> => {
|
|
88
97
|
try {
|
|
89
|
-
await ensureWorkflowResourcesLoaded();
|
|
98
|
+
await awaitRequest(Promise.resolve(ensureWorkflowResourcesLoaded()));
|
|
90
99
|
} catch (error) {
|
|
100
|
+
if (signal?.aborted === true) throw signal.reason ?? error;
|
|
91
101
|
ctx.ui?.notify?.(formatWorkflowResourceLoadWarning(error), "warning");
|
|
92
102
|
}
|
|
93
103
|
};
|
|
@@ -111,13 +121,13 @@ export function makeExecuteWorkflowTool(
|
|
|
111
121
|
case "list":
|
|
112
122
|
case "inputs": {
|
|
113
123
|
await ensureWorkflowResourcesVisible();
|
|
114
|
-
return getRuntime().dispatch(args, { policy });
|
|
124
|
+
return awaitRequest(getRuntime().dispatch(args, { policy, signal }));
|
|
115
125
|
}
|
|
116
126
|
case "run": {
|
|
117
127
|
await ensureWorkflowResourcesVisible();
|
|
118
128
|
// A tool launch is the agent's own action: it is attributed as such and
|
|
119
129
|
// the tool result already reports the run, so it raises no chat notice.
|
|
120
|
-
return getRuntime().dispatch(args, { policy, origin: "agent" });
|
|
130
|
+
return awaitRequest(getRuntime().dispatch(args, { policy, origin: "agent", signal, onRunAccepted }));
|
|
121
131
|
}
|
|
122
132
|
case "status": {
|
|
123
133
|
const target = args.runId;
|
|
@@ -127,7 +137,7 @@ export function makeExecuteWorkflowTool(
|
|
|
127
137
|
return { action: "statusDetail", runId: target, error: resolved.message };
|
|
128
138
|
}
|
|
129
139
|
if (resolved.kind === "not_found") {
|
|
130
|
-
const durable = await getRuntime().inspectDurableWorkflow(target);
|
|
140
|
+
const durable = await awaitRequest(getRuntime().inspectDurableWorkflow(target));
|
|
131
141
|
return durable.kind === "found"
|
|
132
142
|
? { action: "statusDetail", runId: target, detail: durable.detail }
|
|
133
143
|
: { action: "statusDetail", runId: target, error: durable.message };
|
|
@@ -150,7 +160,7 @@ export function makeExecuteWorkflowTool(
|
|
|
150
160
|
case "stages":
|
|
151
161
|
case "stage":
|
|
152
162
|
case "transcript": {
|
|
153
|
-
const resolved = await resolveDurableInspectionSource(args, getRuntime());
|
|
163
|
+
const resolved = await awaitRequest(resolveDurableInspectionSource(args, getRuntime()));
|
|
154
164
|
if (resolved.kind === "error") return durableInspectionError(action, args.runId ?? "", resolved.message);
|
|
155
165
|
const source = resolved.kind === "durable" ? resolved.source : undefined;
|
|
156
166
|
if (action === "stages") return workflowStagesResult(args, source);
|
|
@@ -158,17 +168,17 @@ export function makeExecuteWorkflowTool(
|
|
|
158
168
|
return workflowTranscriptResult(args, source);
|
|
159
169
|
}
|
|
160
170
|
case "send":
|
|
161
|
-
return workflowSendAction(args, sendDeps);
|
|
171
|
+
return awaitRequest(workflowSendAction(args, sendDeps));
|
|
162
172
|
case "pause":
|
|
163
|
-
return workflowPauseAction(args);
|
|
173
|
+
return awaitRequest(workflowPauseAction(args));
|
|
164
174
|
case "reload":
|
|
165
|
-
return workflowReloadAction(args, { reloadWorkflowResources });
|
|
175
|
+
return awaitRequest(workflowReloadAction(args, { reloadWorkflowResources }));
|
|
166
176
|
case "quit":
|
|
167
|
-
return workflowQuitAction(args);
|
|
177
|
+
return awaitRequest(workflowQuitAction(args));
|
|
168
178
|
case "interrupt":
|
|
169
|
-
return workflowInterruptAction(args);
|
|
179
|
+
return awaitRequest(workflowInterruptAction(args));
|
|
170
180
|
case "resume":
|
|
171
|
-
return workflowResumeAction(args, { getRuntime, policy, ensureWorkflowResourcesLoaded });
|
|
181
|
+
return awaitRequest(workflowResumeAction(args, { getRuntime, policy, ensureWorkflowResourcesLoaded }));
|
|
172
182
|
default: {
|
|
173
183
|
const _exhaustive: never = action;
|
|
174
184
|
throw new Error(`Workflow extension: unknown action "${_exhaustive}"`);
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* badges (`✓ n ● n ○ n ✗ n`) in the title.
|
|
7
7
|
* - One compact rounded card per run:
|
|
8
8
|
* title: `<status glyph> <full id>`
|
|
9
|
-
* row 1: `<name> · <dim mode · progress · duration>`
|
|
9
|
+
* row 1: `<name> · <dim mode · progress · live tool nodes · duration>`
|
|
10
10
|
* - Collapsed single-line form below 80 cells:
|
|
11
11
|
* `▾ N background · X ●` in dim+warning.
|
|
12
12
|
*
|
|
@@ -215,6 +215,17 @@ function elapsedLabel(run: RunSnapshot, now: number): string {
|
|
|
215
215
|
return "";
|
|
216
216
|
}
|
|
217
217
|
|
|
218
|
+
function activeToolNodes(run: RunSnapshot): NonNullable<RunSnapshot["toolNodes"]> {
|
|
219
|
+
return (run.toolNodes ?? []).filter((node) => node.status === "pending" || node.status === "running");
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
function activeToolLabel(run: RunSnapshot): string | undefined {
|
|
223
|
+
const nodes = activeToolNodes(run);
|
|
224
|
+
if (nodes.length === 0) return undefined;
|
|
225
|
+
const details = nodes.map((node) => `${node.name} · ${node.status}`).join(", ");
|
|
226
|
+
return nodes.length === 1 ? details : `${nodes.length} tools · ${details}`;
|
|
227
|
+
}
|
|
228
|
+
|
|
218
229
|
function metaLine(run: RunSnapshot, now: number): string {
|
|
219
230
|
if (run.endedAt !== undefined) {
|
|
220
231
|
return elapsedLabel(run, now);
|
|
@@ -224,6 +235,8 @@ function metaLine(run: RunSnapshot, now: number): string {
|
|
|
224
235
|
const parts: string[] = [modeLabel(run)];
|
|
225
236
|
const prog = progressLabel(run);
|
|
226
237
|
if (prog) parts.push(prog);
|
|
238
|
+
const tools = activeToolLabel(run);
|
|
239
|
+
if (tools) parts.push(tools);
|
|
227
240
|
const elapsed = elapsedLabel(run, now);
|
|
228
241
|
if (elapsed) parts.push(elapsed);
|
|
229
242
|
return parts.join(" · ");
|
|
@@ -306,7 +319,7 @@ function plainRunLines(run: RunSnapshot, now: number, allRuns: readonly RunSnaps
|
|
|
306
319
|
// Collapsed (< 80 cell) form
|
|
307
320
|
// ---------------------------------------------------------------------------
|
|
308
321
|
|
|
309
|
-
function themedCollapsed(counts: RunCounts, theme: GraphTheme): string {
|
|
322
|
+
function themedCollapsed(counts: RunCounts, activeTools: number, theme: GraphTheme): string {
|
|
310
323
|
const mauve = hexToAnsi(theme.mauve);
|
|
311
324
|
const dim = hexToAnsi(theme.dim);
|
|
312
325
|
const muted = hexToAnsi(theme.textMuted);
|
|
@@ -316,15 +329,18 @@ function themedCollapsed(counts: RunCounts, theme: GraphTheme): string {
|
|
|
316
329
|
const paused = counts.paused > 0 ? `${dim} · ${RESET}${warning}${counts.paused} ❚❚${RESET}` : "";
|
|
317
330
|
const quit = counts.quit > 0 ? `${dim} · ${RESET}${warning}${counts.quit} quit${RESET}` : "";
|
|
318
331
|
const blocked = counts.blocked > 0 ? `${dim} · ${RESET}${warning}${counts.blocked} ↑${RESET}` : "";
|
|
319
|
-
|
|
332
|
+
const tools =
|
|
333
|
+
activeTools > 0 ? `${dim} · ${RESET}${warning}${activeTools} tool${activeTools === 1 ? "" : "s"}${RESET}` : "";
|
|
334
|
+
return ` ${mauve}▾${RESET} ${muted}${total} background${RESET}${dim} · ${RESET}${warning}${active} ●${RESET}${paused}${quit}${blocked}${tools}`;
|
|
320
335
|
}
|
|
321
336
|
|
|
322
|
-
function plainCollapsed(counts: RunCounts): string {
|
|
337
|
+
function plainCollapsed(counts: RunCounts, activeTools: number): string {
|
|
323
338
|
const total = counts.active + counts.paused + counts.quit + counts.done + counts.blocked + counts.failed;
|
|
324
339
|
const paused = counts.paused > 0 ? ` · ${counts.paused} ❚❚` : "";
|
|
325
340
|
const quit = counts.quit > 0 ? ` · ${counts.quit} quit` : "";
|
|
326
341
|
const blocked = counts.blocked > 0 ? ` · ${counts.blocked} ↑` : "";
|
|
327
|
-
|
|
342
|
+
const tools = activeTools > 0 ? ` · ${activeTools} tool${activeTools === 1 ? "" : "s"}` : "";
|
|
343
|
+
return ` ▾ ${total} background · ${counts.active} ●${paused}${quit}${blocked}${tools}`;
|
|
328
344
|
}
|
|
329
345
|
|
|
330
346
|
// ---------------------------------------------------------------------------
|
|
@@ -369,10 +385,13 @@ export function buildThemedWidgetLines(
|
|
|
369
385
|
|
|
370
386
|
const themed = piTheme !== undefined;
|
|
371
387
|
const graphTheme = deriveGraphTheme({});
|
|
388
|
+
const activeTools = display.filter(isActive).reduce((total, run) => total + activeToolNodes(run).length, 0);
|
|
372
389
|
|
|
373
390
|
// Collapsed single-line form for narrow terminals.
|
|
374
391
|
if (width < COLLAPSED_BREAKPOINT_COLS) {
|
|
375
|
-
return [
|
|
392
|
+
return [
|
|
393
|
+
themed ? themedCollapsed(visibleCounts, activeTools, graphTheme) : plainCollapsed(visibleCounts, activeTools),
|
|
394
|
+
];
|
|
376
395
|
}
|
|
377
396
|
|
|
378
397
|
const total = display.length;
|
|
@@ -287,17 +287,17 @@ export function _schedulePostAutoCompactionContinuationProbe(_reason, willRetry)
|
|
|
287
287
|
pending = new Promise((resolve) => {
|
|
288
288
|
setTimeout(() => {
|
|
289
289
|
void (async () => {
|
|
290
|
-
const
|
|
290
|
+
const settleIfOwned = async () => {
|
|
291
291
|
if (fallbackScopeGeneration === undefined ||
|
|
292
292
|
this._fallbackOriginGeneration !== fallbackScopeGeneration ||
|
|
293
|
-
typeof this.
|
|
293
|
+
typeof this._settleFallbackModelScope !== "function")
|
|
294
294
|
return;
|
|
295
295
|
try {
|
|
296
|
-
await this.
|
|
296
|
+
await this._settleFallbackModelScope();
|
|
297
297
|
}
|
|
298
298
|
catch {
|
|
299
|
-
// A listener must not strand the continuation waiter. The
|
|
300
|
-
// state was already
|
|
299
|
+
// A listener must not strand the continuation waiter. The fallback
|
|
300
|
+
// state was already settled before lifecycle notifications ran.
|
|
301
301
|
}
|
|
302
302
|
};
|
|
303
303
|
try {
|
|
@@ -314,12 +314,12 @@ export function _schedulePostAutoCompactionContinuationProbe(_reason, willRetry)
|
|
|
314
314
|
if (this.isCompacting || this.isStreaming)
|
|
315
315
|
return;
|
|
316
316
|
if (!this.agent.hasQueuedMessages()) {
|
|
317
|
-
await
|
|
317
|
+
await settleIfOwned();
|
|
318
318
|
return;
|
|
319
319
|
}
|
|
320
|
-
// A queued message starts the next user turn.
|
|
321
|
-
//
|
|
322
|
-
await
|
|
320
|
+
// A queued message starts the next user turn. Settle before Agent
|
|
321
|
+
// snapshots the next request's model.
|
|
322
|
+
await settleIfOwned();
|
|
323
323
|
}
|
|
324
324
|
if (this._pendingPostCompactionContinuation !== pending)
|
|
325
325
|
return;
|
|
@@ -329,7 +329,7 @@ export function _schedulePostAutoCompactionContinuationProbe(_reason, willRetry)
|
|
|
329
329
|
this._pendingPostCompactionContinuation = undefined;
|
|
330
330
|
await this._resumeAfterAutoCompaction();
|
|
331
331
|
if (willRetry && this._pendingPostCompactionContinuation === undefined) {
|
|
332
|
-
await
|
|
332
|
+
await settleIfOwned();
|
|
333
333
|
}
|
|
334
334
|
}
|
|
335
335
|
finally {
|