lsd-pi 1.3.2 → 1.3.7

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 (205) hide show
  1. package/dist/cli.js +2 -1
  2. package/dist/lsd-settings-manager.d.ts +2 -0
  3. package/dist/lsd-settings-manager.js +5 -0
  4. package/dist/resource-loader.js +33 -3
  5. package/dist/resources/extensions/browser-tools/tools/codegen.js +5 -5
  6. package/dist/resources/extensions/browser-tools/tools/navigation.js +107 -178
  7. package/dist/resources/extensions/browser-tools/tools/network-mock.js +112 -167
  8. package/dist/resources/extensions/browser-tools/tools/pages.js +182 -234
  9. package/dist/resources/extensions/browser-tools/tools/refs.js +202 -461
  10. package/dist/resources/extensions/browser-tools/tools/session.js +176 -323
  11. package/dist/resources/extensions/browser-tools/tools/state-persistence.js +91 -154
  12. package/dist/resources/extensions/browser-tools/utils.js +1 -1
  13. package/dist/resources/extensions/cache-timer/index.js +3 -2
  14. package/dist/resources/extensions/slash-commands/extension-manifest.json +2 -2
  15. package/dist/resources/extensions/slash-commands/fast.js +73 -0
  16. package/dist/resources/extensions/slash-commands/index.js +2 -0
  17. package/dist/resources/extensions/slash-commands/plan.js +37 -12
  18. package/dist/resources/extensions/subagent/background-job-manager.js +13 -0
  19. package/dist/resources/extensions/subagent/in-process-runner.js +387 -0
  20. package/dist/resources/extensions/subagent/index.js +278 -626
  21. package/dist/resources/extensions/subagent/legacy-runner.js +503 -0
  22. package/dist/resources/extensions/voice/index.js +96 -36
  23. package/dist/resources/extensions/voice/push-to-talk.js +26 -0
  24. package/dist/welcome-screen.js +2 -2
  25. package/package.json +1 -1
  26. package/packages/pi-agent-core/dist/agent.d.ts +19 -0
  27. package/packages/pi-agent-core/dist/agent.d.ts.map +1 -1
  28. package/packages/pi-agent-core/dist/agent.js +16 -0
  29. package/packages/pi-agent-core/dist/agent.js.map +1 -1
  30. package/packages/pi-agent-core/src/agent.ts +32 -2
  31. package/packages/pi-ai/dist/providers/openai-codex-responses.d.ts +34 -1
  32. package/packages/pi-ai/dist/providers/openai-codex-responses.d.ts.map +1 -1
  33. package/packages/pi-ai/dist/providers/openai-codex-responses.js +32 -4
  34. package/packages/pi-ai/dist/providers/openai-codex-responses.js.map +1 -1
  35. package/packages/pi-ai/dist/providers/openai-codex-responses.test.js +127 -16
  36. package/packages/pi-ai/dist/providers/openai-codex-responses.test.js.map +1 -1
  37. package/packages/pi-ai/dist/providers/openai-responses.d.ts +8 -1
  38. package/packages/pi-ai/dist/providers/openai-responses.d.ts.map +1 -1
  39. package/packages/pi-ai/dist/providers/openai-responses.fast-mode.test.d.ts +2 -0
  40. package/packages/pi-ai/dist/providers/openai-responses.fast-mode.test.d.ts.map +1 -0
  41. package/packages/pi-ai/dist/providers/openai-responses.fast-mode.test.js +67 -0
  42. package/packages/pi-ai/dist/providers/openai-responses.fast-mode.test.js.map +1 -0
  43. package/packages/pi-ai/dist/providers/openai-responses.js +21 -3
  44. package/packages/pi-ai/dist/providers/openai-responses.js.map +1 -1
  45. package/packages/pi-ai/dist/providers/simple-options.d.ts.map +1 -1
  46. package/packages/pi-ai/dist/providers/simple-options.js +2 -0
  47. package/packages/pi-ai/dist/providers/simple-options.js.map +1 -1
  48. package/packages/pi-ai/dist/types.d.ts +5 -0
  49. package/packages/pi-ai/dist/types.d.ts.map +1 -1
  50. package/packages/pi-ai/dist/types.js.map +1 -1
  51. package/packages/pi-ai/src/providers/openai-codex-responses.test.ts +143 -20
  52. package/packages/pi-ai/src/providers/openai-codex-responses.ts +47 -4
  53. package/packages/pi-ai/src/providers/openai-responses.fast-mode.test.ts +73 -0
  54. package/packages/pi-ai/src/providers/openai-responses.ts +26 -3
  55. package/packages/pi-ai/src/providers/simple-options.ts +2 -0
  56. package/packages/pi-ai/src/types.ts +5 -0
  57. package/packages/pi-coding-agent/dist/core/keybindings.d.ts +1 -1
  58. package/packages/pi-coding-agent/dist/core/keybindings.d.ts.map +1 -1
  59. package/packages/pi-coding-agent/dist/core/keybindings.js +2 -0
  60. package/packages/pi-coding-agent/dist/core/keybindings.js.map +1 -1
  61. package/packages/pi-coding-agent/dist/core/sdk.d.ts.map +1 -1
  62. package/packages/pi-coding-agent/dist/core/sdk.js +4 -2
  63. package/packages/pi-coding-agent/dist/core/sdk.js.map +1 -1
  64. package/packages/pi-coding-agent/dist/core/settings-manager.collapse-tool-calls.test.d.ts +2 -0
  65. package/packages/pi-coding-agent/dist/core/settings-manager.collapse-tool-calls.test.d.ts.map +1 -0
  66. package/packages/pi-coding-agent/dist/core/settings-manager.collapse-tool-calls.test.js +35 -0
  67. package/packages/pi-coding-agent/dist/core/settings-manager.collapse-tool-calls.test.js.map +1 -0
  68. package/packages/pi-coding-agent/dist/core/settings-manager.d.ts +12 -0
  69. package/packages/pi-coding-agent/dist/core/settings-manager.d.ts.map +1 -1
  70. package/packages/pi-coding-agent/dist/core/settings-manager.fast-mode.test.d.ts +2 -0
  71. package/packages/pi-coding-agent/dist/core/settings-manager.fast-mode.test.d.ts.map +1 -0
  72. package/packages/pi-coding-agent/dist/core/settings-manager.fast-mode.test.js +35 -0
  73. package/packages/pi-coding-agent/dist/core/settings-manager.fast-mode.test.js.map +1 -0
  74. package/packages/pi-coding-agent/dist/core/settings-manager.js +24 -0
  75. package/packages/pi-coding-agent/dist/core/settings-manager.js.map +1 -1
  76. package/packages/pi-coding-agent/dist/core/slash-commands.d.ts.map +1 -1
  77. package/packages/pi-coding-agent/dist/core/slash-commands.js +1 -0
  78. package/packages/pi-coding-agent/dist/core/slash-commands.js.map +1 -1
  79. package/packages/pi-coding-agent/dist/core/system-prompt.d.ts.map +1 -1
  80. package/packages/pi-coding-agent/dist/core/system-prompt.js +6 -1
  81. package/packages/pi-coding-agent/dist/core/system-prompt.js.map +1 -1
  82. package/packages/pi-coding-agent/dist/core/tool-priority.d.ts +4 -0
  83. package/packages/pi-coding-agent/dist/core/tool-priority.d.ts.map +1 -0
  84. package/packages/pi-coding-agent/dist/core/tool-priority.js +18 -0
  85. package/packages/pi-coding-agent/dist/core/tool-priority.js.map +1 -0
  86. package/packages/pi-coding-agent/dist/core/tool-priority.test.d.ts +2 -0
  87. package/packages/pi-coding-agent/dist/core/tool-priority.test.d.ts.map +1 -0
  88. package/packages/pi-coding-agent/dist/core/tool-priority.test.js +27 -0
  89. package/packages/pi-coding-agent/dist/core/tool-priority.test.js.map +1 -0
  90. package/packages/pi-coding-agent/dist/core/tools/edit-diff.d.ts +5 -0
  91. package/packages/pi-coding-agent/dist/core/tools/edit-diff.d.ts.map +1 -1
  92. package/packages/pi-coding-agent/dist/core/tools/edit-diff.js +21 -0
  93. package/packages/pi-coding-agent/dist/core/tools/edit-diff.js.map +1 -1
  94. package/packages/pi-coding-agent/dist/core/tools/edit-diff.test.js +16 -1
  95. package/packages/pi-coding-agent/dist/core/tools/edit-diff.test.js.map +1 -1
  96. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/tool-summary-line.test.d.ts +2 -0
  97. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/tool-summary-line.test.d.ts.map +1 -0
  98. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/tool-summary-line.test.js +34 -0
  99. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/tool-summary-line.test.js.map +1 -0
  100. package/packages/pi-coding-agent/dist/modes/interactive/components/btw-overlay.d.ts +45 -0
  101. package/packages/pi-coding-agent/dist/modes/interactive/components/btw-overlay.d.ts.map +1 -0
  102. package/packages/pi-coding-agent/dist/modes/interactive/components/btw-overlay.js +314 -0
  103. package/packages/pi-coding-agent/dist/modes/interactive/components/btw-overlay.js.map +1 -0
  104. package/packages/pi-coding-agent/dist/modes/interactive/components/btw-overlay.test.d.ts +2 -0
  105. package/packages/pi-coding-agent/dist/modes/interactive/components/btw-overlay.test.d.ts.map +1 -0
  106. package/packages/pi-coding-agent/dist/modes/interactive/components/btw-overlay.test.js +122 -0
  107. package/packages/pi-coding-agent/dist/modes/interactive/components/btw-overlay.test.js.map +1 -0
  108. package/packages/pi-coding-agent/dist/modes/interactive/components/diff.d.ts +7 -5
  109. package/packages/pi-coding-agent/dist/modes/interactive/components/diff.d.ts.map +1 -1
  110. package/packages/pi-coding-agent/dist/modes/interactive/components/diff.js +86 -28
  111. package/packages/pi-coding-agent/dist/modes/interactive/components/diff.js.map +1 -1
  112. package/packages/pi-coding-agent/dist/modes/interactive/components/footer.d.ts +4 -0
  113. package/packages/pi-coding-agent/dist/modes/interactive/components/footer.d.ts.map +1 -1
  114. package/packages/pi-coding-agent/dist/modes/interactive/components/footer.js +23 -10
  115. package/packages/pi-coding-agent/dist/modes/interactive/components/footer.js.map +1 -1
  116. package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.d.ts +8 -0
  117. package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  118. package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.js +52 -6
  119. package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.js.map +1 -1
  120. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts +19 -0
  121. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  122. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js +127 -14
  123. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js.map +1 -1
  124. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-summary-line.d.ts +14 -0
  125. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-summary-line.d.ts.map +1 -0
  126. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-summary-line.js +93 -0
  127. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-summary-line.js.map +1 -0
  128. package/packages/pi-coding-agent/dist/modes/interactive/controllers/__tests__/chat-controller.collapsed-tool-summary.test.d.ts +2 -0
  129. package/packages/pi-coding-agent/dist/modes/interactive/controllers/__tests__/chat-controller.collapsed-tool-summary.test.d.ts.map +1 -0
  130. package/packages/pi-coding-agent/dist/modes/interactive/controllers/__tests__/chat-controller.collapsed-tool-summary.test.js +328 -0
  131. package/packages/pi-coding-agent/dist/modes/interactive/controllers/__tests__/chat-controller.collapsed-tool-summary.test.js.map +1 -0
  132. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
  133. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js +123 -0
  134. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
  135. package/packages/pi-coding-agent/dist/modes/interactive/controllers/extension-ui-controller.js +1 -1
  136. package/packages/pi-coding-agent/dist/modes/interactive/controllers/extension-ui-controller.js.map +1 -1
  137. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.d.ts.map +1 -1
  138. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.js +7 -0
  139. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.js.map +1 -1
  140. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.d.ts +4 -0
  141. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.d.ts.map +1 -1
  142. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.js.map +1 -1
  143. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts +9 -1
  144. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  145. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +103 -23
  146. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
  147. package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.d.ts.map +1 -1
  148. package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.js +41 -0
  149. package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.js.map +1 -1
  150. package/packages/pi-coding-agent/dist/modes/interactive/theme/themes.js +4 -4
  151. package/packages/pi-coding-agent/dist/modes/interactive/theme/themes.js.map +1 -1
  152. package/packages/pi-coding-agent/package.json +1 -1
  153. package/packages/pi-coding-agent/src/core/keybindings.ts +4 -1
  154. package/packages/pi-coding-agent/src/core/sdk.ts +4 -2
  155. package/packages/pi-coding-agent/src/core/settings-manager.collapse-tool-calls.test.ts +46 -0
  156. package/packages/pi-coding-agent/src/core/settings-manager.fast-mode.test.ts +46 -0
  157. package/packages/pi-coding-agent/src/core/settings-manager.ts +36 -0
  158. package/packages/pi-coding-agent/src/core/slash-commands.ts +1 -0
  159. package/packages/pi-coding-agent/src/core/system-prompt.ts +6 -1
  160. package/packages/pi-coding-agent/src/core/tool-priority.test.ts +30 -0
  161. package/packages/pi-coding-agent/src/core/tool-priority.ts +17 -0
  162. package/packages/pi-coding-agent/src/core/tools/edit-diff.test.ts +20 -0
  163. package/packages/pi-coding-agent/src/core/tools/edit-diff.ts +26 -0
  164. package/packages/pi-coding-agent/src/modes/interactive/components/__tests__/tool-summary-line.test.ts +41 -0
  165. package/packages/pi-coding-agent/src/modes/interactive/components/btw-overlay.test.ts +172 -0
  166. package/packages/pi-coding-agent/src/modes/interactive/components/btw-overlay.ts +402 -0
  167. package/packages/pi-coding-agent/src/modes/interactive/components/diff.ts +105 -28
  168. package/packages/pi-coding-agent/src/modes/interactive/components/footer.ts +21 -6
  169. package/packages/pi-coding-agent/src/modes/interactive/components/settings-selector.ts +63 -6
  170. package/packages/pi-coding-agent/src/modes/interactive/components/tool-execution.ts +1262 -1138
  171. package/packages/pi-coding-agent/src/modes/interactive/components/tool-summary-line.ts +120 -0
  172. package/packages/pi-coding-agent/src/modes/interactive/controllers/__tests__/chat-controller.collapsed-tool-summary.test.ts +396 -0
  173. package/packages/pi-coding-agent/src/modes/interactive/controllers/chat-controller.ts +530 -398
  174. package/packages/pi-coding-agent/src/modes/interactive/controllers/extension-ui-controller.ts +1 -1
  175. package/packages/pi-coding-agent/src/modes/interactive/controllers/input-controller.ts +7 -0
  176. package/packages/pi-coding-agent/src/modes/interactive/interactive-mode-state.ts +4 -0
  177. package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +109 -23
  178. package/packages/pi-coding-agent/src/modes/interactive/slash-command-handlers.ts +60 -1
  179. package/packages/pi-coding-agent/src/modes/interactive/theme/themes.ts +4 -4
  180. package/packages/pi-tui/dist/components/editor.js +3 -3
  181. package/packages/pi-tui/dist/components/editor.js.map +1 -1
  182. package/packages/pi-tui/src/components/editor.ts +3 -3
  183. package/pkg/dist/modes/interactive/theme/themes.js +4 -4
  184. package/pkg/dist/modes/interactive/theme/themes.js.map +1 -1
  185. package/pkg/package.json +1 -1
  186. package/src/resources/extensions/browser-tools/tools/codegen.ts +5 -5
  187. package/src/resources/extensions/browser-tools/tools/navigation.ts +118 -196
  188. package/src/resources/extensions/browser-tools/tools/network-mock.ts +114 -205
  189. package/src/resources/extensions/browser-tools/tools/pages.ts +183 -237
  190. package/src/resources/extensions/browser-tools/tools/refs.ts +193 -507
  191. package/src/resources/extensions/browser-tools/tools/session.ts +182 -321
  192. package/src/resources/extensions/browser-tools/tools/state-persistence.ts +94 -172
  193. package/src/resources/extensions/browser-tools/utils.ts +1 -1
  194. package/src/resources/extensions/cache-timer/index.ts +3 -2
  195. package/src/resources/extensions/slash-commands/extension-manifest.json +2 -2
  196. package/src/resources/extensions/slash-commands/fast.ts +89 -0
  197. package/src/resources/extensions/slash-commands/index.ts +2 -0
  198. package/src/resources/extensions/slash-commands/plan.ts +42 -12
  199. package/src/resources/extensions/subagent/background-job-manager.ts +28 -0
  200. package/src/resources/extensions/subagent/in-process-runner.ts +534 -0
  201. package/src/resources/extensions/subagent/index.ts +489 -799
  202. package/src/resources/extensions/subagent/legacy-runner.ts +607 -0
  203. package/src/resources/extensions/voice/index.ts +308 -238
  204. package/src/resources/extensions/voice/push-to-talk.ts +42 -0
  205. package/src/resources/extensions/voice/tests/push-to-talk.test.ts +109 -0
