pi-subagents 0.80.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 (184) hide show
  1. package/CHANGELOG.md +66 -0
  2. package/bundle/agents.js +18 -0
  3. package/bundle/background-work.js +16 -0
  4. package/bundle/capability-ceiling.js +22 -0
  5. package/bundle/child-tool-plan.js +11 -0
  6. package/bundle/chunk-43SCHIMD.js +57 -0
  7. package/bundle/chunk-4QFZZV3O.js +57 -0
  8. package/bundle/chunk-4SDU2BZX.js +1190 -0
  9. package/bundle/chunk-52UBALJL.js +753 -0
  10. package/bundle/chunk-5LFLVSTM.js +260 -0
  11. package/bundle/chunk-5TYMSZX3.js +235 -0
  12. package/bundle/chunk-62OCY2S4.js +14 -0
  13. package/bundle/chunk-7F6RKT6W.js +132 -0
  14. package/bundle/chunk-7IVSUMQQ.js +1337 -0
  15. package/bundle/chunk-7J4ZXMY7.js +18 -0
  16. package/bundle/chunk-7PCKZHR5.js +1234 -0
  17. package/bundle/chunk-7YMXOYEU.js +1373 -0
  18. package/bundle/chunk-B5EKXDHE.js +16870 -0
  19. package/bundle/chunk-CIZQXJ5Q.js +8619 -0
  20. package/bundle/chunk-CTPAE36Y.js +94 -0
  21. package/bundle/chunk-D4EHT5SJ.js +14 -0
  22. package/bundle/chunk-GC4DSPMH.js +946 -0
  23. package/bundle/chunk-GJECHSAR.js +179 -0
  24. package/bundle/chunk-M2CTFYT3.js +4164 -0
  25. package/bundle/chunk-MHZ4G23A.js +1296 -0
  26. package/bundle/chunk-N2BN7BF5.js +159 -0
  27. package/bundle/chunk-NF7IEKBH.js +490 -0
  28. package/bundle/chunk-NGGAFDL6.js +742 -0
  29. package/bundle/chunk-NIXWDZZV.js +9 -0
  30. package/bundle/chunk-NRQ75MGH.js +8327 -0
  31. package/bundle/chunk-OYF6AFDY.js +458 -0
  32. package/bundle/chunk-PLCCBLPY.js +526 -0
  33. package/bundle/chunk-RMQFMMAT.js +416 -0
  34. package/bundle/chunk-SIJ4S3EN.js +276 -0
  35. package/bundle/chunk-UKG4V45O.js +228 -0
  36. package/bundle/chunk-VH5OAWVG.js +4096 -0
  37. package/bundle/chunk-W2SZ22EM.js +5462 -0
  38. package/bundle/chunk-WVD4UEL4.js +9367 -0
  39. package/bundle/chunk-XSWOTIA3.js +175 -0
  40. package/bundle/chunk-YFCK7H2S.js +129 -0
  41. package/bundle/chunk-YKEWIB4M.js +380 -0
  42. package/bundle/chunk-YYREO77V.js +137 -0
  43. package/bundle/chunk-Z7KVHQHL.js +173 -0
  44. package/bundle/chunk-ZLINIYTP.js +8613 -0
  45. package/bundle/chunk-ZQGSDHL6.js +128 -0
  46. package/bundle/control-channel.js +11 -0
  47. package/bundle/delegation.js +14 -0
  48. package/bundle/external-job-provider.js +20 -0
  49. package/bundle/external-runs.js +21 -0
  50. package/bundle/index.js +30 -0
  51. package/bundle/inspectors.js +8 -0
  52. package/bundle/intercom-bridge.js +11 -0
  53. package/bundle/preflight.js +425 -0
  54. package/bundle/project-panes.js +28 -0
  55. package/bundle/required-child-extensions.js +6 -0
  56. package/bundle/shared-types.js +8 -0
  57. package/bundle/workflow-resources.js +7 -0
  58. package/docs/agents.md +31 -0
  59. package/docs/configuration.md +9 -1
  60. package/docs/extension-api.md +27 -0
  61. package/docs/observability.md +6 -2
  62. package/docs/tool-reference.md +3 -1
  63. package/docs/workflows.md +16 -0
  64. package/package.json +19 -19
  65. package/skills/pi-subagents/references/execution-controls.md +5 -4
  66. package/src/agents/agent-management.js +3 -3
  67. package/src/agents/agent-memory.js +3 -1
  68. package/src/agents/agents.js +6 -6
  69. package/src/agents/frontmatter.d.ts +4 -0
  70. package/src/agents/frontmatter.js +24 -3
  71. package/src/agents/skills.js +2 -1
  72. package/src/api/delegation.d.ts +20 -0
  73. package/src/extension/config.js +3 -0
  74. package/src/extension/herdr-pi-bridge.js +17 -10
  75. package/src/extension/index.js +109 -9
  76. package/src/extension/schemas.js +3 -3
  77. package/src/intercom/native-supervisor-channel.js +20 -0
  78. package/src/missions/lifecycle.js +7 -1
  79. package/src/runs/background/active-async-capacity.d.ts +1 -0
  80. package/src/runs/background/active-async-capacity.js +9 -3
  81. package/src/runs/background/async-execution.js +20 -10
  82. package/src/runs/background/async-job-tracker.d.ts +6 -1
  83. package/src/runs/background/async-job-tracker.js +9 -4
  84. package/src/runs/background/async-status.d.ts +2 -0
  85. package/src/runs/background/async-status.js +21 -2
  86. package/src/runs/background/await-async-run.js +3 -7
  87. package/src/runs/background/boot-id.d.ts +12 -0
  88. package/src/runs/background/boot-id.js +47 -0
  89. package/src/runs/background/interrupted-elsewhere.d.ts +22 -0
  90. package/src/runs/background/interrupted-elsewhere.js +68 -0
  91. package/src/runs/background/notify.d.ts +2 -0
  92. package/src/runs/background/notify.js +13 -0
  93. package/src/runs/background/result-delivery-ownership.d.ts +6 -0
  94. package/src/runs/background/result-delivery-ownership.js +19 -1
  95. package/src/runs/background/result-watcher.d.ts +3 -1
  96. package/src/runs/background/result-watcher.js +24 -8
  97. package/src/runs/background/run-child-session.d.ts +1 -6
  98. package/src/runs/background/run-child-session.js +19 -1
  99. package/src/runs/background/run-status.js +2 -1
  100. package/src/runs/background/stale-run-reconciler.d.ts +25 -0
  101. package/src/runs/background/stale-run-reconciler.js +54 -13
  102. package/src/runs/background/subagent-runner-bootstrap.js +3 -0
  103. package/src/runs/background/subagent-runner.d.ts +4 -0
  104. package/src/runs/background/subagent-runner.js +13 -5
  105. package/src/runs/background/subagent-wait.d.ts +1 -1
  106. package/src/runs/background/subagent-wait.js +6 -1
  107. package/src/runs/background/terminal-run-index.d.ts +1 -0
  108. package/src/runs/background/terminal-run-index.js +4 -2
  109. package/src/runs/foreground/async-stop-action.js +3 -1
  110. package/src/runs/foreground/execution.js +45 -5
  111. package/src/runs/foreground/foreground-history.d.ts +16 -2
  112. package/src/runs/foreground/foreground-history.js +133 -31
  113. package/src/runs/foreground/subagent-executor.d.ts +11 -0
  114. package/src/runs/foreground/subagent-executor.js +277 -74
  115. package/src/runs/shared/child-launch.d.ts +2 -2
  116. package/src/runs/shared/child-session.d.ts +3 -2
  117. package/src/runs/shared/child-session.js +51 -8
  118. package/src/runs/shared/child-tool-plan.d.ts +7 -0
  119. package/src/runs/shared/child-tool-plan.js +21 -6
  120. package/src/runs/shared/herdr-connection.d.ts +17 -8
  121. package/src/runs/shared/herdr-connection.js +19 -7
  122. package/src/runs/shared/herdr-external-adapters.d.ts +3 -2
  123. package/src/runs/shared/herdr-external-adapters.js +1 -1
  124. package/src/runs/shared/herdr-machine.d.ts +12 -2
  125. package/src/runs/shared/herdr-machine.js +55 -9
  126. package/src/runs/shared/herdr-pane-host.d.ts +88 -0
  127. package/src/runs/shared/herdr-pane-host.js +155 -0
  128. package/src/runs/shared/herdr-placed-run.d.ts +27 -150
  129. package/src/runs/shared/herdr-placed-run.js +48 -395
  130. package/src/runs/shared/local-machine-access.d.ts +5 -0
  131. package/src/runs/shared/local-machine-access.js +28 -0
  132. package/src/runs/shared/orca-progress-tabs.d.ts +0 -1
  133. package/src/runs/shared/orca-progress-tabs.js +1 -24
  134. package/src/runs/shared/parallel-utils.d.ts +1 -1
  135. package/src/runs/shared/placed-run-owner.d.ts +49 -0
  136. package/src/runs/shared/placed-run-owner.js +93 -0
  137. package/src/runs/shared/placement-host.d.ts +99 -0
  138. package/src/runs/shared/placement-host.js +157 -0
  139. package/src/runs/shared/rex-machine.d.ts +25 -0
  140. package/src/runs/shared/rex-machine.js +83 -0
  141. package/src/runs/shared/rex-pane-host.d.ts +62 -0
  142. package/src/runs/shared/rex-pane-host.js +327 -0
  143. package/src/runs/shared/structured-output.d.ts +1 -0
  144. package/src/runs/shared/structured-output.js +1 -0
  145. package/src/shared/accessible-dir.js +4 -1
  146. package/src/shared/completion-owner.d.ts +5 -0
  147. package/src/shared/completion-owner.js +5 -0
  148. package/src/shared/provider-diagnostics.d.ts +9 -0
  149. package/src/shared/provider-diagnostics.js +75 -0
  150. package/src/shared/session-identity.d.ts +2 -0
  151. package/src/shared/session-identity.js +26 -0
  152. package/src/shared/types.d.ts +48 -6
  153. package/src/slash/delegation-adapters.d.ts +3 -1
  154. package/src/slash/delegation-adapters.js +7 -0
  155. package/src/slash/prompt-template-bridge.js +3 -1
  156. package/src/slash/subagents-admin.d.ts +1 -0
  157. package/src/slash/subagents-admin.js +1 -1
  158. package/src/tui/fleet-layout.d.ts +20 -0
  159. package/src/tui/fleet-layout.js +17 -0
  160. package/src/tui/fleet-viewport.d.ts +12 -0
  161. package/src/tui/fleet-viewport.js +35 -0
  162. package/src/tui/fleet.d.ts +6 -6
  163. package/src/tui/fleet.js +93 -66
  164. package/src/tui/render.d.ts +6 -0
  165. package/src/tui/render.js +3 -3
  166. package/src/tui/subagent-messages.js +1 -0
  167. package/src/workflows/scripted-workflow.d.ts +1 -1
  168. package/src/workflows/structured-workflow-scripts.d.ts +1 -0
  169. package/src/workflows/structured-workflow-scripts.js +5 -4
  170. package/src/workflows/workflow-auto-resume.d.ts +32 -0
  171. package/src/workflows/workflow-auto-resume.js +102 -0
  172. package/src/workflows/workflow-launch-record.d.ts +27 -0
  173. package/src/workflows/workflow-launch-record.js +48 -0
  174. package/src/workflows/workflow-resources.js +2 -2
  175. package/src/workflows/workflow-resume-claim.d.ts +82 -0
  176. package/src/workflows/workflow-resume-claim.js +210 -0
  177. package/src/workflows/workflow-resume.d.ts +56 -0
  178. package/src/workflows/workflow-resume.js +106 -0
  179. package/src/workflows/workflow-reuse.d.ts +15 -9
  180. package/src/workflows/workflow-reuse.js +77 -14
  181. package/src/workflows/workflow-revival.d.ts +2 -0
  182. package/src/workflows/workflow-revival.js +2 -1
  183. package/src/workflows/workflow-stop-cause.d.ts +6 -0
  184. package/src/workflows/workflow-stop-cause.js +8 -0
