@marcoscale98/pi-extensible-workflows 5.19.1-fork.1 → 6.2.1-fork.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +77 -0
- package/README.md +2 -2
- package/dist/src/agent-execution.d.ts +43 -10
- package/dist/src/agent-execution.js +365 -127
- package/dist/src/background-widget.d.ts +0 -1
- package/dist/src/background-widget.js +4 -9
- package/dist/src/decoders.d.ts +2 -3
- package/dist/src/decoders.js +33 -37
- package/dist/src/execution.d.ts +2 -0
- package/dist/src/execution.js +5 -5
- package/dist/src/host-delivery.js +4 -8
- package/dist/src/host-navigator.js +5 -2
- package/dist/src/host-phases.d.ts +1 -0
- package/dist/src/host-recovery.d.ts +6 -6
- package/dist/src/host-recovery.js +14 -22
- package/dist/src/host-runtime.js +56 -5
- package/dist/src/host-view.d.ts +1 -1
- package/dist/src/host-view.js +17 -15
- package/dist/src/host.js +62 -87
- package/dist/src/index.d.ts +1 -2
- package/dist/src/index.js +1523 -1630
- package/dist/src/index.js.map +3 -3
- package/dist/src/paths.d.ts +7 -6
- package/dist/src/paths.js +24 -15
- package/dist/src/persistence.d.ts +1 -0
- package/dist/src/persistence.js +1 -0
- package/dist/src/pi-runtime-runner.js +4 -1
- package/dist/src/registry.d.ts +9 -7
- package/dist/src/registry.js +28 -70
- package/dist/src/runtime/agent-runner.d.ts +2 -0
- package/dist/src/session-lease.d.ts +1 -1
- package/dist/src/session-lease.js +45 -11
- package/dist/src/settings.d.ts +6 -3
- package/dist/src/settings.js +36 -47
- package/dist/src/store.d.ts +3 -1
- package/dist/src/store.js +17 -1
- package/dist/src/trajectory.d.ts +0 -1
- package/dist/src/trajectory.js +2 -3
- package/dist/src/types.d.ts +100 -44
- package/dist/src/types.js +4 -7
- package/dist/src/utils.d.ts +3 -2
- package/dist/src/utils.js +20 -10
- package/dist/src/validation.d.ts +8 -2
- package/dist/src/validation.js +122 -86
- package/dist/starter/index.js +8 -9
- package/dist/starter/index.js.map +1 -1
- package/dist/starter/review-loop.js +8 -7
- package/dist/subagents/index.js +654 -814
- package/dist/subagents/index.js.map +3 -3
- package/dist/subagents/src/contracts.d.ts +4 -3
- package/dist/subagents/src/contracts.js +2 -2
- package/dist/subagents/src/index.d.ts +1 -1
- package/dist/subagents/src/index.js +1 -2
- package/dist/subagents/src/manager.js +49 -70
- package/dist/subagents/src/navigator.js +3 -14
- package/dist/subagents/src/view.d.ts +0 -1
- package/dist/subagents/src/view.js +2 -16
- package/dist/subagents/src/worktree.js +1 -1
- package/dist/trajectory/assets/index.html +8 -8
- package/dist/trajectory/index.js +487 -891
- package/dist/trajectory/index.js.map +3 -3
- package/dist/trajectory/src/assets/index.html +8 -8
- package/dist/trajectory/src/index.js +126 -86
- package/dist/trajectory/src/server.js +264 -25
- package/dist/trajectory/src/server.js.map +3 -3
- package/dist/trajectory/src/startup-mutex.d.ts +17 -0
- package/dist/trajectory/src/startup-mutex.js +172 -0
- package/examples/workflow-extension-template/README.md +4 -7
- package/examples/workflow-extension-template/extension.test.mjs +4 -8
- package/examples/workflow-extension-template/index.js +0 -3
- package/package.json +5 -5
- package/skills/pi-extensible-workflows/SKILL.md +10 -8
- package/src/agent-execution.ts +339 -127
- package/src/background-widget.ts +4 -10
- package/src/decoders.ts +31 -37
- package/src/execution.ts +5 -5
- package/src/host-delivery.ts +4 -8
- package/src/host-navigator.ts +4 -2
- package/src/host-phases.ts +1 -1
- package/src/host-recovery.ts +20 -26
- package/src/host-runtime.ts +44 -6
- package/src/host-view.ts +18 -11
- package/src/host.ts +62 -113
- package/src/index.ts +1 -2
- package/src/paths.ts +18 -12
- package/src/persistence.ts +1 -0
- package/src/pi-runtime-runner.ts +3 -1
- package/src/registry.ts +27 -63
- package/src/runtime/agent-runner.ts +2 -0
- package/src/session-lease.ts +34 -6
- package/src/settings.ts +35 -47
- package/src/store.ts +18 -2
- package/src/trajectory.ts +2 -4
- package/src/types.ts +71 -30
- package/src/utils.ts +18 -9
- package/src/validation.ts +102 -79
- package/starter/index.ts +0 -1
- package/starter/review-loop.ts +8 -7
- package/starter/roles/developer.md +0 -1
- package/starter/roles/oracle.md +0 -1
- package/starter/roles/researcher.md +0 -1
- package/starter/roles/reviewer.md +0 -1
- package/starter/roles/scout.md +0 -1
- package/subagents/README.md +8 -14
- package/subagents/src/contracts.ts +3 -4
- package/subagents/src/index.ts +2 -3
- package/subagents/src/manager.ts +45 -125
- package/subagents/src/navigator.ts +4 -13
- package/subagents/src/view.ts +4 -14
- package/subagents/src/worktree.ts +1 -1
- package/trajectory/src/assets/index.html +8 -8
- package/trajectory/src/index.ts +106 -82
- package/trajectory/src/server.ts +44 -8
- package/trajectory/src/startup-mutex.ts +127 -0
- package/dist/src/roles.d.ts +0 -70
- package/dist/src/roles.js +0 -326
- package/dist/starter/roles/developer.md +0 -6
- package/dist/starter/roles/oracle.md +0 -18
- package/dist/starter/roles/researcher.md +0 -20
- package/dist/starter/roles/reviewer.md +0 -19
- package/dist/starter/roles/scout.md +0 -18
- package/examples/workflow-extension-template/roles/reviewer.md +0 -4
- package/src/roles.ts +0 -327
|
@@ -191,7 +191,7 @@ function renderableAgent(value) {
|
|
|
191
191
|
return false;
|
|
192
192
|
if (value.model.thinking !== undefined && typeof value.model.thinking !== "string")
|
|
193
193
|
return false;
|
|
194
|
-
for (const key of ["id", "label", "parentId", "
|
|
194
|
+
for (const key of ["id", "label", "parentId", "requestedModel"])
|
|
195
195
|
if (value[key] !== undefined && typeof value[key] !== "string")
|
|
196
196
|
return false;
|
|
197
197
|
for (const key of ["attempts", "startedAt", "durationMs", "lastEventAt"])
|
|
@@ -554,8 +554,7 @@ function renderFrame(runs, now, width, offset = 0, theme, scrolling = false, siz
|
|
|
554
554
|
? mark(agent.state, now, paint, run.state)
|
|
555
555
|
: paint(silent >= STALL_MS ? ROLE.failed : ROLE.warn, "⚠")} ${
|
|
556
556
|
// The label is what the workflow called this agent — `reviewer #2`,
|
|
557
|
-
// `scout (api)` —
|
|
558
|
-
// agents of one role are otherwise indistinguishable.
|
|
557
|
+
// `scout (api)` — preserving an explicit label when supplied.
|
|
559
558
|
agent.label ?? agent.name}${silent === undefined
|
|
560
559
|
? ""
|
|
561
560
|
: ` ${paint(silent >= STALL_MS ? ROLE.failed : ROLE.warn, `${silent >= STALL_MS ? "stalled" : "quiet"} ${formatElapsed(silent)}`)}`}`,
|
|
@@ -696,18 +695,15 @@ function receiptFor(run, fallback) {
|
|
|
696
695
|
agents: agents.map((agent) => ({
|
|
697
696
|
id: agent.id,
|
|
698
697
|
...(agent.parentId === undefined ? {} : { parentId: agent.parentId }),
|
|
699
|
-
//
|
|
700
|
-
// from. Two agents of one role are otherwise indistinguishable.
|
|
698
|
+
// Preserve explicit display labels.
|
|
701
699
|
name: agent.label ?? agent.name,
|
|
702
700
|
state: agent.state,
|
|
703
|
-
//
|
|
704
|
-
// name alone does not say whether it reasoned cheaply or deeply.
|
|
701
|
+
// Show both model and thinking level.
|
|
705
702
|
...(agent.model.model
|
|
706
703
|
? {
|
|
707
704
|
model: `${agent.model.model}${agent.model.thinking ? `:${agent.model.thinking}` : ""}`,
|
|
708
705
|
}
|
|
709
706
|
: {}),
|
|
710
|
-
...(agent.role ? { role: agent.role } : {}),
|
|
711
707
|
...(agent.requestedModel ? { requestedModel: agent.requestedModel } : {}),
|
|
712
708
|
tools: [...agent.tools],
|
|
713
709
|
// Cache reads dwarf real input and cost almost nothing, so the split is
|
|
@@ -782,7 +778,6 @@ export function renderReceipt(data, expanded, theme) {
|
|
|
782
778
|
lines.push(`${prefix}${lastAgent ? "╰─" : "├─"} ${glyph(agent.state)} ${agent.name} ${theme.fg("muted", detail)}`);
|
|
783
779
|
const under = `${prefix}${lastAgent ? " " : "│ "} `;
|
|
784
780
|
const meta = [
|
|
785
|
-
agent.role ? `role ${agent.role}` : "",
|
|
786
781
|
agent.requestedModel && agent.requestedModel !== agent.model
|
|
787
782
|
? `via ${agent.requestedModel}`
|
|
788
783
|
: "",
|
package/dist/src/decoders.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type
|
|
1
|
+
import { type PreparedAgentConfiguration, type BudgetApprovalRequest, type JsonValue, type LaunchSnapshot, type RunRecord, type WorkflowBudgetUsage, type WorkflowExtensionSettings } from "./types.js";
|
|
2
2
|
import type { OwnershipRecord } from "./agent-execution.js";
|
|
3
3
|
export interface EffectiveSystemPrompt {
|
|
4
4
|
sessionId: string;
|
|
@@ -13,7 +13,6 @@ export interface RunSummaryAgent {
|
|
|
13
13
|
name: string;
|
|
14
14
|
label?: string;
|
|
15
15
|
state: string;
|
|
16
|
-
role?: string;
|
|
17
16
|
attempts: number;
|
|
18
17
|
}
|
|
19
18
|
export interface RunSummaryArtifacts {
|
|
@@ -59,7 +58,7 @@ export type Journal = {
|
|
|
59
58
|
awaiting?: Record<string, AwaitingCheckpoint>;
|
|
60
59
|
decisions?: Record<string, PendingWorkflowDecision>;
|
|
61
60
|
};
|
|
62
|
-
export declare function
|
|
61
|
+
export declare function decodePreparedAgentConfiguration(value: unknown): PreparedAgentConfiguration | undefined;
|
|
63
62
|
export declare function decodeWorkflowExtensions(value: unknown): WorkflowExtensionSettings | undefined;
|
|
64
63
|
export declare function decodePersistedRun(value: unknown, allowLegacyAgentSessions?: boolean): PersistedRun | undefined;
|
|
65
64
|
export declare function isPersistedRun(value: unknown): value is PersistedRun;
|
package/dist/src/decoders.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AGENT_STATES, BUDGET_DIMENSIONS, BUDGET_EVENT_TYPES, RUN_STATES, isContextFileScope } from "./types.js";
|
|
1
|
+
import { AGENT_STATES, BUDGET_DIMENSIONS, BUDGET_EVENT_TYPES, RUN_STATES, isContextFileScope, LAUNCH_SNAPSHOT_IDENTITY_VERSION } from "./types.js";
|
|
2
2
|
import { finiteNumber, isThinkingLevel, isWorkflowErrorCode, jsonValue, object, positiveInteger, validWorkflowExtensionNamespace } from "./utils.js";
|
|
3
3
|
const INVALID_PERSISTED_VALUE = Symbol("invalid persisted value");
|
|
4
4
|
function integer(value) { return finiteNumber(value) && Number.isInteger(value); }
|
|
@@ -64,28 +64,21 @@ function decodeAgentResourceSelectors(value) {
|
|
|
64
64
|
return { ...(skills === undefined ? {} : { skills }), ...(extensions === undefined ? {} : { extensions }), ...(tools === undefined ? {} : { tools }) };
|
|
65
65
|
}
|
|
66
66
|
function decodeContextFileScopes(value) { return decodeArray(value, (scope) => isContextFileScope(scope) ? scope : undefined); }
|
|
67
|
-
export function
|
|
68
|
-
if (!object(value))
|
|
67
|
+
export function decodePreparedAgentConfiguration(value) {
|
|
68
|
+
if (!object(value) || !jsonValue(value) || typeof value.projectTrusted !== "boolean" || typeof value.systemPromptAppend !== "string")
|
|
69
69
|
return undefined;
|
|
70
|
-
const
|
|
71
|
-
const
|
|
72
|
-
const
|
|
73
|
-
const
|
|
74
|
-
const
|
|
75
|
-
const skills = value.skills === undefined ? undefined : decodeStringArray(value.skills);
|
|
76
|
-
const extensions = value.extensions === undefined ? undefined : decodeStringArray(value.extensions);
|
|
77
|
-
const overrideSystemPrompt = optionalBoolean(value.overrideSystemPrompt);
|
|
70
|
+
const model = decodeModelSpec(value.model);
|
|
71
|
+
const tools = decodeStringArray(value.tools);
|
|
72
|
+
const skills = decodeStringArray(value.skills);
|
|
73
|
+
const extensions = decodeStringArray(value.extensions);
|
|
74
|
+
const settings = decodeWorkflowExtensions(value.settings);
|
|
78
75
|
const contextFiles = value.contextFiles === undefined ? undefined : decodeContextFileScopes(value.contextFiles);
|
|
79
|
-
const
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
if (
|
|
76
|
+
const label = optionalString(value.label);
|
|
77
|
+
const systemPrompt = optionalString(value.systemPrompt);
|
|
78
|
+
const excludeTools = value.excludeTools === undefined ? undefined : decodeStringArray(value.excludeTools);
|
|
79
|
+
if (!model || !tools || !skills || !extensions || !settings || label === INVALID_PERSISTED_VALUE || systemPrompt === INVALID_PERSISTED_VALUE || value.contextFiles !== undefined && !contextFiles || value.excludeTools !== undefined && !excludeTools)
|
|
83
80
|
return undefined;
|
|
84
|
-
|
|
85
|
-
return {
|
|
86
|
-
...(prompt === undefined ? {} : { prompt }), ...(description === undefined ? {} : { description }), ...(foldedModel === undefined ? {} : { model: foldedModel }),
|
|
87
|
-
...(tools === undefined ? {} : { tools }), ...(skills === undefined ? {} : { skills }), ...(extensions === undefined ? {} : { extensions }), ...(overrideSystemPrompt === undefined ? {} : { overrideSystemPrompt }), ...(contextFiles === undefined ? {} : { contextFiles }), ...(extensionSettings === undefined ? {} : { extensionSettings }),
|
|
88
|
-
};
|
|
81
|
+
return { model, tools, skills, extensions, settings, projectTrusted: value.projectTrusted, systemPromptAppend: value.systemPromptAppend, ...(contextFiles === undefined ? {} : { contextFiles }), ...(excludeTools === undefined ? {} : { excludeTools }), ...(label === undefined ? {} : { label }), ...(systemPrompt === undefined ? {} : { systemPrompt }) };
|
|
89
82
|
}
|
|
90
83
|
function decodeWorkflowMetadata(value) {
|
|
91
84
|
if (!object(value) || typeof value.name !== "string")
|
|
@@ -211,17 +204,25 @@ function decodeScheduledAgentOptions(value) {
|
|
|
211
204
|
const parentBreadcrumb = optionalString(value.parentBreadcrumb);
|
|
212
205
|
const worktreeOwner = optionalString(value.worktreeOwner);
|
|
213
206
|
const model = optionalString(value.model);
|
|
214
|
-
const role = typeof value.role === "string" ? value.role : undefined;
|
|
215
207
|
const contextFiles = value.contextFiles === undefined ? undefined : decodeContextFileScopes(value.contextFiles);
|
|
216
208
|
const schema = value.schema === undefined ? undefined : decodeJsonObject(value.schema);
|
|
217
209
|
const retries = optionalNumber(value.retries);
|
|
218
210
|
const timeoutMs = value.timeoutMs;
|
|
211
|
+
if (value.projectTrusted !== undefined && typeof value.projectTrusted !== "boolean")
|
|
212
|
+
return undefined;
|
|
213
|
+
const rawCapabilities = value.capabilities;
|
|
214
|
+
const capabilities = rawCapabilities === undefined ? undefined : object(rawCapabilities) ? { tools: decodeStringArray(rawCapabilities.tools), skills: decodeStringArray(rawCapabilities.skills), extensions: decodeStringArray(rawCapabilities.extensions) } : undefined;
|
|
215
|
+
if (rawCapabilities !== undefined && (!capabilities?.tools || !capabilities.skills || !capabilities.extensions))
|
|
216
|
+
return undefined;
|
|
217
|
+
const configuration = value.configuration === undefined ? undefined : decodePreparedAgentConfiguration(value.configuration);
|
|
218
|
+
if (value.configuration !== undefined && configuration === undefined)
|
|
219
|
+
return undefined;
|
|
219
220
|
const agentOptions = value.agentOptions === undefined ? undefined : decodeJsonObject(value.agentOptions);
|
|
220
221
|
const agentIdentity = value.agentIdentity === undefined ? undefined : decodeIdentity(value.agentIdentity);
|
|
221
222
|
const sessionPath = optionalString(value.sessionPath);
|
|
222
223
|
const continuity = value.continuity === undefined ? undefined : isAgentContinuity(value.continuity) ? value.continuity : INVALID_PERSISTED_VALUE;
|
|
223
224
|
const extensionSettings = value.extensionSettings === undefined ? undefined : decodeWorkflowExtensions(value.extensionSettings);
|
|
224
|
-
if (sessionPath === INVALID_PERSISTED_VALUE || continuity === INVALID_PERSISTED_VALUE || requestedLabel === INVALID_PERSISTED_VALUE || parentBreadcrumb === INVALID_PERSISTED_VALUE || worktreeOwner === INVALID_PERSISTED_VALUE || model === INVALID_PERSISTED_VALUE || value.skills !== undefined && !skills || value.extensions !== undefined && !extensions || (value.
|
|
225
|
+
if (sessionPath === INVALID_PERSISTED_VALUE || continuity === INVALID_PERSISTED_VALUE || requestedLabel === INVALID_PERSISTED_VALUE || parentBreadcrumb === INVALID_PERSISTED_VALUE || worktreeOwner === INVALID_PERSISTED_VALUE || model === INVALID_PERSISTED_VALUE || value.skills !== undefined && !skills || value.extensions !== undefined && !extensions || (value.contextFiles !== undefined && !contextFiles) || (value.schema !== undefined && !schema) || retries === INVALID_PERSISTED_VALUE || (retries !== undefined && !integer(retries)) || (timeoutMs !== undefined && timeoutMs !== null && !finiteNumber(timeoutMs)) || (value.agentOptions !== undefined && !agentOptions) || (value.agentIdentity !== undefined && !agentIdentity) || (value.extensionSettings !== undefined && !extensionSettings))
|
|
225
226
|
return undefined;
|
|
226
227
|
return {
|
|
227
228
|
label: value.label,
|
|
@@ -232,7 +233,6 @@ function decodeScheduledAgentOptions(value) {
|
|
|
232
233
|
...(skills === undefined ? {} : { skills }), ...(extensions === undefined ? {} : { extensions }),
|
|
233
234
|
...(worktreeOwner === undefined ? {} : { worktreeOwner }),
|
|
234
235
|
...(model === undefined ? {} : { model }),
|
|
235
|
-
...(role === undefined ? {} : { role }),
|
|
236
236
|
...(contextFiles === undefined ? {} : { contextFiles }),
|
|
237
237
|
...(schema === undefined ? {} : { schema }),
|
|
238
238
|
...(retries === undefined ? {} : { retries }),
|
|
@@ -240,6 +240,9 @@ function decodeScheduledAgentOptions(value) {
|
|
|
240
240
|
...(sessionPath === undefined ? {} : { sessionPath }),
|
|
241
241
|
...(continuity === undefined ? {} : { continuity }),
|
|
242
242
|
...(agentOptions === undefined ? {} : { agentOptions }),
|
|
243
|
+
...(configuration === undefined ? {} : { configuration }),
|
|
244
|
+
...(value.projectTrusted === undefined ? {} : { projectTrusted: value.projectTrusted }),
|
|
245
|
+
...(capabilities?.tools && capabilities.skills && capabilities.extensions ? { capabilities: { tools: capabilities.tools, skills: capabilities.skills, extensions: capabilities.extensions } } : {}),
|
|
243
246
|
...(agentIdentity === undefined ? {} : { agentIdentity }),
|
|
244
247
|
...(extensionSettings === undefined ? {} : { extensionSettings }),
|
|
245
248
|
};
|
|
@@ -288,12 +291,11 @@ function decodeAgentResourceInspection(value) {
|
|
|
288
291
|
const sources = sourceRecord === undefined ? undefined : {
|
|
289
292
|
global: decodeAgentResourceSelectors(sourceRecord.global),
|
|
290
293
|
project: decodeAgentResourceSelectors(sourceRecord.project),
|
|
291
|
-
...(sourceRecord.role === undefined ? {} : { role: decodeAgentResourceSelectors(sourceRecord.role) }),
|
|
292
294
|
...(sourceRecord.call === undefined ? {} : { call: decodeAgentResourceSelectors(sourceRecord.call) }),
|
|
293
295
|
};
|
|
294
|
-
if (!selectors || !skills || !extensions || !tools || !unmatchedSkills || !unmatchedExtensions || !unmatchedTools || rawSources !== undefined && (!sources || !sources.global || !sources.project || sources.
|
|
296
|
+
if (!selectors || !skills || !extensions || !tools || !unmatchedSkills || !unmatchedExtensions || !unmatchedTools || rawSources !== undefined && (!sources || !sources.global || !sources.project || sources.call === undefined && sourceRecord?.call !== undefined))
|
|
295
297
|
return undefined;
|
|
296
|
-
return { selectors: { skills: [...(selectors.skills ?? [])], extensions: [...(selectors.extensions ?? [])], tools: [...(selectors.tools ?? [])] }, skills, extensions, tools, unmatchedSkills, unmatchedExtensions, unmatchedTools, ...(sources === undefined ? {} : { selectorSources: { global: sources.global ?? {}, project: sources.project ?? {}, ...(sources.
|
|
298
|
+
return { selectors: { skills: [...(selectors.skills ?? [])], extensions: [...(selectors.extensions ?? [])], tools: [...(selectors.tools ?? [])] }, skills, extensions, tools, unmatchedSkills, unmatchedExtensions, unmatchedTools, ...(sources === undefined ? {} : { selectorSources: { global: sources.global ?? {}, project: sources.project ?? {}, ...(sources.call === undefined ? {} : { call: sources.call }) } }) };
|
|
297
299
|
}
|
|
298
300
|
function decodeAgentSetupSummary(value) {
|
|
299
301
|
if (!object(value) || typeof value.cwd !== "string")
|
|
@@ -337,7 +339,6 @@ function decodeAgent(value) {
|
|
|
337
339
|
const worktreeOwner = optionalString(value.worktreeOwner);
|
|
338
340
|
const handle = optionalString(value.handle);
|
|
339
341
|
const continuity = value.continuity === undefined ? undefined : isAgentContinuity(value.continuity) ? value.continuity : INVALID_PERSISTED_VALUE;
|
|
340
|
-
const role = optionalString(value.role);
|
|
341
342
|
const requestedModel = optionalString(value.requestedModel);
|
|
342
343
|
const startedAt = optionalNumber(value.startedAt);
|
|
343
344
|
const durationMs = optionalNumber(value.durationMs);
|
|
@@ -346,7 +347,7 @@ function decodeAgent(value) {
|
|
|
346
347
|
const toolCalls = value.toolCalls === undefined ? undefined : decodeArray(value.toolCalls, decodeAgentToolCall);
|
|
347
348
|
const activity = value.activity === undefined ? undefined : decodeAgentActivity(value.activity);
|
|
348
349
|
const lastEventAt = optionalNumber(value.lastEventAt);
|
|
349
|
-
if (systemPrompt === INVALID_PERSISTED_VALUE || prompt === INVALID_PERSISTED_VALUE || label === INVALID_PERSISTED_VALUE || parentId === INVALID_PERSISTED_VALUE || resultPath === INVALID_PERSISTED_VALUE || parentBreadcrumb === INVALID_PERSISTED_VALUE || worktreeOwner === INVALID_PERSISTED_VALUE ||
|
|
350
|
+
if (systemPrompt === INVALID_PERSISTED_VALUE || prompt === INVALID_PERSISTED_VALUE || label === INVALID_PERSISTED_VALUE || parentId === INVALID_PERSISTED_VALUE || resultPath === INVALID_PERSISTED_VALUE || parentBreadcrumb === INVALID_PERSISTED_VALUE || worktreeOwner === INVALID_PERSISTED_VALUE || requestedModel === INVALID_PERSISTED_VALUE || startedAt === INVALID_PERSISTED_VALUE || durationMs === INVALID_PERSISTED_VALUE || lastEventAt === INVALID_PERSISTED_VALUE || handle === INVALID_PERSISTED_VALUE || continuity === INVALID_PERSISTED_VALUE || handle !== undefined && !positiveInteger(value.turn))
|
|
350
351
|
return undefined;
|
|
351
352
|
if (!model || !tools || value.structuralPath !== undefined && !structuralPath || value.attemptDetails !== undefined && !attemptDetails || value.accounting !== undefined && !accounting || value.toolCalls !== undefined && !toolCalls || value.activity !== undefined && !activity)
|
|
352
353
|
return undefined;
|
|
@@ -365,7 +366,6 @@ function decodeAgent(value) {
|
|
|
365
366
|
...(worktreeOwner === undefined ? {} : { worktreeOwner }),
|
|
366
367
|
...(handle === undefined ? {} : { handle, turn: value.turn }),
|
|
367
368
|
...(continuity === undefined ? {} : { continuity }),
|
|
368
|
-
...(role === undefined ? {} : { role }),
|
|
369
369
|
...(requestedModel === undefined ? {} : { requestedModel }),
|
|
370
370
|
model,
|
|
371
371
|
tools,
|
|
@@ -539,14 +539,12 @@ export function decodeLaunchSnapshot(value) {
|
|
|
539
539
|
const phases = value.phases === undefined ? undefined : decodeStringArray(value.phases);
|
|
540
540
|
const models = decodeStringArray(value.models);
|
|
541
541
|
const tools = decodeStringArray(value.tools);
|
|
542
|
-
const
|
|
543
|
-
const roles = value.roles === undefined ? undefined : decodeRecord(value.roles, decodeAgentDefinition);
|
|
544
|
-
const projectRoles = value.projectRoles === undefined ? undefined : decodeStringArray(value.projectRoles);
|
|
542
|
+
const agentConfigurations = decodeRecord(value.agentConfigurations, decodePreparedAgentConfiguration);
|
|
545
543
|
const schemas = decodeArray(value.schemas, decodeJsonObject);
|
|
546
|
-
if (identityVersion
|
|
544
|
+
if (identityVersion !== LAUNCH_SNAPSHOT_IDENTITY_VERSION || launchMode !== undefined && !isLaunchMode(launchMode) || args === undefined || !metadata || !settings || value.settingsSources !== undefined && !settingsSources || value.budget !== undefined && !budget || settingsPath === INVALID_PERSISTED_VALUE || value.modelAliases !== undefined && !modelAliases || value.phases !== undefined && !phases || !models || !tools || !agentConfigurations || !schemas)
|
|
547
545
|
return undefined;
|
|
548
546
|
return {
|
|
549
|
-
|
|
547
|
+
identityVersion: LAUNCH_SNAPSHOT_IDENTITY_VERSION,
|
|
550
548
|
...(launchMode === undefined ? {} : { launchMode }),
|
|
551
549
|
script: value.script,
|
|
552
550
|
args,
|
|
@@ -559,9 +557,7 @@ export function decodeLaunchSnapshot(value) {
|
|
|
559
557
|
...(phases === undefined ? {} : { phases }),
|
|
560
558
|
models,
|
|
561
559
|
tools,
|
|
562
|
-
|
|
563
|
-
...(roles === undefined ? {} : { roles }),
|
|
564
|
-
...(projectRoles === undefined ? {} : { projectRoles }),
|
|
560
|
+
agentConfigurations,
|
|
565
561
|
schemas,
|
|
566
562
|
};
|
|
567
563
|
}
|
package/dist/src/execution.d.ts
CHANGED
|
@@ -3,6 +3,8 @@ import type { AgentAttempt } from "./agent-execution.js";
|
|
|
3
3
|
import type { AgentIdentity, JsonValue, ShellIdentity, ShellOptions, ShellResult, ToolIdentity, WorkflowBridge, WorkflowExecution } from "./types.js";
|
|
4
4
|
export declare const RPC_LIMIT_BYTES: number;
|
|
5
5
|
export declare const HEARTBEAT_TIMEOUT_MS = 5000;
|
|
6
|
+
/** Script handle names may not start with this: registered-function handles are journaled under it. */
|
|
7
|
+
export declare const FUNCTION_HANDLE_PREFIX = "function-handle";
|
|
6
8
|
export declare function encoded(value: JsonValue): string;
|
|
7
9
|
export declare function encoded(value: unknown): asserts value is JsonValue;
|
|
8
10
|
/** The journal path of a script tool call; the arguments' digest keeps calls with different arguments apart whatever order they start in. */
|
package/dist/src/execution.js
CHANGED
|
@@ -4,7 +4,7 @@ import { tmpdir } from "node:os";
|
|
|
4
4
|
import { join } from "node:path";
|
|
5
5
|
import { StringDecoder } from "node:string_decoder";
|
|
6
6
|
import { structuralPath as operationPath } from "./persistence.js";
|
|
7
|
-
import { WorkflowError,
|
|
7
|
+
import { WorkflowError, sumAccounting, zeroAccounting } from "./types.js";
|
|
8
8
|
import { asWorkflowError, errorText, fail, isWorkflowAuthored, isWorkflowErrorCode, jsonDigest, jsonValue, markWorkflowAuthored, object, positiveInteger } from "./utils.js";
|
|
9
9
|
import { instrumentWorkflow, validateAgentOptions, validateShellCommand, validateShellOptions } from "./validation.js";
|
|
10
10
|
export const RPC_LIMIT_BYTES = 10 * 1024 * 1024;
|
|
@@ -35,6 +35,8 @@ function isRpcMessage(value) {
|
|
|
35
35
|
}
|
|
36
36
|
const HEARTBEAT_INTERVAL_MS = 1000;
|
|
37
37
|
const OUTCOME_ERRORS = new Set(["AGENT_TIMEOUT", "AGENT_FAILED", "RESULT_INVALID"]);
|
|
38
|
+
/** Script handle names may not start with this: registered-function handles are journaled under it. */
|
|
39
|
+
export const FUNCTION_HANDLE_PREFIX = "function-handle";
|
|
38
40
|
const WORK_RESULT_BRAND = "__workResult";
|
|
39
41
|
const childSource = String.raw `
|
|
40
42
|
"use strict";
|
|
@@ -145,15 +147,13 @@ const internalAgent = (...values) => {
|
|
|
145
147
|
void result.then(() => agentInflight.delete(occurrenceKey), () => agentInflight.delete(occurrenceKey));
|
|
146
148
|
return guardedAgentResult(result);
|
|
147
149
|
};
|
|
148
|
-
const AGENT_HANDLE_KEYS = ["name", "role", "model", "tools", "skills", "extensions", "contextFiles", "label"];
|
|
149
150
|
const agentHandleNames = new Set();
|
|
150
151
|
const agentHandleInflight = new Set();
|
|
151
152
|
const internalAgentCreate = (...values) => {
|
|
152
153
|
if (values.length !== 1 || !values[0] || typeof values[0] !== "object" || Array.isArray(values[0])) throw workError("INVALID_METADATA", "agent.create requires one options object");
|
|
153
154
|
const options = values[0];
|
|
154
155
|
const name = named(options.name, "agent.create");
|
|
155
|
-
|
|
156
|
-
if (unsupported) throw workError("INVALID_METADATA", "agent.create option is unsupported: " + unsupported);
|
|
156
|
+
if (name.startsWith("${FUNCTION_HANDLE_PREFIX}/")) throw workError("INVALID_METADATA", "Agent handle names may not start with ${FUNCTION_HANDLE_PREFIX}/");
|
|
157
157
|
if (agentHandleNames.has(name)) throw workError("INVALID_METADATA", "Duplicate agent handle name: " + name);
|
|
158
158
|
agentHandleNames.add(name);
|
|
159
159
|
const config = Object.freeze(Object.fromEntries(Object.entries(options).filter(([key]) => key !== "name")));
|
|
@@ -655,7 +655,7 @@ export function runWorkflow(script, args = null, bridge = {}, signal) {
|
|
|
655
655
|
const opts = validateAgentOptions(values[1]);
|
|
656
656
|
const identity = readAgentIdentity(values[2]);
|
|
657
657
|
const path = agentIdentityPath(identity);
|
|
658
|
-
const label = typeof opts.label === "string" ? opts.label :
|
|
658
|
+
const label = typeof opts.label === "string" ? opts.label : "agent";
|
|
659
659
|
try {
|
|
660
660
|
const result = await bridge.agent(values[0], opts, controller.signal, identity);
|
|
661
661
|
value = branded({ name: label, ok: true, value: result ?? null });
|
|
@@ -101,9 +101,9 @@ const WORKFLOW_ERROR_PROSE = {
|
|
|
101
101
|
REGISTRY_FROZEN: (detail) => `Workflow extension registration is closed: ${detail}.`,
|
|
102
102
|
GLOBAL_COLLISION: (detail) => `The workflow global name is already in use: ${detail}.`,
|
|
103
103
|
MISSING_WORKFLOW: (detail) => `The registered workflow function is unavailable: ${detail}.`,
|
|
104
|
-
UNKNOWN_MODEL: (detail) => `The workflow requested the unavailable model ${detail.replace(/^(?:Unknown model
|
|
104
|
+
UNKNOWN_MODEL: (detail) => `The workflow requested the unavailable model ${detail.replace(/^(?:Unknown model|Invalid model spec|Required model is unavailable):\s*/, "")}.`,
|
|
105
105
|
UNKNOWN_TOOL: (detail) => `The workflow requested the unavailable tool ${detail.replace(/^Unknown tool:\s*/, "")}.`,
|
|
106
|
-
|
|
106
|
+
UNKNOWN_AGENT: (detail) => `The workflow requested an unavailable owned agent: ${detail}.`,
|
|
107
107
|
RUN_OWNED: (detail) => /already owned|active ownership/.test(detail) ? "The workflow session is already in use." : `The workflow session is already in use: ${detail}.`,
|
|
108
108
|
RUN_NOT_FOUND: (detail) => /^Unknown workflow run\b/.test(detail) ? "The workflow run was not found." : `The workflow run was not found: ${detail}.`,
|
|
109
109
|
RPC_LIMIT_EXCEEDED: (detail) => `The workflow communication data exceeded its size limit: ${detail}.`,
|
|
@@ -205,7 +205,6 @@ function boundedWorkflowFailureDiagnostics(value) {
|
|
|
205
205
|
...(value.failedAgent ? { failedAgent: {
|
|
206
206
|
id: utf8Prefix(value.failedAgent.id, 128),
|
|
207
207
|
...(value.failedAgent.label ? { label: utf8Prefix(value.failedAgent.label, 128) } : {}),
|
|
208
|
-
...(value.failedAgent.role ? { role: utf8Prefix(value.failedAgent.role, 128) } : {}),
|
|
209
208
|
structuralPath: value.failedAgent.structuralPath.slice(0, 8).map((part) => utf8Prefix(part, 128)),
|
|
210
209
|
attempt: value.failedAgent.attempt,
|
|
211
210
|
...(value.failedAgent.transport ? { transport: utf8Prefix(value.failedAgent.transport, 128) } : {}),
|
|
@@ -214,7 +213,6 @@ function boundedWorkflowFailureDiagnostics(value) {
|
|
|
214
213
|
completedSiblingAgents: (value.completedSiblingAgents ?? []).slice(0, 16).map((agent) => ({
|
|
215
214
|
id: utf8Prefix(agent.id, 128),
|
|
216
215
|
...(agent.label ? { label: utf8Prefix(agent.label, 128) } : {}),
|
|
217
|
-
...(agent.role ? { role: utf8Prefix(agent.role, 128) } : {}),
|
|
218
216
|
structuralPath: agent.structuralPath.slice(0, 8).map((part) => utf8Prefix(part, 128)),
|
|
219
217
|
})),
|
|
220
218
|
completedSiblingPaths: value.completedSiblingPaths.slice(0, 16).map((path) => path.slice(0, 8).map((part) => utf8Prefix(part, 128))),
|
|
@@ -306,7 +304,6 @@ export async function createWorkflowFailureDiagnostics(store, metadata, error, r
|
|
|
306
304
|
const failedAgent = failedAgentRecord ? {
|
|
307
305
|
id: failedAgentRecord.id,
|
|
308
306
|
...(failedAgentRecord.label ?? failedAgentRecord.name ? { label: failedAgentRecord.label ?? failedAgentRecord.name } : {}),
|
|
309
|
-
...(failedAgentRecord.role ? { role: failedAgentRecord.role } : {}),
|
|
310
307
|
structuralPath: [...(failedAgentRecord.structuralPath ?? [])],
|
|
311
308
|
attempt: Math.max(1, failedAttempt?.attempt ?? failedAgentRecord.attempts),
|
|
312
309
|
...(failedAttempt?.transport ? { transport: failedAttempt.transport } : {}),
|
|
@@ -319,7 +316,6 @@ export async function createWorkflowFailureDiagnostics(store, metadata, error, r
|
|
|
319
316
|
}).map((agent) => ({
|
|
320
317
|
id: agent.id,
|
|
321
318
|
...(agent.label ?? agent.name ? { label: agent.label ?? agent.name } : {}),
|
|
322
|
-
...(agent.role ? { role: agent.role } : {}),
|
|
323
319
|
structuralPath: [...(agent.structuralPath ?? [])],
|
|
324
320
|
}));
|
|
325
321
|
const completedSiblingPaths = completedSiblingAgents.map((agent) => [...agent.structuralPath]);
|
|
@@ -347,9 +343,9 @@ export async function createWorkflowFailureDiagnostics(store, metadata, error, r
|
|
|
347
343
|
});
|
|
348
344
|
}
|
|
349
345
|
export function formatWorkflowFailureDiagnostics(diagnostic) {
|
|
350
|
-
const failedAgent = diagnostic.failedAgent ? `${diagnostic.failedAgent.label ?? diagnostic.failedAgent.id}
|
|
346
|
+
const failedAgent = diagnostic.failedAgent ? `${diagnostic.failedAgent.label ?? diagnostic.failedAgent.id} attempt=${String(diagnostic.failedAgent.attempt)} path=${diagnostic.failedAgent.structuralPath.join(" > ") || "(root)"}${diagnostic.failedAgent.session ? ` session=${diagnostic.failedAgent.session.transport}/${diagnostic.failedAgent.session.sessionId}` : ""}` : "(not persisted)";
|
|
351
347
|
const siblingAgents = diagnostic.completedSiblingAgents;
|
|
352
|
-
const siblings = siblingAgents ? siblingAgents.map((agent) => `${agent.label ?? agent.id}
|
|
348
|
+
const siblings = siblingAgents ? siblingAgents.map((agent) => `${agent.label ?? agent.id} path=${agent.structuralPath.join(" > ") || "(root)"}`).join(", ") || "(none)" : diagnostic.completedSiblingPaths.map((path) => path.join(" > ") || "(root)").join(", ") || "(none)";
|
|
353
349
|
const retry = diagnostic.retry ? [` Retry: ${diagnostic.retry.action}`, ` Replayable completed paths: ${diagnostic.retry.completedPaths.join(", ") || "(none)"}`, ` Incomplete paths: ${diagnostic.retry.incompletePaths.join(", ") || "(unknown)"}`, ` Named worktrees: ${diagnostic.retry.namedWorktrees.join(", ") || "(none)"}`, ` Warning: ${diagnostic.retry.warning}`] : [];
|
|
354
350
|
return [`✗ Workflow: ${diagnostic.workflowName}`, ` Run: ${diagnostic.runId}`, ` State: ${diagnostic.state}`, ` Error: ${diagnostic.error.code}: ${diagnostic.error.message}`, ` Failed at: ${diagnostic.failedAt ?? "(unknown)"}`, ` Failed agent: ${failedAgent}`, ` Completed sibling ${siblingAgents ? "agents" : "paths"}: ${siblings}`, ...retry, ` Artifacts: state=${diagnostic.artifacts.statePath} journal=${diagnostic.artifacts.journalPath}`].join("\n");
|
|
355
351
|
}
|
|
@@ -827,9 +827,10 @@ export function registerWorkflowNavigator(deps) {
|
|
|
827
827
|
const styles = themeWorkflowProgressStyles(theme);
|
|
828
828
|
const agent = selectedAgentRecord();
|
|
829
829
|
const actions = actionMode ? { title: agent ? "Agent actions" : "Run actions", options: actionOptions(), index: actionIndex } : undefined;
|
|
830
|
-
const
|
|
830
|
+
const formatPhaseLines = (scrollTop) => formatWorkflowPhaseDashboard(view.run, view.snapshot, width, { scrollTop, nodeId: selectedNodeId, expandedNodeIds: [...expandedNodeIds], ...(narrow && !detailsMode && !actionMode ? { treeOnly: true } : {}), ...(narrow && (detailsMode || actionMode) ? { detailsOnly: true } : {}), ...(actions ? { actions } : {}) }, styles, progressNow());
|
|
831
|
+
const phaseLines = formatPhaseLines(0);
|
|
831
832
|
const statusLines = stopStatus ? truncateToVisualLines(styles.error(stopStatus), Number.MAX_SAFE_INTEGER, width, 0).visualLines.map((line) => line.trimEnd()) : [];
|
|
832
|
-
|
|
833
|
+
let content = [...statusLines, ...phaseLines];
|
|
833
834
|
const rows = terminalRows();
|
|
834
835
|
const hintRows = rows >= 3 ? 1 : 0;
|
|
835
836
|
const confirmRows = inlineConfirm.rows(width, styles);
|
|
@@ -857,6 +858,8 @@ export function registerWorkflowNavigator(deps) {
|
|
|
857
858
|
selectionNeedsScroll = false;
|
|
858
859
|
}
|
|
859
860
|
dashboardOffset = Math.max(0, Math.min(maxOffset, dashboardOffset));
|
|
861
|
+
if (!narrow && !actionMode)
|
|
862
|
+
content = [...statusLines, ...formatPhaseLines(Math.max(0, dashboardOffset - statusLines.length))];
|
|
860
863
|
const selectedNode = selectedNodeId ? tree.byId.get(selectedNodeId) : undefined;
|
|
861
864
|
const enterAction = selectedNode?.kind === "workflow" ? "run actions" : selectedNode?.kind === "agent" ? "agent actions" : selectedNode?.children.length ? "expand/collapse" : narrow ? "inspect" : "focus details";
|
|
862
865
|
const hint = truncateToVisualLines(theme.fg("dim", inlineConfirm.active() ? inlineConfirm.hint(keybindings) : actionMode ? `${keyLabel("tui.select.up", "↑")}/${keyLabel("tui.select.down", "↓")} actions · ${keyLabel("tui.select.confirm", "enter")} run · ${keyLabel("tui.editor.cursorLeft", "←")} tree · ${keyLabel("tui.select.cancel", "esc")} tree` : `${keyLabel("tui.select.up", "↑")}/${keyLabel("tui.select.down", "↓")} tree · ${keyLabel("tui.editor.cursorLeft", "←")}/${keyLabel("tui.editor.cursorRight", "→")} collapse/expand · ${keyLabel("tui.select.confirm", "enter")} ${enterAction} · a actions · ${keyLabel("tui.select.cancel", "esc")} ${narrow && detailsMode ? "tree" : "back"}${content.length > viewport ? ` · ${keyLabel("tui.select.pageUp", "pgup")}/${keyLabel("tui.select.pageDown", "pgdn")} scroll` : ""} · auto-refresh 1s`), Number.MAX_SAFE_INTEGER, width, 1).visualLines[0] ?? "";
|
|
@@ -45,6 +45,7 @@ export interface WorkflowPhaseSelection {
|
|
|
45
45
|
expandedNodeIds?: readonly string[] | undefined;
|
|
46
46
|
treeOnly?: boolean | undefined;
|
|
47
47
|
detailsOnly?: boolean | undefined;
|
|
48
|
+
scrollTop?: number | undefined;
|
|
48
49
|
actions?: {
|
|
49
50
|
title: string;
|
|
50
51
|
options: readonly string[];
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { Api, Model } from "@earendil-works/pi-ai";
|
|
2
2
|
import { type ExtensionAPI, type ExtensionToolContext } from "@earendil-works/pi-coding-agent";
|
|
3
|
-
import { FairAgentScheduler, WorkflowAgentExecutor, type
|
|
3
|
+
import { FairAgentScheduler, WorkflowAgentExecutor, type AgentExecutionRoot, type AgentProviderFailure, type AgentProviderRecovery } from "./agent-execution.js";
|
|
4
4
|
import { RunStore, type PersistedRun } from "./persistence.js";
|
|
5
5
|
import { WorkflowError, type AgentIdentity, type AgentResourcePolicy, type BudgetApprovalRequest, type JsonValue, type LaunchSnapshot, type ModelSpec, type RunState, type ShellIdentity, type ShellOptions, type ShellResult, type ToolIdentity, type WorkflowMetadata, type WorkflowWorktreeReference } from "./types.js";
|
|
6
6
|
import { type WorkflowRegistryApi } from "./registry.js";
|
|
@@ -24,6 +24,7 @@ export type WorkflowRecoveryContext = {
|
|
|
24
24
|
toolContext?: ExtensionToolContext | undefined;
|
|
25
25
|
signal?: AbortSignal;
|
|
26
26
|
resolvedAliases?: Readonly<Record<string, string>>;
|
|
27
|
+
resolvedDynamicAliasNames?: readonly string[];
|
|
27
28
|
blockedAliases?: ReadonlySet<string>;
|
|
28
29
|
blockedAliasTargets?: Readonly<Record<string, string>>;
|
|
29
30
|
};
|
|
@@ -49,6 +50,7 @@ export type WorkflowRecoveryDependencies = {
|
|
|
49
50
|
modelRegistry?: WorkflowRecoveryContext["modelRegistry"];
|
|
50
51
|
signal: AbortSignal;
|
|
51
52
|
resolvedAliases?: Readonly<Record<string, string>>;
|
|
53
|
+
resolvedDynamicAliasNames?: readonly string[];
|
|
52
54
|
blockedAliases?: ReadonlySet<string>;
|
|
53
55
|
blockedAliasTargets?: Readonly<Record<string, string>>;
|
|
54
56
|
withPreflight: boolean;
|
|
@@ -60,16 +62,13 @@ export type WorkflowRecoveryDependencies = {
|
|
|
60
62
|
knownModels: ReadonlySet<string>;
|
|
61
63
|
availableModels: ReadonlySet<string>;
|
|
62
64
|
currentAliases: Readonly<Record<string, string>>;
|
|
65
|
+
dynamicAliasNames: readonly string[];
|
|
63
66
|
blockedAliases: ReadonlySet<string>;
|
|
64
67
|
blockedAliasTargets: Readonly<Record<string, string>>;
|
|
65
68
|
snapshot: Readonly<LaunchSnapshot>;
|
|
66
69
|
script: string | undefined;
|
|
67
70
|
}>;
|
|
68
|
-
|
|
69
|
-
definitions: Readonly<Record<string, AgentDefinition>>;
|
|
70
|
-
capture: (role: string, model: ModelSpec) => Promise<void>;
|
|
71
|
-
};
|
|
72
|
-
workflowAgentHandler: (store: RunStore, metadata: WorkflowMetadata, lifecycle: RunLifecycle, executor: WorkflowAgentExecutor, cwd: string, runId: string, captureRole?: (role: string, model: ModelSpec) => Promise<void>) => (prompt: string, options: Readonly<Record<string, JsonValue>>, signal: AbortSignal, identity: AgentIdentity) => Promise<JsonValue>;
|
|
71
|
+
workflowAgentHandler: (store: RunStore, metadata: WorkflowMetadata, lifecycle: RunLifecycle, executor: WorkflowAgentExecutor, cwd: string, runId: string) => (prompt: string, options: Readonly<Record<string, JsonValue>>, signal: AbortSignal, identity: AgentIdentity) => Promise<JsonValue>;
|
|
73
72
|
shellForRun: (store: RunStore, metadata: WorkflowMetadata, lifecycle: RunLifecycle, command: string, options: ShellOptions, signal: AbortSignal, identity: ShellIdentity) => Promise<ShellResult>;
|
|
74
73
|
toolForRun: (store: RunStore, lifecycle: RunLifecycle, context: ExtensionToolContext | undefined, identifier: string, args: Readonly<Record<string, JsonValue>>, signal: AbortSignal, identity: ToolIdentity) => Promise<JsonValue>;
|
|
75
74
|
resolveWorktree: (store: RunStore, metadata: WorkflowMetadata, owner: string) => Promise<Readonly<WorkflowWorktreeReference>>;
|
|
@@ -109,6 +108,7 @@ export declare function createWorkflowRecovery(deps: WorkflowRecoveryDependencie
|
|
|
109
108
|
toolContext?: ExtensionToolContext | undefined;
|
|
110
109
|
signal?: AbortSignal | undefined;
|
|
111
110
|
resolvedAliases?: Readonly<Record<string, string>>;
|
|
111
|
+
resolvedDynamicAliasNames?: readonly string[];
|
|
112
112
|
blockedAliases?: ReadonlySet<string>;
|
|
113
113
|
blockedAliasTargets?: Readonly<Record<string, string>>;
|
|
114
114
|
}, modeOverride?: boolean, waitForCompletion?: boolean) => Promise<{
|
|
@@ -28,17 +28,10 @@ function workflowRecoveryGuidance(action, state) {
|
|
|
28
28
|
return "Interrupted workflow runs must be resumed from the interactive /workflow picker";
|
|
29
29
|
return `Only failed workflow runs can be retried; source is ${state}`;
|
|
30
30
|
}
|
|
31
|
-
/** A snapshot can only be re-executed when it carries the current identity scheme and
|
|
32
|
-
function assertResumableSnapshot(snapshot
|
|
31
|
+
/** A snapshot can only be re-executed when it carries the current identity scheme and validated concrete agent configurations. */
|
|
32
|
+
function assertResumableSnapshot(snapshot) {
|
|
33
33
|
if (snapshot.identityVersion !== LAUNCH_SNAPSHOT_IDENTITY_VERSION)
|
|
34
34
|
throw new WorkflowError("RESUME_INCOMPATIBLE", "Workflow launch snapshot identity version is incompatible");
|
|
35
|
-
if (snapshot.roles === undefined)
|
|
36
|
-
throw new WorkflowError("RESUME_INCOMPATIBLE", "Workflow role definitions are missing from the launch snapshot");
|
|
37
|
-
if ((snapshot.projectRoles?.length ?? 0) > 0 && !trustedProject)
|
|
38
|
-
throw new WorkflowError("RESUME_INCOMPATIBLE", "Cannot restore project roles in an untrusted project");
|
|
39
|
-
const missingRole = snapshot.agentTypes.find((role) => !snapshot.roles?.[role]);
|
|
40
|
-
if (missingRole)
|
|
41
|
-
throw new WorkflowError("RESUME_INCOMPATIBLE", `Role definition is missing from the launch snapshot: ${missingRole}`);
|
|
42
35
|
}
|
|
43
36
|
function assertExpectedWorkflowState(expectedState, actualState) {
|
|
44
37
|
if (expectedState !== undefined && expectedState !== actualState)
|
|
@@ -46,7 +39,7 @@ function assertExpectedWorkflowState(expectedState, actualState) {
|
|
|
46
39
|
}
|
|
47
40
|
export function persistedFailure(run, error) { const failedAt = workflowFailedAt(error); return { ...run, error: { code: error.code, message: error.message, ...(failedAt ? { failedAt } : {}) }, ...(failedAt ? { failedAt } : {}) }; }
|
|
48
41
|
export function createWorkflowRecovery(deps) {
|
|
49
|
-
const { pi, home, runs, scheduler, eventPublisher, persistRunState, projectTrusted, resumeHostContext, ensureSessionLease, createAgentExecutor, activeSnapshotTools, frozenResourcePolicy, resolveLaunchPrologue,
|
|
42
|
+
const { pi, home, runs, scheduler, eventPublisher, persistRunState, projectTrusted, resumeHostContext, ensureSessionLease, createAgentExecutor, activeSnapshotTools, frozenResourcePolicy, resolveLaunchPrologue, workflowAgentHandler, shellForRun, toolForRun, resolveWorktree, checkpointBridge, phaseBridge, logBridge, lifecycleFor, createProviderErrorRecovery, cleanupTerminalRun, deliver, deliverTerminal, workflowToolUpdate, registry, modelSpec } = deps;
|
|
50
43
|
const coordinateRunMutation = deps.coordinateRunMutation ?? ((task) => task());
|
|
51
44
|
const budgetDecisionDelivery = (metadata, request) => `Workflow ${metadata.name} budget adjustment ${request.proposalId} for run ${request.runId} requires approval. Consumed usage: ${JSON.stringify(request.consumed)}. Previous limits: ${JSON.stringify(request.previous)}. Proposed limits: ${JSON.stringify(request.proposed)}. Respond with workflow_respond using proposalId ${request.proposalId}.`;
|
|
52
45
|
const appendBudgetDecisionEvent = async (run, request, type) => {
|
|
@@ -70,11 +63,11 @@ export function createWorkflowRecovery(deps) {
|
|
|
70
63
|
const loaded = await run.store.load();
|
|
71
64
|
const trustedProject = context?.projectTrusted ?? run.projectTrusted();
|
|
72
65
|
const rootModel = context?.model ? { ...run.model, provider: context.model.provider, model: context.model.id } : run.model;
|
|
73
|
-
const { settingsPath, currentPolicy, previousAliases, knownModels, availableModels, currentAliases, blockedAliases, blockedAliasTargets, snapshot } = await resolveLaunchPrologue({ snapshot: loaded.snapshot, cwd: run.store.cwd, trustedProject, rootModel, ...(context?.modelRegistry ? { modelRegistry: context.modelRegistry } : {}), signal: run.abortController.signal, withPreflight: false });
|
|
66
|
+
const { settingsPath, currentPolicy, previousAliases, knownModels, availableModels, currentAliases, dynamicAliasNames, blockedAliases, blockedAliasTargets, snapshot } = await resolveLaunchPrologue({ snapshot: loaded.snapshot, cwd: run.store.cwd, trustedProject, rootModel, ...(context?.modelRegistry ? { modelRegistry: context.modelRegistry } : {}), signal: run.abortController.signal, withPreflight: false });
|
|
74
67
|
await run.store.saveSnapshot(snapshot);
|
|
75
68
|
scheduler.updateRunLimit(run.store.runId, snapshot.settings.concurrency);
|
|
76
69
|
scheduler.setRunExtensionSettings(run.store.runId, snapshot.settings.extensionSettings);
|
|
77
|
-
run.executor = createAgentExecutor({ cwd: run.store.cwd, model: run.model, tools: activeSnapshotTools(snapshot.tools, "session"), resourceSelectors: currentPolicy.effective, extensionSettings: snapshot.settings.extensionSettings, availableModels, knownModels, modelAliases: currentAliases,
|
|
70
|
+
run.executor = createAgentExecutor({ cwd: run.store.cwd, projectTrusted: currentPolicy.projectTrusted, model: run.model, tools: activeSnapshotTools(snapshot.tools, "session"), resourceSelectors: currentPolicy.effective, extensionSettings: snapshot.settings.extensionSettings, availableModels, knownModels, modelAliases: currentAliases, dynamicModelAliasNames: dynamicAliasNames, blockedAliases, blockedAliasTargets, settingsPath, runStore: run.store, providerPause: async () => { deliver(`Workflow ${snapshot.metadata.name} paused: provider limit.`); await run.lifecycle.providerPause(); }, agentResourcePolicy: frozenResourcePolicy(currentPolicy) });
|
|
78
71
|
run.executor.setRunContext(workflowRunContext(run.store.cwd, run.store.sessionId, run.store.runId, loaded.snapshot.metadata, loaded.snapshot.args, run.abortController.signal));
|
|
79
72
|
const drift = aliasDrift(previousAliases, currentAliases);
|
|
80
73
|
if (drift.length)
|
|
@@ -92,7 +85,7 @@ export function createWorkflowRecovery(deps) {
|
|
|
92
85
|
await persistRunState(run.store, run.metadata, withoutActiveShells);
|
|
93
86
|
await run.store.validateRetrySource();
|
|
94
87
|
await run.store.validateBorrowedWorktrees();
|
|
95
|
-
assertResumableSnapshot(loaded.snapshot
|
|
88
|
+
assertResumableSnapshot(loaded.snapshot);
|
|
96
89
|
const rootModel = context?.model ? { ...run.model, provider: context.model.provider, model: context.model.id } : run.model;
|
|
97
90
|
const controller = new AbortController();
|
|
98
91
|
if (context?.signal?.aborted)
|
|
@@ -101,7 +94,7 @@ export function createWorkflowRecovery(deps) {
|
|
|
101
94
|
context?.signal?.addEventListener("abort", () => { controller.abort(); }, { once: true });
|
|
102
95
|
}
|
|
103
96
|
run.abortController = controller;
|
|
104
|
-
const { settingsPath, currentPolicy, previousAliases, knownModels, availableModels, currentAliases, blockedAliases, blockedAliasTargets, snapshot, script } = await resolveLaunchPrologue({ snapshot: loaded.snapshot, cwd: run.store.cwd, trustedProject, rootModel, ...(context?.modelRegistry ? { modelRegistry: context.modelRegistry } : {}), signal: controller.signal, ...(context?.resolvedAliases ? { resolvedAliases: context.resolvedAliases } : {}), ...(context?.blockedAliases ? { blockedAliases: context.blockedAliases } : {}), ...(context?.blockedAliasTargets ? { blockedAliasTargets: context.blockedAliasTargets } : {}), withPreflight: true });
|
|
97
|
+
const { settingsPath, currentPolicy, previousAliases, knownModels, availableModels, currentAliases, dynamicAliasNames, blockedAliases, blockedAliasTargets, snapshot, script } = await resolveLaunchPrologue({ snapshot: loaded.snapshot, cwd: run.store.cwd, trustedProject, rootModel, ...(context?.modelRegistry ? { modelRegistry: context.modelRegistry } : {}), signal: controller.signal, ...(context?.resolvedAliases ? { resolvedAliases: context.resolvedAliases } : {}), ...(context?.resolvedDynamicAliasNames ? { resolvedDynamicAliasNames: context.resolvedDynamicAliasNames } : {}), ...(context?.blockedAliases ? { blockedAliases: context.blockedAliases } : {}), ...(context?.blockedAliasTargets ? { blockedAliasTargets: context.blockedAliasTargets } : {}), withPreflight: true });
|
|
105
98
|
if (!script)
|
|
106
99
|
throw new WorkflowError("INTERNAL_ERROR", "Resume preflight did not produce a launch script");
|
|
107
100
|
const persistedSnapshot = modeOverride === undefined ? snapshot : createLaunchSnapshot({ ...snapshot, launchMode: foreground ? "foreground" : "background" });
|
|
@@ -110,8 +103,7 @@ export function createWorkflowRecovery(deps) {
|
|
|
110
103
|
await persistRunState(run.store, run.metadata, (current) => ({ ...current, delivery: { ...(current.delivery ?? {}), mode: foreground ? "foreground" : "background", state: foreground ? "attached" : "pending" } }));
|
|
111
104
|
scheduler.updateRunLimit(run.store.runId, snapshot.settings.concurrency);
|
|
112
105
|
scheduler.setRunExtensionSettings(run.store.runId, snapshot.settings.extensionSettings);
|
|
113
|
-
|
|
114
|
-
run.executor = createAgentExecutor({ cwd: run.store.cwd, model: rootModel, tools: activeSnapshotTools(snapshot.tools, "session"), resourceSelectors: currentPolicy.effective, extensionSettings: snapshot.settings.extensionSettings, availableModels, knownModels, modelAliases: currentAliases, blockedAliases, blockedAliasTargets, settingsPath, agentDefinitions: roles.definitions, runStore: run.store, providerPause: async () => { deliver(`Workflow ${snapshot.metadata.name} paused: provider limit.`); await run.lifecycle.providerPause(); }, agentResourcePolicy: frozenResourcePolicy(currentPolicy) });
|
|
106
|
+
run.executor = createAgentExecutor({ cwd: run.store.cwd, projectTrusted: currentPolicy.projectTrusted, model: rootModel, tools: activeSnapshotTools(snapshot.tools, "session"), resourceSelectors: currentPolicy.effective, extensionSettings: snapshot.settings.extensionSettings, availableModels, knownModels, modelAliases: currentAliases, dynamicModelAliasNames: dynamicAliasNames, blockedAliases, blockedAliasTargets, settingsPath, runStore: run.store, providerPause: async () => { deliver(`Workflow ${snapshot.metadata.name} paused: provider limit.`); await run.lifecycle.providerPause(); }, agentResourcePolicy: frozenResourcePolicy(currentPolicy) });
|
|
115
107
|
const drift = aliasDrift(previousAliases, currentAliases);
|
|
116
108
|
if (drift.length)
|
|
117
109
|
await run.store.appendEvent({ type: "warning", message: `Model alias mappings changed on resume: ${drift.join("; ")}` });
|
|
@@ -119,7 +111,7 @@ export function createWorkflowRecovery(deps) {
|
|
|
119
111
|
run.executor.setRunContext(runContext);
|
|
120
112
|
await scheduler.cancelRun(run.store.runId);
|
|
121
113
|
await run.lifecycle.resume();
|
|
122
|
-
const execution = runWorkflow(script, loaded.snapshot.args, withWorkflowFunctions({ tool: (identifier, args, signal, identity) => toolForRun(run.store, run.lifecycle, context?.toolContext, identifier, args, signal, identity), shell: (command, options, signal, identity) => shellForRun(run.store, run.metadata, run.lifecycle, command, options, signal, identity), agent: workflowAgentHandler(run.store, run.metadata, run.lifecycle, run.executor, run.store.cwd, run.store.runId
|
|
114
|
+
const execution = runWorkflow(script, loaded.snapshot.args, withWorkflowFunctions({ tool: (identifier, args, signal, identity) => toolForRun(run.store, run.lifecycle, context?.toolContext, identifier, args, signal, identity), shell: (command, options, signal, identity) => shellForRun(run.store, run.metadata, run.lifecycle, command, options, signal, identity), agent: workflowAgentHandler(run.store, run.metadata, run.lifecycle, run.executor, run.store.cwd, run.store.runId), worktree: async (owner) => resolveWorktree(run.store, run.metadata, owner), checkpoint: checkpointBridge(run.store.runId, run.store, run.metadata, foreground, hasUI ? ui : undefined), phase: phaseBridge(run.store, run.metadata, run.lifecycle), log: logBridge(run.store, run.lifecycle, run.metadata.name) }, run.store, runContext, registry, snapshot.settings.extensionSettings), controller.signal);
|
|
123
115
|
run.execution = execution;
|
|
124
116
|
const completion = execution.result.then(async (value) => {
|
|
125
117
|
await scheduler.flush(run.store.runId);
|
|
@@ -292,11 +284,11 @@ export function createWorkflowRecovery(deps) {
|
|
|
292
284
|
const trustedProject = projectTrusted(context);
|
|
293
285
|
await sourceStore.validateRetrySource();
|
|
294
286
|
await sourceStore.validateBorrowedWorktrees();
|
|
295
|
-
assertResumableSnapshot(loaded.snapshot
|
|
287
|
+
assertResumableSnapshot(loaded.snapshot);
|
|
296
288
|
const modelRegistry = resumeHostContext(context).modelRegistry;
|
|
297
289
|
const hostModel = object(host.model) && typeof host.model.provider === "string" && typeof host.model.id === "string" ? { provider: host.model.provider, id: host.model.id } : { provider: "", id: "" };
|
|
298
290
|
const rootModel = { provider: hostModel.provider, model: hostModel.id, thinking: pi.getThinkingLevel() };
|
|
299
|
-
const { active, settingsPath, currentPolicy, knownModels, availableModels, currentAliases, blockedAliases, blockedAliasTargets, snapshot: childBaseSnapshot } = await resolveLaunchPrologue({ snapshot: loaded.snapshot, cwd, trustedProject, rootModel, ...(modelRegistry ? { modelRegistry } : {}), signal: signal ?? new AbortController().signal, withPreflight: true });
|
|
291
|
+
const { active, settingsPath, currentPolicy, knownModels, availableModels, currentAliases, dynamicAliasNames, blockedAliases, blockedAliasTargets, snapshot: childBaseSnapshot } = await resolveLaunchPrologue({ snapshot: loaded.snapshot, cwd, trustedProject, rootModel, ...(modelRegistry ? { modelRegistry } : {}), signal: signal ?? new AbortController().signal, withPreflight: true });
|
|
300
292
|
await sourceStore.validateNamedWorktrees();
|
|
301
293
|
for (const name of loaded.run.retry?.namedWorktrees ?? [])
|
|
302
294
|
await sourceStore.resolveNamedWorktree(name);
|
|
@@ -315,10 +307,10 @@ export function createWorkflowRecovery(deps) {
|
|
|
315
307
|
const abortController = new AbortController();
|
|
316
308
|
const providerErrorRecovery = createProviderErrorRecovery(context, availableModels, () => { abortController.abort(); });
|
|
317
309
|
const providerPause = async () => { deliver(`Workflow ${loaded.snapshot.metadata.name} paused: provider limit.`); await lifecycle.providerPause(); };
|
|
318
|
-
const childRun = { executor: createAgentExecutor({ cwd, model, tools: activeSnapshotTools(loaded.snapshot.tools, active), resourceSelectors: currentPolicy.effective, extensionSettings: childBaseSnapshot.settings.extensionSettings, availableModels, knownModels, modelAliases: currentAliases, blockedAliases, blockedAliasTargets, settingsPath,
|
|
310
|
+
const childRun = { executor: createAgentExecutor({ cwd, projectTrusted: currentPolicy.projectTrusted, model, tools: activeSnapshotTools(loaded.snapshot.tools, active), resourceSelectors: currentPolicy.effective, extensionSettings: childBaseSnapshot.settings.extensionSettings, availableModels, knownModels, modelAliases: currentAliases, dynamicModelAliasNames: dynamicAliasNames, blockedAliases, blockedAliasTargets, settingsPath, runStore: childStore, providerPause, agentResourcePolicy: frozenResourcePolicy(currentPolicy) }), store: childStore, metadata: loaded.snapshot.metadata, model, lifecycle, budget: childBudget, abortController, projectTrusted: () => projectTrusted(context), checkpointResolvers: new Map(), ...(providerErrorRecovery ? { providerErrorRecovery } : {}) };
|
|
319
311
|
runs.set(childRunId, childRun);
|
|
320
312
|
childCreated = true;
|
|
321
|
-
return { childRun, parentRunId: loaded.run.id, lineageRootRunId, concurrency: loaded.snapshot.settings.concurrency, ...(childBaseSnapshot.settings.extensionSettings === undefined ? {} : { extensionSettings: childBaseSnapshot.settings.extensionSettings }), hostModel, modelRegistry, currentAliases, blockedAliases, blockedAliasTargets };
|
|
313
|
+
return { childRun, parentRunId: loaded.run.id, lineageRootRunId, concurrency: loaded.snapshot.settings.concurrency, ...(childBaseSnapshot.settings.extensionSettings === undefined ? {} : { extensionSettings: childBaseSnapshot.settings.extensionSettings }), hostModel, modelRegistry, currentAliases, dynamicAliasNames, blockedAliases, blockedAliasTargets };
|
|
322
314
|
}
|
|
323
315
|
finally {
|
|
324
316
|
if (!childCreated)
|
|
@@ -333,7 +325,7 @@ export function createWorkflowRecovery(deps) {
|
|
|
333
325
|
await eventPublisher.runStarted(prepared.childRun.store, prepared.childRun.metadata);
|
|
334
326
|
const { hasUI, ui } = recoveryUi(context);
|
|
335
327
|
const recoveryContext = resumeHostContext(context);
|
|
336
|
-
const completed = await coldResumeRun(prepared.childRun, hasUI, ui, projectTrusted(context), { model: prepared.hostModel, modelRegistry: prepared.modelRegistry, deliveryContext: recoveryContext.deliveryContext, toolContext: recoveryContext.toolContext, resolvedAliases: prepared.currentAliases, blockedAliases: prepared.blockedAliases, blockedAliasTargets: prepared.blockedAliasTargets, ...(signal ? { signal } : {}) }, modeOverride);
|
|
328
|
+
const completed = await coldResumeRun(prepared.childRun, hasUI, ui, projectTrusted(context), { model: prepared.hostModel, modelRegistry: prepared.modelRegistry, deliveryContext: recoveryContext.deliveryContext, toolContext: recoveryContext.toolContext, resolvedAliases: prepared.currentAliases, resolvedDynamicAliasNames: prepared.dynamicAliasNames, blockedAliases: prepared.blockedAliases, blockedAliasTargets: prepared.blockedAliasTargets, ...(signal ? { signal } : {}) }, modeOverride);
|
|
337
329
|
const completion = runs.get(prepared.childRun.store.runId)?.completion;
|
|
338
330
|
if (completion) {
|
|
339
331
|
childStarted = true;
|