@astralyn/pi 0.81.1-2 → 0.82.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (191) hide show
  1. package/CHANGELOG.md +53 -0
  2. package/README.md +14 -1
  3. package/dist/cli/args.d.ts.map +1 -1
  4. package/dist/cli/args.js +1 -0
  5. package/dist/cli/args.js.map +1 -1
  6. package/dist/cli/config-selector.d.ts.map +1 -1
  7. package/dist/cli/config-selector.js +1 -1
  8. package/dist/cli/config-selector.js.map +1 -1
  9. package/dist/cli/startup-ui.d.ts.map +1 -1
  10. package/dist/cli/startup-ui.js +1 -1
  11. package/dist/cli/startup-ui.js.map +1 -1
  12. package/dist/core/agent-session.d.ts +11 -0
  13. package/dist/core/agent-session.d.ts.map +1 -1
  14. package/dist/core/agent-session.js +7 -8
  15. package/dist/core/agent-session.js.map +1 -1
  16. package/dist/core/bash-executor.d.ts.map +1 -1
  17. package/dist/core/bash-executor.js +22 -4
  18. package/dist/core/bash-executor.js.map +1 -1
  19. package/dist/core/compaction/compaction.d.ts.map +1 -1
  20. package/dist/core/compaction/compaction.js +11 -3
  21. package/dist/core/compaction/compaction.js.map +1 -1
  22. package/dist/core/extensions/runner.d.ts.map +1 -1
  23. package/dist/core/extensions/runner.js +4 -0
  24. package/dist/core/extensions/runner.js.map +1 -1
  25. package/dist/core/extensions/types.d.ts +9 -1
  26. package/dist/core/extensions/types.d.ts.map +1 -1
  27. package/dist/core/extensions/types.js.map +1 -1
  28. package/dist/core/model-config.d.ts +30 -0
  29. package/dist/core/model-config.d.ts.map +1 -1
  30. package/dist/core/model-config.js +6 -0
  31. package/dist/core/model-config.js.map +1 -1
  32. package/dist/core/model-registry.d.ts.map +1 -1
  33. package/dist/core/model-registry.js +2 -2
  34. package/dist/core/model-registry.js.map +1 -1
  35. package/dist/core/model-resolver.d.ts.map +1 -1
  36. package/dist/core/model-resolver.js +7 -0
  37. package/dist/core/model-resolver.js.map +1 -1
  38. package/dist/core/model-runtime.d.ts +0 -1
  39. package/dist/core/model-runtime.d.ts.map +1 -1
  40. package/dist/core/model-runtime.js +3 -5
  41. package/dist/core/model-runtime.js.map +1 -1
  42. package/dist/core/remote-catalog-provider.d.ts +1 -1
  43. package/dist/core/remote-catalog-provider.d.ts.map +1 -1
  44. package/dist/core/remote-catalog-provider.js +5 -10
  45. package/dist/core/remote-catalog-provider.js.map +1 -1
  46. package/dist/core/settings-manager.d.ts +1 -1
  47. package/dist/core/settings-manager.d.ts.map +1 -1
  48. package/dist/core/settings-manager.js.map +1 -1
  49. package/dist/core/system-prompt.d.ts.map +1 -1
  50. package/dist/core/system-prompt.js +1 -1
  51. package/dist/core/system-prompt.js.map +1 -1
  52. package/dist/core/tools/bash.d.ts +2 -1
  53. package/dist/core/tools/bash.d.ts.map +1 -1
  54. package/dist/core/tools/bash.js +49 -48
  55. package/dist/core/tools/bash.js.map +1 -1
  56. package/dist/core/tools/find.d.ts.map +1 -1
  57. package/dist/core/tools/find.js +1 -0
  58. package/dist/core/tools/find.js.map +1 -1
  59. package/dist/core/tools/output-accumulator.d.ts +13 -3
  60. package/dist/core/tools/output-accumulator.d.ts.map +1 -1
  61. package/dist/core/tools/output-accumulator.js +36 -6
  62. package/dist/core/tools/output-accumulator.js.map +1 -1
  63. package/dist/core/tools/read.d.ts.map +1 -1
  64. package/dist/core/tools/read.js +4 -3
  65. package/dist/core/tools/read.js.map +1 -1
  66. package/dist/core/tools/tool-definition-wrapper.d.ts.map +1 -1
  67. package/dist/core/tools/tool-definition-wrapper.js +5 -1
  68. package/dist/core/tools/tool-definition-wrapper.js.map +1 -1
  69. package/dist/extensions/index.d.ts.map +1 -1
  70. package/dist/extensions/index.js +2 -0
  71. package/dist/extensions/index.js.map +1 -1
  72. package/dist/extensions/llama/provider.d.ts.map +1 -1
  73. package/dist/extensions/llama/provider.js +1 -2
  74. package/dist/extensions/llama/provider.js.map +1 -1
  75. package/dist/extensions/subagent/activity.d.ts +16 -0
  76. package/dist/extensions/subagent/activity.d.ts.map +1 -0
  77. package/dist/extensions/subagent/activity.js +150 -0
  78. package/dist/extensions/subagent/activity.js.map +1 -0
  79. package/dist/extensions/subagent/agents.d.ts +7 -0
  80. package/dist/extensions/subagent/agents.d.ts.map +1 -0
  81. package/dist/extensions/subagent/agents.js +218 -0
  82. package/dist/extensions/subagent/agents.js.map +1 -0
  83. package/dist/extensions/subagent/constants.d.ts +22 -0
  84. package/dist/extensions/subagent/constants.d.ts.map +1 -0
  85. package/dist/extensions/subagent/constants.js +32 -0
  86. package/dist/extensions/subagent/constants.js.map +1 -0
  87. package/dist/extensions/subagent/index.d.ts +3 -0
  88. package/dist/extensions/subagent/index.d.ts.map +1 -0
  89. package/dist/extensions/subagent/index.js +249 -0
  90. package/dist/extensions/subagent/index.js.map +1 -0
  91. package/dist/extensions/subagent/picker.d.ts +31 -0
  92. package/dist/extensions/subagent/picker.d.ts.map +1 -0
  93. package/dist/extensions/subagent/picker.js +116 -0
  94. package/dist/extensions/subagent/picker.js.map +1 -0
  95. package/dist/extensions/subagent/render.d.ts +8 -0
  96. package/dist/extensions/subagent/render.d.ts.map +1 -0
  97. package/dist/extensions/subagent/render.js +322 -0
  98. package/dist/extensions/subagent/render.js.map +1 -0
  99. package/dist/extensions/subagent/resolve.d.ts +16 -0
  100. package/dist/extensions/subagent/resolve.d.ts.map +1 -0
  101. package/dist/extensions/subagent/resolve.js +103 -0
  102. package/dist/extensions/subagent/resolve.js.map +1 -0
  103. package/dist/extensions/subagent/runner.d.ts +30 -0
  104. package/dist/extensions/subagent/runner.d.ts.map +1 -0
  105. package/dist/extensions/subagent/runner.js +355 -0
  106. package/dist/extensions/subagent/runner.js.map +1 -0
  107. package/dist/extensions/subagent/schema.d.ts +28 -0
  108. package/dist/extensions/subagent/schema.d.ts.map +1 -0
  109. package/dist/extensions/subagent/schema.js +33 -0
  110. package/dist/extensions/subagent/schema.js.map +1 -0
  111. package/dist/extensions/subagent/sdk-runner.d.ts +14 -0
  112. package/dist/extensions/subagent/sdk-runner.d.ts.map +1 -0
  113. package/dist/extensions/subagent/sdk-runner.js +210 -0
  114. package/dist/extensions/subagent/sdk-runner.js.map +1 -0
  115. package/dist/extensions/subagent/settings.d.ts +9 -0
  116. package/dist/extensions/subagent/settings.d.ts.map +1 -0
  117. package/dist/extensions/subagent/settings.js +131 -0
  118. package/dist/extensions/subagent/settings.js.map +1 -0
  119. package/dist/extensions/subagent/types.d.ts +99 -0
  120. package/dist/extensions/subagent/types.d.ts.map +1 -0
  121. package/dist/extensions/subagent/types.js +2 -0
  122. package/dist/extensions/subagent/types.js.map +1 -0
  123. package/dist/modes/interactive/components/extension-editor.d.ts +1 -2
  124. package/dist/modes/interactive/components/extension-editor.d.ts.map +1 -1
  125. package/dist/modes/interactive/components/extension-editor.js +16 -46
  126. package/dist/modes/interactive/components/extension-editor.js.map +1 -1
  127. package/dist/modes/interactive/components/index.d.ts +1 -0
  128. package/dist/modes/interactive/components/index.d.ts.map +1 -1
  129. package/dist/modes/interactive/components/index.js +1 -0
  130. package/dist/modes/interactive/components/index.js.map +1 -1
  131. package/dist/modes/interactive/components/tool-execution.d.ts +2 -0
  132. package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  133. package/dist/modes/interactive/components/tool-execution.js +25 -4
  134. package/dist/modes/interactive/components/tool-execution.js.map +1 -1
  135. package/dist/modes/interactive/components/tool-group.d.ts +16 -0
  136. package/dist/modes/interactive/components/tool-group.d.ts.map +1 -0
  137. package/dist/modes/interactive/components/tool-group.js +64 -0
  138. package/dist/modes/interactive/components/tool-group.js.map +1 -0
  139. package/dist/modes/interactive/external-editor.d.ts +12 -0
  140. package/dist/modes/interactive/external-editor.d.ts.map +1 -0
  141. package/dist/modes/interactive/external-editor.js +37 -0
  142. package/dist/modes/interactive/external-editor.js.map +1 -0
  143. package/dist/modes/interactive/interactive-mode.d.ts +3 -1
  144. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  145. package/dist/modes/interactive/interactive-mode.js +61 -62
  146. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  147. package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
  148. package/dist/modes/rpc/rpc-mode.js +1 -0
  149. package/dist/modes/rpc/rpc-mode.js.map +1 -1
  150. package/dist/utils/clipboard.d.ts.map +1 -1
  151. package/dist/utils/clipboard.js +19 -8
  152. package/dist/utils/clipboard.js.map +1 -1
  153. package/dist/utils/output-decoder.d.ts +52 -0
  154. package/dist/utils/output-decoder.d.ts.map +1 -0
  155. package/dist/utils/output-decoder.js +134 -0
  156. package/dist/utils/output-decoder.js.map +1 -0
  157. package/dist/utils/shell.d.ts +1 -0
  158. package/dist/utils/shell.d.ts.map +1 -1
  159. package/dist/utils/shell.js +21 -0
  160. package/dist/utils/shell.js.map +1 -1
  161. package/dist/utils/version-check.d.ts.map +1 -1
  162. package/dist/utils/version-check.js +3 -1
  163. package/dist/utils/version-check.js.map +1 -1
  164. package/docs/compaction.md +1 -1
  165. package/docs/custom-provider.md +5 -2
  166. package/docs/environment-variables.md +88 -0
  167. package/docs/extensions.md +11 -3
  168. package/docs/index.md +4 -1
  169. package/docs/models.md +6 -2
  170. package/docs/providers.md +8 -1
  171. package/docs/rpc.md +23 -6
  172. package/docs/settings.md +1 -1
  173. package/docs/usage.md +0 -13
  174. package/examples/extensions/custom-compaction.ts +3 -0
  175. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  176. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  177. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  178. package/examples/extensions/gondolin/package-lock.json +2 -2
  179. package/examples/extensions/gondolin/package.json +1 -1
  180. package/examples/extensions/handoff.ts +9 -1
  181. package/examples/extensions/sandbox/package-lock.json +2 -2
  182. package/examples/extensions/sandbox/package.json +1 -1
  183. package/examples/extensions/summarize.ts +3 -0
  184. package/examples/extensions/with-deps/package-lock.json +2 -2
  185. package/examples/extensions/with-deps/package.json +1 -1
  186. package/npm-shrinkwrap.json +16 -15
  187. package/package.json +5 -4
  188. package/dist/core/tools/bash-command-summary.d.ts +0 -7
  189. package/dist/core/tools/bash-command-summary.d.ts.map +0 -1
  190. package/dist/core/tools/bash-command-summary.js +0 -261
  191. package/dist/core/tools/bash-command-summary.js.map +0 -1
