@caupulican/pi-adaptative 0.81.37 → 0.81.39

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 (451) hide show
  1. package/CHANGELOG.md +91 -0
  2. package/README.md +1 -1
  3. package/dist/bundled-resources/extensions/tmux-agent-manager/README.md +18 -1
  4. package/dist/bundled-resources/extensions/tmux-agent-manager/dispatch-grant.d.ts +131 -0
  5. package/dist/bundled-resources/extensions/tmux-agent-manager/dispatch-grant.d.ts.map +1 -0
  6. package/dist/bundled-resources/extensions/tmux-agent-manager/dispatch-grant.js +196 -0
  7. package/dist/bundled-resources/extensions/tmux-agent-manager/dispatch-grant.js.map +1 -0
  8. package/dist/bundled-resources/extensions/tmux-agent-manager/dispatch-grant.ts +308 -0
  9. package/dist/bundled-resources/extensions/tmux-agent-manager/index.d.ts +22 -2
  10. package/dist/bundled-resources/extensions/tmux-agent-manager/index.d.ts.map +1 -1
  11. package/dist/bundled-resources/extensions/tmux-agent-manager/index.js +585 -24
  12. package/dist/bundled-resources/extensions/tmux-agent-manager/index.js.map +1 -1
  13. package/dist/bundled-resources/extensions/tmux-agent-manager/index.ts +749 -27
  14. package/dist/bundled-resources/runtimes/pi-shell-engine/commands/__init__.py +43 -0
  15. package/dist/bundled-resources/runtimes/pi-shell-engine/commands/fs.py +270 -0
  16. package/dist/bundled-resources/runtimes/pi-shell-engine/commands/search.py +252 -0
  17. package/dist/bundled-resources/runtimes/pi-shell-engine/commands/strings.py +399 -0
  18. package/dist/bundled-resources/runtimes/pi-shell-engine/commands/text.py +575 -0
  19. package/dist/bundled-resources/runtimes/pi-shell-engine/context.py +52 -0
  20. package/dist/bundled-resources/runtimes/pi-shell-engine/errors.py +49 -0
  21. package/dist/bundled-resources/runtimes/pi-shell-engine/exec.py +734 -0
  22. package/dist/bundled-resources/runtimes/pi-shell-engine/expand.py +238 -0
  23. package/dist/bundled-resources/runtimes/pi-shell-engine/main.py +132 -0
  24. package/dist/bundled-resources/runtimes/pi-shell-engine/nodes.py +116 -0
  25. package/dist/bundled-resources/runtimes/pi-shell-engine/parser.py +287 -0
  26. package/dist/bundled-resources/runtimes/pi-shell-engine/proc.py +137 -0
  27. package/dist/bundled-resources/runtimes/pi-shell-engine/state.py +100 -0
  28. package/dist/bundled-resources/runtimes/pi-shell-engine/tokens.py +579 -0
  29. package/dist/bundled-resources/skills/tool-call-repair/SKILL.md +19 -9
  30. package/dist/bundled-resources/skills/tool-call-repair/references/failure-grammar.md +22 -5
  31. package/dist/bundled-resources/skills/tool-call-repair/references/repair-catalogue.md +5 -5
  32. package/dist/bundled-resources/skills/tool-call-repair/references/text-protocol-grammar.md +28 -7
  33. package/dist/cli/args.d.ts +3 -0
  34. package/dist/cli/args.d.ts.map +1 -1
  35. package/dist/cli/args.js +15 -0
  36. package/dist/cli/args.js.map +1 -1
  37. package/dist/core/agent-paths.d.ts +49 -0
  38. package/dist/core/agent-paths.d.ts.map +1 -0
  39. package/dist/core/agent-paths.js +107 -0
  40. package/dist/core/agent-paths.js.map +1 -0
  41. package/dist/core/agent-session-services.d.ts.map +1 -1
  42. package/dist/core/agent-session-services.js +3 -3
  43. package/dist/core/agent-session-services.js.map +1 -1
  44. package/dist/core/agent-session.d.ts +234 -17
  45. package/dist/core/agent-session.d.ts.map +1 -1
  46. package/dist/core/agent-session.js +484 -62
  47. package/dist/core/agent-session.js.map +1 -1
  48. package/dist/core/autonomy/contracts.d.ts +9 -0
  49. package/dist/core/autonomy/contracts.d.ts.map +1 -1
  50. package/dist/core/autonomy/contracts.js.map +1 -1
  51. package/dist/core/autonomy/lane-tracker.d.ts +10 -1
  52. package/dist/core/autonomy/lane-tracker.d.ts.map +1 -1
  53. package/dist/core/autonomy/lane-tracker.js +5 -1
  54. package/dist/core/autonomy/lane-tracker.js.map +1 -1
  55. package/dist/core/background-lane-controller.d.ts +122 -6
  56. package/dist/core/background-lane-controller.d.ts.map +1 -1
  57. package/dist/core/background-lane-controller.js +447 -90
  58. package/dist/core/background-lane-controller.js.map +1 -1
  59. package/dist/core/bash-execution-controller.d.ts +4 -0
  60. package/dist/core/bash-execution-controller.d.ts.map +1 -1
  61. package/dist/core/bash-execution-controller.js +7 -1
  62. package/dist/core/bash-execution-controller.js.map +1 -1
  63. package/dist/core/compaction-support.d.ts +13 -3
  64. package/dist/core/compaction-support.d.ts.map +1 -1
  65. package/dist/core/compaction-support.js +43 -7
  66. package/dist/core/compaction-support.js.map +1 -1
  67. package/dist/core/context/context-audit.d.ts +33 -1
  68. package/dist/core/context/context-audit.d.ts.map +1 -1
  69. package/dist/core/context/context-audit.js +31 -5
  70. package/dist/core/context/context-audit.js.map +1 -1
  71. package/dist/core/context/sqlite-runtime-index.d.ts.map +1 -1
  72. package/dist/core/context/sqlite-runtime-index.js +13 -7
  73. package/dist/core/context/sqlite-runtime-index.js.map +1 -1
  74. package/dist/core/context-gc.d.ts.map +1 -1
  75. package/dist/core/context-gc.js +16 -1
  76. package/dist/core/context-gc.js.map +1 -1
  77. package/dist/core/context-pipeline.d.ts +26 -4
  78. package/dist/core/context-pipeline.d.ts.map +1 -1
  79. package/dist/core/context-pipeline.js +137 -14
  80. package/dist/core/context-pipeline.js.map +1 -1
  81. package/dist/core/cost-guard.d.ts +19 -3
  82. package/dist/core/cost-guard.d.ts.map +1 -1
  83. package/dist/core/cost-guard.js +18 -3
  84. package/dist/core/cost-guard.js.map +1 -1
  85. package/dist/core/delegation/session-worker-result.d.ts +34 -4
  86. package/dist/core/delegation/session-worker-result.d.ts.map +1 -1
  87. package/dist/core/delegation/session-worker-result.js +64 -4
  88. package/dist/core/delegation/session-worker-result.js.map +1 -1
  89. package/dist/core/delegation/worker-actions.d.ts.map +1 -1
  90. package/dist/core/delegation/worker-actions.js +1 -1
  91. package/dist/core/delegation/worker-actions.js.map +1 -1
  92. package/dist/core/delegation/worker-result.d.ts +42 -1
  93. package/dist/core/delegation/worker-result.d.ts.map +1 -1
  94. package/dist/core/delegation/worker-result.js +68 -0
  95. package/dist/core/delegation/worker-result.js.map +1 -1
  96. package/dist/core/extensions/loader.d.ts.map +1 -1
  97. package/dist/core/extensions/loader.js +23 -7
  98. package/dist/core/extensions/loader.js.map +1 -1
  99. package/dist/core/extensions/runner.d.ts.map +1 -1
  100. package/dist/core/extensions/runner.js +1 -0
  101. package/dist/core/extensions/runner.js.map +1 -1
  102. package/dist/core/extensions/types.d.ts +51 -0
  103. package/dist/core/extensions/types.d.ts.map +1 -1
  104. package/dist/core/extensions/types.js.map +1 -1
  105. package/dist/core/goal-loop-controller.d.ts +17 -1
  106. package/dist/core/goal-loop-controller.d.ts.map +1 -1
  107. package/dist/core/goal-loop-controller.js +79 -11
  108. package/dist/core/goal-loop-controller.js.map +1 -1
  109. package/dist/core/goals/goal-continuation-controller.d.ts +48 -2
  110. package/dist/core/goals/goal-continuation-controller.d.ts.map +1 -1
  111. package/dist/core/goals/goal-continuation-controller.js +77 -0
  112. package/dist/core/goals/goal-continuation-controller.js.map +1 -1
  113. package/dist/core/goals/goal-continuation-defaults.d.ts +39 -0
  114. package/dist/core/goals/goal-continuation-defaults.d.ts.map +1 -1
  115. package/dist/core/goals/goal-continuation-defaults.js +42 -0
  116. package/dist/core/goals/goal-continuation-defaults.js.map +1 -1
  117. package/dist/core/goals/goal-continuation-prompt.d.ts +4 -0
  118. package/dist/core/goals/goal-continuation-prompt.d.ts.map +1 -1
  119. package/dist/core/goals/goal-continuation-prompt.js +51 -10
  120. package/dist/core/goals/goal-continuation-prompt.js.map +1 -1
  121. package/dist/core/goals/goal-runtime-snapshot.d.ts +101 -2
  122. package/dist/core/goals/goal-runtime-snapshot.d.ts.map +1 -1
  123. package/dist/core/goals/goal-runtime-snapshot.js +87 -4
  124. package/dist/core/goals/goal-runtime-snapshot.js.map +1 -1
  125. package/dist/core/goals/goal-state.d.ts +89 -1
  126. package/dist/core/goals/goal-state.d.ts.map +1 -1
  127. package/dist/core/goals/goal-state.js +71 -5
  128. package/dist/core/goals/goal-state.js.map +1 -1
  129. package/dist/core/goals/goal-tool-core.d.ts +56 -2
  130. package/dist/core/goals/goal-tool-core.d.ts.map +1 -1
  131. package/dist/core/goals/goal-tool-core.js +111 -5
  132. package/dist/core/goals/goal-tool-core.js.map +1 -1
  133. package/dist/core/goals/session-goal-state.d.ts +11 -2
  134. package/dist/core/goals/session-goal-state.d.ts.map +1 -1
  135. package/dist/core/goals/session-goal-state.js +32 -17
  136. package/dist/core/goals/session-goal-state.js.map +1 -1
  137. package/dist/core/keybindings.d.ts +10 -0
  138. package/dist/core/keybindings.d.ts.map +1 -1
  139. package/dist/core/keybindings.js +10 -2
  140. package/dist/core/keybindings.js.map +1 -1
  141. package/dist/core/learning/observation-store.d.ts +12 -3
  142. package/dist/core/learning/observation-store.d.ts.map +1 -1
  143. package/dist/core/learning/observation-store.js +30 -15
  144. package/dist/core/learning/observation-store.js.map +1 -1
  145. package/dist/core/learning/skill-curator.d.ts +5 -1
  146. package/dist/core/learning/skill-curator.d.ts.map +1 -1
  147. package/dist/core/learning/skill-curator.js +21 -19
  148. package/dist/core/learning/skill-curator.js.map +1 -1
  149. package/dist/core/local-runtime-controller.d.ts +65 -3
  150. package/dist/core/local-runtime-controller.d.ts.map +1 -1
  151. package/dist/core/local-runtime-controller.js +186 -26
  152. package/dist/core/local-runtime-controller.js.map +1 -1
  153. package/dist/core/memory/providers/file-store.d.ts +1 -1
  154. package/dist/core/memory/providers/file-store.d.ts.map +1 -1
  155. package/dist/core/memory/providers/file-store.js +6 -6
  156. package/dist/core/memory/providers/file-store.js.map +1 -1
  157. package/dist/core/model-capability.d.ts +34 -0
  158. package/dist/core/model-capability.d.ts.map +1 -1
  159. package/dist/core/model-capability.js +42 -1
  160. package/dist/core/model-capability.js.map +1 -1
  161. package/dist/core/model-router/tool-escalation.d.ts +15 -0
  162. package/dist/core/model-router/tool-escalation.d.ts.map +1 -1
  163. package/dist/core/model-router/tool-escalation.js +23 -1
  164. package/dist/core/model-router/tool-escalation.js.map +1 -1
  165. package/dist/core/model-router-controller.d.ts +34 -7
  166. package/dist/core/model-router-controller.d.ts.map +1 -1
  167. package/dist/core/model-router-controller.js +95 -16
  168. package/dist/core/model-router-controller.js.map +1 -1
  169. package/dist/core/models/adaptation-store.d.ts +7 -0
  170. package/dist/core/models/adaptation-store.d.ts.map +1 -1
  171. package/dist/core/models/adaptation-store.js +47 -11
  172. package/dist/core/models/adaptation-store.js.map +1 -1
  173. package/dist/core/models/default-model-suggestions.d.ts.map +1 -1
  174. package/dist/core/models/default-model-suggestions.js +17 -0
  175. package/dist/core/models/default-model-suggestions.js.map +1 -1
  176. package/dist/core/models/fitness-store.d.ts +3 -0
  177. package/dist/core/models/fitness-store.d.ts.map +1 -1
  178. package/dist/core/models/fitness-store.js +11 -2
  179. package/dist/core/models/fitness-store.js.map +1 -1
  180. package/dist/core/models/llamacpp-runtime.d.ts +180 -0
  181. package/dist/core/models/llamacpp-runtime.d.ts.map +1 -0
  182. package/dist/core/models/llamacpp-runtime.js +475 -0
  183. package/dist/core/models/llamacpp-runtime.js.map +1 -0
  184. package/dist/core/models/local-registration.d.ts +40 -0
  185. package/dist/core/models/local-registration.d.ts.map +1 -1
  186. package/dist/core/models/local-registration.js +94 -5
  187. package/dist/core/models/local-registration.js.map +1 -1
  188. package/dist/core/models/local-runtime.d.ts.map +1 -1
  189. package/dist/core/models/local-runtime.js +9 -7
  190. package/dist/core/models/local-runtime.js.map +1 -1
  191. package/dist/core/models/model-ref.d.ts +7 -0
  192. package/dist/core/models/model-ref.d.ts.map +1 -1
  193. package/dist/core/models/model-ref.js +26 -0
  194. package/dist/core/models/model-ref.js.map +1 -1
  195. package/dist/core/models/needle-runtime.d.ts +257 -0
  196. package/dist/core/models/needle-runtime.d.ts.map +1 -0
  197. package/dist/core/models/needle-runtime.js +519 -0
  198. package/dist/core/models/needle-runtime.js.map +1 -0
  199. package/dist/core/models/prism-llamacpp-lifecycle.d.ts +89 -0
  200. package/dist/core/models/prism-llamacpp-lifecycle.d.ts.map +1 -0
  201. package/dist/core/models/prism-llamacpp-lifecycle.js +121 -0
  202. package/dist/core/models/prism-llamacpp-lifecycle.js.map +1 -0
  203. package/dist/core/package-manager.d.ts.map +1 -1
  204. package/dist/core/package-manager.js +11 -10
  205. package/dist/core/package-manager.js.map +1 -1
  206. package/dist/core/process-matrix/codes.d.ts +72 -0
  207. package/dist/core/process-matrix/codes.d.ts.map +1 -0
  208. package/dist/core/process-matrix/codes.js +15 -0
  209. package/dist/core/process-matrix/codes.js.map +1 -0
  210. package/dist/core/process-matrix/runtime.d.ts +63 -0
  211. package/dist/core/process-matrix/runtime.d.ts.map +1 -0
  212. package/dist/core/process-matrix/runtime.js +310 -0
  213. package/dist/core/process-matrix/runtime.js.map +1 -0
  214. package/dist/core/process-matrix/store.d.ts +22 -0
  215. package/dist/core/process-matrix/store.d.ts.map +1 -0
  216. package/dist/core/process-matrix/store.js +80 -0
  217. package/dist/core/process-matrix/store.js.map +1 -0
  218. package/dist/core/process-matrix/supervisor.d.ts +72 -0
  219. package/dist/core/process-matrix/supervisor.d.ts.map +1 -0
  220. package/dist/core/process-matrix/supervisor.js +130 -0
  221. package/dist/core/process-matrix/supervisor.js.map +1 -0
  222. package/dist/core/profile-registry.d.ts +7 -0
  223. package/dist/core/profile-registry.d.ts.map +1 -1
  224. package/dist/core/profile-registry.js +20 -0
  225. package/dist/core/profile-registry.js.map +1 -1
  226. package/dist/core/python-runtime.d.ts.map +1 -1
  227. package/dist/core/python-runtime.js +3 -3
  228. package/dist/core/python-runtime.js.map +1 -1
  229. package/dist/core/reflection-controller.d.ts +29 -5
  230. package/dist/core/reflection-controller.d.ts.map +1 -1
  231. package/dist/core/reflection-controller.js +215 -126
  232. package/dist/core/reflection-controller.js.map +1 -1
  233. package/dist/core/reload-blockers.d.ts +36 -0
  234. package/dist/core/reload-blockers.d.ts.map +1 -1
  235. package/dist/core/reload-blockers.js +44 -0
  236. package/dist/core/reload-blockers.js.map +1 -1
  237. package/dist/core/resource-loader.d.ts.map +1 -1
  238. package/dist/core/resource-loader.js +8 -7
  239. package/dist/core/resource-loader.js.map +1 -1
  240. package/dist/core/runtime-builder.d.ts +58 -5
  241. package/dist/core/runtime-builder.d.ts.map +1 -1
  242. package/dist/core/runtime-builder.js +209 -25
  243. package/dist/core/runtime-builder.js.map +1 -1
  244. package/dist/core/scout-controller.d.ts +6 -0
  245. package/dist/core/scout-controller.d.ts.map +1 -1
  246. package/dist/core/scout-controller.js +66 -52
  247. package/dist/core/scout-controller.js.map +1 -1
  248. package/dist/core/sdk.d.ts.map +1 -1
  249. package/dist/core/sdk.js +3 -3
  250. package/dist/core/sdk.js.map +1 -1
  251. package/dist/core/session-role.d.ts +31 -0
  252. package/dist/core/session-role.d.ts.map +1 -0
  253. package/dist/core/session-role.js +52 -0
  254. package/dist/core/session-role.js.map +1 -0
  255. package/dist/core/settings-manager.d.ts +42 -3
  256. package/dist/core/settings-manager.d.ts.map +1 -1
  257. package/dist/core/settings-manager.js +94 -60
  258. package/dist/core/settings-manager.js.map +1 -1
  259. package/dist/core/system-prompt-builder.d.ts +12 -0
  260. package/dist/core/system-prompt-builder.d.ts.map +1 -1
  261. package/dist/core/system-prompt-builder.js +6 -0
  262. package/dist/core/system-prompt-builder.js.map +1 -1
  263. package/dist/core/system-prompt.d.ts +14 -1
  264. package/dist/core/system-prompt.d.ts.map +1 -1
  265. package/dist/core/system-prompt.js +20 -3
  266. package/dist/core/system-prompt.js.map +1 -1
  267. package/dist/core/tasks/session-task-state.d.ts +11 -2
  268. package/dist/core/tasks/session-task-state.d.ts.map +1 -1
  269. package/dist/core/tasks/session-task-state.js +27 -11
  270. package/dist/core/tasks/session-task-state.js.map +1 -1
  271. package/dist/core/tasks/task-contract-monitor.d.ts +45 -0
  272. package/dist/core/tasks/task-contract-monitor.d.ts.map +1 -0
  273. package/dist/core/tasks/task-contract-monitor.js +56 -0
  274. package/dist/core/tasks/task-contract-monitor.js.map +1 -0
  275. package/dist/core/tasks/task-state.d.ts +8 -0
  276. package/dist/core/tasks/task-state.d.ts.map +1 -1
  277. package/dist/core/tasks/task-state.js +28 -1
  278. package/dist/core/tasks/task-state.js.map +1 -1
  279. package/dist/core/tool-gate-controller.d.ts.map +1 -1
  280. package/dist/core/tool-gate-controller.js +5 -0
  281. package/dist/core/tool-gate-controller.js.map +1 -1
  282. package/dist/core/tool-recovery-log-records.d.ts +7 -0
  283. package/dist/core/tool-recovery-log-records.d.ts.map +1 -1
  284. package/dist/core/tool-recovery-log-records.js +14 -6
  285. package/dist/core/tool-recovery-log-records.js.map +1 -1
  286. package/dist/core/tool-selection/promotion.d.ts +54 -0
  287. package/dist/core/tool-selection/promotion.d.ts.map +1 -0
  288. package/dist/core/tool-selection/promotion.js +81 -0
  289. package/dist/core/tool-selection/promotion.js.map +1 -0
  290. package/dist/core/tool-selection/tool-performance-store.d.ts +37 -0
  291. package/dist/core/tool-selection/tool-performance-store.d.ts.map +1 -1
  292. package/dist/core/tool-selection/tool-performance-store.js +87 -3
  293. package/dist/core/tool-selection/tool-performance-store.js.map +1 -1
  294. package/dist/core/tool-selection/tool-selection-controller.d.ts +45 -0
  295. package/dist/core/tool-selection/tool-selection-controller.d.ts.map +1 -1
  296. package/dist/core/tool-selection/tool-selection-controller.js +96 -0
  297. package/dist/core/tool-selection/tool-selection-controller.js.map +1 -1
  298. package/dist/core/tools/bash.d.ts +18 -0
  299. package/dist/core/tools/bash.d.ts.map +1 -1
  300. package/dist/core/tools/bash.js +97 -18
  301. package/dist/core/tools/bash.js.map +1 -1
  302. package/dist/core/tools/delegate-status.d.ts +14 -0
  303. package/dist/core/tools/delegate-status.d.ts.map +1 -1
  304. package/dist/core/tools/delegate-status.js +82 -7
  305. package/dist/core/tools/delegate-status.js.map +1 -1
  306. package/dist/core/tools/delegate.d.ts.map +1 -1
  307. package/dist/core/tools/delegate.js +25 -8
  308. package/dist/core/tools/delegate.js.map +1 -1
  309. package/dist/core/tools/find.d.ts.map +1 -1
  310. package/dist/core/tools/find.js +52 -44
  311. package/dist/core/tools/find.js.map +1 -1
  312. package/dist/core/tools/goal.d.ts +94 -3
  313. package/dist/core/tools/goal.d.ts.map +1 -1
  314. package/dist/core/tools/goal.js +165 -15
  315. package/dist/core/tools/goal.js.map +1 -1
  316. package/dist/core/tools/grep.d.ts.map +1 -1
  317. package/dist/core/tools/grep.js +5 -4
  318. package/dist/core/tools/grep.js.map +1 -1
  319. package/dist/core/tools/model-fitness.d.ts +7 -0
  320. package/dist/core/tools/model-fitness.d.ts.map +1 -1
  321. package/dist/core/tools/model-fitness.js +2 -2
  322. package/dist/core/tools/model-fitness.js.map +1 -1
  323. package/dist/core/tools/render-utils.d.ts.map +1 -1
  324. package/dist/core/tools/render-utils.js +1 -1
  325. package/dist/core/tools/render-utils.js.map +1 -1
  326. package/dist/core/tools/shell-contract-router.d.ts +6 -1
  327. package/dist/core/tools/shell-contract-router.d.ts.map +1 -1
  328. package/dist/core/tools/shell-contract-router.js +69 -13
  329. package/dist/core/tools/shell-contract-router.js.map +1 -1
  330. package/dist/core/tools/shell-session.d.ts +89 -0
  331. package/dist/core/tools/shell-session.d.ts.map +1 -0
  332. package/dist/core/tools/shell-session.js +432 -0
  333. package/dist/core/tools/shell-session.js.map +1 -0
  334. package/dist/core/tools/task-steps.d.ts +4 -0
  335. package/dist/core/tools/task-steps.d.ts.map +1 -1
  336. package/dist/core/tools/task-steps.js +63 -8
  337. package/dist/core/tools/task-steps.js.map +1 -1
  338. package/dist/core/tools/tmux-dispatch.d.ts +86 -0
  339. package/dist/core/tools/tmux-dispatch.d.ts.map +1 -0
  340. package/dist/core/tools/tmux-dispatch.js +91 -0
  341. package/dist/core/tools/tmux-dispatch.js.map +1 -0
  342. package/dist/core/tools/windows-shell-engine.d.ts +42 -0
  343. package/dist/core/tools/windows-shell-engine.d.ts.map +1 -0
  344. package/dist/core/tools/windows-shell-engine.js +153 -0
  345. package/dist/core/tools/windows-shell-engine.js.map +1 -0
  346. package/dist/core/tools/windows-shell-state.d.ts +40 -0
  347. package/dist/core/tools/windows-shell-state.d.ts.map +1 -0
  348. package/dist/core/tools/windows-shell-state.js +59 -0
  349. package/dist/core/tools/windows-shell-state.js.map +1 -0
  350. package/dist/core/tools/worktree-sync.d.ts +24 -0
  351. package/dist/core/tools/worktree-sync.d.ts.map +1 -0
  352. package/dist/core/tools/worktree-sync.js +338 -0
  353. package/dist/core/tools/worktree-sync.js.map +1 -0
  354. package/dist/core/trust-manager.d.ts +4 -1
  355. package/dist/core/trust-manager.d.ts.map +1 -1
  356. package/dist/core/trust-manager.js +20 -2
  357. package/dist/core/trust-manager.js.map +1 -1
  358. package/dist/core/util/atomic-file.d.ts +55 -0
  359. package/dist/core/util/atomic-file.d.ts.map +1 -0
  360. package/dist/core/util/atomic-file.js +255 -0
  361. package/dist/core/util/atomic-file.js.map +1 -0
  362. package/dist/core/util/minimatch-cache.d.ts +33 -0
  363. package/dist/core/util/minimatch-cache.d.ts.map +1 -0
  364. package/dist/core/util/minimatch-cache.js +0 -0
  365. package/dist/core/util/minimatch-cache.js.map +1 -0
  366. package/dist/core/worktree-sync/codes.d.ts +227 -0
  367. package/dist/core/worktree-sync/codes.d.ts.map +1 -0
  368. package/dist/core/worktree-sync/codes.js +14 -0
  369. package/dist/core/worktree-sync/codes.js.map +1 -0
  370. package/dist/core/worktree-sync/git-engine.d.ts +156 -0
  371. package/dist/core/worktree-sync/git-engine.d.ts.map +1 -0
  372. package/dist/core/worktree-sync/git-engine.js +1191 -0
  373. package/dist/core/worktree-sync/git-engine.js.map +1 -0
  374. package/dist/core/worktree-sync/lane-gate.d.ts +75 -0
  375. package/dist/core/worktree-sync/lane-gate.d.ts.map +1 -0
  376. package/dist/core/worktree-sync/lane-gate.js +360 -0
  377. package/dist/core/worktree-sync/lane-gate.js.map +1 -0
  378. package/dist/core/worktree-sync/runtime.d.ts +47 -0
  379. package/dist/core/worktree-sync/runtime.d.ts.map +1 -0
  380. package/dist/core/worktree-sync/runtime.js +96 -0
  381. package/dist/core/worktree-sync/runtime.js.map +1 -0
  382. package/dist/core/worktree-sync/store.d.ts +69 -0
  383. package/dist/core/worktree-sync/store.d.ts.map +1 -0
  384. package/dist/core/worktree-sync/store.js +247 -0
  385. package/dist/core/worktree-sync/store.js.map +1 -0
  386. package/dist/core/worktree-sync/watcher.d.ts +29 -0
  387. package/dist/core/worktree-sync/watcher.d.ts.map +1 -0
  388. package/dist/core/worktree-sync/watcher.js +93 -0
  389. package/dist/core/worktree-sync/watcher.js.map +1 -0
  390. package/dist/main.d.ts.map +1 -1
  391. package/dist/main.js +94 -0
  392. package/dist/main.js.map +1 -1
  393. package/dist/migrations.d.ts +9 -0
  394. package/dist/migrations.d.ts.map +1 -1
  395. package/dist/migrations.js +38 -0
  396. package/dist/migrations.js.map +1 -1
  397. package/dist/modes/interactive/auto-learn-controller.d.ts +16 -1
  398. package/dist/modes/interactive/auto-learn-controller.d.ts.map +1 -1
  399. package/dist/modes/interactive/auto-learn-controller.js +50 -8
  400. package/dist/modes/interactive/auto-learn-controller.js.map +1 -1
  401. package/dist/modes/interactive/components/profile-resource-editor.d.ts.map +1 -1
  402. package/dist/modes/interactive/components/profile-resource-editor.js +23 -1
  403. package/dist/modes/interactive/components/profile-resource-editor.js.map +1 -1
  404. package/dist/modes/interactive/interactive-mode.d.ts +1 -0
  405. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  406. package/dist/modes/interactive/interactive-mode.js +4 -0
  407. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  408. package/dist/modes/interactive/local-model-commands.d.ts +43 -0
  409. package/dist/modes/interactive/local-model-commands.d.ts.map +1 -1
  410. package/dist/modes/interactive/local-model-commands.js +290 -3
  411. package/dist/modes/interactive/local-model-commands.js.map +1 -1
  412. package/dist/modes/interactive/session-flow-commands.d.ts.map +1 -1
  413. package/dist/modes/interactive/session-flow-commands.js +24 -4
  414. package/dist/modes/interactive/session-flow-commands.js.map +1 -1
  415. package/dist/utils/fs-watch.d.ts +11 -0
  416. package/dist/utils/fs-watch.d.ts.map +1 -1
  417. package/dist/utils/fs-watch.js +20 -2
  418. package/dist/utils/fs-watch.js.map +1 -1
  419. package/dist/utils/highlight-js-languages.d.ts +4 -0
  420. package/dist/utils/highlight-js-languages.d.ts.map +1 -0
  421. package/dist/utils/highlight-js-languages.js +573 -0
  422. package/dist/utils/highlight-js-languages.js.map +1 -0
  423. package/dist/utils/shell.d.ts +7 -1
  424. package/dist/utils/shell.d.ts.map +1 -1
  425. package/dist/utils/shell.js +39 -9
  426. package/dist/utils/shell.js.map +1 -1
  427. package/dist/utils/syntax-highlight.d.ts.map +1 -1
  428. package/dist/utils/syntax-highlight.js +53 -5
  429. package/dist/utils/syntax-highlight.js.map +1 -1
  430. package/dist/utils/tools-manager.d.ts.map +1 -1
  431. package/dist/utils/tools-manager.js +112 -1
  432. package/dist/utils/tools-manager.js.map +1 -1
  433. package/docs/development.md +2 -0
  434. package/docs/packages.md +1 -1
  435. package/docs/process-matrix.md +120 -0
  436. package/docs/settings.md +5 -2
  437. package/docs/tmux-agent-manager.md +85 -2
  438. package/docs/windows.md +52 -3
  439. package/docs/work-directory.md +29 -0
  440. package/docs/worktree-sync.md +250 -0
  441. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  442. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  443. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  444. package/examples/extensions/sandbox/package-lock.json +2 -2
  445. package/examples/extensions/sandbox/package.json +1 -1
  446. package/examples/extensions/with-deps/package-lock.json +2 -2
  447. package/examples/extensions/with-deps/package.json +1 -1
  448. package/npm-shrinkwrap.json +12 -12
  449. package/package.json +10 -4
  450. package/docs/integration-sweep-builder-blueprint-2026-07-09.md +0 -365
  451. package/docs/integration-sweep-resume-2026-07-09.md +0 -407
