@fitchmultz/pi-subagents 0.0.0-stage → 0.44.4

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 (342) hide show
  1. package/CHANGELOG.md +1645 -0
  2. package/LICENSE +21 -0
  3. package/README.md +1529 -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 +194 -0
  43. package/dist/history/ingest.js +447 -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 +461 -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/broker/broker.js +426 -0
  53. package/dist/pi-intercom/broker/client.js +492 -0
  54. package/dist/pi-intercom/broker/framing.js +65 -0
  55. package/dist/pi-intercom/broker/paths.js +42 -0
  56. package/dist/pi-intercom/broker/pid.js +66 -0
  57. package/dist/pi-intercom/broker/spawn.js +221 -0
  58. package/dist/pi-intercom/config.js +102 -0
  59. package/dist/pi-intercom/index.js +2942 -0
  60. package/dist/pi-intercom/reply-tracker.js +182 -0
  61. package/dist/pi-intercom/session-targets.js +128 -0
  62. package/dist/pi-intercom/subagent-live-events.js +132 -0
  63. package/dist/pi-intercom/topics.js +157 -0
  64. package/dist/pi-intercom/types.js +163 -0
  65. package/dist/pi-intercom/ui/compose.js +269 -0
  66. package/dist/pi-intercom/ui/inline-message.js +80 -0
  67. package/dist/pi-intercom/ui/session-list.js +124 -0
  68. package/dist/runs/background/async-control.js +95 -0
  69. package/dist/runs/background/async-execution.js +676 -0
  70. package/dist/runs/background/async-job-tracker.js +383 -0
  71. package/dist/runs/background/async-result-file.js +58 -0
  72. package/dist/runs/background/async-resume.js +305 -0
  73. package/dist/runs/background/async-status.js +442 -0
  74. package/dist/runs/background/completion-dedupe.js +69 -0
  75. package/dist/runs/background/completion-delivery.js +245 -0
  76. package/dist/runs/background/notify.js +71 -0
  77. package/dist/runs/background/parallel-groups.js +55 -0
  78. package/dist/runs/background/result-watcher.js +426 -0
  79. package/dist/runs/background/run-id-resolver.js +64 -0
  80. package/dist/runs/background/run-status.js +382 -0
  81. package/dist/runs/background/stale-run-reconciler.js +335 -0
  82. package/dist/runs/background/subagent-runner-launcher.js +55 -0
  83. package/dist/runs/background/subagent-runner.js +1872 -0
  84. package/dist/runs/background/top-level-async.js +5 -0
  85. package/dist/runs/foreground/chain-clarify.js +1152 -0
  86. package/dist/runs/foreground/clarify-invocation.js +72 -0
  87. package/dist/runs/foreground/execution-input.js +423 -0
  88. package/dist/runs/foreground/foreground-control.js +817 -0
  89. package/dist/runs/foreground/question-control.js +77 -0
  90. package/dist/runs/foreground/run-async-path.js +205 -0
  91. package/dist/runs/foreground/subagent-executor.js +442 -0
  92. package/dist/runs/foreground/subagent-params.js +165 -0
  93. package/dist/runs/foreground/wait-run.js +139 -0
  94. package/dist/runs/shared/acceptance-contract.js +253 -0
  95. package/dist/runs/shared/acceptance-evaluation.js +256 -0
  96. package/dist/runs/shared/acceptance-finalization.js +280 -0
  97. package/dist/runs/shared/acceptance-reports.js +147 -0
  98. package/dist/runs/shared/acceptance.js +4 -0
  99. package/dist/runs/shared/chain-outputs.js +110 -0
  100. package/dist/runs/shared/child-attempt.js +693 -0
  101. package/dist/runs/shared/child-execution-cwd.js +96 -0
  102. package/dist/runs/shared/child-observations.js +114 -0
  103. package/dist/runs/shared/claude-code.js +199 -0
  104. package/dist/runs/shared/completion-guard.js +31 -0
  105. package/dist/runs/shared/dynamic-fanout.js +274 -0
  106. package/dist/runs/shared/history-index.js +72 -0
  107. package/dist/runs/shared/mcp-direct-tool-allowlist.js +334 -0
  108. package/dist/runs/shared/model-fallback.js +185 -0
  109. package/dist/runs/shared/mutating-tool-guard.js +170 -0
  110. package/dist/runs/shared/native-finalization.js +101 -0
  111. package/dist/runs/shared/native-usage.js +153 -0
  112. package/dist/runs/shared/nested-events.js +700 -0
  113. package/dist/runs/shared/nested-path.js +48 -0
  114. package/dist/runs/shared/nested-render.js +108 -0
  115. package/dist/runs/shared/parallel-utils.js +61 -0
  116. package/dist/runs/shared/parent-receipts.js +68 -0
  117. package/dist/runs/shared/parent-usage.js +121 -0
  118. package/dist/runs/shared/pi-args.js +310 -0
  119. package/dist/runs/shared/pi-spawn.js +52 -0
  120. package/dist/runs/shared/run-history-queries.js +45 -0
  121. package/dist/runs/shared/run-history.js +103 -0
  122. package/dist/runs/shared/run-id-ambiguity.js +4 -0
  123. package/dist/runs/shared/run-records.js +679 -0
  124. package/dist/runs/shared/session-cwd-preload.js +33 -0
  125. package/dist/runs/shared/single-output.js +222 -0
  126. package/dist/runs/shared/streaming-text.js +15 -0
  127. package/dist/runs/shared/structured-output.js +55 -0
  128. package/dist/runs/shared/subagent-control.js +179 -0
  129. package/dist/runs/shared/subagent-prompt-runtime.js +197 -0
  130. package/dist/runs/shared/subagent-tool-loop-guard.js +66 -0
  131. package/dist/runs/shared/supervisor-questions.js +320 -0
  132. package/dist/runs/shared/workflow-graph.js +214 -0
  133. package/dist/runs/shared/workflow-policy.js +84 -0
  134. package/dist/runs/shared/worktree.js +525 -0
  135. package/dist/shared/agent-context-policy.js +126 -0
  136. package/dist/shared/artifacts.js +96 -0
  137. package/dist/shared/atomic-json.js +13 -0
  138. package/dist/shared/cooperative.js +20 -0
  139. package/dist/shared/execution-cwd.js +14 -0
  140. package/dist/shared/file-coalescer.js +21 -0
  141. package/dist/shared/fork-context.js +49 -0
  142. package/dist/shared/formatters.js +85 -0
  143. package/dist/shared/journal-reader.js +407 -0
  144. package/dist/shared/lazy-tools.js +32 -0
  145. package/dist/shared/model-info.js +68 -0
  146. package/dist/shared/native-session.js +15 -0
  147. package/dist/shared/native-tui.js +18 -0
  148. package/dist/shared/native-typebox.js +20 -0
  149. package/dist/shared/post-exit-stdio-guard.js +197 -0
  150. package/dist/shared/prompt-sections.js +8 -0
  151. package/dist/shared/session-entries.js +52 -0
  152. package/dist/shared/session-identity.js +14 -0
  153. package/dist/shared/settings.js +254 -0
  154. package/dist/shared/status-format.js +111 -0
  155. package/dist/shared/temp-root.js +114 -0
  156. package/dist/shared/types.js +207 -0
  157. package/dist/shared/ui-mode.js +3 -0
  158. package/dist/shared/utils.js +334 -0
  159. package/dist/slash/prompt-template-bridge.js +298 -0
  160. package/dist/slash/slash-bridge.js +116 -0
  161. package/dist/slash/slash-commands.js +607 -0
  162. package/dist/slash/slash-live-state.js +246 -0
  163. package/dist/tui/action-hints.js +75 -0
  164. package/dist/tui/agent-history.js +197 -0
  165. package/dist/tui/agent-view.js +1656 -0
  166. package/dist/tui/render.js +1515 -0
  167. package/docs/intercom.md +579 -0
  168. package/docs/performance.md +121 -0
  169. package/package.json +101 -4
  170. package/prompts/gather-context-and-clarify.md +14 -0
  171. package/prompts/parallel-cleanup.md +62 -0
  172. package/prompts/parallel-context-build.md +56 -0
  173. package/prompts/parallel-handoff-plan.md +62 -0
  174. package/prompts/parallel-research.md +51 -0
  175. package/prompts/parallel-review.md +59 -0
  176. package/prompts/review-loop.md +42 -0
  177. package/scripts/build.mjs +134 -0
  178. package/scripts/check-compat.mjs +48 -0
  179. package/scripts/compat-host.mjs +19 -0
  180. package/scripts/linux-smoke.sh +51 -0
  181. package/scripts/local-install-smoke.mjs +66 -0
  182. package/scripts/native-package-smoke.mjs +82 -0
  183. package/scripts/package-smoke.mjs +205 -0
  184. package/scripts/prepare.mjs +67 -0
  185. package/scripts/real-pi-smoke.mjs +415 -0
  186. package/scripts/run-tests.mjs +149 -0
  187. package/skills/pi-intercom/SKILL.md +113 -0
  188. package/skills/pi-intercom/evals/evals.json +29 -0
  189. package/skills/pi-intercom/evals/trigger-evals.json +47 -0
  190. package/skills/pi-intercom/references/peer-sessions.md +69 -0
  191. package/skills/pi-subagents/SKILL.md +76 -0
  192. package/skills/pi-subagents/evals/evals.json +29 -0
  193. package/skills/pi-subagents/evals/trigger-evals.json +42 -0
  194. package/skills/pi-subagents/references/full-orchestration-guide.md +863 -0
  195. package/src/agents/agent-management.ts +813 -0
  196. package/src/agents/agent-scope.ts +7 -0
  197. package/src/agents/agent-selection.ts +23 -0
  198. package/src/agents/agent-serializer.ts +99 -0
  199. package/src/agents/agents.ts +916 -0
  200. package/src/agents/chain-serializer.ts +329 -0
  201. package/src/agents/frontmatter.ts +21 -0
  202. package/src/agents/identity.ts +30 -0
  203. package/src/agents/skills.ts +637 -0
  204. package/src/extension/build-info.ts +2 -0
  205. package/src/extension/compact-tools.ts +114 -0
  206. package/src/extension/config.ts +23 -0
  207. package/src/extension/control-notices.ts +47 -0
  208. package/src/extension/doctor.ts +171 -0
  209. package/src/extension/fanout-child.ts +312 -0
  210. package/src/extension/index.ts +649 -0
  211. package/src/extension/schemas.ts +444 -0
  212. package/src/extension/tool-input.ts +19 -0
  213. package/src/extension/tool-result.ts +16 -0
  214. package/src/history/canonical-result.ts +62 -0
  215. package/src/history/database.ts +59 -0
  216. package/src/history/index.ts +152 -0
  217. package/src/history/ingest.ts +332 -0
  218. package/src/history/queries.ts +241 -0
  219. package/src/history/store.ts +203 -0
  220. package/src/history/types.ts +166 -0
  221. package/src/history/worker.ts +317 -0
  222. package/src/intercom/intercom-bridge.ts +123 -0
  223. package/src/intercom/live-intercom.ts +104 -0
  224. package/src/intercom/result-intercom.ts +429 -0
  225. package/src/pi-intercom/agent-dir.ts +9 -0
  226. package/src/pi-intercom/broker/broker.ts +463 -0
  227. package/src/pi-intercom/broker/client.ts +558 -0
  228. package/src/pi-intercom/broker/framing.ts +77 -0
  229. package/src/pi-intercom/broker/paths.ts +47 -0
  230. package/src/pi-intercom/broker/pid.ts +59 -0
  231. package/src/pi-intercom/broker/spawn.ts +248 -0
  232. package/src/pi-intercom/config.ts +142 -0
  233. package/src/pi-intercom/index.ts +2929 -0
  234. package/src/pi-intercom/reply-tracker.ts +211 -0
  235. package/src/pi-intercom/session-targets.ts +172 -0
  236. package/src/pi-intercom/subagent-live-events.ts +133 -0
  237. package/src/pi-intercom/topics.ts +125 -0
  238. package/src/pi-intercom/types.ts +271 -0
  239. package/src/pi-intercom/ui/compose.ts +289 -0
  240. package/src/pi-intercom/ui/inline-message.ts +92 -0
  241. package/src/pi-intercom/ui/session-list.ts +139 -0
  242. package/src/runs/background/async-control.ts +89 -0
  243. package/src/runs/background/async-execution.ts +850 -0
  244. package/src/runs/background/async-job-tracker.ts +385 -0
  245. package/src/runs/background/async-result-file.ts +62 -0
  246. package/src/runs/background/async-resume.ts +333 -0
  247. package/src/runs/background/async-status.ts +481 -0
  248. package/src/runs/background/completion-dedupe.ts +79 -0
  249. package/src/runs/background/completion-delivery.ts +195 -0
  250. package/src/runs/background/notify.ts +123 -0
  251. package/src/runs/background/parallel-groups.ts +53 -0
  252. package/src/runs/background/result-watcher.ts +423 -0
  253. package/src/runs/background/run-id-resolver.ts +69 -0
  254. package/src/runs/background/run-status.ts +384 -0
  255. package/src/runs/background/stale-run-reconciler.ts +369 -0
  256. package/src/runs/background/subagent-runner-launcher.ts +56 -0
  257. package/src/runs/background/subagent-runner.ts +2053 -0
  258. package/src/runs/background/top-level-async.ts +13 -0
  259. package/src/runs/foreground/chain-clarify.ts +1340 -0
  260. package/src/runs/foreground/clarify-invocation.ts +77 -0
  261. package/src/runs/foreground/execution-input.ts +467 -0
  262. package/src/runs/foreground/foreground-control.ts +821 -0
  263. package/src/runs/foreground/question-control.ts +70 -0
  264. package/src/runs/foreground/run-async-path.ts +243 -0
  265. package/src/runs/foreground/subagent-executor.ts +501 -0
  266. package/src/runs/foreground/subagent-params.ts +320 -0
  267. package/src/runs/foreground/wait-run.ts +121 -0
  268. package/src/runs/shared/acceptance-contract.ts +257 -0
  269. package/src/runs/shared/acceptance-evaluation.ts +275 -0
  270. package/src/runs/shared/acceptance-finalization.ts +353 -0
  271. package/src/runs/shared/acceptance-reports.ts +152 -0
  272. package/src/runs/shared/acceptance.ts +33 -0
  273. package/src/runs/shared/chain-outputs.ts +114 -0
  274. package/src/runs/shared/child-attempt.ts +623 -0
  275. package/src/runs/shared/child-execution-cwd.ts +91 -0
  276. package/src/runs/shared/child-observations.ts +99 -0
  277. package/src/runs/shared/claude-code.ts +243 -0
  278. package/src/runs/shared/completion-guard.ts +35 -0
  279. package/src/runs/shared/dynamic-fanout.ts +295 -0
  280. package/src/runs/shared/history-index.ts +69 -0
  281. package/src/runs/shared/mcp-direct-tool-allowlist.ts +376 -0
  282. package/src/runs/shared/model-fallback.ts +219 -0
  283. package/src/runs/shared/mutating-tool-guard.ts +194 -0
  284. package/src/runs/shared/native-finalization.ts +128 -0
  285. package/src/runs/shared/native-usage.ts +113 -0
  286. package/src/runs/shared/nested-events.ts +752 -0
  287. package/src/runs/shared/nested-path.ts +52 -0
  288. package/src/runs/shared/nested-render.ts +119 -0
  289. package/src/runs/shared/parallel-utils.ts +153 -0
  290. package/src/runs/shared/parent-receipts.ts +59 -0
  291. package/src/runs/shared/parent-usage.ts +121 -0
  292. package/src/runs/shared/pi-args.ts +350 -0
  293. package/src/runs/shared/pi-spawn.ts +52 -0
  294. package/src/runs/shared/run-history-queries.ts +44 -0
  295. package/src/runs/shared/run-history.ts +93 -0
  296. package/src/runs/shared/run-id-ambiguity.ts +4 -0
  297. package/src/runs/shared/run-records.ts +607 -0
  298. package/src/runs/shared/session-cwd-preload.ts +27 -0
  299. package/src/runs/shared/single-output.ts +267 -0
  300. package/src/runs/shared/streaming-text.ts +18 -0
  301. package/src/runs/shared/structured-output.ts +68 -0
  302. package/src/runs/shared/subagent-control.ts +219 -0
  303. package/src/runs/shared/subagent-prompt-runtime.ts +201 -0
  304. package/src/runs/shared/subagent-tool-loop-guard.ts +89 -0
  305. package/src/runs/shared/supervisor-questions.ts +372 -0
  306. package/src/runs/shared/workflow-graph.ts +220 -0
  307. package/src/runs/shared/workflow-policy.ts +125 -0
  308. package/src/runs/shared/worktree.ts +635 -0
  309. package/src/shared/agent-context-policy.ts +184 -0
  310. package/src/shared/artifacts.ts +95 -0
  311. package/src/shared/atomic-json.ts +16 -0
  312. package/src/shared/cooperative.ts +21 -0
  313. package/src/shared/execution-cwd.ts +14 -0
  314. package/src/shared/file-coalescer.ts +29 -0
  315. package/src/shared/fork-context.ts +68 -0
  316. package/src/shared/formatters.ts +84 -0
  317. package/src/shared/journal-reader.ts +333 -0
  318. package/src/shared/lazy-tools.ts +36 -0
  319. package/src/shared/model-info.ts +81 -0
  320. package/src/shared/native-session.ts +8 -0
  321. package/src/shared/native-tui.ts +10 -0
  322. package/src/shared/native-typebox.ts +14 -0
  323. package/src/shared/post-exit-stdio-guard.ts +204 -0
  324. package/src/shared/prompt-sections.ts +10 -0
  325. package/src/shared/session-entries.ts +49 -0
  326. package/src/shared/session-identity.ts +18 -0
  327. package/src/shared/settings.ts +384 -0
  328. package/src/shared/status-format.ts +123 -0
  329. package/src/shared/temp-root.ts +107 -0
  330. package/src/shared/types.ts +1259 -0
  331. package/src/shared/ui-mode.ts +5 -0
  332. package/src/shared/utils.ts +342 -0
  333. package/src/slash/prompt-template-bridge.ts +408 -0
  334. package/src/slash/slash-bridge.ts +178 -0
  335. package/src/slash/slash-commands.ts +617 -0
  336. package/src/slash/slash-live-state.ts +292 -0
  337. package/src/tui/action-hints.ts +69 -0
  338. package/src/tui/agent-history.ts +210 -0
  339. package/src/tui/agent-view.ts +1323 -0
  340. package/src/tui/render.ts +1550 -0
  341. package/tsconfig.build.json +12 -0
  342. package/tsconfig.json +19 -0
