pi-subagents 0.80.0 → 0.81.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 (87) hide show
  1. package/CHANGELOG.md +28 -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-26C3WLYQ.js +8388 -0
  7. package/bundle/chunk-2XYWN7QJ.js +128 -0
  8. package/bundle/chunk-2ZSNH26H.js +4164 -0
  9. package/bundle/chunk-3DC5KBVP.js +235 -0
  10. package/bundle/chunk-3IF2PGW7.js +380 -0
  11. package/bundle/chunk-4QFZZV3O.js +57 -0
  12. package/bundle/chunk-563BHAZ5.js +1365 -0
  13. package/bundle/chunk-5LFLVSTM.js +260 -0
  14. package/bundle/chunk-62OCY2S4.js +14 -0
  15. package/bundle/chunk-74RT5YQE.js +326 -0
  16. package/bundle/chunk-7F6RKT6W.js +132 -0
  17. package/bundle/chunk-7J4ZXMY7.js +18 -0
  18. package/bundle/chunk-ARRKBB2B.js +179 -0
  19. package/bundle/chunk-BJN5LUQY.js +458 -0
  20. package/bundle/chunk-BPS2AGBZ.js +5421 -0
  21. package/bundle/chunk-CEKS3TFO.js +1337 -0
  22. package/bundle/chunk-CTPAE36Y.js +94 -0
  23. package/bundle/chunk-D4EHT5SJ.js +14 -0
  24. package/bundle/chunk-EYNJ6VL6.js +526 -0
  25. package/bundle/chunk-G5DKFLLI.js +1185 -0
  26. package/bundle/chunk-KSWQVGDW.js +1027 -0
  27. package/bundle/chunk-L5ZHE4L4.js +946 -0
  28. package/bundle/chunk-LSRUTHT7.js +57 -0
  29. package/bundle/chunk-N2BN7BF5.js +159 -0
  30. package/bundle/chunk-NF7IEKBH.js +490 -0
  31. package/bundle/chunk-NIXWDZZV.js +9 -0
  32. package/bundle/chunk-NNLASTQE.js +4082 -0
  33. package/bundle/chunk-TUWLXTAM.js +935 -0
  34. package/bundle/chunk-UKG4V45O.js +228 -0
  35. package/bundle/chunk-V2JWDB5U.js +753 -0
  36. package/bundle/chunk-V3BIUPLU.js +9163 -0
  37. package/bundle/chunk-V6MR5TOQ.js +270 -0
  38. package/bundle/chunk-VJHOELQP.js +8245 -0
  39. package/bundle/chunk-WSBNENFS.js +732 -0
  40. package/bundle/chunk-XMFLCKMQ.js +16664 -0
  41. package/bundle/chunk-YFCK7H2S.js +129 -0
  42. package/bundle/chunk-YYREO77V.js +137 -0
  43. package/bundle/chunk-Z7KVHQHL.js +173 -0
  44. package/bundle/chunk-ZBC44JJ3.js +8606 -0
  45. package/bundle/chunk-ZP3HODFH.js +175 -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/extension-api.md +27 -0
  59. package/docs/observability.md +6 -2
  60. package/package.json +18 -18
  61. package/src/agents/frontmatter.d.ts +2 -0
  62. package/src/agents/frontmatter.js +9 -2
  63. package/src/api/delegation.d.ts +20 -0
  64. package/src/extension/index.js +27 -7
  65. package/src/runs/background/run-child-session.js +18 -0
  66. package/src/runs/background/stale-run-reconciler.js +2 -0
  67. package/src/runs/foreground/execution.js +39 -2
  68. package/src/runs/shared/structured-output.d.ts +1 -0
  69. package/src/runs/shared/structured-output.js +1 -0
  70. package/src/shared/accessible-dir.js +4 -1
  71. package/src/shared/provider-diagnostics.d.ts +9 -0
  72. package/src/shared/provider-diagnostics.js +75 -0
  73. package/src/shared/types.d.ts +3 -0
  74. package/src/slash/delegation-adapters.d.ts +3 -1
  75. package/src/slash/delegation-adapters.js +7 -0
  76. package/src/slash/prompt-template-bridge.js +3 -1
  77. package/src/slash/subagents-admin.d.ts +1 -0
  78. package/src/slash/subagents-admin.js +1 -1
  79. package/src/tui/fleet-layout.d.ts +20 -0
  80. package/src/tui/fleet-layout.js +17 -0
  81. package/src/tui/fleet-viewport.d.ts +12 -0
  82. package/src/tui/fleet-viewport.js +35 -0
  83. package/src/tui/fleet.d.ts +5 -6
  84. package/src/tui/fleet.js +69 -51
  85. package/src/workflows/structured-workflow-scripts.d.ts +1 -0
  86. package/src/workflows/structured-workflow-scripts.js +5 -4
  87. package/src/workflows/workflow-resources.js +2 -2