@@ -1,12 +1,14 @@
1
+ import { createHash, randomUUID } from "node:crypto";
1
2
  import { readFileSync, rmSync, writeFileSync } from "node:fs";
2
3
  import { basename, dirname, join } from "node:path";
3
4
  import { classifyFailure, compactToolResultDetailsForRetention, computeRetryDelayMs, createCustomMessage, DEFAULT_RETRY_POLICY, DEFAULT_STREAM_IDLE, RetryController, sleepAbortable, withStreamIdleWatchdog, } from "@caupulican/pi-agent-core";
4
5
  import { calculateContextTokens, compact, createDeterministicCompaction, estimateContextTokens, getLatestCompactionEntry, prepareCompaction, runCompactionLoop, shouldCompact, } from "@caupulican/pi-agent-core/node";
5
- import { cleanupSessionResources, formatToolRepairStandingRule, generateTextToolProtocolPrimer, getSupportedThinkingLevels, isContextOverflow, modelsAreEqual, parseTextToolCalls, streamSimple, } from "@caupulican/pi-ai";
6
+ import { cleanupSessionResources, formatToolRepairStandingRule, formatVariantEnvelope, generateTextToolProtocolPrimer, getSupportedThinkingLevels, isContextOverflow, modelsAreEqual, parseTextToolCalls, streamSimple, } from "@caupulican/pi-ai";
6
7
  import { Type } from "typebox";
