pi-subagents 0.81.0 → 0.82.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 (141) hide show
  1. package/CHANGELOG.md +38 -0
  2. package/bundle/agents.js +3 -3
  3. package/bundle/child-tool-plan.js +2 -2
  4. package/bundle/{chunk-LSRUTHT7.js → chunk-43SCHIMD.js} +1 -1
  5. package/bundle/{chunk-G5DKFLLI.js → chunk-4SDU2BZX.js} +15 -10
  6. package/bundle/{chunk-V2JWDB5U.js → chunk-52UBALJL.js} +2 -2
  7. package/bundle/{chunk-3DC5KBVP.js → chunk-5TYMSZX3.js} +1 -1
  8. package/bundle/{chunk-CEKS3TFO.js → chunk-7IVSUMQQ.js} +6 -6
  9. package/bundle/{chunk-KSWQVGDW.js → chunk-7PCKZHR5.js} +225 -18
  10. package/bundle/{chunk-563BHAZ5.js → chunk-7YMXOYEU.js} +33 -25
  11. package/bundle/{chunk-XMFLCKMQ.js → chunk-B5EKXDHE.js} +336 -130
  12. package/bundle/{chunk-ZBC44JJ3.js → chunk-CIZQXJ5Q.js} +22 -9
  13. package/bundle/{chunk-L5ZHE4L4.js → chunk-GC4DSPMH.js} +4 -4
  14. package/bundle/{chunk-ARRKBB2B.js → chunk-GJECHSAR.js} +2 -2
  15. package/bundle/{chunk-2ZSNH26H.js → chunk-M2CTFYT3.js} +7 -7
  16. package/bundle/{chunk-TUWLXTAM.js → chunk-MHZ4G23A.js} +681 -320
  17. package/bundle/{chunk-WSBNENFS.js → chunk-NGGAFDL6.js} +14 -4
  18. package/bundle/{chunk-VJHOELQP.js → chunk-NRQ75MGH.js} +160 -78
  19. package/bundle/{chunk-BJN5LUQY.js → chunk-OYF6AFDY.js} +1 -1
  20. package/bundle/{chunk-EYNJ6VL6.js → chunk-PLCCBLPY.js} +2 -2
  21. package/bundle/{chunk-74RT5YQE.js → chunk-RMQFMMAT.js} +123 -33
  22. package/bundle/{chunk-V6MR5TOQ.js → chunk-SIJ4S3EN.js} +25 -19
  23. package/bundle/{chunk-NNLASTQE.js → chunk-VH5OAWVG.js} +19 -5
  24. package/bundle/{chunk-BPS2AGBZ.js → chunk-W2SZ22EM.js} +93 -52
  25. package/bundle/{chunk-V3BIUPLU.js → chunk-WVD4UEL4.js} +256 -52
  26. package/bundle/{chunk-ZP3HODFH.js → chunk-XSWOTIA3.js} +1 -1
  27. package/bundle/{chunk-3IF2PGW7.js → chunk-YKEWIB4M.js} +1 -1
  28. package/bundle/{chunk-26C3WLYQ.js → chunk-ZLINIYTP.js} +497 -272
  29. package/bundle/{chunk-2XYWN7QJ.js → chunk-ZQGSDHL6.js} +2 -2
  30. package/bundle/control-channel.js +2 -2
  31. package/bundle/index.js +1 -1
  32. package/bundle/intercom-bridge.js +2 -2
  33. package/bundle/preflight.js +8 -8
  34. package/bundle/project-panes.js +3 -3
  35. package/docs/agents.md +31 -0
  36. package/docs/configuration.md +9 -1
  37. package/docs/tool-reference.md +3 -1
  38. package/docs/workflows.md +16 -0
  39. package/package.json +2 -2
  40. package/skills/pi-subagents/references/execution-controls.md +5 -4
  41. package/src/agents/agent-management.js +3 -3
  42. package/src/agents/agent-memory.js +3 -1
  43. package/src/agents/agents.js +6 -6
  44. package/src/agents/frontmatter.d.ts +2 -0
  45. package/src/agents/frontmatter.js +15 -1
  46. package/src/agents/skills.js +2 -1
  47. package/src/extension/config.js +3 -0
  48. package/src/extension/herdr-pi-bridge.js +17 -10
  49. package/src/extension/index.js +82 -2
  50. package/src/extension/schemas.js +3 -3
  51. package/src/intercom/native-supervisor-channel.js +20 -0
  52. package/src/missions/lifecycle.js +7 -1
  53. package/src/runs/background/active-async-capacity.d.ts +1 -0
  54. package/src/runs/background/active-async-capacity.js +9 -3
  55. package/src/runs/background/async-execution.js +20 -10
  56. package/src/runs/background/async-job-tracker.d.ts +6 -1
  57. package/src/runs/background/async-job-tracker.js +9 -4
  58. package/src/runs/background/async-status.d.ts +2 -0
  59. package/src/runs/background/async-status.js +21 -2
  60. package/src/runs/background/await-async-run.js +3 -7
  61. package/src/runs/background/boot-id.d.ts +12 -0
  62. package/src/runs/background/boot-id.js +47 -0
  63. package/src/runs/background/interrupted-elsewhere.d.ts +22 -0
  64. package/src/runs/background/interrupted-elsewhere.js +68 -0
  65. package/src/runs/background/notify.d.ts +2 -0
  66. package/src/runs/background/notify.js +13 -0
  67. package/src/runs/background/result-delivery-ownership.d.ts +6 -0
  68. package/src/runs/background/result-delivery-ownership.js +19 -1
  69. package/src/runs/background/result-watcher.d.ts +3 -1
  70. package/src/runs/background/result-watcher.js +24 -8
  71. package/src/runs/background/run-child-session.d.ts +1 -6
  72. package/src/runs/background/run-child-session.js +1 -1
  73. package/src/runs/background/run-status.js +2 -1
  74. package/src/runs/background/stale-run-reconciler.d.ts +25 -0
  75. package/src/runs/background/stale-run-reconciler.js +52 -13
  76. package/src/runs/background/subagent-runner-bootstrap.js +3 -0
  77. package/src/runs/background/subagent-runner.d.ts +4 -0
  78. package/src/runs/background/subagent-runner.js +13 -5
  79. package/src/runs/background/subagent-wait.d.ts +1 -1
  80. package/src/runs/background/subagent-wait.js +6 -1
  81. package/src/runs/background/terminal-run-index.d.ts +1 -0
  82. package/src/runs/background/terminal-run-index.js +4 -2
  83. package/src/runs/foreground/async-stop-action.js +3 -1
  84. package/src/runs/foreground/execution.js +6 -3
  85. package/src/runs/foreground/foreground-history.d.ts +16 -2
  86. package/src/runs/foreground/foreground-history.js +133 -31
  87. package/src/runs/foreground/subagent-executor.d.ts +11 -0
  88. package/src/runs/foreground/subagent-executor.js +277 -74
  89. package/src/runs/shared/child-launch.d.ts +2 -2
  90. package/src/runs/shared/child-session.d.ts +3 -2
  91. package/src/runs/shared/child-session.js +51 -8
  92. package/src/runs/shared/child-tool-plan.d.ts +7 -0
  93. package/src/runs/shared/child-tool-plan.js +21 -6
  94. package/src/runs/shared/herdr-connection.d.ts +17 -8
  95. package/src/runs/shared/herdr-connection.js +19 -7
  96. package/src/runs/shared/herdr-external-adapters.d.ts +3 -2
  97. package/src/runs/shared/herdr-external-adapters.js +1 -1
  98. package/src/runs/shared/herdr-machine.d.ts +12 -2
  99. package/src/runs/shared/herdr-machine.js +55 -9
  100. package/src/runs/shared/herdr-pane-host.d.ts +88 -0
  101. package/src/runs/shared/herdr-pane-host.js +155 -0
  102. package/src/runs/shared/herdr-placed-run.d.ts +27 -150
  103. package/src/runs/shared/herdr-placed-run.js +48 -395
  104. package/src/runs/shared/local-machine-access.d.ts +5 -0
  105. package/src/runs/shared/local-machine-access.js +28 -0
  106. package/src/runs/shared/orca-progress-tabs.d.ts +0 -1
  107. package/src/runs/shared/orca-progress-tabs.js +1 -24
  108. package/src/runs/shared/parallel-utils.d.ts +1 -1
  109. package/src/runs/shared/placed-run-owner.d.ts +49 -0
  110. package/src/runs/shared/placed-run-owner.js +93 -0
  111. package/src/runs/shared/placement-host.d.ts +99 -0
  112. package/src/runs/shared/placement-host.js +157 -0
  113. package/src/runs/shared/rex-machine.d.ts +25 -0
  114. package/src/runs/shared/rex-machine.js +83 -0
  115. package/src/runs/shared/rex-pane-host.d.ts +62 -0
  116. package/src/runs/shared/rex-pane-host.js +327 -0
  117. package/src/shared/completion-owner.d.ts +5 -0
  118. package/src/shared/completion-owner.js +5 -0
  119. package/src/shared/session-identity.d.ts +2 -0
  120. package/src/shared/session-identity.js +26 -0
  121. package/src/shared/types.d.ts +45 -6
  122. package/src/tui/fleet.d.ts +1 -0
  123. package/src/tui/fleet.js +24 -15
  124. package/src/tui/render.d.ts +6 -0
  125. package/src/tui/render.js +3 -3
  126. package/src/tui/subagent-messages.js +1 -0
  127. package/src/workflows/scripted-workflow.d.ts +1 -1
  128. package/src/workflows/workflow-auto-resume.d.ts +32 -0
  129. package/src/workflows/workflow-auto-resume.js +102 -0
  130. package/src/workflows/workflow-launch-record.d.ts +27 -0
  131. package/src/workflows/workflow-launch-record.js +48 -0
  132. package/src/workflows/workflow-resume-claim.d.ts +82 -0
  133. package/src/workflows/workflow-resume-claim.js +210 -0
  134. package/src/workflows/workflow-resume.d.ts +56 -0
  135. package/src/workflows/workflow-resume.js +106 -0
  136. package/src/workflows/workflow-reuse.d.ts +15 -9
  137. package/src/workflows/workflow-reuse.js +77 -14
  138. package/src/workflows/workflow-revival.d.ts +2 -0
  139. package/src/workflows/workflow-revival.js +2 -1
  140. package/src/workflows/workflow-stop-cause.d.ts +6 -0
  141. package/src/workflows/workflow-stop-cause.js +8 -0
