@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/types.ts
CHANGED
|
@@ -27,29 +27,30 @@ export const WORKFLOW_BUDGET_EVENT = "workflow:budget-event";
|
|
|
27
27
|
export const WORKFLOW_WORKTREE_CREATED_EVENT = "workflow:worktree-created";
|
|
28
28
|
export const WORKFLOW_BLOCKED_EVENT = "workflow:blocked";
|
|
29
29
|
export const ERROR_CODES = [
|
|
30
|
-
"CONFIG_ERROR", "INVALID_SETTINGS", "INVALID_SYNTAX", "INVALID_METADATA", "DUPLICATE_NAME", "INVALID_SCHEMA", "UNKNOWN_MODEL", "UNKNOWN_TOOL", "
|
|
30
|
+
"CONFIG_ERROR", "INVALID_SETTINGS", "INVALID_SYNTAX", "INVALID_METADATA", "DUPLICATE_NAME", "INVALID_SCHEMA", "UNKNOWN_MODEL", "UNKNOWN_TOOL", "UNKNOWN_AGENT",
|
|
31
31
|
"RUN_OWNED", "RUN_NOT_FOUND", "REGISTRY_FROZEN", "GLOBAL_COLLISION", "MISSING_WORKFLOW", "RPC_LIMIT_EXCEEDED", "SHELL_FAILED", "AGENT_TIMEOUT", "AGENT_FAILED", "AGENT_RESULT_COLLECTED", "RESULT_INVALID",
|
|
32
|
-
"CANCELLED", "WORKER_UNRESPONSIVE", "WORKTREE_FAILED", "RESUME_INCOMPATIBLE", "BUDGET_EXHAUSTED", "INTERNAL_ERROR",
|
|
32
|
+
"TOOL_FAILED", "CANCELLED", "WORKER_UNRESPONSIVE", "WORKTREE_FAILED", "RESUME_INCOMPATIBLE", "BUDGET_EXHAUSTED", "INTERNAL_ERROR",
|
|
33
33
|
] as const;
|
|
34
34
|
|
|
35
35
|
export const THINKING_LEVELS = ["off", "minimal", "low", "medium", "high", "xhigh", "max"] as const;
|
|
36
36
|
export type ThinkingLevel = (typeof THINKING_LEVELS)[number];
|
|
37
|
+
export const CONTEXT_FILE_SCOPES = ["global", "project", "cwd"] as const;
|
|
38
|
+
export type ContextFileScope = (typeof CONTEXT_FILE_SCOPES)[number];
|
|
39
|
+
export function isContextFileScope(value: unknown): value is ContextFileScope { return CONTEXT_FILE_SCOPES.some((scope) => scope === value); }
|
|
37
40
|
export type WorkflowErrorCode = (typeof ERROR_CODES)[number];
|
|
38
41
|
export type JsonValue = null | boolean | number | string | JsonValue[] | { [key: string]: JsonValue };
|
|
39
42
|
export type JsonSchema = { [key: string]: JsonValue };
|
|
40
43
|
type WorkflowSchema = JsonSchema | TSchema;
|
|
41
|
-
export function roleNameOf(value: unknown): string | undefined {
|
|
42
|
-
return typeof value === "string" ? value : undefined;
|
|
43
|
-
}
|
|
44
44
|
// Extra keys are reserved for extensions and forwarded as JSON to agentOptions; core options remain typed.
|
|
45
45
|
export interface AgentOptions<Schema extends TSchema = never> {
|
|
46
46
|
label?: string;
|
|
47
47
|
model?: string;
|
|
48
48
|
tools?: string[];
|
|
49
|
+
/** Exact tool names removed after preparation hooks; never widens the parent ceiling. */
|
|
50
|
+
excludeTools?: string[];
|
|
49
51
|
skills?: string[];
|
|
50
52
|
extensions?: string[];
|
|
51
53
|
contextFiles?: ContextFileScope[];
|
|
52
|
-
role?: string;
|
|
53
54
|
outputSchema?: JsonSchema | Schema;
|
|
54
55
|
retries?: number;
|
|
55
56
|
timeoutMs?: number | null;
|
|
@@ -80,7 +81,7 @@ export type WorkflowRunResumedEvent = WorkflowEventBase;
|
|
|
80
81
|
export interface WorkflowRunStateChangedEvent extends WorkflowEventBase { previousState: RunState; state: RunState; reason?: string; errorCode?: WorkflowErrorCode }
|
|
81
82
|
export interface WorkflowRunCompletedEvent extends WorkflowEventBase { resultPath: string }
|
|
82
83
|
export interface WorkflowRunFailedEvent extends WorkflowEventBase { error: WorkflowErrorShape }
|
|
83
|
-
export interface WorkflowAgentStateChangedEvent extends WorkflowEventBase { agentId: string; displayLabel: string;
|
|
84
|
+
export interface WorkflowAgentStateChangedEvent extends WorkflowEventBase { agentId: string; displayLabel: string; structuralPath: readonly string[]; parentId?: string; parentBreadcrumb?: string; worktreeOwner?: string; previousState?: AgentState; state: AgentState; attempt: number }
|
|
84
85
|
export interface WorkflowPhaseChangedEvent extends WorkflowEventBase { previousPhase?: string; phase: string }
|
|
85
86
|
export type WorkflowCheckpointState = "awaiting" | "approved" | "rejected";
|
|
86
87
|
export interface WorkflowCheckpointStateChangedEvent extends WorkflowEventBase { name: string; state: WorkflowCheckpointState }
|
|
@@ -92,22 +93,21 @@ export interface WorkflowMetadata { name: string; description?: string }
|
|
|
92
93
|
export interface HerdrExtensionSettings { enableFullyInspectableMode?: boolean }
|
|
93
94
|
export interface TrajectoryExtensionSettings { port?: number }
|
|
94
95
|
export interface WorkflowExtensionSettings { readonly [namespace: string]: JsonValue; readonly herdr?: Readonly<HerdrExtensionSettings>; readonly trajectory?: Readonly<TrajectoryExtensionSettings> }
|
|
95
|
-
export type WorkflowExtensionSettingsSource = "global" | "project" | "
|
|
96
|
-
export interface WorkflowExtensionSettingsValidatorContext { source: WorkflowExtensionSettingsSource; cwd: string; projectTrusted: boolean; settingsPath?: string
|
|
96
|
+
export type WorkflowExtensionSettingsSource = "global" | "project" | "effective";
|
|
97
|
+
export interface WorkflowExtensionSettingsValidatorContext { source: WorkflowExtensionSettingsSource; cwd: string; projectTrusted: boolean; settingsPath?: string }
|
|
97
98
|
export type WorkflowExtensionSettingsValidator = (settings: Readonly<WorkflowExtensionSettings>, context: Readonly<WorkflowExtensionSettingsValidatorContext>) => void;
|
|
98
99
|
export interface WorkflowSessionStartEvent { type: "session_start"; reason: "startup" | "reload" | "new" | "resume" | "fork"; previousSessionFile?: string; settings: Readonly<WorkflowExtensionSettings> }
|
|
99
100
|
export interface WorkflowRetentionSettings { olderThanDays?: number; maxTerminalRuns?: number }
|
|
100
|
-
export
|
|
101
|
-
export
|
|
101
|
+
export type CodemodeToolsSetting = "all" | "read-only" | "none";
|
|
102
|
+
export interface WorkflowSettings { concurrency: number; backgroundWidget?: boolean; codemodeTools?: CodemodeToolsSetting; modelAliases?: Readonly<Record<string, string>>; skills?: readonly string[]; extensions?: readonly string[]; extensionSettings?: Readonly<WorkflowExtensionSettings>; tools?: readonly string[]; retention?: Readonly<WorkflowRetentionSettings>; worktreePostCreateCommand?: readonly string[] }
|
|
103
|
+
export type WorkflowSettingsOverrides = Partial<Omit<WorkflowSettings, "backgroundWidget" | "codemodeTools">>;
|
|
102
104
|
export interface WorkflowSettingsSources { concurrency: string; modelAliases: string; skills?: string; extensions?: string; tools?: string; extensionSettings?: string; retention?: string; worktreePostCreateCommand?: string }
|
|
103
|
-
export interface WorkflowSettingsResolution { globalSettingsPath: string; projectSettingsPath: string; projectTrusted: boolean; global: Readonly<WorkflowSettings>; project: Readonly<WorkflowSettingsOverrides>; effective: Readonly<WorkflowSettings>; sources: Readonly<WorkflowSettingsSources> }
|
|
105
|
+
export interface WorkflowSettingsResolution { selectorSources?: AgentResourceSelectorSources; globalSettingsPath: string; projectSettingsPath: string; projectTrusted: boolean; global: Readonly<WorkflowSettings>; project: Readonly<WorkflowSettingsOverrides>; effective: Readonly<WorkflowSettings>; sources: Readonly<WorkflowSettingsSources> }
|
|
104
106
|
export interface AgentResourceSelectors { skills?: readonly string[]; extensions?: readonly string[]; tools?: readonly string[] }
|
|
105
107
|
export interface AgentResourceSelectorSet { skills: readonly string[]; extensions: readonly string[]; tools?: readonly string[] }
|
|
106
|
-
export interface AgentResourceSelectorSources { global: AgentResourceSelectors; project: AgentResourceSelectors;
|
|
107
|
-
export const CONTEXT_FILE_SCOPES = ["global", "project", "cwd"] as const;
|
|
108
|
-
export type ContextFileScope = (typeof CONTEXT_FILE_SCOPES)[number];
|
|
109
|
-
export function isContextFileScope(value: unknown): value is ContextFileScope { return CONTEXT_FILE_SCOPES.some((scope) => scope === value); }
|
|
108
|
+
export interface AgentResourceSelectorSources { global: AgentResourceSelectors; project: AgentResourceSelectors; call?: AgentResourceSelectors }
|
|
110
109
|
export interface AgentResourcePolicy {
|
|
110
|
+
capabilities?: Readonly<{ tools: readonly string[]; skills: readonly string[]; extensions: readonly string[] }>;
|
|
111
111
|
globalSettingsPath: string;
|
|
112
112
|
projectSettingsPath: string;
|
|
113
113
|
projectTrusted: boolean;
|
|
@@ -121,6 +121,8 @@ export interface AgentResourcePolicy {
|
|
|
121
121
|
unmatchedExtensions: readonly string[];
|
|
122
122
|
unmatchedTools?: readonly string[];
|
|
123
123
|
selectorSources: AgentResourceSelectorSources;
|
|
124
|
+
/** Skills loaded by the parent session; ceiling for skills that extensions contribute through resources_discover. Absent for top-level agents. */
|
|
125
|
+
parentSkills?: readonly string[];
|
|
124
126
|
}
|
|
125
127
|
export interface AgentResourceInspection { selectors: AgentResourceSelectorSet; skills: readonly string[]; extensions: readonly string[]; tools: readonly string[]; unmatchedSkills: readonly string[]; unmatchedExtensions: readonly string[]; unmatchedTools: readonly string[]; selectorSources?: AgentResourceSelectorSources }
|
|
126
128
|
|
|
@@ -157,7 +159,6 @@ export interface AgentRecord {
|
|
|
157
159
|
handle?: string;
|
|
158
160
|
turn?: number;
|
|
159
161
|
continuity?: AgentContinuity;
|
|
160
|
-
role?: string;
|
|
161
162
|
requestedModel?: string;
|
|
162
163
|
model: ModelSpec;
|
|
163
164
|
tools: readonly string[];
|
|
@@ -178,6 +179,8 @@ export interface WorkflowRunEvent { type: string; message: string; timestamp?: n
|
|
|
178
179
|
export interface WorkflowRetryProvenance { sourceRunId: string; lineageRootRunId: string; completedPaths: readonly string[]; incompletePaths: readonly string[]; namedWorktrees: readonly string[] }
|
|
179
180
|
export interface WorkflowPhaseRecord { phase: string; afterAgent: number }
|
|
180
181
|
export interface WorkflowPhaseShellActivity { phaseIndex: number; active: number; startedAt: number }
|
|
182
|
+
/** A script call recorded for inspection. Tool payloads stay in the replay journal, not this record. Oversized shell inputs are JSON prefixes, with `inputBytes` their full size. */
|
|
183
|
+
export interface WorkflowScriptCall { kind: "shell" | "tool"; name: string; path: string; input?: JsonValue; inputBytes?: number; phase?: string; startedAt: number; finishedAt?: number; error?: { code: WorkflowErrorCode; message?: string } }
|
|
181
184
|
export interface RunRecord {
|
|
182
185
|
id: string;
|
|
183
186
|
workflowName: string;
|
|
@@ -194,6 +197,8 @@ export interface RunRecord {
|
|
|
194
197
|
activeShells?: number;
|
|
195
198
|
activeShellStartedAt?: number;
|
|
196
199
|
activeShellsByPhase?: readonly WorkflowPhaseShellActivity[];
|
|
200
|
+
activeTools?: readonly { name: string; startedAt: number }[]; // Script tool calls in flight: a live overlay for rendering, never persisted.
|
|
201
|
+
scriptCalls?: readonly WorkflowScriptCall[];
|
|
197
202
|
error?: WorkflowErrorShape;
|
|
198
203
|
failedAt?: string;
|
|
199
204
|
budget?: WorkflowBudget;
|
|
@@ -203,11 +208,10 @@ export interface RunRecord {
|
|
|
203
208
|
events?: readonly WorkflowRunEvent[];
|
|
204
209
|
delivery?: WorkflowRunDelivery;
|
|
205
210
|
}
|
|
206
|
-
export const LAUNCH_SNAPSHOT_IDENTITY_VERSION =
|
|
211
|
+
export const LAUNCH_SNAPSHOT_IDENTITY_VERSION = 6;
|
|
207
212
|
export type WorkflowLaunchMode = "foreground" | "background";
|
|
208
|
-
export interface AgentDefinition { prompt?: string; description?: string; model?: string; thinking?: NonNullable<ModelSpec["thinking"]>; tools?: readonly string[]; skills?: readonly string[]; extensions?: readonly string[]; overrideSystemPrompt?: boolean; contextFiles?: readonly ContextFileScope[]; extensionSettings?: Readonly<WorkflowExtensionSettings> }
|
|
209
213
|
export interface LaunchSnapshot {
|
|
210
|
-
identityVersion
|
|
214
|
+
identityVersion: number;
|
|
211
215
|
launchMode?: WorkflowLaunchMode;
|
|
212
216
|
script: string;
|
|
213
217
|
args: JsonValue;
|
|
@@ -220,14 +224,18 @@ export interface LaunchSnapshot {
|
|
|
220
224
|
phases?: readonly string[];
|
|
221
225
|
models: readonly string[];
|
|
222
226
|
tools: readonly string[];
|
|
223
|
-
|
|
224
|
-
roles?: Readonly<Record<string, AgentDefinition>>;
|
|
225
|
-
projectRoles?: readonly string[];
|
|
227
|
+
agentConfigurations: Readonly<Record<string, PreparedAgentConfiguration>>;
|
|
226
228
|
schemas: readonly JsonSchema[];
|
|
227
229
|
}
|
|
228
|
-
export interface PreflightCapabilities { models: ReadonlySet<string>; tools: ReadonlySet<string>;
|
|
229
|
-
export interface PreflightResult { metadata: WorkflowMetadata; referenced: { phases: readonly string[]; models: readonly string[]; tools: readonly string[];
|
|
230
|
-
export interface
|
|
230
|
+
export interface PreflightCapabilities { models: ReadonlySet<string>; tools: ReadonlySet<string>; modelAliases?: Readonly<Record<string, string>>; knownModels?: ReadonlySet<string>; settingsPath?: string; skipModelAvailability?: boolean }
|
|
231
|
+
export interface PreflightResult { metadata: WorkflowMetadata; referenced: { phases: readonly string[]; models: readonly string[]; tools: readonly string[]; }; schemas: readonly JsonSchema[] }
|
|
232
|
+
export interface AgentHandleSendOptions<Schema extends TSchema = never> { outputSchema?: JsonSchema | Schema; timeoutMs?: number | null }
|
|
233
|
+
/** A persistent agent: each send continues the transcript of the previous completed turn. Sends must be awaited one at a time, in the scope that created the handle. */
|
|
234
|
+
export interface AgentHandle {
|
|
235
|
+
readonly name: string;
|
|
236
|
+
send: { <Schema extends TSchema>(prompt: string, options: Readonly<AgentHandleSendOptions<Schema> & { outputSchema: Schema }>): Promise<Static<Schema>>; (prompt: string, options?: Readonly<AgentHandleSendOptions>): Promise<JsonValue> };
|
|
237
|
+
}
|
|
238
|
+
export interface WorkflowOrchestrationContext { agent: { <Schema extends TSchema>(prompt: string, options: Readonly<AgentOptions<Schema> & { outputSchema: Schema }>): Promise<Static<Schema>>; (prompt: string, options?: Readonly<AgentOptions>): Promise<JsonValue>; create: (options: Readonly<AgentOptions & { name: string }>) => AgentHandle }; shell: (command: string, options?: ShellOptions) => Promise<ShellResult>; prompt: (template: string, values: Readonly<Record<string, JsonValue>>) => string; parallel: <Tasks extends ParallelTasks>(operationName: string, tasks: Tasks) => Promise<ParallelResult<Tasks>>; pipeline: <Items extends PipelineItems, Output extends JsonValue>(operationName: string, items: Items, stages: PipelineStages<Items[keyof Items], Output>) => Promise<PipelineResult<Items, Output>>; withWorktree: <Result extends JsonValue>(name: string, callback: WorkflowWorktreeCallback<Result>) => Promise<Result>; checkpoint: (input: CheckpointInput) => Promise<boolean>; phase: (name: string) => void; log: (message: string) => void }
|
|
231
239
|
export interface WorkflowRunContext { cwd: string; sessionId: string; runId: string; workflow: Readonly<WorkflowMetadata>; args: JsonValue; signal: AbortSignal }
|
|
232
240
|
export interface WorkflowFunctionContext extends WorkflowOrchestrationContext { run: Readonly<WorkflowRunContext>; settings: Readonly<WorkflowExtensionSettings>; invoke: (name: string, input: Readonly<Record<string, JsonValue>>, label?: string) => Promise<JsonValue> }
|
|
233
241
|
export type WorkflowWorktreeCallback<Result extends JsonValue = JsonValue> = (reference: Readonly<WorkflowWorktreeReference>) => Result | Promise<Result>;
|
|
@@ -262,6 +270,8 @@ export interface WorkflowAgentSession {
|
|
|
262
270
|
getState(): Readonly<WorkflowAgentSessionState>;
|
|
263
271
|
getSessionStats(): WorkflowAgentSessionStats;
|
|
264
272
|
getLastAssistant(): WorkflowAgentMessage | undefined;
|
|
273
|
+
/** Set by a transport that already delivered `PreparedAgentSession.turnControl` itself; otherwise the runner steers it before the first prompt. */
|
|
274
|
+
readonly turnControlDelivered?: boolean;
|
|
265
275
|
subscribe(listener: (event: WorkflowAgentSessionEvent) => void): () => void;
|
|
266
276
|
subscribeAsync?(listener: (event: WorkflowAgentSessionEvent) => void | Promise<void>): () => void;
|
|
267
277
|
prompt(text: string): Promise<WorkflowAgentTurnResult>;
|
|
@@ -275,7 +285,8 @@ export interface WorkflowAgentSession {
|
|
|
275
285
|
type SessionTools = NonNullable<CreateAgentSessionOptions["tools"]>;
|
|
276
286
|
type SessionCustomTools = NonNullable<CreateAgentSessionOptions["customTools"]>;
|
|
277
287
|
export interface SessionInput {
|
|
278
|
-
|
|
288
|
+
/** Read-only: setup hooks cannot move an agent, because Pi derives trust, settings and `.pi` resources from cwd. */
|
|
289
|
+
readonly cwd: string;
|
|
279
290
|
model: ModelSpec;
|
|
280
291
|
tools: SessionTools;
|
|
281
292
|
sessionLabel: string;
|
|
@@ -304,6 +315,8 @@ export interface PreparedAgentSession {
|
|
|
304
315
|
readonly sessionLabel: string;
|
|
305
316
|
readonly sessionPath?: string;
|
|
306
317
|
readonly initialPrompt?: string;
|
|
318
|
+
/** Dynamic per-attempt control text (retry state, budget). The runner steers it before the first prompt unless the session reports `turnControlDelivered`. */
|
|
319
|
+
readonly turnControl?: string;
|
|
307
320
|
readonly agentDir?: string;
|
|
308
321
|
readonly customTools?: readonly ToolDefinition[];
|
|
309
322
|
readonly resultTool?: ToolDefinition;
|
|
@@ -325,17 +338,50 @@ export interface AgentTransport { readonly id: string; createSession(prepared: R
|
|
|
325
338
|
export interface AgentSetup { prompt: string; options: AgentOptions; sessionInput: SessionInput; prepared: Readonly<PreparedAgentSession>; transport: AgentTransport }
|
|
326
339
|
export interface AgentSetupContext { readonly run: Readonly<WorkflowRunContext>; readonly identity: Readonly<AgentIdentity>; readonly attempt: number; readonly signal: AbortSignal; readonly tuiIndex?: number; readonly tuiLabel?: string; readonly mode?: AgentInspectionMode; readonly settings: Readonly<WorkflowExtensionSettings> }
|
|
327
340
|
|
|
341
|
+
export interface AgentPreparation {
|
|
342
|
+
label?: string;
|
|
343
|
+
model?: string;
|
|
344
|
+
tools: string[];
|
|
345
|
+
skills: string[];
|
|
346
|
+
extensions: string[];
|
|
347
|
+
contextFiles?: ContextFileScope[];
|
|
348
|
+
systemPrompt?: string;
|
|
349
|
+
systemPromptAppend: string;
|
|
350
|
+
settings: WorkflowExtensionSettings;
|
|
351
|
+
}
|
|
352
|
+
export interface AgentPreparationContext {
|
|
353
|
+
readonly options: Readonly<AgentOptions>;
|
|
354
|
+
readonly cwd: string;
|
|
355
|
+
/** Launch project root that owns project configuration; differs from `cwd` inside worktree scopes. Optional for older hosts. */
|
|
356
|
+
readonly projectCwd?: string;
|
|
357
|
+
readonly agentDir: string;
|
|
358
|
+
readonly projectTrusted: boolean;
|
|
359
|
+
/** `dynamicModelAliasNames` lists the `modelAliases` entries resolved by extensions rather than read from static settings. */
|
|
360
|
+
readonly defaults: Readonly<{ model: ModelSpec; modelAliases: Readonly<Record<string, string>>; dynamicModelAliasNames?: readonly string[]; selectorSources: AgentResourceSelectorSources; settings: Readonly<WorkflowExtensionSettings> }>;
|
|
361
|
+
readonly capabilities: Readonly<{ tools: readonly string[]; skills: readonly string[]; extensions: readonly string[] }>;
|
|
362
|
+
readonly knownModels: ReadonlySet<string>;
|
|
363
|
+
readonly availableModels: ReadonlySet<string>;
|
|
364
|
+
readonly signal: AbortSignal;
|
|
365
|
+
readonly mode: AgentInspectionMode;
|
|
366
|
+
}
|
|
367
|
+
export interface AgentPreparationHook { priority?: number; optionsSchema?: JsonSchema; prepare: (configuration: AgentPreparation, context: Readonly<AgentPreparationContext>) => void | Promise<void> }
|
|
368
|
+
/** `source` is the registering extension's module provenance, when it declared one. */
|
|
369
|
+
export interface RegisteredAgentPreparationHook { name: string; priority: number; optionsSchema?: JsonSchema; source?: string; prepare: AgentPreparationHook["prepare"] }
|
|
370
|
+
/**
|
|
371
|
+
* `excludeTools` holds the exact tool names the agent and its ancestors excluded, frozen with the configuration so setup and
|
|
372
|
+
* children apply them even when re-evaluated options change. It is absent only in configurations prepared before it existed, which cannot resume.
|
|
373
|
+
*/
|
|
374
|
+
export interface PreparedAgentConfiguration extends Omit<AgentPreparation, "model"> { model: ModelSpec; projectTrusted: boolean; excludeTools?: string[] }
|
|
328
375
|
export interface AgentSetupHook { priority?: number; setup: (agent: AgentSetup, context: Readonly<AgentSetupContext>) => void | Promise<void> }
|
|
329
376
|
export interface RegisteredAgentSetupHook { name: string; priority: number; setup: AgentSetupHook["setup"] }
|
|
330
377
|
export interface WorkflowExtensionMetadata { version: string; headline: string }
|
|
331
378
|
export interface WorkflowFunctionSource { readonly module: string; readonly export: string; readonly dependencies: readonly string[] }
|
|
332
|
-
export interface WorkflowRoleDirectoryRegistration { path: string; extension: WorkflowExtensionMetadata; readonly builtin?: true }
|
|
333
379
|
export interface AgentAttemptActionUi { notify(message: string, level?: "info" | "warning" | "error"): void; confirm(title: string, message: string): Promise<boolean>; select(title: string, options: readonly string[]): Promise<string | undefined>; input(title: string, placeholder?: string): Promise<string | undefined>; setWorkingMessage?(message?: string): void }
|
|
334
380
|
export interface StandaloneAgentRecord { readonly id: string; readonly name: string; readonly label?: string; readonly state: "running" | "completed" | "failed" | "stopped"; readonly structuralPath?: readonly string[] }
|
|
335
381
|
export interface StandaloneAgentAttemptActionContext { readonly agent: Readonly<StandaloneAgentRecord>; readonly attempt: Readonly<AgentAttemptSummary>; readonly session?: WorkflowAgentSessionReference; readonly liveSession?: WorkflowAgentSession; readonly prepared?: Readonly<PreparedAgentSession>; readonly handoff?: LiveSessionHandoff; readonly signal: AbortSignal; readonly ui: Readonly<AgentAttemptActionUi> }
|
|
336
382
|
export interface AgentAttemptActionContext { readonly run: Readonly<RunRecord>; readonly agent: Readonly<AgentRecord>; readonly attempt: Readonly<AgentAttemptSummary>; readonly session?: WorkflowAgentSessionReference; readonly liveSession?: WorkflowAgentSession; readonly prepared?: Readonly<PreparedAgentSession>; readonly handoff?: LiveSessionHandoff; readonly signal: AbortSignal; readonly ui: Readonly<AgentAttemptActionUi> }
|
|
337
383
|
export interface AgentAttemptAction { readonly label: string; visible(context: Readonly<AgentAttemptActionContext>): boolean; run(context: Readonly<AgentAttemptActionContext>): void | Promise<void>; visibleStandalone?(context: Readonly<StandaloneAgentAttemptActionContext>): boolean; runStandalone?(context: Readonly<StandaloneAgentAttemptActionContext>): void | Promise<void> }
|
|
338
|
-
export interface WorkflowExtension extends WorkflowExtensionMetadata { description?: string; source?: string; dependencies?: readonly string[]; validateSettings?: WorkflowExtensionSettingsValidator; functions?: Readonly<Record<string, WorkflowFunction>>; modelAliases?: Readonly<Record<string, WorkflowModelAlias>>;
|
|
384
|
+
export interface WorkflowExtension extends WorkflowExtensionMetadata { description?: string; source?: string; dependencies?: readonly string[]; validateSettings?: WorkflowExtensionSettingsValidator; functions?: Readonly<Record<string, WorkflowFunction>>; modelAliases?: Readonly<Record<string, WorkflowModelAlias>>; agentPreparationHooks?: Readonly<Record<string, AgentPreparationHook>>; agentSetupHooks?: Readonly<Record<string, AgentSetupHook>>; agentAttemptActions?: Readonly<Record<string, AgentAttemptAction>> }
|
|
339
385
|
export interface WorkflowJournal { get(path: string): JsonValue | undefined; put(path: string, value: JsonValue): void }
|
|
340
386
|
// The brand keeps instanceof working across the bundled extension entries, which each inline their own copy of this class.
|
|
341
387
|
const WORKFLOW_ERROR_BRAND = Symbol.for("pi-extensible-workflows.workflow-error");
|
|
@@ -348,19 +394,21 @@ export class WorkflowError extends Error {
|
|
|
348
394
|
}
|
|
349
395
|
}
|
|
350
396
|
Object.defineProperty(WorkflowError, WORKFLOW_ERROR_BRAND, { value: true });
|
|
351
|
-
export interface WorkflowFailureAgent { id: string; label?: string;
|
|
352
|
-
export interface WorkflowSiblingAgent { id: string; label?: string;
|
|
397
|
+
export interface WorkflowFailureAgent { id: string; label?: string; structuralPath: readonly string[]; attempt: number; transport?: string; session?: WorkflowAgentSessionReference }
|
|
398
|
+
export interface WorkflowSiblingAgent { id: string; label?: string; structuralPath: readonly string[] }
|
|
353
399
|
export interface WorkflowFailureDiagnostics { runId: string; workflowName: string; state: RunState; failedAt: string | null; error: WorkflowErrorShape; failedAgent?: WorkflowFailureAgent; completedSiblingAgents?: readonly WorkflowSiblingAgent[]; completedSiblingPaths: readonly (readonly string[])[]; retry?: { sourceRunId: string; action: string; completedPaths: readonly string[]; incompletePaths: readonly string[]; namedWorktrees: readonly string[]; warning: string }; artifacts: { runDirectory: string; statePath: string; journalPath: string } }
|
|
354
400
|
export interface CheckpointInput { name: string; prompt: string; context: JsonValue }
|
|
355
401
|
export interface FunctionIdentity { path: string; structuralPath: readonly string[]; occurrence: number; worktreeOwner?: string }
|
|
356
402
|
export type AgentContinuity = "fresh" | "continued";
|
|
357
403
|
export interface AgentIdentity { structuralPath: readonly string[]; callSite: string; occurrence: number; parentBreadcrumb?: string; worktreeOwner?: string; handle?: string; turn?: number }
|
|
358
404
|
export interface ShellIdentity { structuralPath: readonly string[]; callSite: string; occurrence: number; worktreeOwner?: string }
|
|
359
|
-
|
|
405
|
+
/** A script `tools.<id>(args)` call: the n-th call of that tool within one structural scope. */
|
|
406
|
+
export interface ToolIdentity { structuralPath: readonly string[]; occurrence: number }
|
|
407
|
+
export interface WorkflowBridge { agent?: (prompt: string, options: Readonly<Record<string, JsonValue>>, signal: AbortSignal, identity: AgentIdentity) => Promise<JsonValue>; shell?: (command: string, options: ShellOptions, signal: AbortSignal, identity: ShellIdentity) => Promise<ShellResult>; checkpoint?: (input: Readonly<Record<string, JsonValue>>, signal: AbortSignal) => boolean | Promise<boolean>; function?: (name: string, input: Readonly<Record<string, JsonValue>>, signal: AbortSignal, identity: FunctionIdentity) => Promise<JsonValue>; worktree?: (owner: string, signal: AbortSignal) => Promise<Readonly<WorkflowWorktreeReference>>; functions?: Readonly<Record<string, { name: string }>>; phase?: (name: string) => void | Promise<void>; log?: (message: string) => void | Promise<void>; tool?: (identifier: string, args: Readonly<Record<string, JsonValue>>, signal: AbortSignal, identity: ToolIdentity) => Promise<JsonValue> }
|
|
360
408
|
export interface WorkflowExecution { result: Promise<JsonValue>; cancel: () => void }
|
|
361
409
|
export interface StaticWorkflowScope { kind: "parallel" | "pipeline"; name: string | null; key: string | null }
|
|
362
410
|
export type StaticWorkflowExecution = "parallel" | "sequential";
|
|
363
|
-
export interface StaticWorkflowCall { kind: WorkflowCallKind; start: number; end: number; name: string | null; prompt: string | null; model: string | null; label?: string | null;
|
|
411
|
+
export interface StaticWorkflowCall { kind: WorkflowCallKind; start: number; end: number; name: string | null; prompt: string | null; model: string | null; label?: string | null; retries?: number | null; outputSchema?: JsonSchema | null; options?: Readonly<Record<string, JsonValue>> | null; optionKeys?: readonly string[]; execution?: StaticWorkflowExecution; structure?: readonly StaticWorkflowScope[] }
|
|
364
412
|
export interface WorkflowCatalogFunction { name: string; version: string; headline: string; description: string; input: JsonSchema; output: JsonSchema }
|
|
365
413
|
export interface WorkflowCatalogModelAlias { name: string; kind: "static" | "dynamic"; provenance: string; version?: string; headline?: string }
|
|
366
414
|
export interface WorkflowCatalogSettings { concurrency: number; backgroundWidget: boolean; modelAliases: Readonly<Record<string, string>>; skills: readonly string[]; extensions: readonly string[]; tools: readonly string[]; extensionSettings?: Readonly<WorkflowExtensionSettings>; worktreePostCreateCommand?: readonly string[]; globalSettingsPath: string; projectSettingsPath: string; projectTrusted: boolean; sources: WorkflowSettingsSources }
|
|
@@ -371,4 +419,4 @@ export interface WorkflowCatalogIndex { functions: readonly WorkflowCatalogIndex
|
|
|
371
419
|
export interface WorkflowCatalogError { error: { code: "NOT_FOUND"; name: string; message: string } }
|
|
372
420
|
export interface WorkflowValidationParameters { name: string; description?: string; script?: string; scriptPath?: string; args?: unknown }
|
|
373
421
|
export interface WorkflowValidationContext { cwd: string; projectTrusted: boolean; availableModels: ReadonlySet<string>; rootTools: ReadonlySet<string>; modelAliases?: Readonly<Record<string, string>>; knownModels?: ReadonlySet<string>; settingsPath?: string; agentDir?: string; extensionSettings?: Readonly<WorkflowExtensionSettings> }
|
|
374
|
-
export interface ValidatedWorkflowLaunch { script: string; checked: PreflightResult
|
|
422
|
+
export interface ValidatedWorkflowLaunch { script: string; checked: PreflightResult }
|
package/src/utils.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { ERROR_CODES, LAUNCH_SNAPSHOT_IDENTITY_VERSION, WorkflowError, type JsonSchema, type JsonValue, type ModelSpec, type WorkflowErrorCode, type WorkflowExtensionSettings } from "./types.js";
|
|
2
3
|
import { Minimatch } from "minimatch";
|
|
4
|
+
import { THINKING_LEVELS, type ThinkingLevel } from "./types.js";
|
|
3
5
|
export class SerialLane {
|
|
4
6
|
#tail: Promise<void> = Promise.resolve();
|
|
5
7
|
run<T>(task: () => Promise<T>): Promise<T> {
|
|
@@ -18,6 +20,15 @@ export function sanitizeDisplayText(value: string): string { return value.replac
|
|
|
18
20
|
export { object as isObject };
|
|
19
21
|
function isStringKey(key: PropertyKey): key is string { return typeof key === "string"; }
|
|
20
22
|
function stringKeyValue(value: object, key: string): unknown { return object(value) ? value[key] : undefined; }
|
|
23
|
+
function sortedJson(value: JsonValue): JsonValue {
|
|
24
|
+
if (Array.isArray(value)) return value.map(sortedJson);
|
|
25
|
+
if (value === null || typeof value !== "object") return value;
|
|
26
|
+
return Object.fromEntries(Object.keys(value).sort().map((key) => [key, sortedJson(value[key] as JsonValue)]));
|
|
27
|
+
}
|
|
28
|
+
/** A short digest of a JSON value that ignores object key order. */
|
|
29
|
+
export function jsonDigest(value: JsonValue): string {
|
|
30
|
+
return createHash("sha256").update(JSON.stringify(sortedJson(value))).digest("hex").slice(0, 16);
|
|
31
|
+
}
|
|
21
32
|
export function jsonValue(value: unknown, seen = new Set<object>()): value is JsonValue {
|
|
22
33
|
if (value === null || typeof value === "boolean" || typeof value === "string") return true;
|
|
23
34
|
if (typeof value === "number") return Number.isFinite(value);
|
|
@@ -82,11 +93,19 @@ export const WORKFLOW_EXTENSION_NAMESPACE = /^[A-Za-z][A-Za-z0-9_-]*$/;
|
|
|
82
93
|
export function validWorkflowExtensionNamespace(value: string): boolean { return WORKFLOW_EXTENSION_NAMESPACE.test(value) && value !== "__proto__" && value !== "constructor" && value !== "prototype"; }
|
|
83
94
|
export function parseThinking(value: unknown): ModelSpec["thinking"] | undefined { return isThinkingLevel(value) ? value : undefined; }
|
|
84
95
|
export function parseModelReference(value: string): ModelSpec {
|
|
85
|
-
const match = /^([^/:\s]+)\/([
|
|
96
|
+
const match = /^([^/:\s]+)\/([^\s]+)$/.exec(value);
|
|
86
97
|
if (!match?.[1] || !match[2]) fail("UNKNOWN_MODEL", `Invalid model spec: ${value}`);
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
98
|
+
// The model id is everything after the first slash, so namespaced ids such as
|
|
99
|
+
// `deepseek/deepseek-v4.1-flash:nitro` survive intact. Only a trailing segment
|
|
100
|
+
// that names a thinking level is one.
|
|
101
|
+
const reference = match[2];
|
|
102
|
+
const separator = reference.lastIndexOf(":");
|
|
103
|
+
const suffix = separator === -1 ? undefined : reference.slice(separator + 1);
|
|
104
|
+
if (suffix === "") fail("UNKNOWN_MODEL", `Invalid model spec: ${value}`);
|
|
105
|
+
const thinking = suffix !== undefined && isThinkingLevel(suffix) ? suffix : undefined;
|
|
106
|
+
const model = thinking === undefined ? reference : reference.slice(0, separator);
|
|
107
|
+
if (model === "") fail("UNKNOWN_MODEL", `Invalid model spec: ${value}`);
|
|
108
|
+
return { provider: match[1], model, ...(thinking === undefined ? {} : { thinking }) };
|
|
90
109
|
}
|
|
91
110
|
export function assertModelThinking(value: string, path = "model"): void {
|
|
92
111
|
if (value.includes("/") && parseModelReference(value).thinking === undefined) fail("INVALID_METADATA", `${path} must be provider/model:thinking`);
|
|
@@ -157,6 +176,24 @@ export function modelCapability(value: string | ModelSpec, aliases?: Readonly<Re
|
|
|
157
176
|
const parsed = typeof value === "string" ? resolveModelReference(value, aliases, knownModels, settingsPath) : value;
|
|
158
177
|
return `${parsed.provider}/${parsed.model}`;
|
|
159
178
|
}
|
|
179
|
+
type ToolSource = { getActiveTools(): string[]; getAllTools?(): readonly { name: string; exposure?: string }[] };
|
|
180
|
+
/**
|
|
181
|
+
* Tools a session can reach, and so the ceiling for its agents: the declared tools plus the ones
|
|
182
|
+
* only codemode scripts call or tool_search loads, such as MCP tools with the default exposure.
|
|
183
|
+
* Agent sessions allow tools by name, so leaving these out would make them unreachable there.
|
|
184
|
+
*/
|
|
185
|
+
export function reachableTools(pi: ToolSource): string[] {
|
|
186
|
+
const undeclared = pi.getAllTools?.().filter(({ exposure }) => exposure === "codemode" || exposure === "deferred").map(({ name }) => name) ?? [];
|
|
187
|
+
return [...new Set([...pi.getActiveTools(), ...undeclared])];
|
|
188
|
+
}
|
|
189
|
+
/** Provider of the virtual models that expose workflow model aliases in `/model`. */
|
|
190
|
+
export const VIRTUAL_MODEL_PROVIDER = "workflow";
|
|
191
|
+
/** Child sessions do not load the workflow extension, so they receive the alias target instead of its virtual model. */
|
|
192
|
+
export function physicalModel(spec: ModelSpec, aliases: Readonly<Record<string, string>> = {}, knownModels?: ReadonlySet<string>, settingsPath?: string): ModelSpec {
|
|
193
|
+
if (spec.provider !== VIRTUAL_MODEL_PROVIDER || !modelAliasName(spec.model, aliases)) return spec;
|
|
194
|
+
const target = resolveModelReference(spec.model, aliases, knownModels, settingsPath);
|
|
195
|
+
return spec.thinking === undefined ? target : { ...target, thinking: spec.thinking };
|
|
196
|
+
}
|
|
160
197
|
export function aliasDrift(previous: Readonly<Record<string, string>>, current: Readonly<Record<string, string>>): string[] {
|
|
161
198
|
return [...new Set([...Object.keys(previous), ...Object.keys(current)])].sort().flatMap((name) => previous[name] === current[name] ? [] : [`${name}: ${previous[name] ?? "(missing)"} -> ${current[name] ?? "(missing)"}`]);
|
|
162
199
|
}
|