@@ -0,0 +1,185 @@
1
+ export function splitThinkingSuffix(model) {
2
+ const colonIdx = model.lastIndexOf(":");
3
+ if (colonIdx === -1)
4
+ return { baseModel: model, thinkingSuffix: "" };
5
+ return {
6
+ baseModel: model.substring(0, colonIdx),
7
+ thinkingSuffix: model.substring(colonIdx),
8
+ };
9
+ }
10
+ export function resolveModelCandidate(model, availableModels, preferredProvider) {
11
+ if (!model)
12
+ return undefined;
13
+ if (model.includes("/"))
14
+ return model;
15
+ if (!availableModels || availableModels.length === 0)
16
+ return model;
17
+ const { baseModel, thinkingSuffix } = splitThinkingSuffix(model);
18
+ const matches = availableModels.filter((entry) => entry.id === baseModel);
19
+ if (preferredProvider) {
20
+ const preferredMatch = matches.find((entry) => entry.provider === preferredProvider);
21
+ if (preferredMatch)
22
+ return `${preferredMatch.fullId}${thinkingSuffix}`;
23
+ }
24
+ if (matches.length !== 1)
25
+ return model;
26
+ return `${matches[0].fullId}${thinkingSuffix}`;
27
+ }
28
+ export function buildModelCandidates(primaryModel, fallbackModels, availableModels, preferredProvider) {
29
+ const seen = new Set();
30
+ const candidates = [];
31
+ for (const raw of [primaryModel, ...(fallbackModels ?? [])]) {
32
+ if (!raw)
33
+ continue;
34
+ const normalized = resolveModelCandidate(raw.trim(), availableModels, preferredProvider);
35
+ if (!normalized || seen.has(normalized))
36
+ continue;
37
+ seen.add(normalized);
38
+ candidates.push(normalized);
39
+ }
40
+ return candidates;
41
+ }
42
+ const RETRYABLE_MODEL_FAILURE_PATTERNS = [
43
+ /rate\s*limit/i,
44
+ /usage\s*limit/i,
45
+ /too many requests/i,
46
+ /\b429\b/,
47
+ /quota/i,
48
+ /billing/i,
49
+ /credit/i,
50
+ /auth(?:entication)?/i,
51
+ /unauthori[sz]ed/i,
52
+ /forbidden/i,
53
+ /api key/i,
54
+ /token expired/i,
55
+ /invalid key/i,
56
+ /provider.*unavailable/i,
57
+ /model.*unavailable/i,
58
+ /model.*disabled/i,
59
+ /model.*not found/i,
60
+ /unknown model/i,
61
+ /overloaded/i,
62
+ /service unavailable/i,
63
+ /temporar(?:ily)? unavailable/i,
64
+ /connection refused/i,
65
+ /database is locked/i,
66
+ /\bsqlite_busy\b/i,
67
+ /fetch failed/i,
68
+ /network error/i,
69
+ /socket hang up/i,
70
+ /upstream/i,
71
+ /timed? out/i,
72
+ /timeout/i,
73
+ /\b502\b/,
74
+ /\b503\b/,
75
+ /\b504\b/,
76
+ ];
77
+ export function isRetryableModelFailure(error) {
78
+ if (!error)
79
+ return false;
80
+ return RETRYABLE_MODEL_FAILURE_PATTERNS.some((pattern) => pattern.test(error));
81
+ }
82
+ const NON_RECOVERABLE_SAME_MODEL_PATTERNS = [
83
+ /usage\s*limit/i,
84
+ /quota/i,
85
+ /billing/i,
86
+ /credit/i,
87
+ /auth(?:entication)?/i,
88
+ /unauthori[sz]ed/i,
89
+ /forbidden/i,
90
+ /api key/i,
91
+ /token expired/i,
92
+ /invalid key/i,
93
+ /model.*disabled/i,
94
+ /model.*not found/i,
95
+ /unknown model/i,
96
+ ];
97
+ const SAME_MODEL_RECOVERY_PATTERNS = [
98
+ /web\s*socket/i,
99
+ /websocket/i,
100
+ /transport/i,
101
+ /stream/i,
102
+ /socket/i,
103
+ /connection/i,
104
+ /fetch failed/i,
105
+ /network/i,
106
+ /http idle/i,
107
+ /timed? out/i,
108
+ /timeout/i,
109
+ /overloaded/i,
110
+ /service unavailable/i,
111
+ /temporar(?:ily)? unavailable/i,
112
+ /connection refused/i,
113
+ /database is locked/i,
114
+ /\bsqlite_busy\b/i,
115
+ /econnreset/i,
116
+ /etimedout/i,
117
+ /\b502\b/,
118
+ /\b503\b/,
119
+ /\b504\b/,
120
+ ];
121
+ export function isRecoverableSameModelFailure(error, exitCode) {
122
+ if (exitCode === 143)
123
+ return true;
124
+ if (!error)
125
+ return false;
126
+ if (NON_RECOVERABLE_SAME_MODEL_PATTERNS.some((pattern) => pattern.test(error)))
127
+ return false;
128
+ return SAME_MODEL_RECOVERY_PATTERNS.some((pattern) => pattern.test(error));
129
+ }
130
+ export function formatModelAttemptNote(attempt, nextModel) {
131
+ const failure = attempt.error?.trim() || `exit ${attempt.exitCode ?? 1}`;
132
+ return nextModel
133
+ ? `[fallback] ${attempt.model} failed: ${failure}. Retrying with ${nextModel}.`
134
+ : `[fallback] ${attempt.model} failed: ${failure}.`;
135
+ }
136
+ export function formatModelRecoveryAttemptNote(attempt, retryNumber, maxRetries) {
137
+ const failure = attempt.error?.trim() || `exit ${attempt.exitCode ?? 1}`;
138
+ return `[retry] ${attempt.model} failed: ${failure}. Retrying same model (${retryNumber}/${maxRetries}).`;
139
+ }
140
+ export function sumAttemptUsage(attempts) {
141
+ const usage = { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, cost: 0, turns: 0 };
142
+ for (const attempt of attempts) {
143
+ for (const key of ["input", "output", "cacheRead", "cacheWrite", "cost", "turns"])
144
+ usage[key] += attempt.usage?.[key] ?? 0;
145
+ if (attempt.usage?.contributions?.length)
146
+ (usage.contributions ??= []).push(...attempt.usage.contributions);
147
+ }
148
+ return usage;
149
+ }
150
+ export async function runModelAttempts(input) {
151
+ const candidates = input.candidates.length ? input.candidates : [undefined];
152
+ const modelAttempts = [];
153
+ const attemptedModels = [];
154
+ const notes = [];
155
+ let result;
156
+ modelLoop: for (let index = 0; index < candidates.length; index++) {
157
+ const model = candidates[index];
158
+ for (let recovery = 0;; recovery++) {
159
+ result = await input.runAttempt(model, notes);
160
+ if (model)
161
+ attemptedModels.push(model);
162
+ const attempt = {
163
+ model: model ?? result.model ?? "default",
164
+ success: result.exitCode === 0 && !result.error && !result.interrupted,
165
+ exitCode: result.exitCode,
166
+ error: result.error,
167
+ accounting: result.accounting,
168
+ usage: { ...result.usage },
169
+ };
170
+ modelAttempts.push(attempt);
171
+ if (attempt.success || input.signal?.aborted || result.interrupted || result.timedOut || result.resourceLimitExceeded || result.terminalFailure)
172
+ break modelLoop;
173
+ const recoverable = isRecoverableSameModelFailure(result.error, result.exitCode);
174
+ if (recoverable && recovery === 0) {
175
+ notes.push(formatModelRecoveryAttemptNote(attempt, 1, 1));
176
+ continue;
177
+ }
178
+ if ((!recoverable && !isRetryableModelFailure(result.error)) || index === candidates.length - 1)
179
+ break modelLoop;
180
+ notes.push(formatModelAttemptNote(attempt, candidates[index + 1]));
181
+ break;
182
+ }
183
+ }
184
+ return { result, modelAttempts, attemptedModels, notes, usage: sumAttemptUsage(modelAttempts) };
185
+ }
@@ -0,0 +1,170 @@
1
+ const MUTATING_BASH_PATTERNS = [
2
+ /(^|[;&|()\s])rm\s+/,
3
+ /(^|[;&|()\s])mv\s+/,
4
+ /(^|[;&|()\s])cp\s+/,
5
+ /(^|[;&|()\s])mkdir\s+/,
6
+ /(^|[;&|()\s])touch\s+/,
7
+ /(^|[;&|()\s])git\s+apply\b/,
8
+ /(^|[;&|()\s])patch\s+/,
9
+ /(^|[;&|()\s])sed\s+[^\n;&|]*\s-i\b/,
10
+ /(^|[;&|()\s])perl\s+[^\n;&|]*\s-pi\b/,
11
+ /(^|[;&|()]|\n)\s*tee\s+[^|&;]+/,
12
+ /\b(writeFile|writeFileSync|appendFile|appendFileSync)\b/,
13
+ /\bwrite_text\s*\(/,
14
+ /\bopen\s*\([^)]*,\s*["'][wa]/,
15
+ ];
16
+ export function resolveCurrentPath(toolName, args) {
17
+ if (!toolName || !args)
18
+ return undefined;
19
+ const direct = ["path", "file", "filename", "target", "cwd"];
20
+ for (const key of direct) {
21
+ const value = args[key];
22
+ if (typeof value === "string" && value.trim())
23
+ return value.trim();
24
+ }
25
+ if (toolName === "bash") {
26
+ const command = typeof args.command === "string" ? args.command : undefined;
27
+ if (!command)
28
+ return undefined;
29
+ const redirect = command.match(/(?:>>?|tee\s+)\s*([^&>\s][^\s;&|]*)/);
30
+ if (redirect?.[1])
31
+ return redirect[1];
32
+ }
33
+ return undefined;
34
+ }
35
+ function hasUnquotedFileRedirection(command) {
36
+ let inSingle = false;
37
+ let inDouble = false;
38
+ for (let i = 0; i < command.length; i++) {
39
+ const char = command[i];
40
+ if (char === "'" && !inDouble) {
41
+ inSingle = !inSingle;
42
+ continue;
43
+ }
44
+ if (char === '"' && !inSingle) {
45
+ inDouble = !inDouble;
46
+ continue;
47
+ }
48
+ if (inSingle || inDouble)
49
+ continue;
50
+ if (char !== ">")
51
+ continue;
52
+ if (command[i - 1] === "-")
53
+ continue;
54
+ const isDouble = command[i + 1] === ">";
55
+ let cursor = i + (isDouble ? 2 : 1);
56
+ while (cursor < command.length && /\s/.test(command[cursor]))
57
+ cursor++;
58
+ if (cursor >= command.length)
59
+ continue;
60
+ const targetStart = command[cursor];
61
+ if (targetStart === "&" || targetStart === "|" || targetStart === ";")
62
+ continue;
63
+ if (targetStart === "(" || targetStart === ")")
64
+ continue;
65
+ return true;
66
+ }
67
+ return false;
68
+ }
69
+ export function isMutatingBashCommand(command) {
70
+ return hasUnquotedFileRedirection(command) || MUTATING_BASH_PATTERNS.some((pattern) => pattern.test(command));
71
+ }
72
+ export function isMutatingTool(toolName, args) {
73
+ if (!toolName || args?.preview === true)
74
+ return false;
75
+ if (["edit", "write", "apply_edits", "apply_patch", "replace_text", "write_files"].includes(toolName))
76
+ return true;
77
+ if (toolName !== "bash")
78
+ return false;
79
+ const command = typeof args?.command === "string" ? args.command : "";
80
+ if (!command.trim())
81
+ return false;
82
+ return isMutatingBashCommand(command);
83
+ }
84
+ export function createMutationCompletionTracker() {
85
+ const pending = [];
86
+ const take = (input) => {
87
+ const id = typeof input?.toolCallId === "string" ? input.toolCallId : undefined;
88
+ if (id) {
89
+ const index = pending.findIndex((tool) => tool.id === id);
90
+ if (index >= 0)
91
+ return pending.splice(index, 1)[0];
92
+ }
93
+ const name = typeof input?.toolName === "string" ? input.toolName : undefined;
94
+ if (name) {
95
+ const index = pending.findIndex((tool) => tool.tool === name);
96
+ if (index >= 0)
97
+ return pending.splice(index, 1)[0];
98
+ }
99
+ return pending.shift();
100
+ };
101
+ return {
102
+ recordToolStart(input) {
103
+ const tool = input.toolName ?? "tool";
104
+ const entry = {
105
+ id: input.id,
106
+ tool,
107
+ path: input.path,
108
+ mutates: input.mutates ?? isMutatingTool(input.toolName, input.args),
109
+ startedAt: input.startedAt,
110
+ };
111
+ pending.push(entry);
112
+ return entry;
113
+ },
114
+ recordToolResult(input) {
115
+ const entry = take(input);
116
+ if (!entry)
117
+ return undefined;
118
+ const errored = input?.isError === true;
119
+ const details = input?.details && typeof input.details === "object" ? input.details : undefined;
120
+ const committed = Array.isArray(details?.modifiedFiles)
121
+ ? details.modifiedFiles.some((file) => typeof file === "string" && file.trim().length > 0)
122
+ : !errored && !["apply_patch", "replace_text", "write_files"].includes(entry.tool);
123
+ return { ...entry, errored, completedMutation: entry.mutates && details?.preview !== true && committed };
124
+ },
125
+ };
126
+ }
127
+ export function resetMutatingFailureState(state) {
128
+ state.consecutiveFailures = 0;
129
+ state.lastFailureAt = undefined;
130
+ state.recentFailures = [];
131
+ state.lastMutatingPath = undefined;
132
+ state.repeatedPathFailures = 0;
133
+ }
134
+ export function createMutatingFailureState() {
135
+ return {
136
+ consecutiveFailures: 0,
137
+ recentFailures: [],
138
+ repeatedPathFailures: 0,
139
+ };
140
+ }
141
+ export function recordMutatingFailure(state, input, windowMs) {
142
+ if (state.lastFailureAt === undefined || input.ts - state.lastFailureAt > windowMs) {
143
+ state.consecutiveFailures = 0;
144
+ state.recentFailures = [];
145
+ state.repeatedPathFailures = 0;
146
+ state.lastMutatingPath = undefined;
147
+ }
148
+ state.lastFailureAt = input.ts;
149
+ state.consecutiveFailures += 1;
150
+ if (input.path && state.lastMutatingPath === input.path) {
151
+ state.repeatedPathFailures += 1;
152
+ }
153
+ else if (input.path) {
154
+ state.lastMutatingPath = input.path;
155
+ state.repeatedPathFailures = 1;
156
+ }
157
+ state.recentFailures.push(input);
158
+ if (state.recentFailures.length > 3)
159
+ state.recentFailures.shift();
160
+ }
161
+ export function shouldEscalateMutatingFailures(state, threshold) {
162
+ return state.consecutiveFailures >= threshold || state.repeatedPathFailures >= threshold;
163
+ }
164
+ export function summarizeRecentMutatingFailures(state) {
165
+ if (state.recentFailures.length === 0)
166
+ return undefined;
167
+ return state.recentFailures
168
+ .map((entry) => `${entry.tool}${entry.path ? `(${entry.path})` : ""}: ${entry.error}`)
169
+ .join(" | ");
170
+ }
@@ -0,0 +1,101 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import * as fs from "node:fs";
3
+ import * as path from "node:path";
4
+ import { fileURLToPath } from "node:url";
5
+ import { Type } from "../../shared/native-typebox.js";
6
+ import { setPromptSection } from "../../shared/prompt-sections.js";
7
+ import { detectSubagentError, getFinalOutput } from "../../shared/utils.js";
8
+ import { acceptanceFailureMessage, acceptanceSelfReviewConfig, createFinalizationReportRuntime, evaluateAcceptance, formatAcceptanceFinalizationPrompt, readFinalizationReport, resolveFinalizationOutput, stripAcceptanceReport, } from "./acceptance.js";
9
+ import { captureSingleOutputSnapshot, resolveSingleOutput } from "./single-output.js";
10
+ import { readStructuredOutput, validateStructuredOutputValue } from "./structured-output.js";
11
+ import { compactObservedMessage } from "./child-observations.js";
12
+ const CONFIG_ENV = "PI_SUBAGENT_FINALIZATION_CONFIG";
13
+ export const FINALIZATION_EVENT = "subagent.finalization";
14
+ export function createNativeFinalization(acceptance, publicOutput, outputPath) {
15
+ return { nonce: randomUUID(), acceptance, reportRuntime: createFinalizationReportRuntime(publicOutput?.schema), publicOutput,
16
+ outputPath, outputSnapshot: captureSingleOutputSnapshot(outputPath) };
17
+ }
18
+ export function nativeFinalizationLaunch(config) {
19
+ const configPath = path.join(path.dirname(config.reportRuntime.schemaPath), "finalization.json");
20
+ fs.writeFileSync(configPath, JSON.stringify(config), { mode: 0o600 });
21
+ return { extension: fileURLToPath(new URL(`native-finalization${import.meta.url.endsWith(".ts") ? ".ts" : ".js"}`, import.meta.url)), env: { [CONFIG_ENV]: configPath } };
22
+ }
23
+ /** Self-review includes structural and workspace checks; configured verification stays owner-run. */
24
+ export default function registerNativeFinalization(pi) {
25
+ const configPath = process.env[CONFIG_ENV];
26
+ if (!configPath)
27
+ return;
28
+ const config = JSON.parse(fs.readFileSync(configPath, "utf8"));
29
+ const { acceptance, reportRuntime } = config;
30
+ const selfReview = acceptanceSelfReviewConfig(acceptance);
31
+ const messages = [];
32
+ let messageOffset = 0;
33
+ let turn = 0;
34
+ let initialOutput = "";
35
+ let latestOutput = "";
36
+ let initialLedger;
37
+ let resolvedOutput = { fullOutput: "" };
38
+ const registerOutput = (runtime) => pi.registerTool({
39
+ name: "structured_output", label: "Structured Output", description: "Submit the complete output matching the current schema.",
40
+ parameters: Type.Object({ value: Type.Unsafe(runtime.schema) }, { additionalProperties: false }),
41
+ constrainedSampling: { type: "json_schema", strict: "prefer" },
42
+ execute: async (_id, args) => {
43
+ const validation = validateStructuredOutputValue(runtime.schema, args.value);
44
+ if (validation.status === "invalid")
45
+ throw new Error(validation.message);
46
+ fs.writeFileSync(runtime.outputPath, JSON.stringify(args.value), { mode: 0o600 });
47
+ return { content: [{ type: "text", text: "Structured output captured." }], details: {}, terminate: true };
48
+ },
49
+ });
50
+ if (config.publicOutput) {
51
+ registerOutput(config.publicOutput);
52
+ pi.on("before_agent_start", (event) => setPromptSection(event.systemPromptOptions, "subagent_output", "Your final action must call structured_output with JSON matching the current schema. Prose alone does not complete this output contract."));
53
+ }
54
+ pi.on("message_end", (event) => {
55
+ if (["assistant", "user", "toolResult"].includes(event.message.role)) {
56
+ latestOutput = getFinalOutput([event.message]) || latestOutput;
57
+ messages.push(compactObservedMessage(event.message));
58
+ }
59
+ });
60
+ pi.on("agent_before_settle", async (event, ctx) => {
61
+ if (event.outcome !== "completed")
62
+ return;
63
+ const submission = turn === 0
64
+ ? { output: latestOutput }
65
+ : readFinalizationReport(messages, reportRuntime, { messageOffset });
66
+ if (turn === 0) {
67
+ initialOutput = submission.output;
68
+ const hiddenError = detectSubagentError(messages);
69
+ if (hiddenError.hasError)
70
+ submission.error = hiddenError.details ?? `${hiddenError.errorType} failed`;
71
+ if (config.publicOutput)
72
+ submission.error ??= readStructuredOutput(config.publicOutput).error;
73
+ }
74
+ const ledger = await evaluateAcceptance({ acceptance: selfReview, governing: acceptance, cwd: ctx.cwd,
75
+ output: submission.reportSubmissionError ? "" : submission.output, report: submission.reportSubmissionError ? undefined : submission.report });
76
+ initialLedger ??= ledger;
77
+ if (!submission.error && !submission.reportSubmissionError) {
78
+ const output = turn === 0 ? stripAcceptanceReport(submission.output) : resolveFinalizationOutput(submission.output, resolvedOutput.fullOutput);
79
+ resolvedOutput = resolveSingleOutput(config.outputPath, output, turn === 0 ? config.outputSnapshot : resolvedOutput.writtenSnapshot);
80
+ if (resolvedOutput.saveError)
81
+ submission.error = `Failed to save output file '${config.outputPath}': ${resolvedOutput.saveError}`;
82
+ }
83
+ const continueReview = !submission.error && ledger.status !== "blocked" && turn < acceptance.finalization.maxTurns
84
+ && (turn === 0 || Boolean(submission.reportSubmissionError) || ledger.status === "rejected");
85
+ const nextPrompt = continueReview ? formatAcceptanceFinalizationPrompt({ acceptance, initialOutput, initialLedger,
86
+ turn: turn + 1, maxTurns: acceptance.finalization.maxTurns,
87
+ previousFailure: submission.reportSubmissionError ?? acceptanceFailureMessage(ledger), nativeReport: true, outputSchema: config.publicOutput?.schema }) : undefined;
88
+ const marker = { type: FINALIZATION_EVENT, nonce: config.nonce, turn, lastEntryId: ctx.sessionManager.getLeafId() ?? undefined, messageCount: messages.length, at: Date.now(), submission, acceptance: ledger, resolvedOutput, nextPrompt };
89
+ fs.appendFileSync(path.join(path.dirname(reportRuntime.schemaPath), "boundaries.jsonl"), `${JSON.stringify(marker)}\n`, { mode: 0o600 });
90
+ if (!nextPrompt)
91
+ return;
92
+ if (turn === 0) {
93
+ // Native registration refreshes the active schema after the public payload is captured.
94
+ registerOutput(reportRuntime);
95
+ pi.setActiveTools([...new Set([...pi.getActiveTools(), "structured_output"])]);
96
+ }
97
+ turn++;
98
+ messageOffset = messages.length;
99
+ return { entries: [...event.entries, { type: "custom_message", customType: "subagent-self-review", content: nextPrompt, display: false }], continue: true };
100
+ });
101
+ }
@@ -0,0 +1,153 @@
1
+ import { isDeepStrictEqual } from "node:util";
2
+ import { scanJournal, nativeProjection } from "../../shared/journal-reader.js";
3
+ export function snapshotNativeBaseline(file) {
4
+ const ids = new Set();
5
+ let first = true, native = false, entryCount = 0, legacy = false, sessionId;
6
+ if (file)
7
+ try {
8
+ scanJournal(file, (path) => !path.length || path.length === 1 && ["id", "type", "version"].includes(String(path[0])), ({ value }) => {
9
+ if (first) {
10
+ first = false;
11
+ native = value?.type === "session";
12
+ if (native) {
13
+ if (typeof value.id !== "string" || !value.id)
14
+ throw new SyntaxError("Invalid native session identity");
15
+ sessionId = value.id;
16
+ legacy = value.version === 1;
17
+ }
18
+ }
19
+ else if (native)
20
+ entryCount++;
21
+ if (native && typeof value?.id === "string")
22
+ ids.add(value.id);
23
+ }, { requireNewline: true });
24
+ }
25
+ catch (error) {
26
+ if (error.code !== "ENOENT")
27
+ throw error;
28
+ }
29
+ return { ids, entryCount, legacy, sessionId };
30
+ }
31
+ export function snapshotNativeUsage(file) { return snapshotNativeBaseline(file).ids; }
32
+ export function addUsage(usage, value, attribution) {
33
+ for (const key of ["input", "output", "cacheRead", "cacheWrite", "totalTokens", "reasoning", "cacheWrite1h"]) {
34
+ if (value[key] !== undefined && typeof value[key] !== "number")
35
+ throw new Error(`Invalid native usage counter: ${key}`);
36
+ }
37
+ for (const [key, counter] of Object.entries(value)) {
38
+ if (typeof counter === "number" && (!Number.isFinite(counter) || counter < 0))
39
+ throw new Error(`Invalid native usage counter: ${key}`);
40
+ }
41
+ if (value.cost)
42
+ for (const counter of Object.values(value.cost))
43
+ if (!Number.isFinite(counter) || counter < 0)
44
+ throw new Error("Invalid native usage cost");
45
+ usage.input += value.input ?? 0;
46
+ usage.output += value.output ?? 0;
47
+ usage.cacheRead += value.cacheRead ?? 0;
48
+ usage.cacheWrite += value.cacheWrite ?? 0;
49
+ usage.cost += value.cost?.total ?? 0;
50
+ if (attribution)
51
+ (usage.contributions ??= []).push({ ...attribution, usage: value });
52
+ }
53
+ export function validateNativeUsage(value) {
54
+ for (const key of ["input", "output", "cacheRead", "cacheWrite", "totalTokens"]) {
55
+ if (typeof value[key] !== "number" || !Number.isFinite(value[key]) || value[key] < 0)
56
+ throw new Error(`Native usage ${key} is unavailable or invalid`);
57
+ }
58
+ for (const key of ["input", "output", "cacheRead", "cacheWrite", "total"]) {
59
+ if (!value.cost || typeof value.cost[key] !== "number" || !Number.isFinite(value.cost[key]) || value.cost[key] < 0)
60
+ throw new Error(`Native usage cost.${key} is unavailable or invalid`);
61
+ }
62
+ }
63
+ /** One billing reducer for published journals and settled, once-audited in-memory sessions. */
64
+ export function nativeUsageCollector(sessionId, baseline, boundaries = []) {
65
+ const totals = Array.from({ length: Math.max(1, boundaries.length) }, () => ({ input: 0, output: 0, cacheRead: 0, cacheWrite: 0, cost: 0, turns: 0, contributions: [] }));
66
+ let segment = 0;
67
+ const contributions = new Map(), turns = new Set();
68
+ return { totals, append(entry) {
69
+ if (!entry || typeof entry !== "object")
70
+ throw new SyntaxError("Invalid native entry");
71
+ if (entry.type === "message" && (!entry.message || typeof entry.message.role !== "string"))
72
+ throw new SyntaxError("Invalid native message");
73
+ if (!baseline.has(entry.id) && entry.checkpoint !== true) {
74
+ let value, provider, model;
75
+ if (entry.type === "usage") {
76
+ value = entry.usage;
77
+ provider = entry.provider;
78
+ model = entry.model;
79
+ }
80
+ else if (entry.type === "compaction" || entry.type === "branch_summary")
81
+ value = entry.usage;
82
+ else if (entry.type === "message") {
83
+ if (entry.message?.role === "assistant") {
84
+ value = entry.message.usage;
85
+ provider = entry.message.provider;
86
+ model = entry.message.responseModel ?? entry.message.model;
87
+ if (!turns.has(entry.id)) {
88
+ totals[segment].turns++;
89
+ turns.add(entry.id);
90
+ }
91
+ }
92
+ else if (entry.message?.role === "toolResult")
93
+ value = entry.message.usage;
94
+ }
95
+ if (!value && (entry.type === "usage" || entry.type === "message" && entry.message?.role === "assistant"))
96
+ throw new Error(`Required native usage is unavailable: ${entry.id}`);
97
+ if (value) {
98
+ validateNativeUsage(value);
99
+ if (typeof entry.id !== "string" || !entry.id)
100
+ throw new Error("Native usage entry identity is unavailable");
101
+ const next = { id: `${sessionId}:${entry.id}`, provider, model, usage: value };
102
+ const previous = contributions.get(next.id);
103
+ if (previous && !isDeepStrictEqual(previous, next))
104
+ throw new Error(`Conflicting native usage contribution: ${next.id}`);
105
+ if (!previous) {
106
+ contributions.set(next.id, next);
107
+ addUsage(totals[segment], value, { id: next.id, provider, model });
108
+ }
109
+ }
110
+ }
111
+ if (entry.id === boundaries[segment] && segment < totals.length - 1)
112
+ segment++;
113
+ } };
114
+ }
115
+ /** Read native journals without opening a second writer or copying their transcripts. */
116
+ export function readNativeUsage(file, baseline, boundaries = [], options = {}) {
117
+ if (!file)
118
+ return;
119
+ let sessionId, first = true, lastEntryId, reached = false;
120
+ let collector;
121
+ const boundaryReached = Symbol("terminal entry");
122
+ try {
123
+ scanJournal(file, (path, root) => path[0] === "message" && path[1] === "content" ? false : nativeProjection(path, root), ({ value: entry }) => {
124
+ if (first) {
125
+ first = false;
126
+ sessionId = entry?.type === "session" ? entry.id : undefined;
127
+ if (sessionId)
128
+ collector = nativeUsageCollector(sessionId, baseline, boundaries);
129
+ }
130
+ if (!collector)
131
+ return;
132
+ options.onEntry?.(entry);
133
+ collector.append(entry);
134
+ if (entry.type !== "session")
135
+ lastEntryId = entry.id;
136
+ if (entry.id === options.terminalEntryId) {
137
+ reached = true;
138
+ throw boundaryReached;
139
+ }
140
+ }, { requireNewline: true });
141
+ }
142
+ catch (error) {
143
+ if (error.code === "ENOENT")
144
+ return;
145
+ if (error !== boundaryReached)
146
+ throw error;
147
+ }
148
+ if (options.terminalEntryId && !reached)
149
+ throw new Error("Native accounting terminal entry is missing");
150
+ if (sessionId)
151
+ options.onBoundary?.({ sessionId, lastEntryId });
152
+ return collector?.totals;
153
+ }