@@ -1,9 +1,78 @@
1
- // src/runs/shared/herdr-connection.ts
2
- import { spawn, spawnSync } from "node:child_process";
1
+ // src/runs/shared/rex-machine.ts
2
+ import { spawnSync } from "node:child_process";
3
3
  import * as fs from "node:fs";
4
- import * as net from "node:net";
5
4
  import * as os from "node:os";
6
5
  import * as path from "node:path";
6
+ var REX_LOCAL_MACHINE = "local";
7
+ var REX_EXIT_GONE = 3;
8
+ var REX_EXIT_UNREACHABLE = 4;
9
+ function isRexLocalMachine(machine) {
10
+ return machine?.provider === "rex" && !("target" in machine);
11
+ }
12
+ var REX_BUNDLE_BIN = "Library/Application Support/com.superlogical.rex/RexServer/Versions/Current/Rex Server.app/Contents/MacOS/rex";
13
+ var REX_CALL_TIMEOUT_MS = 15e3;
14
+ var REX_MAX_OUTPUT = 1024 * 1024;
15
+ function rexGlobalArgs(server) {
16
+ return server ? ["--autostart=false", "-S", server] : ["--autostart=false"];
17
+ }
18
+ function executable(file) {
19
+ try {
20
+ fs.accessSync(file, fs.constants.X_OK);
21
+ return fs.statSync(file).isFile();
22
+ } catch {
23
+ return false;
24
+ }
25
+ }
26
+ function resolveRexBinary(env = process.env) {
27
+ const fromDir = env.REX_BIN_DIR ? [path.join(env.REX_BIN_DIR, "rex")] : [];
28
+ const fromPath = (env.PATH ?? "").split(path.delimiter).filter((dir) => path.isAbsolute(dir)).map((dir) => path.join(dir, "rex"));
29
+ const found = [...fromDir, ...fromPath, path.join(os.homedir(), REX_BUNDLE_BIN)].find(executable);
30
+ if (!found) throw new Error("The Rex CLI was not found: set REX_BIN_DIR, put rex on PATH, or install Rex.");
31
+ return found;
32
+ }
33
+ function rexCommandEnv(source = process.env) {
34
+ const { REX_SESSION: _session, REX_BLOCK: _block, REX_SERVER: _server, ...env } = source;
35
+ return env;
36
+ }
37
+ function sessionConfirmed(stdout, session) {
38
+ try {
39
+ const parsed = JSON.parse(stdout);
40
+ return parsed?.session_id === session;
41
+ } catch {
42
+ return false;
43
+ }
44
+ }
45
+ function resolveRexLocalPlacement(input) {
46
+ if (process.platform === "win32") throw new Error("Machine 'local' runs the child in a Rex pane, which is not supported on Windows.");
47
+ const env = input.env ?? process.env, session = env.REX_SESSION?.trim(), server = env.REX_SERVER?.trim();
48
+ if (!session) throw new Error("Machine 'local' opens the child in a new tab of this Rex session, but this Pi is not running inside Rex (REX_SESSION is not set).");
49
+ const result = spawnSync(resolveRexBinary(env), [...rexGlobalArgs(server), "session", "inspect", session, "--json"], { encoding: "utf8", env: rexCommandEnv(env), timeout: REX_CALL_TIMEOUT_MS, maxBuffer: REX_MAX_OUTPUT, windowsHide: true });
50
+ if (result.error) throw new Error(`Could not ask Rex about this session: ${result.error.message}`, { cause: result.error });
51
+ if (result.status === REX_EXIT_GONE) throw new Error(`Machine 'local' needs this Pi's Rex session, but the Rex server does not know session ${session}.`);
52
+ if (result.status === REX_EXIT_UNREACHABLE) throw new Error("Machine 'local' needs the Rex server, but it is unreachable.");
53
+ if (result.status !== 0) throw new Error(`rex session inspect exited with code ${result.status}: ${(result.stderr || result.stdout).trim().slice(0, 512)}`);
54
+ if (!sessionConfirmed(result.stdout, session)) throw new Error(`The Rex server did not confirm session ${session}.`);
55
+ const stepCwd = input.stepCwd?.trim();
56
+ const home = stepCwd === "~" || stepCwd?.startsWith("~/") ? path.join(os.homedir(), stepCwd.slice(1)) : void 0;
57
+ const requested = home ?? (stepCwd ? path.resolve(input.cwd, stepCwd) : input.cwd);
58
+ let cwd;
59
+ try {
60
+ cwd = fs.realpathSync(requested);
61
+ } catch (error) {
62
+ throw new Error(`Machine 'local' cwd does not exist: ${requested}.`, { cause: error });
63
+ }
64
+ if (!fs.statSync(cwd).isDirectory()) throw new Error(`Machine 'local' cwd is not a directory: ${requested}.`);
65
+ const machine = { provider: "rex", id: REX_LOCAL_MACHINE, session, cwd };
66
+ if (server) machine.server = server;
67
+ return machine;
68
+ }
69
+
70
+ // src/runs/shared/herdr-connection.ts
71
+ import { spawn, spawnSync as spawnSync2 } from "node:child_process";
72
+ import * as fs2 from "node:fs";
73
+ import * as net from "node:net";
74
+ import * as os2 from "node:os";
75
+ import * as path2 from "node:path";
7
76
  var MAX_DISCOVERY_BYTES = 256 * 1024;