7
8
  import { getAgentDir } from "../config.js";
8
9
  import { stripFrontmatter } from "../utils/frontmatter.js";
9
10
  import { getProcessWorkRun } from "../utils/work-directory.js";
11
+ import { resourceDir, stateFile } from "./agent-paths.js";
10
12
  import { formatNoApiKeyFoundMessage, formatNoModelSelectedMessage } from "./auth-guidance.js";
11
13
  import { buildForegroundEnvelope, formatForegroundEnvelopeObservation } from "./autonomy/foreground-envelope.js";
12
14
  import { evaluateToolGate } from "./autonomy/gates.js";
@@ -23,16 +25,18 @@ import { FailureCorpusRecorder } from "./failure-corpus.js";
23
25
  import { GatewayRegistry } from "./gateways/channel-provider.js";
24
26
  import { GoalLoopController } from "./goal-loop-controller.js";
25
27
  import { buildGoalRuntimeSnapshot, } from "./goals/goal-runtime-snapshot.js";
28
+ import { applyGoalEvent } from "./goals/goal-state.js";
26
29
  import { appendGoalStateSnapshot, getLatestGoalStateSnapshot } from "./goals/session-goal-state.js";
27
30
  import { constrainStreamIdleToHttpTimeout } from "./http-dispatcher.js";
28
31
  import { appendLearningDecisionSnapshot, getLearningDecisionSnapshots } from "./learning/session-learning-decision.js";
29
32
  import { isPromotedFrontmatter, SkillCurator } from "./learning/skill-curator.js";
30
33
  import { LocalRuntimeController } from "./local-runtime-controller.js";
31
34
  import { MemoryController } from "./memory-controller.js";
32
- import { deriveModelCapabilityProfile, filterToolNamesForCapability, } from "./model-capability.js";
35
+ import { deriveModelCapabilityProfile, evaluateLaneWorkerRefusal, filterToolNamesForCapability, } from "./model-capability.js";
36
+ import { isLocalOrManagedRouterModel } from "./model-router/tool-escalation.js";
33
37
  import { formatModelRouterModel, ModelRouterController } from "./model-router-controller.js";
34
38
  import { ModelSelectionController } from "./model-selection-controller.js";
35
- import { ModelAdaptationStore } from "./models/adaptation-store.js";
39
+ import { ModelAdaptationStore, } from "./models/adaptation-store.js";
36
40
  import { HF_TRANSFORMERS_PROVIDER, OLLAMA_PROVIDER } from "./models/local-registration.js";
37
41
  import { DEFAULT_ADAPTIVE_STREAM_IDLE_CEILING_MS, estimateContextPromptTokens, resolveAdaptiveStreamIdleOptions, withModelPerfProfile, } from "./models/perf-profile.js";
38
42
  import { ProfileFilterController } from "./profile-filter-controller.js";
@@ -53,7 +57,8 @@ import { ToolRecoveryLogger } from "./tool-recovery-logger.js";
53
57
  import { formatToolRepairHealthReport } from "./tool-repair-health.js";
54
58
  import { resolveCurrentToolRepairSettings } from "./tool-repair-settings.js";
55
59
  import { ToolPerformanceStore } from "./tool-selection/tool-performance-store.js";
56
- import { ToolSelectionController } from "./tool-selection/tool-selection-controller.js";
60
+ import { formatToolSelectionReport, ToolSelectionController } from "./tool-selection/tool-selection-controller.js";
61
+ import { disposePersistentShellSession } from "./tools/shell-session.js";
57
62
  // ============================================================================
58
63
  // Stream-idle watchdog wiring
59
64
  // ============================================================================
@@ -71,6 +76,19 @@ const MODEL_ADAPTATION_REPAIR_THRESHOLD = 3;
71
76
  const TEXT_TOOL_PROTOCOL_VERSION = 1;
72
77
  const TEXT_TOOL_PROTOCOL_TRIALS_PER_VARIANT = 2;
73
78
  const TEXT_TOOL_PROTOCOL_PARSE_FAILURE_THRESHOLD = 3;
