@fitchmultz/pi-subagents 0.0.0-stage → 0.44.5

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 (347) hide show
  1. package/CHANGELOG.md +1654 -0
  2. package/LICENSE +21 -0
  3. package/README.md +1533 -2
  4. package/agents/context-builder.md +97 -0
  5. package/agents/debugger.md +51 -0
  6. package/agents/delegate.md +13 -0
  7. package/agents/fixer.md +37 -0
  8. package/agents/oracle.md +71 -0
  9. package/agents/planner.md +72 -0
  10. package/agents/researcher.md +51 -0
  11. package/agents/reviewer-claude.md +69 -0
  12. package/agents/reviewer-gpt.md +68 -0
  13. package/agents/reviewer-ponytail.md +82 -0
  14. package/agents/reviewer-security.md +81 -0
  15. package/agents/reviewer.md +69 -0
  16. package/agents/scout.md +66 -0
  17. package/agents/ui-designer.md +51 -0
  18. package/agents/watcher.md +26 -0
  19. package/agents/worker.md +46 -0
  20. package/agents/writer.md +36 -0
  21. package/dist/agents/agent-management.js +923 -0
  22. package/dist/agents/agent-scope.js +7 -0
  23. package/dist/agents/agent-selection.js +20 -0
  24. package/dist/agents/agent-serializer.js +100 -0
  25. package/dist/agents/agents.js +752 -0
  26. package/dist/agents/chain-serializer.js +367 -0
  27. package/dist/agents/frontmatter.js +21 -0
  28. package/dist/agents/identity.js +29 -0
  29. package/dist/agents/skills.js +570 -0
  30. package/dist/extension/build-info.js +1 -0
  31. package/dist/extension/compact-tools.js +112 -0
  32. package/dist/extension/config.js +22 -0
  33. package/dist/extension/control-notices.js +29 -0
  34. package/dist/extension/doctor.js +143 -0
  35. package/dist/extension/fanout-child.js +326 -0
  36. package/dist/extension/index.js +577 -0
  37. package/dist/extension/schemas.js +416 -0
  38. package/dist/extension/tool-input.js +20 -0
  39. package/dist/extension/tool-result.js +14 -0
  40. package/dist/history/canonical-result.js +81 -0
  41. package/dist/history/database.js +72 -0
  42. package/dist/history/index.js +199 -0
  43. package/dist/history/ingest.js +468 -0
  44. package/dist/history/queries.js +350 -0
  45. package/dist/history/store.js +216 -0
  46. package/dist/history/types.js +4 -0
  47. package/dist/history/worker.js +466 -0
  48. package/dist/intercom/intercom-bridge.js +112 -0
  49. package/dist/intercom/live-intercom.js +94 -0
  50. package/dist/intercom/result-intercom.js +373 -0
  51. package/dist/pi-intercom/agent-dir.js +10 -0
  52. package/dist/pi-intercom/bridge.js +750 -0
  53. package/dist/pi-intercom/broker/broker.js +426 -0
  54. package/dist/pi-intercom/broker/client.js +492 -0
  55. package/dist/pi-intercom/broker/framing.js +65 -0
  56. package/dist/pi-intercom/broker/paths.js +42 -0
  57. package/dist/pi-intercom/broker/pid.js +66 -0
  58. package/dist/pi-intercom/broker/spawn.js +221 -0
  59. package/dist/pi-intercom/config.js +102 -0
  60. package/dist/pi-intercom/index.js +2942 -0
  61. package/dist/pi-intercom/reply-tracker.js +182 -0
  62. package/dist/pi-intercom/session-targets.js +128 -0
  63. package/dist/pi-intercom/subagent-live-events.js +132 -0
  64. package/dist/pi-intercom/topics.js +157 -0
  65. package/dist/pi-intercom/types.js +163 -0
  66. package/dist/pi-intercom/ui/compose.js +269 -0
  67. package/dist/pi-intercom/ui/inline-message.js +80 -0
  68. package/dist/pi-intercom/ui/session-list.js +124 -0
  69. package/dist/runs/background/async-control.js +95 -0
  70. package/dist/runs/background/async-execution.js +676 -0
  71. package/dist/runs/background/async-job-tracker.js +383 -0
  72. package/dist/runs/background/async-result-file.js +58 -0
  73. package/dist/runs/background/async-resume.js +305 -0
  74. package/dist/runs/background/async-status.js +442 -0
  75. package/dist/runs/background/completion-dedupe.js +69 -0
  76. package/dist/runs/background/completion-delivery.js +245 -0
  77. package/dist/runs/background/notify.js +71 -0
  78. package/dist/runs/background/parallel-groups.js +55 -0
  79. package/dist/runs/background/result-watcher.js +426 -0
  80. package/dist/runs/background/run-id-resolver.js +64 -0
  81. package/dist/runs/background/run-status.js +382 -0
  82. package/dist/runs/background/stale-run-reconciler.js +335 -0
  83. package/dist/runs/background/subagent-runner-launcher.js +55 -0
  84. package/dist/runs/background/subagent-runner.js +1872 -0
  85. package/dist/runs/background/top-level-async.js +5 -0
  86. package/dist/runs/foreground/chain-clarify.js +1152 -0
  87. package/dist/runs/foreground/clarify-invocation.js +72 -0
  88. package/dist/runs/foreground/execution-input.js +423 -0
  89. package/dist/runs/foreground/foreground-control.js +817 -0
  90. package/dist/runs/foreground/question-control.js +88 -0
  91. package/dist/runs/foreground/run-async-path.js +205 -0
  92. package/dist/runs/foreground/subagent-executor.js +442 -0
  93. package/dist/runs/foreground/subagent-params.js +165 -0
  94. package/dist/runs/foreground/wait-run.js +141 -0
  95. package/dist/runs/shared/acceptance-contract.js +253 -0
  96. package/dist/runs/shared/acceptance-evaluation.js +256 -0
  97. package/dist/runs/shared/acceptance-finalization.js +280 -0
  98. package/dist/runs/shared/acceptance-reports.js +147 -0
  99. package/dist/runs/shared/acceptance.js +4 -0
  100. package/dist/runs/shared/chain-outputs.js +110 -0
  101. package/dist/runs/shared/child-attempt.js +693 -0
  102. package/dist/runs/shared/child-execution-cwd.js +96 -0
  103. package/dist/runs/shared/child-observations.js +114 -0
  104. package/dist/runs/shared/claude-code.js +199 -0
  105. package/dist/runs/shared/completion-guard.js +31 -0
  106. package/dist/runs/shared/dynamic-fanout.js +274 -0
  107. package/dist/runs/shared/history-index.js +72 -0
  108. package/dist/runs/shared/mcp-direct-tool-allowlist.js +334 -0
  109. package/dist/runs/shared/model-fallback.js +185 -0
  110. package/dist/runs/shared/mutating-tool-guard.js +170 -0
  111. package/dist/runs/shared/native-finalization.js +101 -0
  112. package/dist/runs/shared/native-usage.js +153 -0
  113. package/dist/runs/shared/nested-events.js +700 -0
  114. package/dist/runs/shared/nested-path.js +48 -0
  115. package/dist/runs/shared/nested-render.js +108 -0
  116. package/dist/runs/shared/parallel-utils.js +61 -0
  117. package/dist/runs/shared/parent-receipts.js +68 -0
  118. package/dist/runs/shared/parent-usage.js +121 -0
  119. package/dist/runs/shared/pi-args.js +310 -0
  120. package/dist/runs/shared/pi-spawn.js +52 -0
  121. package/dist/runs/shared/run-history-queries.js +45 -0
  122. package/dist/runs/shared/run-history.js +110 -0
  123. package/dist/runs/shared/run-id-ambiguity.js +4 -0
  124. package/dist/runs/shared/run-records.js +679 -0
  125. package/dist/runs/shared/session-cwd-preload.js +33 -0
  126. package/dist/runs/shared/single-output.js +222 -0
  127. package/dist/runs/shared/streaming-text.js +15 -0
  128. package/dist/runs/shared/structured-output.js +55 -0
  129. package/dist/runs/shared/subagent-control.js +179 -0
  130. package/dist/runs/shared/subagent-prompt-runtime.js +197 -0
  131. package/dist/runs/shared/subagent-tool-loop-guard.js +66 -0
  132. package/dist/runs/shared/supervisor-questions.js +350 -0
  133. package/dist/runs/shared/workflow-graph.js +214 -0
  134. package/dist/runs/shared/workflow-policy.js +84 -0
  135. package/dist/runs/shared/worktree.js +525 -0
  136. package/dist/shared/agent-context-policy.js +126 -0
  137. package/dist/shared/artifacts.js +96 -0
  138. package/dist/shared/atomic-json.js +13 -0
  139. package/dist/shared/cooperative.js +20 -0
  140. package/dist/shared/execution-cwd.js +14 -0
  141. package/dist/shared/file-coalescer.js +21 -0
  142. package/dist/shared/fork-context.js +49 -0
  143. package/dist/shared/formatters.js +85 -0
  144. package/dist/shared/journal-reader.js +407 -0
  145. package/dist/shared/lazy-tools.js +32 -0
  146. package/dist/shared/model-info.js +68 -0
  147. package/dist/shared/native-session.js +15 -0
  148. package/dist/shared/native-tui.js +18 -0
  149. package/dist/shared/native-typebox.js +20 -0
  150. package/dist/shared/post-exit-stdio-guard.js +197 -0
  151. package/dist/shared/prompt-sections.js +8 -0
  152. package/dist/shared/session-entries.js +52 -0
  153. package/dist/shared/session-identity.js +14 -0
  154. package/dist/shared/settings.js +254 -0
  155. package/dist/shared/status-format.js +111 -0
  156. package/dist/shared/temp-root.js +114 -0
  157. package/dist/shared/types.js +207 -0
  158. package/dist/shared/ui-mode.js +3 -0
  159. package/dist/shared/utils.js +334 -0
  160. package/dist/slash/prompt-template-bridge.js +298 -0
  161. package/dist/slash/slash-bridge.js +116 -0
  162. package/dist/slash/slash-commands.js +607 -0
  163. package/dist/slash/slash-live-state.js +246 -0
  164. package/dist/tui/action-hints.js +75 -0
  165. package/dist/tui/agent-history.js +197 -0
  166. package/dist/tui/agent-view.js +1656 -0
  167. package/dist/tui/render.js +1515 -0
  168. package/docs/intercom-bridge.md +212 -0
  169. package/docs/intercom.md +581 -0
  170. package/docs/performance.md +121 -0
  171. package/package.json +102 -4
  172. package/prompts/gather-context-and-clarify.md +14 -0
  173. package/prompts/parallel-cleanup.md +62 -0
  174. package/prompts/parallel-context-build.md +56 -0
  175. package/prompts/parallel-handoff-plan.md +62 -0
  176. package/prompts/parallel-research.md +51 -0
  177. package/prompts/parallel-review.md +59 -0
  178. package/prompts/review-loop.md +42 -0
  179. package/scripts/build.mjs +134 -0
  180. package/scripts/check-compat.mjs +48 -0
  181. package/scripts/compat-host.mjs +19 -0
  182. package/scripts/intercom-bridge-credentials.mjs +143 -0
  183. package/scripts/intercom-remote.mjs +103 -0
  184. package/scripts/linux-smoke.sh +51 -0
  185. package/scripts/local-install-smoke.mjs +66 -0
  186. package/scripts/native-package-smoke.mjs +82 -0
  187. package/scripts/package-smoke.mjs +209 -0
  188. package/scripts/prepare.mjs +67 -0
  189. package/scripts/real-pi-smoke.mjs +415 -0
  190. package/scripts/run-tests.mjs +149 -0
  191. package/skills/pi-intercom/SKILL.md +113 -0
  192. package/skills/pi-intercom/evals/evals.json +29 -0
  193. package/skills/pi-intercom/evals/trigger-evals.json +47 -0
  194. package/skills/pi-intercom/references/peer-sessions.md +69 -0
  195. package/skills/pi-subagents/SKILL.md +76 -0
  196. package/skills/pi-subagents/evals/evals.json +29 -0
  197. package/skills/pi-subagents/evals/trigger-evals.json +42 -0
  198. package/skills/pi-subagents/references/full-orchestration-guide.md +863 -0
  199. package/src/agents/agent-management.ts +813 -0
  200. package/src/agents/agent-scope.ts +7 -0
  201. package/src/agents/agent-selection.ts +23 -0
  202. package/src/agents/agent-serializer.ts +99 -0
  203. package/src/agents/agents.ts +916 -0
  204. package/src/agents/chain-serializer.ts +329 -0
  205. package/src/agents/frontmatter.ts +21 -0
  206. package/src/agents/identity.ts +30 -0
  207. package/src/agents/skills.ts +637 -0
  208. package/src/extension/build-info.ts +2 -0
  209. package/src/extension/compact-tools.ts +114 -0
  210. package/src/extension/config.ts +23 -0
  211. package/src/extension/control-notices.ts +47 -0
  212. package/src/extension/doctor.ts +171 -0
  213. package/src/extension/fanout-child.ts +312 -0
  214. package/src/extension/index.ts +649 -0
  215. package/src/extension/schemas.ts +444 -0
  216. package/src/extension/tool-input.ts +19 -0
  217. package/src/extension/tool-result.ts +16 -0
  218. package/src/history/canonical-result.ts +62 -0
  219. package/src/history/database.ts +59 -0
  220. package/src/history/index.ts +156 -0
  221. package/src/history/ingest.ts +349 -0
  222. package/src/history/queries.ts +241 -0
  223. package/src/history/store.ts +203 -0
  224. package/src/history/types.ts +166 -0
  225. package/src/history/worker.ts +321 -0
  226. package/src/intercom/intercom-bridge.ts +123 -0
  227. package/src/intercom/live-intercom.ts +104 -0
  228. package/src/intercom/result-intercom.ts +429 -0
  229. package/src/pi-intercom/agent-dir.ts +9 -0
  230. package/src/pi-intercom/bridge.ts +668 -0
  231. package/src/pi-intercom/broker/broker.ts +463 -0
  232. package/src/pi-intercom/broker/client.ts +558 -0
  233. package/src/pi-intercom/broker/framing.ts +77 -0
  234. package/src/pi-intercom/broker/paths.ts +47 -0
  235. package/src/pi-intercom/broker/pid.ts +59 -0
  236. package/src/pi-intercom/broker/spawn.ts +248 -0
  237. package/src/pi-intercom/config.ts +142 -0
  238. package/src/pi-intercom/index.ts +2929 -0
  239. package/src/pi-intercom/reply-tracker.ts +211 -0
  240. package/src/pi-intercom/session-targets.ts +172 -0
  241. package/src/pi-intercom/subagent-live-events.ts +133 -0
  242. package/src/pi-intercom/topics.ts +125 -0
  243. package/src/pi-intercom/types.ts +271 -0
  244. package/src/pi-intercom/ui/compose.ts +289 -0
  245. package/src/pi-intercom/ui/inline-message.ts +92 -0
  246. package/src/pi-intercom/ui/session-list.ts +139 -0
  247. package/src/runs/background/async-control.ts +89 -0
  248. package/src/runs/background/async-execution.ts +850 -0
  249. package/src/runs/background/async-job-tracker.ts +385 -0
  250. package/src/runs/background/async-result-file.ts +62 -0
  251. package/src/runs/background/async-resume.ts +333 -0
  252. package/src/runs/background/async-status.ts +481 -0
  253. package/src/runs/background/completion-dedupe.ts +79 -0
  254. package/src/runs/background/completion-delivery.ts +195 -0
  255. package/src/runs/background/notify.ts +123 -0
  256. package/src/runs/background/parallel-groups.ts +53 -0
  257. package/src/runs/background/result-watcher.ts +423 -0
  258. package/src/runs/background/run-id-resolver.ts +69 -0
  259. package/src/runs/background/run-status.ts +384 -0
  260. package/src/runs/background/stale-run-reconciler.ts +369 -0
  261. package/src/runs/background/subagent-runner-launcher.ts +56 -0
  262. package/src/runs/background/subagent-runner.ts +2053 -0
  263. package/src/runs/background/top-level-async.ts +13 -0
  264. package/src/runs/foreground/chain-clarify.ts +1340 -0
  265. package/src/runs/foreground/clarify-invocation.ts +77 -0
  266. package/src/runs/foreground/execution-input.ts +467 -0
  267. package/src/runs/foreground/foreground-control.ts +821 -0
  268. package/src/runs/foreground/question-control.ts +82 -0
  269. package/src/runs/foreground/run-async-path.ts +243 -0
  270. package/src/runs/foreground/subagent-executor.ts +501 -0
  271. package/src/runs/foreground/subagent-params.ts +320 -0
  272. package/src/runs/foreground/wait-run.ts +124 -0
  273. package/src/runs/shared/acceptance-contract.ts +257 -0
  274. package/src/runs/shared/acceptance-evaluation.ts +275 -0
  275. package/src/runs/shared/acceptance-finalization.ts +353 -0
  276. package/src/runs/shared/acceptance-reports.ts +152 -0
  277. package/src/runs/shared/acceptance.ts +33 -0
  278. package/src/runs/shared/chain-outputs.ts +114 -0
  279. package/src/runs/shared/child-attempt.ts +623 -0
  280. package/src/runs/shared/child-execution-cwd.ts +91 -0
  281. package/src/runs/shared/child-observations.ts +99 -0
  282. package/src/runs/shared/claude-code.ts +243 -0
  283. package/src/runs/shared/completion-guard.ts +35 -0
  284. package/src/runs/shared/dynamic-fanout.ts +295 -0
  285. package/src/runs/shared/history-index.ts +69 -0
  286. package/src/runs/shared/mcp-direct-tool-allowlist.ts +376 -0
  287. package/src/runs/shared/model-fallback.ts +219 -0
  288. package/src/runs/shared/mutating-tool-guard.ts +194 -0
  289. package/src/runs/shared/native-finalization.ts +128 -0
  290. package/src/runs/shared/native-usage.ts +113 -0
  291. package/src/runs/shared/nested-events.ts +752 -0
  292. package/src/runs/shared/nested-path.ts +52 -0
  293. package/src/runs/shared/nested-render.ts +119 -0
  294. package/src/runs/shared/parallel-utils.ts +153 -0
  295. package/src/runs/shared/parent-receipts.ts +59 -0
  296. package/src/runs/shared/parent-usage.ts +121 -0
  297. package/src/runs/shared/pi-args.ts +350 -0
  298. package/src/runs/shared/pi-spawn.ts +52 -0
  299. package/src/runs/shared/run-history-queries.ts +44 -0
  300. package/src/runs/shared/run-history.ts +100 -0
  301. package/src/runs/shared/run-id-ambiguity.ts +4 -0
  302. package/src/runs/shared/run-records.ts +607 -0
  303. package/src/runs/shared/session-cwd-preload.ts +27 -0
  304. package/src/runs/shared/single-output.ts +267 -0
  305. package/src/runs/shared/streaming-text.ts +18 -0
  306. package/src/runs/shared/structured-output.ts +68 -0
  307. package/src/runs/shared/subagent-control.ts +219 -0
  308. package/src/runs/shared/subagent-prompt-runtime.ts +201 -0
  309. package/src/runs/shared/subagent-tool-loop-guard.ts +89 -0
  310. package/src/runs/shared/supervisor-questions.ts +415 -0
  311. package/src/runs/shared/workflow-graph.ts +220 -0
  312. package/src/runs/shared/workflow-policy.ts +125 -0
  313. package/src/runs/shared/worktree.ts +635 -0
  314. package/src/shared/agent-context-policy.ts +184 -0
  315. package/src/shared/artifacts.ts +95 -0
  316. package/src/shared/atomic-json.ts +16 -0
  317. package/src/shared/cooperative.ts +21 -0
  318. package/src/shared/execution-cwd.ts +14 -0
  319. package/src/shared/file-coalescer.ts +29 -0
  320. package/src/shared/fork-context.ts +68 -0
  321. package/src/shared/formatters.ts +84 -0
  322. package/src/shared/journal-reader.ts +333 -0
  323. package/src/shared/lazy-tools.ts +36 -0
  324. package/src/shared/model-info.ts +81 -0
  325. package/src/shared/native-session.ts +8 -0
  326. package/src/shared/native-tui.ts +10 -0
  327. package/src/shared/native-typebox.ts +14 -0
  328. package/src/shared/post-exit-stdio-guard.ts +204 -0
  329. package/src/shared/prompt-sections.ts +10 -0
  330. package/src/shared/session-entries.ts +49 -0
  331. package/src/shared/session-identity.ts +18 -0
  332. package/src/shared/settings.ts +384 -0
  333. package/src/shared/status-format.ts +123 -0
  334. package/src/shared/temp-root.ts +107 -0
  335. package/src/shared/types.ts +1260 -0
  336. package/src/shared/ui-mode.ts +5 -0
  337. package/src/shared/utils.ts +342 -0
  338. package/src/slash/prompt-template-bridge.ts +408 -0
  339. package/src/slash/slash-bridge.ts +178 -0
  340. package/src/slash/slash-commands.ts +617 -0
  341. package/src/slash/slash-live-state.ts +292 -0
  342. package/src/tui/action-hints.ts +69 -0
  343. package/src/tui/agent-history.ts +210 -0
  344. package/src/tui/agent-view.ts +1323 -0
  345. package/src/tui/render.ts +1550 -0
  346. package/tsconfig.build.json +12 -0
  347. package/tsconfig.json +19 -0