@@ -0,0 +1,534 @@
1
+ import type { AgentToolResult } from "@gsd/pi-agent-core";
2
+ import type { ImageContent, Message } from "@gsd/pi-ai";
3
+ import { createAgentSession, getAgentDir, SessionManager } from "@gsd/pi-coding-agent";
4
+ import type { AgentConfig } from "./agents.js";
5
+ import { resolveConfiguredSubagentModel } from "./configured-model.js";
6
+ import { resolveSubagentModel } from "./model-resolution.js";
7
+ import { readBudgetSubagentModelFromSettings } from "./legacy-runner.js";
8
+ import { loadEffectivePreferences } from "../shared/preferences.js";
9
+
10
+ const MAX_AGENT_DURATION_MS = 30 * 60 * 1000;
11
+ export const MAX_IN_PROCESS_SUBAGENT_DEPTH = 3;
12
+ export const MAX_ACTIVE_DESCENDANTS = 8;
13
+
14
+ const NESTED_EXTENSION_TOOL_ALLOWLIST = new Set([
15
+ "subagent",
16
+ "await_subagent",
17
+ "bg_shell",
18
+ "fetch_page",
19
+ "resolve_library",
20
+ "get_library_docs",
21
+ "tool_search",
22
+ "tool_enable",
23
+ "Skill",
24
+ ]);
25
+
26
+ const parentSessionIdByChildSessionId = new Map<string, string>();
27
+ const childSessionIdsByParentSessionId = new Map<string, Set<string>>();
28
+ const handleBySessionId = new Map<string, { abort: () => void }>();
29
+ let activeDescendantCount = 0;
30
+
31
+ export interface InProcessUsageStats {
32
+ input: number;
33
+ output: number;
34
+ cacheRead: number;
35
+ cacheWrite: number;
36
+ cost: number;
37
+ contextTokens: number;
38
+ turns: number;
39
+ }
40
+
41
+ export interface InProcessSingleResult {
42
+ agent: string;
43
+ agentSource: "bundled" | "user" | "project" | "unknown";
44
+ task: string;
45
+ exitCode: number;
46
+ messages: Message[];
47
+ stderr: string;
48
+ usage: InProcessUsageStats;
49
+ model?: string;
50
+ stopReason?: string;
51
+ errorMessage?: string;
52
+ step?: number;
53
+ backgroundJobId?: string;
54
+ sessionFile?: string;
55
+ parentSessionFile?: string;
56
+ }
57
+
58
+ export interface SubagentHandle {
59
+ result: Promise<InProcessSingleResult>;
60
+ sessionId?: string;
61
+ isBusy: () => boolean;
62
+ prompt: (message: string, images?: ImageContent[]) => Promise<void>;
63
+ steer: (message: string, images?: ImageContent[]) => Promise<void>;
64
+ followUp: (message: string, images?: ImageContent[]) => Promise<void>;
65
+ abort: () => void;
66
+ dispose: () => void;
67
+ }
68
+
69
+ export interface StartedInProcessSingleRun {
70
+ handle: SubagentHandle;
71
+ currentResult: InProcessSingleResult;
72
+ resultPromise: Promise<InProcessSingleResult>;
73
+ }
74
+
75
+ export type InProcessOnUpdateCallback<TDetails> = (partial: AgentToolResult<TDetails>) => void;
76
+
77
+ function getFinalOutput(messages: Message[]): string {
78
+ for (let i = messages.length - 1; i >= 0; i--) {
79
+ const msg = messages[i];
80
+ if (msg.role === "assistant") {
81
+ for (const part of msg.content) {
82
+ if (part.type === "text") return part.text;
83
+ }
84
+ }
85
+ }
86
+ return "";
87
+ }
88
+
89
+ function toErrorString(error: unknown): string {
90
+ return error instanceof Error ? error.message : String(error);
91
+ }
92
+
93
+ function createUnknownAgentResult(
94
+ agents: AgentConfig[],
95
+ agentName: string,
96
+ task: string,
97
+ step: number | undefined,
98
+ parentSessionFile: string | undefined,
99
+ ): InProcessSingleResult {
100
+ const available = agents.map((a) => `"${a.name}"`).join(", ") || "none";
101
+ return {
102
+ agent: agentName,
103
+ agentSource: "unknown",
104
+ task,
105
+ exitCode: 1,
106
+ messages: [],
107
+ stderr: `Unknown agent: "${agentName}". Available agents: ${available}.`,
108
+ usage: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, cost: 0, contextTokens: 0, turns: 0 },
109
+ step,
110
+ parentSessionFile,
111
+ };
112
+ }
113
+
114
+ function createResolvedHandle(result: InProcessSingleResult): StartedInProcessSingleRun {
115
+ const resultPromise = Promise.resolve(result);
116
+ const handle: SubagentHandle = {
117
+ result: resultPromise,
118
+ isBusy: () => false,
119
+ prompt: async () => undefined,
120
+ steer: async () => undefined,
121
+ followUp: async () => undefined,
122
+ abort: () => undefined,
123
+ dispose: () => undefined,
124
+ };
125
+ return { handle, currentResult: result, resultPromise };
126
+ }
127
+
128
+ function reserveDescendantSlot(depth: number): (() => void) | null {
129
+ if (depth <= 1) return () => undefined;
130
+ if (activeDescendantCount >= MAX_ACTIVE_DESCENDANTS) return null;
131
+
132
+ activeDescendantCount += 1;
133
+ let released = false;
134
+ return () => {
135
+ if (released) return;
136
+ released = true;
137
+ activeDescendantCount = Math.max(0, activeDescendantCount - 1);
138
+ };
139
+ }
140
+
141
+ function linkParentChildSession(parentSessionId: string, childSessionId: string): void {
142
+ parentSessionIdByChildSessionId.set(childSessionId, parentSessionId);
143
+ const children = childSessionIdsByParentSessionId.get(parentSessionId) ?? new Set<string>();
144
+ children.add(childSessionId);
145
+ childSessionIdsByParentSessionId.set(parentSessionId, children);
146
+ }
147
+
148
+ function unlinkSessionFromParent(sessionId: string): void {
149
+ const parentSessionId = parentSessionIdByChildSessionId.get(sessionId);
150
+ if (!parentSessionId) return;
151
+
152
+ parentSessionIdByChildSessionId.delete(sessionId);
153
+ const siblings = childSessionIdsByParentSessionId.get(parentSessionId);
154
+ if (!siblings) return;
155
+
156
+ siblings.delete(sessionId);
157
+ if (siblings.size === 0) {
158
+ childSessionIdsByParentSessionId.delete(parentSessionId);
159
+ }
160
+ }
161
+
162
+ function abortDescendantTree(sessionId: string, visited = new Set<string>()): void {
163
+ if (visited.has(sessionId)) return;
164
+ visited.add(sessionId);
165
+
166
+ const descendants = childSessionIdsByParentSessionId.get(sessionId);
167
+ if (!descendants || descendants.size === 0) return;
168
+
169
+ for (const childSessionId of descendants) {
170
+ handleBySessionId.get(childSessionId)?.abort();
171
+ abortDescendantTree(childSessionId, visited);
172
+ }
173
+ }
174
+
175
+ function resolveRequestedToolNames(agent: AgentConfig, defaultActiveToolNames: string[]): string[] {
176
+ const requested = agent.tools && agent.tools.length > 0 ? agent.tools : defaultActiveToolNames;
177
+ return [...new Set(requested)];
178
+ }
179
+
180
+ function applyNestedExtensionToolPolicy(
181
+ toolNames: string[],
182
+ extensionToolNames: Set<string>,
183
+ nestingDepth: number,
184
+ ): { activeToolNames: string[]; droppedTools: string[] } {
185
+ if (nestingDepth <= 1) {
186
+ return { activeToolNames: toolNames, droppedTools: [] };
187
+ }
188
+
189
+ const activeToolNames: string[] = [];
190
+ const droppedTools: string[] = [];
191
+
192
+ for (const toolName of toolNames) {
193
+ if (!extensionToolNames.has(toolName)) {
194
+ activeToolNames.push(toolName);
195
+ continue;
196
+ }
197
+
198
+ if (NESTED_EXTENSION_TOOL_ALLOWLIST.has(toolName)) {
199
+ activeToolNames.push(toolName);
200
+ } else {
201
+ droppedTools.push(toolName);
202
+ }
203
+ }
204
+
205
+ return { activeToolNames, droppedTools };
206
+ }
207
+
208
+ export async function startInProcessSingleAgent<TDetails>(
209
+ defaultCwd: string,
210
+ agents: AgentConfig[],
211
+ agentName: string,
212
+ task: string,
213
+ cwd: string | undefined,
214
+ step: number | undefined,
215
+ modelOverride: string | undefined,
216
+ parentModel: { provider: string; id: string } | undefined,
217
+ signal: AbortSignal | undefined,
218
+ onUpdate: InProcessOnUpdateCallback<TDetails> | undefined,
219
+ makeDetails: (results: InProcessSingleResult[]) => TDetails,
220
+ parentSessionFile: string | undefined,
221
+ depth: number | undefined,
222
+ parentSessionId?: string,
223
+ ancestry?: string[],
224
+ onSubagentEvent?: (event: any, currentResult: InProcessSingleResult) => void,
225
+ ): Promise<StartedInProcessSingleRun> {
226
+ const agent = agents.find((candidate) => candidate.name === agentName);
227
+ if (!agent) {
228
+ return createResolvedHandle(createUnknownAgentResult(agents, agentName, task, step, parentSessionFile));
229
+ }
230
+
231
+ const nestingDepth = depth ?? 1;
232
+ if (nestingDepth > MAX_IN_PROCESS_SUBAGENT_DEPTH) {
233
+ return createResolvedHandle({
234
+ agent: agentName,
235
+ agentSource: agent.source,
236
+ task,
237
+ exitCode: 1,
238
+ messages: [],
239
+ stderr: `Max subagent depth (${MAX_IN_PROCESS_SUBAGENT_DEPTH}) exceeded.`,
240
+ usage: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, cost: 0, contextTokens: 0, turns: 0 },
241
+ step,
242
+ parentSessionFile,
243
+ });
244
+ }
245
+
246
+ const parentAgentName = ancestry && ancestry.length > 0 ? ancestry[ancestry.length - 1] : undefined;
247
+ if (parentAgentName && parentAgentName === agentName) {
248
+ return createResolvedHandle({
249
+ agent: agentName,
250
+ agentSource: agent.source,
251
+ task,
252
+ exitCode: 1,
253
+ messages: [],
254
+ stderr: `Subagent "${agentName}" cannot spawn another subagent with the same name as itself.`,
255
+ usage: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, cost: 0, contextTokens: 0, turns: 0 },
256
+ step,
257
+ parentSessionFile,
258
+ });
259
+ }
260
+
261
+ const releaseDescendantSlot = reserveDescendantSlot(nestingDepth);
262
+ if (nestingDepth > 1 && !releaseDescendantSlot) {
263
+ return createResolvedHandle({
264
+ agent: agentName,
265
+ agentSource: agent.source,
266
+ task,
267
+ exitCode: 1,
268
+ messages: [],
269
+ stderr: `Maximum active descendant subagents (${MAX_ACTIVE_DESCENDANTS}) reached. Wait for running descendants to finish or cancel one before spawning more.`,
270
+ usage: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, cost: 0, contextTokens: 0, turns: 0 },
271
+ step,
272
+ parentSessionFile,
273
+ });
274
+ }
275
+
276
+ const releaseDescendantSlotOnce = (() => {
277
+ let released = false;
278
+ return () => {
279
+ if (released) return;
280
+ released = true;
281
+ releaseDescendantSlot?.();
282
+ };
283
+ })();
284
+
285
+ const preferences = loadEffectivePreferences()?.preferences;
286
+ const settingsBudgetModel = readBudgetSubagentModelFromSettings();
287
+ const resolvedModel = resolveConfiguredSubagentModel(agent, preferences, settingsBudgetModel);
288
+ const inferredModel = resolveSubagentModel(
289
+ { name: agent.name, model: resolvedModel },
290
+ { overrideModel: modelOverride, parentModel },
291
+ );
292
+
293
+ const currentResult: InProcessSingleResult = {
294
+ agent: agentName,
295
+ agentSource: agent.source,
296
+ task,
297
+ exitCode: 0,
298
+ messages: [],
299
+ stderr: "",
300
+ usage: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, cost: 0, contextTokens: 0, turns: 0 },
301
+ model: inferredModel,
302
+ step,
303
+ parentSessionFile,
304
+ };
305
+
306
+ const emitUpdate = () => {
307
+ if (!onUpdate) return;
308
+ onUpdate({
309
+ content: [{ type: "text", text: getFinalOutput(currentResult.messages) || "(running...)" }],
310
+ details: makeDetails([currentResult]),
311
+ });
312
+ };
313
+
314
+ const effectiveCwd = cwd ?? defaultCwd;
315
+ const sessionManager = SessionManager.inMemory(effectiveCwd);
316
+
317
+ let session: Awaited<ReturnType<typeof createAgentSession>>["session"];
318
+ try {
319
+ const created = await createAgentSession({
320
+ cwd: effectiveCwd,
321
+ agentDir: getAgentDir(),
322
+ sessionManager,
323
+ });
324
+ session = created.session;
325
+ await session.bindExtensions({});
326
+ } catch (error) {
327
+ releaseDescendantSlotOnce();
328
+ currentResult.exitCode = 1;
329
+ currentResult.stderr = `Failed to initialize in-process subagent session: ${toErrorString(error)}`;
330
+ return createResolvedHandle(currentResult);
331
+ }
332
+
333
+ if (inferredModel) {
334
+ const [provider, modelId] = inferredModel.split("/");
335
+ if (provider && modelId) {
336
+ const model = session.modelRegistry.find(provider, modelId);
337
+ if (!model) {
338
+ session.dispose();
339
+ releaseDescendantSlotOnce();
340
+ currentResult.exitCode = 1;
341
+ currentResult.stderr = `Unable to resolve model ${inferredModel} for in-process subagent.`;
342
+ return createResolvedHandle(currentResult);
343
+ }
344
+ await session.setModel(model);
345
+ currentResult.model = `${model.provider}/${model.id}`;
346
+ }
347
+ }
348
+
349
+ const defaultActiveToolNames = session.getActiveToolNames();
350
+ const requestedToolNames = resolveRequestedToolNames(agent, defaultActiveToolNames);
351
+ const extensionToolNames = new Set(
352
+ session.extensionRunner?.getAllRegisteredTools().map((registered) => registered.definition.name) ?? [],
353
+ );
354
+ const { activeToolNames: resolvedActiveToolNames, droppedTools } = applyNestedExtensionToolPolicy(
355
+ requestedToolNames,
356
+ extensionToolNames,
357
+ nestingDepth,
358
+ );
359
+ session.setActiveToolsByName(resolvedActiveToolNames);
360
+
361
+ const ancestryChain = ancestry && ancestry.length > 0 ? ancestry.join(" -> ") : "root";
362
+ const nestedPolicyLine = droppedTools.length > 0
363
+ ? `Nested safety policy disabled extension tools: ${droppedTools.join(", ")}.`
364
+ : "";
365
+ const antiRecursionPrompt = [
366
+ `You are already the ${agentName} subagent for this session.`,
367
+ "Do not spawn or delegate to another subagent with the same name as yourself.",
368
+ `If the user asks you to continue ${agentName} work, do that work directly in this session.`,
369
+ `Original delegated task: ${task}`,
370
+ `Current subagent depth: ${nestingDepth}/${MAX_IN_PROCESS_SUBAGENT_DEPTH}.`,
371
+ `Subagent ancestry: ${ancestryChain}.`,
372
+ nestedPolicyLine,
373
+ ].filter(Boolean).join("\n");
374
+ const appendedParts = [antiRecursionPrompt, agent.systemPrompt.trim()].filter((part) => part.length > 0);
375
+ if (appendedParts.length > 0) {
376
+ const appendedPrompt = `${session.systemPrompt}\n\n${appendedParts.join("\n\n")}`;
377
+ session.agent.setSystemPrompt(appendedPrompt);
378
+ }
379
+
380
+ let completed = false;
381
+ let disposed = false;
382
+ const unsubscribe = session.subscribe((event: any) => {
383
+ onSubagentEvent?.(event, currentResult);
384
+
385
+ if (event?.type !== "message_end" || !event.message) return;
386
+ const message = event.message as Message;
387
+ currentResult.messages.push(message);
388
+
389
+ if (message.role === "assistant") {
390
+ currentResult.usage.turns++;
391
+ const usage = message.usage;
392
+ if (usage) {
393
+ currentResult.usage.input += usage.input || 0;
394
+ currentResult.usage.output += usage.output || 0;
395
+ currentResult.usage.cacheRead += usage.cacheRead || 0;
396
+ currentResult.usage.cacheWrite += usage.cacheWrite || 0;
397
+ currentResult.usage.cost += usage.cost?.total || 0;
398
+ currentResult.usage.contextTokens = usage.totalTokens || 0;
399
+ }
400
+ if (message.model) currentResult.model = message.model;
401
+ if (message.stopReason) currentResult.stopReason = message.stopReason;
402
+ if (message.errorMessage) currentResult.errorMessage = message.errorMessage;
403
+ }
404
+
405
+ emitUpdate();
406
+ });
407
+
408
+ const sessionId = session.sessionId;
409
+ const dispose = () => {
410
+ if (disposed) return;
411
+ disposed = true;
412
+
413
+ if (sessionId) {
414
+ handleBySessionId.delete(sessionId);
415
+ unlinkSessionFromParent(sessionId);
416
+ }
417
+ releaseDescendantSlotOnce();
418
+
419
+ unsubscribe();
420
+ session.dispose();
421
+ };
422
+
423
+ const handle: SubagentHandle = {
424
+ result: Promise.resolve(currentResult),
425
+ sessionId,
426
+ isBusy: () => session.isStreaming,
427
+ prompt: (message: string, images?: ImageContent[]) => session.prompt(message, { images }),
428
+ steer: (message: string, images?: ImageContent[]) => session.steer(message, images),
429
+ followUp: (message: string, images?: ImageContent[]) => session.followUp(message, images),
430
+ abort: () => {
431
+ if (sessionId) {
432
+ abortDescendantTree(sessionId);
433
+ }
434
+ void session.abort();
435
+ },
436
+ dispose,
437
+ };
438
+
439
+ if (sessionId) {
440
+ handleBySessionId.set(sessionId, handle);
441
+ if (parentSessionId) {
442
+ linkParentChildSession(parentSessionId, sessionId);
443
+ }
444
+ }
445
+
446
+ let timeoutTimer: ReturnType<typeof setTimeout> | undefined;
447
+ const timeoutPromise = new Promise<never>((_, reject) => {
448
+ timeoutTimer = setTimeout(() => {
449
+ handle.abort();
450
+ reject(new Error(`In-process subagent timed out after ${MAX_AGENT_DURATION_MS}ms.`));
451
+ }, MAX_AGENT_DURATION_MS);
452
+ if (typeof timeoutTimer === "object" && "unref" in timeoutTimer) timeoutTimer.unref();
453
+ });
454
+
455
+ const runPromise = (async () => {
456
+ if (signal?.aborted) {
457
+ handle.abort();
458
+ throw new Error("Subagent was aborted");
459
+ }
460
+
461
+ const onAbort = () => handle.abort();
462
+ signal?.addEventListener("abort", onAbort, { once: true });
463
+ try {
464
+ await session.prompt(task);
465
+ completed = true;
466
+ } finally {
467
+ signal?.removeEventListener("abort", onAbort);
468
+ }
469
+ })();
470
+
471
+ const resultPromise = (async () => {
472
+ try {
473
+ await Promise.race([runPromise, timeoutPromise]);
474
+ } catch (error) {
475
+ currentResult.exitCode = 1;
476
+ currentResult.stderr = toErrorString(error);
477
+ if (signal?.aborted) currentResult.stopReason = "aborted";
478
+ } finally {
479
+ if (timeoutTimer) clearTimeout(timeoutTimer);
480
+ if (!completed && signal?.aborted) {
481
+ currentResult.stopReason = "aborted";
482
+ }
483
+ dispose();
484
+ }
485
+ return currentResult;
486
+ })();
487
+
488
+ handle.result = resultPromise;
489
+
490
+ return {
491
+ handle,
492
+ currentResult,
493
+ resultPromise,
494
+ };
495
+ }
496
+
497
+ export async function runInProcessSingleAgent<TDetails>(
498
+ defaultCwd: string,
499
+ agents: AgentConfig[],
500
+ agentName: string,
501
+ task: string,
502
+ cwd: string | undefined,
503
+ step: number | undefined,
504
+ modelOverride: string | undefined,
505
+ parentModel: { provider: string; id: string } | undefined,
506
+ signal: AbortSignal | undefined,
507
+ onUpdate: InProcessOnUpdateCallback<TDetails> | undefined,
508
+ makeDetails: (results: InProcessSingleResult[]) => TDetails,
509
+ parentSessionFile: string | undefined,
510
+ depth: number | undefined,
511
+ parentSessionId?: string,
512
+ ancestry?: string[],
513
+ onSubagentEvent?: (event: any, currentResult: InProcessSingleResult) => void,
514
+ ): Promise<InProcessSingleResult> {
515
+ const started = await startInProcessSingleAgent(
516
+ defaultCwd,
517
+ agents,
518
+ agentName,
519
+ task,
520
+ cwd,
521
+ step,
522
+ modelOverride,
523
+ parentModel,
524
+ signal,
525
+ onUpdate,
526
+ makeDetails,
527
+ parentSessionFile,
528
+ depth,
529
+ parentSessionId,
530
+ ancestry,
531
+ onSubagentEvent,
532
+ );
533
+ return started.resultPromise;
534
+ }