79
+ /** How often the one-line envelope-format corrective steer (see
80
+ * {@link AgentSession._maybeInjectTextProtocolCorrectiveSteer}) re-fires after the first parse
81
+ * failure this session — every Nth failure thereafter, not every single one, so a model that keeps
82
+ * missing the envelope doesn't get the reminder spliced into every turn (the breaker's same-signature
83
+ * counter is what actually demotes it, at {@link TEXT_TOOL_PROTOCOL_PARSE_FAILURE_THRESHOLD}). */
84
+ const TEXT_TOOL_PROTOCOL_STEER_INTERVAL = 5;
85
+ /** Anti-loop cooldown: the evidence-gated auto-probe (see {@link
86
+ * AgentSession._maybeAutoProbeOnValidationEscalation}) skips a model whose persisted `toolProbe`
87
+ * verdict was written within this window, so a burst of validation-escalation events for a still-
88
+ * failing model — or a very recent explicit `/toolprobe` run, or a future circuit-breaker demote — does
89
+ * not re-fire the (multi-completion) probe every time. Independent of, and complementary to, the
90
+ * per-session `_autoProbedModels` latch. */
91
+ const AUTO_TOOL_PROBE_FRESHNESS_MS = 15 * 60 * 1000;
74
92
  const TEXT_TOOL_PROTOCOL_VARIANTS = [
75
93
  "tool-tag",
76
94
  "tool-call",
@@ -123,6 +141,43 @@ export function parseSkillBlock(text) {
123
141
  }
124
142
  /** customType for spawned-usage roll-up entries (Cost Aggregation, Model A). */
125
143
  export const SPAWNED_USAGE_CUSTOM_TYPE = "spawned_usage";
144
+ /**
145
+ * customType for a persisted runaway-loop-backstop entry: the agent loop stopped a turn stuck
146
+ * repeating one identical tool-call signature. This is the session-log/telemetry sink for
147
+ * {@link Agent.onRunawayStop} — see `_installAgentToolHooks`.
148
+ */
149
+ export const RUNAWAY_STOP_CUSTOM_TYPE = "runaway_stop";
150
+ /**
151
+ * customType for a persisted tool-validation-escalation entry: the agent loop bounced the same
152
+ * tool-call validation failure enough times to escalate. This is the session-log/telemetry sink for
153
+ * {@link Agent.onToolValidationEscalation} — see `_installAgentToolHooks`.
154
+ */
155
+ export const TOOL_VALIDATION_ESCALATION_CUSTOM_TYPE = "tool_validation_escalation";
156
+ /** Fallback {@link IsolatedCompletionOptions.laneKind} for callers that do not tag their lane. */
157
+ export const DEFAULT_ISOLATED_LANE_KIND = "isolated";
158
+ /**
159
+ * Derive a STABLE synthetic cache-affinity key for an isolated completion lane. Isolated calls
160
+ * deliberately never carry the real session id (see reflection-controller.ts's isolation invariants —
161
+ * an isolated call must not entangle with the main session), which today also means every isolated
162
+ * call looks like a brand-new, uncorrelated session to providers with session-affinity headers /
163
+ * `prompt_cache_key` (anthropic.ts's `x-session-affinity`, openai-responses.ts /
164
+ * openai-completions.ts's `prompt_cache_key`), defeating their cache routing.
165
+ *
166
+ * This key is deterministic per `(laneKind, model, systemPrompt)` — the SAME lane calling the SAME
167
+ * model with the SAME (static) system prompt always gets the SAME key, so repeat calls route to the
168
+ * same cache-warm backend — while remaining fully synthetic: it is a salted hash, namespaced with a
169
+ * `lane:` prefix, and never derived from or equal to the real session id.
170
+ */
171
+ export function computeLaneAffinityKey(laneKind, model, systemPrompt) {
172
+ const modelKey = model ? `${model.provider}/${model.id}` : "unknown-model";
173
+ // NUL-separated fields: laneKind/modelKey are drawn from small caller-controlled vocabularies
174
+ // that never contain a raw NUL, so this cannot field-collide the way a plain colon/space join could.
175
+ const digest = createHash("sha256")
176
+ .update(["pi-lane-affinity-v1", laneKind, modelKey, systemPrompt].join("\u0000"))
177
+ .digest("hex")
178
+ .slice(0, 32);
179
+ return `lane:${laneKind}:${digest}`;
180
+ }
126
181
  // ============================================================================
127
182
  // Constants
128
183
  // ============================================================================
@@ -166,6 +221,8 @@ export class AgentSession {
166
221
  _resourceLoader;
167
222
  _customTools;
168
223
  _cwd;
224
+ /** Per-agent persistent shell session identity: stable across runtime reloads, disposed with the session. */
225
+ _shellSessionKey = `agent:${randomUUID()}`;
169
226
  _agentDir;
170
227
  _collectWorkspaceSources;
171
228
  _localRuntimeController;
@@ -175,11 +232,22 @@ export class AgentSession {
175
232
  _repairModeSessionCounts = new Map();
176
233
  _textProtocolParseFailures = new Map();
177
234
  _textProtocolParseObservedThisTurn = false;
235
+ /** Total text-protocol parse-failure events observed this session, backing the
236
+ * {@link TEXT_TOOL_PROTOCOL_STEER_INTERVAL} throttle on {@link _maybeInjectTextProtocolCorrectiveSteer}. */
237
+ _textProtocolCorrectiveSteerCount = 0;
238
+ /** Monotonic counter backing tool-probe/text-protocol-calibration spawned-usage reportIds
239
+ * (see {@link _nextProbeUsageReportId}); guarantees each probe/calibration completion in the
240
+ * session lands in the cost ledger exactly once, even across repeated /toolprobe invocations. */
241
+ _toolProbeUsageReportSeq = 0;
242
+ /** Anti-loop: modelKeys the evidence-gated auto-probe has already fired for THIS session (see
243
+ * {@link _maybeAutoProbeOnValidationEscalation}), so a burst of validation-escalation events for
244
+ * the same still-failing model never re-fires the probe more than once per session. */
245
+ _autoProbedModels = new Set();
178
246
  _textProtocolValidationOutcomeThisTurn;
179
247
  /** Assembles the session's base system prompt from live session state (see
180
248
  * system-prompt-builder.ts); owns the paired _baseSystemPromptOptions. */
181
249
  _systemPromptBuilder;
182
- /** G3/G8 autonomy telemetry sink + status/diagnostic snapshots (see autonomy-telemetry.ts); owns
250
+ /** Autonomy telemetry sink + status/diagnostic snapshots (see autonomy-telemetry.ts); owns
183
251
  * the latest gate outcome and the bounded gate-outcome history. */
184
252
  _autonomyTelemetry;
185
253
  /** Goal auto-continue + research lane + scout-worker delegation + model-fitness probe (see
@@ -208,6 +276,13 @@ export class AgentSession {
208
276
  _analytics;
209
277
  _treeNavigator;
210
278
  _lastCostGuardDecision;
279
+ /**
280
+ * `getSpawnedUsage().cost` snapshotted at the start of the CURRENT foreground prompt cycle (see
281
+ * `_promptUnserialized`), so the cost guard can attribute only background/spawned spend since THIS
282
+ * turn began, not the session's entire lifetime spend. Reset on every new user prompt; every
283
+ * round-trip within the same turn (tool-call iterations) shares this one baseline.
284
+ */
285
+ _costGuardTurnBaselineUsd = 0;
211
286
  /** Per-turn model-router subsystem (see model-router-controller.ts); owns the transient route/intent,
212
287
  * the cheap-turn session buffer, the escalation/retry flags, and the sticky last-decision/skip-reason
213
288
  * used by the status report. Its parallel routed drive path delegates every turn back to
@@ -339,6 +414,9 @@ export class AgentSession {
339
414
  getActiveExtensions: () => this._extensionRunner.activeExtensions,
340
415
  getContextWindow: () => this.model?.contextWindow,
341
416
  getThinkingLevel: () => this.thinkingLevel,
417
+ // The evidence-gated tool-selection hint block — self-gated by kill switch/evidence
418
+ // thresholds inside getActiveHints() itself, so this is a plain always-on pass-through.
419
+ getToolSelectionHints: () => this._toolSelection.getActiveHints(),
342
420
  });
343
421
  this._autonomyTelemetry = new AutonomyTelemetry({
344
422
  getSessionManager: () => this.sessionManager,
@@ -365,6 +443,7 @@ export class AgentSession {
365
443
  isModelExhausted: (model) => this._billingFailover.isExhausted(`${model.provider}/${model.id}`),
366
444
  getModel: () => this.model ?? undefined,
367
445
  isDelegateToolActive: () => this.getActiveToolNames().includes("delegate"),
446
+ isGoalToolActive: () => this.getActiveToolNames().includes("goal"),
368
447
  getCapabilityEnvelope: () => this.capabilityEnvelope,
369
448
  getModelCapabilityProfile: () => this.getModelCapabilityProfile(),
370
449
  emit: (event) => this._emit(event),
@@ -378,7 +457,10 @@ export class AgentSession {
378
457
  readMemoryForLane: (query) => this._memory.readMemoryForLane(query),
379
458
  addSpawnedUsage: (usage, opts) => this.addSpawnedUsage(usage, opts),
380
459
  runIsolatedCompletion: (opts) => this.runIsolatedCompletion(opts),
381
- continueGoalLoop: (options) => this.continueGoalLoop(options),
460
+ // RAW loop, deliberately bypassing the public `continueGoalLoop` — that method now delegates
461
+ // to this controller's own `continueGoalLoopExclusive` guard, so routing through it here would
462
+ // recurse into the guard from inside itself instead of driving the actual continuation pass.
463
+ continueGoalLoop: (options) => this._goalContinuation.continueGoalLoop(options),
382
464
  collectWorkspaceSources: (args) => this._collectWorkspaceSources(args),
383
465
  });
384
466
  this._memory = new MemoryController({
@@ -404,6 +486,10 @@ export class AgentSession {
404
486
  // conservative in the safe direction for the summarizer capacity check.
405
487
  estimateSummarizationInputTokens: () => this._pipeline.estimateCurrentContextTokens(this.agent.state.messages),
406
488
  emitWarning: (message) => this._emit({ type: "warning", message }),
489
+ // Route a managed-local summarizer through the same readiness/residency gate every
490
+ // other isolated consumer uses, so compact() never calls a local model that was never
491
+ // confirmed up, installed, or resident (no-op for cloud models).
492
+ ensureModelReady: (model) => this._localRuntimeController.ensureIsolatedModelReady(model),
407
493
  });
408
494
  this._pipeline = new ContextPipeline({
409
495
  getTurnIndex: () => this._turnIndex,
@@ -419,8 +505,8 @@ export class AgentSession {
419
505
  addSpawnedUsage: (usage, opts) => this.addSpawnedUsage(usage, opts),
420
506
  runIsolatedCompletion: (opts) => this.runIsolatedCompletion(opts),
421
507
  });
422
- const failureCorpusPath = join(this._agentDir, "state", "failure-corpus.jsonl");
423
- this._toolRecoveryEventLogPath = join(this._agentDir, "state", TOOL_RECOVERY_EVENT_LOG_FILE);
508
+ const failureCorpusPath = stateFile(this._agentDir, "failure-corpus.jsonl");
509
+ this._toolRecoveryEventLogPath = stateFile(this._agentDir, TOOL_RECOVERY_EVENT_LOG_FILE);
424
510
  const toolRepairSettings = this._toolRepairSettings();
425
511
  this._failureCorpus = new FailureCorpusRecorder({
426
512
  filePath: failureCorpusPath,
@@ -459,6 +545,7 @@ export class AgentSession {
459
545
  emitAutonomyTelemetry: (event) => this._emitAutonomyTelemetry(event),
460
546
  resolveLaneModel: (pattern) => this._backgroundLanes.resolveLaneModel(pattern),
461
547
  resolveCurationModelIfFit: () => this._resolveCurationModelIfFit(),
548
+ getToolProbeVerdict: (model) => this._toolProbeVerdict(model),
462
549
  });
463
550
  this._reflection = new ReflectionController({
464
551
  getModel: () => this.model,
@@ -482,6 +569,7 @@ export class AgentSession {
482
569
  this._goalContinuation = new GoalLoopController({
483
570
  getGoalRuntimeSnapshot: (settings) => this.getGoalRuntimeSnapshot(settings),
484
571
  prompt: (text, options) => this.prompt(text, options),
572
+ recordGoalContinuationPass: (pass) => this.recordGoalContinuationPass(pass),
485
573
  });
486
574
  this._extensionRunnerRef = config.extensionRunnerRef;
487
575
  this._initialActiveToolNames = config.initialActiveToolNames;
@@ -498,6 +586,7 @@ export class AgentSession {
498
586
  this._runtimeBuilder = new RuntimeBuilder({
499
587
  getAgent: () => this.agent,
500
588
  getCwd: () => this._cwd,
589
+ getShellSessionKey: () => this._shellSessionKey,
501
590
  getAgentDir: () => this._agentDir,
502
591
  getSessionManager: () => this.sessionManager,
503
592
  getSettingsManager: () => this.settingsManager,
@@ -560,11 +649,13 @@ export class AgentSession {
560
649
  startWorkerDelegation: (request) => this._backgroundLanes.startWorkerDelegation(request),
561
650
  getWorkerLaneRecords: () => this._backgroundLanes.getLaneRecords(),
562
651
  getWorkerResultSnapshots: () => this.getWorkerResultSnapshots(),
652
+ resolveManagedLaneId: (id) => this._backgroundLanes.resolveManagedLaneId(id),
563
653
  runWorkerDelegationOnce: (request) => this.runWorkerDelegationOnce(request),
564
654
  runModelFitness: (args) => this.runModelFitness(args),
565
655
  resolveCurationModelIfFit: () => this._resolveCurationModelIfFit(),
566
656
  runIsolatedCompletion: (opts) => this.runIsolatedCompletion(opts),
567
657
  addSpawnedUsage: (usage, opts) => this.addSpawnedUsage(usage, opts),
658
+ getLaneWorkerRefusal: () => this.getLaneWorkerRefusal(),
568
659
  createAgentContextSnapshot: () => this._createAgentContextSnapshot(),
569
660
  getContextUsage: () => this.getContextUsage(),
570
661
  isStreaming: () => this.isStreaming,
@@ -573,6 +664,10 @@ export class AgentSession {
573
664
  getExtensionCommandContextActions: () => this._extensionCommandContextActions,
574
665
  getExtensionShutdownHandler: () => this._extensionShutdownHandler,
575
666
  getExtensionErrorListener: () => this._extensionErrorListener,
667
+ // Stop any pi-spawned local runtime the just-committed reload no longer routes to.
668
+ reconcileLocalRuntimes: () => {
669
+ this._localRuntimeController.reconcile(this._collectEligibleLocalModelsForReconcile());
670
+ },
576
671
  });
577
672
  this._analytics = new SessionAnalytics({
578
673
  getState: () => this.state,
@@ -633,6 +728,7 @@ export class AgentSession {
633
728
  getSessionManager: () => this.sessionManager,
634
729
  getSettingsManager: () => this.settingsManager,
635
730
  isStreaming: () => this.isStreaming,
731
+ getShellSessionKey: () => this._shellSessionKey,
636
732
  });
637
733
  this._profileFilter = new ProfileFilterController({
638
734
  getSettingsManager: () => this.settingsManager,
@@ -847,13 +943,30 @@ export class AgentSession {
847
943
  const settings = this._getAdaptedCompactionSettings();
848
944
  const contextWindow = this.model?.contextWindow ?? 0;
849
945
  if (settings.enabled && contextWindow > 0 && !this.isCompacting) {
946
+ const triggerTokens = this.model?.autoCompactionTriggerTokens;
850
947
  const contextTokens = this._estimateCurrentContextTokens(authoritativeMessages);
851
- if (shouldCompact(contextTokens, contextWindow, settings, this.model?.autoCompactionTriggerTokens)) {
852
- const latestBefore = getLatestCompactionEntry(this.sessionManager.getBranch())?.id;
853
- await this._runAutoCompaction("threshold", false);
854
- const latestAfter = getLatestCompactionEntry(this.sessionManager.getBranch())?.id;
855
- if (latestAfter && latestAfter !== latestBefore) {
856
- currentMessages = this.agent.state.messages.slice();
948
+ if (shouldCompact(contextTokens, contextWindow, settings, triggerTokens)) {
949
+ // This pre-check runs BEFORE context-gc (below, same transform pass), so the
950
+ // raw estimate above can't see this turn's own GC packing. Since context-gc later
951
+ // packs the SAME messages before they're ever sent, a raw-over-threshold turn that
952
+ // GC alone would bring back under threshold doesn't actually need compaction.
953
+ // Project this turn's GC pass read-only (writePayloads=false -- no digest/curation
954
+ // enqueue, no disk write, no artifact-reference release; see
955
+ // ContextPipeline.applyContextGc) to get the same packed output the real pass would
956
+ // produce for these messages, then re-check against ITS estimate. Packing only ever
957
+ // shrinks (never grows) a message, so the projected estimate is never higher than
958
+ // the raw one: this can only SUPPRESS an unnecessary compaction, never skip a
959
+ // genuinely needed one -- the hard near-full trigger inside shouldCompact still
960
+ // fires whenever the projected estimate itself remains over threshold.
961
+ const gcProjection = this._applyContextGc(authoritativeMessages, false);
962
+ const projectedContextTokens = this._estimateCurrentContextTokens(gcProjection.messages);
963
+ if (shouldCompact(projectedContextTokens, contextWindow, settings, triggerTokens)) {
964
+ const latestBefore = getLatestCompactionEntry(this.sessionManager.getBranch())?.id;
965
+ await this._runAutoCompaction("threshold", false);
966
+ const latestAfter = getLatestCompactionEntry(this.sessionManager.getBranch())?.id;
967
+ if (latestAfter && latestAfter !== latestBefore) {
968
+ currentMessages = this.agent.state.messages.slice();
969
+ }
857
970
  }
858
971
  }
859
972
  }
@@ -888,6 +1001,15 @@ export class AgentSession {
888
1001
  * model, full system prompt, converted messages, and tool schemas are known. The guard is a
889
1002
  * projection threshold rather than a hard output cap: warning mode never reduces capability, while
890
1003
  * opt-in downgrade changes only this request's reasoning effort. Best-effort: never throws.
1004
+ *
1005
+ * The ceiling is turn-cumulative: the next foreground call's projection is folded together with
1006
+ * background/research/worker/reflection spend recorded SINCE THIS TURN BEGAN — {@link getSpawnedUsage}'s
1007
+ * already-recorded rollup (the same read-side-deduped total the footer's SUBAGENTS line uses) minus the
1008
+ * baseline snapshotted at the top of `_promptUnserialized` ({@link _costGuardTurnBaselineUsd}) — so a
1009
+ * turn that is cheap in the foreground but has spent heavily via background lanes THIS turn still trips
1010
+ * the warning, while a prior turn's background spend does not keep every later turn's guard stuck
1011
+ * "over". A background lane that finishes mid-turn is attributed to whichever turn it completes in.
1012
+ * Per-lane dollar caps (research/worker `maxUsd`) are separate and untouched by this guard.
891
1013
  */
892
1014
  _resolveCostGuardRequestReasoning(model, context, reasoning, requestMaxTokens) {
893
1015
  try {
@@ -907,7 +1029,10 @@ export class AgentSession {
907
1029
  cost: model.cost,
908
1030
  longContextPricing: model.longContextPricing,
909
1031
  });
910
- const decision = evaluateCostGuard(estUsd, { maxTurnUsd: guard.maxTurnUsd, action: guard.action });
1032
+ // Only spend recorded SINCE this turn's baseline counts -- never negative (a dedup/rollup
1033
+ // correction could otherwise move the total backward transiently).
1034
+ const cumulativeBackgroundUsd = Math.max(0, this.getSpawnedUsage().cost - this._costGuardTurnBaselineUsd);
1035
+ const decision = evaluateCostGuard(estUsd, { maxTurnUsd: guard.maxTurnUsd, action: guard.action }, cumulativeBackgroundUsd);
911
1036
  this._lastCostGuardDecision = decision;
912
1037
  if (!decision.over || guard.action !== "downgrade" || reasoning === undefined)
913
1038
  return reasoning;
@@ -925,7 +1050,7 @@ export class AgentSession {
925
1050
  }
926
1051
  get _skillCurator() {
927
1052
  if (!this._skillCuratorInstance) {
928
- this._skillCuratorInstance = new SkillCurator(join(this._agentDir, "skills"));
1053
+ this._skillCuratorInstance = new SkillCurator(resourceDir("skills", this._agentDir));
929
1054
  }
930
1055
  return this._skillCuratorInstance;
931
1056
  }
@@ -1024,6 +1149,28 @@ export class AgentSession {
1024
1149
  }
1025
1150
  return this.agent.streamFn(model, context, requestOptions);
1026
1151
  }
1152
+ /** Builds a reportId that uniquely identifies one probe/calibration completion within the
1153
+ * session: the model, a caller-supplied `kind` (e.g. "read-task", "echo", or
1154
+ * "text-protocol:<variant>"), and a monotonic sequence number. The sequence number (not
1155
+ * wall-clock time or randomness) is what guarantees no collision even when the same model+kind
1156
+ * is probed again later in the session (e.g. a repeated /toolprobe run), so genuine repeat spend
1157
+ * is never silently deduped away. */
1158
+ _nextProbeUsageReportId(model, kind) {
1159
+ return `tool-probe:${this._modelRef(model)}:${kind}:${this._toolProbeUsageReportSeq++}`;
1160
+ }
1161
+ /** Awaits a tool-probe/text-protocol-calibration stream's result and, if the resolved message
1162
+ * carries reportable usage, rolls it into spawned usage under `reportId` so the turn-scoped cost
1163
+ * guard and daily usage see it. These trial completions previously spent tokens with no
1164
+ * addSpawnedUsage call, making them invisible to both. `addSpawnedUsage` dedups on `reportId`, so
1165
+ * a re-report of the same id is a no-op. */
1166
+ async _resolveProbeStreamCountingUsage(stream, label, reportId) {
1167
+ const message = await stream.result();
1168
+ const usage = message.usage;
1169
+ if (usage && (usage.cost.total > 0 || usage.totalTokens > 0)) {
1170
+ this.addSpawnedUsage(usage, { label, reportId });
1171
+ }
1172
+ return message;
1173
+ }
1027
1174
  _textProtocolCalibrationContext(variant, token) {
1028
1175
  const primer = generateTextToolProtocolPrimer([TEXT_TOOL_PROTOCOL_ECHO_TOOL], { variant });
1029
1176
  const instruction = `Text tool protocol calibration trial. Using the protocol above, call echo with data exactly "${token}". Output only the tool-call envelope.`;
@@ -1055,7 +1202,8 @@ export class AgentSession {
1055
1202
  messages: [{ role: "user", content: [{ type: "text", text: instruction }], timestamp: Date.now() }],
1056
1203
  tools: [NATIVE_TOOL_PROBE_READ_TOOL],
1057
1204
  }, { textToolCallProtocol: false, maxRetries: 0, temperature: 0, maxTokens: 768 });
1058
- return this._messageHasToolCallWithStringArgument(await stream.result(), "read", "path", path);
1205
+ const message = await this._resolveProbeStreamCountingUsage(stream, "tool-probe", this._nextProbeUsageReportId(model, "read-task"));
1206
+ return this._messageHasToolCallWithStringArgument(message, "read", "path", path);
1059
1207
  }
1060
1208
  async _runNativeEchoToolProbeTrial(model, token) {
1061
1209
  const instruction = `Native tool-call capability probe: echo-only. Use provider-native tool calling, not prose. ` +
@@ -1065,7 +1213,8 @@ export class AgentSession {
1065
1213
  messages: [{ role: "user", content: [{ type: "text", text: instruction }], timestamp: Date.now() }],
1066
1214
  tools: [TEXT_TOOL_PROTOCOL_ECHO_TOOL],
1067
1215
  }, { textToolCallProtocol: false, maxRetries: 0, temperature: 0, maxTokens: 256 });
1068
- return this._messageHasToolCallWithStringArgument(await stream.result(), "echo", "data", token);
1216
+ const message = await this._resolveProbeStreamCountingUsage(stream, "tool-probe", this._nextProbeUsageReportId(model, "echo"));
1217
+ return this._messageHasToolCallWithStringArgument(message, "echo", "data", token);
1069
1218
  }
1070
1219
  async _gradeNativeToolCallingForModel(model, token) {
1071
1220
  const path = join(getProcessWorkRun(this._agentDir, "probes", "native-tools").path, `pi-native-probe-${process.pid}-${Date.now()}.txt`);
@@ -1090,7 +1239,7 @@ export class AgentSession {
1090
1239
  temperature: 0,
1091
1240
  maxTokens: 256,
1092
1241
  });
1093
- const message = await stream.result();
1242
+ const message = await this._resolveProbeStreamCountingUsage(stream, "text-protocol-calibration", this._nextProbeUsageReportId(model, `text-protocol:${variant}`));
1094
1243
  const text = message.content
1095
1244
  .filter((block) => block.type === "text")
1096
1245
  .map((block) => block.text)
@@ -1127,40 +1276,51 @@ export class AgentSession {
1127
1276
  }
1128
1277
  return { status: "failed", attemptedAt, variantsTried };
1129
1278
  }
