@marcoscale98/pi-extensible-workflows 5.17.0-fork.1 → 5.19.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.
Files changed (80) hide show
  1. package/CHANGELOG.md +68 -0
  2. package/dist/src/agent-execution.js +81 -39
  3. package/dist/src/background-widget.d.ts +5 -0
  4. package/dist/src/background-widget.js +1 -1
  5. package/dist/src/decoders.js +26 -0
  6. package/dist/src/execution.d.ts +4 -2
  7. package/dist/src/execution.js +43 -2
  8. package/dist/src/host-delivery.js +1 -0
  9. package/dist/src/host-recovery.d.ts +5 -2
  10. package/dist/src/host-recovery.js +3 -3
  11. package/dist/src/host-view.d.ts +5 -0
  12. package/dist/src/host-view.js +29 -5
  13. package/dist/src/host.d.ts +2 -2
  14. package/dist/src/host.js +190 -37
  15. package/dist/src/index.js +495 -93
  16. package/dist/src/index.js.map +3 -3
  17. package/dist/src/paths.d.ts +2 -0
  18. package/dist/src/paths.js +2 -0
  19. package/dist/src/registry.js +1 -1
  20. package/dist/src/roles.js +4 -3
  21. package/dist/src/script-tools.d.ts +27 -0
  22. package/dist/src/script-tools.js +87 -0
  23. package/dist/src/settings.d.ts +13 -0
  24. package/dist/src/settings.js +27 -3
  25. package/dist/src/trajectory.d.ts +12 -2
  26. package/dist/src/trajectory.js +30 -8
  27. package/dist/src/types.d.ts +30 -2
  28. package/dist/src/types.js +1 -1
  29. package/dist/src/utils.d.ts +19 -0
  30. package/dist/src/utils.js +30 -0
  31. package/dist/src/validation.d.ts +6 -1
  32. package/dist/src/validation.js +21 -1
  33. package/dist/starter/index.js +0 -2
  34. package/dist/starter/index.js.map +1 -1
  35. package/dist/starter/review-loop.d.ts +0 -13
  36. package/dist/starter/review-loop.js +0 -2
  37. package/dist/subagents/index.js +267 -159
  38. package/dist/subagents/index.js.map +2 -2
  39. package/dist/subagents/src/contracts.d.ts +45 -29
  40. package/dist/subagents/src/contracts.js +18 -7
  41. package/dist/subagents/src/index.d.ts +1 -1
  42. package/dist/subagents/src/index.js +13 -6
  43. package/dist/subagents/src/manager.js +3 -3
  44. package/dist/subagents/src/view.d.ts +1 -0
  45. package/dist/subagents/src/view.js +13 -52
  46. package/dist/trajectory/assets/index.html +27 -5
  47. package/dist/trajectory/index.js +76 -28
  48. package/dist/trajectory/index.js.map +2 -2
  49. package/dist/trajectory/src/assets/index.html +27 -5
  50. package/dist/trajectory/src/index.js +8 -18
  51. package/dist/trajectory/src/server.js +4 -2
  52. package/dist/trajectory/src/server.js.map +2 -2
  53. package/package.json +6 -6
  54. package/skills/pi-extensible-workflows/SKILL.md +2 -0
  55. package/src/agent-execution.ts +77 -41
  56. package/src/background-widget.ts +1 -1
  57. package/src/decoders.ts +20 -0
  58. package/src/execution.ts +41 -4
  59. package/src/host-delivery.ts +1 -0
  60. package/src/host-recovery.ts +8 -7
  61. package/src/host-view.ts +23 -5
  62. package/src/host.ts +173 -35
  63. package/src/paths.ts +3 -0
  64. package/src/registry.ts +1 -1
  65. package/src/roles.ts +4 -3
  66. package/src/script-tools.ts +76 -0
  67. package/src/settings.ts +22 -4
  68. package/src/trajectory.ts +30 -11
  69. package/src/types.ts +11 -4
  70. package/src/utils.ts +28 -0
  71. package/src/validation.ts +19 -1
  72. package/starter/review-loop.ts +0 -2
  73. package/subagents/README.md +2 -2
  74. package/subagents/src/contracts.ts +18 -7
  75. package/subagents/src/index.ts +16 -6
  76. package/subagents/src/manager.ts +3 -3
  77. package/subagents/src/view.ts +13 -50
  78. package/trajectory/src/assets/index.html +27 -5
  79. package/trajectory/src/index.ts +6 -9
  80. package/trajectory/src/server.ts +2 -2
