pi-subagents 0.82.0 → 0.83.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (93) hide show
  1. package/CHANGELOG.md +46 -0
  2. package/README.md +1 -1
  3. package/bundle/agents.js +4 -4
  4. package/bundle/background-work.js +1 -1
  5. package/bundle/child-tool-plan.js +3 -3
  6. package/bundle/{chunk-7PCKZHR5.js → chunk-4BW55BTL.js} +6 -6
  7. package/bundle/{chunk-7IVSUMQQ.js → chunk-527E3D2J.js} +6 -6
  8. package/bundle/{chunk-NRQ75MGH.js → chunk-5ESXK62X.js} +40 -1076
  9. package/bundle/{chunk-ZQGSDHL6.js → chunk-6B4ITO2W.js} +8 -4
  10. package/bundle/{chunk-CIZQXJ5Q.js → chunk-7F5SK2U3.js} +39 -6
  11. package/bundle/{chunk-OYF6AFDY.js → chunk-BKFWQ642.js} +2 -2
  12. package/bundle/{chunk-ZLINIYTP.js → chunk-CDI3GMNV.js} +48 -32
  13. package/bundle/{chunk-GC4DSPMH.js → chunk-CGXWSNPA.js} +12 -18
  14. package/bundle/{chunk-SIJ4S3EN.js → chunk-CNLDYEH4.js} +12 -12
  15. package/bundle/{chunk-4SDU2BZX.js → chunk-CSSZRTKY.js} +2 -2
  16. package/bundle/{chunk-W2SZ22EM.js → chunk-CZTMUBUI.js} +10 -8
  17. package/bundle/{chunk-WVD4UEL4.js → chunk-HJJAVHFB.js} +28 -24
  18. package/bundle/{chunk-GJECHSAR.js → chunk-HLE22FSH.js} +2 -2
  19. package/bundle/{chunk-M2CTFYT3.js → chunk-HXVKGS33.js} +5 -5
  20. package/bundle/{chunk-5LFLVSTM.js → chunk-JEZOSEWG.js} +2 -0
  21. package/bundle/{chunk-YKEWIB4M.js → chunk-JHZ6DZ5M.js} +1 -1
  22. package/bundle/{chunk-B5EKXDHE.js → chunk-LDMXJIA6.js} +458 -657
  23. package/bundle/{chunk-43SCHIMD.js → chunk-MBDOYZ2V.js} +1 -1
  24. package/bundle/{chunk-XSWOTIA3.js → chunk-MHX22USZ.js} +1 -1
  25. package/bundle/{chunk-VH5OAWVG.js → chunk-OOFW75PV.js} +5 -5
  26. package/bundle/{chunk-RMQFMMAT.js → chunk-P2NCCTZP.js} +231 -10
  27. package/bundle/{chunk-NGGAFDL6.js → chunk-RZJN63GU.js} +12 -5
  28. package/bundle/{chunk-7YMXOYEU.js → chunk-TJQJ3MEA.js} +12 -11
  29. package/bundle/{chunk-5TYMSZX3.js → chunk-VTMNBPCQ.js} +2 -2
  30. package/bundle/{chunk-52UBALJL.js → chunk-WG2OTTAK.js} +2 -2
  31. package/bundle/{chunk-N2BN7BF5.js → chunk-XA3DY6UG.js} +7 -5
  32. package/bundle/{chunk-MHZ4G23A.js → chunk-XXKG3P2W.js} +9 -6
  33. package/bundle/{chunk-PLCCBLPY.js → chunk-Y3I6H6DV.js} +2 -2
  34. package/bundle/control-channel.js +3 -3
  35. package/bundle/index.js +1 -1
  36. package/bundle/intercom-bridge.js +3 -3
  37. package/bundle/preflight.js +9 -9
  38. package/bundle/project-panes.js +4 -4
  39. package/bundle/shared-types.js +1 -1
  40. package/docs/agents.md +6 -7
  41. package/docs/configuration.md +1 -1
  42. package/docs/extension-api.md +19 -4
  43. package/docs/observability.md +1 -0
  44. package/docs/tool-reference.md +1 -1
  45. package/package.json +1 -1
  46. package/skills/council-mode/SKILL.md +1 -4
  47. package/skills/pi-subagents/SKILL.md +130 -107
  48. package/skills/pi-subagents/references/orchestration.md +124 -0
  49. package/skills/pi-subagents/references/prompting-and-roles.md +96 -295
  50. package/src/agents/agent-management.js +8 -14
  51. package/src/agents/agent-serializer.js +1 -2
  52. package/src/agents/agents.js +19 -3
  53. package/src/api/background-work.d.ts +9 -6
  54. package/src/api/background-work.js +14 -7
  55. package/src/extension/index.js +4 -1
  56. package/src/extension/public-execution.d.ts +2 -2
  57. package/src/extension/public-execution.js +3 -3
  58. package/src/extension/reply-workflow-script.js +5 -3
  59. package/src/extension/rpc.js +3 -1
  60. package/src/extension/subagent-guide.js +1 -2
  61. package/src/extension/subagent-options.js +17 -4
  62. package/src/runs/background/async-execution.js +4 -1
  63. package/src/runs/background/async-job-tracker.js +2 -1
  64. package/src/runs/background/async-status.d.ts +2 -0
  65. package/src/runs/background/async-status.js +1 -1
  66. package/src/runs/background/auto-drain.js +2 -3
  67. package/src/runs/background/fleet-view.js +1 -1
  68. package/src/runs/background/run-child-session.js +22 -6
  69. package/src/runs/background/subagent-wait.d.ts +2 -1
  70. package/src/runs/background/subagent-wait.js +5 -2
  71. package/src/runs/background/terminal-run-index.d.ts +1 -0
  72. package/src/runs/background/terminal-run-index.js +40 -1
  73. package/src/runs/foreground/execution.js +22 -6
  74. package/src/runs/foreground/subagent-executor.js +2 -5
  75. package/src/runs/shared/child-launch.js +5 -2
  76. package/src/runs/shared/child-session.d.ts +6 -0
  77. package/src/runs/shared/child-session.js +20 -4
  78. package/src/runs/shared/herdr-machine.d.ts +2 -0
  79. package/src/runs/shared/herdr-machine.js +2 -0
  80. package/src/runs/shared/herdr-placed-run.js +11 -4
  81. package/src/runs/shared/subagent-prompt-runtime.js +11 -17
  82. package/src/shared/disabled-features.d.ts +3 -1
  83. package/src/shared/disabled-features.js +11 -3
  84. package/src/shared/types.d.ts +3 -1
  85. package/src/shared/types.js +2 -0
  86. package/src/tui/fleet.js +1 -0
  87. package/src/workflows/scripted-workflow.js +9 -1070
  88. package/workflow-script-worker.cjs +1067 -0
  89. package/skills/pi-subagents/references/constraints-and-recipes.md +0 -74
  90. package/skills/pi-subagents/references/execution-controls.md +0 -564
  91. package/skills/pi-subagents/references/management-authoring-rpc.md +0 -165
  92. package/skills/pi-subagents/references/multi-lane-orchestration.md +0 -61
  93. package/skills/pi-subagents/references/review-and-validation.md +0 -73
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  registerRuntimeAgent
3
- } from "./chunk-YKEWIB4M.js";
3
+ } from "./chunk-JHZ6DZ5M.js";
4
4
 
