@marcoscale98/pi-extensible-workflows 5.17.0-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 +145 -0
- package/README.md +2 -2
- package/dist/src/agent-execution.d.ts +43 -10
- package/dist/src/agent-execution.js +442 -162
- package/dist/src/background-widget.d.ts +5 -1
- package/dist/src/background-widget.js +5 -10
- package/dist/src/decoders.d.ts +2 -3
- package/dist/src/decoders.js +59 -37
- package/dist/src/execution.d.ts +6 -2
- package/dist/src/execution.js +48 -7
- package/dist/src/host-delivery.js +5 -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 +11 -8
- package/dist/src/host-recovery.js +14 -22
- package/dist/src/host-runtime.js +56 -5
- package/dist/src/host-view.d.ts +6 -1
- package/dist/src/host-view.js +46 -20
- package/dist/src/host.d.ts +2 -2
- package/dist/src/host.js +246 -118
- package/dist/src/index.d.ts +1 -2
- package/dist/src/index.js +1985 -1690
- package/dist/src/index.js.map +3 -3
- package/dist/src/paths.d.ts +8 -5
- package/dist/src/paths.js +24 -13
- 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 +29 -71
- package/dist/src/runtime/agent-runner.d.ts +2 -0
- package/dist/src/script-tools.d.ts +27 -0
- package/dist/src/script-tools.js +87 -0
- package/dist/src/session-lease.d.ts +1 -1
- package/dist/src/session-lease.js +45 -11
- package/dist/src/settings.d.ts +19 -3
- package/dist/src/settings.js +63 -50
- package/dist/src/store.d.ts +3 -1
- package/dist/src/store.js +17 -1
- package/dist/src/trajectory.d.ts +12 -3
- package/dist/src/trajectory.js +32 -11
- package/dist/src/types.d.ts +129 -45
- package/dist/src/types.js +5 -8
- package/dist/src/utils.d.ts +21 -1
- package/dist/src/utils.js +46 -6
- package/dist/src/validation.d.ts +14 -3
- package/dist/src/validation.js +143 -87
- package/dist/starter/index.js +8 -11
- package/dist/starter/index.js.map +1 -1
- package/dist/starter/review-loop.d.ts +0 -13
- package/dist/starter/review-loop.js +8 -9
- package/dist/subagents/index.js +904 -956
- package/dist/subagents/index.js.map +3 -3
- package/dist/subagents/src/contracts.d.ts +48 -31
- package/dist/subagents/src/contracts.js +20 -9
- package/dist/subagents/src/index.d.ts +1 -1
- package/dist/subagents/src/index.js +14 -8
- package/dist/subagents/src/manager.js +52 -73
- package/dist/subagents/src/navigator.js +3 -14
- package/dist/subagents/src/view.d.ts +1 -1
- package/dist/subagents/src/view.js +15 -68
- package/dist/subagents/src/worktree.js +1 -1
- package/dist/trajectory/assets/index.html +35 -13
- package/dist/trajectory/index.js +576 -932
- package/dist/trajectory/index.js.map +3 -3
- package/dist/trajectory/src/assets/index.html +35 -13
- package/dist/trajectory/src/index.js +134 -104
- package/dist/trajectory/src/server.js +268 -27
- 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 +10 -10
- package/skills/pi-extensible-workflows/SKILL.md +12 -8
- package/src/agent-execution.ts +412 -164
- package/src/background-widget.ts +5 -11
- package/src/decoders.ts +51 -37
- package/src/execution.ts +46 -9
- package/src/host-delivery.ts +5 -8
- package/src/host-navigator.ts +4 -2
- package/src/host-phases.ts +1 -1
- package/src/host-recovery.ts +23 -28
- package/src/host-runtime.ts +44 -6
- package/src/host-view.ts +41 -16
- package/src/host.ts +229 -142
- package/src/index.ts +1 -2
- package/src/paths.ts +18 -9
- package/src/persistence.ts +1 -0
- package/src/pi-runtime-runner.ts +3 -1
- package/src/registry.ts +28 -64
- package/src/runtime/agent-runner.ts +2 -0
- package/src/script-tools.ts +76 -0
- package/src/session-lease.ts +34 -6
- package/src/settings.ts +56 -50
- package/src/store.ts +18 -2
- package/src/trajectory.ts +32 -15
- package/src/types.ts +82 -34
- package/src/utils.ts +42 -5
- package/src/validation.ts +121 -80
- package/starter/index.ts +0 -1
- package/starter/review-loop.ts +8 -9
- 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 +10 -16
- package/subagents/src/contracts.ts +21 -11
- package/subagents/src/index.ts +17 -8
- package/subagents/src/manager.ts +48 -128
- package/subagents/src/navigator.ts +4 -13
- package/subagents/src/view.ts +16 -63
- package/subagents/src/worktree.ts +1 -1
- package/trajectory/src/assets/index.html +35 -13
- package/trajectory/src/index.ts +112 -91
- package/trajectory/src/server.ts +46 -10
- package/trajectory/src/startup-mutex.ts +127 -0
- package/dist/src/roles.d.ts +0 -70
- package/dist/src/roles.js +0 -325
- 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 -326
package/src/background-widget.ts
CHANGED
|
@@ -49,7 +49,7 @@ const ENTRY_TYPE = "piewf-run-receipt";
|
|
|
49
49
|
* Repaint interval. Fast enough that the spinner reads as motion rather than a
|
|
50
50
|
* stutter; the underlying data is only re-read when an event says it changed.
|
|
51
51
|
*/
|
|
52
|
-
const REPAINT_MS = 125;
|
|
52
|
+
export const REPAINT_MS = 125;
|
|
53
53
|
|
|
54
54
|
/**
|
|
55
55
|
* Filesystem rescan interval. Shell-only activity has no event, but it does not
|
|
@@ -190,7 +190,6 @@ interface ReceiptAgent {
|
|
|
190
190
|
name: string;
|
|
191
191
|
state: string;
|
|
192
192
|
model?: string;
|
|
193
|
-
role?: string;
|
|
194
193
|
requestedModel?: string;
|
|
195
194
|
tools?: readonly string[];
|
|
196
195
|
/** Accepted for rendering older receipt entries; new receipts persist granted tools. */
|
|
@@ -283,7 +282,7 @@ function optionalNumber(value: unknown): boolean {
|
|
|
283
282
|
function renderableAgent(value: unknown): boolean {
|
|
284
283
|
if (!object(value) || typeof value.name !== "string" || typeof value.state !== "string" || !object(value.model) || typeof value.model.model !== "string" || !Array.isArray(value.tools) || !value.tools.every((tool) => typeof tool === "string")) return false;
|
|
285
284
|
if (value.model.thinking !== undefined && typeof value.model.thinking !== "string") return false;
|
|
286
|
-
for (const key of ["id", "label", "parentId", "
|
|
285
|
+
for (const key of ["id", "label", "parentId", "requestedModel"] as const) if (value[key] !== undefined && typeof value[key] !== "string") return false;
|
|
287
286
|
for (const key of ["attempts", "startedAt", "durationMs", "lastEventAt"] as const) if (!optionalNumber(value[key])) return false;
|
|
288
287
|
const accounting = value.accounting;
|
|
289
288
|
if (accounting !== undefined && (!object(accounting) || !["input", "output", "cacheRead", "cacheWrite", "cost"].every((key) => optionalNumber(accounting[key])))) return false;
|
|
@@ -695,8 +694,7 @@ function renderFrame(runs: readonly Run[], now: number, width: number, offset =
|
|
|
695
694
|
: paint(silent >= STALL_MS ? ROLE.failed : ROLE.warn, "⚠")
|
|
696
695
|
} ${
|
|
697
696
|
// The label is what the workflow called this agent — `reviewer #2`,
|
|
698
|
-
// `scout (api)` —
|
|
699
|
-
// agents of one role are otherwise indistinguishable.
|
|
697
|
+
// `scout (api)` — preserving an explicit label when supplied.
|
|
700
698
|
agent.label ?? agent.name
|
|
701
699
|
}${
|
|
702
700
|
silent === undefined
|
|
@@ -848,18 +846,15 @@ function receiptFor(run: Run, fallback?: { message?: string }): Receipt {
|
|
|
848
846
|
agents: agents.map((agent) => ({
|
|
849
847
|
id: agent.id,
|
|
850
848
|
...(agent.parentId === undefined ? {} : { parentId: agent.parentId }),
|
|
851
|
-
//
|
|
852
|
-
// from. Two agents of one role are otherwise indistinguishable.
|
|
849
|
+
// Preserve explicit display labels.
|
|
853
850
|
name: agent.label ?? agent.name,
|
|
854
851
|
state: agent.state,
|
|
855
|
-
//
|
|
856
|
-
// name alone does not say whether it reasoned cheaply or deeply.
|
|
852
|
+
// Show both model and thinking level.
|
|
857
853
|
...(agent.model.model
|
|
858
854
|
? {
|
|
859
855
|
model: `${agent.model.model}${agent.model.thinking ? `:${agent.model.thinking}` : ""}`,
|
|
860
856
|
}
|
|
861
857
|
: {}),
|
|
862
|
-
...(agent.role ? { role: agent.role } : {}),
|
|
863
858
|
...(agent.requestedModel ? { requestedModel: agent.requestedModel } : {}),
|
|
864
859
|
tools: [...agent.tools],
|
|
865
860
|
// Cache reads dwarf real input and cost almost nothing, so the split is
|
|
@@ -947,7 +942,6 @@ export function renderReceipt(data: Receipt, expanded: boolean, theme: Theme): s
|
|
|
947
942
|
|
|
948
943
|
const under = `${prefix}${lastAgent ? " " : "│ "} `;
|
|
949
944
|
const meta = [
|
|
950
|
-
agent.role ? `role ${agent.role}` : "",
|
|
951
945
|
agent.requestedModel && agent.requestedModel !== agent.model
|
|
952
946
|
? `via ${agent.requestedModel}`
|
|
953
947
|
: "",
|
package/src/decoders.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { AGENT_STATES, BUDGET_DIMENSIONS, BUDGET_EVENT_TYPES, RUN_STATES, isContextFileScope, type AgentAccounting, type AgentActivity, type AgentAttemptSummary, type AgentContinuity, type
|
|
1
|
+
import { AGENT_STATES, BUDGET_DIMENSIONS, BUDGET_EVENT_TYPES, RUN_STATES, isContextFileScope, type AgentAccounting, type AgentActivity, type AgentAttemptSummary, type AgentContinuity, type PreparedAgentConfiguration, LAUNCH_SNAPSHOT_IDENTITY_VERSION, type AgentRecord, type AgentResourceInspection, type AgentResourceSelectors, type BudgetApprovalRequest, type BudgetDimension, type BudgetEvent, type BudgetEventType, type ContextFileScope, type JsonValue, type LaunchSnapshot, type ModelSpec, type RunRecord, type WorkflowBudgetUsage, type WorkflowExtensionSettings, type WorkflowRetentionSettings, type WorkflowRunEvent } from "./types.js";
|
|
2
2
|
import type { OwnershipRecord, ScheduledAgentOptions } from "./agent-execution.js";
|
|
3
3
|
import { finiteNumber, isThinkingLevel, isWorkflowErrorCode, jsonValue, object, positiveInteger, validWorkflowExtensionNamespace } from "./utils.js";
|
|
4
4
|
|
|
5
5
|
export interface EffectiveSystemPrompt { sessionId: string; attempt: number; turn: number; sha256: string; prompt: string }
|
|
6
6
|
export type PersistedRun = RunRecord;
|
|
7
|
-
export interface RunSummaryAgent { id: string; name: string; label?: string; state: string;
|
|
7
|
+
export interface RunSummaryAgent { id: string; name: string; label?: string; state: string; attempts: number }
|
|
8
8
|
export interface RunSummaryArtifacts { runDirectory: string; statePath: string; journalPath: string; snapshotPath: string; workflowPath: string; resultPath: string; summaryPath: string }
|
|
9
9
|
export interface RunSummary { schemaVersion: 1; runId: string; sessionId: string; workflowName: string; state: RunRecord["state"]; createdAt: string; updatedAt: string; terminalAt?: string; usage: WorkflowBudgetUsage; agents: readonly RunSummaryAgent[]; error?: RunRecord["error"]; failedAt?: string; replayablePaths: readonly string[]; incompletePaths: readonly string[]; artifacts: RunSummaryArtifacts }
|
|
10
10
|
export interface CompletedOperation { path: string; value: JsonValue }
|
|
@@ -17,6 +17,7 @@ type PersistedAgent = RunRecord["agents"][number];
|
|
|
17
17
|
type PersistedPhaseRecord = NonNullable<RunRecord["phaseHistory"]>[number];
|
|
18
18
|
type PersistedShellActivity = NonNullable<RunRecord["activeShellsByPhase"]>[number];
|
|
19
19
|
type PersistedDelivery = NonNullable<RunRecord["delivery"]>;
|
|
20
|
+
type PersistedScriptCall = NonNullable<RunRecord["scriptCalls"]>[number];
|
|
20
21
|
type PersistedIdentity = NonNullable<ScheduledAgentOptions["agentIdentity"]>;
|
|
21
22
|
type PersistedOptions = ScheduledAgentOptions;
|
|
22
23
|
|
|
@@ -78,25 +79,19 @@ function decodeAgentResourceSelectors(value: unknown): AgentResourceSelectors |
|
|
|
78
79
|
return { ...(skills === undefined ? {} : { skills }), ...(extensions === undefined ? {} : { extensions }), ...(tools === undefined ? {} : { tools }) };
|
|
79
80
|
}
|
|
80
81
|
function decodeContextFileScopes(value: unknown): ContextFileScope[] | undefined { return decodeArray(value, (scope) => isContextFileScope(scope) ? scope : undefined); }
|
|
81
|
-
export function
|
|
82
|
-
if (!object(value)) return undefined;
|
|
83
|
-
const
|
|
84
|
-
const
|
|
85
|
-
const
|
|
86
|
-
const
|
|
87
|
-
const
|
|
88
|
-
const skills = value.skills === undefined ? undefined : decodeStringArray(value.skills);
|
|
89
|
-
const extensions = value.extensions === undefined ? undefined : decodeStringArray(value.extensions);
|
|
90
|
-
const overrideSystemPrompt = optionalBoolean(value.overrideSystemPrompt);
|
|
82
|
+
export function decodePreparedAgentConfiguration(value: unknown): PreparedAgentConfiguration | undefined {
|
|
83
|
+
if (!object(value) || !jsonValue(value) || typeof value.projectTrusted !== "boolean" || typeof value.systemPromptAppend !== "string") return undefined;
|
|
84
|
+
const model = decodeModelSpec(value.model);
|
|
85
|
+
const tools = decodeStringArray(value.tools);
|
|
86
|
+
const skills = decodeStringArray(value.skills);
|
|
87
|
+
const extensions = decodeStringArray(value.extensions);
|
|
88
|
+
const settings = decodeWorkflowExtensions(value.settings);
|
|
91
89
|
const contextFiles = value.contextFiles === undefined ? undefined : decodeContextFileScopes(value.contextFiles);
|
|
92
|
-
const
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
return {
|
|
97
|
-
...(prompt === undefined ? {} : { prompt }), ...(description === undefined ? {} : { description }), ...(foldedModel === undefined ? {} : { model: foldedModel }),
|
|
98
|
-
...(tools === undefined ? {} : { tools }), ...(skills === undefined ? {} : { skills }), ...(extensions === undefined ? {} : { extensions }), ...(overrideSystemPrompt === undefined ? {} : { overrideSystemPrompt }), ...(contextFiles === undefined ? {} : { contextFiles }), ...(extensionSettings === undefined ? {} : { extensionSettings }),
|
|
99
|
-
};
|
|
90
|
+
const label = optionalString(value.label);
|
|
91
|
+
const systemPrompt = optionalString(value.systemPrompt);
|
|
92
|
+
const excludeTools = value.excludeTools === undefined ? undefined : decodeStringArray(value.excludeTools);
|
|
93
|
+
if (!model || !tools || !skills || !extensions || !settings || label === INVALID_PERSISTED_VALUE || systemPrompt === INVALID_PERSISTED_VALUE || value.contextFiles !== undefined && !contextFiles || value.excludeTools !== undefined && !excludeTools) return undefined;
|
|
94
|
+
return { model, tools, skills, extensions, settings, projectTrusted: value.projectTrusted, systemPromptAppend: value.systemPromptAppend, ...(contextFiles === undefined ? {} : { contextFiles }), ...(excludeTools === undefined ? {} : { excludeTools }), ...(label === undefined ? {} : { label }), ...(systemPrompt === undefined ? {} : { systemPrompt }) };
|
|
100
95
|
}
|
|
101
96
|
function decodeWorkflowMetadata(value: unknown): LaunchSnapshot["metadata"] | undefined {
|
|
102
97
|
if (!object(value) || typeof value.name !== "string") return undefined;
|
|
@@ -200,17 +195,22 @@ function decodeScheduledAgentOptions(value: unknown): PersistedOptions | undefin
|
|
|
200
195
|
const parentBreadcrumb = optionalString(value.parentBreadcrumb);
|
|
201
196
|
const worktreeOwner = optionalString(value.worktreeOwner);
|
|
202
197
|
const model = optionalString(value.model);
|
|
203
|
-
const role = typeof value.role === "string" ? value.role : undefined;
|
|
204
198
|
const contextFiles = value.contextFiles === undefined ? undefined : decodeContextFileScopes(value.contextFiles);
|
|
205
199
|
const schema = value.schema === undefined ? undefined : decodeJsonObject(value.schema);
|
|
206
200
|
const retries = optionalNumber(value.retries);
|
|
207
201
|
const timeoutMs = value.timeoutMs;
|
|
202
|
+
if (value.projectTrusted !== undefined && typeof value.projectTrusted !== "boolean") return undefined;
|
|
203
|
+
const rawCapabilities = value.capabilities;
|
|
204
|
+
const capabilities = rawCapabilities === undefined ? undefined : object(rawCapabilities) ? { tools: decodeStringArray(rawCapabilities.tools), skills: decodeStringArray(rawCapabilities.skills), extensions: decodeStringArray(rawCapabilities.extensions) } : undefined;
|
|
205
|
+
if (rawCapabilities !== undefined && (!capabilities?.tools || !capabilities.skills || !capabilities.extensions)) return undefined;
|
|
206
|
+
const configuration = value.configuration === undefined ? undefined : decodePreparedAgentConfiguration(value.configuration);
|
|
207
|
+
if (value.configuration !== undefined && configuration === undefined) return undefined;
|
|
208
208
|
const agentOptions = value.agentOptions === undefined ? undefined : decodeJsonObject(value.agentOptions);
|
|
209
209
|
const agentIdentity = value.agentIdentity === undefined ? undefined : decodeIdentity(value.agentIdentity);
|
|
210
210
|
const sessionPath = optionalString(value.sessionPath);
|
|
211
211
|
const continuity = value.continuity === undefined ? undefined : isAgentContinuity(value.continuity) ? value.continuity : INVALID_PERSISTED_VALUE;
|
|
212
212
|
const extensionSettings = value.extensionSettings === undefined ? undefined : decodeWorkflowExtensions(value.extensionSettings);
|
|
213
|
-
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.
|
|
213
|
+
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)) return undefined;
|
|
214
214
|
return {
|
|
215
215
|
label: value.label,
|
|
216
216
|
...(requestedLabel === undefined ? {} : { requestedLabel }),
|
|
@@ -220,7 +220,6 @@ function decodeScheduledAgentOptions(value: unknown): PersistedOptions | undefin
|
|
|
220
220
|
...(skills === undefined ? {} : { skills }), ...(extensions === undefined ? {} : { extensions }),
|
|
221
221
|
...(worktreeOwner === undefined ? {} : { worktreeOwner }),
|
|
222
222
|
...(model === undefined ? {} : { model }),
|
|
223
|
-
...(role === undefined ? {} : { role }),
|
|
224
223
|
...(contextFiles === undefined ? {} : { contextFiles }),
|
|
225
224
|
...(schema === undefined ? {} : { schema }),
|
|
226
225
|
...(retries === undefined ? {} : { retries }),
|
|
@@ -228,6 +227,9 @@ function decodeScheduledAgentOptions(value: unknown): PersistedOptions | undefin
|
|
|
228
227
|
...(sessionPath === undefined ? {} : { sessionPath }),
|
|
229
228
|
...(continuity === undefined ? {} : { continuity }),
|
|
230
229
|
...(agentOptions === undefined ? {} : { agentOptions }),
|
|
230
|
+
...(configuration === undefined ? {} : { configuration }),
|
|
231
|
+
...(value.projectTrusted === undefined ? {} : { projectTrusted: value.projectTrusted }),
|
|
232
|
+
...(capabilities?.tools && capabilities.skills && capabilities.extensions ? { capabilities: { tools: capabilities.tools, skills: capabilities.skills, extensions: capabilities.extensions } } : {}),
|
|
231
233
|
...(agentIdentity === undefined ? {} : { agentIdentity }),
|
|
232
234
|
...(extensionSettings === undefined ? {} : { extensionSettings }),
|
|
233
235
|
};
|
|
@@ -268,11 +270,10 @@ function decodeAgentResourceInspection(value: unknown): AgentResourceInspection
|
|
|
268
270
|
const sources = sourceRecord === undefined ? undefined : {
|
|
269
271
|
global: decodeAgentResourceSelectors(sourceRecord.global),
|
|
270
272
|
project: decodeAgentResourceSelectors(sourceRecord.project),
|
|
271
|
-
...(sourceRecord.role === undefined ? {} : { role: decodeAgentResourceSelectors(sourceRecord.role) }),
|
|
272
273
|
...(sourceRecord.call === undefined ? {} : { call: decodeAgentResourceSelectors(sourceRecord.call) }),
|
|
273
274
|
};
|
|
274
|
-
if (!selectors || !skills || !extensions || !tools || !unmatchedSkills || !unmatchedExtensions || !unmatchedTools || rawSources !== undefined && (!sources || !sources.global || !sources.project || sources.
|
|
275
|
-
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.
|
|
275
|
+
if (!selectors || !skills || !extensions || !tools || !unmatchedSkills || !unmatchedExtensions || !unmatchedTools || rawSources !== undefined && (!sources || !sources.global || !sources.project || sources.call === undefined && sourceRecord?.call !== undefined)) return undefined;
|
|
276
|
+
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 }) } }) };
|
|
276
277
|
}
|
|
277
278
|
function decodeAgentSetupSummary(value: unknown): NonNullable<NonNullable<AgentRecord["attemptDetails"]>[number]["setup"]> | undefined {
|
|
278
279
|
if (!object(value) || typeof value.cwd !== "string") return undefined;
|
|
@@ -310,7 +311,6 @@ function decodeAgent(value: unknown): AgentRecord | undefined {
|
|
|
310
311
|
const worktreeOwner = optionalString(value.worktreeOwner);
|
|
311
312
|
const handle = optionalString(value.handle);
|
|
312
313
|
const continuity = value.continuity === undefined ? undefined : isAgentContinuity(value.continuity) ? value.continuity : INVALID_PERSISTED_VALUE;
|
|
313
|
-
const role = optionalString(value.role);
|
|
314
314
|
const requestedModel = optionalString(value.requestedModel);
|
|
315
315
|
const startedAt = optionalNumber(value.startedAt);
|
|
316
316
|
const durationMs = optionalNumber(value.durationMs);
|
|
@@ -319,7 +319,7 @@ function decodeAgent(value: unknown): AgentRecord | undefined {
|
|
|
319
319
|
const toolCalls = value.toolCalls === undefined ? undefined : decodeArray(value.toolCalls, decodeAgentToolCall);
|
|
320
320
|
const activity = value.activity === undefined ? undefined : decodeAgentActivity(value.activity);
|
|
321
321
|
const lastEventAt = optionalNumber(value.lastEventAt);
|
|
322
|
-
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 ||
|
|
322
|
+
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)) return undefined;
|
|
323
323
|
if (!model || !tools || value.structuralPath !== undefined && !structuralPath || value.attemptDetails !== undefined && !attemptDetails || value.accounting !== undefined && !accounting || value.toolCalls !== undefined && !toolCalls || value.activity !== undefined && !activity) return undefined;
|
|
324
324
|
return {
|
|
325
325
|
...(systemPrompt === undefined ? {} : { systemPrompt }),
|
|
@@ -336,7 +336,6 @@ function decodeAgent(value: unknown): AgentRecord | undefined {
|
|
|
336
336
|
...(worktreeOwner === undefined ? {} : { worktreeOwner }),
|
|
337
337
|
...(handle === undefined ? {} : { handle, turn: value.turn as number }),
|
|
338
338
|
...(continuity === undefined ? {} : { continuity }),
|
|
339
|
-
...(role === undefined ? {} : { role }),
|
|
340
339
|
...(requestedModel === undefined ? {} : { requestedModel }),
|
|
341
340
|
model,
|
|
342
341
|
tools,
|
|
@@ -394,6 +393,22 @@ function decodeRunEvent(value: unknown): WorkflowRunEvent | undefined {
|
|
|
394
393
|
if (timestamp === INVALID_PERSISTED_VALUE) return undefined;
|
|
395
394
|
return { type: value.type, message: value.message, ...(timestamp === undefined ? {} : { timestamp }) };
|
|
396
395
|
}
|
|
396
|
+
function decodeScriptCall(value: unknown): PersistedScriptCall | undefined {
|
|
397
|
+
if (!object(value) || value.kind !== "shell" && value.kind !== "tool" || typeof value.name !== "string" || typeof value.path !== "string" || !finiteNumber(value.startedAt)) return undefined;
|
|
398
|
+
const shell = value.kind === "shell";
|
|
399
|
+
if (shell && !jsonValue(value.input)) return undefined;
|
|
400
|
+
const inputBytes = shell ? optionalNumber(value.inputBytes) : undefined;
|
|
401
|
+
const phase = optionalString(value.phase);
|
|
402
|
+
const finishedAt = optionalNumber(value.finishedAt);
|
|
403
|
+
let error: PersistedScriptCall["error"];
|
|
404
|
+
if (value.error !== undefined) {
|
|
405
|
+
if (shell) error = decodeWorkflowError(value.error);
|
|
406
|
+
else if (object(value.error) && typeof value.error.code === "string") error = { code: isWorkflowErrorCode(value.error.code) ? value.error.code : "INTERNAL_ERROR" };
|
|
407
|
+
if (!error) return undefined;
|
|
408
|
+
}
|
|
409
|
+
if (inputBytes === INVALID_PERSISTED_VALUE || phase === INVALID_PERSISTED_VALUE || finishedAt === INVALID_PERSISTED_VALUE) return undefined;
|
|
410
|
+
return { kind: value.kind, name: value.name, path: value.path, ...(shell && jsonValue(value.input) ? { input: value.input } : {}), ...(inputBytes === undefined ? {} : { inputBytes }), ...(phase === undefined ? {} : { phase }), startedAt: value.startedAt, ...(finishedAt === undefined ? {} : { finishedAt }), ...(error === undefined ? {} : { error }) };
|
|
411
|
+
}
|
|
397
412
|
function decodeDelivery(value: unknown): PersistedDelivery | undefined {
|
|
398
413
|
if (!object(value)) return undefined;
|
|
399
414
|
const mode = value.mode;
|
|
@@ -423,6 +438,8 @@ export function decodePersistedRun(value: unknown, allowLegacyAgentSessions = fa
|
|
|
423
438
|
const budgetEvents = value.budgetEvents === undefined ? undefined : decodeArray(value.budgetEvents, decodeBudgetEvent);
|
|
424
439
|
const events = value.events === undefined ? undefined : decodeArray(value.events, decodeRunEvent);
|
|
425
440
|
const delivery = value.delivery === undefined ? undefined : decodeDelivery(value.delivery);
|
|
441
|
+
const scriptCalls = value.scriptCalls === undefined ? undefined : decodeArray(value.scriptCalls, decodeScriptCall);
|
|
442
|
+
if (value.scriptCalls !== undefined && !scriptCalls) return undefined;
|
|
426
443
|
if (!agentSessions || !agents || parentRunId === INVALID_PERSISTED_VALUE || value.retry !== undefined && !retry || phase === INVALID_PERSISTED_VALUE || value.phaseHistory !== undefined && !phaseHistory || phaseHistoryIndex === INVALID_PERSISTED_VALUE || activeShells === INVALID_PERSISTED_VALUE || activeShellStartedAt === INVALID_PERSISTED_VALUE || value.activeShellsByPhase !== undefined && !activeShellsByPhase || value.error !== undefined && !error || failedAt === INVALID_PERSISTED_VALUE || value.budget !== undefined && !budget || budgetVersion === INVALID_PERSISTED_VALUE || value.budgetVersion !== undefined && !integer(budgetVersion) || value.usage !== undefined && !usage || value.budgetEvents !== undefined && !budgetEvents || value.events !== undefined && !events || value.delivery !== undefined && !delivery) return undefined;
|
|
427
444
|
if (value.agentSessions === undefined && !allowLegacyAgentSessions) return undefined;
|
|
428
445
|
return {
|
|
@@ -441,6 +458,7 @@ export function decodePersistedRun(value: unknown, allowLegacyAgentSessions = fa
|
|
|
441
458
|
...(activeShells === undefined ? {} : { activeShells }),
|
|
442
459
|
...(activeShellStartedAt === undefined ? {} : { activeShellStartedAt }),
|
|
443
460
|
...(activeShellsByPhase === undefined ? {} : { activeShellsByPhase }),
|
|
461
|
+
...(scriptCalls === undefined ? {} : { scriptCalls }),
|
|
444
462
|
...(error === undefined ? {} : { error }),
|
|
445
463
|
...(failedAt === undefined ? {} : { failedAt }),
|
|
446
464
|
...(budget === undefined ? {} : { budget }),
|
|
@@ -466,13 +484,11 @@ export function decodeLaunchSnapshot(value: unknown): LaunchSnapshot | undefined
|
|
|
466
484
|
const phases = value.phases === undefined ? undefined : decodeStringArray(value.phases);
|
|
467
485
|
const models = decodeStringArray(value.models);
|
|
468
486
|
const tools = decodeStringArray(value.tools);
|
|
469
|
-
const
|
|
470
|
-
const roles = value.roles === undefined ? undefined : decodeRecord(value.roles, decodeAgentDefinition);
|
|
471
|
-
const projectRoles = value.projectRoles === undefined ? undefined : decodeStringArray(value.projectRoles);
|
|
487
|
+
const agentConfigurations = decodeRecord(value.agentConfigurations, decodePreparedAgentConfiguration);
|
|
472
488
|
const schemas = decodeArray(value.schemas, decodeJsonObject);
|
|
473
|
-
if (identityVersion
|
|
489
|
+
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) return undefined;
|
|
474
490
|
return {
|
|
475
|
-
|
|
491
|
+
identityVersion: LAUNCH_SNAPSHOT_IDENTITY_VERSION,
|
|
476
492
|
...(launchMode === undefined ? {} : { launchMode }),
|
|
477
493
|
script: value.script,
|
|
478
494
|
args,
|
|
@@ -485,9 +501,7 @@ export function decodeLaunchSnapshot(value: unknown): LaunchSnapshot | undefined
|
|
|
485
501
|
...(phases === undefined ? {} : { phases }),
|
|
486
502
|
models,
|
|
487
503
|
tools,
|
|
488
|
-
|
|
489
|
-
...(roles === undefined ? {} : { roles }),
|
|
490
|
-
...(projectRoles === undefined ? {} : { projectRoles }),
|
|
504
|
+
agentConfigurations,
|
|
491
505
|
schemas,
|
|
492
506
|
};
|
|
493
507
|
}
|
package/src/execution.ts
CHANGED
|
@@ -5,9 +5,9 @@ import { join } from "node:path";
|
|
|
5
5
|
import { StringDecoder } from "node:string_decoder";
|
|
6
6
|
import { RunStore, structuralPath as operationPath } from "./persistence.js";
|
|
7
7
|
import type { AgentAttempt } from "./agent-execution.js";
|
|
8
|
-
import type { AgentIdentity, AgentAttemptSummary, FunctionIdentity, JsonValue, ShellIdentity, ShellOptions, ShellResult, WorkflowAgentSessionReference, WorkflowBridge, WorkflowErrorCode, WorkflowExecution } from "./types.js";
|
|
9
|
-
import { WorkflowError,
|
|
10
|
-
import { asWorkflowError, errorText, fail, isWorkflowAuthored, isWorkflowErrorCode, jsonValue, markWorkflowAuthored, object, positiveInteger } from "./utils.js";
|
|
8
|
+
import type { AgentIdentity, AgentAttemptSummary, FunctionIdentity, JsonValue, ShellIdentity, ShellOptions, ShellResult, ToolIdentity, WorkflowAgentSessionReference, WorkflowBridge, WorkflowErrorCode, WorkflowExecution } from "./types.js";
|
|
9
|
+
import { WorkflowError, sumAccounting, zeroAccounting } from "./types.js";
|
|
10
|
+
import { asWorkflowError, errorText, fail, isWorkflowAuthored, isWorkflowErrorCode, jsonDigest, jsonValue, markWorkflowAuthored, object, positiveInteger } from "./utils.js";
|
|
11
11
|
import { instrumentWorkflow, validateAgentOptions, validateShellCommand, validateShellOptions } from "./validation.js";
|
|
12
12
|
|
|
13
13
|
export const RPC_LIMIT_BYTES = 10 * 1024 * 1024;
|
|
@@ -41,6 +41,8 @@ function isRpcMessage(value: unknown): value is RpcMessage {
|
|
|
41
41
|
const HEARTBEAT_INTERVAL_MS = 1000;
|
|
42
42
|
|
|
43
43
|
const OUTCOME_ERRORS = new Set<string>(["AGENT_TIMEOUT", "AGENT_FAILED", "RESULT_INVALID"]);
|
|
44
|
+
/** Script handle names may not start with this: registered-function handles are journaled under it. */
|
|
45
|
+
export const FUNCTION_HANDLE_PREFIX = "function-handle";
|
|
44
46
|
const WORK_RESULT_BRAND = "__workResult";
|
|
45
47
|
|
|
46
48
|
const childSource = String.raw`
|
|
@@ -152,15 +154,13 @@ const internalAgent = (...values) => {
|
|
|
152
154
|
void result.then(() => agentInflight.delete(occurrenceKey), () => agentInflight.delete(occurrenceKey));
|
|
153
155
|
return guardedAgentResult(result);
|
|
154
156
|
};
|
|
155
|
-
const AGENT_HANDLE_KEYS = ["name", "role", "model", "tools", "skills", "extensions", "contextFiles", "label"];
|
|
156
157
|
const agentHandleNames = new Set();
|
|
157
158
|
const agentHandleInflight = new Set();
|
|
158
159
|
const internalAgentCreate = (...values) => {
|
|
159
160
|
if (values.length !== 1 || !values[0] || typeof values[0] !== "object" || Array.isArray(values[0])) throw workError("INVALID_METADATA", "agent.create requires one options object");
|
|
160
161
|
const options = values[0];
|
|
161
162
|
const name = named(options.name, "agent.create");
|
|
162
|
-
|
|
163
|
-
if (unsupported) throw workError("INVALID_METADATA", "agent.create option is unsupported: " + unsupported);
|
|
163
|
+
if (name.startsWith("${FUNCTION_HANDLE_PREFIX}/")) throw workError("INVALID_METADATA", "Agent handle names may not start with ${FUNCTION_HANDLE_PREFIX}/");
|
|
164
164
|
if (agentHandleNames.has(name)) throw workError("INVALID_METADATA", "Duplicate agent handle name: " + name);
|
|
165
165
|
agentHandleNames.add(name);
|
|
166
166
|
const config = Object.freeze(Object.fromEntries(Object.entries(options).filter(([key]) => key !== "name")));
|
|
@@ -275,6 +275,30 @@ const functions = Object.freeze(Object.fromEntries(Object.entries(config.functio
|
|
|
275
275
|
Object.defineProperty(result, "toJSON", { value() { throw workError("INVALID_METADATA", "Workflow function result is a Promise; await it before serialization"); } });
|
|
276
276
|
return result;
|
|
277
277
|
}])));
|
|
278
|
+
// tools.<id>(args) runs a Pi session tool through the host. Any identifier resolves, so a resumed run
|
|
279
|
+
// replays journaled calls even when the tool is gone; the host rejects identifiers it cannot call.
|
|
280
|
+
// A call is the n-th call with the same arguments in its scope, so replay does not depend on the
|
|
281
|
+
// order in which concurrent calls start.
|
|
282
|
+
const NOT_TOOLS = new Set(["then", "toJSON", "toString", "valueOf", "constructor"]);
|
|
283
|
+
const sortedJson = value => Array.isArray(value) ? value.map(sortedJson) : value && typeof value === "object" ? Object.fromEntries(Object.keys(value).sort().map(key => [key, sortedJson(value[key])])) : value;
|
|
284
|
+
const toolOccurrences = new Map();
|
|
285
|
+
const toolCall = id => (...values) => {
|
|
286
|
+
if (worktreeOwners.getStore()) throw workError("INVALID_METADATA", "tools." + id + " is unavailable inside withWorktree: session tools resolve paths against the session working directory");
|
|
287
|
+
if (values.length > 1 || values[0] !== undefined && (!values[0] || typeof values[0] !== "object" || Array.isArray(values[0]))) throw workError("INVALID_METADATA", "tools." + id + " takes one arguments object");
|
|
288
|
+
const args = values[0] === undefined ? {} : values[0];
|
|
289
|
+
const structuralPath = inheritedAgentPath.getStore() || [];
|
|
290
|
+
const key = JSON.stringify([structuralPath, id, sortedJson(args)]);
|
|
291
|
+
const occurrence = (toolOccurrences.get(key) || 0) + 1;
|
|
292
|
+
toolOccurrences.set(key, occurrence);
|
|
293
|
+
return guardedResult(rpc("tool", [id, args, { structuralPath: [...structuralPath], occurrence }]), "tool");
|
|
294
|
+
};
|
|
295
|
+
const tools = new Proxy(Object.create(null), {
|
|
296
|
+
get: (_target, key) => typeof key === "string" && !NOT_TOOLS.has(key) ? toolCall(key) : undefined,
|
|
297
|
+
// Probing would branch on the session that resumed the run; the launch already checks named tools.
|
|
298
|
+
has: () => { throw workError("INVALID_METADATA", "Workflow scripts cannot probe tools with 'in'; call tools.<name>(...) directly, and the launch checks that the tool exists"); },
|
|
299
|
+
set: () => false,
|
|
300
|
+
defineProperty: () => false,
|
|
301
|
+
});
|
|
278
302
|
const recordEntries = (value, kind) => {
|
|
279
303
|
if (!value || typeof value !== "object" || Array.isArray(value)) throw workError("INVALID_METADATA", kind + " must be a record");
|
|
280
304
|
return Object.entries(value);
|
|
@@ -335,7 +359,7 @@ const pipeline = async (operationName, items, stages) => {
|
|
|
335
359
|
return Object.fromEntries(results.map(result => [result.name, result.value]));
|
|
336
360
|
};
|
|
337
361
|
const safeMath = Object.fromEntries(Object.getOwnPropertyNames(Math).filter(name => name !== "random").map(name => [name, Math[name]]));
|
|
338
|
-
const sandbox = { agent, shell, withWorktree: rejectWorktree, prompt, checkpoint, parallel, pipeline, phase, log, args: config.args, Promise, JSON, Math: Object.freeze(safeMath) };
|
|
362
|
+
const sandbox = { agent, shell, withWorktree: rejectWorktree, prompt, checkpoint, parallel, pipeline, phase, log, tools, args: config.args, Promise, JSON, Math: Object.freeze(safeMath) };
|
|
339
363
|
for (const [name, fn] of Object.entries(functions)) Object.defineProperty(sandbox, name, { value: fn, writable: false, configurable: false });
|
|
340
364
|
for (const name of ["Date","eval","Function","WebAssembly","process","require","module","exports","console","fetch","XMLHttpRequest","WebSocket","performance","crypto","setTimeout","setInterval","setImmediate","queueMicrotask","Intl","SharedArrayBuffer","Atomics"]) sandbox[name] = undefined;
|
|
341
365
|
const context = vm.createContext(sandbox, { codeGeneration: { strings: false, wasm: false } });
|
|
@@ -384,6 +408,14 @@ function readShellIdentity(value: unknown): ShellIdentity {
|
|
|
384
408
|
const identity = readAgentIdentity(value);
|
|
385
409
|
return { structuralPath: identity.structuralPath, callSite: identity.callSite, occurrence: identity.occurrence, ...(identity.worktreeOwner ? { worktreeOwner: identity.worktreeOwner } : {}) };
|
|
386
410
|
}
|
|
411
|
+
function readToolIdentity(value: unknown): ToolIdentity {
|
|
412
|
+
if (!object(value) || !Array.isArray(value.structuralPath) || !value.structuralPath.every((part): part is string => typeof part === "string" && Boolean(part.trim())) || !positiveInteger(value.occurrence)) fail("INTERNAL_ERROR", "Invalid workflow tool identity");
|
|
413
|
+
return { structuralPath: [...value.structuralPath], occurrence: value.occurrence };
|
|
414
|
+
}
|
|
415
|
+
/** The journal path of a script tool call; the arguments' digest keeps calls with different arguments apart whatever order they start in. */
|
|
416
|
+
export function toolIdentityPath(identifier: string, args: JsonValue, identity: ToolIdentity): string {
|
|
417
|
+
return operationPath("tool", ...identity.structuralPath, identifier, `args:${jsonDigest(args)}`, String(identity.occurrence));
|
|
418
|
+
}
|
|
387
419
|
export function agentHandleTurnPath(handle: string, turn: number): string {
|
|
388
420
|
return operationPath("agent", "handle", handle, `turn:${String(turn)}`);
|
|
389
421
|
}
|
|
@@ -552,7 +584,7 @@ export function runWorkflow(script: string, args: JsonValue = null, bridge: Work
|
|
|
552
584
|
const opts = validateAgentOptions(values[1]);
|
|
553
585
|
const identity = readAgentIdentity(values[2]);
|
|
554
586
|
const path = agentIdentityPath(identity);
|
|
555
|
-
const label = typeof opts.label === "string" ? opts.label :
|
|
587
|
+
const label = typeof opts.label === "string" ? opts.label : "agent";
|
|
556
588
|
try {
|
|
557
589
|
const result = await bridge.agent(values[0], opts, controller.signal, identity);
|
|
558
590
|
value = branded({ name: label, ok: true, value: result ?? null });
|
|
@@ -595,6 +627,11 @@ export function runWorkflow(script: string, args: JsonValue = null, bridge: Work
|
|
|
595
627
|
if (!OUTCOME_ERRORS.has(typed.code)) throw typed;
|
|
596
628
|
value = branded({ name, ok: false, failedAt: name, error: workerErrorShape(typed) });
|
|
597
629
|
}
|
|
630
|
+
} else if (method === "tool") {
|
|
631
|
+
if (typeof values[0] !== "string" || !object(values[1])) fail("INTERNAL_ERROR", "tool requires an identifier and object arguments");
|
|
632
|
+
const identity = readToolIdentity(values[2]);
|
|
633
|
+
if (!bridge.tool) fail("UNKNOWN_TOOL", `tools.${values[0]} is unavailable: this run has no session tools`);
|
|
634
|
+
value = await bridge.tool(values[0], values[1], controller.signal, identity);
|
|
598
635
|
} else if (method === "worktree") {
|
|
599
636
|
if (!bridge.worktree || typeof values[0] !== "string" || !values[0]) fail("INTERNAL_ERROR", "worktree requires an active host bridge and scope");
|
|
600
637
|
value = await bridge.worktree(values[0], controller.signal);
|
|
@@ -649,4 +686,4 @@ export async function persistAgentAttempts(store: RunStore, id: string, attempts
|
|
|
649
686
|
return { ...run, agents: run.agents.map((candidate) => candidate.id === id ? { ...candidate, attempts: attempts.length, attemptDetails, accounting: total } : candidate), agentSessions: [...run.agentSessions.filter(({ transport, sessionId }) => !sessionKeys.has(`${transport}:${sessionId}`)), ...sessions] };
|
|
650
687
|
});
|
|
651
688
|
}
|
|
652
|
-
export type { AgentIdentity, FunctionIdentity, ShellIdentity, WorkflowBridge, WorkflowExecution } from "./types.js";
|
|
689
|
+
export type { AgentIdentity, FunctionIdentity, ShellIdentity, ToolIdentity, WorkflowBridge, WorkflowExecution } from "./types.js";
|
package/src/host-delivery.ts
CHANGED
|
@@ -95,13 +95,14 @@ const WORKFLOW_ERROR_PROSE: Record<WorkflowErrorCode, (detail: string) => string
|
|
|
95
95
|
REGISTRY_FROZEN: (detail) => `Workflow extension registration is closed: ${detail}.`,
|
|
96
96
|
GLOBAL_COLLISION: (detail) => `The workflow global name is already in use: ${detail}.`,
|
|
97
97
|
MISSING_WORKFLOW: (detail) => `The registered workflow function is unavailable: ${detail}.`,
|
|
98
|
-
UNKNOWN_MODEL: (detail) => `The workflow requested the unavailable model ${detail.replace(/^(?:Unknown model
|
|
98
|
+
UNKNOWN_MODEL: (detail) => `The workflow requested the unavailable model ${detail.replace(/^(?:Unknown model|Invalid model spec|Required model is unavailable):\s*/, "")}.`,
|
|
99
99
|
UNKNOWN_TOOL: (detail) => `The workflow requested the unavailable tool ${detail.replace(/^Unknown tool:\s*/, "")}.`,
|
|
100
|
-
|
|
100
|
+
UNKNOWN_AGENT: (detail) => `The workflow requested an unavailable owned agent: ${detail}.`,
|
|
101
101
|
RUN_OWNED: (detail) => /already owned|active ownership/.test(detail) ? "The workflow session is already in use." : `The workflow session is already in use: ${detail}.`,
|
|
102
102
|
RUN_NOT_FOUND: (detail) => /^Unknown workflow run\b/.test(detail) ? "The workflow run was not found." : `The workflow run was not found: ${detail}.`,
|
|
103
103
|
RPC_LIMIT_EXCEEDED: (detail) => `The workflow communication data exceeded its size limit: ${detail}.`,
|
|
104
104
|
SHELL_FAILED: (detail) => `The workflow shell command failed: ${detail}.`,
|
|
105
|
+
TOOL_FAILED: (detail) => `The workflow tool call failed: ${detail}.`,
|
|
105
106
|
AGENT_TIMEOUT: (detail) => `The workflow agent timed out: ${detail}.`,
|
|
106
107
|
AGENT_FAILED: (detail) => `The workflow agent failed: ${detail}.`,
|
|
107
108
|
AGENT_RESULT_COLLECTED: (detail) => `The nested agent result was already collected: ${detail}.`,
|
|
@@ -212,7 +213,6 @@ function boundedWorkflowFailureDiagnostics(value: WorkflowFailureDiagnostics): W
|
|
|
212
213
|
...(value.failedAgent ? { failedAgent: {
|
|
213
214
|
id: utf8Prefix(value.failedAgent.id, 128),
|
|
214
215
|
...(value.failedAgent.label ? { label: utf8Prefix(value.failedAgent.label, 128) } : {}),
|
|
215
|
-
...(value.failedAgent.role ? { role: utf8Prefix(value.failedAgent.role, 128) } : {}),
|
|
216
216
|
structuralPath: value.failedAgent.structuralPath.slice(0, 8).map((part) => utf8Prefix(part, 128)),
|
|
217
217
|
attempt: value.failedAgent.attempt,
|
|
218
218
|
...(value.failedAgent.transport ? { transport: utf8Prefix(value.failedAgent.transport, 128) } : {}),
|
|
@@ -221,7 +221,6 @@ function boundedWorkflowFailureDiagnostics(value: WorkflowFailureDiagnostics): W
|
|
|
221
221
|
completedSiblingAgents: (value.completedSiblingAgents ?? []).slice(0, 16).map((agent) => ({
|
|
222
222
|
id: utf8Prefix(agent.id, 128),
|
|
223
223
|
...(agent.label ? { label: utf8Prefix(agent.label, 128) } : {}),
|
|
224
|
-
...(agent.role ? { role: utf8Prefix(agent.role, 128) } : {}),
|
|
225
224
|
structuralPath: agent.structuralPath.slice(0, 8).map((part) => utf8Prefix(part, 128)),
|
|
226
225
|
})),
|
|
227
226
|
completedSiblingPaths: value.completedSiblingPaths.slice(0, 16).map((path) => path.slice(0, 8).map((part) => utf8Prefix(part, 128))),
|
|
@@ -277,7 +276,6 @@ export async function createWorkflowFailureDiagnostics(store: RunStore, metadata
|
|
|
277
276
|
const failedAgent = failedAgentRecord ? {
|
|
278
277
|
id: failedAgentRecord.id,
|
|
279
278
|
...(failedAgentRecord.label ?? failedAgentRecord.name ? { label: failedAgentRecord.label ?? failedAgentRecord.name } : {}),
|
|
280
|
-
...(failedAgentRecord.role ? { role: failedAgentRecord.role } : {}),
|
|
281
279
|
structuralPath: [...(failedAgentRecord.structuralPath ?? [])],
|
|
282
280
|
attempt: Math.max(1, failedAttempt?.attempt ?? failedAgentRecord.attempts),
|
|
283
281
|
...(failedAttempt?.transport ? { transport: failedAttempt.transport } : {}),
|
|
@@ -289,7 +287,6 @@ export async function createWorkflowFailureDiagnostics(store: RunStore, metadata
|
|
|
289
287
|
}).map((agent) => ({
|
|
290
288
|
id: agent.id,
|
|
291
289
|
...(agent.label ?? agent.name ? { label: agent.label ?? agent.name } : {}),
|
|
292
|
-
...(agent.role ? { role: agent.role } : {}),
|
|
293
290
|
structuralPath: [...(agent.structuralPath ?? [])],
|
|
294
291
|
} satisfies WorkflowSiblingAgent));
|
|
295
292
|
const completedSiblingPaths = completedSiblingAgents.map((agent) => [...agent.structuralPath]);
|
|
@@ -315,9 +312,9 @@ export async function createWorkflowFailureDiagnostics(store: RunStore, metadata
|
|
|
315
312
|
}
|
|
316
313
|
|
|
317
314
|
export function formatWorkflowFailureDiagnostics(diagnostic: WorkflowFailureDiagnostics): string {
|
|
318
|
-
const failedAgent = diagnostic.failedAgent ? `${diagnostic.failedAgent.label ?? diagnostic.failedAgent.id}
|
|
315
|
+
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)";
|
|
319
316
|
const siblingAgents = diagnostic.completedSiblingAgents;
|
|
320
|
-
const siblings = siblingAgents ? siblingAgents.map((agent) => `${agent.label ?? agent.id}
|
|
317
|
+
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)";
|
|
321
318
|
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}`] : [];
|
|
322
319
|
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");
|
|
323
320
|
}
|
package/src/host-navigator.ts
CHANGED
|
@@ -700,9 +700,10 @@ export function registerWorkflowNavigator(deps: WorkflowNavigatorDependencies):
|
|
|
700
700
|
const styles = themeWorkflowProgressStyles(theme);
|
|
701
701
|
const agent = selectedAgentRecord();
|
|
702
702
|
const actions = actionMode ? { title: agent ? "Agent actions" : "Run actions", options: actionOptions(), index: actionIndex } : undefined;
|
|
703
|
-
const
|
|
703
|
+
const formatPhaseLines = (scrollTop: number): string[] => 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());
|
|
704
|
+
const phaseLines = formatPhaseLines(0);
|
|
704
705
|
const statusLines = stopStatus ? truncateToVisualLines(styles.error(stopStatus), Number.MAX_SAFE_INTEGER, width, 0).visualLines.map((line) => line.trimEnd()) : [];
|
|
705
|
-
|
|
706
|
+
let content = [...statusLines, ...phaseLines];
|
|
706
707
|
const rows = terminalRows();
|
|
707
708
|
const hintRows = rows >= 3 ? 1 : 0;
|
|
708
709
|
const confirmRows = inlineConfirm.rows(width, styles);
|
|
@@ -725,6 +726,7 @@ export function registerWorkflowNavigator(deps: WorkflowNavigatorDependencies):
|
|
|
725
726
|
selectionNeedsScroll = false;
|
|
726
727
|
}
|
|
727
728
|
dashboardOffset = Math.max(0, Math.min(maxOffset, dashboardOffset));
|
|
729
|
+
if (!narrow && !actionMode) content = [...statusLines, ...formatPhaseLines(Math.max(0, dashboardOffset - statusLines.length))];
|
|
728
730
|
const selectedNode = selectedNodeId ? tree.byId.get(selectedNodeId) : undefined;
|
|
729
731
|
const enterAction = selectedNode?.kind === "workflow" ? "run actions" : selectedNode?.kind === "agent" ? "agent actions" : selectedNode?.children.length ? "expand/collapse" : narrow ? "inspect" : "focus details";
|
|
730
732
|
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] ?? "";
|
package/src/host-phases.ts
CHANGED
|
@@ -90,7 +90,7 @@ export function buildWorkflowPhaseModel(run: WorkflowPhaseRun, source?: Workflow
|
|
|
90
90
|
if (unassignedAgents.length) result.unassignedAgents = unassignedAgents;
|
|
91
91
|
return result;
|
|
92
92
|
}
|
|
93
|
-
export interface WorkflowPhaseSelection { phaseId?: string | undefined; agentId?: string | undefined; nodeId?: string | undefined; expandedNodeIds?: readonly string[] | undefined; treeOnly?: boolean | undefined; detailsOnly?: boolean | undefined; actions?: { title: string; options: readonly string[]; index: number } | undefined }
|
|
93
|
+
export interface WorkflowPhaseSelection { phaseId?: string | undefined; agentId?: string | undefined; nodeId?: string | undefined; expandedNodeIds?: readonly string[] | undefined; treeOnly?: boolean | undefined; detailsOnly?: boolean | undefined; scrollTop?: number | undefined; actions?: { title: string; options: readonly string[]; index: number } | undefined }
|
|
94
94
|
export type WorkflowPhaseTreeNodeKind = "workflow" | "phase" | "operation" | "agent" | "shell";
|
|
95
95
|
export interface WorkflowPhaseTreeNode { id: string; kind: WorkflowPhaseTreeNodeKind; label: string; depth: number; phaseId: string; operationPath: readonly string[]; parentId?: string; children: readonly string[]; state: WorkflowPhaseState | RunState | AgentRecord["state"]; agentId?: string; agent?: AgentRecord; phase?: WorkflowPhaseView; shellActivity?: WorkflowPhaseShellActivity }
|
|
96
96
|
export interface WorkflowPhaseTree { roots: readonly string[]; nodes: readonly WorkflowPhaseTreeNode[]; byId: ReadonlyMap<string, WorkflowPhaseTreeNode> }
|