package/dist/src/index.js CHANGED
@@ -42,6 +42,7 @@ var ERROR_CODES = [
42
42
  "AGENT_FAILED",
43
43
  "AGENT_RESULT_COLLECTED",
44
44
  "RESULT_INVALID",
45
+ "TOOL_FAILED",
45
46
  "CANCELLED",
46
47
  "WORKER_UNRESPONSIVE",
47
48
  "WORKTREE_FAILED",
@@ -135,6 +136,7 @@ function createToolTimingExtension(clock = Date.now) {
135
136
  }
136
137
 
137
138
  // src/utils.ts
139
+ import { createHash } from "node:crypto";
138
140
  import { Minimatch } from "minimatch";
139
141
  var SerialLane = class {
140
142
  #tail = Promise.resolve();
@@ -160,6 +162,14 @@ function isStringKey(key) {
160
162
  function stringKeyValue(value, key) {
161
163
  return object(value) ? value[key] : void 0;
162
164
  }
165
+ function sortedJson(value) {
166
+ if (Array.isArray(value)) return value.map(sortedJson);
167
+ if (value === null || typeof value !== "object") return value;
168
+ return Object.fromEntries(Object.keys(value).sort().map((key) => [key, sortedJson(value[key])]));
169
+ }
170
+ function jsonDigest(value) {
171
+ return createHash("sha256").update(JSON.stringify(sortedJson(value))).digest("hex").slice(0, 16);
172
+ }
163
173
  function jsonValue(value, seen = /* @__PURE__ */ new Set()) {
164
174
  if (value === null || typeof value === "boolean" || typeof value === "string") return true;
165
175
  if (typeof value === "number") return Number.isFinite(value);
@@ -320,6 +330,16 @@ function resolveModelReference(value, aliases = {}, knownModels, settingsPath) {
320
330
  };
321
331
  return resolveReference(value, []);
322
332
  }
333
+ function reachableTools(pi) {
334
+ const undeclared = pi.getAllTools?.().filter(({ exposure }) => exposure === "codemode" || exposure === "deferred").map(({ name }) => name) ?? [];
335
+ return [.../* @__PURE__ */ new Set([...pi.getActiveTools(), ...undeclared])];
336
+ }
337
+ var VIRTUAL_MODEL_PROVIDER = "workflow";
338
+ function physicalModel(spec, aliases = {}, knownModels, settingsPath) {
339
+ if (spec.provider !== VIRTUAL_MODEL_PROVIDER || !modelAliasName(spec.model, aliases)) return spec;
340
+ const target = resolveModelReference(spec.model, aliases, knownModels, settingsPath);
341
+ return spec.thinking === void 0 ? target : { ...target, thinking: spec.thinking };
342
+ }
323
343
  function modelCapability(value, aliases, knownModels, settingsPath) {
324
344
  const parsed = typeof value === "string" ? resolveModelReference(value, aliases, knownModels, settingsPath) : value;
325
345
  return `${parsed.provider}/${parsed.model}`;
@@ -378,7 +398,7 @@ function validateSchema(schema, at = "schema") {
378
398
  }
379
399
 
380
400
  // src/paths.ts
381
- import { createHash } from "node:crypto";
401
+ import { createHash as createHash2 } from "node:crypto";
382
402
  import { realpathSync } from "node:fs";
383
403
  import { readdir } from "node:fs/promises";
384
404
  import { homedir } from "node:os";
@@ -394,6 +414,9 @@ function canonicalPath(path) {
394
414
  return absolute;
395
415
  }
396
416
  }
417
+ function extensionIdentity(path) {
418
+ return path.startsWith("builtin:") ? path : canonicalPath(path);
419
+ }
397
420
  function sameFilesystemPath(left, right) {
398
421
  return canonicalPath(left) === canonicalPath(right);
399
422
  }
@@ -403,7 +426,7 @@ function workflowProjectSettingsPath(cwd) {
403
426
  function projectStorageKey(cwd) {
404
427
  const exact = resolve(cwd);
405
428
  const slug = safePart(basename(exact)) || "root";
406
- return `${slug}-${createHash("sha256").update(exact).digest("hex").slice(0, 12)}`;
429
+ return `${slug}-${createHash2("sha256").update(exact).digest("hex").slice(0, 12)}`;
407
430
  }
408
431
  function projectSessionsDirectory(cwd, home = homedir()) {
409
432
  return join(home, ".pi", "workflows", "projects", projectStorageKey(cwd), "sessions");
@@ -1201,6 +1224,22 @@ function decodeRunEvent(value) {
1201
1224
  if (timestamp === INVALID_PERSISTED_VALUE) return void 0;
1202
1225
  return { type: value.type, message: value.message, ...timestamp === void 0 ? {} : { timestamp } };
1203
1226
  }
1227
+ function decodeScriptCall(value) {
1228
+ if (!object(value) || value.kind !== "shell" && value.kind !== "tool" || typeof value.name !== "string" || typeof value.path !== "string" || !finiteNumber(value.startedAt)) return void 0;
1229
+ const shell = value.kind === "shell";
1230
+ if (shell && !jsonValue(value.input)) return void 0;
1231
+ const inputBytes = shell ? optionalNumber(value.inputBytes) : void 0;
1232
+ const phase = optionalString(value.phase);
1233
+ const finishedAt = optionalNumber(value.finishedAt);
1234
+ let error;
1235
+ if (value.error !== void 0) {
1236
+ if (shell) error = decodeWorkflowError(value.error);
1237
+ else if (object(value.error) && typeof value.error.code === "string") error = { code: isWorkflowErrorCode(value.error.code) ? value.error.code : "INTERNAL_ERROR" };
1238
+ if (!error) return void 0;
1239
+ }
1240
+ if (inputBytes === INVALID_PERSISTED_VALUE || phase === INVALID_PERSISTED_VALUE || finishedAt === INVALID_PERSISTED_VALUE) return void 0;
1241
+ return { kind: value.kind, name: value.name, path: value.path, ...shell && jsonValue(value.input) ? { input: value.input } : {}, ...inputBytes === void 0 ? {} : { inputBytes }, ...phase === void 0 ? {} : { phase }, startedAt: value.startedAt, ...finishedAt === void 0 ? {} : { finishedAt }, ...error === void 0 ? {} : { error } };
1242
+ }
1204
1243
  function decodeDelivery(value) {
1205
1244
  if (!object(value)) return void 0;
1206
1245
  const mode = value.mode;
@@ -1230,6 +1269,8 @@ function decodePersistedRun(value, allowLegacyAgentSessions = false) {
1230
1269
  const budgetEvents = value.budgetEvents === void 0 ? void 0 : decodeArray(value.budgetEvents, decodeBudgetEvent);
1231
1270
  const events = value.events === void 0 ? void 0 : decodeArray(value.events, decodeRunEvent);
1232
1271
  const delivery = value.delivery === void 0 ? void 0 : decodeDelivery(value.delivery);
1272
+ const scriptCalls = value.scriptCalls === void 0 ? void 0 : decodeArray(value.scriptCalls, decodeScriptCall);
1273
+ if (value.scriptCalls !== void 0 && !scriptCalls) return void 0;
1233
1274
  if (!agentSessions || !agents || parentRunId === INVALID_PERSISTED_VALUE || value.retry !== void 0 && !retry || phase === INVALID_PERSISTED_VALUE || value.phaseHistory !== void 0 && !phaseHistory || phaseHistoryIndex === INVALID_PERSISTED_VALUE || activeShells === INVALID_PERSISTED_VALUE || activeShellStartedAt === INVALID_PERSISTED_VALUE || value.activeShellsByPhase !== void 0 && !activeShellsByPhase || value.error !== void 0 && !error || failedAt === INVALID_PERSISTED_VALUE || value.budget !== void 0 && !budget || budgetVersion === INVALID_PERSISTED_VALUE || value.budgetVersion !== void 0 && !integer(budgetVersion) || value.usage !== void 0 && !usage || value.budgetEvents !== void 0 && !budgetEvents || value.events !== void 0 && !events || value.delivery !== void 0 && !delivery) return void 0;
1234
1275
  if (value.agentSessions === void 0 && !allowLegacyAgentSessions) return void 0;
1235
1276
  return {
@@ -1248,6 +1289,7 @@ function decodePersistedRun(value, allowLegacyAgentSessions = false) {
1248
1289
  ...activeShells === void 0 ? {} : { activeShells },
1249
1290
  ...activeShellStartedAt === void 0 ? {} : { activeShellStartedAt },
1250
1291
  ...activeShellsByPhase === void 0 ? {} : { activeShellsByPhase },
1292
+ ...scriptCalls === void 0 ? {} : { scriptCalls },
1251
1293
  ...error === void 0 ? {} : { error },
1252
1294
  ...failedAt === void 0 ? {} : { failedAt },
1253
1295
  ...budget === void 0 ? {} : { budget },
@@ -1527,7 +1569,7 @@ async function listRunIds(cwd, sessionId, home = homedir2(), cleanTemps = true)
1527
1569
 
1528
1570
  // src/store.ts
1529
1571
  import { execFile as execFile2 } from "node:child_process";
1530
- import { createHash as createHash2, randomUUID as randomUUID3 } from "node:crypto";
1572
+ import { createHash as createHash3, randomUUID as randomUUID3 } from "node:crypto";
1531
1573
  import { access, mkdir as mkdir2, readdir as readdir3, readFile as readFile3, realpath, rename as rename3, rm as rm3, stat as stat2, writeFile as writeFile2 } from "node:fs/promises";
1532
1574
  import { homedir as homedir3 } from "node:os";
1533
1575
  import { dirname, join as join3, relative, resolve as resolve2, sep } from "node:path";
@@ -1725,7 +1767,7 @@ var RunStore = class _RunStore {
1725
1767
  }
1726
1768
  }
1727
1769
  async appendSystemPromptV2(entry) {
1728
- const sha256 = createHash2("sha256").update(entry.prompt).digest("hex");
1770
+ const sha256 = createHash3("sha256").update(entry.prompt).digest("hex");
1729
1771
  const bodyPath = join3(systemPromptBodiesPath(this.directory), sha256);
1730
1772
  try {
1731
1773
  await access(bodyPath);
@@ -1778,7 +1820,7 @@ var RunStore = class _RunStore {
1778
1820
  const sha256 = record3.sha256;
1779
1821
  if (typeof sessionId !== "string" || !sessionId || !positiveInteger(attempt) || !positiveInteger(turn) || typeof sha256 !== "string" || !/^[0-9a-f]{64}$/.test(sha256)) throw new Error("Persisted system-prompt record is invalid");
1780
1822
  const prompt = await readFile3(join3(systemPromptBodiesPath(this.directory), sha256), "utf8");
1781
- if (createHash2("sha256").update(prompt).digest("hex") !== sha256) throw new Error("Persisted system-prompt body is invalid");
1823
+ if (createHash3("sha256").update(prompt).digest("hex") !== sha256) throw new Error("Persisted system-prompt body is invalid");
1782
1824
  entries.push({ sessionId, attempt, turn, sha256, prompt });
1783
1825
  }
1784
1826
  if (sequence < highest) throw new Error("Persisted system-prompt sequence is invalid");
@@ -1920,7 +1962,7 @@ var RunStore = class _RunStore {
1920
1962
  });
1921
1963
  }
1922
1964
  #worktreeKey(owner) {
1923
- return createHash2("sha256").update(`${this.sessionId}\0${this.runId}\0${owner}`).digest("hex").slice(0, 16);
1965
+ return createHash3("sha256").update(`${this.sessionId}\0${this.runId}\0${owner}`).digest("hex").slice(0, 16);
1924
1966
  }
1925
1967
  expectedWorktree(owner) {
1926
1968
  const key = this.#worktreeKey(owner);
@@ -2391,7 +2433,7 @@ function workflowSettingsPath(agentDir = getAgentDir()) {
2391
2433
  return join4(agentDir, ROLE_DIRECTORY, "settings.json");
2392
2434
  }
2393
2435
  function normalizedResourcePath(value, settingsPath) {
2394
- if (value === "*") return value;
2436
+ if (value === "*" || value.startsWith("builtin:")) return value;
2395
2437
  let expanded = value === "~" ? homedir4() : value.startsWith("~/") || value.startsWith("~\\") ? join4(homedir4(), value.slice(2)) : value;
2396
2438
  if (expanded.startsWith("file://")) expanded = fileURLToPath(expanded);
2397
2439
  const resolved = resolve3(dirname2(settingsPath), expanded);
@@ -2503,7 +2545,7 @@ function parseSettings(path, partial) {
2503
2545
  fail("CONFIG_ERROR", `Invalid workflow settings JSON at ${path}: ${errorText(error)}`);
2504
2546
  }
2505
2547
  if (!object(parsed)) fail("INVALID_SETTINGS", `Workflow settings at ${path} must be an object`);
2506
- const allowed = /* @__PURE__ */ new Set(["concurrency", "modelAliases", "skills", "extensions", "extensionSettings", "tools", "retention", "worktreePostCreateCommand", ...partial ? [] : ["backgroundWidget"]]);
2548
+ const allowed = /* @__PURE__ */ new Set(["concurrency", "modelAliases", "skills", "extensions", "extensionSettings", "tools", "retention", "worktreePostCreateCommand", ...partial ? [] : ["backgroundWidget", "codemodeTools"]]);
2507
2549
  const unknown = Object.keys(parsed).find((key) => !allowed.has(key));
2508
2550
  if (Object.prototype.hasOwnProperty.call(parsed, "disabledAgentResources")) fail("INVALID_SETTINGS", `disabledAgentResources is no longer supported; use skills, extensions, and tools selectors (settings: ${path})`);
2509
2551
  if (unknown) fail("INVALID_SETTINGS", `Unknown workflow setting at ${path}: ${unknown}`);
@@ -2511,6 +2553,8 @@ function parseSettings(path, partial) {
2511
2553
  if (concurrency !== void 0 && (!positiveInteger(concurrency) || concurrency > 16)) fail("INVALID_SETTINGS", `${path}.concurrency must be an integer from 1 to 16`);
2512
2554
  const backgroundWidget = parsed.backgroundWidget === void 0 ? partial ? void 0 : DEFAULT_SETTINGS.backgroundWidget : parsed.backgroundWidget;
2513
2555
  if (backgroundWidget !== void 0 && typeof backgroundWidget !== "boolean") fail("INVALID_SETTINGS", `${path}.backgroundWidget must be a boolean`);
2556
+ const codemodeTools = parsed.codemodeTools;
2557
+ if (codemodeTools !== void 0 && codemodeTools !== "all" && codemodeTools !== "read-only" && codemodeTools !== "none") fail("INVALID_SETTINGS", `${path}.codemodeTools must be "all", "read-only", or "none"`);
2514
2558
  const modelAliases = parsed.modelAliases === void 0 ? void 0 : validateModelAliases(parsed.modelAliases, path);
2515
2559
  const skills = validateSelectorList(parsed.skills, path, "skills");
2516
2560
  const tools = validateSelectorList(parsed.tools, path, "tools");
@@ -2521,6 +2565,7 @@ function parseSettings(path, partial) {
2521
2565
  return Object.freeze({
2522
2566
  ...concurrency === void 0 ? {} : { concurrency },
2523
2567
  ...backgroundWidget === void 0 ? {} : { backgroundWidget },
2568
+ ...codemodeTools === void 0 ? {} : { codemodeTools },
2524
2569
  ...modelAliases === void 0 ? {} : { modelAliases },
2525
2570
  ...skills === void 0 ? {} : { skills },
2526
2571
  ...extensions === void 0 ? {} : { extensions },
@@ -2533,6 +2578,18 @@ function parseSettings(path, partial) {
2533
2578
  function loadSettings(path = workflowSettingsPath()) {
2534
2579
  return parseSettings(path, false);
2535
2580
  }
2581
+ var READ_ONLY_WORKFLOW_TOOLS = /* @__PURE__ */ new Set(["workflow_status", "workflow_catalog", "subagents_inspect"]);
2582
+ function workflowToolExposure(name, codemodeTools) {
2583
+ const readOnly = READ_ONLY_WORKFLOW_TOOLS.has(name);
2584
+ return { ...codemodeTools === "none" || codemodeTools === "read-only" && !readOnly ? { exposure: "model-only" } : {}, ...readOnly ? { annotations: { readOnlyHint: true } } : {} };
2585
+ }
2586
+ function loadCodemodeToolsSetting(agentDir) {
2587
+ try {
2588
+ return loadSettings(workflowSettingsPath(agentDir)).codemodeTools;
2589
+ } catch {
2590
+ return void 0;
2591
+ }
2592
+ }
2536
2593
  function loadSettingsOverrides(path) {
2537
2594
  return parseSettings(path, true);
2538
2595
  }
@@ -2607,7 +2664,7 @@ function saveModelAliases(path = workflowSettingsPath(), aliases = {}) {
2607
2664
  }
2608
2665
 
2609
2666
  // src/registry.ts
2610
- var RESERVED_GLOBALS = /* @__PURE__ */ new Set(["agent", "shell", "prompt", "checkpoint", "parallel", "pipeline", "phase", "withWorktree", "log", "args", "Promise", "JSON", "Math", "Date", "eval", "Function", "WebAssembly", "process", "require", "module", "exports", "console", "fetch", "XMLHttpRequest", "WebSocket", "performance", "crypto", "setTimeout", "setInterval", "setImmediate", "queueMicrotask", "Intl", "SharedArrayBuffer", "Atomics", "globalThis", "global", "undefined", "NaN", "Infinity", "extensions", "workflow_catalog"]);
2667
+ var RESERVED_GLOBALS = /* @__PURE__ */ new Set(["agent", "shell", "tools", "prompt", "checkpoint", "parallel", "pipeline", "phase", "withWorktree", "log", "args", "Promise", "JSON", "Math", "Date", "eval", "Function", "WebAssembly", "process", "require", "module", "exports", "console", "fetch", "XMLHttpRequest", "WebSocket", "performance", "crypto", "setTimeout", "setInterval", "setImmediate", "queueMicrotask", "Intl", "SharedArrayBuffer", "Atomics", "globalThis", "global", "undefined", "NaN", "Infinity", "extensions", "workflow_catalog"]);
2611
2668
  var IDENTIFIER = /^[A-Za-z_$][\w$]*$/;
2612
2669
  var SEMVER = /^\d+\.\d+\.\d+(?:-[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?(?:\+[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?$/;
2613
2670
  var PACKAGE_NAME = /^(?:@[a-z0-9][a-z0-9._-]*\/)?[a-z0-9][a-z0-9._-]*$/;
@@ -3127,7 +3184,7 @@ function loadAgentDefinitions(cwd, agentDir = getAgentDir2(), projectTrusted2 =
3127
3184
  function canonicalExtensionSelector(selector, base = process.cwd()) {
3128
3185
  const negated = selector.startsWith("!");
3129
3186
  const body = negated ? selector.slice(1) : selector;
3130
- if (body === "*" || body === "**" || body.startsWith("**/")) return selector;
3187
+ if (body === "*" || body === "**" || body.startsWith("**/") || body.startsWith("builtin:")) return selector;
3131
3188
  const resolved = resolve4(base, body);
3132
3189
  if (resourcePatternHasMagic(body)) return `${negated ? "!" : ""}${resolved}`;
3133
3190
  return `${negated ? "!" : ""}${canonicalPath(resolved)}`;
@@ -3188,7 +3245,7 @@ function resolveRole(name, options) {
3188
3245
  if (outsideRootTool) fail("UNKNOWN_TOOL", `Tool is outside the launching session boundary: ${outsideRootTool}`);
3189
3246
  }
3190
3247
  const selectedSkills = resources.skills === void 0 ? void 0 : selectResourcesByLayers(selectorLayers.skills, resources.skills);
3191
- const extensionResources = resources.extensions?.map((extension) => canonicalPath(extension));
3248
+ const extensionResources = resources.extensions?.map((extension) => extensionIdentity(extension));
3192
3249
  const selectedExtensions = extensionResources === void 0 ? void 0 : selectResourcesByLayers(selectorLayers.extensions, extensionResources);
3193
3250
  const unmatchedSkills = resources.skills === void 0 ? void 0 : unmatchedResourcePatterns(selectorLayers.skills.flatMap((layer) => layer ?? []), resources.skills);
3194
3251
  const unmatchedExtensions = extensionResources === void 0 ? void 0 : unmatchedResourcePatterns(selectorLayers.extensions.flatMap((layer) => layer ?? []), extensionResources);
@@ -3408,6 +3465,19 @@ function callHasTrailingComma(source, call) {
3408
3465
  }
3409
3466
  return current?.type.label === ")" && previous?.type.label === ",";
3410
3467
  }
3468
+ function scriptToolReferences(script) {
3469
+ const body = workflowBody(script);
3470
+ if (!body.trim()) return [];
3471
+ const names = /* @__PURE__ */ new Set();
3472
+ const opaque = (node, parent) => {
3473
+ if (node.type === "Identifier" && node.name === "tools") {
3474
+ if (parent?.type === "MemberExpression" && parent.object === node && !parent.computed && parent.property.type === "Identifier") names.add(parent.property.name);
3475
+ else if (!(parent?.type === "Property" && parent.key === node && !parent.computed && !parent.shorthand || parent?.type === "MemberExpression" && parent.property === node && !parent.computed)) return true;
3476
+ }
3477
+ return astChildren(node).some((child) => opaque(child, node));
3478
+ };
3479
+ return opaque(parseWorkflow(body)) ? void 0 : [...names];
3480
+ }
3411
3481
  function instrumentWorkflow(script) {
3412
3482
  const body = workflowBody(script);
3413
3483
  if (!body.trim()) return body;
@@ -4013,6 +4083,30 @@ const functions = Object.freeze(Object.fromEntries(Object.entries(config.functio
4013
4083
  Object.defineProperty(result, "toJSON", { value() { throw workError("INVALID_METADATA", "Workflow function result is a Promise; await it before serialization"); } });
4014
4084
  return result;
4015
4085
  }])));
4086
+ // tools.<id>(args) runs a Pi session tool through the host. Any identifier resolves, so a resumed run
4087
+ // replays journaled calls even when the tool is gone; the host rejects identifiers it cannot call.
4088
+ // A call is the n-th call with the same arguments in its scope, so replay does not depend on the
4089
+ // order in which concurrent calls start.
4090
+ const NOT_TOOLS = new Set(["then", "toJSON", "toString", "valueOf", "constructor"]);
4091
+ 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;
4092
+ const toolOccurrences = new Map();
4093
+ const toolCall = id => (...values) => {
4094
+ if (worktreeOwners.getStore()) throw workError("INVALID_METADATA", "tools." + id + " is unavailable inside withWorktree: session tools resolve paths against the session working directory");
4095
+ 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");
4096
+ const args = values[0] === undefined ? {} : values[0];
4097
+ const structuralPath = inheritedAgentPath.getStore() || [];
4098
+ const key = JSON.stringify([structuralPath, id, sortedJson(args)]);
4099
+ const occurrence = (toolOccurrences.get(key) || 0) + 1;
4100
+ toolOccurrences.set(key, occurrence);
4101
+ return guardedResult(rpc("tool", [id, args, { structuralPath: [...structuralPath], occurrence }]), "tool");
4102
+ };
4103
+ const tools = new Proxy(Object.create(null), {
4104
+ get: (_target, key) => typeof key === "string" && !NOT_TOOLS.has(key) ? toolCall(key) : undefined,
4105
+ // Probing would branch on the session that resumed the run; the launch already checks named tools.
4106
+ has: () => { throw workError("INVALID_METADATA", "Workflow scripts cannot probe tools with 'in'; call tools.<name>(...) directly, and the launch checks that the tool exists"); },
4107
+ set: () => false,
4108
+ defineProperty: () => false,
4109
+ });
4016
4110
  const recordEntries = (value, kind) => {
4017
4111
  if (!value || typeof value !== "object" || Array.isArray(value)) throw workError("INVALID_METADATA", kind + " must be a record");
4018
4112
  return Object.entries(value);
@@ -4073,7 +4167,7 @@ const pipeline = async (operationName, items, stages) => {
4073
4167
  return Object.fromEntries(results.map(result => [result.name, result.value]));
4074
4168
  };
4075
4169
  const safeMath = Object.fromEntries(Object.getOwnPropertyNames(Math).filter(name => name !== "random").map(name => [name, Math[name]]));
4076
- const sandbox = { agent, shell, withWorktree: rejectWorktree, prompt, checkpoint, parallel, pipeline, phase, log, args: config.args, Promise, JSON, Math: Object.freeze(safeMath) };
4170
+ const sandbox = { agent, shell, withWorktree: rejectWorktree, prompt, checkpoint, parallel, pipeline, phase, log, tools, args: config.args, Promise, JSON, Math: Object.freeze(safeMath) };
4077
4171
  for (const [name, fn] of Object.entries(functions)) Object.defineProperty(sandbox, name, { value: fn, writable: false, configurable: false });
4078
4172
  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;
4079
4173
  const context = vm.createContext(sandbox, { codeGeneration: { strings: false, wasm: false } });
@@ -4117,6 +4211,13 @@ function readShellIdentity(value) {
4117
4211
  const identity = readAgentIdentity(value);
4118
4212
  return { structuralPath: identity.structuralPath, callSite: identity.callSite, occurrence: identity.occurrence, ...identity.worktreeOwner ? { worktreeOwner: identity.worktreeOwner } : {} };
4119
4213
  }
4214
+ function readToolIdentity(value) {
4215
+ if (!object(value) || !Array.isArray(value.structuralPath) || !value.structuralPath.every((part) => typeof part === "string" && Boolean(part.trim())) || !positiveInteger(value.occurrence)) fail("INTERNAL_ERROR", "Invalid workflow tool identity");
4216
+ return { structuralPath: [...value.structuralPath], occurrence: value.occurrence };
4217
+ }
4218
+ function toolIdentityPath(identifier, args, identity) {
4219
+ return structuralPath("tool", ...identity.structuralPath, identifier, `args:${jsonDigest(args)}`, String(identity.occurrence));
4220
+ }
4120
4221
  function agentHandleTurnPath(handle, turn) {
4121
4222
  return structuralPath("agent", "handle", handle, `turn:${String(turn)}`);
4122
4223
  }
@@ -4409,6 +4510,11 @@ function runWorkflow(script, args = null, bridge = {}, signal) {
4409
4510
  if (!OUTCOME_ERRORS.has(typed.code)) throw typed;
4410
4511
  value = branded({ name, ok: false, failedAt: name, error: workerErrorShape(typed) });
4411
4512
  }
4513
+ } else if (method === "tool") {
4514
+ if (typeof values[0] !== "string" || !object(values[1])) fail("INTERNAL_ERROR", "tool requires an identifier and object arguments");
4515
+ const identity = readToolIdentity(values[2]);
4516
+ if (!bridge.tool) fail("UNKNOWN_TOOL", `tools.${values[0]} is unavailable: this run has no session tools`);
4517
+ value = await bridge.tool(values[0], values[1], controller.signal, identity);
4412
4518
  } else if (method === "worktree") {
4413
4519
  if (!bridge.worktree || typeof values[0] !== "string" || !values[0]) fail("INTERNAL_ERROR", "worktree requires an active host bridge and scope");
4414
4520
  value = await bridge.worktree(values[0], controller.signal);
@@ -4480,7 +4586,7 @@ import { basename as basename4, dirname as dirname5, join as join8, resolve as r
4480
4586
  import { fileURLToPath as fileURLToPath3 } from "node:url";
4481
4587
  import { Type } from "@earendil-works/pi-ai";
4482
4588
  import { Compile } from "typebox/compile";
4483
- import { createAgentSession, DefaultPackageManager, DefaultResourceLoader, defineTool, getAgentDir as getAgentDir3, ModelRuntime, SessionManager, SettingsManager } from "@earendil-works/pi-coding-agent";
4589
+ import { createAgentSession, createCodemodeExtension, createMcpExtension, createToolSearchExtension, DefaultPackageManager, DefaultResourceLoader, defineTool, getAgentDir as getAgentDir3, ModelRuntime, SessionManager, SettingsManager } from "@earendil-works/pi-coding-agent";
4484
4590
 
4485
4591
  // src/pi-runtime-adapter.ts
4486
4592
  var TURN_START_EVENTS = /* @__PURE__ */ new Set(["turn_start", "turn_started", "turnStarted"]);
@@ -5319,16 +5425,25 @@ function parseModel(value, fallback, aliases = {}, knownModels, settingsPath) {
5319
5425
  function accounting(stats) {
5320
5426
  return { input: stats.tokens.input, output: stats.tokens.output, cacheRead: stats.tokens.cacheRead, cacheWrite: stats.tokens.cacheWrite, cost: stats.cost };
5321
5427
  }
5322
- var extensionDirectory = dirname5(fileURLToPath3(import.meta.url));
5323
- var workflowPackageRoot = basename4(dirname5(extensionDirectory)) === "dist" ? resolve6(extensionDirectory, "../..") : resolve6(extensionDirectory, "..");
5324
- var WORKFLOW_HOST_ENTRIES = /* @__PURE__ */ new Set([
5325
- canonicalPath(resolve6(workflowPackageRoot, "src/index.ts")),
5326
- canonicalPath(resolve6(workflowPackageRoot, "dist/src/index.js")),
5327
- canonicalPath(resolve6(workflowPackageRoot, "starter/index.ts")),
5328
- canonicalPath(resolve6(workflowPackageRoot, "dist/starter/index.js")),
5329
- canonicalPath(resolve6(workflowPackageRoot, "subagents/index.ts")),
5330
- canonicalPath(resolve6(workflowPackageRoot, "dist/subagents/index.js"))
5331
- ]);
5428
+ var WORKFLOW_HOST_ENTRY_PATHS = ["src/index.ts", "dist/src/index.js", "starter/index.ts", "dist/starter/index.js", "subagents/index.ts", "dist/subagents/index.js"];
5429
+ function isWorkflowHostEntry(path) {
5430
+ const entryPath = canonicalPath(path);
5431
+ let packageRoot2 = dirname5(entryPath);
5432
+ while (packageRoot2 !== dirname5(packageRoot2)) {
5433
+ const packageMetadataPath = join8(packageRoot2, "package.json");
5434
+ if (existsSync(packageMetadataPath)) {
5435
+ try {
5436
+ const metadata = JSON.parse(readFileSync5(packageMetadataPath, "utf8"));
5437
+ if (typeof metadata !== "object" || metadata === null || !("name" in metadata) || !isCorePackageName(metadata.name)) return false;
5438
+ return WORKFLOW_HOST_ENTRY_PATHS.some((entry) => canonicalPath(join8(packageRoot2, entry)) === entryPath);
5439
+ } catch {
5440
+ return false;
5441
+ }
5442
+ }
5443
+ packageRoot2 = dirname5(packageRoot2);
5444
+ }
5445
+ return false;
5446
+ }
5332
5447
  var WORKFLOW_DIRECTORY = "pi-extensible-workflows";
5333
5448
  function workflowSystemPromptPath(cwd, agentDir, projectTrusted2) {
5334
5449
  const projectPath = join8(cwd, ".pi", WORKFLOW_DIRECTORY, "SYSTEM.md");
@@ -5361,33 +5476,40 @@ function loadPiPackageDirectory() {
5361
5476
  }
5362
5477
  }).catch((error) => ({ error: `could not load @earendil-works/pi-coding-agent package metadata: ${errorText(error)}` }));
5363
5478
  }
5364
- var nativePromptTemplateModule;
5365
- async function loadNativePromptTemplateModule() {
5479
+ var nativeCoreModules = /* @__PURE__ */ new Map();
5480
+ async function loadNativeCoreModule(file, label) {
5366
5481
  const packageDirectory = await loadPiPackageDirectory();
5367
- let promptTemplatePath;
5482
+ let modulePath;
5368
5483
  if (packageDirectory.directory) {
5369
- const candidate = resolve6(packageDirectory.directory, "dist/core/prompt-templates.js");
5370
- if (existsSync(candidate)) promptTemplatePath = candidate;
5484
+ const candidate = resolve6(packageDirectory.directory, "dist/core", file);
5485
+ if (existsSync(candidate)) modulePath = candidate;
5371
5486
  }
5372
- promptTemplatePath ??= resolve6(dirname5(fileURLToPath3(import.meta.resolve("@earendil-works/pi-coding-agent"))), "core/prompt-templates.js");
5487
+ modulePath ??= resolve6(dirname5(fileURLToPath3(import.meta.resolve("@earendil-works/pi-coding-agent"))), "core", file);
5373
5488
  const packageDirectoryError = packageDirectory.error ? ` Package directory lookup failed: ${packageDirectory.error}` : "";
5374
- return nativePromptTemplateModule ??= import(promptTemplatePath).then((module) => module).catch((error) => {
5375
- throw new Error(`Could not load Pi prompt templates.${packageDirectoryError}`, { cause: error });
5376
- });
5489
+ let loaded = nativeCoreModules.get(file);
5490
+ if (!loaded) {
5491
+ loaded = import(modulePath).catch((error) => {
5492
+ throw new Error(`Could not load Pi ${label}.${packageDirectoryError}`, { cause: error });
5493
+ });
5494
+ nativeCoreModules.set(file, loaded);
5495
+ }
5496
+ return loaded;
5377
5497
  }
5378
5498
  async function expandPromptTemplateForInspection(text, templates) {
5379
- return (await loadNativePromptTemplateModule()).expandPromptTemplate(text, templates);
5499
+ return (await loadNativeCoreModule("prompt-templates.js", "prompt templates")).expandPromptTemplate(text, templates);
5500
+ }
5501
+ async function buildSystemPromptForInspection(options) {
5502
+ return (await loadNativeCoreModule("system-prompt.js", "system prompt builder")).buildSystemPrompt(options);
5380
5503
  }
5381
5504
  async function preparePiPrompt(native, text) {
5382
5505
  const session = native;
5383
5506
  const diagnostics = [];
5384
5507
  const runner = session._extensionRunner;
5385
- const baseSystemPrompt = typeof session._baseSystemPrompt === "string" ? session._baseSystemPrompt : session.systemPrompt ?? "";
5508
+ const baseSystemPrompt = session.systemPrompt ?? "";
5386
5509
  const baseOptions = session._baseSystemPromptOptions;
5387
5510
  const expandSkillCommand = session._expandSkillCommand;
5388
5511
  const templates = session.promptTemplates;
5389
5512
  if (!runner) diagnostics.push({ type: "error", message: "Pi prompt inspection seam is unavailable: extension runner is missing", source: "Pi session" });
5390
- if (typeof session._baseSystemPrompt !== "string") diagnostics.push({ type: "error", message: "Pi prompt inspection seam is unavailable: base system prompt is missing", source: "Pi session" });
5391
5513
  if (baseOptions === void 0) diagnostics.push({ type: "error", message: "Pi prompt inspection seam is unavailable: base system prompt options are missing", source: "Pi session" });
5392
5514
  if (typeof expandSkillCommand !== "function") diagnostics.push({ type: "error", message: "Pi prompt inspection seam is unavailable: skill expansion is missing", source: "Pi session" });
5393
5515
  if (!Array.isArray(templates)) diagnostics.push({ type: "error", message: "Pi prompt inspection seam is unavailable: prompt templates are missing", source: "Pi session" });
@@ -5412,9 +5534,11 @@ async function preparePiPrompt(native, text) {
5412
5534
  diagnostics.push({ type: "error", message: `Pi prompt template expansion is unavailable: ${errorText(error)}`, source: "Pi session" });
5413
5535
  }
5414
5536
  }
5415
- const result = !inputHandled && runner && baseOptions !== void 0 ? await runner.emitBeforeAgentStart(expandedPrompt, void 0, baseSystemPrompt, baseOptions) : void 0;
5537
+ const result = !inputHandled && runner && baseOptions !== void 0 ? await runner.emitBeforeAgentStart(expandedPrompt, void 0, baseOptions) : void 0;
5416
5538
  const prepared = result;
5417
- return { prompt: text, expandedPrompt, systemPrompt: typeof prepared?.systemPrompt === "string" ? prepared.systemPrompt : baseSystemPrompt, systemPromptOptions: baseOptions, messages: prepared?.messages ?? [], inputHandled, diagnostics };
5539
+ const systemPromptOptions = prepared?.systemPromptOptions ?? baseOptions;
5540
+ const systemPrompt = prepared?.systemPromptOptions === void 0 ? baseSystemPrompt : await buildSystemPromptForInspection(prepared.systemPromptOptions);
5541
+ return { prompt: text, expandedPrompt, systemPrompt, systemPromptOptions, messages: prepared?.messages ?? [], inputHandled, diagnostics };
5418
5542
  } finally {
5419
5543
  unsubscribe?.();
5420
5544
  }
@@ -5443,6 +5567,17 @@ function flushExtensionProviders(resourceLoader, modelRuntime) {
5443
5567
  runtime.pendingNativeProviderRegistrations = [];
5444
5568
  return failures;
5445
5569
  }
5570
+ function builtinExtensions() {
5571
+ return [
5572
+ { name: "codemode", factory: createCodemodeExtension(), replaceable: true, builtin: true },
5573
+ { name: "tool-search", factory: createToolSearchExtension(), replaceable: true, builtin: true },
5574
+ { name: "mcp", factory: createMcpExtension(), replaceable: true, builtin: true }
5575
+ ];
5576
+ }
5577
+ var BUILTIN_EXTENSION_PREFIX = "builtin:";
5578
+ function isLlamaModel(model) {
5579
+ return model.provider === "llama.cpp";
5580
+ }
5446
5581
  async function createLocalPiSessionHandle(input, sessionStartEvent) {
5447
5582
  const agentDir = input.agentDir ?? getAgentDir3();
5448
5583
  const systemPromptSource = workflowSystemPromptPath(input.cwd, agentDir, input.resourcePolicy?.projectTrusted ?? true);
@@ -5455,13 +5590,16 @@ async function createLocalPiSessionHandle(input, sessionStartEvent) {
5455
5590
  const tools = [.../* @__PURE__ */ new Set([...input.tools, ...customTools.map(({ name }) => name)])];
5456
5591
  let settingsManager;
5457
5592
  let resourceLoader;
5593
+ const builtins = builtinExtensions();
5594
+ const builtinNames = builtins.flatMap((extension) => typeof extension === "function" ? [] : [extension.name]);
5595
+ const extensionFactories = [...builtins, ...input.extensionFactories ?? []];
5458
5596
  const policy = input.resourcePolicy;
5459
5597
  if (policy) {
5460
5598
  settingsManager = SettingsManager.create(input.cwd, agentDir, { projectTrusted: false });
5461
5599
  settingsManager.setProjectTrusted(policy.projectTrusted);
5462
- const packageManager = new DefaultPackageManager({ cwd: input.cwd, agentDir, settingsManager });
5600
+ const packageManager = new DefaultPackageManager({ cwd: input.cwd, agentDir, settingsManager, builtinExtensions: builtinNames });
5463
5601
  const resolved = await packageManager.resolve();
5464
- const discoveredExtensions = [...new Set(resolved.extensions.filter(({ enabled, metadata }) => enabled && (policy.projectTrusted || metadata.scope !== "project")).map(({ path }) => canonicalPath(path)))];
5602
+ const discoveredExtensions = [...new Set(resolved.extensions.filter(({ enabled, metadata }) => enabled && (policy.projectTrusted || metadata.scope !== "project")).map(({ path }) => extensionIdentity(path)))];
5465
5603
  const selectorSources = policy.selectorSources;
5466
5604
  const selection = resolveRole(void 0, {
5467
5605
  cwd: input.cwd,
@@ -5469,7 +5607,7 @@ async function createLocalPiSessionHandle(input, sessionStartEvent) {
5469
5607
  resources: { extensions: discoveredExtensions }
5470
5608
  });
5471
5609
  const selectedExtensions = selection.selectedExtensions ?? [];
5472
- const extensionPaths = selectedExtensions.filter((path) => !WORKFLOW_HOST_ENTRIES.has(path));
5610
+ const extensionPaths = selectedExtensions.filter((path) => path.startsWith(BUILTIN_EXTENSION_PREFIX) || !isWorkflowHostEntry(path));
5473
5611
  policy.selectedExtensions = selectedExtensions;
5474
5612
  policy.unmatchedExtensions = selection.unmatchedExtensions ?? [];
5475
5613
  const skillPaths = [...new Set(resolved.skills.filter(({ enabled, metadata }) => enabled && (policy.projectTrusted || metadata.scope !== "project")).map(({ path }) => path))];
@@ -5489,7 +5627,7 @@ async function createLocalPiSessionHandle(input, sessionStartEvent) {
5489
5627
  additionalExtensionPaths: extensionPaths,
5490
5628
  noSkills: true,
5491
5629
  additionalSkillPaths: [.../* @__PURE__ */ new Set([...skillPaths, ...input.additionalSkillPaths ?? []])],
5492
- ...input.extensionFactories?.length ? { extensionFactories: input.extensionFactories } : {},
5630
+ extensionFactories,
5493
5631
  ...contextFilesOverride ? { agentsFilesOverride: contextFilesOverride } : {},
5494
5632
  skillsOverride: (base) => {
5495
5633
  const disabledSkills = updateSkillMatches(base.skills);
@@ -5501,17 +5639,17 @@ async function createLocalPiSessionHandle(input, sessionStartEvent) {
5501
5639
  await resourceLoader.reload();
5502
5640
  } else {
5503
5641
  settingsManager = SettingsManager.create(input.cwd, agentDir, { projectTrusted: true });
5504
- const packageManager = new DefaultPackageManager({ cwd: input.cwd, agentDir, settingsManager });
5642
+ const packageManager = new DefaultPackageManager({ cwd: input.cwd, agentDir, settingsManager, builtinExtensions: builtinNames });
5505
5643
  const resolved = await packageManager.resolve();
5506
- const extensionPaths = [...new Set(resolved.extensions.filter(({ enabled }) => enabled).map(({ path }) => canonicalPath(path)).filter((path) => !WORKFLOW_HOST_ENTRIES.has(path)))];
5507
- resourceLoader = new DefaultResourceLoader({ cwd: input.cwd, agentDir, settingsManager, noExtensions: true, additionalExtensionPaths: extensionPaths, ...input.additionalSkillPaths?.length ? { additionalSkillPaths: [...input.additionalSkillPaths] } : {}, ...input.extensionFactories?.length ? { extensionFactories: input.extensionFactories } : {}, ...contextFilesOverride ? { agentsFilesOverride: contextFilesOverride } : {}, ...systemPromptOptions, ...input.systemPromptAppend ? { appendSystemPromptOverride: (base) => [...base, input.systemPromptAppend ?? ""] } : {} });
5644
+ const extensionPaths = [...new Set(resolved.extensions.filter(({ enabled }) => enabled).map(({ path }) => extensionIdentity(path)).filter((path) => path.startsWith(BUILTIN_EXTENSION_PREFIX) || !isWorkflowHostEntry(path)))];
5645
+ resourceLoader = new DefaultResourceLoader({ cwd: input.cwd, agentDir, settingsManager, noExtensions: true, additionalExtensionPaths: extensionPaths, ...input.additionalSkillPaths?.length ? { additionalSkillPaths: [...input.additionalSkillPaths] } : {}, extensionFactories, ...contextFilesOverride ? { agentsFilesOverride: contextFilesOverride } : {}, ...systemPromptOptions, ...input.systemPromptAppend ? { appendSystemPromptOverride: (base) => [...base, input.systemPromptAppend ?? ""] } : {} });
5508
5646
  await resourceLoader.reload();
5509
5647
  }
5510
5648
  const providerFailures = flushExtensionProviders(resourceLoader, modelRuntime);
5511
5649
  await modelRuntime.refresh({ allowNetwork: false });
5512
5650
  const model = modelRuntime.getModel(input.model.provider, input.model.model);
5513
5651
  if (!model) {
5514
- const because = providerFailures.length > 0 ? ` (provider registration failed \u2014 ${providerFailures.join("; ")})` : "";
5652
+ const because = providerFailures.length > 0 ? ` (provider registration failed \u2014 ${providerFailures.join("; ")})` : isLlamaModel(input.model) ? " (Pi does not expose its built-in llama.cpp provider to extension sessions; declare the llama.cpp server as a provider in models.json and use that model instead)" : "";
5515
5653
  throw new WorkflowError("UNKNOWN_MODEL", `Unknown model: ${input.model.provider}/${input.model.model}${because}`);
5516
5654
  }
5517
5655
  const effectiveSessionStartEvent = input.settings === void 0 && sessionStartEvent === void 0 ? void 0 : { ...sessionStartEvent ?? { type: "session_start", reason: "startup" }, settings: input.settings ?? Object.freeze({}) };
@@ -5536,13 +5674,13 @@ async function createLocalPiSessionHandle(input, sessionStartEvent) {
5536
5674
  await shutdown("quit").catch(() => void 0);
5537
5675
  throw error;
5538
5676
  }
5539
- const resourcePaths = { extensions: resourceLoader.getExtensions().extensions.filter(({ path }) => !path.startsWith("<")).map(({ resolvedPath }) => canonicalPath(resolvedPath)), skills: resourceLoader.getSkills().skills.map(({ filePath }) => canonicalPath(filePath)) };
5677
+ const resourcePaths = { extensions: resourceLoader.getExtensions().extensions.filter(({ path }) => !path.startsWith("<")).map(({ resolvedPath }) => extensionIdentity(resolvedPath)), skills: resourceLoader.getSkills().skills.map(({ filePath }) => canonicalPath(filePath)) };
5540
5678
  const resourceInspection = () => {
5541
5679
  const extensions = resourceLoader.getExtensions();
5542
5680
  const skills = resourceLoader.getSkills();
5543
5681
  const diagnostics = [...extensions.errors.map(({ path, error }) => ({ type: "error", message: error, source: path })), ...skills.diagnostics.map(({ type, path, message }) => ({ type, message, ...path ? { source: path } : {} }))];
5544
5682
  const systemSource = systemPromptSource;
5545
- return { extensions: resourceLoader.getExtensions().extensions.filter(({ path }) => !path.startsWith("<")).map(({ resolvedPath }) => canonicalPath(resolvedPath)), skills: skills.skills.map(({ name }) => name), diagnostics, ...systemSource ? { systemPromptSource: systemSource } : resourceLoader.getSystemPrompt() !== void 0 ? { systemPromptSource: "Pi resource loader" } : {} };
5683
+ return { extensions: resourceLoader.getExtensions().extensions.filter(({ path }) => !path.startsWith("<")).map(({ resolvedPath }) => extensionIdentity(resolvedPath)), skills: skills.skills.map(({ name }) => name), diagnostics, ...systemSource ? { systemPromptSource: systemSource } : resourceLoader.getSystemPrompt() !== void 0 ? { systemPromptSource: "Pi resource loader" } : {} };
5546
5684
  };
5547
5685
  const managedSession = Object.assign(session, {
5548
5686
  getLeafId: () => manager.getLeafId(),
@@ -6204,7 +6342,7 @@ var WorkflowAgentExecutor = class {
6204
6342
  ...options.contextFiles === void 0 ? {} : { contextFiles: options.contextFiles },
6205
6343
  ...options.effectiveTools === void 0 ? {} : { effectiveTools: options.effectiveTools }
6206
6344
  });
6207
- return { model: resolved.model ?? this.root.model, ...resolved.requestedModel === void 0 ? {} : { requestedModel: resolved.requestedModel }, tools: resolved.tools ?? [], ...resolved.overrideSystemPrompt ? { systemPrompt: resolved.prompt } : {}, systemPromptAppend: resolved.overrideSystemPrompt ? "" : resolved.prompt, ...resolved.contextFiles === void 0 ? {} : { contextFiles: resolved.contextFiles } };
6345
+ return { model: physicalModel(resolved.model ?? this.root.model, this.root.modelAliases, this.root.knownModels, this.root.settingsPath), ...resolved.requestedModel === void 0 ? {} : { requestedModel: resolved.requestedModel }, tools: resolved.tools ?? [], ...resolved.overrideSystemPrompt ? { systemPrompt: resolved.prompt } : {}, systemPromptAppend: resolved.overrideSystemPrompt ? "" : resolved.prompt, ...resolved.contextFiles === void 0 ? {} : { contextFiles: resolved.contextFiles } };
6208
6346
  }
6209
6347
  async execute(task, options, signal, customTools = [], setSteer, beforeRetry) {
6210
6348
  const executionSignal = signal ?? this.root.runContext?.signal;
@@ -6633,6 +6771,7 @@ var FairAgentScheduler = class {
6633
6771
  label: "Child Agent",
6634
6772
  description: "Start a direct child agent",
6635
6773
  parameters: Type.Object({ prompt: Type.String(), label: Type.String(), tools: Type.Optional(Type.Array(Type.String())), skills: Type.Optional(Type.Array(Type.String())), extensions: Type.Optional(Type.Array(Type.String())), model: Type.Optional(Type.String()), role: Type.Optional(Type.String()), contextFiles: Type.Optional(Type.Array(Type.String())), outputSchema: Type.Optional(Type.Record(Type.String(), Type.Unknown())), retries: Type.Optional(Type.Integer({ minimum: 0 })), timeoutMs: Type.Optional(Type.Union([Type.Integer({ minimum: 1 }), Type.Null()])) }, { additionalProperties: true }),
6774
+ outputSchema: Type.Object({ id: Type.String() }),
6636
6775
  execute: async (_id, params) => {
6637
6776
  if (!isChildAgentToolParams(params)) throw new WorkflowError("INVALID_METADATA", "Invalid child agent parameters");
6638
6777
  validateAgentOptions(params);
@@ -6643,7 +6782,7 @@ var FairAgentScheduler = class {
6643
6782
  Reflect.deleteProperty(agentOptions, "prompt");
6644
6783
  const options = { label: params.label, requestedLabel: params.label, cwd: parent.options.cwd, tools, agentOptions, ...params.skills ? { skills: params.skills } : {}, ...params.extensions ? { extensions: params.extensions } : {}, ...params.model ? { model: params.model } : {}, ...params.role ? { role: params.role } : {}, ...params.contextFiles ? { contextFiles: params.contextFiles } : {}, ...outputSchema === void 0 ? {} : { schema: outputSchema }, ...params.retries === void 0 ? {} : { retries: params.retries }, ...params.timeoutMs === void 0 ? {} : { timeoutMs: params.timeoutMs } };
6645
6784
  const child = this.spawn(parent.runId, params.prompt, options, parentId);
6646
- return { content: [{ type: "text", text: JSON.stringify({ id: child.id }) }], details: { id: child.id } };
6785
+ return { content: [{ type: "text", text: JSON.stringify({ id: child.id }) }], details: { id: child.id }, structuredContent: { id: child.id } };
6647
6786
  }
6648
6787
  });
6649
6788
  const resultTool = defineTool({
@@ -6651,10 +6790,11 @@ var FairAgentScheduler = class {
6651
6790
  label: "Child Result",
6652
6791
  description: "Wait for a direct child and return its result once; repeated retrieval fails with AGENT_RESULT_COLLECTED",
6653
6792
  parameters: Type.Object({ id: Type.String() }),
6793
+ outputSchema: Type.Object({ id: Type.String(), ok: Type.Boolean(), value: Type.Optional(Type.Unknown()), error: Type.Optional(Type.Object({ code: Type.String(), message: Type.String() })) }),
6654
6794
  execute: async (_id, params) => {
6655
6795
  const value = await this.result(parentId, params.id);
6656
6796
  if (!value.ok && value.error.code === "BUDGET_EXHAUSTED") throw new WorkflowError("BUDGET_EXHAUSTED", value.error.message);
6657
- return { content: [{ type: "text", text: JSON.stringify(value) }], details: value };
6797
+ return { content: [{ type: "text", text: JSON.stringify(value) }], details: value, structuredContent: value };
6658
6798
  }
6659
6799
  });
6660
6800
  const steerTool = defineTool({
@@ -7257,6 +7397,7 @@ var WORKFLOW_ERROR_PROSE = {
7257
7397
  RUN_NOT_FOUND: (detail) => /^Unknown workflow run\b/.test(detail) ? "The workflow run was not found." : `The workflow run was not found: ${detail}.`,
7258
7398
  RPC_LIMIT_EXCEEDED: (detail) => `The workflow communication data exceeded its size limit: ${detail}.`,
7259
7399
  SHELL_FAILED: (detail) => `The workflow shell command failed: ${detail}.`,
7400
+ TOOL_FAILED: (detail) => `The workflow tool call failed: ${detail}.`,
7260
7401
  AGENT_TIMEOUT: (detail) => `The workflow agent timed out: ${detail}.`,
7261
7402
  AGENT_FAILED: (detail) => `The workflow agent failed: ${detail}.`,
7262
7403
  AGENT_RESULT_COLLECTED: (detail) => `The nested agent result was already collected: ${detail}.`,
@@ -7547,7 +7688,7 @@ function persistedFailure(run, error) {
7547
7688
  return { ...run, error: { code: error.code, message: error.message, ...failedAt ? { failedAt } : {} }, ...failedAt ? { failedAt } : {} };
7548
7689
  }
7549
7690
  function createWorkflowRecovery(deps) {
7550
- const { pi, home, runs, scheduler, eventPublisher, persistRunState, projectTrusted: projectTrusted2, resumeHostContext: resumeHostContext2, ensureSessionLease, createAgentExecutor, activeSnapshotTools, frozenResourcePolicy: frozenResourcePolicy2, resolveLaunchPrologue, resumeRoles, workflowAgentHandler, shellForRun, resolveWorktree, checkpointBridge, phaseBridge, logBridge, lifecycleFor, createProviderErrorRecovery, cleanupTerminalRun, deliver: deliver2, deliverTerminal, workflowToolUpdate: workflowToolUpdate2, registry, modelSpec: modelSpec2 } = deps;
7691
+ const { pi, home, runs, scheduler, eventPublisher, persistRunState, projectTrusted: projectTrusted2, resumeHostContext: resumeHostContext2, ensureSessionLease, createAgentExecutor, activeSnapshotTools, frozenResourcePolicy: frozenResourcePolicy2, resolveLaunchPrologue, resumeRoles, workflowAgentHandler, shellForRun, toolForRun, resolveWorktree, checkpointBridge, phaseBridge, logBridge, lifecycleFor, createProviderErrorRecovery, cleanupTerminalRun, deliver: deliver2, deliverTerminal, workflowToolUpdate: workflowToolUpdate2, registry, modelSpec: modelSpec2 } = deps;
7551
7692
  const coordinateRunMutation = deps.coordinateRunMutation ?? ((task) => task());
7552
7693
  const budgetDecisionDelivery = (metadata, request) => `Workflow ${metadata.name} budget adjustment ${request.proposalId} for run ${request.runId} requires approval. Consumed usage: ${JSON.stringify(request.consumed)}. Previous limits: ${JSON.stringify(request.previous)}. Proposed limits: ${JSON.stringify(request.proposed)}. Respond with workflow_respond using proposalId ${request.proposalId}.`;
7553
7694
  const appendBudgetDecisionEvent = async (run, request, type) => {
@@ -7619,7 +7760,7 @@ function createWorkflowRecovery(deps) {
7619
7760
  run.executor.setRunContext(runContext);
7620
7761
  await scheduler.cancelRun(run.store.runId);
7621
7762
  await run.lifecycle.resume();
7622
- const execution = runWorkflow(script, loaded.snapshot.args, withWorkflowFunctions({ shell: (command, options, signal, identity) => shellForRun(run.store, run.metadata, run.lifecycle, command, options, signal, identity), agent: workflowAgentHandler(run.store, run.metadata, run.lifecycle, run.executor, run.store.cwd, run.store.runId, roles.capture), worktree: async (owner) => resolveWorktree(run.store, run.metadata, owner), checkpoint: checkpointBridge(run.store.runId, run.store, run.metadata, foreground, hasUI ? ui : void 0), phase: phaseBridge(run.store, run.metadata, run.lifecycle), log: logBridge(run.store, run.lifecycle, run.metadata.name) }, run.store, runContext, registry, snapshot.settings.extensionSettings), controller.signal);
7763
+ const execution = runWorkflow(script, loaded.snapshot.args, withWorkflowFunctions({ tool: (identifier, args, signal, identity) => toolForRun(run.store, run.lifecycle, context?.toolContext, identifier, args, signal, identity), shell: (command, options, signal, identity) => shellForRun(run.store, run.metadata, run.lifecycle, command, options, signal, identity), agent: workflowAgentHandler(run.store, run.metadata, run.lifecycle, run.executor, run.store.cwd, run.store.runId, roles.capture), worktree: async (owner) => resolveWorktree(run.store, run.metadata, owner), checkpoint: checkpointBridge(run.store.runId, run.store, run.metadata, foreground, hasUI ? ui : void 0), phase: phaseBridge(run.store, run.metadata, run.lifecycle), log: logBridge(run.store, run.lifecycle, run.metadata.name) }, run.store, runContext, registry, snapshot.settings.extensionSettings), controller.signal);
7623
7764
  run.execution = execution;
7624
7765
  const completion = execution.result.then(async (value) => {
7625
7766
  await scheduler.flush(run.store.runId);
@@ -7818,7 +7959,7 @@ function createWorkflowRecovery(deps) {
7818
7959
  await eventPublisher.runStarted(prepared.childRun.store, prepared.childRun.metadata);
7819
7960
  const { hasUI, ui } = recoveryUi(context);
7820
7961
  const recoveryContext = resumeHostContext2(context);
7821
- const completed = await coldResumeRun(prepared.childRun, hasUI, ui, projectTrusted2(context), { model: prepared.hostModel, modelRegistry: prepared.modelRegistry, deliveryContext: recoveryContext.deliveryContext, resolvedAliases: prepared.currentAliases, blockedAliases: prepared.blockedAliases, blockedAliasTargets: prepared.blockedAliasTargets, ...signal ? { signal } : {} }, modeOverride);
7962
+ const completed = await coldResumeRun(prepared.childRun, hasUI, ui, projectTrusted2(context), { model: prepared.hostModel, modelRegistry: prepared.modelRegistry, deliveryContext: recoveryContext.deliveryContext, toolContext: recoveryContext.toolContext, resolvedAliases: prepared.currentAliases, blockedAliases: prepared.blockedAliases, blockedAliasTargets: prepared.blockedAliasTargets, ...signal ? { signal } : {} }, modeOverride);
7822
7963
  const completion = runs.get(prepared.childRun.store.runId)?.completion;
7823
7964
  if (completion) {
7824
7965
  childStarted = true;
@@ -9131,6 +9272,15 @@ function formatShellActivity(activeShells, startedAt, spinner2, styles, now) {
9131
9272
  const timing = started && startedAt !== void 0 && !Number.isNaN(started.getTime()) ? ` ${styles.dim(`started=${started.toISOString()} elapsed=${formatWorkflowRuntime(Math.max(0, now - startedAt))}`)}` : "";
9132
9273
  return `${styles.accent(spinner2)} shell ${styles.accent("[running]")} ${styles.dim(`(${String(count)} active)`)}${timing}`;
9133
9274
  }
9275
+ function formatToolActivity(run, spinner2, styles, now) {
9276
+ const calls = run.activeTools ?? [];
9277
+ if (!calls.length) return void 0;
9278
+ const counts = /* @__PURE__ */ new Map();
9279
+ for (const { name } of calls) counts.set(name, (counts.get(name) ?? 0) + 1);
9280
+ const names = [...counts].map(([name, count]) => count > 1 ? `${name} \xD7${String(count)}` : name).join(", ");
9281
+ const elapsed = formatWorkflowRuntime(Math.max(0, now - Math.min(...calls.map(({ startedAt }) => startedAt))));
9282
+ return `${styles.accent(spinner2)} tools ${styles.accent("[running]")} ${sanitizeDisplayText(names)} ${styles.dim(`elapsed=${elapsed}`)}`;
9283
+ }
9134
9284
  function formatLogTimestamp(timestamp) {
9135
9285
  if (timestamp === void 0 || !Number.isFinite(timestamp)) return "--:--:--";
9136
9286
  const date = new Date(timestamp);
@@ -9211,12 +9361,15 @@ function workflowProgressLines(run, spinner2, styles, now, expanded, width, show
9211
9361
  const scopedShells = (run.activeShellsByPhase?.length ?? 0) > 0;
9212
9362
  const shellActivity = scopedShells ? void 0 : formatShellActivity(run.activeShells, run.activeShellStartedAt, spinner2, styles, now);
9213
9363
  if (shellActivity) lines.push(` ${shellActivity}`);
9364
+ const toolActivity = formatToolActivity(run, spinner2, styles, now);
9365
+ if (toolActivity) lines.push(` ${toolActivity}`);
9214
9366
  const byId = new Map(run.agents.map((agent) => [agent.id, agent]));
9215
9367
  const position = new Map(run.agents.map((agent, index) => [agent, index]));
9216
9368
  const renderAgents = (agents, nested2) => renderGroupedAgents(agents, ({ agent, depth }, grouped) => {
9217
- const icon = agentStateGlyph(agent.state, spinner2);
9369
+ const icon = agentStateGlyph(agent.state, "\u25CF");
9218
9370
  const indent = " ".repeat((grouped ? 2 : 1) + depth);
9219
- const activity = SETTLED_AGENT_STATES.has(agent.state) ? "" : formatAgentActivity(agent, spinner2, styles, now);
9371
+ const idleSpinner = agent.state === "running" && !agentActivityLabel(agent) ? styles.accent(spinner2) : "";
9372
+ const activity = SETTLED_AGENT_STATES.has(agent.state) ? "" : [idleSpinner, formatAgentActivity(agent, spinner2, styles, now)].filter(Boolean).join(" ");
9220
9373
  const name = grouped ? agent.label ?? agent.name : styledAgentBreadcrumb(agent, byId, styles);
9221
9374
  const state2 = progressStyleForState(agent.state, styles);
9222
9375
  const detail = expanded ? formatWorkflowAgentDetail(agent, now) : "";
@@ -9514,7 +9667,7 @@ ${progressText}` : progressText, width);
9514
9667
  invalidate() {
9515
9668
  const displayed = currentRun();
9516
9669
  const now = Date.now();
9517
- if (!progress || !refresh || displayed.state !== "running" || !displayed.agents.some((agent) => agent.state === "running") && (displayed.activeShells ?? 0) <= 0) return;
9670
+ if (!progress || !refresh || displayed.state !== "running" || !displayed.agents.some((agent) => agent.state === "running") && (displayed.activeShells ?? 0) <= 0 && !displayed.activeTools?.length) return;
9518
9671
  if (progress.refresh || now - progress.lastRefreshAt < WORKFLOW_PROGRESS_REFRESH_MS) return;
9519
9672
  progress.lastRefreshAt = now;
9520
9673
  const inputRun = progress.inputRun;
@@ -9727,6 +9880,8 @@ function formatNavigatorDashboard(run, checkpoints, worktrees, now = Date.now())
9727
9880
  const lines = [header, meta, ...formatCompactBudgetStatus(run)];
9728
9881
  const shellActivity = formatShellActivity(run.activeShells, run.activeShellStartedAt, "\u2826", PLAIN_WORKFLOW_PROGRESS_STYLES, now);
9729
9882
  if (shellActivity) lines.push(` ${shellActivity}`);
9883
+ const toolActivity = formatToolActivity(run, "\u2826", PLAIN_WORKFLOW_PROGRESS_STYLES, now);
9884
+ if (toolActivity) lines.push(` ${toolActivity}`);
9730
9885
  if (run.error) lines.push(`Error: ${run.error.code}: ${run.error.message}`);
9731
9886
  if (run.events?.length) lines.push(...run.events.filter((event) => event.type === "warning").map((event) => `Warning: ${event.message}`));
9732
9887
  lines.push("");
@@ -9766,6 +9921,8 @@ function formatNavigatorRun(loaded, checkpoints, worktrees, now = Date.now()) {
9766
9921
  ];
9767
9922
  const shellActivity = formatShellActivity(run.activeShells, run.activeShellStartedAt, "\u2826", PLAIN_WORKFLOW_PROGRESS_STYLES, now);
9768
9923
  if (shellActivity) lines.push(` ${shellActivity}`);
9924
+ const toolActivity = formatToolActivity(run, "\u2826", PLAIN_WORKFLOW_PROGRESS_STYLES, now);
9925
+ if (toolActivity) lines.push(` ${toolActivity}`);
9769
9926
  if (run.error) lines.push(`Run error: ${run.error.code}: ${run.error.message}`);
9770
9927
  if (run.events?.length) lines.push(...run.events.filter((event) => event.type === "warning").map((event) => `Warning: ${event.message}`));
9771
9928
  const aliases = snapshot.modelAliases ?? snapshot.settings.modelAliases;
@@ -9865,6 +10022,8 @@ function formatWorkflowPhaseDashboard(run, snapshot, width, selection = {}, styl
9865
10022
  const scopedShells = (run.activeShellsByPhase?.length ?? 0) > 0;
9866
10023
  const shellActivity = scopedShells ? void 0 : formatShellActivity(run.activeShells, run.activeShellStartedAt, "\u2826", styles, now);
9867
10024
  if (shellActivity) lines.push(` ${shellActivity}`);
10025
+ const toolActivity = formatToolActivity(run, "\u2826", styles, now);
10026
+ if (toolActivity) lines.push(` ${toolActivity}`);
9868
10027
  const actionRows = () => {
9869
10028
  const actions = selection.actions;
9870
10029
  if (!actions) return [];
@@ -11101,6 +11260,66 @@ Mutations are available through workflow tools.`, "info");
11101
11260
  });
11102
11261
  }
11103
11262
 
11263
+ // src/script-tools.ts
11264
+ function scriptToolExcluded(name) {
11265
+ return name === "workflow" || name.startsWith("workflow_") || name.startsWith("subagents_");
11266
+ }
11267
+ function scriptToolIdentifier(name) {
11268
+ let identifier = "";
11269
+ for (const char of name) identifier += (identifier === "" ? /^[A-Za-z_$]$/ : /^[A-Za-z0-9_$]$/).test(char) ? char : "_";
11270
+ return identifier || "_";
11271
+ }
11272
+ function scriptToolContext(ctx) {
11273
+ return object(ctx) && typeof ctx.executeTool === "function" && Array.isArray(ctx.tools) ? ctx : void 0;
11274
+ }
11275
+ function scriptToolIdentifiers(context) {
11276
+ return (context?.tools ?? []).filter(({ name }) => !scriptToolExcluded(name)).map(({ name }) => scriptToolIdentifier(name));
11277
+ }
11278
+ function scriptTool(context, identifier) {
11279
+ return context.tools.find(({ name }) => !scriptToolExcluded(name) && scriptToolIdentifier(name) === identifier);
11280
+ }
11281
+ function scriptToolValue(tool, outcome) {
11282
+ const structured = tool.outputSchema === void 0 ? void 0 : outcome.result.structuredContent;
11283
+ const text = outcome.result.content.flatMap((item) => item.type === "text" ? [item.text] : []).join("\n");
11284
+ if (structured === void 0 && outcome.isError) throw new WorkflowError("TOOL_FAILED", `${tool.name}: ${text || "failed"}`);
11285
+ let value;
11286
+ try {
11287
+ value = JSON.parse(JSON.stringify(structured ?? text));
11288
+ } catch (error) {
11289
+ throw new WorkflowError("TOOL_FAILED", `${tool.name} returned a result that is not JSON: ${errorText(error)}`);
11290
+ }
11291
+ if (!jsonValue(value)) throw new WorkflowError("TOOL_FAILED", `${tool.name} returned a result that is not JSON`);
11292
+ if (Buffer.byteLength(JSON.stringify(value)) > RPC_LIMIT_BYTES) throw new WorkflowError("TOOL_FAILED", `${tool.name} returned more than the 10 MB workflow RPC limit`);
11293
+ return value;
11294
+ }
11295
+ function validateScriptToolReferences(script, context) {
11296
+ const references = scriptToolReferences(script);
11297
+ if (!references?.length) return;
11298
+ if (!context) throw new WorkflowError("UNKNOWN_TOOL", `Workflow scripts can call session tools only when launched by the workflow tool; this launch has none for ${references.map((name) => `tools.${name}`).join(", ")}`);
11299
+ const identifiers = scriptToolIdentifiers(context);
11300
+ const unknown = references.filter((name) => !identifiers.includes(name));
11301
+ if (unknown.length) throw new WorkflowError("UNKNOWN_TOOL", `Workflow script calls tools this session cannot call: ${unknown.map((name) => `tools.${name}`).join(", ")}`);
11302
+ const ambiguous = references.filter((name) => identifiers.indexOf(name) !== identifiers.lastIndexOf(name));
11303
+ if (ambiguous.length) throw new WorkflowError("UNKNOWN_TOOL", `Workflow script calls tools whose names collide after normalization: ${ambiguous.map((name) => `tools.${name}`).join(", ")}`);
11304
+ }
11305
+ function describeScriptOutput(schema) {
11306
+ if (!object(schema) || schema.type !== "object" || !object(schema.properties)) return schema === void 0 ? void 0 : "a JSON value";
11307
+ const required = new Set(Array.isArray(schema.required) ? schema.required : []);
11308
+ return `\`{ ${Object.keys(schema.properties).map((name) => required.has(name) ? name : `${name}?`).join(", ")} }\``;
11309
+ }
11310
+ function prepareScriptToolLoadout(loadout) {
11311
+ if (loadout.declared.some(({ name }) => name === "codemode")) return void 0;
11312
+ const callable = new Set(loadout.callable.map(({ name }) => name));
11313
+ const descriptions = {};
11314
+ for (const tool of loadout.declared) {
11315
+ const output = callable.has(tool.name) && !scriptToolExcluded(tool.name) ? describeScriptOutput(tool.outputSchema) : void 0;
11316
+ if (output) descriptions[tool.name] = `${tool.description.trim()}
11317
+
11318
+ Workflow scripts: \`tools.${scriptToolIdentifier(tool.name)}(args)\` resolves to ${output}.`;
11319
+ }
11320
+ return { descriptions };
11321
+ }
11322
+
11104
11323
  // src/changelog.ts
11105
11324
  import { mkdir as mkdir3, readFile as readFile6 } from "node:fs/promises";
11106
11325
  import { dirname as dirname6, join as join14 } from "node:path";
@@ -11202,7 +11421,7 @@ async function showChangelogNotice(context, agentDir, packageDirectory) {
11202
11421
  }
11203
11422
 
11204
11423
  // src/trajectory.ts
11205
- import { createHash as createHash3 } from "node:crypto";
11424
+ import { createHash as createHash4 } from "node:crypto";
11206
11425
  import { open as open2, readFile as readFile7, readdir as readdir5, stat as stat4 } from "node:fs/promises";
11207
11426
  import { homedir as homedir7 } from "node:os";
11208
11427
  import { basename as basename5, dirname as dirname7, join as join15, resolve as resolve8 } from "node:path";
@@ -11234,7 +11453,8 @@ var SUBAGENTS_RUN_PARAMETERS = Type2.Object({
11234
11453
  timeoutMs: Type2.Optional(Type2.Union([Type2.Integer({ minimum: 1, description: "Optional execution timeout in milliseconds" }), Type2.Null()]))
11235
11454
  }, { additionalProperties: false });
11236
11455
  var SUBAGENTS_INSPECT_PARAMETERS = Type2.Object({
11237
- id: Type2.Optional(Type2.String({ description: "Subagent ID; omit to list ordered run summaries" }))
11456
+ id: Type2.Optional(Type2.String({ description: "Subagent ID; omit to list ordered run summaries" })),
11457
+ scope: Type2.Optional(Type2.Union([Type2.Literal("session"), Type2.Literal("all")], { description: "List scope when id is omitted: session (default) lists runs from the current Pi session; all lists every stored run" }))
11238
11458
  }, { additionalProperties: false });
11239
11459
  var SUBAGENTS_ID_PARAMETERS = Type2.Object({
11240
11460
  id: Type2.String({ description: "Subagent ID" })
@@ -11249,6 +11469,21 @@ var SUBAGENTS_STOP_PARAMETERS = Type2.Object({
11249
11469
  var SUBAGENTS_RETRY_PARAMETERS = Type2.Object({
11250
11470
  id: Type2.String({ description: "Failed or stopped subagent ID to retry" })
11251
11471
  }, { additionalProperties: false });
11472
+ var SUBAGENT_STATUS_OUTPUT = Type2.Object({
11473
+ id: Type2.String(),
11474
+ state: Type2.String(),
11475
+ sessionId: Type2.Optional(Type2.String()),
11476
+ startedAt: Type2.Optional(Type2.Number()),
11477
+ finishedAt: Type2.Optional(Type2.Number()),
11478
+ attempts: Type2.Optional(Type2.Number()),
11479
+ attemptDetails: Type2.Optional(Type2.Array(Type2.Unknown())),
11480
+ worktree: Type2.Optional(Type2.Object({ path: Type2.String(), branch: Type2.String() })),
11481
+ error: Type2.Optional(Type2.Object({ code: Type2.String(), message: Type2.String() })),
11482
+ progress: Type2.Optional(Type2.Unknown()),
11483
+ value: Type2.Optional(Type2.Unknown())
11484
+ });
11485
+ var SUBAGENTS_INSPECT_OUTPUT = Type2.Union([SUBAGENT_STATUS_OUTPUT, Type2.Array(Type2.Object({ id: Type2.String(), state: Type2.String() }))]);
11486
+ var SUBAGENTS_STEER_OUTPUT = Type2.Object({ id: Type2.String(), accepted: Type2.Boolean() });
11252
11487
  function normalizeSubagentRunRequest(value) {
11253
11488
  if (!Value2.Check(SUBAGENTS_RUN_PARAMETERS, value)) throw new WorkflowError("INVALID_METADATA", "Invalid subagents_run parameters");
11254
11489
  if (typeof value.worktree === "string" && !value.worktree.trim()) throw new WorkflowError("INVALID_METADATA", "worktree name must be a non-empty string");
@@ -11451,7 +11686,7 @@ function transcriptToolCallId(value) {
11451
11686
  return void 0;
11452
11687
  }
11453
11688
  function isTimingTranscriptEntry(value) {
11454
- return object(value) && value.type === "custom" && value.customType === "pi-workflows:tool-timing";
11689
+ return object(value) && value.type === "custom" && value.customType === TOOL_TIMING_ENTRY_TYPE;
11455
11690
  }
11456
11691
  function timingToolCallId(value) {
11457
11692
  if (!object(value) || !isTimingTranscriptEntry(value) || !object(value.data)) return void 0;
@@ -11572,7 +11807,7 @@ function withPiToolDescriptions(run) {
11572
11807
  function canonicalExtensionSelector2(selector, base) {
11573
11808
  const negated = selector.startsWith("!");
11574
11809
  const body = negated ? selector.slice(1) : selector;
11575
- if (body === "*" || body === "**" || body.startsWith("**/")) return selector;
11810
+ if (body === "*" || body === "**" || body.startsWith("**/") || body.startsWith("builtin:")) return selector;
11576
11811
  const resolved = resolve8(base, body);
11577
11812
  if (resourcePatternHasMagic(body)) return `${negated ? "!" : ""}${resolved}`;
11578
11813
  return `${negated ? "!" : ""}${canonicalPath(resolved)}`;
@@ -11647,11 +11882,17 @@ function failedAgentOutput(agent) {
11647
11882
  const status = agent.state === "cancelled" ? "cancelled" : "failed";
11648
11883
  return { status, code: error?.code ?? (status === "cancelled" ? "CANCELLED" : "AGENT_FAILED"), message: error?.message ?? `Agent ${agent.state}` };
11649
11884
  }
11885
+ function operationOutput(operation) {
11886
+ const bytes = serializedJsonBytes(operation.value);
11887
+ return bytes > DEFAULT_MAX_BYTES2 ? { status: "truncated", kind: "result", bytes } : { status: "available", value: operation.value, bytes };
11888
+ }
11889
+ function outputForCall(call, operation) {
11890
+ if (operation !== void 0) return call.kind === "tool" ? { status: "completed" } : operationOutput(operation);
11891
+ if (call.error) return { status: call.error.code === "CANCELLED" ? "cancelled" : "failed", code: call.error.code, ...call.kind === "shell" ? { message: call.error.message ?? "Shell failed" } : {} };
11892
+ return call.finishedAt === void 0 ? { status: "pending" } : { status: "unavailable" };
11893
+ }
11650
11894
  function outputForAgent(agent, operation) {
11651
- if (operation !== void 0) {
11652
- const bytes = serializedJsonBytes(operation.value);
11653
- return bytes > DEFAULT_MAX_BYTES2 ? { status: "truncated", kind: "result", bytes } : { status: "available", value: operation.value, bytes };
11654
- }
11895
+ if (operation !== void 0) return operationOutput(operation);
11655
11896
  if (agent.state === "failed" || agent.state === "cancelled") return failedAgentOutput(agent);
11656
11897
  if (agent.state === "completed") return { status: "unavailable" };
11657
11898
  return { status: "pending" };
@@ -11659,7 +11900,17 @@ function outputForAgent(agent, operation) {
11659
11900
  function applyTrajectoryAgentOutputs(run, operations) {
11660
11901
  const byPath = new Map(operations.map((operation) => [operation.path, operation]));
11661
11902
  const agents = run.agents.map((agent) => ({ ...agent, output: outputForAgent(agent, agent.resultPath === void 0 ? void 0 : byPath.get(agent.resultPath)) }));
11662
- return { ...run, agents };
11903
+ const scriptCalls = run.scriptCalls?.map((call) => {
11904
+ const recorded = { ...call, output: outputForCall(call, byPath.get(call.path)) };
11905
+ if (call.kind === "tool") {
11906
+ delete recorded.input;
11907
+ delete recorded.inputBytes;
11908
+ if (call.error) recorded.error = { code: call.error.code };
11909
+ }
11910
+ return recorded;
11911
+ });
11912
+ const rest = run;
11913
+ return { ...rest, agents, ...scriptCalls === void 0 ? {} : { scriptCalls } };
11663
11914
  }
11664
11915
  function overlayTrajectoryRun(run, overlay) {
11665
11916
  const overlaid = overlay(run);
@@ -11669,7 +11920,7 @@ function overlayTrajectoryRun(run, overlay) {
11669
11920
  const output = previous?.status === "available" || previous?.status === "truncated" ? previous : outputForAgent(agent, void 0);
11670
11921
  return { ...agent, output };
11671
11922
  });
11672
- return { ...overlaid, agents };
11923
+ return { ...overlaid, agents, ...run.scriptCalls === void 0 ? {} : { scriptCalls: run.scriptCalls } };
11673
11924
  }
11674
11925
  async function loadTrajectoryRun(store, includeTranscripts = true) {
11675
11926
  const value = await store.load();
@@ -11719,7 +11970,7 @@ function createTrajectoryRunLoader(cwd, sessionId, home = homedir7(), overlay) {
11719
11970
  return createCachedRunLoader(cwd, sessionId, home, (store) => loadTrajectoryRun(store), fileMtime, overlay);
11720
11971
  }
11721
11972
  function transcriptRevision(info) {
11722
- const digest = createHash3("sha256").update(`${String(info.mtimeNs)}:${String(info.size)}`).digest();
11973
+ const digest = createHash4("sha256").update(`${String(info.mtimeNs)}:${String(info.size)}`).digest();
11723
11974
  return Math.max(1, digest.readUIntBE(0, 6));
11724
11975
  }
11725
11976
  async function transcriptMetadata(path) {
@@ -12002,6 +12253,12 @@ function mainAgentError(error) {
12002
12253
  Object.assign(presented, typed);
12003
12254
  return presented;
12004
12255
  }
12256
+ function completionControlValue(result, controlRunId) {
12257
+ const record3 = { ...controlRunId === void 0 ? {} : { runId: controlRunId }, ...result };
12258
+ delete record3.run;
12259
+ delete record3.completion;
12260
+ return record3;
12261
+ }
12005
12262
  function completionControlContent(result, controlRunId) {
12006
12263
  const record3 = object(result) ? { ...result } : void 0;
12007
12264
  if (record3 && controlRunId !== void 0 && record3.runId === void 0) record3.runId = controlRunId;
@@ -12047,7 +12304,7 @@ function formatWorkflowPreview(args) {
12047
12304
  return [`workflow ${name}`, typeof args.description === "string" && args.description.trim() ? args.description.trim() : ""].filter(Boolean).join("\n");
12048
12305
  }
12049
12306
  var WORKFLOW_TOOL_LABEL = "Workflow";
12050
- var WORKFLOW_TOOL_DESCRIPTION = "Run a deterministic JavaScript workflow with a named inline or file-backed parallel-to-summary path by default";
12307
+ var WORKFLOW_TOOL_DESCRIPTION = "Run a deterministic JavaScript workflow with a named inline or file-backed parallel-to-summary path by default. Scripts call this session's tools as `await tools.<name>(args)`, outside withWorktree: a tool resolves to its text unless its description gives a `tools.<name>(args)` result shape, and failures reject";
12051
12308
  var WORKFLOW_TOOL_PROMPT_SNIPPET = "Run a deterministic, resumable JavaScript workflow. Prefer a named inline script that fans out independent work with parallel(...), awaits the keyed results before interpolating them into one summarizing agent(...), and returns. Provide exactly one of script or scriptPath and a non-empty name. Registered catalog functions are available as globals inside the script; call them there, for example return await someFunction(args). Advanced controls include registered functions, outputSchema, budgets, checkpoints, worktrees, retry/resume, CLI export, and pipelines. Runs are in the background by default; completion arrives as a follow-up message. Set foreground: true when the caller must wait for the final value. Manage runs from the interactive /workflow picker; use workflow_status, workflow_resume, workflow_retry, workflow_stop, and workflow_respond for explicit tool controls. If a foreground call detaches before its result is accepted, its terminal success or failure is promoted to one follow-up message. Foreground results include the completed run ID. Recovery inherits the source launch mode; legacy snapshots without launchMode recover in the background. Set foreground: true or false on workflow_resume/workflow_retry to override it; foreground recovery waits for terminal value and run details, while background recovery returns immediately and delivers completion or failure as a follow-up. After failure follow-ups, especially CANCELLED or interrupted runs, call workflow_status({ runId }) before recovery or replacement work, then pass its state as expectedState to workflow_retry/workflow_resume so recovery cannot act on a state that changed. Recovery map: agent(..., { retries }) reruns one agent call in the same run for transient failures; workflow_retry({ runId, expectedState?, foreground? }) replays a failed run into a child; workflow_resume({ runId, expectedState?, budget?, foreground? }) continues a budget_exhausted run; parentRunId on a new launch only borrows named worktrees and never replays or resumes.";
12052
12309
  var WORKFLOW_TOOL_PARAMETERS = Type3.Object({
12053
12310
  name: Type3.String({ description: "Required non-empty workflow name" }),
@@ -12062,6 +12319,19 @@ var WORKFLOW_TOOL_PARAMETERS = Type3.Object({
12062
12319
  }, { additionalProperties: false });
12063
12320
  var WORKFLOW_STATUS_PARAMETERS = Type3.Object({ runId: Type3.String({ description: "Workflow run ID visible in the current project" }) }, { additionalProperties: false });
12064
12321
  var WORKFLOW_RETRY_PARAMETERS = Type3.Object({ runId: Type3.String({ description: "Explicit failed workflow run ID" }), expectedState: Type3.Optional(Type3.String({ description: "Persisted source state observed before recovery" })), foreground: Type3.Optional(Type3.Boolean({ description: "Override the source launch mode for this recovery" })) });
12322
+ var WORKFLOW_ERROR_OUTPUT = Type3.Object({ code: Type3.String(), message: Type3.String() });
12323
+ var WORKFLOW_LAUNCH_OUTPUT = Type3.Object({ runId: Type3.String(), state: Type3.String(), detached: Type3.Optional(Type3.Boolean()), value: Type3.Optional(Type3.Unknown()) });
12324
+ var WORKFLOW_STATUS_OUTPUT = Type3.Object({ runId: Type3.String(), workflowName: Type3.String(), state: Type3.String(), error: Type3.Optional(WORKFLOW_ERROR_OUTPUT), failedAt: Type3.Optional(Type3.String()), budget: Type3.Optional(Type3.Unknown()), usage: Type3.Optional(Type3.Unknown()), phase: Type3.Optional(Type3.String()), delivery: Type3.Optional(Type3.Object({ mode: Type3.String(), state: Type3.String() })), agents: Type3.Array(Type3.Object({ id: Type3.String(), label: Type3.Optional(Type3.String()), path: Type3.String(), state: Type3.String(), lastEventAt: Type3.Optional(Type3.Number()), accounting: Type3.Optional(Type3.Unknown()) })) });
12325
+ var WORKFLOW_STOP_OUTPUT = Type3.Object({ runId: Type3.String(), state: Type3.String(), stopped: Type3.Boolean(), reason: Type3.Optional(Type3.String()) });
12326
+ var WORKFLOW_RECOVERY_OUTPUT = Type3.Object({ state: Type3.String(), runId: Type3.Optional(Type3.String()), parentRunId: Type3.Optional(Type3.String()), proposalId: Type3.Optional(Type3.String()), value: Type3.Optional(Type3.Unknown()) });
12327
+ var WORKFLOW_RESPOND_OUTPUT = Type3.Object({ state: Type3.String(), approved: Type3.Boolean(), reason: Type3.String(), accepted: Type3.Optional(Type3.Boolean()), runId: Type3.Optional(Type3.String()), value: Type3.Optional(Type3.Unknown()) });
12328
+ var WORKFLOW_CATALOG_ALIAS_OUTPUT = Type3.Object({ name: Type3.String(), kind: Type3.String(), provenance: Type3.String(), version: Type3.Optional(Type3.String()), headline: Type3.Optional(Type3.String()) });
12329
+ var WORKFLOW_CATALOG_OUTPUT = Type3.Union([
12330
+ Type3.Object({ functions: Type3.Array(Type3.Object({ name: Type3.String(), description: Type3.String(), input: Type3.Unknown() })), modelAliasEntries: Type3.Optional(Type3.Array(WORKFLOW_CATALOG_ALIAS_OUTPUT)), settings: Type3.Optional(Type3.Unknown()) }),
12331
+ Type3.Object({ name: Type3.String(), version: Type3.String(), headline: Type3.String(), description: Type3.String(), input: Type3.Unknown(), output: Type3.Unknown() }),
12332
+ WORKFLOW_CATALOG_ALIAS_OUTPUT,
12333
+ Type3.Object({ error: Type3.Object({ code: Type3.String(), name: Type3.String(), message: Type3.String() }) })
12334
+ ]);
12065
12335
  function workflowToolUpdate(run) {
12066
12336
  const done = run.agents.filter((agent) => SETTLED_AGENT_STATES.has(agent.state)).length;
12067
12337
  return { content: [{ type: "text", text: `Workflow: ${run.workflowName} (${String(done)}/${String(run.agents.length)} done) [${run.state}]` }], details: { runId: run.id, run } };
@@ -12144,7 +12414,42 @@ function modelInventory(root, registry) {
12144
12414
  }
12145
12415
  function resumeHostContext(ctx) {
12146
12416
  const model = object(ctx) && object(ctx.model) && typeof ctx.model.provider === "string" && typeof ctx.model.id === "string" ? { provider: ctx.model.provider, id: ctx.model.id } : void 0;
12147
- return { model, modelRegistry: contextHostCapabilities(ctx).modelRegistry, deliveryContext: completionContext(ctx) };
12417
+ return { model, modelRegistry: contextHostCapabilities(ctx).modelRegistry, deliveryContext: completionContext(ctx), toolContext: scriptToolContext(ctx) };
12418
+ }
12419
+ var SCRIPT_CALL_TEXT_LIMIT_BYTES = 16 * 1024;
12420
+ function scriptCallInput(input) {
12421
+ const text = JSON.stringify(input);
12422
+ const bytes = Buffer.byteLength(text);
12423
+ return bytes <= SCRIPT_CALL_TEXT_LIMIT_BYTES ? { input } : { input: utf8Prefix(text, SCRIPT_CALL_TEXT_LIMIT_BYTES), inputBytes: bytes };
12424
+ }
12425
+ function startScriptCall(run, call) {
12426
+ const recorded = { ...call, ...run.phase === void 0 ? {} : { phase: run.phase } };
12427
+ const calls = run.scriptCalls ?? [];
12428
+ return { ...run, scriptCalls: calls.some(({ path }) => path === call.path) ? calls.map((current) => current.path === call.path ? recorded : current) : [...calls, recorded] };
12429
+ }
12430
+ function finishScriptCall(run, path, error) {
12431
+ const finishedAt = Date.now();
12432
+ return { ...run, scriptCalls: (run.scriptCalls ?? []).map((call) => call.path === path ? { ...call, finishedAt, ...error ? { error: { code: error.code, ...call.kind === "shell" ? { message: utf8Prefix(error.message, SCRIPT_CALL_TEXT_LIMIT_BYTES) } : {} } } : {} } : call) };
12433
+ }
12434
+ function registerAliasVirtualModels(pi, names, settingsPath) {
12435
+ const register = object(pi) ? asFn(pi.registerVirtualModel) : void 0;
12436
+ if (!register) return;
12437
+ for (const name of names) {
12438
+ const definition = {
12439
+ provider: VIRTUAL_MODEL_PROVIDER,
12440
+ id: name,
12441
+ name: `${name} (workflow alias)`,
12442
+ thinkingLevels: THINKING_LEVELS,
12443
+ route: (request, ctx) => {
12444
+ const aliases = resolveWorkflowSettings(ctx.cwd, projectTrusted(ctx), settingsPath).effective.modelAliases ?? {};
12445
+ const target = resolveModelReference(name, aliases, new Set(ctx.modelRegistry.getAll().map((model2) => `${model2.provider}/${model2.id}`)), settingsPath);
12446
+ const model = ctx.modelRegistry.find(target.provider, target.model);
12447
+ if (!model) throw new WorkflowError("UNKNOWN_MODEL", `Unknown model alias ${name} resolved to ${target.provider}/${target.model}`);
12448
+ return { model, thinkingLevel: request.thinkingLevel };
12449
+ }
12450
+ };
12451
+ Reflect.apply(register, pi, [definition]);
12452
+ }
12148
12453
  }
12149
12454
  async function resolveLaunchAliases(registry, staticAliases, context, availableModels, knownModels, settingsPath) {
12150
12455
  const dynamic = typeof registry.resolveModelAliases === "function" ? await registry.resolveModelAliases(context, new Set(Object.keys(staticAliases))) : {};
@@ -12175,10 +12480,13 @@ function workflowExtension(pi, home, clipboard = copyToClipboard, transport = lo
12175
12480
  });
12176
12481
  let backgroundWidgetEnabled = true;
12177
12482
  try {
12178
- backgroundWidgetEnabled = loadSettings(workflowSettingsPath(extensionAgentDir)).backgroundWidget ?? true;
12483
+ const globalSettings = loadSettings(workflowSettingsPath(extensionAgentDir));
12484
+ backgroundWidgetEnabled = globalSettings.backgroundWidget ?? true;
12485
+ registerAliasVirtualModels(pi, Object.keys(globalSettings.modelAliases ?? {}), workflowSettingsPath(extensionAgentDir));
12179
12486
  } catch {
12180
12487
  }
12181
12488
  const backgroundWidgetController = widget(pi, backgroundWidgetEnabled);
12489
+ const codemodeTools = loadCodemodeToolsSetting(extensionAgentDir);
12182
12490
  const logBridge = (store, lifecycle, workflowName) => async (message) => {
12183
12491
  const timestamp = Date.now();
12184
12492
  const bounded = utf8Prefix(message, DELIVERY_LIMIT_BYTES);
@@ -12470,7 +12778,10 @@ Choose what to do`, ["Retry", "Change model", "Abort workflow"]);
12470
12778
  if (replayed) return readShellResult(replayed.value);
12471
12779
  const shellStartedAt = Date.now();
12472
12780
  let shellPhaseIndex = -1;
12473
- const started = await persistRunState(store, metadata, (current) => {
12781
+ const call = { kind: "shell", name: "shell", path, ...scriptCallInput({ command, ...options.timeoutMs === void 0 ? {} : { timeoutMs: options.timeoutMs }, ...options.env === void 0 ? {} : { env: Object.keys(options.env) }, ...identity.worktreeOwner ? { worktree: identity.worktreeOwner } : {} }), startedAt: shellStartedAt };
12782
+ let failure;
12783
+ const started = await persistRunState(store, metadata, (run) => {
12784
+ const current = startScriptCall(run, call);
12474
12785
  const history = current.phaseHistory ?? [];
12475
12786
  if (current.phase !== void 0) shellPhaseIndex = current.phaseHistoryIndex ?? (history.length ? history.length - 1 : 0);
12476
12787
  const phaseActivities = [...current.activeShellsByPhase ?? []];
@@ -12486,8 +12797,12 @@ Choose what to do`, ["Retry", "Change model", "Abort workflow"]);
12486
12797
  if (!jsonValue(result)) fail("SHELL_FAILED", "Shell result is not JSON-compatible");
12487
12798
  await store.complete(path, result);
12488
12799
  return result;
12800
+ } catch (error) {
12801
+ failure = asWorkflowError(error);
12802
+ throw error;
12489
12803
  } finally {
12490
- const stopped = await persistRunState(store, metadata, (current) => {
12804
+ const stopped = await persistRunState(store, metadata, (run) => {
12805
+ const current = finishScriptCall(run, path, failure);
12491
12806
  const phaseActivities = [...current.activeShellsByPhase ?? []];
12492
12807
  const phaseActivityIndex = phaseActivities.findIndex(({ phaseIndex }) => phaseIndex === shellPhaseIndex);
12493
12808
  const phaseActivity = phaseActivities[phaseActivityIndex];
@@ -12507,6 +12822,55 @@ Choose what to do`, ["Retry", "Change model", "Abort workflow"]);
12507
12822
  await lifecycle.leave();
12508
12823
  }
12509
12824
  };
12825
+ const toolForRun = async (store, lifecycle, context, identifier, args, signal, identity) => {
12826
+ await lifecycle.enter();
12827
+ try {
12828
+ const path = toolIdentityPath(identifier, args, identity);
12829
+ const replayed = await store.replay(path);
12830
+ if (replayed) return replayed.value;
12831
+ if (!context) fail("UNKNOWN_TOOL", `tools.${identifier} needs a run launched or recovered by a Pi tool call; runs resumed from /workflow or at session start, and headless launches, only replay journaled tool calls`);
12832
+ const render = async () => {
12833
+ try {
12834
+ const update = runs.get(store.runId)?.update;
12835
+ if (update) update(workflowToolUpdate(withLiveActivities((await store.load()).run)));
12836
+ } catch {
12837
+ }
12838
+ };
12839
+ const record3 = async (update) => {
12840
+ try {
12841
+ await store.updateState(update);
12842
+ } catch {
12843
+ }
12844
+ };
12845
+ let value;
12846
+ try {
12847
+ const tool = scriptTool(context, identifier);
12848
+ if (!tool) fail("UNKNOWN_TOOL", `tools.${identifier} is not a tool this session can call`);
12849
+ const cancelled = () => signal.aborted;
12850
+ if (cancelled()) fail("CANCELLED", "Workflow cancelled");
12851
+ await record3((run) => startScriptCall(run, { kind: "tool", name: tool.name, path, startedAt: Date.now() }));
12852
+ const finished = liveAgents.toolStarted(store.runId, tool.name);
12853
+ try {
12854
+ await render();
12855
+ const outcome = await context.executeTool(tool.name, args, { signal });
12856
+ if (cancelled()) fail("CANCELLED", "Workflow cancelled");
12857
+ value = scriptToolValue(tool, outcome);
12858
+ } finally {
12859
+ finished();
12860
+ }
12861
+ } catch (error) {
12862
+ const failure = error instanceof WorkflowError ? error : new WorkflowError("TOOL_FAILED", `tools.${identifier}: ${errorText(error)}`);
12863
+ await record3((run) => finishScriptCall(run, path, failure));
12864
+ throw failure;
12865
+ }
12866
+ await store.complete(path, value);
12867
+ await record3((run) => finishScriptCall(run, path));
12868
+ await render();
12869
+ return value;
12870
+ } finally {
12871
+ await lifecycle.leave();
12872
+ }
12873
+ };
12510
12874
  const lifecycleFor = (store, state, metadata) => new RunLifecycle(state, async (next, previous, reason) => {
12511
12875
  const run = runs.get(store.runId);
12512
12876
  if (!run) throw new WorkflowError("INTERNAL_ERROR", `Unknown production run: ${store.runId}`);
@@ -12763,16 +13127,18 @@ Respond with workflow_respond.`);
12763
13127
  return decision;
12764
13128
  };
12765
13129
  };
12766
- const registerControlTool = (name, label, description, parameters, run) => {
13130
+ const registerControlTool = (name, label, description, parameters, outputSchema, run) => {
12767
13131
  pi.registerTool({
12768
13132
  name,
12769
13133
  label,
12770
13134
  description,
12771
13135
  parameters,
13136
+ outputSchema,
13137
+ ...workflowToolExposure(name, codemodeTools),
12772
13138
  async execute(_id, params, signal, _onUpdate, ctx) {
12773
13139
  try {
12774
13140
  const result = await run(params, signal, ctx);
12775
- return { content: [{ type: "text", text: result.text }], details: result.details };
13141
+ return { content: [{ type: "text", text: result.text }], details: result.details, structuredContent: result.structured };
12776
13142
  } catch (error) {
12777
13143
  throw mainAgentError(error);
12778
13144
  }
@@ -12790,18 +13156,21 @@ Respond with workflow_respond.`);
12790
13156
  "Workflow Respond",
12791
13157
  "Approve or reject one pending workflow checkpoint or budget decision",
12792
13158
  Type3.Object({ runId: Type3.String(), name: Type3.Optional(Type3.String()), proposalId: Type3.Optional(Type3.String()), approved: Type3.Boolean() }, { additionalProperties: false }),
13159
+ WORKFLOW_RESPOND_OUTPUT,
12793
13160
  async (params, signal, ctx) => {
12794
13161
  if (params.proposalId) {
12795
13162
  const result = await recovery.answerBudgetDecision(params.runId, params.proposalId, params.approved, false, ctx, signal);
12796
13163
  if (!result) {
12797
13164
  const denied = { state: "budget_exhausted", approved: false, reason: "proposal_not_pending" };
12798
- return { text: JSON.stringify(denied), details: denied };
13165
+ return { text: JSON.stringify(denied), details: denied, structured: denied };
12799
13166
  }
12800
- return { text: completionControlContent(result, params.runId), details: { ...result, reason: params.approved ? "approved" : "rejected" } };
13167
+ const reason = params.approved ? "approved" : "rejected";
13168
+ return { text: completionControlContent(result, params.runId), details: { ...result, reason }, structured: { ...completionControlValue(result, params.runId), reason } };
12801
13169
  }
12802
13170
  if (!params.name) throw new WorkflowError("INVALID_METADATA", "workflow_respond requires name or proposalId");
12803
13171
  const accepted = await answerCheckpoint(params.runId, params.name, params.approved);
12804
- return { text: accepted ? "Checkpoint response accepted." : "Checkpoint is not awaiting a response.", details: { accepted, state: accepted ? "checkpoint_answered" : "not_pending", approved: params.approved, reason: "checkpoint" } };
13172
+ const details = { accepted, state: accepted ? "checkpoint_answered" : "not_pending", approved: params.approved, reason: "checkpoint" };
13173
+ return { text: accepted ? "Checkpoint response accepted." : "Checkpoint is not awaiting a response.", details, structured: details };
12805
13174
  }
12806
13175
  );
12807
13176
  registerControlTool(
@@ -12809,9 +13178,10 @@ Respond with workflow_respond.`);
12809
13178
  "Workflow Stop",
12810
13179
  "Stop an active workflow run by ID",
12811
13180
  Type3.Object({ runId: Type3.String() }, { additionalProperties: false }),
13181
+ WORKFLOW_STOP_OUTPUT,
12812
13182
  async (params) => {
12813
13183
  const result = await stopWorkflowRun(params.runId);
12814
- return { text: JSON.stringify(result), details: result };
13184
+ return { text: JSON.stringify(result), details: result, structured: result };
12815
13185
  }
12816
13186
  );
12817
13187
  registerControlTool(
@@ -12819,9 +13189,10 @@ Respond with workflow_respond.`);
12819
13189
  "Workflow Status",
12820
13190
  "Read a compact summary of a workflow run in the current project",
12821
13191
  WORKFLOW_STATUS_PARAMETERS,
13192
+ WORKFLOW_STATUS_OUTPUT,
12822
13193
  async (params, _signal, ctx) => {
12823
13194
  const result = await workflowStatusRun(params.runId, ctx);
12824
- return { text: JSON.stringify(result), details: result };
13195
+ return { text: JSON.stringify(result), details: result, structured: result };
12825
13196
  }
12826
13197
  );
12827
13198
  let catalogRegistered = false;
@@ -12834,13 +13205,15 @@ Respond with workflow_respond.`);
12834
13205
  if (!catalog.functions.length && !hasAliases && !hasSettings) return;
12835
13206
  pi.registerTool({
12836
13207
  name: "workflow_catalog",
13208
+ ...workflowToolExposure("workflow_catalog", codemodeTools),
13209
+ outputSchema: WORKFLOW_CATALOG_OUTPUT,
12837
13210
  label: "Workflow Catalog",
12838
13211
  description: "List reusable workflow functions and model aliases; pass `name` to load one entry in full",
12839
13212
  parameters: Type3.Object({ name: Type3.Optional(Type3.String({ description: "Registered function or model alias name for full detail" })) }, { additionalProperties: false }),
12840
13213
  async execute(_id, params = {}) {
12841
13214
  const context = { cwd, projectTrusted: trustedProject, globalSettingsPath: workflowSettingsPath(extensionAgentDir) };
12842
13215
  const result = params.name === void 0 ? registry.catalogIndex(context) : registry.catalogDetail(params.name, context);
12843
- return { content: [{ type: "text", text: JSON.stringify(result) }], details: result };
13216
+ return { content: [{ type: "text", text: JSON.stringify(result) }], details: result, structuredContent: result };
12844
13217
  },
12845
13218
  renderCall(args, theme) {
12846
13219
  const title = theme.fg("toolTitle", theme.bold("workflow_catalog"));
@@ -12869,9 +13242,9 @@ Respond with workflow_respond.`);
12869
13242
  const captured = roleCapture(store, snapshot, definitions, project);
12870
13243
  return { definitions, capture: (role, model) => captured.capture(role, model) };
12871
13244
  };
12872
- const activeSnapshotTools = (tools, active) => active === "session" ? new Set(tools.filter((tool) => pi.getActiveTools().includes(tool) && tool !== "workflow_catalog")) : new Set(tools.filter((tool) => active.has(tool) || tool === "workflow_catalog"));
13245
+ const activeSnapshotTools = (tools, active) => active === "session" ? new Set(tools.filter((tool) => reachableTools(pi).includes(tool) && tool !== "workflow_catalog")) : new Set(tools.filter((tool) => active.has(tool) || tool === "workflow_catalog"));
12873
13246
  const resumeLaunchPrologue = async (input) => {
12874
- const active = new Set(pi.getActiveTools().filter((tool) => !INTERNAL_WORKFLOW_TOOLS.includes(tool)));
13247
+ const active = new Set(reachableTools(pi).filter((tool) => !INTERNAL_WORKFLOW_TOOLS.includes(tool)));
12875
13248
  const missing = input.snapshot.tools.filter((tool) => tool !== "workflow_catalog").find((tool) => !active.has(tool));
12876
13249
  if (missing) throw new WorkflowError("RESUME_INCOMPATIBLE", `Required tool is unavailable: ${missing}`);
12877
13250
  const settingsPath = workflowSettingsPath(extensionAgentDir);
@@ -12961,6 +13334,7 @@ Respond with workflow_respond.`);
12961
13334
  resumeRoles,
12962
13335
  workflowAgentHandler,
12963
13336
  shellForRun,
13337
+ toolForRun,
12964
13338
  resolveWorktree,
12965
13339
  checkpointBridge,
12966
13340
  phaseBridge,
@@ -13035,9 +13409,10 @@ Respond with workflow_respond.`);
13035
13409
  "Workflow Retry",
13036
13410
  "Retry a failed workflow run by replaying its completed structural operations",
13037
13411
  WORKFLOW_RETRY_PARAMETERS,
13412
+ WORKFLOW_RECOVERY_OUTPUT,
13038
13413
  async (params, signal, ctx) => {
13039
13414
  const result = await recovery.retryWorkflowRun(params.runId, ctx, signal, params.foreground, params.expectedState);
13040
- return { text: completionControlContent(result), details: result };
13415
+ return { text: completionControlContent(result), details: result, structured: completionControlValue(result) };
13041
13416
  }
13042
13417
  );
13043
13418
  registerControlTool(
@@ -13045,9 +13420,10 @@ Respond with workflow_respond.`);
13045
13420
  "Workflow Resume",
13046
13421
  "Resume an exhausted workflow with unchanged or patched aggregate budgets",
13047
13422
  Type3.Object({ runId: Type3.String(), expectedState: Type3.Optional(Type3.String({ description: "Persisted source state observed before recovery" })), budget: Type3.Optional(Type3.Unknown()), foreground: Type3.Optional(Type3.Boolean({ description: "Override the source launch mode for this recovery" })) }, { additionalProperties: false }),
13423
+ WORKFLOW_RECOVERY_OUTPUT,
13048
13424
  async (params, signal, ctx) => {
13049
13425
  const result = await recovery.resumeWorkflowRun(params.runId, params.budget, ctx, signal, params.foreground, true, params.expectedState);
13050
- return { text: completionControlContent(result), details: result };
13426
+ return { text: completionControlContent(result), details: result, structured: completionControlValue(result) };
13051
13427
  }
13052
13428
  );
13053
13429
  const deliverStaleTerminal = async (store, run) => {
@@ -13179,6 +13555,8 @@ ${content}` };
13179
13555
  description: WORKFLOW_TOOL_DESCRIPTION,
13180
13556
  promptSnippet: WORKFLOW_TOOL_PROMPT_SNIPPET,
13181
13557
  parameters: WORKFLOW_TOOL_PARAMETERS,
13558
+ outputSchema: WORKFLOW_LAUNCH_OUTPUT,
13559
+ prepareLoadout: prepareScriptToolLoadout,
13182
13560
  async execute(toolCallId, params, signal, onUpdate, ctx) {
13183
13561
  let resolveDetached;
13184
13562
  let foregroundStore;
@@ -13188,7 +13566,7 @@ ${content}` };
13188
13566
  }) : void 0;
13189
13567
  const detachedToolResult = (run) => {
13190
13568
  const detached = { runId: run.id, state: "running", detached: true };
13191
- return { content: [{ type: "text", text: JSON.stringify(detached) }], details: { ...detached, run, preview: `Moved workflow ${run.id} to background.` } };
13569
+ return { content: [{ type: "text", text: JSON.stringify(detached) }], details: { ...detached, run, preview: `Moved workflow ${run.id} to background.` }, structuredContent: detached };
13192
13570
  };
13193
13571
  try {
13194
13572
  const headless = object(ctx) && ctx.headless === true;
@@ -13201,7 +13579,7 @@ ${content}` };
13201
13579
  const inventory = modelInventory(rootModel, modelRegistry);
13202
13580
  const knownModels = inventory.knownModels;
13203
13581
  const availableModels = inventory.availableModels;
13204
- const rootTools = pi.getActiveTools().filter((name) => !INTERNAL_WORKFLOW_TOOLS.includes(name));
13582
+ const rootTools = reachableTools(pi).filter((name) => !INTERNAL_WORKFLOW_TOOLS.includes(name));
13205
13583
  const trustedProject = projectTrusted(ctx);
13206
13584
  const launchCwd = typeof ctx.cwd === "string" ? ctx.cwd : process.cwd();
13207
13585
  const launch = workflowLaunchSettings(launchCwd, trustedProject, settingsPath, params.concurrency);
@@ -13219,6 +13597,8 @@ ${content}` };
13219
13597
  if (trustedProject && launch.resolution.project.extensionSettings !== void 0) registry.validateExtensionSettings(launch.resolution.project.extensionSettings, { source: "project", cwd: ctx.cwd, projectTrusted: true, settingsPath: launch.resolution.projectSettingsPath });
13220
13598
  const validated = validateWorkflowLaunchWithRegistry(params, { cwd: ctx.cwd, agentDir: extensionAgentDir, projectTrusted: trustedProject, availableModels, rootTools: new Set(rootTools), modelAliases, knownModels, settingsPath, ...settings.extensionSettings === void 0 ? {} : { extensionSettings: settings.extensionSettings } }, registry);
13221
13599
  const { script, checked, agentDefinitions, projectAgentDefinitions, roleNames } = validated;
13600
+ const toolContext = scriptToolContext(ctx);
13601
+ validateScriptToolReferences(script, toolContext);
13222
13602
  await ensureSessionLease(ctx.cwd, ctx.sessionManager.getSessionId());
13223
13603
  const runId = randomUUID5();
13224
13604
  const args = params.args ?? null;
@@ -13289,7 +13669,7 @@ ${content}` };
13289
13669
  runs.set(runId, runRecord);
13290
13670
  if (params.foreground && onUpdate) onUpdate(workflowToolUpdate((await store.load()).run));
13291
13671
  scheduler.addRun(runId, settings.concurrency, () => runs.get(runId)?.budget.checkAgentLaunch(), settings.extensionSettings);
13292
- const execution = runWorkflow(script, args, withWorkflowFunctions({ shell: (command, options, signal2, identity) => shellForRun(store, checked.metadata, lifecycle, command, options, signal2, identity), agent: workflowAgentHandler(store, checked.metadata, lifecycle, executor, ctx.cwd, runId, (role, model) => capturedRoles.capture(role, model)), worktree: async (owner) => resolveWorktree(store, checked.metadata, owner), checkpoint: checkpointBridge(runId, store, checked.metadata, () => runs.get(runId)?.foreground ?? foregroundAttached, ctx.hasUI ? ctx.ui : void 0, headless), phase: phaseBridge(store, checked.metadata, lifecycle), log: logBridge(store, lifecycle, checked.metadata.name) }, store, runContext, registry, settings.extensionSettings), runController.signal);
13672
+ const execution = runWorkflow(script, args, withWorkflowFunctions({ tool: (identifier, toolArgs, signal2, identity) => toolForRun(store, lifecycle, toolContext, identifier, toolArgs, signal2, identity), shell: (command, options, signal2, identity) => shellForRun(store, checked.metadata, lifecycle, command, options, signal2, identity), agent: workflowAgentHandler(store, checked.metadata, lifecycle, executor, ctx.cwd, runId, (role, model) => capturedRoles.capture(role, model)), worktree: async (owner) => resolveWorktree(store, checked.metadata, owner), checkpoint: checkpointBridge(runId, store, checked.metadata, () => runs.get(runId)?.foreground ?? foregroundAttached, ctx.hasUI ? ctx.ui : void 0, headless), phase: phaseBridge(store, checked.metadata, lifecycle), log: logBridge(store, lifecycle, checked.metadata.name) }, store, runContext, registry, settings.extensionSettings), runController.signal);
13293
13673
  runRecord.execution = execution;
13294
13674
  await eventPublisher.runStarted(store, checked.metadata);
13295
13675
  const finish = execution.result.then(async (value2) => {
@@ -13327,7 +13707,7 @@ ${content}` };
13327
13707
  }, async (error) => {
13328
13708
  await deliveryController.deliverTerminal(store, deliverFailureContent(error), true);
13329
13709
  });
13330
- return { content: [{ type: "text", text: JSON.stringify({ runId, state: "running" }) }], details: { runId, preview: `Started workflow ${runId}.` } };
13710
+ return { content: [{ type: "text", text: JSON.stringify({ runId, state: "running" }) }], details: { runId, preview: `Started workflow ${runId}.` }, structuredContent: { runId, state: "running" } };
13331
13711
  }
13332
13712
  void completion.then(async (result) => {
13333
13713
  await deliveryController.deliverDetachedTerminal(toolCallId, completionContent("background", result));
@@ -13349,7 +13729,7 @@ ${content}` };
13349
13729
  }
13350
13730
  deliveryController.foregroundDeliveries.delete(toolCallId);
13351
13731
  const run = (await store.load()).run;
13352
- return { content: [{ type: "text", text: foregroundDelivery.content }, ...foregroundDelivery.inlined ? [{ type: "text", text: `Workflow run ID: ${runId}` }] : []], details: { runId, value, run } };
13732
+ return { content: [{ type: "text", text: foregroundDelivery.content }, ...foregroundDelivery.inlined ? [{ type: "text", text: `Workflow run ID: ${runId}` }] : []], details: { runId, value, run }, structuredContent: { runId, state: "completed", value } };
13353
13733
  } catch (error) {
13354
13734
  if (params.foreground && foregroundStore && completionInstalled) {
13355
13735
  const claim = await deliveryController.claimForegroundDelivery(foregroundStore, toolCallId);
@@ -13429,7 +13809,7 @@ ${content}` };
13429
13809
  return textBlock(isPartial ? "Workflow starting..." : runDetails?.preview ?? (content?.type === "text" ? content.text : "Workflow finished"));
13430
13810
  }
13431
13811
  };
13432
- pi.registerTool(workflowTool);
13812
+ pi.registerTool({ ...workflowTool, ...workflowToolExposure(workflowTool.name, codemodeTools) });
13433
13813
  registerWorkflowNavigator({ pi, home, clipboard, extensionAgentDir, runs, terminalRunStates, hardTerminalRunStates: HARD_TERMINAL_RUN_STATES, ensureSessionLease, coordinateRunMutation, answerCheckpoint, recovery, stopWorkflowRun, moveForegroundToBackground: deliveryController.moveForegroundToBackground, isForegroundAttached: deliveryController.isForegroundAttached, liveAgents, registry, projectTrusted, resumeHostContext, resumeSelectedWorkflow, reportBlocked: reportWorkflowBlocked, trajectoryProvider, setNavigatorOpen: (open3) => {
13434
13814
  if (open3) backgroundWidgetController.suspend();
13435
13815
  else backgroundWidgetController.resume();
@@ -13458,6 +13838,7 @@ ${content}` };
13458
13838
  }
13459
13839
  var LiveAgentRegistry = class {
13460
13840
  #byRun = /* @__PURE__ */ new Map();
13841
+ #tools = /* @__PURE__ */ new Map();
13461
13842
  get(runId, agentId) {
13462
13843
  return this.#byRun.get(runId)?.get(agentId);
13463
13844
  }
@@ -13486,19 +13867,32 @@ var LiveAgentRegistry = class {
13486
13867
  setEventTime(runId, agentId, timestamp) {
13487
13868
  if (timestamp !== void 0) this.#state(runId, agentId).lastEventAt = timestamp;
13488
13869
  }
13870
+ /** Marks a script tool call in flight until the returned callback runs. */
13871
+ toolStarted(runId, name) {
13872
+ const call = { name, startedAt: Date.now() };
13873
+ const calls = this.#tools.get(runId) ?? /* @__PURE__ */ new Set();
13874
+ this.#tools.set(runId, calls.add(call));
13875
+ return () => {
13876
+ calls.delete(call);
13877
+ if (!calls.size && this.#tools.get(runId) === calls) this.#tools.delete(runId);
13878
+ };
13879
+ }
13489
13880
  deleteRun(runId) {
13490
13881
  this.#byRun.delete(runId);
13882
+ this.#tools.delete(runId);
13491
13883
  }
13492
- /** Overlays live activity and freshness onto a persisted run; returns the input when nothing is live. */
13884
+ /** Overlays live activity, freshness, and script tool calls onto a persisted run; returns the input when nothing is live. */
13493
13885
  overlay(run) {
13886
+ const tools = this.#tools.get(run.id);
13887
+ const withTools = tools?.size ? { ...run, activeTools: [...tools].map((call) => ({ ...call })) } : run;
13494
13888
  const agents = this.#byRun.get(run.id);
13495
- if (!agents?.size) return run;
13889
+ if (!agents?.size) return withTools;
13496
13890
  const next = run.agents.map((agent) => {
13497
13891
  const live = agents.get(agent.id);
13498
13892
  if (!live || live.activity === void 0 && live.lastEventAt === void 0) return agent;
13499
13893
  return { ...agent, ...live.activity === void 0 ? {} : { activity: live.activity }, ...live.lastEventAt === void 0 ? {} : { lastEventAt: live.lastEventAt } };
13500
13894
  });
13501
- return next.some((agent, index) => agent !== run.agents[index]) ? { ...run, agents: next } : run;
13895
+ return next.some((agent, index) => agent !== run.agents[index]) ? { ...withTools, agents: next } : withTools;
13502
13896
  }
13503
13897
  #state(runId, agentId) {
13504
13898
  const agents = this.#byRun.get(runId) ?? /* @__PURE__ */ new Map();
@@ -13835,6 +14229,7 @@ export {
13835
14229
  SessionLease,
13836
14230
  THINKING_LEVELS,
13837
14231
  TOOL_TIMING_ENTRY_TYPE,
14232
+ VIRTUAL_MODEL_PROVIDER,
13838
14233
  WORKFLOW_AGENT_STALL_THRESHOLD_MS,
13839
14234
  WORKFLOW_AGENT_STATE_CHANGED_EVENT,
13840
14235
  WORKFLOW_BLOCKED_EVENT,
@@ -13936,10 +14331,12 @@ export {
13936
14331
  isTurnEnd,
13937
14332
  isWorkflowAuthored,
13938
14333
  isWorkflowErrorCode,
14334
+ jsonDigest,
13939
14335
  jsonObject,
13940
14336
  jsonValue,
13941
14337
  listPersistedSessionIds,
13942
14338
  loadAgentDefinitions,
14339
+ loadCodemodeToolsSetting,
13943
14340
  loadLaunchSnapshot,
13944
14341
  loadProjectAgentDefinitions,
13945
14342
  loadRole,
@@ -13969,6 +14366,7 @@ export {
13969
14366
  parseThinking,
13970
14367
  persistActiveAgentAttempt,
13971
14368
  persistAgentAttempts,
14369
+ physicalModel,
13972
14370
  positiveInteger,
13973
14371
  preflight,
13974
14372
  prepareAgentSetupForInspection,
@@ -13977,6 +14375,7 @@ export {
13977
14375
  progressStyleForState,
13978
14376
  projectSessionsDirectory,
13979
14377
  projectStorageKey,
14378
+ reachableTools,
13980
14379
  readShellResult,
13981
14380
  registerWorkflowExtension,
13982
14381
  registeredWorkflowFunctionSources,
@@ -14002,11 +14401,13 @@ export {
14002
14401
  sameFilesystemPath,
14003
14402
  sanitizeDisplayText,
14004
14403
  saveModelAliases,
14404
+ scriptToolReferences,
14005
14405
  selectResourcesByLayers,
14006
14406
  shellIdentityPath,
14007
14407
  structuralPath,
14008
14408
  sumAccounting,
14009
14409
  themeWorkflowProgressStyles,
14410
+ toolIdentityPath,
14010
14411
  truncateWorkflowProgress,
14011
14412
  unknownModel,
14012
14413
  unmatchedResourcePatterns,
@@ -14042,6 +14443,7 @@ export {
14042
14443
  workflowRoleDirectories,
14043
14444
  workflowScriptArtifact,
14044
14445
  workflowSettingsPath,
14446
+ workflowToolExposure,
14045
14447
  zeroAccounting
14046
14448
  };
14047
14449
  //# sourceMappingURL=index.js.map