@@ -0,0 +1,276 @@
1
+ import {
2
+ rewriteSubagentPrompt
3
+ } from "./chunk-GC4DSPMH.js";
4
+ import {
5
+ package_default
6
+ } from "./chunk-ZQGSDHL6.js";
7
+ import "./chunk-W2SZ22EM.js";
8
+ import {
9
+ MODEL_ONLY_TOOL
10
+ } from "./chunk-VH5OAWVG.js";
11
+ import "./chunk-NF7IEKBH.js";
12
+ import {
13
+ appendAgentRefinementOverlay
14
+ } from "./chunk-PLCCBLPY.js";
15
+ import "./chunk-NRQ75MGH.js";
16
+ import "./chunk-NIXWDZZV.js";
17
+ import "./chunk-XSWOTIA3.js";
18
+ import "./chunk-7F6RKT6W.js";
19
+ import {
20
+ assertNoToolModifierEntries
21
+ } from "./chunk-4SDU2BZX.js";
22
+ import "./chunk-CTPAE36Y.js";
23
+ import "./chunk-5TYMSZX3.js";
24
+ import {
25
+ HERDR_PI_MAX_FRAME_BYTES,
26
+ HERDR_PI_PROTOCOL,
27
+ HERDR_PI_RUNTIME_DIR_ENV,
28
+ HERDR_PI_RUN_ENV,
29
+ HerdrPiFrameDecoder,
30
+ encodeHerdrPiFrame,
31
+ validateHerdrPiRunId
32
+ } from "./chunk-4QFZZV3O.js";
33
+ import "./chunk-N2BN7BF5.js";
34
+ import {
35
+ buildAgentMemoryInjection,
36
+ buildSkillInjection,
37
+ discoverAgents,
38
+ resolveExistingReadPaths,
39
+ resolveSkills
40
+ } from "./chunk-CIZQXJ5Q.js";
41
+ import "./chunk-5LFLVSTM.js";
42
+ import "./chunk-YFCK7H2S.js";
43
+ import "./chunk-Z7KVHQHL.js";
44
+
45
+ // src/extension/herdr-pi-bridge.ts
46
+ import { spawnSync } from "node:child_process";
47
+ import * as fs from "node:fs";
48
+ import * as net from "node:net";
49
+ import * as path from "node:path";
50
+ import { Type } from "typebox";
51
+ function gitEvidence(cwd) {
52
+ const run = (args) => spawnSync("git", args, { cwd, encoding: "utf8", timeout: 5e3, maxBuffer: 128 * 1024 });
53
+ const inside = run(["rev-parse", "--is-inside-work-tree"]);
54
+ if (inside.status !== 0) return void 0;
55
+ const head = run(["rev-parse", "HEAD"]);
56
+ const branch = run(["symbolic-ref", "--quiet", "--short", "HEAD"]);
57
+ const dirty = run(["status", "--porcelain"]);
58
+ return { ...head.status === 0 ? { head: head.stdout.trim() } : {}, ...branch.status === 0 ? { branch: branch.stdout.trim() } : {}, dirty: Boolean(dirty.stdout.trim()) };
59
+ }
60
+ function resolveRemoteHerdrResources(cwd, resources, remoteDefaultTools = []) {
61
+ if (!/^[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 && (!Array.isArray(resources.reads) || resources.reads.some((name) => typeof name !== "string"))) throw new Error("Remote logical resource names are invalid.");
62
+ const discovered = discoverAgents(cwd, "both").agents.filter((agent2) => agent2.name === resources.agent || agent2.aliases?.includes(resources.agent));
63
+ if (discovered.length !== 1) throw new Error(`Remote agent '${resources.agent}' was not found unambiguously in ${cwd}.`);
64
+ const agent = discovered[0];
65
+ const names = resources.skills ?? agent.skills ?? [];
66
+ const skills = resolveSkills(names, cwd);
67
+ if (skills.missing.length) throw new Error(`Remote Pi skills not found: ${skills.missing.join(", ")}. Configure them on the saved machine.`);
68
+ const skillPrompt = buildSkillInjection(skills.resolved);
69
+ const reads = resources.reads === void 0 ? agent.defaultReads ?? false : resources.reads;
70
+ const readPaths = Array.isArray(reads) ? resolveExistingReadPaths(reads, cwd) : [];
71
+ const systemPrompt = appendAgentRefinementOverlay(`${agent.systemPrompt}${buildAgentMemoryInjection(agent, cwd)}${skillPrompt ? `
72
+
73
+ ${skillPrompt}` : ""}${readPaths.length ? `
74
+
75
+ [Read from: ${readPaths.join(", ")}]` : ""}`, { cwd, agentName: agent.name });
76
+ assertNoToolModifierEntries(agent.name, agent.tools ?? []);
77
+ const denied = new Set(agent.excludeTools ?? []);
78
+ const tools = (agent.tools === void 0 ? remoteDefaultTools : agent.tools).filter((tool) => !denied.has(tool) && (!resources.toolCeiling || resources.toolCeiling.includes(tool)));
79
+ if (agent.tools === void 0 && !tools.length) throw new Error(`Remote agent '${agent.name}' resolved no default active tools within the inherited ceiling.`);
80
+ return { agent: agent.name, skills: names, tools, systemPrompt, inheritProjectContext: agent.inheritProjectContext, inheritGlobalContext: agent.inheritGlobalContext, inheritSkills: agent.inheritSkills };
81
+ }
82
+ function registerHerdrPiBridge(pi) {
83
+ const runId = validateHerdrPiRunId(process.env[HERDR_PI_RUN_ENV]);
84
+ const runDir = process.env[HERDR_PI_RUNTIME_DIR_ENV];
85
+ if (!runDir || !path.isAbsolute(runDir) || fs.lstatSync(runDir).isSymbolicLink()) throw new Error("Herdr bridge runtime directory was not authoritatively provisioned.");
86
+ fs.chmodSync(runDir, 448);
87
+ const socketPath = path.join(runDir, "bridge.sock");
88
+ const manifestPath = path.join(runDir, "manifest.json");
89
+ for (const candidate of [socketPath, manifestPath]) {
90
+ try {
91
+ const stat = fs.lstatSync(candidate);
92
+ if (stat.isSymbolicLink()) throw new Error(`Refusing symlink at '${candidate}'.`);
93
+ fs.rmSync(candidate, { force: true });
94
+ } catch (error) {
95
+ if (error.code !== "ENOENT") throw error;
96
+ }
97
+ }
98
+ let client;
99
+ let context;
100
+ let nativeSessionId;
101
+ let finalAssistant;
102
+ let activeRequestId;
103
+ let resolvedSystemPrompt;
104
+ let resolvedContextPolicy;
105
+ let configured = false;
106
+ const pending = /* @__PURE__ */ new Map();
107
+ const supervisor = /* @__PURE__ */ new Map();
108
+ let evidenceCursor = 0;
109
+ let journalBytes = 0;
110
+ const journal = [];
111
+ const send = (frame) => {
112
+ const record = { protocol: HERDR_PI_PROTOCOL, runId, ...frame, cursor: ++evidenceCursor };
113
+ const size = Buffer.byteLength(JSON.stringify(record));
114
+ journal.push(record);
115
+ journalBytes += size;
116
+ while (journal.length > 512 || journalBytes > 8 * 1024 * 1024) {
117
+ const removed = journal.shift();
118
+ if (removed) journalBytes -= Buffer.byteLength(JSON.stringify(removed));
119
+ }
120
+ if (!client || client.destroyed) return;
121
+ client.write(encodeHerdrPiFrame(record));
122
+ };
123
+ const sessionId = (ctx) => ctx.sessionManager?.getSessionId?.();
124
+ pi.registerTool({
125
+ name: "contact_supervisor",
126
+ ...MODEL_ONLY_TOOL,
127
+ label: "Contact supervisor",
128
+ description: "Contact the parent/supervisor session for a blocking decision, structured interview, or progress update.",
129
+ parameters: Type.Object({ reason: Type.Union([Type.Literal("need_decision"), Type.Literal("interview_request"), Type.Literal("progress_update")]), message: Type.Optional(Type.String({ maxLength: 65536 })), interview: Type.Optional(Type.Unknown()) }, { additionalProperties: false }),
130
+ execute: async (_toolCallId, input) => {
131
+ if (input.reason !== "interview_request" && !input.message?.trim()) throw new Error("message is required for supervisor decisions and progress updates.");
132
+ const id = `sup_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 8)}`;
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 });
134
+ if (input.reason === "progress_update") {
135
+ await new Promise((resolve, reject) => supervisor.set(id, { reason: input.reason, resolve, reject }));
136
+ return { content: [{ type: "text", text: "Supervisor progress update queued." }], details: { delivered: true, requestId: id, reason: input.reason } };
137
+ }
138
+ const reason = input.reason;
139
+ const answer = await new Promise((resolve, reject) => supervisor.set(id, { reason, resolve, reject }));
140
+ const details = { requestId: id, reason };
141
+ if (reason === "interview_request") try {
142
+ details.structuredReply = JSON.parse(answer.replace(/^```(?:json)?\s*|\s*```$/giu, ""));
143
+ } catch {
144
+ details.structuredReplyParseError = "Supervisor interview reply was not valid JSON.";
145
+ }
146
+ return { content: [{ type: "text", text: `**Reply from supervisor:**
147
+ ${answer}` }], details };
148
+ }
149
+ });
150
+ const execute = async (requestId, ctx) => {
151
+ const request = pending.get(requestId);
152
+ if (!request) throw new Error(`Unknown or already consumed bridge request '${requestId}'.`);
153
+ pending.delete(requestId);
154
+ if (sessionId(ctx) !== nativeSessionId) throw new Error("Native Pi session identity changed.");
155
+ send({ type: "accepted", requestId, operation: request.operation, nativeSessionId });
156
+ const api = pi;
157
+ if (request.operation === "prompt") {
158
+ activeRequestId = requestId;
159
+ finalAssistant = void 0;
160
+ api.sendUserMessage(request.text ?? "");
161
+ } else if (request.operation === "steer") api.sendUserMessage(request.text ?? "", { deliverAs: "steer" });
162
+ else if (request.operation === "follow-up") api.sendUserMessage(request.text ?? "", { deliverAs: "followUp" });
163
+ else if (request.operation === "abort") api.abort();
164
+ else {
165
+ const entry = request.supervisorId ? supervisor.get(request.supervisorId) : void 0;
166
+ if (!entry) throw new Error("Unknown supervisor request.");
167
+ supervisor.delete(request.supervisorId);
168
+ if (request.error !== void 0) entry.reject(new Error(request.error));
169
+ else entry.resolve(request.text ?? "");
170
+ }
171
+ send({ type: "control-ack", requestId, operation: request.operation, nativeSessionId });
172
+ };
173
+ pi.registerCommand("pi-subagents-bridge", { description: "Internal pane-native request dispatch", handler: (args, ctx) => execute(args.trim(), ctx) });
174
+ const on = pi.on;
175
+ on("before_agent_start", (event) => {
176
+ const options = event.systemPromptOptions;
177
+ if (typeof options?.appendSystemPrompt === "string") options.appendSystemPrompt = "";
178
+ return resolvedSystemPrompt && resolvedContextPolicy ? { systemPrompt: `${rewriteSubagentPrompt(typeof event.systemPrompt === "string" ? event.systemPrompt : "", { inheritProjectContext: resolvedContextPolicy.inheritProjectContext, inheritGlobalContext: resolvedContextPolicy.inheritGlobalContext, inheritSkills: resolvedContextPolicy.inheritSkills })}
179
+
180
+ <active_agent name=${JSON.stringify(resolvedContextPolicy.agent)}/>
181
+
182
+ ${resolvedSystemPrompt}` } : void 0;
183
+ });
184
+ on("session_start", (_event, ctx) => {
185
+ context = ctx;
186
+ nativeSessionId = sessionId(ctx);
187
+ if (!nativeSessionId) throw new Error("Pane-native Pi bridge requires a persisted native session identity.");
188
+ fs.writeFileSync(manifestPath, JSON.stringify({ protocol: HERDR_PI_PROTOCOL, packageVersion: package_default.version, runId, socketPath, nativeSessionId, pid: process.pid }), { mode: 384, flag: "wx" });
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()) });
190
+ });
191
+ on("message_end", (event) => {
192
+ const message = event.message;
193
+ if (message?.role === "assistant") finalAssistant = message;
194
+ send({ type: "event", requestId: activeRequestId, event: { ...event, message }, nativeSessionId });
195
+ });
196
+ on("agent_settled", (_event, ctx) => {
197
+ const requestId = activeRequestId;
198
+ send({ type: "settled", requestId, nativeSessionId, idle: ctx.isIdle(), pending: ctx.hasPendingMessages(), finalAssistant, finalGit: gitEvidence(process.cwd()) });
199
+ if (ctx.isIdle() && !ctx.hasPendingMessages()) activeRequestId = void 0;
200
+ });
201
+ on("session_shutdown", () => {
202
+ server.close();
203
+ client?.destroy();
204
+ try {
205
+ fs.rmSync(runDir, { recursive: true, force: true });
206
+ } catch {
207
+ }
208
+ });
209
+ const server = net.createServer((socket) => {
210
+ if (client && !client.destroyed) {
211
+ socket.destroy();
212
+ return;
213
+ }
214
+ client = socket;
215
+ const decoder = new HerdrPiFrameDecoder();
216
+ let total = 0;
217
+ for (const record of journal) socket.write(encodeHerdrPiFrame(record));
218
+ socket.on("data", (chunk) => {
219
+ total += chunk.byteLength;
220
+ if (total > HERDR_PI_MAX_FRAME_BYTES * 32) {
221
+ socket.destroy(new Error("Bridge byte budget exceeded."));
222
+ return;
223
+ }
224
+ try {
225
+ for (const frame of decoder.push(chunk)) {
226
+ if (frame.runId !== runId) throw new Error("Bridge run identity mismatch.");
227
+ if (frame.type === "supervisor-delivered" && typeof frame.requestId === "string") {
228
+ const entry = supervisor.get(frame.requestId);
229
+ if (!entry || entry.reason !== "progress_update") throw new Error("Unknown supervisor delivery acknowledgement.");
230
+ supervisor.delete(frame.requestId);
231
+ entry.resolve("delivered");
232
+ continue;
233
+ }
234
+ if (frame.type === "configure") {
235
+ if (configured || typeof frame.requestId !== "string" || !frame.resources || typeof frame.resources !== "object") throw new Error("Invalid or duplicate bridge configuration.");
236
+ const resources = frame.resources;
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)));
238
+ const invalidReads = resources.reads !== void 0 && resources.reads !== false && (!Array.isArray(resources.reads) || resources.reads.some((name) => typeof name !== "string"));
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.");
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() : []);
241
+ resolvedSystemPrompt = resolved.systemPrompt;
242
+ resolvedContextPolicy = resolved;
243
+ pi.setActiveTools(resolved.tools);
244
+ const acknowledged = context ? pi.getActiveTools() : [];
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.");
246
+ configured = true;
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
+ continue;
249
+ }
250
+ if (frame.type !== "prepare" || typeof frame.requestId !== "string" || pending.has(frame.requestId) || !configured) throw new Error("Invalid, duplicate, or unconfigured bridge request.");
251
+ const operation = frame.operation;
252
+ if (!["prompt", "steer", "follow-up", "abort", "supervisor-reply"].includes(operation)) throw new Error("Unsupported bridge operation.");
253
+ const text = frame.text;
254
+ if (text !== void 0 && (typeof text !== "string" || Buffer.byteLength(text) > 256 * 1024)) throw new Error("Bridge request text is invalid or too large.");
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 } : {} });
258
+ send({ type: "prepared", requestId: frame.requestId, nativeSessionId });
259
+ }
260
+ } catch (error) {
261
+ socket.destroy(error);
262
+ }
263
+ });
264
+ socket.on("close", () => {
265
+ if (client === socket) client = void 0;
266
+ });
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()) });
268
+ });
269
+ server.listen(socketPath, () => {
270
+ fs.chmodSync(socketPath, 384);
271
+ });
272
+ }
273
+ export {
274
+ registerHerdrPiBridge as default,
275
+ resolveRemoteHerdrResources
276
+ };
@@ -0,0 +1,228 @@
1
+ import {
2
+ previewDisplayText,
3
+ safeTerminalText,
4
+ sanitizeDisplayText,
5
+ truncateDisplayText
6
+ } from "./chunk-YFCK7H2S.js";
7
+
8
+ // src/api/external-runs.ts
9
+ var EXTERNAL_RUN_REGISTRY_VERSION = 2;
10
+ var EXTERNAL_RUN_REGISTRY_KEY = "pi-subagents.external-runs.v2";
11
+ var EXTERNAL_RUN_LIMITS = {
12
+ maxCachedRuns: 100,
13
+ maxSnapshotRuns: 20,
14
+ maxIdentityLength: 160,
15
+ /**
16
+ * A Pi session id is the session file path, which routinely exceeds a short
17
+ * identity budget in nested worktrees, so it is bounded like the other paths.
18
+ */
19
+ maxSessionIdLength: 4096,
20
+ maxTextLength: 160,
21
+ maxPreviewLength: 4096,
22
+ maxPathLength: 4096,
23
+ maxSerializedBytes: 32 * 1024
24
+ };
25
+ var trustedRecordsByRegistry = /* @__PURE__ */ new WeakMap();
26
+ var RUN_FIELD_NAMES = [
27
+ "id",
28
+ "sessionId",
29
+ "source",
30
+ "label",
31
+ "state",
32
+ "startedAt",
33
+ "updatedAt",
34
+ "endedAt",
35
+ "currentAction",
36
+ "preview",
37
+ "reportPath",
38
+ "transcriptPath"
39
+ ];
40
+ var RUN_FIELDS = new Set(RUN_FIELD_NAMES);
41
+ var UPDATE_FIELDS = new Set([...RUN_FIELDS].filter((field) => field !== "id" && field !== "sessionId" && field !== "source"));
42
+ function registry() {
43
+ const key2 = Symbol.for(EXTERNAL_RUN_REGISTRY_KEY);
44
+ const target = globalThis;
45
+ const existing = target[key2];
46
+ if (existing === void 0) {
47
+ const created = { version: EXTERNAL_RUN_REGISTRY_VERSION, runs: /* @__PURE__ */ new Map() };
48
+ target[key2] = created;
49
+ return created;
50
+ }
51
+ if (!existing || typeof existing !== "object" || Array.isArray(existing)) throw new Error(`Malformed external-run registry at Symbol.for("${EXTERNAL_RUN_REGISTRY_KEY}").`);
52
+ const candidate = existing;
53
+ if (candidate.version !== EXTERNAL_RUN_REGISTRY_VERSION || !(candidate.runs instanceof Map)) throw new Error(`Unsupported external-run registry at Symbol.for("${EXTERNAL_RUN_REGISTRY_KEY}").`);
54
+ return candidate;
55
+ }
56
+ function trustedRecords(current) {
57
+ const cached = trustedRecordsByRegistry.get(current);
58
+ if (cached) return cached;
59
+ const records = /* @__PURE__ */ new Map();
60
+ trustedRecordsByRegistry.set(current, records);
61
+ return records;
62
+ }
63
+ function inputObject(value, field, allowed) {
64
+ if (!value || typeof value !== "object" || Array.isArray(value)) throw new Error(`${field} must be an object.`);
65
+ const input = value;
66
+ const unknown = Object.keys(input).filter((key2) => !allowed.has(key2));
67
+ if (unknown.length) throw new Error(`${field} has unknown fields: ${unknown.join(", ")}.`);
68
+ return input;
69
+ }
70
+ function identity(value, field, maxLength = EXTERNAL_RUN_LIMITS.maxIdentityLength) {
71
+ if (typeof value !== "string" || value.length === 0 || value.trim() !== value || value.includes("\0")) throw new Error(`${field} must be a non-empty trimmed string without NUL characters.`);
72
+ if (value.length > maxLength) throw new Error(`${field} must be at most ${maxLength} characters.`);
73
+ if (/[\t\n\p{Zl}\p{Zp}]/u.test(value) || safeTerminalText(value) !== value) throw new Error(`${field} must contain only display-safe text.`);
74
+ return value;
75
+ }
76
+ function displayText(value, field, maxLength, required = false) {
77
+ if (value === void 0 && !required) return void 0;
78
+ if (typeof value !== "string" || value.length === 0) throw new Error(`${field} must be a non-empty string.`);
79
+ const safe = sanitizeDisplayText(value.slice(0, Math.max(maxLength * 4, maxLength)));
80
+ if (!safe) throw new Error(`${field} must contain displayable text.`);
81
+ return truncateDisplayText(safe, maxLength);
82
+ }
83
+ function timestamp(value, field, required = false) {
84
+ if (value === void 0 && !required) return void 0;
85
+ if (typeof value !== "number" || !Number.isSafeInteger(value) || value < 0 || value > 864e13) throw new Error(`${field} must be a non-negative safe timestamp.`);
86
+ return value;
87
+ }
88
+ function state(value, field) {
89
+ if (value === "queued" || value === "running" || value === "completed" || value === "failed" || value === "stopped") return value;
90
+ throw new Error(`${field} is invalid.`);
91
+ }
92
+ function validateRun(value) {
93
+ const run = inputObject(value, "External run", RUN_FIELDS);
94
+ const updatedAt = timestamp(run.updatedAt, "External run updatedAt");
95
+ const endedAt = timestamp(run.endedAt, "External run endedAt");
96
+ const currentAction = displayText(run.currentAction, "External run currentAction", EXTERNAL_RUN_LIMITS.maxTextLength);
97
+ const preview = displayText(run.preview, "External run preview", EXTERNAL_RUN_LIMITS.maxPreviewLength);
98
+ const reportPath = displayText(run.reportPath, "External run reportPath", EXTERNAL_RUN_LIMITS.maxPathLength);
99
+ const transcriptPath = displayText(run.transcriptPath, "External run transcriptPath", EXTERNAL_RUN_LIMITS.maxPathLength);
100
+ return {
101
+ id: identity(run.id, "External run id"),
102
+ sessionId: identity(run.sessionId, "External run sessionId", EXTERNAL_RUN_LIMITS.maxSessionIdLength),
103
+ source: displayText(run.source, "External run source", EXTERNAL_RUN_LIMITS.maxTextLength, true),
104
+ label: displayText(run.label, "External run label", EXTERNAL_RUN_LIMITS.maxTextLength, true),
105
+ state: state(run.state, "External run state"),
106
+ startedAt: timestamp(run.startedAt, "External run startedAt", true),
107
+ ...updatedAt !== void 0 ? { updatedAt } : {},
108
+ ...endedAt !== void 0 ? { endedAt } : {},
109
+ ...currentAction ? { currentAction } : {},
110
+ ...preview ? { preview } : {},
111
+ ...reportPath ? { reportPath } : {},
112
+ ...transcriptPath ? { transcriptPath } : {}
113
+ };
114
+ }
115
+ function key(sessionId, id) {
116
+ return `${sessionId}\0${id}`;
117
+ }
118
+ function clone(run) {
119
+ return { ...run };
120
+ }
121
+ function trustedRecord(value, normalized) {
122
+ const candidate = value;
123
+ return {
124
+ value,
125
+ normalized,
126
+ keys: Object.keys(candidate),
127
+ values: RUN_FIELD_NAMES.map((field) => candidate[field])
128
+ };
129
+ }
130
+ function trustedRecordValue(value, record) {
131
+ if (!record || record.value !== value || !value || typeof value !== "object") return void 0;
132
+ try {
133
+ const candidate = value;
134
+ const keys = Object.keys(candidate);
135
+ if (keys.length !== record.keys.length || keys.some((key2, index) => key2 !== record.keys[index])) return void 0;
136
+ for (const [index, field] of RUN_FIELD_NAMES.entries()) {
137
+ if (candidate[field] !== record.values[index]) return void 0;
138
+ }
139
+ return record.normalized;
140
+ } catch {
141
+ return void 0;
142
+ }
143
+ }
144
+ function rememberTrustedRecord(current, cacheKey, value, normalized) {
145
+ trustedRecords(current).set(cacheKey, trustedRecord(value, normalized));
146
+ }
147
+ function normalizeCachedRecord(current, cacheKey, value) {
148
+ const run = validateRun(value);
149
+ rememberTrustedRecord(current, cacheKey, value, run);
150
+ return run;
151
+ }
152
+ function registerExternalRun(input) {
153
+ const run = validateRun(input);
154
+ const current = registry();
155
+ const runKey = key(run.sessionId, run.id);
156
+ if (current.runs.has(runKey)) throw new Error(`External run '${run.id}' is already registered for session '${run.sessionId}'.`);
157
+ if (current.runs.size >= EXTERNAL_RUN_LIMITS.maxCachedRuns) throw new Error(`External-run registry supports at most ${EXTERNAL_RUN_LIMITS.maxCachedRuns} cached runs.`);
158
+ current.runs.set(runKey, run);
159
+ rememberTrustedRecord(current, runKey, run, run);
160
+ return clone(run);
161
+ }
162
+ function updateExternalRun(sessionId, id, update) {
163
+ const safeSessionId = identity(sessionId, "External run sessionId", EXTERNAL_RUN_LIMITS.maxSessionIdLength);
164
+ const safeId = identity(id, "External run id");
165
+ const patch = inputObject(update, "External run update", UPDATE_FIELDS);
166
+ const current = registry();
167
+ const runKey = key(safeSessionId, safeId);
168
+ const previous = current.runs.get(runKey);
169
+ if (!previous) throw new Error(`External run '${safeId}' is not registered for session '${safeSessionId}'.`);
170
+ const next = validateRun({ ...previous, ...patch });
171
+ current.runs.set(runKey, next);
172
+ rememberTrustedRecord(current, runKey, next, next);
173
+ return clone(next);
174
+ }
175
+ function unregisterExternalRun(sessionId, id) {
176
+ const current = registry();
177
+ const runKey = key(identity(sessionId, "External run sessionId", EXTERNAL_RUN_LIMITS.maxSessionIdLength), identity(id, "External run id"));
178
+ const deleted = current.runs.delete(runKey);
179
+ if (deleted) trustedRecords(current).delete(runKey);
180
+ return deleted;
181
+ }
182
+ function snapshotBytes(runs) {
183
+ return Buffer.byteLength(JSON.stringify(runs), "utf8");
184
+ }
185
+ function getErrorMessage(error) {
186
+ return error instanceof Error ? error.message : String(error);
187
+ }
188
+ function snapshotExternalRuns(sessionId, options = {}) {
189
+ const safeSessionId = identity(sessionId, "External-run snapshot sessionId", EXTERNAL_RUN_LIMITS.maxSessionIdLength);
190
+ const current = registry();
191
+ const trusted = trustedRecords(current);
192
+ const sessionPrefix = `${safeSessionId}\0`;
193
+ const runs = [];
194
+ for (const [cacheKey, value] of current.runs.entries()) {
195
+ if (typeof cacheKey !== "string" || !cacheKey.startsWith(sessionPrefix)) continue;
196
+ try {
197
+ const run = trustedRecordValue(value, trusted.get(cacheKey)) ?? normalizeCachedRecord(current, cacheKey, value);
198
+ if (run.sessionId === safeSessionId) runs.push(run);
199
+ } catch (error) {
200
+ const message = `Malformed cached external run '${previewDisplayText(cacheKey.slice(sessionPrefix.length), EXTERNAL_RUN_LIMITS.maxIdentityLength)}': ${getErrorMessage(error)}`;
201
+ if (!options.ignoreMalformed) throw new Error(message, { cause: error instanceof Error ? error : void 0 });
202
+ current.runs.delete(cacheKey);
203
+ trusted.delete(cacheKey);
204
+ options.onMalformedRecord?.(message);
205
+ }
206
+ }
207
+ runs.sort((left, right) => {
208
+ const leftActive = left.state === "queued" || left.state === "running";
209
+ const rightActive = right.state === "queued" || right.state === "running";
210
+ if (leftActive !== rightActive) return leftActive ? -1 : 1;
211
+ return (right.updatedAt ?? right.endedAt ?? right.startedAt) - (left.updatedAt ?? left.endedAt ?? left.startedAt) || left.id.localeCompare(right.id);
212
+ });
213
+ const snapshot = runs.slice(0, EXTERNAL_RUN_LIMITS.maxSnapshotRuns).map(clone);
214
+ while (snapshot.length > 0 && snapshotBytes(snapshot) > EXTERNAL_RUN_LIMITS.maxSerializedBytes) snapshot.pop();
215
+ return snapshot;
216
+ }
217
+ var listExternalRuns = snapshotExternalRuns;
218
+
219
+ export {
220
+ EXTERNAL_RUN_REGISTRY_VERSION,
221
+ EXTERNAL_RUN_REGISTRY_KEY,
222
+ EXTERNAL_RUN_LIMITS,
223
+ registerExternalRun,
224
+ updateExternalRun,
225
+ unregisterExternalRun,
226
+ snapshotExternalRuns,
227
+ listExternalRuns
228
+ };