@@ -0,0 +1,218 @@
1
+ import { existsSync, readdirSync, readFileSync, statSync } from "node:fs";
2
+ import { dirname, join } from "node:path";
3
+ import { CONFIG_DIR_NAME, getAgentDir } from "../../config.js";
4
+ import { parseFrontmatter } from "../../utils/frontmatter.js";
5
+ import { BUILTIN_TOOL_NAMES, DEFAULT_AGENT_TOOLS, EXPLORER_TOOLS } from "./constants.js";
6
+ const AGENT_NAME_PATTERN = /^[a-z0-9][a-z0-9_-]{0,79}$/u;
7
+ const BUILTIN_TOOL_SET = new Set(BUILTIN_TOOL_NAMES);
8
+ const TOOL_DESCRIPTION_AGENT_LIST_LIMIT = 8_000;
9
+ const TOOL_DESCRIPTION_AGENT_SUMMARY_LIMIT = 480;
10
+ function utf8Prefix(text, maxBytes) {
11
+ let output = "";
12
+ let bytes = 0;
13
+ for (const character of text) {
14
+ const characterBytes = Buffer.byteLength(character, "utf8");
15
+ if (bytes + characterBytes > maxBytes)
16
+ break;
17
+ output += character;
18
+ bytes += characterBytes;
19
+ }
20
+ return output;
21
+ }
22
+ const BUILTIN_AGENTS = [
23
+ {
24
+ name: "general",
25
+ description: "General-purpose implementation agent with coding tools",
26
+ tools: [...DEFAULT_AGENT_TOOLS],
27
+ systemPrompt: [
28
+ "Work independently on the delegated task from start to finish.",
29
+ "Inspect relevant repository instructions before changing files.",
30
+ "Prefer editing existing files; never create documentation files unless the task explicitly asks for them.",
31
+ "Keep changes focused, run appropriate verification, and report exact paths, checks, blockers, and remaining risks.",
32
+ ].join("\n"),
33
+ source: "builtin",
34
+ filePath: "<builtin:general>",
35
+ backend: "sdk",
36
+ },
37
+ {
38
+ name: "explorer",
39
+ description: 'Fast read-only agent for finding files, searching code, and answering codebase questions. State thoroughness in the prompt: "quick" for a targeted lookup, "medium" for checking likely related locations, or "very thorough" for exhaustive sweeps across locations and naming conventions',
40
+ tools: [...EXPLORER_TOOLS],
41
+ systemPrompt: [
42
+ "You are a fast read-only exploration agent; return findings as quickly as possible.",
43
+ "Explore the delegated question without modifying files.",
44
+ "Use read and search tools to gather exact evidence, batching independent searches and reads instead of running them one at a time.",
45
+ "Match the depth the caller requested: quick targets the direct question, medium checks likely related locations, and very thorough sweeps multiple locations and naming conventions.",
46
+ "Return concise findings with precise paths, symbols, relationships, and unresolved uncertainties.",
47
+ ].join("\n"),
48
+ source: "builtin",
49
+ filePath: "<builtin:explorer>",
50
+ backend: "sdk",
51
+ },
52
+ ];
53
+ function isDirectory(path) {
54
+ try {
55
+ return statSync(path).isDirectory();
56
+ }
57
+ catch {
58
+ return false;
59
+ }
60
+ }
61
+ function findNearestProjectAgentsDir(cwd) {
62
+ let current = cwd;
63
+ while (true) {
64
+ const candidate = join(current, CONFIG_DIR_NAME, "agents");
65
+ if (isDirectory(candidate))
66
+ return candidate;
67
+ const parent = dirname(current);
68
+ if (parent === current)
69
+ return undefined;
70
+ current = parent;
71
+ }
72
+ }
73
+ function parseTools(raw, filePath) {
74
+ if (!raw?.trim())
75
+ return [...DEFAULT_AGENT_TOOLS];
76
+ const tools = [
77
+ ...new Set(raw
78
+ .split(",")
79
+ .map((tool) => tool.trim())
80
+ .filter(Boolean)),
81
+ ];
82
+ const unsupported = tools.filter((tool) => !BUILTIN_TOOL_SET.has(tool));
83
+ if (unsupported.length > 0) {
84
+ throw new Error(`Unsupported tool(s) ${unsupported.join(", ")} in ${filePath}.`);
85
+ }
86
+ return tools;
87
+ }
88
+ // Frontmatter model/thinking keys are intentionally ignored: agent files
89
+ // travel across machines (project repos), so a pinned model rarely exists
90
+ // in the reader's environment. Model and thinking come from /agents
91
+ // overrides or default to the parent session.
92
+ function parseAgentFile(filePath, source) {
93
+ const content = readFileSync(filePath, "utf8");
94
+ const { frontmatter, body } = parseFrontmatter(content);
95
+ const name = frontmatter.name?.trim() ?? "";
96
+ const description = frontmatter.description?.trim() ?? "";
97
+ if (!name)
98
+ throw new Error("Missing required frontmatter field: name.");
99
+ if (!AGENT_NAME_PATTERN.test(name)) {
100
+ throw new Error(`Agent name "${name}" must use lowercase letters, digits, hyphens, or underscores.`);
101
+ }
102
+ if (!description)
103
+ throw new Error("Missing required frontmatter field: description.");
104
+ if (!body.trim())
105
+ throw new Error("Agent prompt body must not be empty.");
106
+ const backend = frontmatter.backend?.trim();
107
+ if (backend && backend !== "sdk")
108
+ throw new Error(`Backend "${backend}" is not implemented; use "sdk".`);
109
+ return {
110
+ name,
111
+ description,
112
+ tools: parseTools(frontmatter.tools, filePath),
113
+ systemPrompt: body.trim(),
114
+ source,
115
+ filePath,
116
+ backend: "sdk",
117
+ };
118
+ }
119
+ function loadAgentDirectory(dir, source) {
120
+ if (!existsSync(dir))
121
+ return { agents: [], diagnostics: [] };
122
+ let entries;
123
+ try {
124
+ entries = readdirSync(dir, { withFileTypes: true }).sort((left, right) => left.name.localeCompare(right.name));
125
+ }
126
+ catch (error) {
127
+ return {
128
+ agents: [],
129
+ diagnostics: [{ path: dir, source, message: error instanceof Error ? error.message : String(error) }],
130
+ };
131
+ }
132
+ const agents = [];
133
+ const diagnostics = [];
134
+ for (const entry of entries) {
135
+ if (!entry.name.endsWith(".md") || (!entry.isFile() && !entry.isSymbolicLink()))
136
+ continue;
137
+ const filePath = join(dir, entry.name);
138
+ try {
139
+ agents.push(parseAgentFile(filePath, source));
140
+ }
141
+ catch (error) {
142
+ diagnostics.push({
143
+ path: filePath,
144
+ source,
145
+ message: error instanceof Error ? error.message : String(error),
146
+ });
147
+ }
148
+ }
149
+ return { agents, diagnostics };
150
+ }
151
+ export function discoverAgents(cwd, options) {
152
+ const userDir = join(options.agentDir ?? getAgentDir(), "agents");
153
+ const projectAgentsDir = findNearestProjectAgentsDir(cwd);
154
+ const user = loadAgentDirectory(userDir, "user");
155
+ const project = options.projectTrusted && projectAgentsDir
156
+ ? loadAgentDirectory(projectAgentsDir, "project")
157
+ : { agents: [], diagnostics: [] };
158
+ const agents = new Map(BUILTIN_AGENTS.map((agent) => [agent.name, { ...agent, tools: [...agent.tools] }]));
159
+ for (const agent of user.agents)
160
+ agents.set(agent.name, agent);
161
+ for (const agent of project.agents)
162
+ agents.set(agent.name, agent);
163
+ return {
164
+ agents: [...agents.values()],
165
+ diagnostics: [...user.diagnostics, ...project.diagnostics],
166
+ projectAgentsDir,
167
+ projectAgentsTrusted: options.projectTrusted,
168
+ };
169
+ }
170
+ export function subagentToolDescription(discovery) {
171
+ const agentLines = [];
172
+ let agentListLength = 0;
173
+ for (const [index, agent] of discovery.agents.entries()) {
174
+ const normalizedDescription = agent.description.replace(/\s+/gu, " ").trim();
175
+ const description = Buffer.byteLength(normalizedDescription, "utf8") <= TOOL_DESCRIPTION_AGENT_SUMMARY_LIMIT
176
+ ? normalizedDescription
177
+ : `${utf8Prefix(normalizedDescription, TOOL_DESCRIPTION_AGENT_SUMMARY_LIMIT - Buffer.byteLength("…", "utf8"))}…`;
178
+ const defaultLabel = agent.name === "general" ? " (default)" : "";
179
+ const tools = agent.tools.length > 0 ? agent.tools.join(", ") : "none";
180
+ const line = `- ${agent.name}${defaultLabel}: ${description} (Tools: ${tools})`;
181
+ const lineLength = Buffer.byteLength(line, "utf8") + 1;
182
+ if (agentListLength + lineLength > TOOL_DESCRIPTION_AGENT_LIST_LIMIT) {
183
+ let omitted = discovery.agents.length - index;
184
+ let notice = `- ${omitted} additional profile${omitted === 1 ? "" : "s"} omitted from this bounded description; /agents lists every profile.`;
185
+ while (agentLines.length > 0 &&
186
+ agentListLength + Buffer.byteLength(notice, "utf8") + 1 > TOOL_DESCRIPTION_AGENT_LIST_LIMIT) {
187
+ const removed = agentLines.pop();
188
+ if (!removed)
189
+ break;
190
+ agentListLength -= Buffer.byteLength(removed, "utf8") + 1;
191
+ omitted++;
192
+ notice = `- ${omitted} additional profile${omitted === 1 ? "" : "s"} omitted from this bounded description; /agents lists every profile.`;
193
+ }
194
+ agentLines.push(notice);
195
+ break;
196
+ }
197
+ agentLines.push(line);
198
+ agentListLength += lineLength;
199
+ }
200
+ return [
201
+ "Delegate bounded work to isolated one-shot subagents. Choose exactly one mode: prompt for one task, tasks for independent parallel work, or chain for sequential work whose later prompts may include {previous}.",
202
+ "",
203
+ "Available agent profiles:",
204
+ ...agentLines,
205
+ "",
206
+ "When not to delegate:",
207
+ "- Read a known file directly with read, or search for a known symbol directly with grep.",
208
+ "- Do not delegate a trivial task when the coordination overhead exceeds the work.",
209
+ "",
210
+ "Writing the briefing:",
211
+ "- Brief the worker like a capable colleague entering the project now: include the objective, relevant context, exact paths or symbols already known, constraints, and expected output.",
212
+ "- Never delegate understanding with vague instructions such as 'fix this based on your findings'; state what must be investigated or changed and whether the worker should research, implement, or review.",
213
+ "- Ask for a concise report when only findings are needed. Workers cannot see the parent conversation or ask the end user questions.",
214
+ "",
215
+ "Subagent results are reports for you to interpret and relay. Surface the important findings, changes, verification, and unresolved risks to the user.",
216
+ ].join("\n");
217
+ }
218
+ //# sourceMappingURL=agents.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agents.js","sourceRoot":"","sources":["../../../src/extensions/subagent/agents.ts"],"names":[],"mappings":"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","sourcesContent":["import { type Dirent, existsSync, readdirSync, readFileSync, statSync } from \"node:fs\";\nimport { dirname, join } from \"node:path\";\nimport { CONFIG_DIR_NAME, getAgentDir } from \"../../config.ts\";\nimport { parseFrontmatter } from \"../../utils/frontmatter.ts\";\nimport { BUILTIN_TOOL_NAMES, DEFAULT_AGENT_TOOLS, EXPLORER_TOOLS } from \"./constants.ts\";\nimport type { AgentDefinition, AgentDiagnostic, AgentDiscoveryResult, AgentSource } from \"./types.ts\";\n\nconst AGENT_NAME_PATTERN = /^[a-z0-9][a-z0-9_-]{0,79}$/u;\nconst BUILTIN_TOOL_SET = new Set<string>(BUILTIN_TOOL_NAMES);\nconst TOOL_DESCRIPTION_AGENT_LIST_LIMIT = 8_000;\nconst TOOL_DESCRIPTION_AGENT_SUMMARY_LIMIT = 480;\n\nfunction utf8Prefix(text: string, maxBytes: number): string {\n\tlet output = \"\";\n\tlet bytes = 0;\n\tfor (const character of text) {\n\t\tconst characterBytes = Buffer.byteLength(character, \"utf8\");\n\t\tif (bytes + characterBytes > maxBytes) break;\n\t\toutput += character;\n\t\tbytes += characterBytes;\n\t}\n\treturn output;\n}\n\nconst BUILTIN_AGENTS: AgentDefinition[] = [\n\t{\n\t\tname: \"general\",\n\t\tdescription: \"General-purpose implementation agent with coding tools\",\n\t\ttools: [...DEFAULT_AGENT_TOOLS],\n\t\tsystemPrompt: [\n\t\t\t\"Work independently on the delegated task from start to finish.\",\n\t\t\t\"Inspect relevant repository instructions before changing files.\",\n\t\t\t\"Prefer editing existing files; never create documentation files unless the task explicitly asks for them.\",\n\t\t\t\"Keep changes focused, run appropriate verification, and report exact paths, checks, blockers, and remaining risks.\",\n\t\t].join(\"\\n\"),\n\t\tsource: \"builtin\",\n\t\tfilePath: \"<builtin:general>\",\n\t\tbackend: \"sdk\",\n\t},\n\t{\n\t\tname: \"explorer\",\n\t\tdescription:\n\t\t\t'Fast read-only agent for finding files, searching code, and answering codebase questions. State thoroughness in the prompt: \"quick\" for a targeted lookup, \"medium\" for checking likely related locations, or \"very thorough\" for exhaustive sweeps across locations and naming conventions',\n\t\ttools: [...EXPLORER_TOOLS],\n\t\tsystemPrompt: [\n\t\t\t\"You are a fast read-only exploration agent; return findings as quickly as possible.\",\n\t\t\t\"Explore the delegated question without modifying files.\",\n\t\t\t\"Use read and search tools to gather exact evidence, batching independent searches and reads instead of running them one at a time.\",\n\t\t\t\"Match the depth the caller requested: quick targets the direct question, medium checks likely related locations, and very thorough sweeps multiple locations and naming conventions.\",\n\t\t\t\"Return concise findings with precise paths, symbols, relationships, and unresolved uncertainties.\",\n\t\t].join(\"\\n\"),\n\t\tsource: \"builtin\",\n\t\tfilePath: \"<builtin:explorer>\",\n\t\tbackend: \"sdk\",\n\t},\n];\n\nfunction isDirectory(path: string): boolean {\n\ttry {\n\t\treturn statSync(path).isDirectory();\n\t} catch {\n\t\treturn false;\n\t}\n}\n\nfunction findNearestProjectAgentsDir(cwd: string): string | undefined {\n\tlet current = cwd;\n\twhile (true) {\n\t\tconst candidate = join(current, CONFIG_DIR_NAME, \"agents\");\n\t\tif (isDirectory(candidate)) return candidate;\n\t\tconst parent = dirname(current);\n\t\tif (parent === current) return undefined;\n\t\tcurrent = parent;\n\t}\n}\n\nfunction parseTools(raw: string | undefined, filePath: string): string[] {\n\tif (!raw?.trim()) return [...DEFAULT_AGENT_TOOLS];\n\tconst tools = [\n\t\t...new Set(\n\t\t\traw\n\t\t\t\t.split(\",\")\n\t\t\t\t.map((tool) => tool.trim())\n\t\t\t\t.filter(Boolean),\n\t\t),\n\t];\n\tconst unsupported = tools.filter((tool) => !BUILTIN_TOOL_SET.has(tool));\n\tif (unsupported.length > 0) {\n\t\tthrow new Error(`Unsupported tool(s) ${unsupported.join(\", \")} in ${filePath}.`);\n\t}\n\treturn tools;\n}\n\n// Frontmatter model/thinking keys are intentionally ignored: agent files\n// travel across machines (project repos), so a pinned model rarely exists\n// in the reader's environment. Model and thinking come from /agents\n// overrides or default to the parent session.\nfunction parseAgentFile(filePath: string, source: Exclude<AgentSource, \"builtin\">): AgentDefinition {\n\tconst content = readFileSync(filePath, \"utf8\");\n\tconst { frontmatter, body } = parseFrontmatter<Record<string, string>>(content);\n\tconst name = frontmatter.name?.trim() ?? \"\";\n\tconst description = frontmatter.description?.trim() ?? \"\";\n\tif (!name) throw new Error(\"Missing required frontmatter field: name.\");\n\tif (!AGENT_NAME_PATTERN.test(name)) {\n\t\tthrow new Error(`Agent name \"${name}\" must use lowercase letters, digits, hyphens, or underscores.`);\n\t}\n\tif (!description) throw new Error(\"Missing required frontmatter field: description.\");\n\tif (!body.trim()) throw new Error(\"Agent prompt body must not be empty.\");\n\tconst backend = frontmatter.backend?.trim();\n\tif (backend && backend !== \"sdk\") throw new Error(`Backend \"${backend}\" is not implemented; use \"sdk\".`);\n\treturn {\n\t\tname,\n\t\tdescription,\n\t\ttools: parseTools(frontmatter.tools, filePath),\n\t\tsystemPrompt: body.trim(),\n\t\tsource,\n\t\tfilePath,\n\t\tbackend: \"sdk\",\n\t};\n}\n\nfunction loadAgentDirectory(\n\tdir: string,\n\tsource: Exclude<AgentSource, \"builtin\">,\n): { agents: AgentDefinition[]; diagnostics: AgentDiagnostic[] } {\n\tif (!existsSync(dir)) return { agents: [], diagnostics: [] };\n\tlet entries: Dirent[];\n\ttry {\n\t\tentries = readdirSync(dir, { withFileTypes: true }).sort((left, right) => left.name.localeCompare(right.name));\n\t} catch (error) {\n\t\treturn {\n\t\t\tagents: [],\n\t\t\tdiagnostics: [{ path: dir, source, message: error instanceof Error ? error.message : String(error) }],\n\t\t};\n\t}\n\tconst agents: AgentDefinition[] = [];\n\tconst diagnostics: AgentDiagnostic[] = [];\n\tfor (const entry of entries) {\n\t\tif (!entry.name.endsWith(\".md\") || (!entry.isFile() && !entry.isSymbolicLink())) continue;\n\t\tconst filePath = join(dir, entry.name);\n\t\ttry {\n\t\t\tagents.push(parseAgentFile(filePath, source));\n\t\t} catch (error) {\n\t\t\tdiagnostics.push({\n\t\t\t\tpath: filePath,\n\t\t\t\tsource,\n\t\t\t\tmessage: error instanceof Error ? error.message : String(error),\n\t\t\t});\n\t\t}\n\t}\n\treturn { agents, diagnostics };\n}\n\nexport function discoverAgents(\n\tcwd: string,\n\toptions: { projectTrusted: boolean; agentDir?: string },\n): AgentDiscoveryResult {\n\tconst userDir = join(options.agentDir ?? getAgentDir(), \"agents\");\n\tconst projectAgentsDir = findNearestProjectAgentsDir(cwd);\n\tconst user = loadAgentDirectory(userDir, \"user\");\n\tconst project =\n\t\toptions.projectTrusted && projectAgentsDir\n\t\t\t? loadAgentDirectory(projectAgentsDir, \"project\")\n\t\t\t: { agents: [], diagnostics: [] };\n\tconst agents = new Map(BUILTIN_AGENTS.map((agent) => [agent.name, { ...agent, tools: [...agent.tools] }]));\n\tfor (const agent of user.agents) agents.set(agent.name, agent);\n\tfor (const agent of project.agents) agents.set(agent.name, agent);\n\treturn {\n\t\tagents: [...agents.values()],\n\t\tdiagnostics: [...user.diagnostics, ...project.diagnostics],\n\t\tprojectAgentsDir,\n\t\tprojectAgentsTrusted: options.projectTrusted,\n\t};\n}\n\nexport function subagentToolDescription(discovery: AgentDiscoveryResult): string {\n\tconst agentLines: string[] = [];\n\tlet agentListLength = 0;\n\tfor (const [index, agent] of discovery.agents.entries()) {\n\t\tconst normalizedDescription = agent.description.replace(/\\s+/gu, \" \").trim();\n\t\tconst description =\n\t\t\tBuffer.byteLength(normalizedDescription, \"utf8\") <= TOOL_DESCRIPTION_AGENT_SUMMARY_LIMIT\n\t\t\t\t? normalizedDescription\n\t\t\t\t: `${utf8Prefix(normalizedDescription, TOOL_DESCRIPTION_AGENT_SUMMARY_LIMIT - Buffer.byteLength(\"…\", \"utf8\"))}…`;\n\t\tconst defaultLabel = agent.name === \"general\" ? \" (default)\" : \"\";\n\t\tconst tools = agent.tools.length > 0 ? agent.tools.join(\", \") : \"none\";\n\t\tconst line = `- ${agent.name}${defaultLabel}: ${description} (Tools: ${tools})`;\n\t\tconst lineLength = Buffer.byteLength(line, \"utf8\") + 1;\n\t\tif (agentListLength + lineLength > TOOL_DESCRIPTION_AGENT_LIST_LIMIT) {\n\t\t\tlet omitted = discovery.agents.length - index;\n\t\t\tlet notice = `- ${omitted} additional profile${omitted === 1 ? \"\" : \"s\"} omitted from this bounded description; /agents lists every profile.`;\n\t\t\twhile (\n\t\t\t\tagentLines.length > 0 &&\n\t\t\t\tagentListLength + Buffer.byteLength(notice, \"utf8\") + 1 > TOOL_DESCRIPTION_AGENT_LIST_LIMIT\n\t\t\t) {\n\t\t\t\tconst removed = agentLines.pop();\n\t\t\t\tif (!removed) break;\n\t\t\t\tagentListLength -= Buffer.byteLength(removed, \"utf8\") + 1;\n\t\t\t\tomitted++;\n\t\t\t\tnotice = `- ${omitted} additional profile${omitted === 1 ? \"\" : \"s\"} omitted from this bounded description; /agents lists every profile.`;\n\t\t\t}\n\t\t\tagentLines.push(notice);\n\t\t\tbreak;\n\t\t}\n\t\tagentLines.push(line);\n\t\tagentListLength += lineLength;\n\t}\n\n\treturn [\n\t\t\"Delegate bounded work to isolated one-shot subagents. Choose exactly one mode: prompt for one task, tasks for independent parallel work, or chain for sequential work whose later prompts may include {previous}.\",\n\t\t\"\",\n\t\t\"Available agent profiles:\",\n\t\t...agentLines,\n\t\t\"\",\n\t\t\"When not to delegate:\",\n\t\t\"- Read a known file directly with read, or search for a known symbol directly with grep.\",\n\t\t\"- Do not delegate a trivial task when the coordination overhead exceeds the work.\",\n\t\t\"\",\n\t\t\"Writing the briefing:\",\n\t\t\"- Brief the worker like a capable colleague entering the project now: include the objective, relevant context, exact paths or symbols already known, constraints, and expected output.\",\n\t\t\"- Never delegate understanding with vague instructions such as 'fix this based on your findings'; state what must be investigated or changed and whether the worker should research, implement, or review.\",\n\t\t\"- Ask for a concise report when only findings are needed. Workers cannot see the parent conversation or ask the end user questions.\",\n\t\t\"\",\n\t\t\"Subagent results are reports for you to interpret and relay. Surface the important findings, changes, verification, and unresolved risks to the user.\",\n\t].join(\"\\n\");\n}\n"]}
@@ -0,0 +1,22 @@
1
+ export declare const SUBAGENT_TOOL_NAME = "subagent";
2
+ export declare const SUBAGENT_TOOL_LABEL = "Subagent";
3
+ export declare const SUBAGENT_COMMAND_NAME = "agents";
4
+ export declare const SUBAGENT_CONFIG_VERSION = 1;
5
+ export declare const SUBAGENT_CONFIG_FILE = "subagent.json";
6
+ export declare const MAX_TASKS = 8;
7
+ export declare const MAX_CONCURRENCY = 3;
8
+ export declare const BUILTIN_TOOL_NAMES: readonly ["read", "bash", "edit", "write", "grep", "find", "ls"];
9
+ export declare const DEFAULT_AGENT_TOOLS: readonly ["read", "bash", "edit", "write"];
10
+ export declare const EXPLORER_TOOLS: readonly ["read", "grep", "find", "ls"];
11
+ export declare const THINKING_LEVELS: readonly ["off", "minimal", "low", "medium", "high", "xhigh", "max"];
12
+ export declare const LIVE_TEXT_LIMIT: number;
13
+ export declare const ACTIVITY_LIMIT = 80;
14
+ export declare const ACTIVITY_TEXT_LIMIT: number;
15
+ export declare const SINGLE_OUTPUT_LIMIT: number;
16
+ export declare const PARALLEL_TASK_OUTPUT_LIMIT: number;
17
+ export declare const PARALLEL_OUTPUT_LIMIT: number;
18
+ export declare const CHAIN_HANDOFF_LIMIT: number;
19
+ export declare const ERROR_TEXT_LIMIT: number;
20
+ export declare const DETAILS_OUTPUT_LIMIT: number;
21
+ export declare const DETAILS_ACTIVITY_LIMIT = 4;
22
+ //# sourceMappingURL=constants.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../../src/extensions/subagent/constants.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,kBAAkB,aAAa,CAAC;AAC7C,eAAO,MAAM,mBAAmB,aAAa,CAAC;AAC9C,eAAO,MAAM,qBAAqB,WAAW,CAAC;AAC9C,eAAO,MAAM,uBAAuB,IAAI,CAAC;AACzC,eAAO,MAAM,oBAAoB,kBAAkB,CAAC;AAEpD,eAAO,MAAM,SAAS,IAAI,CAAC;AAC3B,eAAO,MAAM,eAAe,IAAI,CAAC;AAEjC,eAAO,MAAM,kBAAkB,kEAAmE,CAAC;AAEnG,eAAO,MAAM,mBAAmB,4CAA6C,CAAC;AAE9E,eAAO,MAAM,cAAc,yCAA0C,CAAC;AAEtE,eAAO,MAAM,eAAe,sEAQiB,CAAC;AAE9C,eAAO,MAAM,eAAe,QAAW,CAAC;AACxC,eAAO,MAAM,cAAc,KAAK,CAAC;AACjC,eAAO,MAAM,mBAAmB,QAAY,CAAC;AAC7C,eAAO,MAAM,mBAAmB,QAAY,CAAC;AAC7C,eAAO,MAAM,0BAA0B,QAAY,CAAC;AACpD,eAAO,MAAM,qBAAqB,QAAY,CAAC;AAC/C,eAAO,MAAM,mBAAmB,QAAY,CAAC;AAC7C,eAAO,MAAM,gBAAgB,QAAW,CAAC;AACzC,eAAO,MAAM,oBAAoB,QAAa,CAAC;AAC/C,eAAO,MAAM,sBAAsB,IAAI,CAAC","sourcesContent":["import type { ThinkingLevel } from \"@earendil-works/pi-agent-core\";\n\nexport const SUBAGENT_TOOL_NAME = \"subagent\";\nexport const SUBAGENT_TOOL_LABEL = \"Subagent\";\nexport const SUBAGENT_COMMAND_NAME = \"agents\";\nexport const SUBAGENT_CONFIG_VERSION = 1;\nexport const SUBAGENT_CONFIG_FILE = \"subagent.json\";\n\nexport const MAX_TASKS = 8;\nexport const MAX_CONCURRENCY = 3;\n\nexport const BUILTIN_TOOL_NAMES = [\"read\", \"bash\", \"edit\", \"write\", \"grep\", \"find\", \"ls\"] as const;\n// Matches the parent session's default toolset; bash covers search needs.\nexport const DEFAULT_AGENT_TOOLS = [\"read\", \"bash\", \"edit\", \"write\"] as const;\n// Read-only agents get the standalone search tools instead of bash.\nexport const EXPLORER_TOOLS = [\"read\", \"grep\", \"find\", \"ls\"] as const;\n\nexport const THINKING_LEVELS = [\n\t\"off\",\n\t\"minimal\",\n\t\"low\",\n\t\"medium\",\n\t\"high\",\n\t\"xhigh\",\n\t\"max\",\n] as const satisfies readonly ThinkingLevel[];\n\nexport const LIVE_TEXT_LIMIT = 8 * 1024;\nexport const ACTIVITY_LIMIT = 80;\nexport const ACTIVITY_TEXT_LIMIT = 24 * 1024;\nexport const SINGLE_OUTPUT_LIMIT = 32 * 1024;\nexport const PARALLEL_TASK_OUTPUT_LIMIT = 12 * 1024;\nexport const PARALLEL_OUTPUT_LIMIT = 48 * 1024;\nexport const CHAIN_HANDOFF_LIMIT = 16 * 1024;\nexport const ERROR_TEXT_LIMIT = 8 * 1024;\nexport const DETAILS_OUTPUT_LIMIT = 120 * 1024;\nexport const DETAILS_ACTIVITY_LIMIT = 4;\n"]}
@@ -0,0 +1,32 @@
1
+ export const SUBAGENT_TOOL_NAME = "subagent";
2
+ export const SUBAGENT_TOOL_LABEL = "Subagent";
3
+ export const SUBAGENT_COMMAND_NAME = "agents";
4
+ export const SUBAGENT_CONFIG_VERSION = 1;
5
+ export const SUBAGENT_CONFIG_FILE = "subagent.json";
6
+ export const MAX_TASKS = 8;
7
+ export const MAX_CONCURRENCY = 3;
8
+ export const BUILTIN_TOOL_NAMES = ["read", "bash", "edit", "write", "grep", "find", "ls"];
9
+ // Matches the parent session's default toolset; bash covers search needs.
10
+ export const DEFAULT_AGENT_TOOLS = ["read", "bash", "edit", "write"];
11
+ // Read-only agents get the standalone search tools instead of bash.
12
+ export const EXPLORER_TOOLS = ["read", "grep", "find", "ls"];
13
+ export const THINKING_LEVELS = [
14
+ "off",
15
+ "minimal",
16
+ "low",
17
+ "medium",
18
+ "high",
19
+ "xhigh",
20
+ "max",
21
+ ];
22
+ export const LIVE_TEXT_LIMIT = 8 * 1024;
23
+ export const ACTIVITY_LIMIT = 80;
24
+ export const ACTIVITY_TEXT_LIMIT = 24 * 1024;
25
+ export const SINGLE_OUTPUT_LIMIT = 32 * 1024;
26
+ export const PARALLEL_TASK_OUTPUT_LIMIT = 12 * 1024;
27
+ export const PARALLEL_OUTPUT_LIMIT = 48 * 1024;
28
+ export const CHAIN_HANDOFF_LIMIT = 16 * 1024;
29
+ export const ERROR_TEXT_LIMIT = 8 * 1024;
30
+ export const DETAILS_OUTPUT_LIMIT = 120 * 1024;
31
+ export const DETAILS_ACTIVITY_LIMIT = 4;
32
+ //# sourceMappingURL=constants.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.js","sourceRoot":"","sources":["../../../src/extensions/subagent/constants.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,kBAAkB,GAAG,UAAU,CAAC;AAC7C,MAAM,CAAC,MAAM,mBAAmB,GAAG,UAAU,CAAC;AAC9C,MAAM,CAAC,MAAM,qBAAqB,GAAG,QAAQ,CAAC;AAC9C,MAAM,CAAC,MAAM,uBAAuB,GAAG,CAAC,CAAC;AACzC,MAAM,CAAC,MAAM,oBAAoB,GAAG,eAAe,CAAC;AAEpD,MAAM,CAAC,MAAM,SAAS,GAAG,CAAC,CAAC;AAC3B,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,CAAC;AAEjC,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,CAAU,CAAC;AACnG,0EAA0E;AAC1E,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,CAAU,CAAC;AAC9E,oEAAoE;AACpE,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,CAAU,CAAC;AAEtE,MAAM,CAAC,MAAM,eAAe,GAAG;IAC9B,KAAK;IACL,SAAS;IACT,KAAK;IACL,QAAQ;IACR,MAAM;IACN,OAAO;IACP,KAAK;CACuC,CAAC;AAE9C,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,GAAG,IAAI,CAAC;AACxC,MAAM,CAAC,MAAM,cAAc,GAAG,EAAE,CAAC;AACjC,MAAM,CAAC,MAAM,mBAAmB,GAAG,EAAE,GAAG,IAAI,CAAC;AAC7C,MAAM,CAAC,MAAM,mBAAmB,GAAG,EAAE,GAAG,IAAI,CAAC;AAC7C,MAAM,CAAC,MAAM,0BAA0B,GAAG,EAAE,GAAG,IAAI,CAAC;AACpD,MAAM,CAAC,MAAM,qBAAqB,GAAG,EAAE,GAAG,IAAI,CAAC;AAC/C,MAAM,CAAC,MAAM,mBAAmB,GAAG,EAAE,GAAG,IAAI,CAAC;AAC7C,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,GAAG,IAAI,CAAC;AACzC,MAAM,CAAC,MAAM,oBAAoB,GAAG,GAAG,GAAG,IAAI,CAAC;AAC/C,MAAM,CAAC,MAAM,sBAAsB,GAAG,CAAC,CAAC","sourcesContent":["import type { ThinkingLevel } from \"@earendil-works/pi-agent-core\";\n\nexport const SUBAGENT_TOOL_NAME = \"subagent\";\nexport const SUBAGENT_TOOL_LABEL = \"Subagent\";\nexport const SUBAGENT_COMMAND_NAME = \"agents\";\nexport const SUBAGENT_CONFIG_VERSION = 1;\nexport const SUBAGENT_CONFIG_FILE = \"subagent.json\";\n\nexport const MAX_TASKS = 8;\nexport const MAX_CONCURRENCY = 3;\n\nexport const BUILTIN_TOOL_NAMES = [\"read\", \"bash\", \"edit\", \"write\", \"grep\", \"find\", \"ls\"] as const;\n// Matches the parent session's default toolset; bash covers search needs.\nexport const DEFAULT_AGENT_TOOLS = [\"read\", \"bash\", \"edit\", \"write\"] as const;\n// Read-only agents get the standalone search tools instead of bash.\nexport const EXPLORER_TOOLS = [\"read\", \"grep\", \"find\", \"ls\"] as const;\n\nexport const THINKING_LEVELS = [\n\t\"off\",\n\t\"minimal\",\n\t\"low\",\n\t\"medium\",\n\t\"high\",\n\t\"xhigh\",\n\t\"max\",\n] as const satisfies readonly ThinkingLevel[];\n\nexport const LIVE_TEXT_LIMIT = 8 * 1024;\nexport const ACTIVITY_LIMIT = 80;\nexport const ACTIVITY_TEXT_LIMIT = 24 * 1024;\nexport const SINGLE_OUTPUT_LIMIT = 32 * 1024;\nexport const PARALLEL_TASK_OUTPUT_LIMIT = 12 * 1024;\nexport const PARALLEL_OUTPUT_LIMIT = 48 * 1024;\nexport const CHAIN_HANDOFF_LIMIT = 16 * 1024;\nexport const ERROR_TEXT_LIMIT = 8 * 1024;\nexport const DETAILS_OUTPUT_LIMIT = 120 * 1024;\nexport const DETAILS_ACTIVITY_LIMIT = 4;\n"]}
@@ -0,0 +1,3 @@
1
+ import type { ExtensionAPI } from "../../core/extensions/types.ts";
2
+ export default function subagent(pi: ExtensionAPI): void;
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/extensions/subagent/index.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAEX,YAAY,EAGZ,MAAM,gCAAgC,CAAC;AA8LxC,MAAM,CAAC,OAAO,UAAU,QAAQ,CAAC,EAAE,EAAE,YAAY,GAAG,IAAI,CAgGvD","sourcesContent":["import { join } from \"node:path\";\nimport type { ThinkingLevel } from \"@earendil-works/pi-agent-core\";\nimport type { Api, Model } from \"@earendil-works/pi-ai\";\nimport { getSupportedThinkingLevels } from \"@earendil-works/pi-ai/compat\";\nimport { getAgentDir } from \"../../config.ts\";\nimport type {\n\tAgentToolResult,\n\tExtensionAPI,\n\tExtensionCommandContext,\n\tExtensionContext,\n} from \"../../core/extensions/types.ts\";\nimport type { ModelRegistry } from \"../../core/model-registry.ts\";\nimport { ModelRuntime } from \"../../core/model-runtime.ts\";\nimport { discoverAgents, subagentToolDescription } from \"./agents.ts\";\nimport { SUBAGENT_COMMAND_NAME, SUBAGENT_TOOL_LABEL, SUBAGENT_TOOL_NAME, THINKING_LEVELS } from \"./constants.ts\";\nimport { type PickerItem, SearchPickerComponent } from \"./picker.ts\";\nimport { renderSubagentCall, renderSubagentResult } from \"./render.ts\";\nimport type { ParentModelContext } from \"./resolve.ts\";\nimport { ConcurrencyGate, runSubagentInvocation, statusSummary } from \"./runner.ts\";\nimport { SubagentParamsSchema } from \"./schema.ts\";\nimport { loadSubagentConfig, resetProfileOverrides, updateProfileOverride } from \"./settings.ts\";\nimport type { AgentDefinition, AgentDiscoveryResult, SubagentDetails, SubagentProfileOverride } from \"./types.ts\";\n\nfunction formatParentModel(model: { provider: string; id: string } | undefined): string {\n\treturn model ? `${model.provider}/${model.id}` : \"none\";\n}\n\n// Settings resolve in two layers: a saved override wins, otherwise the\n// profile inherits the parent session. Inherited rows show the value\n// currently in effect so the menu doubles as a status view.\nfunction effectiveSettings(\n\toverride: SubagentProfileOverride | undefined,\n\tctx: ExtensionCommandContext,\n\tparentThinking: string,\n): { model: string; thinking: string } {\n\treturn {\n\t\tmodel: override?.model ?? `inherit (${formatParentModel(ctx.model)})`,\n\t\tthinking: override?.thinking ?? `inherit (${parentThinking})`,\n\t};\n}\n\n// The thinking picker lists what the effective model actually supports;\n// if the saved override names a model that is unavailable right now,\n// every level shows and the runtime clamp decides.\nfunction effectiveModelFor(\n\tctx: ExtensionCommandContext,\n\toverride: SubagentProfileOverride | undefined,\n): Model<Api> | undefined {\n\tif (override?.model) {\n\t\tconst separator = override.model.indexOf(\"/\");\n\t\tif (separator <= 0) return undefined;\n\t\treturn ctx.modelRegistry.find(override.model.slice(0, separator), override.model.slice(separator + 1));\n\t}\n\treturn ctx.model;\n}\n\nasync function pickModel(\n\tctx: ExtensionCommandContext,\n\tprofileName: string,\n\toverride: SubagentProfileOverride | undefined,\n): Promise<void> {\n\tconst items: PickerItem<string | null>[] = [\n\t\t{\n\t\t\tvalue: null,\n\t\t\tlabel: \"inherit\",\n\t\t\tdetail: `Follow the parent session (${formatParentModel(ctx.model)})`,\n\t\t\tcurrent: !override?.model,\n\t\t},\n\t\t...ctx.modelRegistry.getAvailable().map((model) => ({\n\t\t\tvalue: `${model.provider}/${model.id}`,\n\t\t\tlabel: `${model.provider}/${model.id}`,\n\t\t\tdetail: model.name && model.name !== model.id ? model.name : undefined,\n\t\t\tcurrent: override?.model === `${model.provider}/${model.id}`,\n\t\t})),\n\t];\n\tconst selected = await ctx.ui.custom<string | null | undefined>(\n\t\t(_tui, theme, _keybindings, done) => new SearchPickerComponent(theme, `Model for ${profileName}`, items, done),\n\t);\n\tif (selected === undefined) return;\n\tawait updateProfileOverride(profileName, { model: selected ?? undefined }, getAgentDir());\n}\n\nasync function pickThinking(\n\tctx: ExtensionCommandContext,\n\tprofileName: string,\n\toverride: SubagentProfileOverride | undefined,\n\tparentThinking: string,\n): Promise<void> {\n\tconst model = effectiveModelFor(ctx, override);\n\tconst levels = model ? (getSupportedThinkingLevels(model) as ThinkingLevel[]) : [...THINKING_LEVELS];\n\tconst items: PickerItem<ThinkingLevel | null>[] = [\n\t\t{\n\t\t\tvalue: null,\n\t\t\tlabel: \"inherit\",\n\t\t\tdetail: `Follow the parent session (${parentThinking})`,\n\t\t\tcurrent: !override?.thinking,\n\t\t},\n\t\t...levels.map((level) => ({ value: level, label: level, current: override?.thinking === level })),\n\t];\n\tconst selected = await ctx.ui.custom<ThinkingLevel | null | undefined>(\n\t\t(_tui, theme, _keybindings, done) => new SearchPickerComponent(theme, `Thinking for ${profileName}`, items, done),\n\t);\n\tif (selected === undefined) return;\n\tawait updateProfileOverride(profileName, { thinking: selected ?? undefined }, getAgentDir());\n}\n\nasync function configureProfile(\n\tctx: ExtensionCommandContext,\n\tagent: AgentDefinition,\n\tparentThinking: string,\n): Promise<void> {\n\tconst agentDir = getAgentDir();\n\twhile (true) {\n\t\tconst config = await loadSubagentConfig(agentDir);\n\t\tconst override = config.profiles[agent.name];\n\t\tconst effective = effectiveSettings(override, ctx, parentThinking);\n\t\tconst action = await ctx.ui.select(`${agent.name} — ${agent.description}`, [\n\t\t\t`Model: ${effective.model}`,\n\t\t\t`Thinking: ${effective.thinking}`,\n\t\t\t\"Back\",\n\t\t]);\n\t\tif (!action || action === \"Back\") return;\n\t\tif (action.startsWith(\"Model:\")) {\n\t\t\tawait pickModel(ctx, agent.name, override);\n\t\t\tcontinue;\n\t\t}\n\t\tawait pickThinking(ctx, agent.name, override, parentThinking);\n\t}\n}\n\nasync function syncParentProviders(\n\truntime: ModelRuntime,\n\tregistry: ModelRegistry,\n\tsyncedIds: Set<string>,\n): Promise<void> {\n\tconst nextIds = new Set(registry.getRegisteredProviderIds());\n\tfor (const id of syncedIds) {\n\t\tif (!nextIds.has(id)) runtime.unregisterProvider(id);\n\t}\n\tfor (const id of nextIds) {\n\t\tconst native = registry.getRegisteredNativeProvider(id);\n\t\tif (native) runtime.registerNativeProvider(native);\n\t\telse {\n\t\t\tconst config = registry.getRegisteredProviderConfig(id);\n\t\t\tif (config) runtime.registerProvider(id, config);\n\t\t}\n\t\tconst auth = await registry.getProviderAuth(id);\n\t\tif (auth?.auth.apiKey) await runtime.setRuntimeApiKey(id, auth.auth.apiKey, { allowNetwork: false });\n\t}\n\tsyncedIds.clear();\n\tfor (const id of nextIds) syncedIds.add(id);\n}\n\nasync function showAgentsCommand(ctx: ExtensionCommandContext, parentThinking: string): Promise<void> {\n\tif (!ctx.hasUI) {\n\t\tctx.ui.notify(\"/agents requires an interactive UI.\", \"warning\");\n\t\treturn;\n\t}\n\tconst agentDir = getAgentDir();\n\tconst reset = \"Reset all overrides\";\n\tconst done = \"Done\";\n\twhile (true) {\n\t\tconst discovery = discoverAgents(ctx.cwd, { projectTrusted: ctx.isProjectTrusted(), agentDir });\n\t\tconst config = await loadSubagentConfig(agentDir);\n\t\tconst rows = discovery.agents.map((agent) => {\n\t\t\tconst effective = effectiveSettings(config.profiles[agent.name], ctx, parentThinking);\n\t\t\treturn `${agent.name} — model: ${effective.model} · thinking: ${effective.thinking}`;\n\t\t});\n\t\tconst issueCount = discovery.diagnostics.length;\n\t\tconst issues = issueCount ? [`Show ${issueCount} agent file issue${issueCount === 1 ? \"\" : \"s\"}`] : [];\n\t\tconst action = await ctx.ui.select(\"Subagent profiles — select one to configure\", [\n\t\t\t...rows,\n\t\t\t...issues,\n\t\t\treset,\n\t\t\tdone,\n\t\t]);\n\t\tif (!action || action === done) return;\n\t\tif (action === reset) {\n\t\t\tconst confirmed = await ctx.ui.confirm(\n\t\t\t\t\"Reset profile overrides?\",\n\t\t\t\t\"Clear every saved Subagent model and thinking override?\",\n\t\t\t);\n\t\t\tif (confirmed) {\n\t\t\t\tawait resetProfileOverrides(agentDir);\n\t\t\t\tctx.ui.notify(\"All profiles now inherit the parent session.\", \"info\");\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\t\tif (issues.length > 0 && action === issues[0]) {\n\t\t\tctx.ui.notify(\n\t\t\t\tdiscovery.diagnostics.map((diagnostic) => `${diagnostic.path}: ${diagnostic.message}`).join(\"\\n\"),\n\t\t\t\t\"warning\",\n\t\t\t);\n\t\t\tcontinue;\n\t\t}\n\t\tconst agent = discovery.agents.find((candidate) => action.startsWith(`${candidate.name} — `));\n\t\tif (agent) await configureProfile(ctx, agent, parentThinking);\n\t}\n}\n\nexport default function subagent(pi: ExtensionAPI): void {\n\tconst gate = new ConcurrencyGate();\n\tconst activeAborters = new Set<() => Promise<void>>();\n\tconst syncedProviderIds = new Set<string>();\n\tlet modelRuntimePromise: Promise<ModelRuntime> | undefined;\n\n\tconst getModelRuntime = async (ctx: ExtensionContext): Promise<ModelRuntime> => {\n\t\tif (!modelRuntimePromise) {\n\t\t\tconst agentDir = getAgentDir();\n\t\t\tmodelRuntimePromise = ModelRuntime.create({\n\t\t\t\tauthPath: join(agentDir, \"auth.json\"),\n\t\t\t\tmodelsPath: join(agentDir, \"models.json\"),\n\t\t\t});\n\t\t}\n\t\tconst runtime = await modelRuntimePromise;\n\t\tawait syncParentProviders(runtime, ctx.modelRegistry, syncedProviderIds);\n\t\treturn runtime;\n\t};\n\n\tconst registerSubagentTool = (discovery: AgentDiscoveryResult): void => {\n\t\tpi.registerTool<typeof SubagentParamsSchema, SubagentDetails>({\n\t\t\tname: SUBAGENT_TOOL_NAME,\n\t\t\tlabel: SUBAGENT_TOOL_LABEL,\n\t\t\tdescription: subagentToolDescription(discovery),\n\t\t\tpromptSnippet: \"Delegate focused research, review, or implementation tasks to isolated subagents\",\n\t\t\tpromptGuidelines: [\n\t\t\t\t\"Use subagent when a bounded task benefits from isolated context, parallel investigation, or a sequential specialist handoff; choose a profile and write its briefing using the tool description.\",\n\t\t\t],\n\t\t\tparameters: SubagentParamsSchema,\n\t\t\tasync execute(_toolCallId, params, signal, onUpdate, ctx): Promise<AgentToolResult<SubagentDetails>> {\n\t\t\t\tconst discovery = discoverAgents(ctx.cwd, {\n\t\t\t\t\tprojectTrusted: ctx.isProjectTrusted(),\n\t\t\t\t\tagentDir: getAgentDir(),\n\t\t\t\t});\n\t\t\t\tconst runtime = await getModelRuntime(ctx);\n\t\t\t\tconst parent: ParentModelContext = {\n\t\t\t\t\tmodel: ctx.model,\n\t\t\t\t\tthinking: pi.getThinkingLevel(),\n\t\t\t\t\tmodelRegistry: ctx.modelRegistry,\n\t\t\t\t};\n\t\t\t\tconst execution = await runSubagentInvocation({\n\t\t\t\t\tparams,\n\t\t\t\t\tparentCwd: ctx.cwd,\n\t\t\t\t\tagents: discovery.agents,\n\t\t\t\t\tparent,\n\t\t\t\t\tmodelRuntime: runtime,\n\t\t\t\t\tagentDir: getAgentDir(),\n\t\t\t\t\tconfigAgentDir: getAgentDir(),\n\t\t\t\t\tprojectTrusted: ctx.isProjectTrusted(),\n\t\t\t\t\tsignal,\n\t\t\t\t\tgate,\n\t\t\t\t\tonUpdate: (details) => {\n\t\t\t\t\t\tonUpdate?.({ content: [{ type: \"text\", text: statusSummary(details) }], details });\n\t\t\t\t\t},\n\t\t\t\t\tregisterAbort: (abort) => {\n\t\t\t\t\t\tactiveAborters.add(abort);\n\t\t\t\t\t\treturn () => activeAborters.delete(abort);\n\t\t\t\t\t},\n\t\t\t\t});\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [{ type: \"text\", text: execution.content }],\n\t\t\t\t\tdetails: execution.details,\n\t\t\t\t\tusage: execution.usage,\n\t\t\t\t};\n\t\t\t},\n\t\t\trenderCall(args, theme) {\n\t\t\t\treturn renderSubagentCall(args, theme);\n\t\t\t},\n\t\t\trenderResult(result, options, theme, context) {\n\t\t\t\treturn renderSubagentResult(result, options, theme, context.isError);\n\t\t\t},\n\t\t});\n\t};\n\n\tregisterSubagentTool(discoverAgents(process.cwd(), { projectTrusted: false, agentDir: getAgentDir() }));\n\tpi.on(\"session_start\", (_event, ctx) => {\n\t\tregisterSubagentTool(\n\t\t\tdiscoverAgents(ctx.cwd, { projectTrusted: ctx.isProjectTrusted(), agentDir: getAgentDir() }),\n\t\t);\n\t});\n\n\tpi.registerCommand(SUBAGENT_COMMAND_NAME, {\n\t\tdescription: \"Configure Subagent profiles, models, and thinking levels\",\n\t\thandler: async (_args, ctx) => showAgentsCommand(ctx, pi.getThinkingLevel()),\n\t});\n\n\tpi.on(\"tool_result\", async (event) => {\n\t\tif (event.toolName !== SUBAGENT_TOOL_NAME) return;\n\t\tconst details = event.details as SubagentDetails | undefined;\n\t\tif (details?.status === \"failed\" || details?.status === \"aborted\") return { isError: true };\n\t});\n\n\tpi.on(\"session_shutdown\", async () => {\n\t\tawait Promise.allSettled([...activeAborters].map((abort) => abort()));\n\t\tactiveAborters.clear();\n\t});\n}\n"]}
@@ -0,0 +1,249 @@
1
+ import { join } from "node:path";
2
+ import { getSupportedThinkingLevels } from "@earendil-works/pi-ai/compat";
3
+ import { getAgentDir } from "../../config.js";
4
+ import { ModelRuntime } from "../../core/model-runtime.js";
5
+ import { discoverAgents, subagentToolDescription } from "./agents.js";
6
+ import { SUBAGENT_COMMAND_NAME, SUBAGENT_TOOL_LABEL, SUBAGENT_TOOL_NAME, THINKING_LEVELS } from "./constants.js";
7
+ import { SearchPickerComponent } from "./picker.js";
8
+ import { renderSubagentCall, renderSubagentResult } from "./render.js";
9
+ import { ConcurrencyGate, runSubagentInvocation, statusSummary } from "./runner.js";
10
+ import { SubagentParamsSchema } from "./schema.js";
11
+ import { loadSubagentConfig, resetProfileOverrides, updateProfileOverride } from "./settings.js";
12
+ function formatParentModel(model) {
13
+ return model ? `${model.provider}/${model.id}` : "none";
14
+ }
15
+ // Settings resolve in two layers: a saved override wins, otherwise the
16
+ // profile inherits the parent session. Inherited rows show the value
17
+ // currently in effect so the menu doubles as a status view.
18
+ function effectiveSettings(override, ctx, parentThinking) {
19
+ return {
20
+ model: override?.model ?? `inherit (${formatParentModel(ctx.model)})`,
21
+ thinking: override?.thinking ?? `inherit (${parentThinking})`,
22
+ };
23
+ }
24
+ // The thinking picker lists what the effective model actually supports;
25
+ // if the saved override names a model that is unavailable right now,
26
+ // every level shows and the runtime clamp decides.
27
+ function effectiveModelFor(ctx, override) {
28
+ if (override?.model) {
29
+ const separator = override.model.indexOf("/");
30
+ if (separator <= 0)
31
+ return undefined;
32
+ return ctx.modelRegistry.find(override.model.slice(0, separator), override.model.slice(separator + 1));
33
+ }
34
+ return ctx.model;
35
+ }
36
+ async function pickModel(ctx, profileName, override) {
37
+ const items = [
38
+ {
39
+ value: null,
40
+ label: "inherit",
41
+ detail: `Follow the parent session (${formatParentModel(ctx.model)})`,
42
+ current: !override?.model,
43
+ },
44
+ ...ctx.modelRegistry.getAvailable().map((model) => ({
45
+ value: `${model.provider}/${model.id}`,
46
+ label: `${model.provider}/${model.id}`,
47
+ detail: model.name && model.name !== model.id ? model.name : undefined,
48
+ current: override?.model === `${model.provider}/${model.id}`,
49
+ })),
50
+ ];
51
+ const selected = await ctx.ui.custom((_tui, theme, _keybindings, done) => new SearchPickerComponent(theme, `Model for ${profileName}`, items, done));
52
+ if (selected === undefined)
53
+ return;
54
+ await updateProfileOverride(profileName, { model: selected ?? undefined }, getAgentDir());
55
+ }
56
+ async function pickThinking(ctx, profileName, override, parentThinking) {
57
+ const model = effectiveModelFor(ctx, override);
58
+ const levels = model ? getSupportedThinkingLevels(model) : [...THINKING_LEVELS];
59
+ const items = [
60
+ {
61
+ value: null,
62
+ label: "inherit",
63
+ detail: `Follow the parent session (${parentThinking})`,
64
+ current: !override?.thinking,
65
+ },
66
+ ...levels.map((level) => ({ value: level, label: level, current: override?.thinking === level })),
67
+ ];
68
+ const selected = await ctx.ui.custom((_tui, theme, _keybindings, done) => new SearchPickerComponent(theme, `Thinking for ${profileName}`, items, done));
69
+ if (selected === undefined)
70
+ return;
71
+ await updateProfileOverride(profileName, { thinking: selected ?? undefined }, getAgentDir());
72
+ }
73
+ async function configureProfile(ctx, agent, parentThinking) {
74
+ const agentDir = getAgentDir();
75
+ while (true) {
76
+ const config = await loadSubagentConfig(agentDir);
77
+ const override = config.profiles[agent.name];
78
+ const effective = effectiveSettings(override, ctx, parentThinking);
79
+ const action = await ctx.ui.select(`${agent.name} — ${agent.description}`, [
80
+ `Model: ${effective.model}`,
81
+ `Thinking: ${effective.thinking}`,
82
+ "Back",
83
+ ]);
84
+ if (!action || action === "Back")
85
+ return;
86
+ if (action.startsWith("Model:")) {
87
+ await pickModel(ctx, agent.name, override);
88
+ continue;
89
+ }
90
+ await pickThinking(ctx, agent.name, override, parentThinking);
91
+ }
92
+ }
93
+ async function syncParentProviders(runtime, registry, syncedIds) {
94
+ const nextIds = new Set(registry.getRegisteredProviderIds());
95
+ for (const id of syncedIds) {
96
+ if (!nextIds.has(id))
97
+ runtime.unregisterProvider(id);
98
+ }
99
+ for (const id of nextIds) {
100
+ const native = registry.getRegisteredNativeProvider(id);
101
+ if (native)
102
+ runtime.registerNativeProvider(native);
103
+ else {
104
+ const config = registry.getRegisteredProviderConfig(id);
105
+ if (config)
106
+ runtime.registerProvider(id, config);
107
+ }
108
+ const auth = await registry.getProviderAuth(id);
109
+ if (auth?.auth.apiKey)
110
+ await runtime.setRuntimeApiKey(id, auth.auth.apiKey, { allowNetwork: false });
111
+ }
112
+ syncedIds.clear();
113
+ for (const id of nextIds)
114
+ syncedIds.add(id);
115
+ }
116
+ async function showAgentsCommand(ctx, parentThinking) {
117
+ if (!ctx.hasUI) {
118
+ ctx.ui.notify("/agents requires an interactive UI.", "warning");
119
+ return;
120
+ }
121
+ const agentDir = getAgentDir();
122
+ const reset = "Reset all overrides";
123
+ const done = "Done";
124
+ while (true) {
125
+ const discovery = discoverAgents(ctx.cwd, { projectTrusted: ctx.isProjectTrusted(), agentDir });
126
+ const config = await loadSubagentConfig(agentDir);
127
+ const rows = discovery.agents.map((agent) => {
128
+ const effective = effectiveSettings(config.profiles[agent.name], ctx, parentThinking);
129
+ return `${agent.name} — model: ${effective.model} · thinking: ${effective.thinking}`;
130
+ });
131
+ const issueCount = discovery.diagnostics.length;
132
+ const issues = issueCount ? [`Show ${issueCount} agent file issue${issueCount === 1 ? "" : "s"}`] : [];
133
+ const action = await ctx.ui.select("Subagent profiles — select one to configure", [
134
+ ...rows,
135
+ ...issues,
136
+ reset,
137
+ done,
138
+ ]);
139
+ if (!action || action === done)
140
+ return;
141
+ if (action === reset) {
142
+ const confirmed = await ctx.ui.confirm("Reset profile overrides?", "Clear every saved Subagent model and thinking override?");
143
+ if (confirmed) {
144
+ await resetProfileOverrides(agentDir);
145
+ ctx.ui.notify("All profiles now inherit the parent session.", "info");
146
+ }
147
+ continue;
148
+ }
149
+ if (issues.length > 0 && action === issues[0]) {
150
+ ctx.ui.notify(discovery.diagnostics.map((diagnostic) => `${diagnostic.path}: ${diagnostic.message}`).join("\n"), "warning");
151
+ continue;
152
+ }
153
+ const agent = discovery.agents.find((candidate) => action.startsWith(`${candidate.name} — `));
154
+ if (agent)
155
+ await configureProfile(ctx, agent, parentThinking);
156
+ }
157
+ }
158
+ export default function subagent(pi) {
159
+ const gate = new ConcurrencyGate();
160
+ const activeAborters = new Set();
161
+ const syncedProviderIds = new Set();
162
+ let modelRuntimePromise;
163
+ const getModelRuntime = async (ctx) => {
164
+ if (!modelRuntimePromise) {
165
+ const agentDir = getAgentDir();
166
+ modelRuntimePromise = ModelRuntime.create({
167
+ authPath: join(agentDir, "auth.json"),
168
+ modelsPath: join(agentDir, "models.json"),
169
+ });
170
+ }
171
+ const runtime = await modelRuntimePromise;
172
+ await syncParentProviders(runtime, ctx.modelRegistry, syncedProviderIds);
173
+ return runtime;
174
+ };
175
+ const registerSubagentTool = (discovery) => {
176
+ pi.registerTool({
177
+ name: SUBAGENT_TOOL_NAME,
178
+ label: SUBAGENT_TOOL_LABEL,
179
+ description: subagentToolDescription(discovery),
180
+ promptSnippet: "Delegate focused research, review, or implementation tasks to isolated subagents",
181
+ promptGuidelines: [
182
+ "Use subagent when a bounded task benefits from isolated context, parallel investigation, or a sequential specialist handoff; choose a profile and write its briefing using the tool description.",
183
+ ],
184
+ parameters: SubagentParamsSchema,
185
+ async execute(_toolCallId, params, signal, onUpdate, ctx) {
186
+ const discovery = discoverAgents(ctx.cwd, {
187
+ projectTrusted: ctx.isProjectTrusted(),
188
+ agentDir: getAgentDir(),
189
+ });
190
+ const runtime = await getModelRuntime(ctx);
191
+ const parent = {
192
+ model: ctx.model,
193
+ thinking: pi.getThinkingLevel(),
194
+ modelRegistry: ctx.modelRegistry,
195
+ };
196
+ const execution = await runSubagentInvocation({
197
+ params,
198
+ parentCwd: ctx.cwd,
199
+ agents: discovery.agents,
200
+ parent,
201
+ modelRuntime: runtime,
202
+ agentDir: getAgentDir(),
203
+ configAgentDir: getAgentDir(),
204
+ projectTrusted: ctx.isProjectTrusted(),
205
+ signal,
206
+ gate,
207
+ onUpdate: (details) => {
208
+ onUpdate?.({ content: [{ type: "text", text: statusSummary(details) }], details });
209
+ },
210
+ registerAbort: (abort) => {
211
+ activeAborters.add(abort);
212
+ return () => activeAborters.delete(abort);
213
+ },
214
+ });
215
+ return {
216
+ content: [{ type: "text", text: execution.content }],
217
+ details: execution.details,
218
+ usage: execution.usage,
219
+ };
220
+ },
221
+ renderCall(args, theme) {
222
+ return renderSubagentCall(args, theme);
223
+ },
224
+ renderResult(result, options, theme, context) {
225
+ return renderSubagentResult(result, options, theme, context.isError);
226
+ },
227
+ });
228
+ };
229
+ registerSubagentTool(discoverAgents(process.cwd(), { projectTrusted: false, agentDir: getAgentDir() }));
230
+ pi.on("session_start", (_event, ctx) => {
231
+ registerSubagentTool(discoverAgents(ctx.cwd, { projectTrusted: ctx.isProjectTrusted(), agentDir: getAgentDir() }));
232
+ });
233
+ pi.registerCommand(SUBAGENT_COMMAND_NAME, {
234
+ description: "Configure Subagent profiles, models, and thinking levels",
235
+ handler: async (_args, ctx) => showAgentsCommand(ctx, pi.getThinkingLevel()),
236
+ });
237
+ pi.on("tool_result", async (event) => {
238
+ if (event.toolName !== SUBAGENT_TOOL_NAME)
239
+ return;
240
+ const details = event.details;
241
+ if (details?.status === "failed" || details?.status === "aborted")
242
+ return { isError: true };
243
+ });
244
+ pi.on("session_shutdown", async () => {
245
+ await Promise.allSettled([...activeAborters].map((abort) => abort()));
246
+ activeAborters.clear();
247
+ });
248
+ }
249
+ //# sourceMappingURL=index.js.map