8
77
  var MAX_RPC_BYTES = 4 * 1024 * 1024;
9
78
  var SUBSCRIPTION_ACK_TIMEOUT_MS = 15e3;
@@ -52,11 +121,14 @@ function sshEnvCommand(session, command) {
52
121
  return `/usr/bin/env -u HERDR_SOCKET_PATH -u HERDR_SESSION${assignment} ${remoteShellCommand(command)}`;
53
122
  }
54
123
  function runHerdrRemoteCommand(machine, command, options = {}) {
55
- return spawnSync(options.sshBin ?? "ssh", [...herdrSshArgs(options.env), machine.target, command], { encoding: "utf8", env: hardenedSshEnv(options.env), timeout: options.timeout ?? 15e3, maxBuffer: options.maxBuffer ?? MAX_DISCOVERY_BYTES, windowsHide: true });
124
+ return spawnSync2(options.sshBin ?? "ssh", [...herdrSshArgs(options.env), machine.target, command], { encoding: "utf8", env: hardenedSshEnv(options.env), timeout: options.timeout ?? 15e3, maxBuffer: options.maxBuffer ?? MAX_DISCOVERY_BYTES, windowsHide: true });
56
125
  }
57
126
  function runHerdrRemoteCommandAsync(machine, command, options = {}) {
58
- return new Promise((resolve) => {
59
- const child = spawn(options.sshBin ?? "ssh", [...herdrSshArgs(options.env), machine.target, command], { env: hardenedSshEnv(options.env), stdio: ["ignore", "pipe", "pipe"], windowsHide: true });
127
+ return runBoundedCommand(options.sshBin ?? "ssh", [...herdrSshArgs(options.env), machine.target, command], { env: hardenedSshEnv(options.env), timeout: options.timeout, maxBuffer: options.maxBuffer });
128
+ }
129
+ function runBoundedCommand(file, args, options) {
130
+ return new Promise((resolve2) => {
131
+ const child = spawn(file, args, { env: options.env, stdio: ["ignore", "pipe", "pipe"], windowsHide: true });
60
132
  const stdout = [], stderr = [], maxBuffer = options.maxBuffer ?? MAX_DISCOVERY_BYTES;
61
133
  let stdoutBytes = 0, stderrBytes = 0, error, settled = false, escalation;
62
134
  const finish = (status, spawnError = error) => {
@@ -64,7 +136,7 @@ function runHerdrRemoteCommandAsync(machine, command, options = {}) {
64
136
  settled = true;
65
137
  clearTimeout(timer);
66
138
  if (escalation) clearTimeout(escalation);
67
- resolve({ status, stdout: Buffer.concat(stdout).toString("utf8"), stderr: Buffer.concat(stderr).toString("utf8"), ...spawnError ? { error: spawnError } : {} });
139
+ resolve2({ status, stdout: Buffer.concat(stdout).toString("utf8"), stderr: Buffer.concat(stderr).toString("utf8"), ...spawnError ? { error: spawnError } : {} });
68
140
  };
69
141
  const terminate = () => {
70
142
  child.kill("SIGTERM");
@@ -100,7 +172,7 @@ function parseHerdrEndpoint(value, expectedSession) {
100
172
  }
101
173
  if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) throw new Error("Remote Herdr endpoint discovery returned no endpoint.");
102
174
  const p = parsed;
103
- if (typeof p.socket !== "string" || !path.posix.isAbsolute(p.socket) || typeof p.version !== "string" || typeof p.protocol !== "number") throw new Error("Remote Herdr endpoint discovery returned incomplete identity.");
175
+ if (typeof p.socket !== "string" || !path2.posix.isAbsolute(p.socket) || typeof p.version !== "string" || typeof p.protocol !== "number") throw new Error("Remote Herdr endpoint discovery returned incomplete identity.");
104
176
  const session = canonicalHerdrSession(typeof p.session === "string" ? p.session : null), expected = canonicalHerdrSession(expectedSession);
105
177
  if (expected !== session) throw new Error(`Remote Herdr session identity mismatch: expected ${expected ?? "default"}, received ${session ?? "default"}.`);
106
178
  if (p.running !== true || p.compatible !== true || p.endpoint_compatible === false) throw new Error("The selected remote Herdr session is stopped or incompatible.");
@@ -122,7 +194,7 @@ var SocketRpcClient = class {
122
194
  this.#subscriptionAckTimeoutMs = subscriptionAckTimeoutMs;
123
195
  }
124
196
  call(method, params = {}, timeoutMs = 15e3) {
125
- return new Promise((resolve, reject) => {
197
+ return new Promise((resolve2, reject) => {
126
198
  const socket = net.createConnection(this.#socketPath);
127
199
  let bytes = 0;
128
200
  let data = Buffer.alloc(0);
@@ -135,7 +207,7 @@ var SocketRpcClient = class {
135
207
  finished = true;
136
208
  clearTimeout(timer);
137
209
  socket.destroy();
138
- error ? reject(error) : resolve(value);
210
+ error ? reject(error) : resolve2(value);
139
211
  };
140
212
  socket.on("connect", () => socket.write(`${JSON.stringify({ id, method, params })}
141
213
  `));
@@ -172,8 +244,8 @@ var SocketRpcClient = class {
172
244
  const id = String(++this.#next);
173
245
  let buffer = Buffer.alloc(0), bytes = 0, acknowledged = false, intentional = false, notified = false, settled = false;
174
246
  let resolveAck, rejectAck;
175
- const acknowledgement = new Promise((resolve, reject) => {
176
- resolveAck = resolve;
247
+ const acknowledgement = new Promise((resolve2, reject) => {
248
+ resolveAck = resolve2;
177
249
  rejectAck = reject;
178
250
  });
179
251
  const failHandshake = (error) => {
@@ -260,19 +332,19 @@ async function waitForSocket(socketPath, child) {
260
332
  for (let attempt = 0; attempt < 100; attempt++) {
261
333
  if (child.exitCode !== null) throw new Error(`SSH StreamLocal forwarding exited with code ${child.exitCode}.`);
262
334
  try {
263
- const stat = fs.lstatSync(socketPath);
335
+ const stat = fs2.lstatSync(socketPath);
264
336
  if (stat.isSocket()) {
265
- fs.chmodSync(socketPath, 384);
337
+ fs2.chmodSync(socketPath, 384);
266
338
  return;
267
339
  }
268
340
  } catch {
269
341
  }
270
- await new Promise((resolve) => setTimeout(resolve, 25));
342
+ await new Promise((resolve2) => setTimeout(resolve2, 25));
271
343
  }
272
344
  throw new Error("SSH StreamLocal forwarding did not create its local socket.");
273
345
  }
274
346
  async function terminateChild(child) {
275
- const wait = (ms) => child.exitCode !== null || child.signalCode !== null ? Promise.resolve() : Promise.race([new Promise((resolve) => child.once("exit", () => resolve())), new Promise((resolve) => setTimeout(resolve, ms))]);
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))]);
276
348
  if (child.exitCode === null && child.signalCode === null) child.kill("SIGTERM");
277
349
  await wait(1e3);
278
350
  if (child.exitCode === null && child.signalCode === null) {
@@ -280,22 +352,20 @@ async function terminateChild(child) {
280
352
  await wait(1e3);
281
353
  }
282
354
  }
283
- async function connectHerdrMachine(machine, options = {}) {
284
- if (process.platform === "win32") throw new Error("Pane-native Herdr placement requires OpenSSH StreamLocal forwarding and is not supported on Windows.");
285
- const endpoint = await discoverHerdrEndpoint(machine, options);
286
- const dir = fs.mkdtempSync(path.join(os.tmpdir(), "pi-subagents-herdr-"));
287
- fs.chmodSync(dir, 448);
355
+ function openSshSockets(machine, options) {
356
+ const dir = fs2.mkdtempSync(path2.join(os2.tmpdir(), "pi-subagents-herdr-"));
357
+ fs2.chmodSync(dir, 448);
288
358
  const children = /* @__PURE__ */ new Set();
289
359
  const forward = async (remotePath, name) => {
290
- if (!path.posix.isAbsolute(remotePath)) throw new Error("Remote socket path must be absolute.");
291
- const local = path.join(dir, `${name}.sock`);
360
+ if (!path2.posix.isAbsolute(remotePath)) throw new Error("Remote socket path must be absolute.");
361
+ const local = path2.join(dir, `${name}.sock`);
292
362
  const child = spawn(options.sshBin ?? "ssh", [...herdrSshArgs(options.env), "-N", "-o", "StreamLocalBindUnlink=yes", "-L", `${local}:${remotePath}`, machine.target], { env: hardenedSshEnv(options.env), stdio: "ignore", windowsHide: true });
293
363
  children.add(child);
294
364
  const stop = async () => {
295
365
  await terminateChild(child);
296
366
  children.delete(child);
297
367
  try {
298
- fs.rmSync(local, { force: true });
368
+ fs2.rmSync(local, { force: true });
299
369
  } catch {
300
370
  }
301
371
  };
@@ -307,20 +377,40 @@ async function connectHerdrMachine(machine, options = {}) {
307
377
  }
308
378
  return { socketPath: local, close: stop };
309
379
  };
310
- const primary = await forward(endpoint.socket, "herdr").catch((error) => {
311
- fs.rmSync(dir, { recursive: true, force: true });
312
- throw error;
313
- });
314
- return { endpoint, socketPath: primary.socketPath, client: new SocketRpcClient(primary.socketPath), forwardRemoteSocket: forward, close: async () => {
380
+ return { forwardRemoteSocket: forward, close: async () => {
315
381
  await Promise.all([...children].map(terminateChild));
316
382
  children.clear();
317
- fs.rmSync(dir, { recursive: true, force: true });
383
+ fs2.rmSync(dir, { recursive: true, force: true });
318
384
  } };
319
385
  }
386
+ function sshMachineAccess(machine, options = {}) {
387
+ return { shellCommand: remoteShellCommand, run: (command, limits = {}) => runHerdrRemoteCommand(machine, command, { ...options, ...limits }), runAsync: (command, limits = {}) => runHerdrRemoteCommandAsync(machine, command, { ...options, ...limits }), openSockets: () => openSshSockets(machine, options) };
388
+ }
389
+ async function connectHerdrMachine(machine, options = {}) {
390
+ if (process.platform === "win32") throw new Error("Pane-native Herdr placement requires OpenSSH StreamLocal forwarding and is not supported on Windows.");
391
+ const endpoint = await discoverHerdrEndpoint(machine, options);
392
+ const sockets = sshMachineAccess(machine, options).openSockets();
393
+ const primary = await sockets.forwardRemoteSocket(endpoint.socket, "herdr").catch(async (error) => {
394
+ await sockets.close();
395
+ throw error;
396
+ });
397
+ return { endpoint, socketPath: primary.socketPath, client: new SocketRpcClient(primary.socketPath), forwardRemoteSocket: sockets.forwardRemoteSocket, close: sockets.close };
398
+ }
320
399
 
321
400
  export {
322
401
  remoteShellCommand,
323
- runHerdrRemoteCommand,
324
- runHerdrRemoteCommandAsync,
325
- connectHerdrMachine
402
+ runBoundedCommand,
403
+ sshMachineAccess,
404
+ connectHerdrMachine,
405
+ REX_LOCAL_MACHINE,
406
+ REX_EXIT_GONE,
407
+ REX_EXIT_UNREACHABLE,
408
+ isRexLocalMachine,
409
+ REX_BUNDLE_BIN,
410
+ REX_CALL_TIMEOUT_MS,
411
+ REX_MAX_OUTPUT,
412
+ rexGlobalArgs,
413
+ resolveRexBinary,
414
+ rexCommandEnv,
415
+ resolveRexLocalPlacement
326
416
  };
@@ -1,23 +1,26 @@
1
1
  import {
2
2
  rewriteSubagentPrompt
3
- } from "./chunk-L5ZHE4L4.js";
3
+ } from "./chunk-GC4DSPMH.js";
4
4
  import {
5
5
  package_default
6
- } from "./chunk-2XYWN7QJ.js";
7
- import "./chunk-BPS2AGBZ.js";
6
+ } from "./chunk-ZQGSDHL6.js";
7
+ import "./chunk-W2SZ22EM.js";
8
8
  import {
9
9
  MODEL_ONLY_TOOL
10
- } from "./chunk-NNLASTQE.js";
10
+ } from "./chunk-VH5OAWVG.js";
11
11
  import "./chunk-NF7IEKBH.js";
12
12
  import {
13
13
  appendAgentRefinementOverlay
14
- } from "./chunk-EYNJ6VL6.js";
15
- import "./chunk-VJHOELQP.js";
14
+ } from "./chunk-PLCCBLPY.js";
15
+ import "./chunk-NRQ75MGH.js";
16
16
  import "./chunk-NIXWDZZV.js";
17
- import "./chunk-ZP3HODFH.js";
17
+ import "./chunk-XSWOTIA3.js";
18
18
  import "./chunk-7F6RKT6W.js";
19
+ import {
20
+ assertNoToolModifierEntries
21
+ } from "./chunk-4SDU2BZX.js";
19
22
  import "./chunk-CTPAE36Y.js";
20
- import "./chunk-3DC5KBVP.js";
23
+ import "./chunk-5TYMSZX3.js";
21
24
  import {
22
25
  HERDR_PI_MAX_FRAME_BYTES,
23
26
  HERDR_PI_PROTOCOL,
@@ -34,7 +37,7 @@ import {
34
37
  discoverAgents,
35
38
  resolveExistingReadPaths,
36
39
  resolveSkills
37
- } from "./chunk-ZBC44JJ3.js";
40
+ } from "./chunk-CIZQXJ5Q.js";
38
41
  import "./chunk-5LFLVSTM.js";
39
42
  import "./chunk-YFCK7H2S.js";
40
43
  import "./chunk-Z7KVHQHL.js";
@@ -70,6 +73,7 @@ function resolveRemoteHerdrResources(cwd, resources, remoteDefaultTools = []) {
70
73
  ${skillPrompt}` : ""}${readPaths.length ? `
71
74
 
72
75
  [Read from: ${readPaths.join(", ")}]` : ""}`, { cwd, agentName: agent.name });
76
+ assertNoToolModifierEntries(agent.name, agent.tools ?? []);
73
77
  const denied = new Set(agent.excludeTools ?? []);
74
78
  const tools = (agent.tools === void 0 ? remoteDefaultTools : agent.tools).filter((tool) => !denied.has(tool) && (!resources.toolCeiling || resources.toolCeiling.includes(tool)));
75
79
  if (agent.tools === void 0 && !tools.length) throw new Error(`Remote agent '${agent.name}' resolved no default active tools within the inherited ceiling.`);
@@ -117,7 +121,6 @@ function registerHerdrPiBridge(pi) {
117
121
  client.write(encodeHerdrPiFrame(record));
118
122
  };
119
123
  const sessionId = (ctx) => ctx.sessionManager?.getSessionId?.();
120
- const activeTools = (ctx) => ctx.getActiveTools?.() ?? [];
121
124
  pi.registerTool({
122
125
  name: "contact_supervisor",
123
126
  ...MODEL_ONLY_TOOL,
@@ -129,11 +132,11 @@ function registerHerdrPiBridge(pi) {
129
132
  const id = `sup_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 8)}`;
130
133
  send({ type: "supervisor-request", requestId: id, reason: input.reason, message: input.message?.slice(0, 65536) ?? "", ...input.interview !== void 0 ? { interview: input.interview } : {}, expectsReply: input.reason !== "progress_update", nativeSessionId });
131
134
  if (input.reason === "progress_update") {
132
- await new Promise((resolve) => supervisor.set(id, { reason: input.reason, resolve }));
135
+ await new Promise((resolve, reject) => supervisor.set(id, { reason: input.reason, resolve, reject }));
133
136
  return { content: [{ type: "text", text: "Supervisor progress update queued." }], details: { delivered: true, requestId: id, reason: input.reason } };
134
137
  }
135
138
  const reason = input.reason;
136
- const answer = await new Promise((resolve) => supervisor.set(id, { reason, resolve }));
139
+ const answer = await new Promise((resolve, reject) => supervisor.set(id, { reason, resolve, reject }));
137
140
  const details = { requestId: id, reason };
138
141
  if (reason === "interview_request") try {
139
142
  details.structuredReply = JSON.parse(answer.replace(/^```(?:json)?\s*|\s*```$/giu, ""));
@@ -162,7 +165,8 @@ ${answer}` }], details };
162
165
  const entry = request.supervisorId ? supervisor.get(request.supervisorId) : void 0;
163
166
  if (!entry) throw new Error("Unknown supervisor request.");
164
167
  supervisor.delete(request.supervisorId);
165
- entry.resolve(request.text ?? "");
168
+ if (request.error !== void 0) entry.reject(new Error(request.error));
169
+ else entry.resolve(request.text ?? "");
166
170
  }
167
171
  send({ type: "control-ack", requestId, operation: request.operation, nativeSessionId });
168
172
  };
@@ -182,7 +186,7 @@ ${resolvedSystemPrompt}` } : void 0;
182
186
  nativeSessionId = sessionId(ctx);
183
187
  if (!nativeSessionId) throw new Error("Pane-native Pi bridge requires a persisted native session identity.");
184
188
  fs.writeFileSync(manifestPath, JSON.stringify({ protocol: HERDR_PI_PROTOCOL, packageVersion: package_default.version, runId, socketPath, nativeSessionId, pid: process.pid }), { mode: 384, flag: "wx" });
185
- send({ type: "ready", packageVersion: package_default.version, nativeSessionId, sessionFile: ctx.sessionManager?.getSessionFile?.(), cwd: process.cwd(), tools: activeTools(ctx), model: ctx.model?.provider && ctx.model.id ? `${ctx.model.provider}/${ctx.model.id}` : void 0, configured, activeRequestId, evidenceCursor, evidenceFloor: journal[0]?.cursor ?? evidenceCursor + 1, initialGit: gitEvidence(process.cwd()) });
189
+ send({ type: "ready", packageVersion: package_default.version, nativeSessionId, sessionFile: ctx.sessionManager?.getSessionFile?.(), cwd: process.cwd(), tools: pi.getActiveTools(), model: ctx.model?.provider && ctx.model.id ? `${ctx.model.provider}/${ctx.model.id}` : void 0, configured, activeRequestId, evidenceCursor, evidenceFloor: journal[0]?.cursor ?? evidenceCursor + 1, initialGit: gitEvidence(process.cwd()) });
186
190
  });
187
191
  on("message_end", (event) => {
188
192
  const message = event.message;
@@ -233,11 +237,11 @@ ${resolvedSystemPrompt}` } : void 0;
233
237
  const invalidSkills = resources.skills !== void 0 && (!Array.isArray(resources.skills) || resources.skills.some((name) => typeof name !== "string" || !/^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/u.test(name)));
234
238
  const invalidReads = resources.reads !== void 0 && resources.reads !== false && (!Array.isArray(resources.reads) || resources.reads.some((name) => typeof name !== "string"));
235
239
  if (typeof resources.agent !== "string" || !/^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/u.test(resources.agent) || invalidSkills || invalidReads) throw new Error("Remote logical resource names are invalid.");
236
- const resolved = resolveRemoteHerdrResources(process.cwd(), { agent: resources.agent, ...resources.skills ? { skills: resources.skills } : {}, ...resources.toolCeiling ? { toolCeiling: resources.toolCeiling } : {}, ...resources.reads !== void 0 ? { reads: resources.reads } : {} }, context ? activeTools(context) : []);
240
+ const resolved = resolveRemoteHerdrResources(process.cwd(), { agent: resources.agent, ...resources.skills ? { skills: resources.skills } : {}, ...resources.toolCeiling ? { toolCeiling: resources.toolCeiling } : {}, ...resources.reads !== void 0 ? { reads: resources.reads } : {} }, context ? pi.getActiveTools() : []);
237
241
  resolvedSystemPrompt = resolved.systemPrompt;
238
242
  resolvedContextPolicy = resolved;
239
- pi.setActiveTools?.(resolved.tools);
240
- const acknowledged = context ? activeTools(context) : [];
243
+ pi.setActiveTools(resolved.tools);
244
+ const acknowledged = context ? pi.getActiveTools() : [];
241
245
  if (acknowledged.some((tool) => !resolved.tools.includes(tool)) || resolved.tools.some((tool) => !acknowledged.includes(tool))) throw new Error("Remote Pi did not apply the resolved active-tool set exactly.");
242
246
  configured = true;
243
247
  send({ type: "configured", requestId: frame.requestId, nativeSessionId, agent: resolved.agent, skills: resolved.skills, model: context?.model?.provider && context.model.id ? `${context.model.provider}/${context.model.id}` : void 0, tools: acknowledged });
@@ -248,7 +252,9 @@ ${resolvedSystemPrompt}` } : void 0;
248
252
  if (!["prompt", "steer", "follow-up", "abort", "supervisor-reply"].includes(operation)) throw new Error("Unsupported bridge operation.");
249
253
  const text = frame.text;
250
254
  if (text !== void 0 && (typeof text !== "string" || Buffer.byteLength(text) > 256 * 1024)) throw new Error("Bridge request text is invalid or too large.");
251
- pending.set(frame.requestId, { operation, ...text !== void 0 ? { text } : {}, ...typeof frame.supervisorId === "string" ? { supervisorId: frame.supervisorId } : {} });
255
+ const error = frame.error;
256
+ if (error !== void 0 && (operation !== "supervisor-reply" || typeof error !== "string" || Buffer.byteLength(error) > 256 * 1024)) throw new Error("Bridge supervisor error is invalid or too large.");
257
+ pending.set(frame.requestId, { operation, ...text !== void 0 ? { text } : {}, ...typeof frame.supervisorId === "string" ? { supervisorId: frame.supervisorId } : {}, ...error !== void 0 ? { error } : {} });
252
258
  send({ type: "prepared", requestId: frame.requestId, nativeSessionId });
253
259
  }
254
260
  } catch (error) {
@@ -258,7 +264,7 @@ ${resolvedSystemPrompt}` } : void 0;
258
264
  socket.on("close", () => {
259
265
  if (client === socket) client = void 0;
260
266
  });
261
- if (context && nativeSessionId) send({ type: "ready", packageVersion: package_default.version, nativeSessionId, sessionFile: context.sessionManager?.getSessionFile?.(), cwd: process.cwd(), tools: activeTools(context), model: context.model?.provider && context.model.id ? `${context.model.provider}/${context.model.id}` : void 0, configured, activeRequestId, evidenceCursor, evidenceFloor: journal[0]?.cursor ?? evidenceCursor + 1, initialGit: gitEvidence(process.cwd()) });
267
+ if (context && nativeSessionId) send({ type: "ready", packageVersion: package_default.version, nativeSessionId, sessionFile: context.sessionManager?.getSessionFile?.(), cwd: process.cwd(), tools: pi.getActiveTools(), model: context.model?.provider && context.model.id ? `${context.model.provider}/${context.model.id}` : void 0, configured, activeRequestId, evidenceCursor, evidenceFloor: journal[0]?.cursor ?? evidenceCursor + 1, initialGit: gitEvidence(process.cwd()) });
262
268
  });
263
269
  server.listen(socketPath, () => {
264
270
  fs.chmodSync(socketPath, 384);
@@ -39,10 +39,10 @@ import {
39
39
  validateModelResponseAliases,
40
40
  withWorkflowChildRunModel,
41
41
  withWorktreeTransaction
42
- } from "./chunk-VJHOELQP.js";
42
+ } from "./chunk-NRQ75MGH.js";
43
43
  import {
44
44
  validateIntercomBridgeConfig
45
- } from "./chunk-ZP3HODFH.js";
45
+ } from "./chunk-XSWOTIA3.js";
46
46
  import {
47
47
  stableJsonDigest
48
48
  } from "./chunk-7F6RKT6W.js";
@@ -73,7 +73,7 @@ import {
73
73
  validateToolBudgetConfig,
74
74
  waitForFileSystemRetry,
75
75
  writeAtomicJson
76
- } from "./chunk-ZBC44JJ3.js";
76
+ } from "./chunk-CIZQXJ5Q.js";
77
77
  import {
78
78
  DIRS,
79
79
  RUNNER_LAUNCHER_NAME_PATTERN,
@@ -2133,6 +2133,7 @@ function isAsyncStatusIsolationError(asyncDir, error) {
2133
2133
  const message = getErrorMessage(error);
2134
2134
  return /^(?:Failed to (?:inspect|read|parse|validate) async status file|Invalid async status file) '/.test(message) || message.startsWith(`Invalid async status '${statusPath}'`) || message.startsWith(`Invalid host step '${statusPath}`) || /^(workflowChildren|Invalid workflowChildren)/.test(message);
2135
2135
  }
2136
+ var reportedCorruptStatus = /* @__PURE__ */ new Map();
2136
2137
  function isolateCorruptActiveRun(asyncDir, runId, error, now) {
2137
2138
  const statusPath = path3.join(asyncDir, "status.json");
2138
2139
  const processTerminal = readProcessTerminal(asyncDir, { runId });
@@ -2152,7 +2153,18 @@ function isolateCorruptActiveRun(asyncDir, runId, error, now) {
2152
2153
  markerAction = `failed to release active marker: ${getErrorMessage(releaseError)}`;
2153
2154
  }
2154
2155
  }
2155
- console.error(`[pi-subagents] Skipping corrupt active async run '${runId}' at '${statusPath}': ${getErrorMessage(error)}; ${markerAction}.`);
2156
+ const line = `[pi-subagents] Skipping corrupt active async run '${runId}' at '${statusPath}': ${getErrorMessage(error)}; ${markerAction}.`;
2157
+ let fileSignature = "";
2158
+ try {
2159
+ const stat = fs3.statSync(statusPath);
2160
+ fileSignature = `${stat.size}:${stat.mtimeMs}`;
2161
+ } catch {
2162
+ }
2163
+ const signature = `${fileSignature}
2164
+ ${line}`;
2165
+ if (reportedCorruptStatus.get(statusPath) === signature) return;
2166
+ reportedCorruptStatus.set(statusPath, signature);
2167
+ console.error(line);
2156
2168
  }
2157
2169
  function isNotFoundError(error) {
2158
2170
  return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
@@ -2467,14 +2479,16 @@ function listAsyncRuns(asyncDirRoot, options = {}, observeStatus) {
2467
2479
  const asyncDir = path3.join(asyncDirRoot, entry);
2468
2480
  let status;
2469
2481
  try {
2482
+ let owned = false;
2470
2483
  if (owners) {
2471
2484
  const stored = readStatus(asyncDir);
2472
2485
  if (stored && !owners.has(stored.sessionId)) {
2473
2486
  observeStatus?.(stored);
2474
2487
  continue;
2475
2488
  }
2489
+ owned = stored !== null;
2476
2490
  }
2477
- const reconciliation = options.reconcile === false ? void 0 : reconcileAsyncRun(asyncDir, { resultsDir: options.resultsDir, kill: options.kill, now: options.now }, observeStatus);
2491
+ const reconciliation = options.reconcile === false ? void 0 : reconcileAsyncRun(asyncDir, { resultsDir: options.resultsDir, kill: options.kill, now: options.now, adoptCompletionOwnerId: owned ? options.adoptCompletionOwnerId : void 0 }, observeStatus);
2478
2492
  status = reconciliation?.status ?? readStatus(asyncDir);
2479
2493
  if (options.reconcile === false) observeStatus?.(status);
2480
2494
  } catch (error) {