@@ -0,0 +1,270 @@
1
+ import {
2
+ rewriteSubagentPrompt
3
+ } from "./chunk-L5ZHE4L4.js";
4
+ import {
5
+ package_default
6
+ } from "./chunk-2XYWN7QJ.js";
7
+ import "./chunk-BPS2AGBZ.js";
8
+ import {
9
+ MODEL_ONLY_TOOL
10
+ } from "./chunk-NNLASTQE.js";
11
+ import "./chunk-NF7IEKBH.js";
12
+ import {
13
+ appendAgentRefinementOverlay
14
+ } from "./chunk-EYNJ6VL6.js";
15
+ import "./chunk-VJHOELQP.js";
16
+ import "./chunk-NIXWDZZV.js";
17
+ import "./chunk-ZP3HODFH.js";
18
+ import "./chunk-7F6RKT6W.js";
19
+ import "./chunk-CTPAE36Y.js";
20
+ import "./chunk-3DC5KBVP.js";
21
+ import {
22
+ HERDR_PI_MAX_FRAME_BYTES,
23
+ HERDR_PI_PROTOCOL,
24
+ HERDR_PI_RUNTIME_DIR_ENV,
25
+ HERDR_PI_RUN_ENV,
26
+ HerdrPiFrameDecoder,
27
+ encodeHerdrPiFrame,
28
+ validateHerdrPiRunId
29
+ } from "./chunk-4QFZZV3O.js";
30
+ import "./chunk-N2BN7BF5.js";
31
+ import {
32
+ buildAgentMemoryInjection,
33
+ buildSkillInjection,
34
+ discoverAgents,
35
+ resolveExistingReadPaths,
36
+ resolveSkills
37
+ } from "./chunk-ZBC44JJ3.js";
38
+ import "./chunk-5LFLVSTM.js";
39
+ import "./chunk-YFCK7H2S.js";
40
+ import "./chunk-Z7KVHQHL.js";
41
+
42
+ // src/extension/herdr-pi-bridge.ts
43
+ import { spawnSync } from "node:child_process";
44
+ import * as fs from "node:fs";
45
+ import * as net from "node:net";
46
+ import * as path from "node:path";
47
+ import { Type } from "typebox";
48
+ function gitEvidence(cwd) {
49
+ const run = (args) => spawnSync("git", args, { cwd, encoding: "utf8", timeout: 5e3, maxBuffer: 128 * 1024 });
50
+ const inside = run(["rev-parse", "--is-inside-work-tree"]);
51
+ if (inside.status !== 0) return void 0;
52
+ const head = run(["rev-parse", "HEAD"]);
53
+ const branch = run(["symbolic-ref", "--quiet", "--short", "HEAD"]);
54
+ const dirty = run(["status", "--porcelain"]);
55
+ return { ...head.status === 0 ? { head: head.stdout.trim() } : {}, ...branch.status === 0 ? { branch: branch.stdout.trim() } : {}, dirty: Boolean(dirty.stdout.trim()) };
56
+ }
57
+ function resolveRemoteHerdrResources(cwd, resources, remoteDefaultTools = []) {
58
+ 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.");
59
+ const discovered = discoverAgents(cwd, "both").agents.filter((agent2) => agent2.name === resources.agent || agent2.aliases?.includes(resources.agent));
60
+ if (discovered.length !== 1) throw new Error(`Remote agent '${resources.agent}' was not found unambiguously in ${cwd}.`);
61
+ const agent = discovered[0];
62
+ const names = resources.skills ?? agent.skills ?? [];
63
+ const skills = resolveSkills(names, cwd);
64
+ if (skills.missing.length) throw new Error(`Remote Pi skills not found: ${skills.missing.join(", ")}. Configure them on the saved machine.`);
65
+ const skillPrompt = buildSkillInjection(skills.resolved);
66
+ const reads = resources.reads === void 0 ? agent.defaultReads ?? false : resources.reads;
67
+ const readPaths = Array.isArray(reads) ? resolveExistingReadPaths(reads, cwd) : [];
68
+ const systemPrompt = appendAgentRefinementOverlay(`${agent.systemPrompt}${buildAgentMemoryInjection(agent, cwd)}${skillPrompt ? `
69
+
70
+ ${skillPrompt}` : ""}${readPaths.length ? `
71
+
72
+ [Read from: ${readPaths.join(", ")}]` : ""}`, { cwd, agentName: agent.name });
73
+ const denied = new Set(agent.excludeTools ?? []);
74
+ const tools = (agent.tools === void 0 ? remoteDefaultTools : agent.tools).filter((tool) => !denied.has(tool) && (!resources.toolCeiling || resources.toolCeiling.includes(tool)));
75
+ if (agent.tools === void 0 && !tools.length) throw new Error(`Remote agent '${agent.name}' resolved no default active tools within the inherited ceiling.`);
76
+ return { agent: agent.name, skills: names, tools, systemPrompt, inheritProjectContext: agent.inheritProjectContext, inheritGlobalContext: agent.inheritGlobalContext, inheritSkills: agent.inheritSkills };
77
+ }
78
+ function registerHerdrPiBridge(pi) {
79
+ const runId = validateHerdrPiRunId(process.env[HERDR_PI_RUN_ENV]);
80
+ const runDir = process.env[HERDR_PI_RUNTIME_DIR_ENV];
81
+ if (!runDir || !path.isAbsolute(runDir) || fs.lstatSync(runDir).isSymbolicLink()) throw new Error("Herdr bridge runtime directory was not authoritatively provisioned.");
82
+ fs.chmodSync(runDir, 448);
83
+ const socketPath = path.join(runDir, "bridge.sock");
84
+ const manifestPath = path.join(runDir, "manifest.json");
85
+ for (const candidate of [socketPath, manifestPath]) {
86
+ try {
87
+ const stat = fs.lstatSync(candidate);
88
+ if (stat.isSymbolicLink()) throw new Error(`Refusing symlink at '${candidate}'.`);
89
+ fs.rmSync(candidate, { force: true });
90
+ } catch (error) {
91
+ if (error.code !== "ENOENT") throw error;
92
+ }
93
+ }
94
+ let client;
95
+ let context;
96
+ let nativeSessionId;
97
+ let finalAssistant;
98
+ let activeRequestId;
99
+ let resolvedSystemPrompt;
100
+ let resolvedContextPolicy;
101
+ let configured = false;
102
+ const pending = /* @__PURE__ */ new Map();
103
+ const supervisor = /* @__PURE__ */ new Map();
104
+ let evidenceCursor = 0;
105
+ let journalBytes = 0;
106
+ const journal = [];
107
+ const send = (frame) => {
108
+ const record = { protocol: HERDR_PI_PROTOCOL, runId, ...frame, cursor: ++evidenceCursor };
109
+ const size = Buffer.byteLength(JSON.stringify(record));
110
+ journal.push(record);
111
+ journalBytes += size;
112
+ while (journal.length > 512 || journalBytes > 8 * 1024 * 1024) {
113
+ const removed = journal.shift();
114
+ if (removed) journalBytes -= Buffer.byteLength(JSON.stringify(removed));
115
+ }
116
+ if (!client || client.destroyed) return;
117
+ client.write(encodeHerdrPiFrame(record));
118
+ };
119
+ const sessionId = (ctx) => ctx.sessionManager?.getSessionId?.();
120
+ const activeTools = (ctx) => ctx.getActiveTools?.() ?? [];
121
+ pi.registerTool({
122
+ name: "contact_supervisor",
123
+ ...MODEL_ONLY_TOOL,
124
+ label: "Contact supervisor",
125
+ description: "Contact the parent/supervisor session for a blocking decision, structured interview, or progress update.",
126
+ 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 }),
127
+ execute: async (_toolCallId, input) => {
128
+ if (input.reason !== "interview_request" && !input.message?.trim()) throw new Error("message is required for supervisor decisions and progress updates.");
129
+ const id = `sup_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 8)}`;
130
+ 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
+ if (input.reason === "progress_update") {
132
+ await new Promise((resolve) => supervisor.set(id, { reason: input.reason, resolve }));
133
+ return { content: [{ type: "text", text: "Supervisor progress update queued." }], details: { delivered: true, requestId: id, reason: input.reason } };
134
+ }
135
+ const reason = input.reason;
136
+ const answer = await new Promise((resolve) => supervisor.set(id, { reason, resolve }));
137
+ const details = { requestId: id, reason };
138
+ if (reason === "interview_request") try {
139
+ details.structuredReply = JSON.parse(answer.replace(/^```(?:json)?\s*|\s*```$/giu, ""));
140
+ } catch {
141
+ details.structuredReplyParseError = "Supervisor interview reply was not valid JSON.";
142
+ }
143
+ return { content: [{ type: "text", text: `**Reply from supervisor:**
144
+ ${answer}` }], details };
145
+ }
146
+ });
147
+ const execute = async (requestId, ctx) => {
148
+ const request = pending.get(requestId);
149
+ if (!request) throw new Error(`Unknown or already consumed bridge request '${requestId}'.`);
150
+ pending.delete(requestId);
151
+ if (sessionId(ctx) !== nativeSessionId) throw new Error("Native Pi session identity changed.");
152
+ send({ type: "accepted", requestId, operation: request.operation, nativeSessionId });
153
+ const api = pi;
154
+ if (request.operation === "prompt") {
155
+ activeRequestId = requestId;
156
+ finalAssistant = void 0;
157
+ api.sendUserMessage(request.text ?? "");
158
+ } else if (request.operation === "steer") api.sendUserMessage(request.text ?? "", { deliverAs: "steer" });
159
+ else if (request.operation === "follow-up") api.sendUserMessage(request.text ?? "", { deliverAs: "followUp" });
160
+ else if (request.operation === "abort") api.abort();
161
+ else {
162
+ const entry = request.supervisorId ? supervisor.get(request.supervisorId) : void 0;
163
+ if (!entry) throw new Error("Unknown supervisor request.");
164
+ supervisor.delete(request.supervisorId);
165
+ entry.resolve(request.text ?? "");
166
+ }
167
+ send({ type: "control-ack", requestId, operation: request.operation, nativeSessionId });
168
+ };
169
+ pi.registerCommand("pi-subagents-bridge", { description: "Internal pane-native request dispatch", handler: (args, ctx) => execute(args.trim(), ctx) });
170
+ const on = pi.on;
171
+ on("before_agent_start", (event) => {
172
+ const options = event.systemPromptOptions;
173
+ if (typeof options?.appendSystemPrompt === "string") options.appendSystemPrompt = "";
174
+ return resolvedSystemPrompt && resolvedContextPolicy ? { systemPrompt: `${rewriteSubagentPrompt(typeof event.systemPrompt === "string" ? event.systemPrompt : "", { inheritProjectContext: resolvedContextPolicy.inheritProjectContext, inheritGlobalContext: resolvedContextPolicy.inheritGlobalContext, inheritSkills: resolvedContextPolicy.inheritSkills })}
175
+
176
+ <active_agent name=${JSON.stringify(resolvedContextPolicy.agent)}/>
177
+
178
+ ${resolvedSystemPrompt}` } : void 0;
179
+ });
180
+ on("session_start", (_event, ctx) => {
181
+ context = ctx;
182
+ nativeSessionId = sessionId(ctx);
183
+ if (!nativeSessionId) throw new Error("Pane-native Pi bridge requires a persisted native session identity.");
184
+ 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()) });
186
+ });
187
+ on("message_end", (event) => {
188
+ const message = event.message;
189
+ if (message?.role === "assistant") finalAssistant = message;
190
+ send({ type: "event", requestId: activeRequestId, event: { ...event, message }, nativeSessionId });
191
+ });
192
+ on("agent_settled", (_event, ctx) => {
193
+ const requestId = activeRequestId;
194
+ send({ type: "settled", requestId, nativeSessionId, idle: ctx.isIdle(), pending: ctx.hasPendingMessages(), finalAssistant, finalGit: gitEvidence(process.cwd()) });
195
+ if (ctx.isIdle() && !ctx.hasPendingMessages()) activeRequestId = void 0;
196
+ });
197
+ on("session_shutdown", () => {
198
+ server.close();
199
+ client?.destroy();
200
+ try {
201
+ fs.rmSync(runDir, { recursive: true, force: true });
202
+ } catch {
203
+ }
204
+ });
205
+ const server = net.createServer((socket) => {
206
+ if (client && !client.destroyed) {
207
+ socket.destroy();
208
+ return;
209
+ }
210
+ client = socket;
211
+ const decoder = new HerdrPiFrameDecoder();
212
+ let total = 0;
213
+ for (const record of journal) socket.write(encodeHerdrPiFrame(record));
214
+ socket.on("data", (chunk) => {
215
+ total += chunk.byteLength;
216
+ if (total > HERDR_PI_MAX_FRAME_BYTES * 32) {
217
+ socket.destroy(new Error("Bridge byte budget exceeded."));
218
+ return;
219
+ }
220
+ try {
221
+ for (const frame of decoder.push(chunk)) {
222
+ if (frame.runId !== runId) throw new Error("Bridge run identity mismatch.");
223
+ if (frame.type === "supervisor-delivered" && typeof frame.requestId === "string") {
224
+ const entry = supervisor.get(frame.requestId);
225
+ if (!entry || entry.reason !== "progress_update") throw new Error("Unknown supervisor delivery acknowledgement.");
226
+ supervisor.delete(frame.requestId);
227
+ entry.resolve("delivered");
228
+ continue;
229
+ }
230
+ if (frame.type === "configure") {
231
+ if (configured || typeof frame.requestId !== "string" || !frame.resources || typeof frame.resources !== "object") throw new Error("Invalid or duplicate bridge configuration.");
232
+ const resources = frame.resources;
233
+ 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
+ const invalidReads = resources.reads !== void 0 && resources.reads !== false && (!Array.isArray(resources.reads) || resources.reads.some((name) => typeof name !== "string"));
235
+ 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) : []);
237
+ resolvedSystemPrompt = resolved.systemPrompt;
238
+ resolvedContextPolicy = resolved;
239
+ pi.setActiveTools?.(resolved.tools);
240
+ const acknowledged = context ? activeTools(context) : [];
241
+ 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
+ configured = true;
243
+ 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 });
244
+ continue;
245
+ }
246
+ if (frame.type !== "prepare" || typeof frame.requestId !== "string" || pending.has(frame.requestId) || !configured) throw new Error("Invalid, duplicate, or unconfigured bridge request.");
247
+ const operation = frame.operation;
248
+ if (!["prompt", "steer", "follow-up", "abort", "supervisor-reply"].includes(operation)) throw new Error("Unsupported bridge operation.");
249
+ const text = frame.text;
250
+ 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 } : {} });
252
+ send({ type: "prepared", requestId: frame.requestId, nativeSessionId });
253
+ }
254
+ } catch (error) {
255
+ socket.destroy(error);
256
+ }
257
+ });
258
+ socket.on("close", () => {
259
+ if (client === socket) client = void 0;
260
+ });
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()) });
262
+ });
263
+ server.listen(socketPath, () => {
264
+ fs.chmodSync(socketPath, 384);
265
+ });
266
+ }
267
+ export {
268
+ registerHerdrPiBridge as default,
269
+ resolveRemoteHerdrResources
270
+ };