5
5
  // src/agents/runtime-agent-events.ts
6
6
  var RUNTIME_AGENT_REGISTER_EVENT = "pi-subagents:runtime-agent-register:v1";
@@ -3,7 +3,7 @@ import {
3
3
  } from "./chunk-7F6RKT6W.js";
4
4
  import {
5
5
  getAgentDir
6
- } from "./chunk-CIZQXJ5Q.js";
6
+ } from "./chunk-7F5SK2U3.js";
7
7
 
8
8
  // src/intercom/intercom-bridge.ts
9
9
  import * as fs from "node:fs";
@@ -39,10 +39,10 @@ import {
39
39
  validateModelResponseAliases,
40
40
  withWorkflowChildRunModel,
41
41
  withWorktreeTransaction
42
- } from "./chunk-NRQ75MGH.js";
42
+ } from "./chunk-5ESXK62X.js";
43
43
  import {
44
44
  validateIntercomBridgeConfig
45
- } from "./chunk-XSWOTIA3.js";
45
+ } from "./chunk-MHX22USZ.js";
46
46
  import {
47
47
  stableJsonDigest
48
48
  } from "./chunk-7F6RKT6W.js";
@@ -73,12 +73,12 @@ import {
73
73
  validateToolBudgetConfig,
74
74
  waitForFileSystemRetry,
75
75
  writeAtomicJson
76
- } from "./chunk-CIZQXJ5Q.js";
76
+ } from "./chunk-7F5SK2U3.js";
77
77
  import {
78
78
  DIRS,
79
79
  RUNNER_LAUNCHER_NAME_PATTERN,
80
80
  TEMP_ROOT_DIR
81
- } from "./chunk-5LFLVSTM.js";
81
+ } from "./chunk-JEZOSEWG.js";
82
82
  import {
83
83
  previewDisplayText,
84
84
  sanitizeDisplayText,
@@ -2454,7 +2454,7 @@ function listAsyncRuns(asyncDirRoot, options = {}, observeStatus) {
2454
2454
  }
2455
2455
  if (wantsTerminal) {
2456
2456
  for (const sessionId of owners ?? [void 0]) {
2457
- for (const entry of readRecentTerminalRunIndex(asyncDirRoot, { sessionId, ...options.entryLimit !== void 0 ? { limit: options.entryLimit } : {} })) indexed.add(entry);
2457
+ for (const entry of readRecentTerminalRunIndex(asyncDirRoot, { sessionId, onCorruptStatus: options.onCorruptStatus, ...options.entryLimit !== void 0 ? { limit: options.entryLimit } : {} })) indexed.add(entry);
2458
2458
  }
2459
2459
  }
2460
2460
  entries = [...indexed];
@@ -1,3 +1,9 @@
1
+ import {
2
+ CODE_OWNED_EXTERNAL_CLI_ADAPTER_IDS,
3
+ getAgentDir,
4
+ getProjectConfigDir
5
+ } from "./chunk-7F5SK2U3.js";
6
+
1
7
  // src/runs/shared/rex-machine.ts
2
8
  import { spawnSync } from "node:child_process";
3
9
  import * as fs from "node:fs";
@@ -67,6 +73,11 @@ function resolveRexLocalPlacement(input) {
67
73
  return machine;
68
74
  }
69
75
 
76
+ // src/runs/shared/herdr-machine.ts
77
+ import { spawnSync as spawnSync3 } from "node:child_process";
78
+ import * as fs3 from "node:fs";
79
+ import * as path3 from "node:path";
80
+
70
81
  // src/runs/shared/herdr-connection.ts
71
82
  import { spawn, spawnSync as spawnSync2 } from "node:child_process";
72
83
  import * as fs2 from "node:fs";
@@ -127,7 +138,7 @@ function runHerdrRemoteCommandAsync(machine, command, options = {}) {
127
138
  return runBoundedCommand(options.sshBin ?? "ssh", [...herdrSshArgs(options.env), machine.target, command], { env: hardenedSshEnv(options.env), timeout: options.timeout, maxBuffer: options.maxBuffer });
128
139
  }
129
140
  function runBoundedCommand(file, args, options) {
130
- return new Promise((resolve2) => {
141
+ return new Promise((resolve3) => {
131
142
  const child = spawn(file, args, { env: options.env, stdio: ["ignore", "pipe", "pipe"], windowsHide: true });
132
143
  const stdout = [], stderr = [], maxBuffer = options.maxBuffer ?? MAX_DISCOVERY_BYTES;
133
144
  let stdoutBytes = 0, stderrBytes = 0, error, settled = false, escalation;
@@ -136,7 +147,7 @@ function runBoundedCommand(file, args, options) {
136
147
  settled = true;
137
148
  clearTimeout(timer);
138
149
  if (escalation) clearTimeout(escalation);
139
- resolve2({ status, stdout: Buffer.concat(stdout).toString("utf8"), stderr: Buffer.concat(stderr).toString("utf8"), ...spawnError ? { error: spawnError } : {} });
150
+ resolve3({ status, stdout: Buffer.concat(stdout).toString("utf8"), stderr: Buffer.concat(stderr).toString("utf8"), ...spawnError ? { error: spawnError } : {} });
140
151
  };
141
152
  const terminate = () => {
142
153
  child.kill("SIGTERM");
@@ -194,7 +205,7 @@ var SocketRpcClient = class {
194
205
  this.#subscriptionAckTimeoutMs = subscriptionAckTimeoutMs;
195
206
  }
196
207
  call(method, params = {}, timeoutMs = 15e3) {
197
- return new Promise((resolve2, reject) => {
208
+ return new Promise((resolve3, reject) => {
198
209
  const socket = net.createConnection(this.#socketPath);
199
210
  let bytes = 0;
200
211
  let data = Buffer.alloc(0);
@@ -207,7 +218,7 @@ var SocketRpcClient = class {
207
218
  finished = true;
208
219
  clearTimeout(timer);
209
220
  socket.destroy();
210
- error ? reject(error) : resolve2(value);
221
+ error ? reject(error) : resolve3(value);
211
222
  };
212
223
  socket.on("connect", () => socket.write(`${JSON.stringify({ id, method, params })}
213
224
  `));
@@ -244,8 +255,8 @@ var SocketRpcClient = class {
244
255
  const id = String(++this.#next);
245
256
  let buffer = Buffer.alloc(0), bytes = 0, acknowledged = false, intentional = false, notified = false, settled = false;
246
257
  let resolveAck, rejectAck;
247
- const acknowledgement = new Promise((resolve2, reject) => {
248
- resolveAck = resolve2;
258
+ const acknowledgement = new Promise((resolve3, reject) => {
259
+ resolveAck = resolve3;
249
260
  rejectAck = reject;
250
261
  });
251
262
  const failHandshake = (error) => {
@@ -339,12 +350,12 @@ async function waitForSocket(socketPath, child) {
339
350
  }
340
351
  } catch {
341
352
  }
342
- await new Promise((resolve2) => setTimeout(resolve2, 25));
353
+ await new Promise((resolve3) => setTimeout(resolve3, 25));
343
354
  }
344
355
  throw new Error("SSH StreamLocal forwarding did not create its local socket.");
345
356
  }
346
357
  async function terminateChild(child) {
347
- const wait = (ms) => child.exitCode !== null || child.signalCode !== null ? Promise.resolve() : Promise.race([new Promise((resolve2) => child.once("exit", () => resolve2())), new Promise((resolve2) => setTimeout(resolve2, ms))]);
358
+ const wait = (ms) => child.exitCode !== null || child.signalCode !== null ? Promise.resolve() : Promise.race([new Promise((resolve3) => child.once("exit", () => resolve3())), new Promise((resolve3) => setTimeout(resolve3, ms))]);
348
359
  if (child.exitCode === null && child.signalCode === null) child.kill("SIGTERM");
349
360
  await wait(1e3);
350
361
  if (child.exitCode === null && child.signalCode === null) {
@@ -397,12 +408,220 @@ async function connectHerdrMachine(machine, options = {}) {
397
408
  return { endpoint, socketPath: primary.socketPath, client: new SocketRpcClient(primary.socketPath), forwardRemoteSocket: sockets.forwardRemoteSocket, close: sockets.close };
398
409
  }
399
410
 
411
+ // src/runs/shared/herdr-machine.ts
412
+ var MAX_MACHINE_NAME_LENGTH = 128;
413
+ var HERDR_MACHINE_LIST_TIMEOUT_MS = 7500;
414
+ var MAX_HERDR_MACHINE_LIST_BYTES = 1024 * 1024;
415
+ var CONTROL_CHARS = /[\u0000-\u001f\u007f]/u;
416
+ var SUPPORTED_MACHINE_ADAPTERS = new Set(CODE_OWNED_EXTERNAL_CLI_ADAPTER_IDS);
417
+ function validateMachineName(value) {
418
+ const trimmed = value.trim();
419
+ if (!trimmed) throw new Error("Herdr machine id or label is required.");
420
+ if (trimmed.length > MAX_MACHINE_NAME_LENGTH) throw new Error(`Herdr machine '${trimmed.slice(0, 24)}\u2026' exceeds ${MAX_MACHINE_NAME_LENGTH} characters.`);
421
+ if (CONTROL_CHARS.test(trimmed)) throw new Error("Herdr machine id or label contains control characters.");
422
+ return trimmed;
423
+ }
424
+ function validateTarget(value, requested) {
425
+ const target = value.trim();
426
+ if (!target || target.startsWith("-") || /[\s\u0000-\u001f\u007f]/u.test(target)) {
427
+ throw new Error(`Herdr machine '${requested}' has an ssh target that cannot be passed safely: ${JSON.stringify(target)}.`);
428
+ }
429
+ return target;
430
+ }
431
+ function isRemoteAbsolute(value) {
432
+ return value.startsWith("/") || value === "~" || value.startsWith("~/");
433
+ }
434
+ function validateRemoteCwd(value, requested) {
435
+ const cwd = value.trim();
436
+ if (!isRemoteAbsolute(cwd)) throw new Error(`Herdr machine '${requested}' cwd must be an absolute POSIX path or start with '~': ${JSON.stringify(cwd)}.`);
437
+ if (CONTROL_CHARS.test(cwd)) throw new Error(`Herdr machine '${requested}' cwd contains control characters.`);
438
+ return cwd.length > 1 ? cwd.replace(/\/+$/u, "") : cwd;
439
+ }
440
+ function parseMachineCatalog(json) {
441
+ let parsed;
442
+ try {
443
+ parsed = JSON.parse(json);
444
+ } catch (error) {
445
+ throw new Error(`Failed to parse herdr machine list --json: ${error instanceof Error ? error.message : String(error)}`);
446
+ }
447
+ const entries = Array.isArray(parsed) ? parsed : parsed && typeof parsed === "object" && Array.isArray(parsed.machines) ? parsed.machines : void 0;
448
+ if (!entries) throw new Error("herdr machine list --json returned no machine list.");
449
+ return entries.flatMap((entry) => {
450
+ if (!entry || typeof entry !== "object" || Array.isArray(entry)) return [];
451
+ const record = entry;
452
+ if (typeof record.id !== "string" || !record.id.trim() || typeof record.target !== "string") return [];
453
+ return [{
454
+ id: record.id.trim(),
455
+ ...typeof record.label === "string" && record.label.trim() ? { label: record.label.trim() } : {},
456
+ target: record.target,
457
+ ...typeof record.session === "string" && record.session.trim() ? { session: record.session.trim() } : {},
458
+ enabled: record.enabled !== false
459
+ }];
460
+ });
461
+ }
462
+ function readHerdrMachineCatalog(env, herdrBin) {
463
+ const result = spawnSync3(herdrBin, ["machine", "list", "--json"], {
464
+ env,
465
+ encoding: "utf-8",
466
+ maxBuffer: MAX_HERDR_MACHINE_LIST_BYTES,
467
+ timeout: HERDR_MACHINE_LIST_TIMEOUT_MS,
468
+ windowsHide: true
469
+ });
470
+ if (result.error) {
471
+ const code = result.error.code;
472
+ if (code === "ENOENT") throw new Error(`Herdr CLI '${herdrBin}' was not found on PATH. Saved-machine placement needs Herdr installed locally.`);
473
+ throw new Error(`Failed to run herdr machine list --json: ${result.error.message}`);
474
+ }
475
+ if (result.status !== 0) throw new Error(`herdr machine list --json exited with code ${result.status}: ${(result.stderr || result.stdout).trim()}`);
476
+ return parseMachineCatalog(result.stdout);
477
+ }
478
+ function selectMachine(catalog, requested) {
479
+ const byId = catalog.find((entry) => entry.id === requested);
480
+ const matches = byId ? [byId] : catalog.filter((entry) => entry.label === requested);
481
+ if (matches.length > 1) throw new Error(`Machine label '${requested}' is ambiguous; use its profile ID.`);
482
+ const machine = matches[0];
483
+ if (!machine) {
484
+ const saved = catalog.filter((entry) => entry.enabled).map((entry) => entry.label ?? entry.id);
485
+ throw new Error(`Herdr machine '${requested}' was not found. Saved machines: ${saved.length ? saved.join(", ") : "none"}. Add one with herdr machine add <target> --label <name>.`);
486
+ }
487
+ if (!machine.enabled) throw new Error(`Machine '${requested}' is disabled. Run herdr machine enable ${machine.id}.`);
488
+ return machine;
489
+ }
490
+ function readJsonObject(filePath) {
491
+ if (!fs3.existsSync(filePath)) return {};
492
+ let parsed;
493
+ try {
494
+ parsed = JSON.parse(fs3.readFileSync(filePath, "utf-8"));
495
+ } catch (error) {
496
+ throw new Error(`Failed to read settings file '${filePath}': ${error instanceof Error ? error.message : String(error)}`);
497
+ }
498
+ return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : {};
499
+ }
500
+ function machineSettingsFrom(settings, keys, filePath) {
501
+ const subagents = settings.subagents;
502
+ if (!subagents || typeof subagents !== "object" || Array.isArray(subagents)) return void 0;
503
+ const machines = subagents.machines;
504
+ if (machines === void 0) return void 0;
505
+ if (!machines || typeof machines !== "object" || Array.isArray(machines)) throw new Error(`Subagent settings in '${filePath}' have invalid 'machines'; expected an object keyed by machine label or id.`);
506
+ const key = keys.find((candidate) => candidate in machines);
507
+ if (key === void 0) return void 0;
508
+ const value = machines[key];
509
+ if (!value || typeof value !== "object" || Array.isArray(value)) throw new Error(`Subagent settings in '${filePath}' have invalid 'machines.${key}'; expected an object with 'cwd' and optional 'env'.`);
510
+ const record = value;
511
+ const entry = {};
512
+ if (record.cwd !== void 0) {
513
+ if (typeof record.cwd !== "string" || !record.cwd.trim()) throw new Error(`Subagent settings in '${filePath}' have invalid 'machines.${key}.cwd'; expected a non-empty string.`);
514
+ entry.cwd = validateRemoteCwd(record.cwd, key);
515
+ }
516
+ if (record.env !== void 0) {
517
+ if (!record.env || typeof record.env !== "object" || Array.isArray(record.env) || Object.entries(record.env).some(([name, item]) => !/^[A-Za-z_][A-Za-z0-9_]*$/u.test(name) || typeof item !== "string")) {
518
+ throw new Error(`Subagent settings in '${filePath}' have invalid 'machines.${key}.env'; expected an object of string values keyed by variable name.`);
519
+ }
520
+ entry.env = { ...record.env };
521
+ if (Object.keys(entry.env).length > 0) {
522
+ throw new Error(`Subagent settings in '${filePath}' set 'machines.${key}.env'. Saved-machine runs use the remote Herdr/Pi environment; configure credentials and environment on '${key}' and remove the local env map.`);
523
+ }
524
+ }
525
+ if (record.rex !== void 0) {
526
+ const rex = record.rex, ssh = rex && typeof rex === "object" && !Array.isArray(rex) && Object.keys(rex).every((field) => field === "ssh") ? rex.ssh : void 0;
527
+ if (typeof ssh !== "string") throw new Error(`Subagent settings in '${filePath}' have invalid 'machines.${key}.rex'; expected { "ssh": "<ssh target>" }.`);
528
+ entry.rex = { ssh: validateTarget(ssh, key) };
529
+ }
530
+ return entry;
531
+ }
532
+ function readMachineSettings(cwd, keys) {
533
+ const userPath = path3.join(getAgentDir(), "settings.json");
534
+ const projectPath = path3.join(getProjectConfigDir(findProjectRootForSettings(cwd)), "settings.json");
535
+ const user = machineSettingsFrom(readJsonObject(userPath), keys, userPath);
536
+ const project = projectPath === userPath ? void 0 : machineSettingsFrom(readJsonObject(projectPath), keys, projectPath);
537
+ if (project?.rex) throw new Error(`Project settings '${projectPath}' set a Rex SSH host in 'machines'. Move the rex entry to your user settings '${userPath}'; only user settings choose which host a task runs on.`);
538
+ if (!user && !project) return void 0;
539
+ return { ...user, ...project };
540
+ }
541
+ function userRexMachine(machine) {
542
+ const userPath = path3.join(getAgentDir(), "settings.json");
543
+ return machineSettingsFrom(readJsonObject(userPath), [machine], userPath)?.rex !== void 0;
544
+ }
545
+ function findProjectRootForSettings(cwd) {
546
+ let current = path3.resolve(cwd);
547
+ while (true) {
548
+ if (fs3.existsSync(path3.join(getProjectConfigDir(current), "settings.json"))) return current;
549
+ const parent = path3.dirname(current);
550
+ if (parent === current) return path3.resolve(cwd);
551
+ current = parent;
552
+ }
553
+ }
554
+ function remoteMachineCwd(stepCwd, root, name) {
555
+ const step = stepCwd?.trim();
556
+ if (step && isRemoteAbsolute(step)) return step;
557
+ if (!root) throw new Error(`No root for ${name} in this repo. Set subagents.machines.${name}.cwd in .pi/settings.json or pass an absolute cwd on that machine.`);
558
+ return step ? path3.posix.join(root, step) : root;
559
+ }
560
+ function resolveHerdrMachinePlacement(input) {
561
+ const requested = validateMachineName(input.machine);
562
+ const catalog = input.catalogJson !== void 0 ? parseMachineCatalog(input.catalogJson) : readHerdrMachineCatalog(input.env ?? process.env, input.herdrBin ?? process.env.HERDR_BIN ?? "herdr");
563
+ const selected = selectMachine(catalog, requested);
564
+ const name = selected.label ?? selected.id;
565
+ const keys = [.../* @__PURE__ */ new Set([requested, ...selected.label ? [selected.label] : [], selected.id])];
566
+ const settings = input.settings ?? readMachineSettings(input.cwd, keys);
567
+ if (settings?.env && Object.keys(settings.env).length > 0) {
568
+ throw new Error(`Saved-machine environment for '${name}' must be configured remotely. Remove machines.${name}.env and configure the remote Herdr/Pi session instead.`);
569
+ }
570
+ const cwd = remoteMachineCwd(input.stepCwd, settings?.cwd, name);
571
+ return {
572
+ machine: {
573
+ provider: "herdr",
574
+ id: selected.id,
575
+ ...selected.label ? { label: selected.label } : {},
576
+ target: validateTarget(selected.target, requested),
577
+ ...selected.session && selected.session !== "default" ? { session: selected.session } : {},
578
+ cwd: validateRemoteCwd(cwd, requested)
579
+ },
580
+ ...settings?.env ? { env: settings.env } : {}
581
+ };
582
+ }
583
+ function resolveMachinePlacement(input) {
584
+ const requested = validateMachineName(input.machine);
585
+ const settings = input.settings ?? readMachineSettings(input.cwd, [requested]);
586
+ if (requested === REX_LOCAL_MACHINE) {
587
+ if (settings?.rex) throw new Error("Machine 'local' is reserved for a Rex pane on this machine; give the Rex SSH host in subagents.machines.local.rex another name.");
588
+ return { machine: resolveRexLocalPlacement({ cwd: input.cwd, stepCwd: input.stepCwd, env: input.env }) };
589
+ }
590
+ if (settings?.rex) return { machine: { provider: "rex", id: requested, target: settings.rex.ssh, cwd: validateRemoteCwd(remoteMachineCwd(input.stepCwd, settings.cwd, requested), requested) } };
591
+ return resolveHerdrMachinePlacement(input);
592
+ }
593
+ var PLACED_RESTORE_UNSUPPORTED = "Pane-native remote Pi does not support fork, resume, or revival; use fresh context.";
594
+ function formatHerdrMachineRunnerUnsupported(input) {
595
+ if (input.machine === void 0) return void 0;
596
+ const name = input.machine.trim();
597
+ let rex;
598
+ try {
599
+ rex = name === REX_LOCAL_MACHINE || userRexMachine(name);
600
+ } catch (error) {
601
+ return error instanceof Error ? error.message : String(error);
602
+ }
603
+ if (rex) {
604
+ if (input.runnerType !== void 0 && input.runnerType !== "pi") return `Agent '${input.agentName}' requested machine '${name}', but Rex supports native Pi children only. Remove the machine or use a native Pi agent.`;
605
+ if (input.worktree === true) return `Agent '${input.agentName}' requested machine '${name}', but managed worktrees cannot be combined with machine placement.`;
606
+ if (process.platform === "win32") return `Machine '${name}' runs the child in a Rex pane, which is not supported on Windows.`;
607
+ return void 0;
608
+ }
609
+ if (input.runnerType !== void 0 && input.runnerType !== "pi" && input.runnerType !== "external-cli") {
610
+ return `Agent '${input.agentName}' requested machine '${input.machine}', but this runner cannot use pane-native Herdr placement. Use native Pi or a built-in Claude, Codex, or Cursor profile.`;
611
+ }
612
+ if (input.runnerType === "external-cli" && (input.adapter === void 0 || !SUPPORTED_MACHINE_ADAPTERS.has(input.adapter))) {
613
+ return `Agent '${input.agentName}' requested machine '${input.machine}', but generic external-cli commands cannot be remote-wrapped safely. Use claude-code, claude-code-writer, codex-exec, codex-exec-writer, cursor-agent, or cursor-agent-writer.`;
614
+ }
615
+ if (input.worktree === true) return `Agent '${input.agentName}' requested machine '${input.machine}', but managed worktrees are local git operations and cannot be combined with a Herdr saved machine.`;
616
+ if (process.platform === "win32") return "Herdr saved-machine pane transport requires hardened OpenSSH StreamLocal forwarding, which is not supported from a Windows host yet.";
617
+ return void 0;
618
+ }
619
+
400
620
  export {
401
621
  remoteShellCommand,
402
622
  runBoundedCommand,
403
623
  sshMachineAccess,
404
624
  connectHerdrMachine,
405
- REX_LOCAL_MACHINE,
406
625
  REX_EXIT_GONE,
407
626
  REX_EXIT_UNREACHABLE,
408
627
  isRexLocalMachine,
@@ -412,5 +631,7 @@ export {
412
631
  rexGlobalArgs,
413
632
  resolveRexBinary,
414
633
  rexCommandEnv,
415
- resolveRexLocalPlacement
634
+ resolveMachinePlacement,
635
+ PLACED_RESTORE_UNSUPPORTED,
636
+ formatHerdrMachineRunnerUnsupported
416
637
  };
@@ -11,19 +11,19 @@ import {
11
11
  validateAuthorityPolicy,
12
12
  validateMissionStoreConfig,
13
13
  validateModelResponseAliases
14
- } from "./chunk-NRQ75MGH.js";
14
+ } from "./chunk-5ESXK62X.js";
15
15
  import {
16
16
  getAgentDir,
17
17
  readStatus,
18
18
  validatePermissionConfig,
19
19
  writePrivateAtomicJson
20
- } from "./chunk-CIZQXJ5Q.js";
20
+ } from "./chunk-7F5SK2U3.js";
21
21
  import {
22
22
  FLEET_KEYBINDING_ACTIONS,
23
23
  RUNNER_LAUNCHER_NAME_PATTERN,
24
24
  RUNNER_LAUNCHER_NAME_RULE,
25
25
  TEMP_ROOT_DIR
26
- } from "./chunk-5LFLVSTM.js";
26
+ } from "./chunk-JEZOSEWG.js";
27
27
 
28
28
  // src/shared/disabled-features.ts
29
29
  var SUBAGENT_FEATURES = {
@@ -104,16 +104,22 @@ function disabledFeatureUseError(request, surface, label = "subagent") {
104
104
  }
105
105
  return void 0;
106
106
  }
107
+ var WORKFLOW_SCRIPTS_REPLACEMENT = "Instead of workflow scripts, use the top-level tasks or chain fields.";
107
108
  function disabledFeatureNotice(surface) {
108
109
  if (surface.features.size === 0) return void 0;
109
110
  const lines = [...surface.features].map((feature) => {
110
111
  const { actions, params, disabledBy } = featureSurface(feature);
111
112
  const parts = [`options ${params.join(", ")}`, ...actions.length > 0 ? [`actions ${actions.join(", ")}`] : []];
112
- return `- ${disabledBy}: ${parts.join("; ")}`;
113
+ return `- ${disabledBy}: ${parts.join("; ")}${feature === "workflow-scripts" ? `. ${WORKFLOW_SCRIPTS_REPLACEMENT}` : ""}`;
113
114
  });
114
- return `Disabled by config in this session. The reference below still lists these, but calls that use them are rejected:
115
+ return `Disabled by config in this session. The guide below may still describe these, but calls that use them are rejected:
115
116
  ${lines.join("\n")}`;
116
117
  }
118
+ function disabledFeatureSectionNotice(surface) {
119
+ if (surface.features.size === 0) return void 0;
120
+ const replacement = surface.features.has("workflow-scripts") ? ` ${WORKFLOW_SCRIPTS_REPLACEMENT}` : "";
121
+ return `Some options and actions are disabled by config in this session; guide topic tool-reference lists them.${replacement}`;
122
+ }
117
123
 
118
124
  // src/runs/background/active-async-capacity.ts
119
125
  import { createHash, randomUUID } from "node:crypto";
@@ -729,6 +735,7 @@ export {
729
735
  resolveDisabledFeatureSurface,
730
736
  disabledFeatureUseError,
731
737
  disabledFeatureNotice,
738
+ disabledFeatureSectionNotice,
732
739
  ActiveAsyncCapacityError,
733
740
  resolveMaxActiveAsyncRunsPerSession,
734
741
  resolveAbandonedSlotReleaseAfterMs,
@@ -6,35 +6,35 @@ import {
6
6
  resolveWorkflowForegroundSteeringTarget,
7
7
  steerAsyncRun,
8
8
  steerWorkflowForegroundTarget
9
- } from "./chunk-7IVSUMQQ.js";
9
+ } from "./chunk-527E3D2J.js";
10
10
  import {
11
11
  FLEET_STATUS_WIDGET_KEY,
12
12
  reportFleetWarning,
13
13
  runningTone,
14
14
  stepTaskSummary
15
- } from "./chunk-M2CTFYT3.js";
15
+ } from "./chunk-HXVKGS33.js";
16
16
  import {
17
17
  foregroundChildStateLabel,
18
18
  formatAsyncRunTranscript,
19
19
  stopAsyncRun
20
- } from "./chunk-7PCKZHR5.js";
20
+ } from "./chunk-4BW55BTL.js";
21
21
  import {
22
22
  contextModeBadge,
23
23
  contextModeLabel,
24
24
  decodeUtf8Tail,
25
25
  listAsyncRuns
26
- } from "./chunk-VH5OAWVG.js";
27
- import "./chunk-NRQ75MGH.js";
28
- import "./chunk-OYF6AFDY.js";
26
+ } from "./chunk-OOFW75PV.js";
27
+ import "./chunk-5ESXK62X.js";
28
+ import "./chunk-BKFWQ642.js";
29
29
  import "./chunk-NIXWDZZV.js";
30
- import "./chunk-XSWOTIA3.js";
30
+ import "./chunk-MHX22USZ.js";
31
31
  import "./chunk-7F6RKT6W.js";
32
32
  import "./chunk-CTPAE36Y.js";
33
33
  import "./chunk-7J4ZXMY7.js";
34
34
  import {
35
35
  getArtifactPaths,
36
36
  getArtifactsDir
37
- } from "./chunk-5TYMSZX3.js";
37
+ } from "./chunk-VTMNBPCQ.js";
38
38
  import {
39
39
  snapshotExternalRuns
40
40
  } from "./chunk-UKG4V45O.js";
@@ -46,10 +46,10 @@ import {
46
46
  formatTokens,
47
47
  readStatus,
48
48
  shortenPath
49
- } from "./chunk-CIZQXJ5Q.js";
49
+ } from "./chunk-7F5SK2U3.js";
50
50
  import {
51
51
  DIRS
52
- } from "./chunk-5LFLVSTM.js";
52
+ } from "./chunk-JEZOSEWG.js";
53
53
  import "./chunk-YFCK7H2S.js";
54
54
  import "./chunk-Z7KVHQHL.js";
55
55
 
@@ -302,7 +302,8 @@ function collectFleetSnapshot(state, options = {}) {
302
302
  ...state.currentSessionId ? { sessionId: state.currentSessionId } : {},
303
303
  entryLimit: MAX_FLEET_HISTORY_CANDIDATES,
304
304
  resultsDir: options.resultsDir ?? DIRS.results,
305
- reconcile: false
305
+ reconcile: false,
306
+ onCorruptStatus: (warning) => reportFleetWarning(state, warning)
306
307
  }).filter((run) => !trackedIds.has(run.id));
307
308
  runs = [...trackedRuns, ...history];
308
309
  } else {
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  getAgentDir
3
- } from "./chunk-CIZQXJ5Q.js";
3
+ } from "./chunk-7F5SK2U3.js";
4
4
  import {
5
5
  TEMP_ARTIFACTS_DIR
6
- } from "./chunk-5LFLVSTM.js";
6
+ } from "./chunk-JEZOSEWG.js";
7
7
 
8
8
  // src/shared/artifacts.ts
9
9
  import * as fs from "node:fs";
@@ -3,11 +3,11 @@ import {
3
3
  } from "./chunk-7J4ZXMY7.js";
4
4
  import {
5
5
  getProjectSubagentsDir
6
- } from "./chunk-5TYMSZX3.js";
6
+ } from "./chunk-VTMNBPCQ.js";
7
7
  import {
8
8
  getPiSpawnCommand,
9
9
  writeAtomicJson
10
- } from "./chunk-CIZQXJ5Q.js";
10
+ } from "./chunk-7F5SK2U3.js";
11
11
 
12
12
  // src/inspectors/herdr/project-panes.ts
13
13
  import * as fs from "node:fs";
@@ -104,14 +104,16 @@ function listBackgroundWorkWakeChannels() {
104
104
  }
105
105
  return [...channels];
106
106
  }
107
- function snapshotBackgroundWork(sessionId, nowMs = Date.now()) {
108
- validateString(sessionId, "Background-work snapshot sessionId", MAX_SESSION_ID_LENGTH);
107
+ function snapshotBackgroundWork(sessionIds, nowMs = Date.now()) {
108
+ const accepted = [...new Set(sessionIds.map((value) => validateString(value, "Background-work snapshot sessionId", MAX_SESSION_ID_LENGTH)))];
109
+ const sessionId = accepted[0];
110
+ if (sessionId === void 0) throw new Error("Background-work snapshot needs at least one session id.");
109
111
  const providers = listBackgroundWorkProviders();
110
112
  const items = [];
111
113
  const identities = /* @__PURE__ */ new Set();
112
114
  for (const provider of providers) {
113
115
  try {
114
- provider.reconcile?.({ sessionId, nowMs });
116
+ provider.reconcile?.({ sessionId, sessionIds: [...accepted], nowMs });
115
117
  } catch (error) {
116
118
  throw new Error(
117
119
  `Background-work provider '${provider.name}' reconcile failed: ${error instanceof Error ? error.message : String(error)}`,
@@ -120,7 +122,7 @@ function snapshotBackgroundWork(sessionId, nowMs = Date.now()) {
120
122
  }
121
123
  let active;
122
124
  try {
123
- active = provider.listActiveWork({ sessionId, nowMs });
125
+ active = provider.listActiveWork({ sessionId, sessionIds: [...accepted], nowMs });
124
126
  } catch (error) {
125
127
  throw new Error(
126
128
  `Background-work provider '${provider.name}' listActiveWork failed: ${error instanceof Error ? error.message : String(error)}`,
@@ -140,7 +142,7 @@ function snapshotBackgroundWork(sessionId, nowMs = Date.now()) {
140
142
  throw new Error(`Background-work provider '${provider.name}' returned duplicate item '${item.id}' for session '${item.sessionId}'.`);
141
143
  }
142
144
  identities.add(identity);
143
- if (item.sessionId === sessionId) items.push({ provider: provider.name, ...item });
145
+ if (accepted.includes(item.sessionId)) items.push({ provider: provider.name, ...item });
144
146
  });
145
147
  }
146
148
  return {
@@ -1,4 +1,5 @@
1
1
  import {
2
+ PLACED_RESTORE_UNSUPPORTED,
2
3
  REX_BUNDLE_BIN,
3
4
  REX_CALL_TIMEOUT_MS,
4
5
  REX_EXIT_GONE,
@@ -12,10 +13,10 @@ import {
12
13
  rexGlobalArgs,
13
14
  runBoundedCommand,
14
15
  sshMachineAccess
15
- } from "./chunk-RMQFMMAT.js";
16
+ } from "./chunk-P2NCCTZP.js";
16
17
  import {
17
18
  package_default
18
- } from "./chunk-ZQGSDHL6.js";
19
+ } from "./chunk-6B4ITO2W.js";
19
20
  import {
20
21
  HERDR_PI_MODE_ENV,
21
22
  HERDR_PI_PROTOCOL,
@@ -28,8 +29,8 @@ import {
28
29
  import {
29
30
  getAgentDir,
30
31
  getPiSpawnCommand
31
- } from "./chunk-CIZQXJ5Q.js";
32
- import "./chunk-5LFLVSTM.js";
32
+ } from "./chunk-7F5SK2U3.js";
33
+ import "./chunk-JEZOSEWG.js";
33
34
  import "./chunk-YFCK7H2S.js";
34
35
  import "./chunk-Z7KVHQHL.js";
35
36
 
@@ -840,11 +841,11 @@ function safeRunId(value) {
840
841
  return validateHerdrPiRunId(`${prefix}_${randomBytes2(8).toString("hex")}`);
841
842
  }
842
843
  function serializeHerdrPiLaunch(launch) {
843
- if (launch.storage.kind === "file") throw new Error("Pane-native remote Pi does not support fork, resume, or revival; use fresh context.");
844
+ if (launch.storage.kind === "file" && fs3.existsSync(launch.storage.sessionFile)) throw new Error(PLACED_RESTORE_UNSUPPORTED);
844
845
  if (launch.extensionPaths.length || launch.requiredExtensions?.length) throw new Error("Pane-native remote Pi cannot transfer local extension paths; install/configure pi-subagents on the remote machine.");
845
846
  if (launch.processEnv && Object.values(launch.processEnv).some((value) => value !== void 0 && value !== "__none__")) throw new Error("Pane-native remote Pi cannot transfer local bindings, MCP selections, or process environment.");
846
847
  if (launch.runtime.nestedRoute || launch.runtime.fanoutChild || launch.runtime.depth > 1) throw new Error("Nested delegation from a pane-native remote Pi is not supported.");
847
- if (launch.excludeTools?.length || launch.runtime.permissions || launch.runtime.toolBudget || launch.runtime.requiredExtensions || launch.runtime.requiredTools?.length || launch.runtime.mcpDirectTools?.length) throw new Error("Pane-native remote Pi cannot yet represent exclusions, permission rules, tool budgets, MCP direct tools, or required-tool/extension contracts; remove machine placement.");
848
+ if (launch.excludeTools?.length || launch.runtime.permissions || launch.runtime.toolBudget || launch.runtime.requiredExtensions || launch.runtime.mcpDirectTools?.length) throw new Error("Pane-native remote Pi cannot yet represent exclusions, permission rules, tool budgets, MCP direct tools, or required-extension contracts; remove machine placement.");
848
849
  const resources = launch.remoteResources;
849
850
  if (!resources || !/^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/u.test(resources.agent) || resources.skills?.some((name) => !/^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/u.test(name)) || resources.toolCeiling?.some((name) => !/^[A-Za-z0-9_-]{1,64}$/u.test(name)) || resources.reads !== void 0 && resources.reads !== false && resources.reads.some((read) => typeof read !== "string" || Buffer.byteLength(read) > 4096)) throw new Error("Pane-native remote resources require bounded logical agent, skill, tool, and read names.");
850
851
  const args = [];
@@ -1254,6 +1255,8 @@ async function startPlacedPiSession(launch, owner, policy) {
1254
1255
  const configured = await Promise.race([bridge.configure(policy.resources), bridge.failure]);
1255
1256
  if (configured.nativeSessionId !== manifest.nativeSessionId || configured.agent !== policy.resources.agent) throw new Error("Remote Pi resource resolution identity mismatch.");
1256
1257
  if (!Array.isArray(configured.tools) || policy.resources.toolCeiling && configured.tools.some((tool) => !policy.resources.toolCeiling.includes(String(tool)))) throw new Error("Remote Pi active-tool ceiling acknowledgement mismatch.");
1258
+ const missingRequired = (launch.runtime.requiredTools ?? []).filter((tool) => !configured.tools.includes(tool));
1259
+ if (missingRequired.length) throw new Error(`Remote Pi agent '${policy.resources.agent}' did not activate required tools: ${missingRequired.join(", ")}.`);
1257
1260
  if (launch.model && configured.model !== launch.model.split(":")[0]) throw new Error(`Remote Pi model '${launch.model}' did not resolve to the requested provider/model.`);
1258
1261
  const identity = { runId, machineId: machine.id, target: isRexLocalMachine(machine) ? machine.id : machine.target, session: owner.host.sessionOf(connection), workspaceId: owned.workspaceId, tabId: owned.tabId, paneId: owned.paneId, terminalId, agentName, nativeSessionId: manifest.nativeSessionId, cwd: machine.cwd, runtimeDir };
1259
1262
  owner.journal(identity);
@@ -1,12 +1,12 @@
1
1
  import {
2
2
  getProjectSubagentsDir
3
- } from "./chunk-5TYMSZX3.js";
3
+ } from "./chunk-VTMNBPCQ.js";
4
4
  import {
5
5
  discoverAgents,
6
6
  formatUnknownAgentError,
7
7
  resolveAgentName,
8
8
  unknownAgentDiagnosticContext
9
- } from "./chunk-CIZQXJ5Q.js";
9
+ } from "./chunk-7F5SK2U3.js";
10
10
 
11
11
  // src/agents/agent-refinements.ts
12
12
  import { createHash, randomUUID } from "node:crypto";
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  requestAsyncStop
3
- } from "./chunk-OYF6AFDY.js";
3
+ } from "./chunk-BKFWQ642.js";
4
4
  import "./chunk-NIXWDZZV.js";
5
- import "./chunk-CIZQXJ5Q.js";
6
- import "./chunk-5LFLVSTM.js";
5
+ import "./chunk-7F5SK2U3.js";
6
+ import "./chunk-JEZOSEWG.js";
7
7
  import "./chunk-YFCK7H2S.js";
8
8
  import "./chunk-Z7KVHQHL.js";
9
9
  export {
package/bundle/index.js CHANGED
@@ -17,7 +17,7 @@ function readInstalledPackageVersion() {
17
17
  var loadedPackageVersion = readInstalledPackageVersion();
18
18
 
19
19
  // index.ts
20
- var registerExtension = process.env[HERDR_PI_MODE_ENV] === "1" ? (await import("./chunk-SIJ4S3EN.js")).default : process.env.PI_SUBAGENT_CHILD === "1" ? void 0 : (await import("./chunk-WVD4UEL4.js")).default;
20
+ var registerExtension = process.env[HERDR_PI_MODE_ENV] === "1" ? (await import("./chunk-CNLDYEH4.js")).default : process.env.PI_SUBAGENT_CHILD === "1" ? void 0 : (await import("./chunk-HJJAVHFB.js")).default;
21
21
  function registerSubagentExtension(pi) {
22
22
  const installedVersion = readInstalledPackageVersion();
23
23
  if (installedVersion !== loadedPackageVersion) {