@@ -0,0 +1,96 @@
1
+ import * as fs from "node:fs";
2
+ import * as path from "node:path";
3
+ import { TEMP_ARTIFACTS_DIR } from "./types.js";
4
+ import { getAgentDir } from "./utils.js";
5
+ import { ensureSafeTempPath } from "./temp-root.js";
6
+ const CLEANUP_MARKER_FILE = ".last-cleanup";
7
+ export const ARTIFACT_CLEANUP_DAYS = 7;
8
+ export function getArtifactsDir(sessionFile) {
9
+ if (sessionFile) {
10
+ const sessionDir = path.dirname(sessionFile);
11
+ return path.join(sessionDir, "subagent-artifacts");
12
+ }
13
+ return TEMP_ARTIFACTS_DIR;
14
+ }
15
+ export function getArtifactPaths(artifactsDir, runId, agent, index) {
16
+ ensureSafeTempPath(artifactsDir);
17
+ const suffix = index !== undefined ? `_${index}` : "";
18
+ const safeAgent = agent.replace(/[^\w.-]/g, "_").slice(0, 128);
19
+ const base = `${runId}_${safeAgent}${suffix}`;
20
+ return {
21
+ inputPath: path.join(artifactsDir, `${base}_input.md`),
22
+ outputPath: path.join(artifactsDir, `${base}_output.md`),
23
+ metadataPath: path.join(artifactsDir, `${base}_meta.json`),
24
+ };
25
+ }
26
+ export function appendJsonl(filePath, line) {
27
+ fs.appendFileSync(filePath, `${line}\n`);
28
+ }
29
+ export async function cleanupOldArtifacts(dir, maxAgeDays) {
30
+ ensureSafeTempPath(dir);
31
+ try {
32
+ await fs.promises.access(dir);
33
+ }
34
+ catch {
35
+ return;
36
+ }
37
+ const markerPath = path.join(dir, CLEANUP_MARKER_FILE);
38
+ const now = Date.now();
39
+ try {
40
+ const stat = await fs.promises.lstat(markerPath);
41
+ if (!stat.isSymbolicLink() && now - stat.mtimeMs < 24 * 60 * 60 * 1000)
42
+ return;
43
+ await fs.promises.unlink(markerPath);
44
+ }
45
+ catch (error) {
46
+ if (error.code !== "ENOENT")
47
+ return;
48
+ }
49
+ const maxAgeMs = maxAgeDays * 24 * 60 * 60 * 1000;
50
+ const cutoff = now - maxAgeMs;
51
+ for (const file of await fs.promises.readdir(dir)) {
52
+ if (file === CLEANUP_MARKER_FILE)
53
+ continue;
54
+ const filePath = path.join(dir, file);
55
+ try {
56
+ const stat = await fs.promises.lstat(filePath);
57
+ if (stat.mtimeMs < cutoff) {
58
+ await fs.promises.unlink(filePath);
59
+ }
60
+ }
61
+ catch {
62
+ // Artifact cleanup is best-effort housekeeping. Skip files that disappear
63
+ // or become unreadable while scanning so one bad entry does not block the rest.
64
+ }
65
+ }
66
+ try {
67
+ await fs.promises.writeFile(markerPath, String(now), { flag: "wx", mode: 0o600 });
68
+ }
69
+ catch (error) {
70
+ if (error.code !== "EEXIST")
71
+ throw error;
72
+ }
73
+ }
74
+ export async function cleanupAllArtifactDirs(maxAgeDays) {
75
+ await cleanupOldArtifacts(TEMP_ARTIFACTS_DIR, maxAgeDays);
76
+ const sessionsBase = path.join(getAgentDir(), "sessions");
77
+ let dirs;
78
+ try {
79
+ dirs = await fs.promises.readdir(sessionsBase);
80
+ }
81
+ catch {
82
+ // Session artifact cleanup is best-effort. If the sessions root cannot be read,
83
+ // skip cleanup instead of failing extension startup.
84
+ return;
85
+ }
86
+ for (const dir of dirs) {
87
+ const artifactsDir = path.join(sessionsBase, dir, "subagent-artifacts");
88
+ try {
89
+ await cleanupOldArtifacts(artifactsDir, maxAgeDays);
90
+ }
91
+ catch {
92
+ // Session cleanup is best-effort. Keep going so one unreadable session dir
93
+ // does not block cleanup for the rest.
94
+ }
95
+ }
96
+ }
@@ -0,0 +1,13 @@
1
+ import * as fs from "node:fs";
2
+ import * as path from "node:path";
3
+ export function writeAtomicJson(filePath, payload) {
4
+ fs.mkdirSync(path.dirname(filePath), { recursive: true });
5
+ const tempPath = path.join(path.dirname(filePath), `.${path.basename(filePath)}.${process.pid}.${Date.now()}.${Math.random().toString(36).slice(2)}.tmp`);
6
+ try {
7
+ fs.writeFileSync(tempPath, JSON.stringify(payload, null, 2), { encoding: "utf-8", mode: 0o600 });
8
+ fs.renameSync(tempPath, filePath);
9
+ }
10
+ finally {
11
+ fs.rmSync(tempPath, { force: true });
12
+ }
13
+ }
@@ -0,0 +1,20 @@
1
+ import { setImmediate as yieldToInput } from "node:timers/promises";
2
+ export function runSynchronously(steps) {
3
+ let step = steps.next();
4
+ while (!step.done)
5
+ step = steps.next();
6
+ return step.value;
7
+ }
8
+ /** Batched recovery and delivery leave room for input between records. */
9
+ export async function runCooperatively(steps) {
10
+ let step = steps.next();
11
+ for (let count = 0; !step.done; step = steps.next()) {
12
+ // ponytail: one recovery record remains atomic; move a demonstrated
13
+ // single-record stall off-thread rather than increasing this batch size.
14
+ if (++count === 32) {
15
+ await yieldToInput();
16
+ count = 0;
17
+ }
18
+ }
19
+ return step.value;
20
+ }
@@ -0,0 +1,14 @@
1
+ import { hasExecutionCwdOwner } from "../runs/shared/child-execution-cwd.js";
2
+ /** Capture the active directory owner once per invocation, before asynchronous preparation. */
3
+ export function resolveExecutionCwd(pi, ctx) {
4
+ const request = { sessionManager: ctx.sessionManager };
5
+ pi.events.emit("pi-change-working-dir:resolve-execution-cwd", request);
6
+ if (request.result?.error)
7
+ throw new Error(request.result.error);
8
+ if (request.result && !request.result.cwd)
9
+ throw new Error("Working-directory owner did not return an execution directory.");
10
+ if (!request.result && pi.getAllTools && pi.getCommands && hasExecutionCwdOwner({ getAllTools: pi.getAllTools, getCommands: pi.getCommands })) {
11
+ throw new Error("The loaded directory extension did not resolve the execution directory. Update pi-change-working-dir before delegating work.");
12
+ }
13
+ return request.result?.cwd ?? ctx.cwd;
14
+ }
@@ -0,0 +1,21 @@
1
+ export function createFileCoalescer(handler, defaultDelayMs) {
2
+ const pending = new Map();
3
+ return {
4
+ schedule(file, delayMs = defaultDelayMs) {
5
+ if (pending.has(file))
6
+ return false;
7
+ const timer = setTimeout(() => {
8
+ pending.delete(file);
9
+ handler(file);
10
+ }, delayMs);
11
+ pending.set(file, timer);
12
+ return true;
13
+ },
14
+ clear() {
15
+ for (const timer of pending.values()) {
16
+ clearTimeout(timer);
17
+ }
18
+ pending.clear();
19
+ },
20
+ };
21
+ }
@@ -0,0 +1,49 @@
1
+ import * as fs from "node:fs";
2
+ import { SessionManager } from "@earendil-works/pi-coding-agent";
3
+ import { requestChildExecutionCwd } from "../runs/shared/child-execution-cwd.js";
4
+ export function resolveSubagentContext(value) {
5
+ return value === "fork" ? "fork" : "fresh";
6
+ }
7
+ export function createForkContextResolver(sessionManager, requestedContext) {
8
+ if (resolveSubagentContext(requestedContext) !== "fork") {
9
+ return {
10
+ sessionFileForIndex: () => undefined,
11
+ };
12
+ }
13
+ const parentSessionFile = sessionManager.getSessionFile();
14
+ if (!parentSessionFile) {
15
+ throw new Error("Forked subagent context requires a persisted parent session.");
16
+ }
17
+ const leafId = sessionManager.getLeafId();
18
+ if (!leafId) {
19
+ throw new Error("Forked subagent context requires a current leaf to fork from.");
20
+ }
21
+ const sessionDir = sessionManager.getSessionDir();
22
+ const cachedSessionFiles = new Map();
23
+ return {
24
+ sessionFileForIndex(index = 0) {
25
+ const cached = cachedSessionFiles.get(index);
26
+ if (cached)
27
+ return cached;
28
+ try {
29
+ if (!fs.existsSync(parentSessionFile)) {
30
+ throw new Error(`Parent session file does not exist: ${parentSessionFile}. Pi has not persisted enough history to fork yet.`);
31
+ }
32
+ const sessionFile = SessionManager.open(parentSessionFile, sessionDir).createBranchedSession(leafId);
33
+ if (!sessionFile) {
34
+ throw new Error("Session manager did not return a forked session file.");
35
+ }
36
+ if (!fs.existsSync(sessionFile)) {
37
+ throw new Error(`Session manager returned a forked session file that does not exist: ${sessionFile}`);
38
+ }
39
+ requestChildExecutionCwd(sessionFile);
40
+ cachedSessionFiles.set(index, sessionFile);
41
+ return sessionFile;
42
+ }
43
+ catch (error) {
44
+ const cause = error instanceof Error ? error : new Error(String(error));
45
+ throw new Error(`Failed to create forked subagent session: ${cause.message}`, { cause });
46
+ }
47
+ },
48
+ };
49
+ }
@@ -0,0 +1,85 @@
1
+ /**
2
+ * Formatting utilities for display output
3
+ */
4
+ import { splitKnownThinkingSuffix, THINKING_LEVELS } from "./model-info.js";
5
+ /**
6
+ * Format token count with k suffix for large numbers
7
+ */
8
+ export function formatTokens(n) {
9
+ return n < 1000 ? String(n) : n < 10000 ? `${(n / 1000).toFixed(1)}k` : `${Math.round(n / 1000)}k`;
10
+ }
11
+ export function formatModelThinking(model, thinking) {
12
+ const parsed = model ? splitKnownThinkingSuffix(model) : undefined;
13
+ const displayModel = parsed?.baseModel ?? model;
14
+ const explicitThinking = THINKING_LEVELS.find((level) => level === thinking?.trim());
15
+ const displayThinking = parsed?.thinkingSuffix ? parsed.thinkingSuffix.slice(1) : explicitThinking;
16
+ return [displayModel, displayThinking ? `thinking ${displayThinking}` : undefined].filter(Boolean).join(" · ");
17
+ }
18
+ /**
19
+ * Format usage statistics into a compact string
20
+ */
21
+ export function formatUsage(u, model) {
22
+ const parts = [];
23
+ if (u.turns)
24
+ parts.push(`${u.turns} turn${u.turns > 1 ? "s" : ""}`);
25
+ if (u.input)
26
+ parts.push(`in:${formatTokens(u.input)}`);
27
+ if (u.output)
28
+ parts.push(`out:${formatTokens(u.output)}`);
29
+ if (u.cacheRead)
30
+ parts.push(`R${formatTokens(u.cacheRead)}`);
31
+ if (u.cacheWrite)
32
+ parts.push(`W${formatTokens(u.cacheWrite)}`);
33
+ if (u.cost)
34
+ parts.push(`$${u.cost.toFixed(4)}`);
35
+ if (model)
36
+ parts.push(model);
37
+ return parts.join(" ");
38
+ }
39
+ /**
40
+ * Format duration in human-readable form
41
+ */
42
+ export function formatDuration(ms) {
43
+ if (ms < 1000)
44
+ return `${ms}ms`;
45
+ if (ms < 60000)
46
+ return `${(ms / 1000).toFixed(1)}s`;
47
+ return `${Math.floor(ms / 60000)}m${Math.floor((ms % 60000) / 1000)}s`;
48
+ }
49
+ /**
50
+ * Format a tool call for display
51
+ */
52
+ export function formatToolCall(name, args, expanded = false) {
53
+ switch (name) {
54
+ case "bash": {
55
+ const command = typeof args.command === "string" ? args.command : "";
56
+ const maxLength = expanded ? 240 : 60;
57
+ return `$ ${command.slice(0, maxLength)}${command.length > maxLength ? "..." : ""}`;
58
+ }
59
+ case "read":
60
+ case "write":
61
+ case "edit": {
62
+ const target = typeof args.path === "string"
63
+ ? args.path
64
+ : typeof args.file_path === "string"
65
+ ? args.file_path
66
+ : "";
67
+ return `${name} ${shortenPath(target)}`;
68
+ }
69
+ default: {
70
+ const s = JSON.stringify(args);
71
+ const maxLength = expanded ? 160 : 40;
72
+ return `${name} ${s.slice(0, maxLength)}${s.length > maxLength ? "..." : ""}`;
73
+ }
74
+ }
75
+ }
76
+ /**
77
+ * Shorten a path by replacing home directory with ~
78
+ */
79
+ export function shortenPath(p) {
80
+ const home = process.env.HOME;
81
+ if (home && p.startsWith(home)) {
82
+ return `~${p.slice(home.length)}`;
83
+ }
84
+ return p;
85
+ }