1279
+ /**
1280
+ * A PURE READER of the persisted calibration — never calibrates inline and
1281
+ * never throws out of the prompt path. A model whose flag is on but has no valid current-version
1282
+ * protocol on record falls back to native for this turn (with a warning pointing at /toolprobe)
1283
+ * instead of blocking the user's message behind up to 8 inline calibration completions or
1284
+ * aborting the turn entirely. Calibration now only ever happens off the hot path: explicit
1285
+ * /toolprobe, or the capability-gate spine's evidence-gated auto-probe.
1286
+ */
1130
1287
  async _ensureTextToolProtocolForActiveModel() {
1131
1288
  const model = this.agent.state.model;
1132
1289
  if (!this._textProtocolFlag(model)) {
1133
1290
  this.agent.textToolCallProtocol = undefined;
1134
1291
  return;
1135
1292
  }
1293
+ // Force-enable (a global settings override or Model.textToolCallProtocol) wins regardless of
1294
+ // whether this model has ever been graded-probed — that is the point of an explicit override.
1295
+ // Preserve it exactly as before: default to the tool-tag variant, no inline calibration. Only
1296
+ // the graded-evidence path below (flag on solely because of a persisted toolProbe verdict)
1297
+ // needs a valid persisted protocol to proceed.
1298
+ const forceEnabled = this._toolRepairSettings().textProtocol === true || model?.textToolCallProtocol === true;
1136
1299
  const modelKey = this._modelAdaptationKeyFor(model);
1137
- if (!modelKey) {
1300
+ if (!modelKey || forceEnabled) {
1138
1301
  this.agent.textToolCallProtocol = true;
1139
1302
  return;
1140
1303
  }
1141
1304
  const profile = this._modelAdaptationStore.get(modelKey);
1142
- if (profile.protocol?.version === TEXT_TOOL_PROTOCOL_VERSION) {
1143
- if (profile.protocol.status === "failed") {
1144
- this.agent.textToolCallProtocol = undefined;
1145
- throw new Error(`Previous text tool protocol calibration failed for ${modelKey} at ${profile.protocol.attemptedAt}. ` +
1146
- `Variants tried: ${profile.protocol.variantsTried.join(", ")}. ` +
1147
- `Run /toolhealth for details or /toolprotocol-reset ${modelKey} to retry calibration.`);
1148
- }
1305
+ if (profile.protocol?.version === TEXT_TOOL_PROTOCOL_VERSION && profile.protocol.status !== "failed") {
1149
1306
  this.agent.textToolCallProtocol = { variant: profile.protocol.variant };
1150
1307
  return;
1151
1308
  }
1152
- const result = await this._calibrateTextToolProtocolForModel(model, modelKey, { persistFailure: true });
1153
- if (result.status === "calibrated") {
1154
- this.agent.textToolCallProtocol = { variant: result.variant };
1155
- return;
1156
- }
1157
1309
  this.agent.textToolCallProtocol = undefined;
1158
- throw new Error(`Model ${modelKey} cannot follow the text tool protocol after calibration. ` +
1159
- `Run /toolhealth for details or /toolprotocol-reset ${modelKey} to retry calibration.`);
1310
+ this._emit({
1311
+ type: "warning",
1312
+ message: profile.protocol?.status === "failed"
1313
+ ? `Text tool protocol calibration for ${modelKey} previously failed (variants tried: ${profile.protocol.variantsTried.join(", ")}); falling back to native tool calls this turn. Run /toolprobe ${modelKey} to recalibrate.`
1314
+ : `Text tool protocol for ${modelKey} has no valid calibration on record; falling back to native tool calls this turn. Run /toolprobe ${modelKey} to calibrate.`,
1315
+ });
1160
1316
  }
1161
1317
  _modelRef(model) {
1162
1318
  return `${model.provider}/${model.id}`;
1163
1319
  }
1320
+ /** The persisted `/toolprobe` verdict on record for this model, if any (undefined = unprobed). */
1321
+ _toolProbeVerdict(model) {
1322
+ return this._modelAdaptationStore.get(this._modelRef(model)).toolProbe?.status;
1323
+ }
1164
1324
  _formatToolProbeReport(results) {
1165
1325
  const lines = [
1166
1326
  "Tool probe results:",
@@ -1258,11 +1418,22 @@ export class AgentSession {
1258
1418
  }
1259
1419
  return { results, table: this._formatToolProbeReport(results) };
1260
1420
  }
1421
+ /**
1422
+ * The text-protocol circuit breaker. Every parse failure (from either the
1423
+ * live per-completion {@link Agent.onTextToolProtocolParse} callback or the post-hoc detection in
1424
+ * {@link _recordTextToolProtocolParseOutcomeFromLastAssistant}) gets a throttled one-line
1425
+ * corrective steer for the next turn. On the 3rd consecutive SAME-SIGNATURE failure — graded
1426
+ * evidence a model genuinely cannot speak this dialect, not a single bad turn — the breaker also
1427
+ * demotes the persisted tool-probe verdict to "none" so {@link _textProtocolFlag} reads false next
1428
+ * turn and native is attempted instead of thrashing on a protocol this model has proven it can't
1429
+ * follow.
1430
+ */
1261
1431
  _handleTextToolProtocolParse(event) {
1262
1432
  this._textProtocolParseObservedThisTurn = true;
1263
1433
  const modelKey = `${event.provider}/${event.model}`;
1264
1434
  if (event.status === "parsed")
1265
1435
  return;
1436
+ this._maybeInjectTextProtocolCorrectiveSteer(event.variant);
1266
1437
  const signature = `${event.variant}:${event.reason ?? "failed"}`;
1267
1438
  const previous = this._textProtocolParseFailures.get(modelKey);
1268
1439
  const repeats = previous?.signature === signature ? previous.repeats + 1 : 1;
@@ -1275,6 +1446,43 @@ export class AgentSession {
1275
1446
  this.agent.textToolCallProtocol = undefined;
1276
1447
  }
1277
1448
  this._textProtocolParseFailures.delete(modelKey);
1449
+ // Demote on graded evidence -- TEXT_TOOL_PROTOCOL_PARSE_FAILURE_THRESHOLD identical-signature
1450
+ // parse failures in a row -- rather than thrashing every turn. The fresh probedAt doubles as
1451
+ // the capability-gate spine's auto-probe freshness gate, so this demotion doesn't immediately
1452
+ // trigger a re-probe/re-phone loop.
1453
+ const probedAt = new Date().toISOString();
1454
+ this._modelAdaptationStore.setToolProbe(modelKey, {
1455
+ version: TEXT_TOOL_PROTOCOL_VERSION,
1456
+ status: "none",
1457
+ probedAt,
1458
+ nativeGrade: profile.toolProbe?.nativeGrade,
1459
+ diagnostic: `Text protocol parsing failed ${TEXT_TOOL_PROTOCOL_PARSE_FAILURE_THRESHOLD}x in a row with signature "${signature}".`,
1460
+ }, probedAt);
1461
+ this._emit({
1462
+ type: "warning",
1463
+ message: `Text tool protocol for ${modelKey} stopped parsing after ${TEXT_TOOL_PROTOCOL_PARSE_FAILURE_THRESHOLD} attempts; demoted to native fallback. Run /toolprobe ${modelKey} to recalibrate.`,
1464
+ });
1465
+ }
1466
+ /**
1467
+ * A phone model whose envelope fails to parse gets no corrective guidance today —
1468
+ * the runaway-loop backstop only trips on repeated PARSED tool-call signatures (agent-loop.ts), so
1469
+ * an every-turn unparseable-prose model never trips it. Inject a one-line reminder of the envelope
1470
+ * shape as a nextTurn message, reusing {@link formatVariantEnvelope} so the reminder can never
1471
+ * drift from the grammar the primer actually teaches. Throttled to the first failure this session
1472
+ * and then every {@link TEXT_TOOL_PROTOCOL_STEER_INTERVAL}th after — the breaker above is what
1473
+ * actually demotes a genuinely-failing model at the 3rd same-signature failure; this is guidance,
1474
+ * not a second counter.
1475
+ */
1476
+ _maybeInjectTextProtocolCorrectiveSteer(variant) {
1477
+ this._textProtocolCorrectiveSteerCount++;
1478
+ if (this._textProtocolCorrectiveSteerCount !== 1 &&
1479
+ this._textProtocolCorrectiveSteerCount % TEXT_TOOL_PROTOCOL_STEER_INTERVAL !== 0) {
1480
+ return;
1481
+ }
1482
+ const reminder = `Reminder: to call a tool, emit exactly this envelope shape: ${formatVariantEnvelope(variant, "TOOL", '{"arg":"value"}')} — no other format is recognized. Reasoning may appear as prose before the envelope, never inside it.`;
1483
+ this.sendCustomMessage({ customType: "text-protocol-corrective-steer", content: reminder, display: false }, { deliverAs: "nextTurn" }).catch(() => {
1484
+ // Best-effort steer; a failure to queue it must not break parse-failure handling.
1485
+ });
1278
1486
  }
1279
1487
  _handleTextToolProtocolValidationOutcome(event) {
1280
1488
  if (event.source !== "text-protocol")
@@ -1543,10 +1751,10 @@ export class AgentSession {
1543
1751
  ...this._profileFilter.profileDeniedResourceObservations(),
1544
1752
  ...this._profileFilter.getInertExtensionWarnings(),
1545
1753
  ...this._unboundToolGrantWarnings,
1546
- // G7: auto-built per-turn foreground envelope (observe-only; not enforced). Falls back to a
1754
+ // Auto-built per-turn foreground envelope (observe-only; not enforced). Falls back to a
1547
1755
  // live preview when no turn has run yet so /context always shows the current scope.
1548
1756
  formatForegroundEnvelopeObservation(this._currentForegroundEnvelope ?? this._buildForegroundEnvelopeFromState()),
1549
- // G14 (ratified): a user disable always beats a profile grant — surface the conflict.
1757
+ // A user disable always beats a profile grant — surface the conflict.
1550
1758
  ...["tools", "skills", "prompts", "extensions"].flatMap((kind) => this.settingsManager
1551
1759
  .getProfileGrantsOverriddenByUserDisable(kind)
1552
1760
  .map((entry) => `profile grants ${kind} "${entry}" but your disable list overrides it (user disable wins; re-enable to use)`)),
@@ -1558,7 +1766,10 @@ export class AgentSession {
1558
1766
  return formatContextCompositionDashboard(this.getContextCompositionReport());
1559
1767
  }
1560
1768
  formatToolRepairHealthReport() {
1561
- return formatToolRepairHealthReport(this._modelAdaptationStore, new Date(), this._toolRecoveryLogger.getStats());
1769
+ return [
1770
+ formatToolRepairHealthReport(this._modelAdaptationStore, new Date(), this._toolRecoveryLogger.getStats()),
1771
+ formatToolSelectionReport(this._toolSelection.getReport()),
1772
+ ].join("\n\n");
1562
1773
  }
1563
1774
  async flushToolRecoveryLogsForTests(timeoutMs = 1000) {
1564
1775
  await this._toolRecoveryLogger.flush(timeoutMs);
@@ -1625,6 +1836,119 @@ export class AgentSession {
1625
1836
  _installAgentToolHooks() {
1626
1837
  this.agent.beforeToolCall = this._toolGate.beforeToolCall;
1627
1838
  this.agent.afterToolCall = this._toolGate.afterToolCall;
1839
+ this.agent.onRunawayStop = (info) => this._handleRunawayStop(info);
1840
+ this.agent.onToolValidationEscalation = (event) => this._handleToolValidationEscalation(event);
1841
+ }
1842
+ /**
1843
+ * The runaway-loop backstop ({@link Agent.maxStallTurns}) stopped a turn stuck repeating one
1844
+ * identical tool-call signature. Previously silent — this is the first host handler. Records a
1845
+ * session-log/telemetry entry (see {@link RUNAWAY_STOP_CUSTOM_TYPE}) and surfaces a user-visible
1846
+ * warning through the same event the context-window/compaction backstops use.
1847
+ */
1848
+ _handleRunawayStop(info) {
1849
+ const record = {
1850
+ signature: info.signature,
1851
+ repeats: info.repeats,
1852
+ model: this.model?.id,
1853
+ provider: this.model?.provider,
1854
+ at: new Date().toISOString(),
1855
+ };
1856
+ this.sessionManager.appendCustomEntry(RUNAWAY_STOP_CUSTOM_TYPE, record);
1857
+ this._emit({
1858
+ type: "warning",
1859
+ message: `Stopped: the model repeated the same tool call ${info.repeats} times in a row without making progress. Review the last tool result and steer or retry with a different approach.`,
1860
+ });
1861
+ }
1862
+ /** Anti-loop: true when this model's persisted tool-probe verdict was written within the
1863
+ * auto-probe freshness window ({@link AUTO_TOOL_PROBE_FRESHNESS_MS}) — covers a very recent
1864
+ * explicit `/toolprobe` run or a fresh circuit-breaker demote from an EARLIER session, complementing
1865
+ * the in-session {@link _autoProbedModels} latch. */
1866
+ _hasFreshToolProbeVerdict(modelKey) {
1867
+ const probedAt = this._modelAdaptationStore.get(modelKey).toolProbe?.probedAt;
1868
+ if (!probedAt)
1869
+ return false;
1870
+ const age = Date.now() - new Date(probedAt).getTime();
1871
+ return Number.isFinite(age) && age >= 0 && age < AUTO_TOOL_PROBE_FRESHNESS_MS;
1872
+ }
1873
+ /**
1874
+ * Evidence-gated native→phone auto-probe for a LOCAL/MANAGED model (never cloud — see
1875
+ * {@link isLocalOrManagedRouterModel}) that just crossed the tool-argument-validation escalation
1876
+ * threshold — repeated identical validation failures with no successful native call in between,
1877
+ * which is exactly the graded evidence {@link Agent.onToolValidationEscalation} already requires
1878
+ * before firing. Runs the SAME probe `/toolprobe` uses ({@link _probeToolCallingForModel}: native
1879
+ * trials first, so a model that can actually tool-call natively still resolves to verdict
1880
+ * "native" and is never phoned) entirely OFF the hot path — fired here but never awaited by the
1881
+ * caller, so a slow or failing probe can never block or throw the user's in-flight turn.
1882
+ * Anti-loop: skipped when this session already auto-probed this model, or a fresh persisted
1883
+ * verdict already exists ({@link _hasFreshToolProbeVerdict}) — otherwise a model that keeps
1884
+ * failing validation every turn would re-fire the (multi-completion) probe every single turn.
1885
+ */
1886
+ _maybeAutoProbeOnValidationEscalation(model) {
1887
+ const modelKey = this._modelRef(model);
1888
+ if (this._autoProbedModels.has(modelKey) || this._hasFreshToolProbeVerdict(modelKey))
1889
+ return;
1890
+ this._autoProbedModels.add(modelKey);
1891
+ void this._probeToolCallingForModel(model)
1892
+ .then((result) => {
1893
+ if (this._disposed)
1894
+ return;
1895
+ const detail = result.verdict === "text-protocol"
1896
+ ? ` (variant ${result.variant}); it will use the text tool protocol starting next turn`
1897
+ : result.verdict === "none"
1898
+ ? " — no working tool-call path was found; run /toolprobe for details"
1899
+ : "; native tool calls stay in use";
1900
+ this._emit({
1901
+ type: "warning",
1902
+ message: `Auto-probed ${modelKey} after repeated native tool-call validation failures: verdict "${result.verdict}"${detail}.`,
1903
+ });
1904
+ })
1905
+ .catch((error) => {
1906
+ if (this._disposed)
1907
+ return;
1908
+ this._emit({
1909
+ type: "warning",
1910
+ message: `Auto-probe for ${modelKey} (triggered by repeated tool-call validation failures) did not complete: ${error instanceof Error ? error.message : String(error)}.`,
1911
+ });
1912
+ });
1913
+ }
1914
+ /**
1915
+ * A repeated identical tool-argument-validation failure crossed the escalation threshold
1916
+ * ({@link Agent.toolValidationEscalationThreshold}) — the graded evidence the capability-gate
1917
+ * spine acts on. Always records a session-log/telemetry entry (see {@link
1918
+ * TOOL_VALIDATION_ESCALATION_CUSTOM_TYPE}), then branches on the failing model's class:
1919
+ * - LOCAL/MANAGED ({@link isLocalOrManagedRouterModel}, never cloud): the failure is evidence the
1920
+ * model may lack native tool-calling, so it fires the evidence-gated native→phone auto-probe
1921
+ * off the hot path ({@link _maybeAutoProbeOnValidationEscalation}). Escalating a local model's
1922
+ * ROUTER TIER on a tool-call failure would not fix a capability problem, so this branch never
1923
+ * touches the model router.
1924
+ * - CLOUD (known tool-capable): the failure is evidence the routed tier is too weak for this
1925
+ * request, so it escalates via {@link ModelRouterController.requestValidationFailureEscalation}
1926
+ * — de-conflated from the beforeToolCall mutation gate ({@link
1927
+ * ModelRouterController.maybeEscalateToolCall}/`shouldEscalateModelRouterTool`): repeated
1928
+ * validation failure is grounds to escalate REGARDLESS of the failing tool's mutation status,
1929
+ * so a read-only tool's repeated failure now escalates too (previously a no-op, since the old
1930
+ * code reused the mutation gate verbatim for this unrelated signal). Cloud models are never
1931
+ * probe-gated or phoned by this handler.
1932
+ * If the registry can no longer resolve `event.model`/`event.provider` (e.g. the model was
1933
+ * unregistered mid-session), falls back to the cloud/tier-escalation path — the previously
1934
+ * existing behavior — rather than silently dropping the signal.
1935
+ */
1936
+ _handleToolValidationEscalation(event) {
1937
+ const record = {
1938
+ tool: event.tool,
1939
+ signature: event.signature,
1940
+ repeats: event.repeats,
1941
+ model: event.model,
1942
+ provider: event.provider,
1943
+ at: new Date().toISOString(),
1944
+ };
1945
+ this.sessionManager.appendCustomEntry(TOOL_VALIDATION_ESCALATION_CUSTOM_TYPE, record);
1946
+ const model = this._modelRegistry.find(event.provider, event.model);
1947
+ if (model && isLocalOrManagedRouterModel(model)) {
1948
+ this._maybeAutoProbeOnValidationEscalation(model);
1949
+ return;
1950
+ }
1951
+ this._modelRouter.requestValidationFailureEscalation();
1628
1952
  }
1629
1953
  // =========================================================================
1630
1954
  // Event Subscription
@@ -1970,18 +2294,19 @@ export class AgentSession {
1970
2294
  this.abortCompaction();
1971
2295
  this.abortBranchSummary();
1972
2296
  this.abortBash();
2297
+ disposePersistentShellSession(this._shellSessionKey);
1973
2298
  this._cancelPrefixWarm();
1974
2299
  this.agent.abort();
1975
- // R8: stop any deployment-registered gateway channels / schedulers.
2300
+ // Stop any deployment-registered gateway channels / schedulers.
1976
2301
  void this._gatewayRegistry.stop().catch(() => { });
1977
- // Bug #21: abort any in-flight background reflection so it cannot keep spending tokens or
2302
+ // Abort any in-flight background reflection so it cannot keep spending tokens or
1978
2303
  // write memory/skills against this now-disposed session.
1979
2304
  this._disposed = true;
1980
2305
  this._reflectionAbort.abort();
1981
2306
  // Abort any in-flight research pass or delegated worker for the same reason: a disposed
1982
2307
  // session must not keep spending tokens or persist evidence against dead state.
1983
2308
  this._backgroundLanes.abortInFlightLanes();
1984
- // Bug #20: clear the hooks this session installed on the shared agent so their closures stop
2309
+ // Clear the hooks this session installed on the shared agent so their closures stop
1985
2310
  // pinning this (deactivated) session — and all its history/maps — in memory if the agent
1986
2311
  // instance outlives the session.
1987
2312
  this.agent.afterToolCall = undefined;
@@ -1994,7 +2319,7 @@ export class AgentSession {
1994
2319
  this._disconnectFromAgent();
1995
2320
  this._eventListeners = [];
1996
2321
  // Best-effort memory cleanup (release locks/handles). Write-side onSessionEnd is wired on a
1997
- // true session-end hook (P3); file-store shutdown is a no-op.
2322
+ // true session-end hook; file-store shutdown is a no-op.
1998
2323
  void this._memory
1999
2324
  .getMemoryManager()
2000
2325
  .shutdownAll()
@@ -2049,7 +2374,7 @@ export class AgentSession {
2049
2374
  getActiveToolNames() {
2050
2375
  return this.agent.state.tools.map((t) => t.name);
2051
2376
  }
2052
- /** G7: build a foreground {@link CapabilityEnvelope} from the live session state (active tools, cwd, cost ceiling). */
2377
+ /** Build a foreground {@link CapabilityEnvelope} from the live session state (active tools, cwd, cost ceiling). */
2053
2378
  _buildForegroundEnvelopeFromState() {
2054
2379
  return buildForegroundEnvelope({
2055
2380
  turnIndex: this._turnIndex,
@@ -2059,7 +2384,7 @@ export class AgentSession {
2059
2384
  });
2060
2385
  }
2061
2386
  /**
2062
- * G7: (re)build the foreground envelope for the current turn. Visibility only -- the foreground
2387
+ * (Re)build the foreground envelope for the current turn. Visibility only -- the foreground
2063
2388
  * envelope is NOT enforced this round. Best-effort: never throws into the turn.
2064
2389
  */
2065
2390
  _refreshForegroundEnvelope() {
@@ -2070,7 +2395,7 @@ export class AgentSession {
2070
2395
  // Visibility only: a failure to build the envelope must never disturb the turn.
2071
2396
  }
2072
2397
  }
2073
- /** G7: the auto-constructed foreground envelope for the current/most-recent turn (visibility only). */
2398
+ /** The auto-constructed foreground envelope for the current/most-recent turn (visibility only). */
2074
2399
  getForegroundEnvelope() {
2075
2400
  return this._currentForegroundEnvelope;
2076
2401
  }
@@ -2199,7 +2524,7 @@ export class AgentSession {
2199
2524
  }
2200
2525
  /**
2201
2526
  * Build a system prompt for a specific tool surface WITHOUT touching the session's base prompt
2202
- * state (G4 router-swap; see {@link SystemPromptBuilder.buildSystemPromptForToolNames}).
2527
+ * state (used by the router's model swap; see {@link SystemPromptBuilder.buildSystemPromptForToolNames}).
2203
2528
  */
2204
2529
  _buildSystemPromptForToolNames(toolNames) {
2205
2530
  return this._systemPromptBuilder.buildSystemPromptForToolNames(toolNames);
@@ -2236,6 +2561,12 @@ export class AgentSession {
2236
2561
  getTransformersRuntime(modelId, baseUrl) {
2237
2562
  return this._localRuntimeController.getTransformersRuntime(modelId, baseUrl);
2238
2563
  }
2564
+ /** Shared {@link PrismLlamaCppRuntime} for pi's own managed prism install — see
2565
+ * {@link LocalRuntimeController.getPrismLlamaCppRuntime}. Delegates so `/models` and the
2566
+ * readiness gate share the SAME cached instance, same contract as getLocalRuntime above. */
2567
+ getPrismLlamaCppRuntime() {
2568
+ return this._localRuntimeController.getPrismLlamaCppRuntime();
2569
+ }
2239
2570
  /** models.json registers a local model's baseUrl as `<server>/v1` (OpenAI-compat); the runtime's
2240
2571
  * own health/boot endpoints are on the Ollama-native server root. Delegates to
2241
2572
  * {@link LocalRuntimeController}; kept here for `_warnIfManualModelChoiceIsRisky`'s own use. */
@@ -2250,6 +2581,27 @@ export class AgentSession {
2250
2581
  async _ensureRouteModelReady(resolved) {
2251
2582
  return this._localRuntimeController.ensureRouteModelReady(resolved);
2252
2583
  }
2584
+ /**
2585
+ * Every local model the CURRENT (post-reload) configuration could still route a turn to —
2586
+ * the foreground model plus any router tier (cheap/medium/expensive) that still resolves to a
2587
+ * real, authed, non-exhausted model. Fed to {@link LocalRuntimeController.reconcile} via the
2588
+ * `reconcileLocalRuntimes` hook above, ONLY after a reload generation has fully committed, so a
2589
+ * local model dropped from the live configuration has its pi-spawned runtime stopped instead of
2590
+ * leaking a child process, while one still referenced here is left untouched. Read-only — never
2591
+ * used for routing itself.
2592
+ */
2593
+ _collectEligibleLocalModelsForReconcile() {
2594
+ const models = [];
2595
+ const foregroundModel = this.agent.state.model;
2596
+ if (foregroundModel)
2597
+ models.push(foregroundModel);
2598
+ for (const tier of ["cheap", "medium", "expensive"]) {
2599
+ const resolved = this._modelRouter.resolveConfiguredTierModel(tier);
2600
+ if (resolved)
2601
+ models.push(resolved);
2602
+ }
2603
+ return models;
2604
+ }
2253
2605
  getModelRouterStatus(formatLabel) {
2254
2606
  return this._modelRouter.getStatus(formatLabel);
2255
2607
  }
@@ -2310,6 +2662,10 @@ export class AgentSession {
2310
2662
  return this._promptUnserialized(text, options);
2311
2663
  }
2312
2664
  async _promptUnserialized(text, options) {
2665
+ // Start of a new foreground prompt cycle -- rebaseline the cost guard's background-spend
2666
+ // window so a PRIOR turn's background/spawned spend doesn't keep this turn's guard permanently
2667
+ // tripped. Every round trip within this same turn (tool-call iterations) shares this baseline.
2668
+ this._costGuardTurnBaselineUsd = this.getSpawnedUsage().cost;
2313
2669
  this._applyToolRepairLayerSettings();
2314
2670
  this._cancelPrefixWarm();
2315
2671
  const expandPromptTemplates = options?.expandPromptTemplates ?? true;
@@ -2322,7 +2678,7 @@ export class AgentSession {
2322
2678
  // selected/authenticated — can un-register it from _earlyDisplayedUserMessages instead of
2323
2679
  // leaking the reference forever.
2324
2680
  let userMessage;
2325
- // R4 effectiveness feedback: remember the recall page + the query so we can score, after the
2681
+ // Effectiveness feedback: remember the recall page + the query so we can score, after the
2326
2682
  // response, whether the agent actually used the recalled context.
2327
2683
  let injectedRecall = "";
2328
2684
  let recallQuery = "";
@@ -2445,7 +2801,7 @@ export class AgentSession {
2445
2801
  }
2446
2802
  // Build messages array (recall page, then custom message if any, then user message)
2447
2803
  messages = [];
2448
- // R3: cross-session similarity recall. For a substantive turn, ask the memory providers to
2804
+ // Cross-session similarity recall. For a substantive turn, ask the memory providers to
2449
2805
  // prefetch a relevant <memory_context> page from past sessions and prepend it as data ahead of
2450
2806
  // the user message. Best-effort and gated: trivial turns are skipped, and providers return ""
2451
2807
  // (no page) when nothing is relevant — so it stays net-negative and the GC packs stale pages.
@@ -2457,8 +2813,8 @@ export class AgentSession {
2457
2813
  recallQuery = expandedText;
2458
2814
  // Inject as a GC-managed custom context message (role "custom", customType
2459
2815
  // "memory_context"), NOT a persisted user message: the semantic-memory context-GC packs
2460
- // stale recall pages so they don't accumulate forever (Bug #7), and the transcript index
2461
- // only re-reads user/assistant text so recalled snippets can't recirculate (Bug #10).
2816
+ // stale recall pages so they don't accumulate forever, and the transcript index
2817
+ // only re-reads user/assistant text so recalled snippets can't recirculate.
2462
2818
  messages.push(createCustomMessage("memory_context", recall, false, undefined, new Date().toISOString()));
2463
2819
  }
2464
2820
  }
@@ -2528,7 +2884,7 @@ export class AgentSession {
2528
2884
  this._textProtocolValidationOutcomeThisTurn = undefined;
2529
2885
  await this._modelRouter.runRoutedTurn(messages, routedTurnModel, routedTurnRouteDecision);
2530
2886
  this._recordTextToolProtocolParseOutcomeFromLastAssistant();
2531
- // R4: score whether the agent actually used the recalled context, so the recall gate can adapt.
2887
+ // Score whether the agent actually used the recalled context, so the recall gate can adapt.
2532
2888
  if (injectedRecall) {
2533
2889
  const response = this._findLastAssistantMessage();
2534
2890
  const responseText = response
@@ -2968,8 +3324,12 @@ export class AgentSession {
2968
3324
  getBaseKeepRecentTokens: () => settings.keepRecentTokens,
2969
3325
  resolveModelAndAuth: async (modelTier) => {
2970
3326
  const model = modelTier === "cheap" ? selectedCompactionModel : sessionModel;
2971
- const { apiKey, headers } = await this._resolveCompactionModelAndAuth(model, sessionModel);
2972
- return { model, apiKey, headers };
3327
+ // Return the resolution result AS-IS: it may have fallen back to a different model
3328
+ // (e.g. session model, when the tier's model failed auth or the readiness gate),
3329
+ // and `failure` must reach the retry loop's auth-failed escalation rather than be
3330
+ // dropped — dropping it would pair the wrong model with the fallback's credentials
3331
+ // and silently skip the loop's visible failure/escalation handling.
3332
+ return this._resolveCompactionModelAndAuth(model, sessionModel);
2973
3333
  },
2974
3334
  summarizeAndVerify: async (params, model, apiKey, headers, branch) => {
2975
3335
  const preparation = prepareCompaction(branch, {
@@ -3548,6 +3908,9 @@ export class AgentSession {
3548
3908
  reportSpawnedUsage: (usage, opts) => {
3549
3909
  this.addSpawnedUsage(usage, opts);
3550
3910
  },
3911
+ reportManagedLane: (event) => {
3912
+ this._backgroundLanes.recordManagedLane(event);
3913
+ },
3551
3914
  }, {
3552
3915
  getModel: () => this.model,
3553
3916
  isIdle: () => !this.isStreaming,
@@ -3608,15 +3971,15 @@ export class AgentSession {
3608
3971
  registerContextMemoryProvider(provider) {
3609
3972
  this._memory.registerContextMemoryProvider(provider);
3610
3973
  }
3611
- /** R8: the gateway/scheduler registry. A deployment runner registers providers and drives start/stop. */
3974
+ /** The gateway/scheduler registry. A deployment runner registers providers and drives start/stop. */
3612
3975
  get gateways() {
3613
3976
  return this._gatewayRegistry;
3614
3977
  }
3615
- /** R8: register a deployment-supplied transport channel (gateway). */
3978
+ /** Register a deployment-supplied transport channel (gateway). */
3616
3979
  registerChannelProvider(provider) {
3617
3980
  this._gatewayRegistry.registerChannel(provider);
3618
3981
  }
3619
- /** R8: register a deployment-supplied job scheduler (cron). */
3982
+ /** Register a deployment-supplied job scheduler (cron). */
3620
3983
  registerJobScheduler(provider) {
3621
3984
  this._gatewayRegistry.registerScheduler(provider);
3622
3985
  }
@@ -3775,7 +4138,32 @@ export class AgentSession {
3775
4138
  * Retrieve the latest valid goal state snapshot from the session log.
3776
4139
  */
3777
4140
  getGoalStateSnapshot() {
3778
- return getLatestGoalStateSnapshot(this.sessionManager.getEntries());
4141
+ return getLatestGoalStateSnapshot(this.sessionManager);
4142
+ }
4143
+ /**
4144
+ * Persist one submitted continuation pass's turn/wall-clock/spend contribution onto the active
4145
+ * goal's durable cumulative budget (see `GoalState.continuationTurnsUsed` et al.). This is the
4146
+ * "persistence dep" `GoalLoopController` calls once per pass actually submitted — it, not the
4147
+ * loop controller, is where USD gets attributed: it reads the session's OWN cumulative model
4148
+ * spend (`getCostSummary().ownCost` — deliberately excludes worker/subagent spend, which is
4149
+ * tracked and budgeted separately) and threads that single absolute reading into a
4150
+ * `record_continuation_budget` event; the pure reducer in `goal-state.ts` derives the delta.
4151
+ * A no-op when no goal state exists (defensive — in practice this is only ever called right
4152
+ * after a pass the loop already confirmed was submitted against an active goal).
4153
+ */
4154
+ recordGoalContinuationPass(pass) {
4155
+ const state = this.getGoalStateSnapshot();
4156
+ if (!state)
4157
+ return;
4158
+ const sessionCostUsd = this.getCostSummary().ownCost;
4159
+ const updated = applyGoalEvent(state, {
4160
+ type: "record_continuation_budget",
4161
+ turns: pass.turns,
4162
+ wallClockMs: pass.wallClockMs,
4163
+ sessionCostUsd,
4164
+ now: new Date().toISOString(),
4165
+ });
4166
+ this.saveGoalStateSnapshot(updated);
3779
4167
  }
3780
4168
  /** Save native task-step state to the active session log. */
3781
4169
  saveTaskStepsStateSnapshot(state) {
@@ -3783,7 +4171,7 @@ export class AgentSession {
3783
4171
  }
3784
4172
  /** Retrieve the latest valid native task-step state from the active session log. */
3785
4173
  getTaskStepsStateSnapshot() {
3786
- return getLatestTaskStepsStateSnapshot(this.sessionManager.getEntries());
4174
+ return getLatestTaskStepsStateSnapshot(this.sessionManager);
3787
4175
  }
3788
4176
  /**
3789
4177
  * Save a snapshot of the evidence bundle to the session log.
@@ -3806,7 +4194,7 @@ export class AgentSession {
3806
4194
  getLaneRecords() {
3807
4195
  return this._backgroundLanes.getLaneRecords();
3808
4196
  }
3809
- // G3/G8 autonomy telemetry + gate-outcome history live in AutonomyTelemetry (see
4197
+ // Autonomy telemetry + gate-outcome history live in AutonomyTelemetry (see
3810
4198
  // autonomy-telemetry.ts). These stubs keep the god file's internal call surface stable while the
3811
4199
  // sink logic and the owned gate-outcome fields live there.
3812
4200
  _emitAutonomyTelemetry(event) {
@@ -3815,7 +4203,7 @@ export class AgentSession {
3815
4203
  _recordGateOutcome(outcome) {
3816
4204
  this._autonomyTelemetry.recordGateOutcome(outcome);
3817
4205
  }
3818
- /** G8: copies of the bounded gate-outcome history, oldest first, latest last. */
4206
+ /** Copies of the bounded gate-outcome history, oldest first, latest last. */
3819
4207
  getGateOutcomeHistory() {
3820
4208
  return this._autonomyTelemetry.getGateOutcomeHistory();
3821
4209
  }
@@ -3831,10 +4219,18 @@ export class AgentSession {
3831
4219
  getLearningDecisionSnapshots() {
3832
4220
  return getLearningDecisionSnapshots(this.sessionManager.getEntries());
3833
4221
  }
4222
+ /**
4223
+ * The single injection point that makes the goal-continuation snapshot lane-aware:
4224
+ * `laneRecords` feeds BOTH `evaluateGoalContinuation`'s "waiting" branch (a worker dispatched
4225
+ * against an open requirement) and the per-goal worker-spend overlay — read fresh here so BOTH the
4226
+ * goal loop (`GoalLoopController`) and the idle scheduler (`BackgroundLaneController`) see the
4227
+ * same live lane state, since both reach this same method.
4228
+ */
3834
4229
  getGoalRuntimeSnapshot(settings) {
3835
4230
  return buildGoalRuntimeSnapshot({
3836
- entries: this.sessionManager.getEntries(),
4231
+ sessionManager: this.sessionManager,
3837
4232
  settings,
4233
+ laneRecords: this._backgroundLanes.getLaneRecords(),
3838
4234
  });
3839
4235
  }
3840
4236
  /**
@@ -3847,6 +4243,25 @@ export class AgentSession {
3847
4243
  mode: this.settingsManager.getModelCapabilitySettings().mode,
3848
4244
  });
3849
4245
  }
4246
+ /**
4247
+ * Whether the CURRENT session model may drive a worktree-sync lane worker (see
4248
+ * `evaluateLaneWorkerRefusal` in model-capability.ts): full capability class, a DECLARED
4249
+ * (registry) context window, an ADVERTISED native tool-call path (`Model.textToolCallProtocol`
4250
+ * unset/false -- `true` means phone-only), and no graded `/toolprobe` demotion to
4251
+ * "text-protocol"/"none" on record. An unprobed model (no verdict on record yet) is eligible on
4252
+ * its advertised support alone. `undefined` means eligible.
4253
+ */
4254
+ getLaneWorkerRefusal() {
4255
+ const profile = this.getModelCapabilityProfile();
4256
+ const model = this.model;
4257
+ const verdict = model ? this._toolProbeVerdict(model) : undefined;
4258
+ return evaluateLaneWorkerRefusal({
4259
+ capabilityClass: profile.class,
4260
+ contextWindow: profile.contextWindow,
4261
+ toolCallingAdvertised: model?.textToolCallProtocol !== true,
4262
+ toolCallingDemoted: verdict === "text-protocol" || verdict === "none",
4263
+ });
4264
+ }
3850
4265
  /**
3851
4266
  * Run one bounded, read-only research pass and persist its results. Delegates to
3852
4267
  * {@link BackgroundLaneController}; see there for the full gating/budget/dedupe contract.
@@ -3875,8 +4290,15 @@ export class AgentSession {
3875
4290
  async continueGoalOnce(options) {
3876
4291
  return this._goalContinuation.continueGoalOnce(options);
3877
4292
  }
4293
+ /**
4294
+ * Public entry point for BOTH idle autosteer and manual (`/goal start`, `/goal-continue`)
4295
+ * continuation. Delegates to {@link BackgroundLaneController.continueGoalLoopExclusive}, the
4296
+ * single-flight guard that prevents two goal loops from racing to submit prompts through the
4297
+ * same session (which throws "Agent is already processing" from the second submission). Do not
4298
+ * call `this._goalContinuation.continueGoalLoop` directly from here — that bypasses the guard.
4299
+ */
3878
4300
  async continueGoalLoop(options) {
3879
- return this._goalContinuation.continueGoalLoop(options);
4301
+ return this._backgroundLanes.continueGoalLoopExclusive(options);
3880
4302
  }
3881
4303
  /**
3882
4304
  * Run a one-shot LLM completion fully ISOLATED from the main session — the load-bearing primitive
@@ -3886,7 +4308,7 @@ export class AgentSession {
3886
4308
  return this._reflection.runIsolatedCompletion(opts);
3887
4309
  }
3888
4310
  /**
3889
- * Native end-of-loop reflection pass (R2). Delegates to {@link ReflectionController}; returns null
4311
+ * Native end-of-loop reflection pass. Delegates to {@link ReflectionController}; returns null
3890
4312
  * when the demand gate skips or in a child session.
3891
4313
  */
3892
4314
  async runReflectionPass(input) {