pi-extensible-workflows 0.3.2 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,4 +1,4 @@
1
- import { type WorkflowScriptDefinition, type WorkflowSettings } from "./index.js";
1
+ import { type AgentResourcePolicy, type WorkflowScriptDefinition, type WorkflowSettings } from "./index.js";
2
2
  export type DoctorSeverity = "error" | "warning";
3
3
  export interface DoctorDiagnostic {
4
4
  severity: DoctorSeverity;
@@ -34,6 +34,8 @@ export interface DoctorPiState {
34
34
  path?: string;
35
35
  message: string;
36
36
  }[];
37
+ extensions?: readonly string[];
38
+ skills?: readonly string[];
37
39
  workflows: Readonly<Record<string, WorkflowScriptDefinition>>;
38
40
  }
39
41
  export interface DoctorReport {
@@ -45,6 +47,7 @@ export interface DoctorReport {
45
47
  activeTools: readonly string[];
46
48
  roles: readonly DoctorRole[];
47
49
  workflows: readonly DoctorWorkflow[];
50
+ resourcePolicy: AgentResourcePolicy;
48
51
  diagnostics: readonly DoctorDiagnostic[];
49
52
  }
50
53
  export interface DoctorOptions {
@@ -2,7 +2,7 @@ import { readFileSync, readdirSync, realpathSync } from "node:fs";
2
2
  import { basename, dirname, extname, join, resolve } from "node:path";
3
3
  import { InMemoryCredentialStore } from "@earendil-works/pi-ai";
4
4
  import { ModelRuntime, createAgentSessionFromServices, createAgentSessionServices, getAgentDir, hasTrustRequiringProjectResources, SessionManager, SettingsManager, } from "@earendil-works/pi-coding-agent";
5
- import { DEFAULT_SETTINGS, loadSettings, parseModelReference, parseRoleMarkdown, preflight, registeredWorkflowDefinitions, workflowRoleDirectories, workflowSettingsPath, WorkflowError, } from "./index.js";
5
+ import { DEFAULT_SETTINGS, loadSettings, parseModelReference, resolveAgentResourcePolicy, resolveModelReference, parseRoleMarkdown, preflight, registeredWorkflowDefinitions, workflowRoleDirectories, workflowProjectSettingsPath, workflowSettingsPath, WorkflowError, } from "./index.js";
6
6
  const THINKING_HINT = "Use off, minimal, low, medium, high, xhigh, or max.";
7
7
  function canonical(path) {
8
8
  const absolute = resolve(path);
@@ -65,7 +65,7 @@ async function discoverPi(cwd, agentDir) {
65
65
  agentDir,
66
66
  settingsManager,
67
67
  modelRuntime,
68
- resourceLoaderOptions: { noSkills: true, noPromptTemplates: true, noThemes: true, noContextFiles: true },
68
+ resourceLoaderOptions: { noPromptTemplates: true, noThemes: true, noContextFiles: true },
69
69
  resourceLoaderReloadOptions: { resolveProjectTrust: async () => trusted },
70
70
  });
71
71
  const allModels = services.modelRuntime.getModels();
@@ -76,11 +76,14 @@ async function discoverPi(cwd, agentDir) {
76
76
  const { session } = await createAgentSessionFromServices({ services, sessionManager: SessionManager.inMemory(), model });
77
77
  const activeTools = session.agent.state.tools.map(({ name }) => name).filter((name) => name !== "workflow" && name !== "workflow_respond" && name !== "workflow_catalog");
78
78
  const extensions = services.resourceLoader.getExtensions();
79
+ const skills = services.resourceLoader.getSkills().skills;
79
80
  return {
80
81
  trust: { required, trusted, source },
81
82
  activeTools,
82
83
  knownModels: allModels.map(({ provider, id }) => `${provider}/${id}`),
83
84
  availableModels: availableModels.map(({ provider, id }) => `${provider}/${id}`),
85
+ extensions: extensions.extensions.map(({ resolvedPath }) => resolvedPath),
86
+ skills: skills.map(({ name }) => name),
84
87
  extensionErrors: [
85
88
  ...extensions.errors.map(({ path, error }) => ({ path, message: error })),
86
89
  ...services.diagnostics.filter(({ type }) => type === "error").map(({ message }) => ({ message })),
@@ -112,9 +115,13 @@ function roleFilesFrom(dirs) {
112
115
  function diagnostic(severity, code, message, source, hint) {
113
116
  return { severity, code, message, ...(source ? { source } : {}), ...(hint ? { hint } : {}) };
114
117
  }
115
- function validateModel(value, known, available, source, diagnostics) {
118
+ function emptyResourcePolicy(globalSettingsPath, cwd, projectTrusted) {
119
+ const empty = { skills: [], extensions: [] };
120
+ return { globalSettingsPath, projectSettingsPath: workflowProjectSettingsPath(cwd), projectTrusted, global: empty, project: empty, effective: empty, unmatchedSkills: [], unmatchedExtensions: [] };
121
+ }
122
+ function validateModel(value, known, available, source, diagnostics, aliases, settingsPath) {
116
123
  try {
117
- const parsed = parseModelReference(value);
124
+ const parsed = resolveModelReference(value, aliases, known, settingsPath);
118
125
  const name = `${parsed.provider}/${parsed.model}`;
119
126
  if (!known.has(name) || !available.has(name))
120
127
  diagnostics.push(diagnostic("warning", "MODEL_UNAVAILABLE", `Model is valid-shaped but unavailable: ${name}`, source));
@@ -123,10 +130,10 @@ function validateModel(value, known, available, source, diagnostics) {
123
130
  diagnostics.push(diagnostic("error", "MODEL_INVALID", error.message, source, error.message.includes("thinking") ? THINKING_HINT : "Use provider/model or provider/model:thinking."));
124
131
  }
125
132
  }
126
- function inspectRole(path, activeTools, knownModels, availableModels, diagnostics) {
133
+ function inspectRole(path, activeTools, knownModels, availableModels, diagnostics, aliases, settingsPath) {
127
134
  let definition;
128
135
  try {
129
- definition = parseRoleMarkdown(readFileSync(path, "utf8"), true);
136
+ definition = parseRoleMarkdown(readFileSync(path, "utf8"), true, path);
130
137
  }
131
138
  catch (error) {
132
139
  diagnostics.push(diagnostic("error", "ROLE_FRONTMATTER", error.message, path, "Fix the role YAML frontmatter."));
@@ -140,7 +147,7 @@ function inspectRole(path, activeTools, knownModels, availableModels, diagnostic
140
147
  if (/{{\s*[^{}]+\s*}}/.test(body))
141
148
  diagnostics.push(diagnostic("warning", "ROLE_PLACEHOLDER", "Role body contains an unsupported placeholder-looking token", path));
142
149
  if (definition.model)
143
- validateModel(definition.model, knownModels, availableModels, path, diagnostics);
150
+ validateModel(definition.model, knownModels, availableModels, path, diagnostics, aliases, settingsPath);
144
151
  for (const tool of definition.tools ?? [])
145
152
  if (!activeTools.has(tool))
146
153
  diagnostics.push(diagnostic("error", "ROLE_TOOL_INACTIVE", `Tool is unknown or inactive: ${tool}`, path, "Use a tool listed under Active tools or enable its Pi extension."));
@@ -149,6 +156,14 @@ function inspectRole(path, activeTools, knownModels, availableModels, diagnostic
149
156
  class DoctorModelSet extends Set {
150
157
  has(value) { parseModelReference(value); return true; }
151
158
  }
159
+ function matchResourcePolicy(policy, pi) {
160
+ const extensions = new Set((pi.extensions ?? []).map(canonical));
161
+ const skills = new Set(pi.skills ?? []);
162
+ return { ...policy, unmatchedSkills: policy.effective.skills.filter((name) => !skills.has(name)), unmatchedExtensions: policy.effective.extensions.filter((path) => !extensions.has(canonical(path))) };
163
+ }
164
+ function resourcePolicySource(policy, kind, selector) {
165
+ return policy.project[kind].includes(selector) && !policy.global[kind].includes(selector) ? policy.projectSettingsPath : policy.globalSettingsPath;
166
+ }
152
167
  export async function doctor(options = {}) {
153
168
  const cwd = canonical(options.cwd ?? process.cwd());
154
169
  const agentDir = canonical(options.agentDir ?? getAgentDir());
@@ -175,9 +190,24 @@ export async function doctor(options = {}) {
175
190
  diagnostics.push(diagnostic("warning", "PROJECT_UNTRUSTED", "Pi project resources are inactive because the project is not trusted", cwd, "Open this project in Pi, choose Trust, then rerun doctor."));
176
191
  for (const error of pi.extensionErrors)
177
192
  diagnostics.push(diagnostic("error", "EXTENSION_LOAD", error.message, error.path, "Fix or disable the failing Pi extension."));
193
+ let resourcePolicy;
194
+ try {
195
+ resourcePolicy = matchResourcePolicy(resolveAgentResourcePolicy(cwd, pi.trust.trusted, settingsPath), pi);
196
+ }
197
+ catch (error) {
198
+ const message = error instanceof Error ? error.message : String(error);
199
+ if (!diagnostics.some(({ code, source }) => code === "SETTINGS_INVALID" && source === settingsPath))
200
+ diagnostics.push(diagnostic("error", "SETTINGS_INVALID", message, message.includes(".json") ? message.match(/(?:settings: )?([^ )]+\.json)/)?.[1] : undefined, "Fix or remove the invalid workflow settings file."));
201
+ resourcePolicy = emptyResourcePolicy(settingsPath, cwd, pi.trust.trusted);
202
+ }
203
+ for (const skill of resourcePolicy.unmatchedSkills)
204
+ diagnostics.push(diagnostic("warning", "AGENT_RESOURCE_UNMATCHED", `Disabled skill selector currently matches no discovered skill: ${skill}`, `${resourcePolicySource(resourcePolicy, "skills", skill)}.disabledAgentResources.skills`));
205
+ for (const extension of resourcePolicy.unmatchedExtensions)
206
+ diagnostics.push(diagnostic("warning", "AGENT_RESOURCE_UNMATCHED", `Disabled extension selector currently matches no discovered extension source: ${extension}`, `${resourcePolicySource(resourcePolicy, "extensions", extension)}.disabledAgentResources.extensions`));
178
207
  const activeTools = new Set(pi.activeTools);
179
208
  const knownModels = new Set(pi.knownModels);
180
209
  const availableModels = new Set(pi.availableModels);
210
+ const aliases = settings.modelAliases ?? {};
181
211
  const roles = [];
182
212
  const definitions = new Map();
183
213
  const globalPaths = new Map();
@@ -186,7 +216,7 @@ export async function doctor(options = {}) {
186
216
  const name = basename(path, ".md");
187
217
  roles.push({ name, path, scope: "global", active: true });
188
218
  globalPaths.set(name, path);
189
- const definition = inspectRole(path, activeTools, knownModels, availableModels, diagnostics);
219
+ const definition = inspectRole(path, activeTools, knownModels, availableModels, diagnostics, aliases, settingsPath);
190
220
  if (definition)
191
221
  definitions.set(name, definition);
192
222
  }
@@ -204,7 +234,7 @@ export async function doctor(options = {}) {
204
234
  global.overriddenBy = path;
205
235
  }
206
236
  }
207
- const definition = inspectRole(path, activeTools, knownModels, availableModels, diagnostics);
237
+ const definition = inspectRole(path, activeTools, knownModels, availableModels, diagnostics, aliases, settingsPath);
208
238
  if (definition)
209
239
  definitions.set(name, definition);
210
240
  else
@@ -218,6 +248,9 @@ export async function doctor(options = {}) {
218
248
  models: new DoctorModelSet(pi.knownModels),
219
249
  tools: activeTools,
220
250
  agentTypes: new Set(definitions.keys()),
251
+ modelAliases: aliases,
252
+ knownModels,
253
+ settingsPath,
221
254
  }, [], { name, description: workflow.description });
222
255
  for (const model of checked.referenced.models)
223
256
  if (!knownModels.has(model) || !availableModels.has(model))
@@ -233,7 +266,7 @@ export async function doctor(options = {}) {
233
266
  const severityOrder = { error: 0, warning: 1 };
234
267
  diagnostics.sort((left, right) => severityOrder[left.severity] - severityOrder[right.severity] || (left.source ?? "").localeCompare(right.source ?? "") || left.code.localeCompare(right.code) || left.message.localeCompare(right.message));
235
268
  roles.sort((left, right) => left.name.localeCompare(right.name) || left.scope.localeCompare(right.scope));
236
- return { cwd, agentDir, settingsPath, settings, trust: pi.trust, activeTools: [...activeTools].sort(), roles, workflows, diagnostics };
269
+ return { cwd, agentDir, settingsPath, settings, trust: pi.trust, activeTools: [...activeTools].sort(), roles, workflows, resourcePolicy, diagnostics };
237
270
  }
238
271
  function count(report, severity) { return report.diagnostics.filter((item) => item.severity === severity).length; }
239
272
  export function doctorExitCode(report) { return count(report, "error") > 0 ? 1 : 0; }
@@ -245,11 +278,23 @@ export function formatDoctorReport(report) {
245
278
  `- CWD: \`${report.cwd}\``,
246
279
  `- Agent dir: \`${report.agentDir}\``,
247
280
  `- Workflow settings: \`${report.settingsPath}\``,
248
- `- Limits: concurrency=${String(report.settings.concurrency)}, maxAgentLaunches=${String(report.settings.maxAgentLaunches)}`,
281
+ `- Limits: concurrency=${String(report.settings.concurrency)}`,
249
282
  "",
250
283
  "## Trust/resources",
251
284
  `- [${report.trust.trusted ? "ok" : "warning"}] ${report.trust.source}`,
252
285
  "",
286
+ "## Agent resource exclusions",
287
+ `- Global settings: \`${report.resourcePolicy.globalSettingsPath}\``,
288
+ `- Global skills: ${report.resourcePolicy.global.skills.join(", ") || "(none)"}`,
289
+ `- Global extensions: ${report.resourcePolicy.global.extensions.join(", ") || "(none)"}`,
290
+ `- Project settings: \`${report.resourcePolicy.projectSettingsPath}\` (${report.resourcePolicy.projectTrusted ? "trusted" : "ignored: project untrusted"})`,
291
+ `- Project skills: ${report.resourcePolicy.project.skills.join(", ") || "(none)"}`,
292
+ `- Project extensions: ${report.resourcePolicy.project.extensions.join(", ") || "(none)"}`,
293
+ `- Effective skills: ${report.resourcePolicy.effective.skills.join(", ") || "(none)"}`,
294
+ `- Effective extensions: ${report.resourcePolicy.effective.extensions.join(", ") || "(none)"}`,
295
+ `- Unmatched skills: ${report.resourcePolicy.unmatchedSkills.join(", ") || "(none)"}`,
296
+ `- Unmatched extensions: ${report.resourcePolicy.unmatchedExtensions.join(", ") || "(none)"}`,
297
+ "",
253
298
  "## Active tools",
254
299
  ...(report.activeTools.length ? report.activeTools.map((tool) => `- \`${tool}\``) : ["- None resolved"]),
255
300
  "",
@@ -1,13 +1,23 @@
1
1
  import { Type } from "@earendil-works/pi-ai";
2
2
  import { copyToClipboard, type ExtensionAPI } from "@earendil-works/pi-coding-agent";
3
- import { type AgentActivity, type AgentAttempt, type AgentDefinition, type SessionFactory } from "./agent-execution.js";
3
+ import { type AgentActivity, type AgentAccounting, type AgentAttempt, type AgentBudgetHooks, type AgentDefinition, type AgentSetupHook, type RegisteredAgentSetupHook, type SessionFactory } from "./agent-execution.js";
4
4
  import { RunStore } from "./persistence.js";
5
5
  import type { AwaitingCheckpoint, PersistedRun, WorktreeReference } from "./persistence.js";
6
- export declare const RUN_STATES: readonly ["queued", "running", "pausing", "paused", "awaiting_input", "completed", "failed", "stopped", "interrupted"];
6
+ export declare const RUN_STATES: readonly ["queued", "running", "pausing", "paused", "awaiting_input", "completed", "failed", "stopped", "interrupted", "budget_exhausted"];
7
7
  export declare const AGENT_STATES: readonly ["queued", "running", "waiting_for_child", "paused", "retrying", "completed", "failed", "cancelled"];
8
8
  export declare const WORKFLOW_ASYNC_STARTED_EVENT = "workflow:async-started";
9
9
  export declare const WORKFLOW_ASYNC_COMPLETE_EVENT = "workflow:async-complete";
10
- export declare const ERROR_CODES: readonly ["INVALID_SETTINGS", "INVALID_SYNTAX", "INVALID_METADATA", "DUPLICATE_NAME", "INVALID_SCHEMA", "UNKNOWN_MODEL", "UNKNOWN_TOOL", "UNKNOWN_AGENT_TYPE", "RUN_LIMIT_EXCEEDED", "RUN_OWNED", "REGISTRY_FROZEN", "GLOBAL_COLLISION", "MISSING_WORKFLOW", "RPC_LIMIT_EXCEEDED", "AGENT_TIMEOUT", "AGENT_FAILED", "RESULT_INVALID", "CANCELLED", "WORKER_UNRESPONSIVE", "WORKTREE_FAILED", "RESUME_INCOMPATIBLE", "INTERNAL_ERROR"];
10
+ export declare const WORKFLOW_RUN_STARTED_EVENT = "workflow:run-started";
11
+ export declare const WORKFLOW_RUN_RESUMED_EVENT = "workflow:run-resumed";
12
+ export declare const WORKFLOW_RUN_STATE_CHANGED_EVENT = "workflow:run-state-changed";
13
+ export declare const WORKFLOW_RUN_COMPLETED_EVENT = "workflow:run-completed";
14
+ export declare const WORKFLOW_RUN_FAILED_EVENT = "workflow:run-failed";
15
+ export declare const WORKFLOW_AGENT_STATE_CHANGED_EVENT = "workflow:agent-state-changed";
16
+ export declare const WORKFLOW_PHASE_CHANGED_EVENT = "workflow:phase-changed";
17
+ export declare const WORKFLOW_CHECKPOINT_STATE_CHANGED_EVENT = "workflow:checkpoint-state-changed";
18
+ export declare const WORKFLOW_BUDGET_EVENT = "workflow:budget-event";
19
+ export declare const WORKFLOW_WORKTREE_CREATED_EVENT = "workflow:worktree-created";
20
+ export declare const ERROR_CODES: readonly ["CONFIG_ERROR", "INVALID_SETTINGS", "INVALID_SYNTAX", "INVALID_METADATA", "DUPLICATE_NAME", "INVALID_SCHEMA", "UNKNOWN_MODEL", "UNKNOWN_TOOL", "UNKNOWN_AGENT_TYPE", "RUN_OWNED", "REGISTRY_FROZEN", "GLOBAL_COLLISION", "MISSING_WORKFLOW", "RPC_LIMIT_EXCEEDED", "AGENT_TIMEOUT", "AGENT_FAILED", "RESULT_INVALID", "CANCELLED", "WORKER_UNRESPONSIVE", "WORKTREE_FAILED", "RESUME_INCOMPATIBLE", "BUDGET_EXHAUSTED", "INTERNAL_ERROR"];
11
21
  export type RunState = (typeof RUN_STATES)[number];
12
22
  export type AgentState = (typeof AGENT_STATES)[number];
13
23
  export type WorkflowErrorCode = (typeof ERROR_CODES)[number];
@@ -17,10 +27,100 @@ export type JsonValue = null | boolean | number | string | JsonValue[] | {
17
27
  export type JsonSchema = {
18
28
  [key: string]: JsonValue;
19
29
  };
30
+ export type BudgetDimension = "tokens" | "costUsd" | "durationMs" | "agentLaunches";
31
+ export interface BudgetLimits {
32
+ soft?: number;
33
+ hard?: number;
34
+ }
35
+ export type WorkflowBudget = Partial<Record<BudgetDimension, BudgetLimits>>;
36
+ export type WorkflowBudgetPatch = Partial<Record<BudgetDimension, BudgetLimits | {
37
+ soft?: number | null;
38
+ hard?: number | null;
39
+ } | null>>;
40
+ export interface WorkflowBudgetUsage {
41
+ tokens: number;
42
+ costUsd: number;
43
+ durationMs: number;
44
+ agentLaunches: number;
45
+ }
46
+ export type BudgetEventType = "soft_crossed" | "hard_overrun" | "hard_exhausted" | "adjustment_requested" | "adjustment_approved" | "adjustment_rejected";
47
+ export interface BudgetEvent {
48
+ type: BudgetEventType;
49
+ budgetVersion: number;
50
+ dimensions: readonly BudgetDimension[];
51
+ usage: WorkflowBudgetUsage;
52
+ limits: WorkflowBudget;
53
+ at: number;
54
+ proposalId?: string;
55
+ previous?: WorkflowBudget;
56
+ proposed?: WorkflowBudget;
57
+ }
58
+ export interface BudgetApprovalRequest {
59
+ kind: "budget";
60
+ proposalId: string;
61
+ runId: string;
62
+ consumed: WorkflowBudgetUsage;
63
+ previous: WorkflowBudget;
64
+ proposed: WorkflowBudget;
65
+ budgetVersion: number;
66
+ }
20
67
  export interface WorkflowErrorShape {
21
68
  code: WorkflowErrorCode;
22
69
  message: string;
23
70
  }
71
+ export interface WorkflowEventBase {
72
+ runId: string;
73
+ sessionId: string;
74
+ workflowName: string;
75
+ cwd: string;
76
+ runDirectory: string;
77
+ timestamp: number;
78
+ }
79
+ export type WorkflowRunStartedEvent = WorkflowEventBase;
80
+ export type WorkflowRunResumedEvent = WorkflowEventBase;
81
+ export interface WorkflowRunStateChangedEvent extends WorkflowEventBase {
82
+ previousState: RunState;
83
+ state: RunState;
84
+ reason?: string;
85
+ errorCode?: WorkflowErrorCode;
86
+ }
87
+ export interface WorkflowRunCompletedEvent extends WorkflowEventBase {
88
+ resultPath: string;
89
+ }
90
+ export interface WorkflowRunFailedEvent extends WorkflowEventBase {
91
+ error: WorkflowErrorShape;
92
+ }
93
+ export interface WorkflowAgentStateChangedEvent extends WorkflowEventBase {
94
+ agentId: string;
95
+ displayLabel: string;
96
+ role?: string;
97
+ structuralPath: readonly string[];
98
+ parentId?: string;
99
+ parentBreadcrumb?: string;
100
+ worktreeOwner?: string;
101
+ previousState?: AgentState;
102
+ state: AgentState;
103
+ attempt: number;
104
+ }
105
+ export interface WorkflowPhaseChangedEvent extends WorkflowEventBase {
106
+ previousPhase?: string;
107
+ phase: string;
108
+ }
109
+ export type WorkflowCheckpointState = "awaiting" | "approved" | "rejected";
110
+ export interface WorkflowCheckpointStateChangedEvent extends WorkflowEventBase {
111
+ name: string;
112
+ state: WorkflowCheckpointState;
113
+ }
114
+ export interface WorkflowBudgetEvent extends WorkflowEventBase {
115
+ type: BudgetEventType;
116
+ budgetVersion: number;
117
+ dimensions: readonly BudgetDimension[];
118
+ usage: WorkflowBudgetUsage;
119
+ limits: WorkflowBudget;
120
+ proposalId?: string;
121
+ previous?: WorkflowBudget;
122
+ proposed?: WorkflowBudget;
123
+ }
24
124
  export interface ModelSpec {
25
125
  provider: string;
26
126
  model: string;
@@ -32,7 +132,34 @@ export interface WorkflowMetadata {
32
132
  }
33
133
  export interface WorkflowSettings {
34
134
  concurrency: number;
35
- maxAgentLaunches: number;
135
+ modelAliases?: Readonly<Record<string, string>>;
136
+ disabledAgentResources?: Readonly<AgentResourceExclusions>;
137
+ }
138
+ export interface AgentResourceExclusions {
139
+ skills: readonly string[];
140
+ extensions: readonly string[];
141
+ }
142
+ export interface AgentResourcePolicy {
143
+ globalSettingsPath: string;
144
+ projectSettingsPath: string;
145
+ projectTrusted: boolean;
146
+ global: AgentResourceExclusions;
147
+ project: AgentResourceExclusions;
148
+ effective: AgentResourceExclusions;
149
+ unmatchedSkills: string[];
150
+ unmatchedExtensions: string[];
151
+ }
152
+ export interface AgentSetupSummary {
153
+ hookNames: readonly string[];
154
+ model: ModelSpec;
155
+ tools: readonly string[];
156
+ cwd: string;
157
+ disabledAgentResources?: {
158
+ skills: readonly string[];
159
+ extensions: readonly string[];
160
+ unmatchedSkills: readonly string[];
161
+ unmatchedExtensions: readonly string[];
162
+ };
36
163
  }
37
164
  export interface AgentAttemptSummary {
38
165
  attempt: number;
@@ -49,6 +176,13 @@ export interface AgentAttemptSummary {
49
176
  cacheWrite: number;
50
177
  cost: number;
51
178
  };
179
+ setup?: AgentSetupSummary;
180
+ }
181
+ export interface WorkflowWorktreeCreatedEvent extends WorkflowEventBase {
182
+ owner: string;
183
+ branch: string;
184
+ path: string;
185
+ base: string;
52
186
  }
53
187
  export interface AgentRecord {
54
188
  id: string;
@@ -57,8 +191,11 @@ export interface AgentRecord {
57
191
  path: string;
58
192
  state: AgentState;
59
193
  parentId?: string;
194
+ structuralPath?: readonly string[];
60
195
  parentBreadcrumb?: string;
196
+ worktreeOwner?: string;
61
197
  role?: string;
198
+ requestedModel?: string;
62
199
  model: ModelSpec;
63
200
  tools: readonly string[];
64
201
  attempts: number;
@@ -77,6 +214,10 @@ export interface AgentRecord {
77
214
  }[];
78
215
  activity?: AgentActivity | undefined;
79
216
  }
217
+ export interface WorkflowRunEvent {
218
+ type: "warning";
219
+ message: string;
220
+ }
80
221
  export interface RunRecord {
81
222
  id: string;
82
223
  workflowName: string;
@@ -86,13 +227,22 @@ export interface RunRecord {
86
227
  phase?: string;
87
228
  agents: readonly AgentRecord[];
88
229
  error?: WorkflowErrorShape;
230
+ budget?: WorkflowBudget;
231
+ budgetVersion?: number;
232
+ usage?: WorkflowBudgetUsage;
233
+ budgetEvents?: readonly BudgetEvent[];
234
+ events?: readonly WorkflowRunEvent[];
89
235
  }
236
+ export declare const LAUNCH_SNAPSHOT_IDENTITY_VERSION = 3;
90
237
  export interface LaunchSnapshot {
91
238
  identityVersion?: number;
92
239
  script: string;
93
240
  args: JsonValue;
94
241
  metadata: WorkflowMetadata;
95
242
  settings: WorkflowSettings;
243
+ budget?: WorkflowBudget;
244
+ settingsPath?: string;
245
+ modelAliases?: Readonly<Record<string, string>>;
96
246
  models: readonly string[];
97
247
  tools: readonly string[];
98
248
  agentTypes: readonly string[];
@@ -104,6 +254,10 @@ export interface PreflightCapabilities {
104
254
  models: ReadonlySet<string>;
105
255
  tools: ReadonlySet<string>;
106
256
  agentTypes: ReadonlySet<string>;
257
+ modelAliases?: Readonly<Record<string, string>>;
258
+ knownModels?: ReadonlySet<string>;
259
+ settingsPath?: string;
260
+ skipModelAvailability?: boolean;
107
261
  }
108
262
  export interface PreflightResult {
109
263
  metadata: WorkflowMetadata;
@@ -153,13 +307,13 @@ export interface WorkflowScriptDefinition {
153
307
  script: string;
154
308
  }
155
309
  export interface WorkflowExtension {
156
- namespace: string;
157
310
  version: string;
158
311
  headline: string;
159
312
  description: string;
160
313
  functions?: Readonly<Record<string, WorkflowFunction>>;
161
314
  variables?: Readonly<Record<string, WorkflowVariable>>;
162
315
  workflows?: Readonly<Record<string, WorkflowScriptDefinition>>;
316
+ agentSetupHooks?: Readonly<Record<string, AgentSetupHook>>;
163
317
  }
164
318
  export interface WorkflowJournal {
165
319
  get(path: string): JsonValue | undefined;
@@ -173,7 +327,7 @@ export declare function formatWorkflowFailure(error: unknown): string;
173
327
  export declare class RunLifecycle {
174
328
  #private;
175
329
  private readonly changed?;
176
- constructor(state?: RunState, changed?: ((state: RunState) => void | Promise<void>) | undefined);
330
+ constructor(state?: RunState, changed?: ((state: RunState, previousState: RunState, reason?: string) => void | Promise<void>) | undefined);
177
331
  get state(): RunState;
178
332
  enter(): Promise<void>;
179
333
  leave(): Promise<void>;
@@ -182,10 +336,43 @@ export declare class RunLifecycle {
182
336
  pause(): Promise<void>;
183
337
  resume(): Promise<void>;
184
338
  providerPause(): Promise<void>;
185
- terminal(state: "completed" | "failed" | "stopped" | "interrupted"): Promise<void>;
339
+ terminal(state: "completed" | "failed" | "stopped" | "interrupted" | "budget_exhausted", reason?: string): Promise<void>;
186
340
  }
187
341
  export declare const DEFAULT_SETTINGS: Readonly<WorkflowSettings>;
342
+ export declare function validateBudget(value: unknown): WorkflowBudget | undefined;
343
+ export declare function validateBudgetPatch(value: unknown): WorkflowBudgetPatch;
344
+ export declare class WorkflowBudgetRuntime {
345
+ #private;
346
+ readonly budget: WorkflowBudget | undefined;
347
+ readonly version: number;
348
+ constructor(budget: WorkflowBudget | undefined, version?: number, usage?: Partial<WorkflowBudgetUsage>, events?: readonly BudgetEvent[], options?: {
349
+ now?: () => number;
350
+ onChange?: () => void;
351
+ active?: boolean;
352
+ });
353
+ get usage(): WorkflowBudgetUsage;
354
+ get events(): readonly BudgetEvent[];
355
+ get hardExhausted(): boolean;
356
+ checkAgentLaunch(): void;
357
+ beforeAttempt(): void;
358
+ beforeTurn(): void;
359
+ afterTurn(accounting: AgentAccounting, final: boolean): void;
360
+ instruction(agentId?: string): string | undefined;
361
+ forAgent(agentId: string): AgentBudgetHooks;
362
+ transition(state: RunState): void;
363
+ recordEvent(event: BudgetEvent): void;
364
+ snapshot(): {
365
+ usage: WorkflowBudgetUsage;
366
+ budgetEvents: readonly BudgetEvent[];
367
+ };
368
+ }
369
+ export declare function mergeBudget(budget: WorkflowBudget | undefined, patch: WorkflowBudgetPatch): WorkflowBudget | undefined;
370
+ export declare function budgetRelaxed(previous: WorkflowBudget | undefined, next: WorkflowBudget | undefined): boolean;
371
+ export declare function exhaustedBudgetDimensions(budget: WorkflowBudget | undefined, usage: WorkflowBudgetUsage): BudgetDimension[];
372
+ export declare function resumeBudgetAllowed(budget: WorkflowBudget | undefined, usage: WorkflowBudgetUsage): boolean;
188
373
  export declare function parseModelReference(value: string): ModelSpec;
374
+ export declare function validateModelAliases(value: unknown, settingsPath?: string): Readonly<Record<string, string>>;
375
+ export declare function resolveModelReference(value: string, aliases?: Readonly<Record<string, string>>, knownModels?: ReadonlySet<string>, settingsPath?: string): ModelSpec;
189
376
  export interface CheckpointInput {
190
377
  name: string;
191
378
  prompt: string;
@@ -193,8 +380,12 @@ export interface CheckpointInput {
193
380
  }
194
381
  export declare function validateCheckpoint(value: unknown): CheckpointInput;
195
382
  export declare function workflowSettingsPath(agentDir?: string): string;
383
+ export declare function workflowProjectSettingsPath(cwd: string): string;
384
+ export declare function mergeAgentResourceExclusions(...values: (AgentResourceExclusions | undefined)[]): AgentResourceExclusions;
196
385
  export declare function loadSettings(path?: string): Readonly<WorkflowSettings>;
197
- export declare function parseRoleMarkdown(content: string, strict?: boolean): AgentDefinition;
386
+ export declare function resolveAgentResourcePolicy(cwd: string, projectTrusted: boolean, globalSettingsPath?: string): AgentResourcePolicy;
387
+ export declare function saveModelAliases(path?: string, aliases?: Readonly<Record<string, string>>): void;
388
+ export declare function parseRoleMarkdown(content: string, strict?: boolean, rolePath?: string): AgentDefinition;
198
389
  export declare function workflowRoleDirectories(agentDir?: string): readonly string[];
199
390
  export declare function loadAgentDefinitions(cwd: string, agentDir?: string, projectTrusted?: boolean): Readonly<Record<string, AgentDefinition>>;
200
391
  export type StaticWorkflowExecution = "parallel" | "sequential";
@@ -204,7 +395,7 @@ export interface StaticWorkflowScope {
204
395
  key: string | null;
205
396
  }
206
397
  export interface StaticWorkflowCall {
207
- kind: "agent" | "parallel" | "pipeline" | "checkpoint" | "phase" | "withWorktree";
398
+ kind: "agent" | "conversation" | "parallel" | "pipeline" | "checkpoint" | "phase" | "withWorktree";
208
399
  start: number;
209
400
  end: number;
210
401
  name: string | null;
@@ -222,7 +413,6 @@ export interface StaticWorkflowCall {
222
413
  export declare function inspectWorkflowScript(script: string): StaticWorkflowCall[];
223
414
  export interface WorkflowCatalogFunction {
224
415
  name: string;
225
- namespace: string;
226
416
  version: string;
227
417
  headline: string;
228
418
  extensionDescription: string;
@@ -232,7 +422,6 @@ export interface WorkflowCatalogFunction {
232
422
  }
233
423
  export interface WorkflowCatalogVariable {
234
424
  name: string;
235
- namespace: string;
236
425
  version: string;
237
426
  headline: string;
238
427
  extensionDescription: string;
@@ -241,7 +430,6 @@ export interface WorkflowCatalogVariable {
241
430
  }
242
431
  export interface WorkflowCatalogWorkflow {
243
432
  name: string;
244
- namespace: string;
245
433
  version: string;
246
434
  headline: string;
247
435
  extensionDescription: string;
@@ -251,6 +439,7 @@ export interface WorkflowCatalog {
251
439
  functions: readonly WorkflowCatalogFunction[];
252
440
  variables: readonly WorkflowCatalogVariable[];
253
441
  workflows: readonly WorkflowCatalogWorkflow[];
442
+ modelAliases?: Readonly<Record<string, string>>;
254
443
  }
255
444
  export declare class WorkflowRegistry {
256
445
  #private;
@@ -261,15 +450,14 @@ export declare class WorkflowRegistry {
261
450
  workflows(): Readonly<Record<string, WorkflowScriptDefinition>>;
262
451
  catalog(): WorkflowCatalog;
263
452
  globals(): Readonly<Record<string, {
264
- namespace: string;
265
453
  name: string;
266
454
  }>>;
267
- invokeFunction(namespace: string, name: string, input: unknown, context: Readonly<WorkflowFunctionContext>, path: string, journal: WorkflowJournal): Promise<JsonValue>;
455
+ invokeFunction(name: string, input: unknown, context: Readonly<WorkflowFunctionContext>, path: string, journal: WorkflowJournal): Promise<JsonValue>;
268
456
  variables(): readonly {
269
- namespace: string;
270
457
  name: string;
271
458
  variable: WorkflowVariable;
272
459
  }[];
460
+ agentSetupHooks(): readonly RegisteredAgentSetupHook[];
273
461
  }
274
462
  export declare function registerWorkflowExtension(extension: WorkflowExtension): void;
275
463
  export declare function workflowCatalog(): WorkflowCatalog;
@@ -291,7 +479,7 @@ export declare const WORKFLOW_TOOL_PARAMETERS: Type.TObject<{
291
479
  args: Type.TOptional<Type.TUnknown>;
292
480
  foreground: Type.TOptional<Type.TBoolean>;
293
481
  concurrency: Type.TOptional<Type.TInteger>;
294
- maxAgentLaunches: Type.TOptional<Type.TInteger>;
482
+ budget: Type.TOptional<Type.TUnknown>;
295
483
  }>;
296
484
  export declare function preflight(script: string, capabilities: PreflightCapabilities, schemas?: readonly unknown[], metadata?: WorkflowMetadata): PreflightResult;
297
485
  export interface WorkflowValidationParameters {
@@ -305,6 +493,9 @@ export interface WorkflowValidationContext {
305
493
  projectTrusted: boolean;
306
494
  availableModels: ReadonlySet<string>;
307
495
  rootTools: ReadonlySet<string>;
496
+ modelAliases?: Readonly<Record<string, string>>;
497
+ knownModels?: ReadonlySet<string>;
498
+ settingsPath?: string;
308
499
  }
309
500
  export interface ValidatedWorkflowLaunch {
310
501
  script: string;
@@ -327,13 +518,16 @@ export interface AgentIdentity {
327
518
  occurrence: number;
328
519
  parentBreadcrumb?: string;
329
520
  worktreeOwner?: string;
521
+ conversation?: {
522
+ name: string;
523
+ turn: number;
524
+ };
330
525
  }
331
526
  export interface WorkflowBridge {
332
527
  agent?: (prompt: string, options: Readonly<Record<string, JsonValue>>, signal: AbortSignal, identity: AgentIdentity) => Promise<JsonValue>;
333
528
  checkpoint?: (input: Readonly<Record<string, JsonValue>>, signal: AbortSignal) => boolean | Promise<boolean>;
334
- function?: (namespace: string, name: string, input: Readonly<Record<string, JsonValue>>, path: string, signal: AbortSignal, worktreeOwner?: string) => Promise<JsonValue>;
529
+ function?: (name: string, input: Readonly<Record<string, JsonValue>>, path: string, signal: AbortSignal, worktreeOwner?: string, structuralPath?: readonly string[]) => Promise<JsonValue>;
335
530
  functions?: Readonly<Record<string, {
336
- namespace: string;
337
531
  name: string;
338
532
  }>>;
339
533
  variables?: Readonly<Record<string, JsonValue>>;
@@ -345,18 +539,19 @@ export interface WorkflowExecution {
345
539
  cancel: () => void;
346
540
  }
347
541
  export declare function runWorkflow(script: string, args?: JsonValue, bridge?: WorkflowBridge, signal?: AbortSignal): WorkflowExecution;
348
- export declare function persistActiveAgentAttempt(store: RunStore, id: string, active: Pick<AgentAttempt, "attempt" | "sessionId" | "sessionFile">): Promise<void>;
542
+ export declare function persistActiveAgentAttempt(store: RunStore, id: string, active: Pick<AgentAttempt, "attempt" | "sessionId" | "sessionFile" | "setup">): Promise<void>;
349
543
  export declare function persistAgentAttempts(store: RunStore, id: string, attempts: readonly AgentAttempt[]): Promise<void>;
350
544
  export declare function formatWorkflowProgress(run: PersistedRun, spinner?: string): string;
545
+ export declare function formatBudgetStatus(run: Pick<PersistedRun, "budget" | "budgetVersion" | "usage" | "budgetEvents">): string[];
351
546
  export declare function formatNavigatorDashboard(run: PersistedRun, checkpoints: readonly AwaitingCheckpoint[], worktrees: readonly WorktreeReference[]): string;
352
547
  export declare function formatNavigatorRun(loaded: {
353
548
  run: PersistedRun;
354
549
  snapshot: Readonly<LaunchSnapshot>;
355
- }, checkpoints: readonly AwaitingCheckpoint[], worktrees: readonly WorktreeReference[]): string;
550
+ }, checkpoints: readonly AwaitingCheckpoint[], _worktrees: readonly WorktreeReference[]): string;
356
551
  export default function workflowExtension(pi: ExtensionAPI, home?: string, clipboard?: typeof copyToClipboard, createSession?: SessionFactory): void;
357
552
  export { acquireSessionLease, projectStorageKey, RunStore, runsDirectory, SessionLease, structuralPath } from "./persistence.js";
358
- export type { AwaitingCheckpoint, CompletedOperation, NativeSessionReference, PersistedOwnershipNode, PersistedRun, WorktreeReference } from "./persistence.js";
553
+ export type { AwaitingCheckpoint, CompletedOperation, ConversationHead, NativeSessionReference, PendingWorkflowDecision, PersistedConversation, PersistedOwnershipNode, PersistedRun, WorktreeReference } from "./persistence.js";
359
554
  export { FairAgentScheduler, WorkflowAgentExecutor } from "./agent-execution.js";
360
- export type { AgentAccounting, AgentAttempt, AgentDefinition, AgentExecutionOptions, AgentExecutionResult, AgentExecutionRoot, AgentProgress, AgentToolCallProgress } from "./agent-execution.js";
555
+ export type { AgentAccounting, AgentAttempt, AgentBudgetHooks, AgentDefinition, AgentExecutionOptions, AgentExecutionResult, AgentExecutionRoot, AgentProgress, AgentSetup, AgentSetupContext, AgentSetupHook, AgentToolCallProgress, RegisteredAgentSetupHook, SessionInput } from "./agent-execution.js";
361
556
  export { doctor, doctorExitCode, formatDoctorReport } from "./doctor.js";
362
557
  export type { DoctorDiagnostic, DoctorOptions, DoctorPiState, DoctorReport, DoctorRole, DoctorSeverity, DoctorTrust, DoctorWorkflow } from "./doctor.js";