@caupulican/pi-adaptative 0.81.38 → 0.81.40

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 (881) hide show
  1. package/CHANGELOG.md +131 -0
  2. package/README.md +2 -0
  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 +133 -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 +198 -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 +311 -0
  9. package/dist/bundled-resources/extensions/tmux-agent-manager/index.d.ts +31 -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 +823 -79
  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 +1043 -86
  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 +7 -7
  32. package/dist/bundled-resources/skills/tool-call-repair/references/text-protocol-grammar.md +28 -7
  33. package/dist/cli/args.d.ts +5 -0
  34. package/dist/cli/args.d.ts.map +1 -1
  35. package/dist/cli/args.js +24 -0
  36. package/dist/cli/args.js.map +1 -1
  37. package/dist/core/agent-directory-layout.d.ts +15 -0
  38. package/dist/core/agent-directory-layout.d.ts.map +1 -0
  39. package/dist/core/agent-directory-layout.js +72 -0
  40. package/dist/core/agent-directory-layout.js.map +1 -0
  41. package/dist/core/agent-paths.d.ts +66 -0
  42. package/dist/core/agent-paths.d.ts.map +1 -0
  43. package/dist/core/agent-paths.js +170 -0
  44. package/dist/core/agent-paths.js.map +1 -0
  45. package/dist/core/agent-session-runtime.d.ts +52 -8
  46. package/dist/core/agent-session-runtime.d.ts.map +1 -1
  47. package/dist/core/agent-session-runtime.js +397 -79
  48. package/dist/core/agent-session-runtime.js.map +1 -1
  49. package/dist/core/agent-session-services.d.ts +1 -0
  50. package/dist/core/agent-session-services.d.ts.map +1 -1
  51. package/dist/core/agent-session-services.js +4 -3
  52. package/dist/core/agent-session-services.js.map +1 -1
  53. package/dist/core/agent-session.d.ts +255 -103
  54. package/dist/core/agent-session.d.ts.map +1 -1
  55. package/dist/core/agent-session.js +754 -1173
  56. package/dist/core/agent-session.js.map +1 -1
  57. package/dist/core/autonomy/approval-gate.d.ts +3 -2
  58. package/dist/core/autonomy/approval-gate.d.ts.map +1 -1
  59. package/dist/core/autonomy/approval-gate.js +6 -28
  60. package/dist/core/autonomy/approval-gate.js.map +1 -1
  61. package/dist/core/autonomy/bounded-completion.d.ts +1 -1
  62. package/dist/core/autonomy/bounded-completion.d.ts.map +1 -1
  63. package/dist/core/autonomy/bounded-completion.js +1 -1
  64. package/dist/core/autonomy/bounded-completion.js.map +1 -1
  65. package/dist/core/autonomy/contracts.d.ts +26 -9
  66. package/dist/core/autonomy/contracts.d.ts.map +1 -1
  67. package/dist/core/autonomy/contracts.js.map +1 -1
  68. package/dist/core/autonomy/foreground-envelope.d.ts +8 -2
  69. package/dist/core/autonomy/foreground-envelope.d.ts.map +1 -1
  70. package/dist/core/autonomy/foreground-envelope.js +8 -25
  71. package/dist/core/autonomy/foreground-envelope.js.map +1 -1
  72. package/dist/core/autonomy/gates.d.ts.map +1 -1
  73. package/dist/core/autonomy/gates.js +8 -10
  74. package/dist/core/autonomy/gates.js.map +1 -1
  75. package/dist/core/autonomy/lane-private-paths.d.ts +2 -0
  76. package/dist/core/autonomy/lane-private-paths.d.ts.map +1 -0
  77. package/dist/core/autonomy/lane-private-paths.js +16 -0
  78. package/dist/core/autonomy/lane-private-paths.js.map +1 -0
  79. package/dist/core/autonomy/lane-tool-surface.d.ts +10 -0
  80. package/dist/core/autonomy/lane-tool-surface.d.ts.map +1 -1
  81. package/dist/core/autonomy/lane-tool-surface.js +46 -8
  82. package/dist/core/autonomy/lane-tool-surface.js.map +1 -1
  83. package/dist/core/autonomy/lane-tracker.d.ts +24 -1
  84. package/dist/core/autonomy/lane-tracker.d.ts.map +1 -1
  85. package/dist/core/autonomy/lane-tracker.js +43 -1
  86. package/dist/core/autonomy/lane-tracker.js.map +1 -1
  87. package/dist/core/autonomy/risk-assessment.d.ts +1 -2
  88. package/dist/core/autonomy/risk-assessment.d.ts.map +1 -1
  89. package/dist/core/autonomy/risk-assessment.js +1 -123
  90. package/dist/core/autonomy/risk-assessment.js.map +1 -1
  91. package/dist/core/autonomy/session-lane-record.d.ts +4 -4
  92. package/dist/core/autonomy/session-lane-record.d.ts.map +1 -1
  93. package/dist/core/autonomy/session-lane-record.js +16 -29
  94. package/dist/core/autonomy/session-lane-record.js.map +1 -1
  95. package/dist/core/autonomy/telemetry-events.d.ts +1 -1
  96. package/dist/core/autonomy/telemetry-events.d.ts.map +1 -1
  97. package/dist/core/autonomy/telemetry-events.js +1 -1
  98. package/dist/core/autonomy/telemetry-events.js.map +1 -1
  99. package/dist/core/autonomy-telemetry.d.ts +7 -7
  100. package/dist/core/autonomy-telemetry.d.ts.map +1 -1
  101. package/dist/core/autonomy-telemetry.js +9 -9
  102. package/dist/core/autonomy-telemetry.js.map +1 -1
  103. package/dist/core/background-lane-controller.d.ts +90 -156
  104. package/dist/core/background-lane-controller.d.ts.map +1 -1
  105. package/dist/core/background-lane-controller.js +192 -968
  106. package/dist/core/background-lane-controller.js.map +1 -1
  107. package/dist/core/bash-execution-controller.d.ts +4 -0
  108. package/dist/core/bash-execution-controller.d.ts.map +1 -1
  109. package/dist/core/bash-execution-controller.js +7 -1
  110. package/dist/core/bash-execution-controller.js.map +1 -1
  111. package/dist/core/capability-contract.d.ts +4 -0
  112. package/dist/core/capability-contract.d.ts.map +1 -0
  113. package/dist/core/capability-contract.js +28 -0
  114. package/dist/core/capability-contract.js.map +1 -0
  115. package/dist/core/compaction-controller.d.ts +90 -0
  116. package/dist/core/compaction-controller.d.ts.map +1 -0
  117. package/dist/core/compaction-controller.js +452 -0
  118. package/dist/core/compaction-controller.js.map +1 -0
  119. package/dist/core/compaction-support.d.ts +24 -13
  120. package/dist/core/compaction-support.d.ts.map +1 -1
  121. package/dist/core/compaction-support.js +43 -7
  122. package/dist/core/compaction-support.js.map +1 -1
  123. package/dist/core/context/context-artifacts.d.ts +6 -2
  124. package/dist/core/context/context-artifacts.d.ts.map +1 -1
  125. package/dist/core/context/context-artifacts.js +34 -2
  126. package/dist/core/context/context-artifacts.js.map +1 -1
  127. package/dist/core/context/context-audit.d.ts +33 -1
  128. package/dist/core/context/context-audit.d.ts.map +1 -1
  129. package/dist/core/context/context-audit.js +31 -5
  130. package/dist/core/context/context-audit.js.map +1 -1
  131. package/dist/core/context/context-store-retention.d.ts +8 -2
  132. package/dist/core/context/context-store-retention.d.ts.map +1 -1
  133. package/dist/core/context/context-store-retention.js +158 -35
  134. package/dist/core/context/context-store-retention.js.map +1 -1
  135. package/dist/core/context/okf-memory.d.ts.map +1 -1
  136. package/dist/core/context/okf-memory.js +9 -11
  137. package/dist/core/context/okf-memory.js.map +1 -1
  138. package/dist/core/context/sqlite-runtime-index.d.ts.map +1 -1
  139. package/dist/core/context/sqlite-runtime-index.js +6 -8
  140. package/dist/core/context/sqlite-runtime-index.js.map +1 -1
  141. package/dist/core/context-gc.d.ts +3 -0
  142. package/dist/core/context-gc.d.ts.map +1 -1
  143. package/dist/core/context-gc.js +26 -6
  144. package/dist/core/context-gc.js.map +1 -1
  145. package/dist/core/context-pipeline.d.ts +26 -4
  146. package/dist/core/context-pipeline.d.ts.map +1 -1
  147. package/dist/core/context-pipeline.js +146 -18
  148. package/dist/core/context-pipeline.js.map +1 -1
  149. package/dist/core/cost-guard.d.ts +19 -3
  150. package/dist/core/cost-guard.d.ts.map +1 -1
  151. package/dist/core/cost-guard.js +18 -3
  152. package/dist/core/cost-guard.js.map +1 -1
  153. package/dist/core/default-tool-surface.d.ts.map +1 -1
  154. package/dist/core/default-tool-surface.js +1 -0
  155. package/dist/core/default-tool-surface.js.map +1 -1
  156. package/dist/core/delegation/managed-lane-controller.d.ts +34 -0
  157. package/dist/core/delegation/managed-lane-controller.d.ts.map +1 -0
  158. package/dist/core/delegation/managed-lane-controller.js +217 -0
  159. package/dist/core/delegation/managed-lane-controller.js.map +1 -0
  160. package/dist/core/delegation/session-worker-claim.d.ts +44 -0
  161. package/dist/core/delegation/session-worker-claim.d.ts.map +1 -0
  162. package/dist/core/delegation/session-worker-claim.js +98 -0
  163. package/dist/core/delegation/session-worker-claim.js.map +1 -0
  164. package/dist/core/delegation/worker-actions.d.ts +7 -5
  165. package/dist/core/delegation/worker-actions.d.ts.map +1 -1
  166. package/dist/core/delegation/worker-actions.js +20 -4
  167. package/dist/core/delegation/worker-actions.js.map +1 -1
  168. package/dist/core/delegation/worker-claim.d.ts +56 -0
  169. package/dist/core/delegation/worker-claim.d.ts.map +1 -0
  170. package/dist/core/delegation/worker-claim.js +239 -0
  171. package/dist/core/delegation/worker-claim.js.map +1 -0
  172. package/dist/core/delegation/worker-delegation-controller.d.ts +110 -0
  173. package/dist/core/delegation/worker-delegation-controller.d.ts.map +1 -0
  174. package/dist/core/delegation/worker-delegation-controller.js +861 -0
  175. package/dist/core/delegation/worker-delegation-controller.js.map +1 -0
  176. package/dist/core/delegation/worker-delegation-request.d.ts +11 -0
  177. package/dist/core/delegation/worker-delegation-request.d.ts.map +1 -0
  178. package/dist/core/delegation/worker-delegation-request.js +2 -0
  179. package/dist/core/delegation/worker-delegation-request.js.map +1 -0
  180. package/dist/core/delegation/worker-dispatch-scheduler.d.ts +40 -0
  181. package/dist/core/delegation/worker-dispatch-scheduler.d.ts.map +1 -0
  182. package/dist/core/delegation/worker-dispatch-scheduler.js +93 -0
  183. package/dist/core/delegation/worker-dispatch-scheduler.js.map +1 -0
  184. package/dist/core/delegation/worker-execution-policy.d.ts +62 -0
  185. package/dist/core/delegation/worker-execution-policy.d.ts.map +1 -0
  186. package/dist/core/delegation/worker-execution-policy.js +160 -0
  187. package/dist/core/delegation/worker-execution-policy.js.map +1 -0
  188. package/dist/core/delegation/worker-lane-projection.d.ts +14 -0
  189. package/dist/core/delegation/worker-lane-projection.d.ts.map +1 -0
  190. package/dist/core/delegation/worker-lane-projection.js +100 -0
  191. package/dist/core/delegation/worker-lane-projection.js.map +1 -0
  192. package/dist/core/delegation/worker-lifecycle.d.ts +97 -0
  193. package/dist/core/delegation/worker-lifecycle.d.ts.map +1 -0
  194. package/dist/core/delegation/worker-lifecycle.js +339 -0
  195. package/dist/core/delegation/worker-lifecycle.js.map +1 -0
  196. package/dist/core/delegation/worker-notification-coordinator.d.ts +36 -0
  197. package/dist/core/delegation/worker-notification-coordinator.d.ts.map +1 -0
  198. package/dist/core/delegation/worker-notification-coordinator.js +90 -0
  199. package/dist/core/delegation/worker-notification-coordinator.js.map +1 -0
  200. package/dist/core/delegation/worker-profile-resolver.d.ts +55 -0
  201. package/dist/core/delegation/worker-profile-resolver.d.ts.map +1 -0
  202. package/dist/core/delegation/worker-profile-resolver.js +113 -0
  203. package/dist/core/delegation/worker-profile-resolver.js.map +1 -0
  204. package/dist/core/delegation/worker-runner.d.ts +25 -14
  205. package/dist/core/delegation/worker-runner.d.ts.map +1 -1
  206. package/dist/core/delegation/worker-runner.js +122 -53
  207. package/dist/core/delegation/worker-runner.js.map +1 -1
  208. package/dist/core/delegation/worker-task-label.d.ts +3 -0
  209. package/dist/core/delegation/worker-task-label.d.ts.map +1 -0
  210. package/dist/core/delegation/worker-task-label.js +11 -0
  211. package/dist/core/delegation/worker-task-label.js.map +1 -0
  212. package/dist/core/doctor.d.ts +8 -0
  213. package/dist/core/doctor.d.ts.map +1 -1
  214. package/dist/core/doctor.js +32 -1
  215. package/dist/core/doctor.js.map +1 -1
  216. package/dist/core/extensions/index.d.ts +1 -1
  217. package/dist/core/extensions/index.d.ts.map +1 -1
  218. package/dist/core/extensions/index.js.map +1 -1
  219. package/dist/core/extensions/loader.d.ts +5 -1
  220. package/dist/core/extensions/loader.d.ts.map +1 -1
  221. package/dist/core/extensions/loader.js +68 -13
  222. package/dist/core/extensions/loader.js.map +1 -1
  223. package/dist/core/extensions/runner.d.ts.map +1 -1
  224. package/dist/core/extensions/runner.js +7 -0
  225. package/dist/core/extensions/runner.js.map +1 -1
  226. package/dist/core/extensions/storage.d.ts +8 -0
  227. package/dist/core/extensions/storage.d.ts.map +1 -0
  228. package/dist/core/extensions/storage.js +28 -0
  229. package/dist/core/extensions/storage.js.map +1 -0
  230. package/dist/core/extensions/types.d.ts +101 -1
  231. package/dist/core/extensions/types.d.ts.map +1 -1
  232. package/dist/core/extensions/types.js.map +1 -1
  233. package/dist/core/failure-corpus.d.ts +1 -15
  234. package/dist/core/failure-corpus.d.ts.map +1 -1
  235. package/dist/core/failure-corpus.js +9 -37
  236. package/dist/core/failure-corpus.js.map +1 -1
  237. package/dist/core/foreground-recovery-controller.d.ts +52 -0
  238. package/dist/core/foreground-recovery-controller.d.ts.map +1 -0
  239. package/dist/core/foreground-recovery-controller.js +111 -0
  240. package/dist/core/foreground-recovery-controller.js.map +1 -0
  241. package/dist/core/gateways/channel-provider.d.ts +3 -0
  242. package/dist/core/gateways/channel-provider.d.ts.map +1 -1
  243. package/dist/core/gateways/channel-provider.js +31 -6
  244. package/dist/core/gateways/channel-provider.js.map +1 -1
  245. package/dist/core/goal-loop-controller.d.ts +17 -1
  246. package/dist/core/goal-loop-controller.d.ts.map +1 -1
  247. package/dist/core/goal-loop-controller.js +79 -11
  248. package/dist/core/goal-loop-controller.js.map +1 -1
  249. package/dist/core/goals/goal-acceptance.d.ts +11 -0
  250. package/dist/core/goals/goal-acceptance.d.ts.map +1 -0
  251. package/dist/core/goals/goal-acceptance.js +25 -0
  252. package/dist/core/goals/goal-acceptance.js.map +1 -0
  253. package/dist/core/goals/goal-auto-continue-controller.d.ts +27 -0
  254. package/dist/core/goals/goal-auto-continue-controller.d.ts.map +1 -0
  255. package/dist/core/goals/goal-auto-continue-controller.js +93 -0
  256. package/dist/core/goals/goal-auto-continue-controller.js.map +1 -0
  257. package/dist/core/goals/goal-continuation-controller.d.ts +48 -2
  258. package/dist/core/goals/goal-continuation-controller.d.ts.map +1 -1
  259. package/dist/core/goals/goal-continuation-controller.js +87 -0
  260. package/dist/core/goals/goal-continuation-controller.js.map +1 -1
  261. package/dist/core/goals/goal-continuation-defaults.d.ts +39 -0
  262. package/dist/core/goals/goal-continuation-defaults.d.ts.map +1 -1
  263. package/dist/core/goals/goal-continuation-defaults.js +42 -0
  264. package/dist/core/goals/goal-continuation-defaults.js.map +1 -1
  265. package/dist/core/goals/goal-continuation-prompt.d.ts +5 -1
  266. package/dist/core/goals/goal-continuation-prompt.d.ts.map +1 -1
  267. package/dist/core/goals/goal-continuation-prompt.js +56 -15
  268. package/dist/core/goals/goal-continuation-prompt.js.map +1 -1
  269. package/dist/core/goals/goal-lifecycle.d.ts +20 -0
  270. package/dist/core/goals/goal-lifecycle.d.ts.map +1 -0
  271. package/dist/core/goals/goal-lifecycle.js +17 -0
  272. package/dist/core/goals/goal-lifecycle.js.map +1 -0
  273. package/dist/core/goals/goal-runtime-snapshot.d.ts +105 -4
  274. package/dist/core/goals/goal-runtime-snapshot.d.ts.map +1 -1
  275. package/dist/core/goals/goal-runtime-snapshot.js +91 -6
  276. package/dist/core/goals/goal-runtime-snapshot.js.map +1 -1
  277. package/dist/core/goals/goal-state.d.ts +90 -1
  278. package/dist/core/goals/goal-state.d.ts.map +1 -1
  279. package/dist/core/goals/goal-state.js +78 -15
  280. package/dist/core/goals/goal-state.js.map +1 -1
  281. package/dist/core/goals/goal-tool-core.d.ts +58 -2
  282. package/dist/core/goals/goal-tool-core.d.ts.map +1 -1
  283. package/dist/core/goals/goal-tool-core.js +82 -6
  284. package/dist/core/goals/goal-tool-core.js.map +1 -1
  285. package/dist/core/goals/session-goal-state.d.ts +13 -6
  286. package/dist/core/goals/session-goal-state.d.ts.map +1 -1
  287. package/dist/core/goals/session-goal-state.js +20 -28
  288. package/dist/core/goals/session-goal-state.js.map +1 -1
  289. package/dist/core/human-input.d.ts +100 -0
  290. package/dist/core/human-input.d.ts.map +1 -0
  291. package/dist/core/human-input.js +362 -0
  292. package/dist/core/human-input.js.map +1 -0
  293. package/dist/core/index.d.ts +9 -1
  294. package/dist/core/index.d.ts.map +1 -1
  295. package/dist/core/index.js +9 -1
  296. package/dist/core/index.js.map +1 -1
  297. package/dist/core/{telemetry.d.ts → install-telemetry.d.ts} +1 -1
  298. package/dist/core/install-telemetry.d.ts.map +1 -0
  299. package/dist/core/{telemetry.js → install-telemetry.js} +1 -1
  300. package/dist/core/install-telemetry.js.map +1 -0
  301. package/dist/core/keybindings.d.ts +27 -1
  302. package/dist/core/keybindings.d.ts.map +1 -1
  303. package/dist/core/keybindings.js +34 -8
  304. package/dist/core/keybindings.js.map +1 -1
  305. package/dist/core/learning/learning-audit.d.ts +2 -4
  306. package/dist/core/learning/learning-audit.d.ts.map +1 -1
  307. package/dist/core/learning/learning-audit.js +9 -29
  308. package/dist/core/learning/learning-audit.js.map +1 -1
  309. package/dist/core/learning/learning-gate.d.ts.map +1 -1
  310. package/dist/core/learning/learning-gate.js +1 -6
  311. package/dist/core/learning/learning-gate.js.map +1 -1
  312. package/dist/core/learning/observation-store.d.ts +12 -3
  313. package/dist/core/learning/observation-store.d.ts.map +1 -1
  314. package/dist/core/learning/observation-store.js +30 -15
  315. package/dist/core/learning/observation-store.js.map +1 -1
  316. package/dist/core/learning/session-learning-decision.d.ts +2 -4
  317. package/dist/core/learning/session-learning-decision.d.ts.map +1 -1
  318. package/dist/core/learning/session-learning-decision.js +9 -29
  319. package/dist/core/learning/session-learning-decision.js.map +1 -1
  320. package/dist/core/learning/skill-curator.d.ts +5 -1
  321. package/dist/core/learning/skill-curator.d.ts.map +1 -1
  322. package/dist/core/learning/skill-curator.js +21 -19
  323. package/dist/core/learning/skill-curator.js.map +1 -1
  324. package/dist/core/local-runtime-controller.d.ts +65 -3
  325. package/dist/core/local-runtime-controller.d.ts.map +1 -1
  326. package/dist/core/local-runtime-controller.js +186 -26
  327. package/dist/core/local-runtime-controller.js.map +1 -1
  328. package/dist/core/memory/providers/file-store.d.ts +1 -1
  329. package/dist/core/memory/providers/file-store.d.ts.map +1 -1
  330. package/dist/core/memory/providers/file-store.js +6 -6
  331. package/dist/core/memory/providers/file-store.js.map +1 -1
  332. package/dist/core/memory-controller.d.ts.map +1 -1
  333. package/dist/core/memory-controller.js +4 -4
  334. package/dist/core/memory-controller.js.map +1 -1
  335. package/dist/core/model-capability.d.ts +34 -0
  336. package/dist/core/model-capability.d.ts.map +1 -1
  337. package/dist/core/model-capability.js +43 -1
  338. package/dist/core/model-capability.js.map +1 -1
  339. package/dist/core/model-resolver.d.ts +1 -1
  340. package/dist/core/model-resolver.d.ts.map +1 -1
  341. package/dist/core/model-resolver.js +6 -9
  342. package/dist/core/model-resolver.js.map +1 -1
  343. package/dist/core/model-router/intent-classifier.d.ts +1 -4
  344. package/dist/core/model-router/intent-classifier.d.ts.map +1 -1
  345. package/dist/core/model-router/intent-classifier.js +1 -156
  346. package/dist/core/model-router/intent-classifier.js.map +1 -1
  347. package/dist/core/model-router/tool-escalation.d.ts +15 -0
  348. package/dist/core/model-router/tool-escalation.d.ts.map +1 -1
  349. package/dist/core/model-router/tool-escalation.js +23 -1
  350. package/dist/core/model-router/tool-escalation.js.map +1 -1
  351. package/dist/core/model-router-controller.d.ts +34 -7
  352. package/dist/core/model-router-controller.d.ts.map +1 -1
  353. package/dist/core/model-router-controller.js +95 -16
  354. package/dist/core/model-router-controller.js.map +1 -1
  355. package/dist/core/model-selection-controller.d.ts +4 -4
  356. package/dist/core/model-selection-controller.d.ts.map +1 -1
  357. package/dist/core/model-selection-controller.js.map +1 -1
  358. package/dist/core/model-tool-protocol.d.ts +23 -0
  359. package/dist/core/model-tool-protocol.d.ts.map +1 -0
  360. package/dist/core/model-tool-protocol.js +43 -0
  361. package/dist/core/model-tool-protocol.js.map +1 -0
  362. package/dist/core/models/adaptation-store.d.ts +10 -5
  363. package/dist/core/models/adaptation-store.d.ts.map +1 -1
  364. package/dist/core/models/adaptation-store.js +129 -81
  365. package/dist/core/models/adaptation-store.js.map +1 -1
  366. package/dist/core/models/default-model-suggestions.d.ts.map +1 -1
  367. package/dist/core/models/default-model-suggestions.js +17 -0
  368. package/dist/core/models/default-model-suggestions.js.map +1 -1
  369. package/dist/core/models/fitness-store.d.ts +4 -11
  370. package/dist/core/models/fitness-store.d.ts.map +1 -1
  371. package/dist/core/models/fitness-store.js +87 -51
  372. package/dist/core/models/fitness-store.js.map +1 -1
  373. package/dist/core/models/host-state-store.d.ts +34 -0
  374. package/dist/core/models/host-state-store.d.ts.map +1 -0
  375. package/dist/core/models/host-state-store.js +86 -0
  376. package/dist/core/models/host-state-store.js.map +1 -0
  377. package/dist/core/models/llamacpp-runtime.d.ts +180 -0
  378. package/dist/core/models/llamacpp-runtime.d.ts.map +1 -0
  379. package/dist/core/models/llamacpp-runtime.js +475 -0
  380. package/dist/core/models/llamacpp-runtime.js.map +1 -0
  381. package/dist/core/models/local-registration.d.ts +40 -0
  382. package/dist/core/models/local-registration.d.ts.map +1 -1
  383. package/dist/core/models/local-registration.js +94 -5
  384. package/dist/core/models/local-registration.js.map +1 -1
  385. package/dist/core/models/local-runtime.d.ts.map +1 -1
  386. package/dist/core/models/local-runtime.js +6 -5
  387. package/dist/core/models/local-runtime.js.map +1 -1
  388. package/dist/core/models/model-ref.d.ts +7 -0
  389. package/dist/core/models/model-ref.d.ts.map +1 -1
  390. package/dist/core/models/model-ref.js +26 -0
  391. package/dist/core/models/model-ref.js.map +1 -1
  392. package/dist/core/models/needle-runtime.d.ts +257 -0
  393. package/dist/core/models/needle-runtime.d.ts.map +1 -0
  394. package/dist/core/models/needle-runtime.js +519 -0
  395. package/dist/core/models/needle-runtime.js.map +1 -0
  396. package/dist/core/models/prism-llamacpp-lifecycle.d.ts +89 -0
  397. package/dist/core/models/prism-llamacpp-lifecycle.d.ts.map +1 -0
  398. package/dist/core/models/prism-llamacpp-lifecycle.js +121 -0
  399. package/dist/core/models/prism-llamacpp-lifecycle.js.map +1 -0
  400. package/dist/core/orchestration/agent-resume.d.ts +24 -0
  401. package/dist/core/orchestration/agent-resume.d.ts.map +1 -0
  402. package/dist/core/orchestration/agent-resume.js +90 -0
  403. package/dist/core/orchestration/agent-resume.js.map +1 -0
  404. package/dist/core/orchestration/capability-gateway.d.ts +61 -0
  405. package/dist/core/orchestration/capability-gateway.d.ts.map +1 -0
  406. package/dist/core/orchestration/capability-gateway.js +152 -0
  407. package/dist/core/orchestration/capability-gateway.js.map +1 -0
  408. package/dist/core/orchestration/contracts.d.ts +343 -0
  409. package/dist/core/orchestration/contracts.d.ts.map +1 -0
  410. package/dist/core/orchestration/contracts.js +135 -0
  411. package/dist/core/orchestration/contracts.js.map +1 -0
  412. package/dist/core/orchestration/delegation-ledger.d.ts +56 -0
  413. package/dist/core/orchestration/delegation-ledger.d.ts.map +1 -0
  414. package/dist/core/orchestration/delegation-ledger.js +197 -0
  415. package/dist/core/orchestration/delegation-ledger.js.map +1 -0
  416. package/dist/core/orchestration/event-store.d.ts +83 -0
  417. package/dist/core/orchestration/event-store.d.ts.map +1 -0
  418. package/dist/core/orchestration/event-store.js +560 -0
  419. package/dist/core/orchestration/event-store.js.map +1 -0
  420. package/dist/core/orchestration/lane-tool-manifests.d.ts +9 -0
  421. package/dist/core/orchestration/lane-tool-manifests.d.ts.map +1 -0
  422. package/dist/core/orchestration/lane-tool-manifests.js +37 -0
  423. package/dist/core/orchestration/lane-tool-manifests.js.map +1 -0
  424. package/dist/core/orchestration/model-binding.d.ts +15 -0
  425. package/dist/core/orchestration/model-binding.d.ts.map +1 -0
  426. package/dist/core/orchestration/model-binding.js +29 -0
  427. package/dist/core/orchestration/model-binding.js.map +1 -0
  428. package/dist/core/orchestration/policy-compiler.d.ts +49 -0
  429. package/dist/core/orchestration/policy-compiler.d.ts.map +1 -0
  430. package/dist/core/orchestration/policy-compiler.js +191 -0
  431. package/dist/core/orchestration/policy-compiler.js.map +1 -0
  432. package/dist/core/orchestration/policy-gate.d.ts +18 -0
  433. package/dist/core/orchestration/policy-gate.d.ts.map +1 -0
  434. package/dist/core/orchestration/policy-gate.js +24 -0
  435. package/dist/core/orchestration/policy-gate.js.map +1 -0
  436. package/dist/core/orchestration/profile-registry.d.ts +51 -0
  437. package/dist/core/orchestration/profile-registry.d.ts.map +1 -0
  438. package/dist/core/orchestration/profile-registry.js +393 -0
  439. package/dist/core/orchestration/profile-registry.js.map +1 -0
  440. package/dist/core/orchestration/profile-store.d.ts +31 -0
  441. package/dist/core/orchestration/profile-store.d.ts.map +1 -0
  442. package/dist/core/orchestration/profile-store.js +136 -0
  443. package/dist/core/orchestration/profile-store.js.map +1 -0
  444. package/dist/core/orchestration/risk-budget.d.ts +9 -0
  445. package/dist/core/orchestration/risk-budget.d.ts.map +1 -0
  446. package/dist/core/orchestration/risk-budget.js +48 -0
  447. package/dist/core/orchestration/risk-budget.js.map +1 -0
  448. package/dist/core/orchestration/task-runtime.d.ts +168 -0
  449. package/dist/core/orchestration/task-runtime.d.ts.map +1 -0
  450. package/dist/core/orchestration/task-runtime.js +1600 -0
  451. package/dist/core/orchestration/task-runtime.js.map +1 -0
  452. package/dist/core/orchestration/work-state-projection.d.ts +32 -0
  453. package/dist/core/orchestration/work-state-projection.d.ts.map +1 -0
  454. package/dist/core/orchestration/work-state-projection.js +116 -0
  455. package/dist/core/orchestration/work-state-projection.js.map +1 -0
  456. package/dist/core/orchestration/worker-execution-contract.d.ts +21 -0
  457. package/dist/core/orchestration/worker-execution-contract.d.ts.map +1 -0
  458. package/dist/core/orchestration/worker-execution-contract.js +179 -0
  459. package/dist/core/orchestration/worker-execution-contract.js.map +1 -0
  460. package/dist/core/orchestration/worker-result-adapter.d.ts +21 -0
  461. package/dist/core/orchestration/worker-result-adapter.d.ts.map +1 -0
  462. package/dist/core/orchestration/worker-result-adapter.js +109 -0
  463. package/dist/core/orchestration/worker-result-adapter.js.map +1 -0
  464. package/dist/core/package-manager.d.ts.map +1 -1
  465. package/dist/core/package-manager.js +11 -10
  466. package/dist/core/package-manager.js.map +1 -1
  467. package/dist/core/process-identity.d.ts +11 -0
  468. package/dist/core/process-identity.d.ts.map +1 -0
  469. package/dist/core/process-identity.js +26 -0
  470. package/dist/core/process-identity.js.map +1 -0
  471. package/dist/core/process-matrix/codes.d.ts +83 -0
  472. package/dist/core/process-matrix/codes.d.ts.map +1 -0
  473. package/dist/core/process-matrix/codes.js +15 -0
  474. package/dist/core/process-matrix/codes.js.map +1 -0
  475. package/dist/core/process-matrix/resume-launcher.d.ts +26 -0
  476. package/dist/core/process-matrix/resume-launcher.d.ts.map +1 -0
  477. package/dist/core/process-matrix/resume-launcher.js +62 -0
  478. package/dist/core/process-matrix/resume-launcher.js.map +1 -0
  479. package/dist/core/process-matrix/runtime.d.ts +78 -0
  480. package/dist/core/process-matrix/runtime.d.ts.map +1 -0
  481. package/dist/core/process-matrix/runtime.js +633 -0
  482. package/dist/core/process-matrix/runtime.js.map +1 -0
  483. package/dist/core/process-matrix/store.d.ts +25 -0
  484. package/dist/core/process-matrix/store.d.ts.map +1 -0
  485. package/dist/core/process-matrix/store.js +189 -0
  486. package/dist/core/process-matrix/store.js.map +1 -0
  487. package/dist/core/process-matrix/supervisor.d.ts +75 -0
  488. package/dist/core/process-matrix/supervisor.d.ts.map +1 -0
  489. package/dist/core/process-matrix/supervisor.js +161 -0
  490. package/dist/core/process-matrix/supervisor.js.map +1 -0
  491. package/dist/core/profile-registry.d.ts +7 -0
  492. package/dist/core/profile-registry.d.ts.map +1 -1
  493. package/dist/core/profile-registry.js +25 -7
  494. package/dist/core/profile-registry.js.map +1 -1
  495. package/dist/core/python-runtime.d.ts.map +1 -1
  496. package/dist/core/python-runtime.js +3 -3
  497. package/dist/core/python-runtime.js.map +1 -1
  498. package/dist/core/reflection-controller.d.ts +33 -9
  499. package/dist/core/reflection-controller.d.ts.map +1 -1
  500. package/dist/core/reflection-controller.js +217 -127
  501. package/dist/core/reflection-controller.js.map +1 -1
  502. package/dist/core/reload-blockers.d.ts +36 -0
  503. package/dist/core/reload-blockers.d.ts.map +1 -1
  504. package/dist/core/reload-blockers.js +50 -8
  505. package/dist/core/reload-blockers.js.map +1 -1
  506. package/dist/core/research/evidence-bundle.d.ts.map +1 -1
  507. package/dist/core/research/evidence-bundle.js +1 -6
  508. package/dist/core/research/evidence-bundle.js.map +1 -1
  509. package/dist/core/research/lane-model-resolver.d.ts +33 -0
  510. package/dist/core/research/lane-model-resolver.d.ts.map +1 -0
  511. package/dist/core/research/lane-model-resolver.js +65 -0
  512. package/dist/core/research/lane-model-resolver.js.map +1 -0
  513. package/dist/core/research/model-fitness-controller.d.ts +38 -0
  514. package/dist/core/research/model-fitness-controller.d.ts.map +1 -0
  515. package/dist/core/research/model-fitness-controller.js +106 -0
  516. package/dist/core/research/model-fitness-controller.js.map +1 -0
  517. package/dist/core/research/model-fitness.d.ts +1 -1
  518. package/dist/core/research/model-fitness.d.ts.map +1 -1
  519. package/dist/core/research/model-fitness.js +4 -4
  520. package/dist/core/research/model-fitness.js.map +1 -1
  521. package/dist/core/research/research-gate.d.ts.map +1 -1
  522. package/dist/core/research/research-gate.js +7 -7
  523. package/dist/core/research/research-gate.js.map +1 -1
  524. package/dist/core/research/research-lane-controller.d.ts +62 -0
  525. package/dist/core/research/research-lane-controller.d.ts.map +1 -0
  526. package/dist/core/research/research-lane-controller.js +292 -0
  527. package/dist/core/research/research-lane-controller.js.map +1 -0
  528. package/dist/core/research/session-evidence-bundle.d.ts +2 -4
  529. package/dist/core/research/session-evidence-bundle.d.ts.map +1 -1
  530. package/dist/core/research/session-evidence-bundle.js +10 -46
  531. package/dist/core/research/session-evidence-bundle.js.map +1 -1
  532. package/dist/core/resource-loader.d.ts.map +1 -1
  533. package/dist/core/resource-loader.js +15 -9
  534. package/dist/core/resource-loader.js.map +1 -1
  535. package/dist/core/risk-classifier.d.ts +6 -0
  536. package/dist/core/risk-classifier.d.ts.map +1 -0
  537. package/dist/core/risk-classifier.js +225 -0
  538. package/dist/core/risk-classifier.js.map +1 -0
  539. package/dist/core/runtime-builder.d.ts +76 -17
  540. package/dist/core/runtime-builder.d.ts.map +1 -1
  541. package/dist/core/runtime-builder.js +367 -124
  542. package/dist/core/runtime-builder.js.map +1 -1
  543. package/dist/core/scout-controller.d.ts +6 -0
  544. package/dist/core/scout-controller.d.ts.map +1 -1
  545. package/dist/core/scout-controller.js +66 -52
  546. package/dist/core/scout-controller.js.map +1 -1
  547. package/dist/core/sdk.d.ts +6 -3
  548. package/dist/core/sdk.d.ts.map +1 -1
  549. package/dist/core/sdk.js +64 -27
  550. package/dist/core/sdk.js.map +1 -1
  551. package/dist/core/session-analytics.d.ts.map +1 -1
  552. package/dist/core/session-analytics.js +28 -1
  553. package/dist/core/session-analytics.js.map +1 -1
  554. package/dist/core/session-image-store.d.ts +33 -0
  555. package/dist/core/session-image-store.d.ts.map +1 -0
  556. package/dist/core/session-image-store.js +283 -0
  557. package/dist/core/session-image-store.js.map +1 -0
  558. package/dist/core/session-role.d.ts +33 -0
  559. package/dist/core/session-role.d.ts.map +1 -0
  560. package/dist/core/session-role.js +58 -0
  561. package/dist/core/session-role.js.map +1 -0
  562. package/dist/core/session-snapshot.d.ts +34 -0
  563. package/dist/core/session-snapshot.d.ts.map +1 -0
  564. package/dist/core/session-snapshot.js +68 -0
  565. package/dist/core/session-snapshot.js.map +1 -0
  566. package/dist/core/session-supervision-runtime.d.ts +31 -0
  567. package/dist/core/session-supervision-runtime.d.ts.map +1 -0
  568. package/dist/core/session-supervision-runtime.js +97 -0
  569. package/dist/core/session-supervision-runtime.js.map +1 -0
  570. package/dist/core/settings-manager.d.ts +51 -7
  571. package/dist/core/settings-manager.d.ts.map +1 -1
  572. package/dist/core/settings-manager.js +168 -82
  573. package/dist/core/settings-manager.js.map +1 -1
  574. package/dist/core/system-prompt-builder.d.ts +12 -0
  575. package/dist/core/system-prompt-builder.d.ts.map +1 -1
  576. package/dist/core/system-prompt-builder.js +6 -0
  577. package/dist/core/system-prompt-builder.js.map +1 -1
  578. package/dist/core/system-prompt.d.ts +14 -1
  579. package/dist/core/system-prompt.d.ts.map +1 -1
  580. package/dist/core/system-prompt.js +20 -3
  581. package/dist/core/system-prompt.js.map +1 -1
  582. package/dist/core/tasks/session-task-state.d.ts +13 -6
  583. package/dist/core/tasks/session-task-state.d.ts.map +1 -1
  584. package/dist/core/tasks/session-task-state.js +20 -22
  585. package/dist/core/tasks/session-task-state.js.map +1 -1
  586. package/dist/core/tasks/task-contract-monitor.d.ts +45 -0
  587. package/dist/core/tasks/task-contract-monitor.d.ts.map +1 -0
  588. package/dist/core/tasks/task-contract-monitor.js +56 -0
  589. package/dist/core/tasks/task-contract-monitor.js.map +1 -0
  590. package/dist/core/tasks/task-projection.d.ts +20 -0
  591. package/dist/core/tasks/task-projection.d.ts.map +1 -0
  592. package/dist/core/tasks/task-projection.js +47 -0
  593. package/dist/core/tasks/task-projection.js.map +1 -0
  594. package/dist/core/tasks/task-state.d.ts +12 -0
  595. package/dist/core/tasks/task-state.d.ts.map +1 -1
  596. package/dist/core/tasks/task-state.js +56 -12
  597. package/dist/core/tasks/task-state.js.map +1 -1
  598. package/dist/core/tool-capability-policy.d.ts +13 -0
  599. package/dist/core/tool-capability-policy.d.ts.map +1 -0
  600. package/dist/core/tool-capability-policy.js +85 -0
  601. package/dist/core/tool-capability-policy.js.map +1 -0
  602. package/dist/core/tool-gate-controller.d.ts.map +1 -1
  603. package/dist/core/tool-gate-controller.js +5 -0
  604. package/dist/core/tool-gate-controller.js.map +1 -1
  605. package/dist/core/tool-protocol-controller.d.ts +91 -0
  606. package/dist/core/tool-protocol-controller.d.ts.map +1 -0
  607. package/dist/core/tool-protocol-controller.js +512 -0
  608. package/dist/core/tool-protocol-controller.js.map +1 -0
  609. package/dist/core/tool-recovery-log-records.d.ts +6 -0
  610. package/dist/core/tool-recovery-log-records.d.ts.map +1 -1
  611. package/dist/core/tool-recovery-log-records.js +13 -31
  612. package/dist/core/tool-recovery-log-records.js.map +1 -1
  613. package/dist/core/tool-selection/promotion.d.ts +54 -0
  614. package/dist/core/tool-selection/promotion.d.ts.map +1 -0
  615. package/dist/core/tool-selection/promotion.js +81 -0
  616. package/dist/core/tool-selection/promotion.js.map +1 -0
  617. package/dist/core/tool-selection/tool-performance-store.d.ts +39 -6
  618. package/dist/core/tool-selection/tool-performance-store.d.ts.map +1 -1
  619. package/dist/core/tool-selection/tool-performance-store.js +153 -110
  620. package/dist/core/tool-selection/tool-performance-store.js.map +1 -1
  621. package/dist/core/tool-selection/tool-selection-controller.d.ts +45 -0
  622. package/dist/core/tool-selection/tool-selection-controller.d.ts.map +1 -1
  623. package/dist/core/tool-selection/tool-selection-controller.js +96 -0
  624. package/dist/core/tool-selection/tool-selection-controller.js.map +1 -1
  625. package/dist/core/tools/ask-question.d.ts +124 -0
  626. package/dist/core/tools/ask-question.d.ts.map +1 -0
  627. package/dist/core/tools/ask-question.js +693 -0
  628. package/dist/core/tools/ask-question.js.map +1 -0
  629. package/dist/core/tools/bash.d.ts +18 -0
  630. package/dist/core/tools/bash.d.ts.map +1 -1
  631. package/dist/core/tools/bash.js +95 -16
  632. package/dist/core/tools/bash.js.map +1 -1
  633. package/dist/core/tools/delegate-status.d.ts +44 -2
  634. package/dist/core/tools/delegate-status.d.ts.map +1 -1
  635. package/dist/core/tools/delegate-status.js +214 -19
  636. package/dist/core/tools/delegate-status.js.map +1 -1
  637. package/dist/core/tools/delegate.d.ts +15 -13
  638. package/dist/core/tools/delegate.d.ts.map +1 -1
  639. package/dist/core/tools/delegate.js +124 -22
  640. package/dist/core/tools/delegate.js.map +1 -1
  641. package/dist/core/tools/extensionify.d.ts.map +1 -1
  642. package/dist/core/tools/extensionify.js +1 -0
  643. package/dist/core/tools/extensionify.js.map +1 -1
  644. package/dist/core/tools/goal.d.ts +94 -3
  645. package/dist/core/tools/goal.d.ts.map +1 -1
  646. package/dist/core/tools/goal.js +165 -15
  647. package/dist/core/tools/goal.js.map +1 -1
  648. package/dist/core/tools/model-fitness.d.ts +7 -0
  649. package/dist/core/tools/model-fitness.d.ts.map +1 -1
  650. package/dist/core/tools/model-fitness.js +4 -4
  651. package/dist/core/tools/model-fitness.js.map +1 -1
  652. package/dist/core/tools/orchestration-panel.d.ts +43 -0
  653. package/dist/core/tools/orchestration-panel.d.ts.map +1 -0
  654. package/dist/core/tools/orchestration-panel.js +168 -0
  655. package/dist/core/tools/orchestration-panel.js.map +1 -0
  656. package/dist/core/tools/run-process.d.ts +26 -0
  657. package/dist/core/tools/run-process.d.ts.map +1 -0
  658. package/dist/core/tools/run-process.js +138 -0
  659. package/dist/core/tools/run-process.js.map +1 -0
  660. package/dist/core/tools/shell-contract-router.d.ts +6 -1
  661. package/dist/core/tools/shell-contract-router.d.ts.map +1 -1
  662. package/dist/core/tools/shell-contract-router.js +23 -2
  663. package/dist/core/tools/shell-contract-router.js.map +1 -1
  664. package/dist/core/tools/shell-session.d.ts +89 -0
  665. package/dist/core/tools/shell-session.d.ts.map +1 -0
  666. package/dist/core/tools/shell-session.js +432 -0
  667. package/dist/core/tools/shell-session.js.map +1 -0
  668. package/dist/core/tools/task-steps.d.ts +6 -0
  669. package/dist/core/tools/task-steps.d.ts.map +1 -1
  670. package/dist/core/tools/task-steps.js +165 -8
  671. package/dist/core/tools/task-steps.js.map +1 -1
  672. package/dist/core/tools/tmux-dispatch.d.ts +84 -0
  673. package/dist/core/tools/tmux-dispatch.d.ts.map +1 -0
  674. package/dist/core/tools/tmux-dispatch.js +91 -0
  675. package/dist/core/tools/tmux-dispatch.js.map +1 -0
  676. package/dist/core/tools/windows-shell-engine.d.ts +42 -0
  677. package/dist/core/tools/windows-shell-engine.d.ts.map +1 -0
  678. package/dist/core/tools/windows-shell-engine.js +151 -0
  679. package/dist/core/tools/windows-shell-engine.js.map +1 -0
  680. package/dist/core/tools/windows-shell-state.d.ts +40 -0
  681. package/dist/core/tools/windows-shell-state.d.ts.map +1 -0
  682. package/dist/core/tools/windows-shell-state.js +59 -0
  683. package/dist/core/tools/windows-shell-state.js.map +1 -0
  684. package/dist/core/tools/worktree-sync.d.ts +24 -0
  685. package/dist/core/tools/worktree-sync.d.ts.map +1 -0
  686. package/dist/core/tools/worktree-sync.js +338 -0
  687. package/dist/core/tools/worktree-sync.js.map +1 -0
  688. package/dist/core/trust-manager.d.ts +4 -1
  689. package/dist/core/trust-manager.d.ts.map +1 -1
  690. package/dist/core/trust-manager.js +20 -2
  691. package/dist/core/trust-manager.js.map +1 -1
  692. package/dist/core/util/atomic-file.d.ts +55 -0
  693. package/dist/core/util/atomic-file.d.ts.map +1 -0
  694. package/dist/core/util/atomic-file.js +255 -0
  695. package/dist/core/util/atomic-file.js.map +1 -0
  696. package/dist/core/util/bounded-jsonl.d.ts +10 -0
  697. package/dist/core/util/bounded-jsonl.d.ts.map +1 -0
  698. package/dist/core/util/bounded-jsonl.js +75 -0
  699. package/dist/core/util/bounded-jsonl.js.map +1 -0
  700. package/dist/core/util/minimatch-cache.d.ts +33 -0
  701. package/dist/core/util/minimatch-cache.d.ts.map +1 -0
  702. package/dist/core/util/minimatch-cache.js +0 -0
  703. package/dist/core/util/minimatch-cache.js.map +1 -0
  704. package/dist/core/util/value-guards.d.ts +5 -0
  705. package/dist/core/util/value-guards.d.ts.map +1 -0
  706. package/dist/core/util/value-guards.js +12 -0
  707. package/dist/core/util/value-guards.js.map +1 -0
  708. package/dist/core/worktree-sync/codes.d.ts +227 -0
  709. package/dist/core/worktree-sync/codes.d.ts.map +1 -0
  710. package/dist/core/worktree-sync/codes.js +14 -0
  711. package/dist/core/worktree-sync/codes.js.map +1 -0
  712. package/dist/core/worktree-sync/git-engine.d.ts +156 -0
  713. package/dist/core/worktree-sync/git-engine.d.ts.map +1 -0
  714. package/dist/core/worktree-sync/git-engine.js +1191 -0
  715. package/dist/core/worktree-sync/git-engine.js.map +1 -0
  716. package/dist/core/worktree-sync/lane-gate.d.ts +75 -0
  717. package/dist/core/worktree-sync/lane-gate.d.ts.map +1 -0
  718. package/dist/core/worktree-sync/lane-gate.js +360 -0
  719. package/dist/core/worktree-sync/lane-gate.js.map +1 -0
  720. package/dist/core/worktree-sync/runtime.d.ts +47 -0
  721. package/dist/core/worktree-sync/runtime.d.ts.map +1 -0
  722. package/dist/core/worktree-sync/runtime.js +96 -0
  723. package/dist/core/worktree-sync/runtime.js.map +1 -0
  724. package/dist/core/worktree-sync/store.d.ts +69 -0
  725. package/dist/core/worktree-sync/store.d.ts.map +1 -0
  726. package/dist/core/worktree-sync/store.js +252 -0
  727. package/dist/core/worktree-sync/store.js.map +1 -0
  728. package/dist/core/worktree-sync/watcher.d.ts +29 -0
  729. package/dist/core/worktree-sync/watcher.d.ts.map +1 -0
  730. package/dist/core/worktree-sync/watcher.js +93 -0
  731. package/dist/core/worktree-sync/watcher.js.map +1 -0
  732. package/dist/index.d.ts +13 -3
  733. package/dist/index.d.ts.map +1 -1
  734. package/dist/index.js +12 -2
  735. package/dist/index.js.map +1 -1
  736. package/dist/main.d.ts.map +1 -1
  737. package/dist/main.js +179 -1
  738. package/dist/main.js.map +1 -1
  739. package/dist/migrations.d.ts +7 -0
  740. package/dist/migrations.d.ts.map +1 -1
  741. package/dist/migrations.js +197 -1
  742. package/dist/migrations.js.map +1 -1
  743. package/dist/modes/interactive/auto-learn-controller.d.ts +16 -1
  744. package/dist/modes/interactive/auto-learn-controller.d.ts.map +1 -1
  745. package/dist/modes/interactive/auto-learn-controller.js +50 -8
  746. package/dist/modes/interactive/auto-learn-controller.js.map +1 -1
  747. package/dist/modes/interactive/clipboard-input.d.ts +7 -1
  748. package/dist/modes/interactive/clipboard-input.d.ts.map +1 -1
  749. package/dist/modes/interactive/clipboard-input.js +44 -6
  750. package/dist/modes/interactive/clipboard-input.js.map +1 -1
  751. package/dist/modes/interactive/components/profile-resource-editor.d.ts.map +1 -1
  752. package/dist/modes/interactive/components/profile-resource-editor.js +23 -1
  753. package/dist/modes/interactive/components/profile-resource-editor.js.map +1 -1
  754. package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  755. package/dist/modes/interactive/components/settings-selector.js +2 -2
  756. package/dist/modes/interactive/components/settings-selector.js.map +1 -1
  757. package/dist/modes/interactive/config-backup.d.ts.map +1 -1
  758. package/dist/modes/interactive/config-backup.js +5 -4
  759. package/dist/modes/interactive/config-backup.js.map +1 -1
  760. package/dist/modes/interactive/extension-ui-host.d.ts +14 -0
  761. package/dist/modes/interactive/extension-ui-host.d.ts.map +1 -1
  762. package/dist/modes/interactive/extension-ui-host.js +76 -15
  763. package/dist/modes/interactive/extension-ui-host.js.map +1 -1
  764. package/dist/modes/interactive/interactive-mode.d.ts +4 -0
  765. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  766. package/dist/modes/interactive/interactive-mode.js +67 -1
  767. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  768. package/dist/modes/interactive/key-handlers.d.ts.map +1 -1
  769. package/dist/modes/interactive/key-handlers.js +1 -1
  770. package/dist/modes/interactive/key-handlers.js.map +1 -1
  771. package/dist/modes/interactive/local-model-commands.d.ts +43 -0
  772. package/dist/modes/interactive/local-model-commands.d.ts.map +1 -1
  773. package/dist/modes/interactive/local-model-commands.js +290 -3
  774. package/dist/modes/interactive/local-model-commands.js.map +1 -1
  775. package/dist/modes/interactive/profile-menu-controller.d.ts.map +1 -1
  776. package/dist/modes/interactive/profile-menu-controller.js +7 -7
  777. package/dist/modes/interactive/profile-menu-controller.js.map +1 -1
  778. package/dist/modes/interactive/report-commands.d.ts.map +1 -1
  779. package/dist/modes/interactive/report-commands.js +21 -0
  780. package/dist/modes/interactive/report-commands.js.map +1 -1
  781. package/dist/modes/interactive/session-flow-commands.d.ts +17 -2
  782. package/dist/modes/interactive/session-flow-commands.d.ts.map +1 -1
  783. package/dist/modes/interactive/session-flow-commands.js +74 -21
  784. package/dist/modes/interactive/session-flow-commands.js.map +1 -1
  785. package/dist/modes/interactive/session-io-commands.d.ts.map +1 -1
  786. package/dist/modes/interactive/session-io-commands.js +16 -6
  787. package/dist/modes/interactive/session-io-commands.js.map +1 -1
  788. package/dist/modes/interactive/session-replacement-errors.d.ts +7 -0
  789. package/dist/modes/interactive/session-replacement-errors.d.ts.map +1 -0
  790. package/dist/modes/interactive/session-replacement-errors.js +15 -0
  791. package/dist/modes/interactive/session-replacement-errors.js.map +1 -0
  792. package/dist/modes/interactive/startup-checks.d.ts.map +1 -1
  793. package/dist/modes/interactive/startup-checks.js +1 -1
  794. package/dist/modes/interactive/startup-checks.js.map +1 -1
  795. package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
  796. package/dist/modes/rpc/rpc-mode.js +82 -23
  797. package/dist/modes/rpc/rpc-mode.js.map +1 -1
  798. package/dist/modes/rpc/rpc-types.d.ts +15 -0
  799. package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
  800. package/dist/modes/rpc/rpc-types.js +138 -1
  801. package/dist/modes/rpc/rpc-types.js.map +1 -1
  802. package/dist/utils/clipboard-image.d.ts.map +1 -1
  803. package/dist/utils/clipboard-image.js +2 -13
  804. package/dist/utils/clipboard-image.js.map +1 -1
  805. package/dist/utils/clipboard-native.d.ts +1 -0
  806. package/dist/utils/clipboard-native.d.ts.map +1 -1
  807. package/dist/utils/clipboard-native.js.map +1 -1
  808. package/dist/utils/clipboard.d.ts +1 -0
  809. package/dist/utils/clipboard.d.ts.map +1 -1
  810. package/dist/utils/clipboard.js +52 -1
  811. package/dist/utils/clipboard.js.map +1 -1
  812. package/dist/utils/fs-watch.d.ts +11 -0
  813. package/dist/utils/fs-watch.d.ts.map +1 -1
  814. package/dist/utils/fs-watch.js +20 -2
  815. package/dist/utils/fs-watch.js.map +1 -1
  816. package/dist/utils/highlight-js-languages.d.ts +4 -0
  817. package/dist/utils/highlight-js-languages.d.ts.map +1 -0
  818. package/dist/utils/highlight-js-languages.js +573 -0
  819. package/dist/utils/highlight-js-languages.js.map +1 -0
  820. package/dist/utils/platform.d.ts +3 -0
  821. package/dist/utils/platform.d.ts.map +1 -0
  822. package/dist/utils/platform.js +8 -0
  823. package/dist/utils/platform.js.map +1 -0
  824. package/dist/utils/shell.d.ts +7 -1
  825. package/dist/utils/shell.d.ts.map +1 -1
  826. package/dist/utils/shell.js +21 -6
  827. package/dist/utils/shell.js.map +1 -1
  828. package/dist/utils/syntax-highlight.d.ts.map +1 -1
  829. package/dist/utils/syntax-highlight.js +53 -5
  830. package/dist/utils/syntax-highlight.js.map +1 -1
  831. package/dist/utils/tools-manager.d.ts.map +1 -1
  832. package/dist/utils/tools-manager.js +112 -1
  833. package/dist/utils/tools-manager.js.map +1 -1
  834. package/dist/utils/work-directory.d.ts +11 -0
  835. package/dist/utils/work-directory.d.ts.map +1 -1
  836. package/dist/utils/work-directory.js +30 -9
  837. package/dist/utils/work-directory.js.map +1 -1
  838. package/docs/UI_POLISH_ORCHESTRATION.md +44 -0
  839. package/docs/UI_QUALITY_ORCHESTRATION.md +30 -0
  840. package/docs/docs.json +4 -0
  841. package/docs/extensions.md +62 -2
  842. package/docs/harness-architecture.md +156 -0
  843. package/docs/keybindings.md +11 -1
  844. package/docs/process-matrix.md +153 -0
  845. package/docs/rpc.md +45 -2
  846. package/docs/sessions.md +15 -0
  847. package/docs/settings.md +128 -11
  848. package/docs/task-steps.md +8 -0
  849. package/docs/telemetry.md +19 -0
  850. package/docs/tmux-agent-manager.md +92 -2
  851. package/docs/ui-reference/README.md +9 -0
  852. package/docs/ui-reference/orchestration-after.txt +7 -0
  853. package/docs/ui-reference/orchestration-before.txt +7 -0
  854. package/docs/ui-reference/orchestration-reference.txt +6 -0
  855. package/docs/usage.md +14 -2
  856. package/docs/windows.md +52 -3
  857. package/docs/work-directory.md +42 -2
  858. package/docs/worktree-sync.md +250 -0
  859. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  860. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  861. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  862. package/examples/extensions/question.ts +34 -245
  863. package/examples/extensions/questionnaire.ts +41 -407
  864. package/examples/extensions/sandbox/package-lock.json +2 -2
  865. package/examples/extensions/sandbox/package.json +1 -1
  866. package/examples/extensions/with-deps/package-lock.json +2 -2
  867. package/examples/extensions/with-deps/package.json +1 -1
  868. package/npm-shrinkwrap.json +12 -12
  869. package/package.json +10 -4
  870. package/dist/core/delegation/session-worker-result.d.ts +0 -16
  871. package/dist/core/delegation/session-worker-result.d.ts.map +0 -1
  872. package/dist/core/delegation/session-worker-result.js +0 -53
  873. package/dist/core/delegation/session-worker-result.js.map +0 -1
  874. package/dist/core/delegation/worker-result.d.ts +0 -15
  875. package/dist/core/delegation/worker-result.d.ts.map +0 -1
  876. package/dist/core/delegation/worker-result.js +0 -161
  877. package/dist/core/delegation/worker-result.js.map +0 -1
  878. package/dist/core/telemetry.d.ts.map +0 -1
  879. package/dist/core/telemetry.js.map +0 -1
  880. package/docs/integration-sweep-builder-blueprint-2026-07-09.md +0 -365
  881. package/docs/integration-sweep-resume-2026-07-09.md +0 -407
@@ -10,7 +10,7 @@
10
10
  */
11
11
  import type { Agent, ThinkingLevel } from "@caupulican/pi-agent-core";
12
12
  import type { SessionManager } from "@caupulican/pi-agent-core/node";
13
- import type { Model } from "@caupulican/pi-ai";
13
+ import type { Api, Model } from "@caupulican/pi-ai";
14
14
  import type { AgentSessionEvent, ModelCycleResult } from "./agent-session.ts";
15
15
  import type { ExtensionRunner } from "./extensions/index.ts";
16
16
  import type { ModelCapabilityProfile } from "./model-capability.ts";
@@ -19,7 +19,7 @@ import type { OllamaRuntime } from "./models/local-runtime.ts";
19
19
  import type { SettingsManager } from "./settings-manager.ts";
20
20
  export interface ModelSelectionControllerDeps {
21
21
  getAgent(): Agent;
22
- getModel(): Model<any> | undefined;
22
+ getModel(): Model<Api> | undefined;
23
23
  getThinkingLevel(): ThinkingLevel;
24
24
  getModelRegistry(): ModelRegistry;
25
25
  getSessionManager(): SessionManager;
@@ -28,7 +28,7 @@ export interface ModelSelectionControllerDeps {
28
28
  getAgentDir(): string;
29
29
  /** Scoped models (--models flag), used by the cycle path. */
30
30
  getScopedModels(): Array<{
31
- model: Model<any>;
31
+ model: Model<Api>;
32
32
  thinkingLevel?: ThinkingLevel;
33
33
  }>;
34
34
  /** The user-requested active tool set, re-applied when the model's capability class changes. */
@@ -56,7 +56,7 @@ export declare class ModelSelectionController {
56
56
  * Validates that auth is configured, saves to session and settings.
57
57
  * @throws Error if no auth is configured for the model
58
58
  */
59
- setModel(model: Model<any>, options?: {
59
+ setModel(model: Model<Api>, options?: {
60
60
  persistSettings?: boolean;
61
61
  }): Promise<void>;
62
62
  private _resyncToolSurfaceForModel;
@@ -1 +1 @@
1
- {"version":3,"file":"model-selection-controller.d.ts","sourceRoot":"","sources":["../../src/core/model-selection-controller.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * User-facing model + thinking-level selection.\n *\n * Extracted verbatim from agent-session.ts (god-file decomposition). Owns the manual/explicit model\n * switch and cycle paths and the thinking-level set/cycle/clamp — the deliberate human choices, as\n * opposed to the per-turn auto-routing owned by ModelRouterController. It mutates `agent.state.model`\n * / `agent.state.thinkingLevel` through deps and re-derives the capability-scoped tool surface, but the\n * session keeps the `model`/`thinkingLevel` GETTERS so the drive loop's reads are untouched. Model\n * choice is advisory-only: it warns on a bad-fit fitness probe or an oversized local model, never blocks.\n */\n\nimport { totalmem } from \"node:os\";\nimport type { Agent, ThinkingLevel } from \"@caupulican/pi-agent-core\";\nimport type { SessionManager } from \"@caupulican/pi-agent-core/node\";\nimport type { Api, Model } from \"@caupulican/pi-ai\";\nimport { clampThinkingLevel, getSupportedThinkingLevels, modelsAreEqual } from \"@caupulican/pi-ai\";\nimport type { AgentSessionEvent, ModelCycleResult } from \"./agent-session.ts\";\nimport { DEFAULT_THINKING_LEVEL } from \"./defaults.ts\";\nimport type { ExtensionRunner } from \"./extensions/index.ts\";\nimport type { ModelCapabilityProfile } from \"./model-capability.ts\";\nimport type { ModelRegistry } from \"./model-registry.ts\";\nimport { FitnessStore } from \"./models/fitness-store.ts\";\nimport { OLLAMA_PROVIDER } from \"./models/local-registration.ts\";\nimport type { OllamaRuntime } from \"./models/local-runtime.ts\";\nimport { matchesInstalledLocalModel } from \"./models/model-ref.ts\";\nimport { isProbeAllFailed } from \"./research/model-fitness.ts\";\nimport type { SettingsManager } from \"./settings-manager.ts\";\n\n/** Standard thinking levels (fallback set when the current model is unknown). */\nconst THINKING_LEVELS: ThinkingLevel[] = [\"off\", \"minimal\", \"low\", \"medium\", \"high\"];\n\nexport interface ModelSelectionControllerDeps {\n\tgetAgent(): Agent;\n\tgetModel(): Model<any> | undefined;\n\tgetThinkingLevel(): ThinkingLevel;\n\tgetModelRegistry(): ModelRegistry;\n\tgetSessionManager(): SessionManager;\n\tgetSettingsManager(): SettingsManager;\n\tgetExtensionRunner(): ExtensionRunner;\n\tgetAgentDir(): string;\n\t/** Scoped models (--models flag), used by the cycle path. */\n\tgetScopedModels(): Array<{ model: Model<any>; thinkingLevel?: ThinkingLevel }>;\n\t/** The user-requested active tool set, re-applied when the model's capability class changes. */\n\tgetRequestedActiveToolNames(): string[] | undefined;\n\tgetActiveToolNames(): string[];\n\tsetActiveToolsByName(toolNames: string[]): void;\n\tgetModelCapabilityProfile(): ModelCapabilityProfile;\n\t/** Rebuild the base prompt after a live orchestration-level change without altering requested tools. */\n\trefreshBaseSystemPrompt(): void;\n\t/** Session event emit (warnings + thinking_level_changed; model_select goes via the extension runner). */\n\temit(event: AgentSessionEvent): void;\n\t/** Context-window-usage warning check (session-owned, compaction-adjacent). */\n\tcheckContextWindowUsageWarning(): void;\n\t/** Resolve the Ollama server URL for a model base URL (local-runtime controller). */\n\tderiveOllamaServerUrl(modelBaseUrl: string): string;\n\t/** Local Ollama runtime for the given server URL (used for the oversized-model risk check). */\n\tgetLocalRuntime(serverUrl: string): OllamaRuntime;\n}\n\nexport class ModelSelectionController {\n\tprivate readonly deps: ModelSelectionControllerDeps;\n\n\tconstructor(deps: ModelSelectionControllerDeps) {\n\t\tthis.deps = deps;\n\t}\n\n\tprivate async _emitModelSelect(\n\t\tnextModel: Model<any>,\n\t\tpreviousModel: Model<any> | undefined,\n\t\tsource: \"set\" | \"cycle\" | \"restore\",\n\t): Promise<void> {\n\t\tif (modelsAreEqual(previousModel, nextModel)) return;\n\t\tawait this.deps.getExtensionRunner().emit({\n\t\t\ttype: \"model_select\",\n\t\t\tmodel: nextModel,\n\t\t\tpreviousModel,\n\t\t\tsource,\n\t\t});\n\t}\n\n\t/**\n\t * Set model directly.\n\t * Validates that auth is configured, saves to session and settings.\n\t * @throws Error if no auth is configured for the model\n\t */\n\tasync setModel(model: Model<any>, options: { persistSettings?: boolean } = {}): Promise<void> {\n\t\tif (!this.deps.getModelRegistry().hasConfiguredAuth(model)) {\n\t\t\tthrow new Error(`No API key for ${model.provider}/${model.id}`);\n\t\t}\n\n\t\tconst persistSettings = options.persistSettings ?? true;\n\t\tconst previousModel = this.deps.getModel();\n\t\tconst thinkingLevel = this._getThinkingLevelForModelSwitch();\n\t\tthis.deps.getAgent().state.model = model;\n\t\tthis.deps.getSessionManager().appendModelChange(model.provider, model.id);\n\t\tif (persistSettings) {\n\t\t\tthis.deps.getSettingsManager().setDefaultModelAndProvider(model.provider, model.id);\n\t\t}\n\n\t\t// Re-clamp thinking level for new model's capabilities\n\t\tthis.setThinkingLevel(thinkingLevel, { persistSettings });\n\t\tthis._resyncToolSurfaceForModel();\n\n\t\tawait this._emitModelSelect(model, previousModel, \"set\");\n\t\tthis.deps.checkContextWindowUsageWarning();\n\t\tawait this._warnIfManualModelChoiceIsRisky(model);\n\t}\n\n\tprivate _resyncToolSurfaceForModel(): void {\n\t\t// Re-derive the model-capability tool surface for the new model (restores the full requested\n\t\t// set when moving small -> large, reduces it when moving large -> small). Every switch path,\n\t\t// including model cycling and RPC commands that delegate to it, must pass through this seam.\n\t\tconst requestedActiveToolNames = this.deps.getRequestedActiveToolNames();\n\t\tif (requestedActiveToolNames) {\n\t\t\tconst before = this.deps.getActiveToolNames().join(\",\");\n\t\t\tthis.deps.setActiveToolsByName(requestedActiveToolNames);\n\t\t\tconst capability = this.deps.getModelCapabilityProfile();\n\t\t\tif (capability.class !== \"full\" && this.deps.getActiveToolNames().join(\",\") !== before) {\n\t\t\t\tthis.deps.emit({\n\t\t\t\t\ttype: \"warning\",\n\t\t\t\t\tmessage: `Small-context model detected (${capability.contextWindow ?? \"unknown\"} tokens, class '${capability.class}'): active tools reduced to [${this.deps.getActiveToolNames().join(\", \")}]; background lanes ${capability.backgroundLanesEnabled ? \"enabled\" : \"disabled\"}.`,\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Manual model choice is a deliberate human decision, not an auto-adoption flow — it is\n\t * ADVISORY ONLY: warn on evidence the model is a bad fit, but never block and never prompt\n\t * (print/RPC modes only ever see plain warning text through the existing `warning` event, the\n\t * same channel `_checkContextWindowUsageWarning` above uses). Two independent checks, both\n\t * best-effort:\n\t * - a recorded all-lanes-failed fitness probe on THIS host (see `isProbeAllFailed`);\n\t * - for an Ollama-served local model, weights that exceed ~90% of total system memory, which\n\t * is the exact failure the OOM report reproduced (llama-server needs the whole model resident).\n\t */\n\tprivate async _warnIfManualModelChoiceIsRisky(model: Model<Api>): Promise<void> {\n\t\tconst canonicalRef = `${model.provider}/${model.id}`;\n\t\ttry {\n\t\t\tconst fitness = FitnessStore.forAgentDir(this.deps.getAgentDir())\n\t\t\t\t.getForHost()\n\t\t\t\t.find((entry) => entry.model === canonicalRef);\n\t\t\tif (fitness && isProbeAllFailed(fitness.report)) {\n\t\t\t\tthis.deps.emit({\n\t\t\t\t\ttype: \"warning\",\n\t\t\t\t\tmessage: `${canonicalRef} failed its fitness probe on all surfaces on this host (probed ${fitness.at}) — it is likely to fail in production too. Proceeding because you set it manually.`,\n\t\t\t\t});\n\t\t\t}\n\t\t} catch {\n\t\t\t// advisory only; a lookup failure must never block a manual model choice\n\t\t}\n\n\t\tif (model.provider !== OLLAMA_PROVIDER) return;\n\t\ttry {\n\t\t\tconst serverUrl = this.deps.deriveOllamaServerUrl(model.baseUrl);\n\t\t\tconst installed = await this.deps.getLocalRuntime(serverUrl).list();\n\t\t\tconst entry = installed.find((candidate) => matchesInstalledLocalModel(model.id, candidate.name));\n\t\t\tif (!entry) return;\n\t\t\tconst memoryBudget = totalmem() * 0.9;\n\t\t\tif (entry.sizeBytes > memoryBudget) {\n\t\t\t\tconst sizeGb = (entry.sizeBytes / 1e9).toFixed(1);\n\t\t\t\tconst totalGb = (totalmem() / 1e9).toFixed(1);\n\t\t\t\tthis.deps.emit({\n\t\t\t\t\ttype: \"warning\",\n\t\t\t\t\tmessage: `${canonicalRef} is ~${sizeGb}GB, over 90% of this machine's ${totalGb}GB RAM — llama-server is likely to OOM when it runs. Proceeding because you set it manually.`,\n\t\t\t\t});\n\t\t\t}\n\t\t} catch {\n\t\t\t// advisory only; an unreachable local server must never block a manual model choice\n\t\t}\n\t}\n\n\t/**\n\t * Cycle to next/previous model.\n\t * Uses scoped models (from --models flag) if available, otherwise all available models.\n\t * @param direction - \"forward\" (default) or \"backward\"\n\t * @returns The new model info, or undefined if only one model available\n\t */\n\tasync cycleModel(direction: \"forward\" | \"backward\" = \"forward\"): Promise<ModelCycleResult | undefined> {\n\t\tif (this.deps.getScopedModels().length > 0) {\n\t\t\treturn this._cycleScopedModel(direction);\n\t\t}\n\t\treturn this._cycleAvailableModel(direction);\n\t}\n\n\tprivate async _cycleScopedModel(direction: \"forward\" | \"backward\"): Promise<ModelCycleResult | undefined> {\n\t\tconst scopedModels = this.deps\n\t\t\t.getScopedModels()\n\t\t\t.filter((scoped) => this.deps.getModelRegistry().hasConfiguredAuth(scoped.model));\n\t\tif (scopedModels.length <= 1) return undefined;\n\n\t\tconst currentModel = this.deps.getModel();\n\t\tlet currentIndex = scopedModels.findIndex((sm) => modelsAreEqual(sm.model, currentModel));\n\n\t\tif (currentIndex === -1) currentIndex = 0;\n\t\tconst len = scopedModels.length;\n\t\tconst nextIndex = direction === \"forward\" ? (currentIndex + 1) % len : (currentIndex - 1 + len) % len;\n\t\tconst next = scopedModels[nextIndex];\n\t\tconst thinkingLevel = this._getThinkingLevelForModelSwitch(next.thinkingLevel);\n\n\t\t// Apply model\n\t\tthis.deps.getAgent().state.model = next.model;\n\t\tthis.deps.getSessionManager().appendModelChange(next.model.provider, next.model.id);\n\t\tthis.deps.getSettingsManager().setDefaultModelAndProvider(next.model.provider, next.model.id);\n\n\t\t// Apply thinking level.\n\t\t// - Explicit scoped model thinking level overrides current session level\n\t\t// - Undefined scoped model thinking level inherits the current session preference\n\t\t// setThinkingLevel clamps to model capabilities.\n\t\tthis.setThinkingLevel(thinkingLevel);\n\t\tthis._resyncToolSurfaceForModel();\n\n\t\tawait this._emitModelSelect(next.model, currentModel, \"cycle\");\n\n\t\tthis.deps.checkContextWindowUsageWarning();\n\t\tawait this._warnIfManualModelChoiceIsRisky(next.model);\n\n\t\treturn { model: next.model, thinkingLevel: this.deps.getThinkingLevel(), isScoped: true };\n\t}\n\n\tprivate async _cycleAvailableModel(direction: \"forward\" | \"backward\"): Promise<ModelCycleResult | undefined> {\n\t\tconst availableModels = await this.deps.getModelRegistry().getAvailable();\n\t\tif (availableModels.length <= 1) return undefined;\n\n\t\tconst currentModel = this.deps.getModel();\n\t\tlet currentIndex = availableModels.findIndex((m) => modelsAreEqual(m, currentModel));\n\n\t\tif (currentIndex === -1) currentIndex = 0;\n\t\tconst len = availableModels.length;\n\t\tconst nextIndex = direction === \"forward\" ? (currentIndex + 1) % len : (currentIndex - 1 + len) % len;\n\t\tconst nextModel = availableModels[nextIndex];\n\n\t\tconst thinkingLevel = this._getThinkingLevelForModelSwitch();\n\t\tthis.deps.getAgent().state.model = nextModel;\n\t\tthis.deps.getSessionManager().appendModelChange(nextModel.provider, nextModel.id);\n\t\tthis.deps.getSettingsManager().setDefaultModelAndProvider(nextModel.provider, nextModel.id);\n\n\t\t// Re-clamp thinking level for new model's capabilities\n\t\tthis.setThinkingLevel(thinkingLevel);\n\t\tthis._resyncToolSurfaceForModel();\n\n\t\tawait this._emitModelSelect(nextModel, currentModel, \"cycle\");\n\n\t\tthis.deps.checkContextWindowUsageWarning();\n\t\tawait this._warnIfManualModelChoiceIsRisky(nextModel);\n\n\t\treturn { model: nextModel, thinkingLevel: this.deps.getThinkingLevel(), isScoped: false };\n\t}\n\n\t/**\n\t * Set thinking level.\n\t * Clamps to model capabilities based on available thinking levels.\n\t * Saves to session and settings only if the level actually changes.\n\t */\n\tsetThinkingLevel(level: ThinkingLevel, options: { persistSettings?: boolean } = {}): void {\n\t\tconst availableLevels = this.getAvailableThinkingLevels();\n\t\tconst effectiveLevel = availableLevels.includes(level) ? level : this._clampThinkingLevel(level, availableLevels);\n\n\t\t// Only persist if actually changing\n\t\tconst previousLevel = this.deps.getAgent().state.thinkingLevel;\n\t\tconst isChanging = effectiveLevel !== previousLevel;\n\t\tconst persistSettings = options.persistSettings ?? true;\n\n\t\tthis.deps.getAgent().state.thinkingLevel = effectiveLevel;\n\n\t\tif (isChanging) {\n\t\t\tthis.deps.refreshBaseSystemPrompt();\n\t\t\tthis.deps.getSessionManager().appendThinkingLevelChange(effectiveLevel);\n\t\t\tif (persistSettings && (this.supportsThinking() || effectiveLevel !== \"off\")) {\n\t\t\t\tthis.deps.getSettingsManager().setDefaultThinkingLevel(effectiveLevel);\n\t\t\t}\n\t\t\tthis.deps.emit({ type: \"thinking_level_changed\", level: effectiveLevel });\n\t\t\tvoid this.deps.getExtensionRunner().emit({\n\t\t\t\ttype: \"thinking_level_select\",\n\t\t\t\tlevel: effectiveLevel,\n\t\t\t\tpreviousLevel,\n\t\t\t});\n\t\t}\n\t}\n\n\t/**\n\t * Cycle to next thinking level.\n\t * @returns New level, or undefined if model doesn't support thinking\n\t */\n\tcycleThinkingLevel(): ThinkingLevel | undefined {\n\t\tif (!this.supportsThinking()) return undefined;\n\n\t\tconst levels = this.getAvailableThinkingLevels();\n\t\tconst currentIndex = levels.indexOf(this.deps.getThinkingLevel());\n\t\tconst nextIndex = (currentIndex + 1) % levels.length;\n\t\tconst nextLevel = levels[nextIndex];\n\n\t\tthis.setThinkingLevel(nextLevel);\n\t\treturn nextLevel;\n\t}\n\n\t/**\n\t * Get available thinking levels for current model.\n\t * The provider will clamp to what the specific model supports internally.\n\t */\n\tgetAvailableThinkingLevels(): ThinkingLevel[] {\n\t\tconst model = this.deps.getModel();\n\t\tif (!model) return THINKING_LEVELS;\n\t\treturn getSupportedThinkingLevels(model) as ThinkingLevel[];\n\t}\n\n\t/**\n\t * Check if current model supports thinking/reasoning.\n\t */\n\tsupportsThinking(): boolean {\n\t\treturn !!this.deps.getModel()?.reasoning;\n\t}\n\n\tprivate _getThinkingLevelForModelSwitch(explicitLevel?: ThinkingLevel): ThinkingLevel {\n\t\tif (explicitLevel !== undefined) {\n\t\t\treturn explicitLevel;\n\t\t}\n\t\tif (!this.supportsThinking()) {\n\t\t\treturn this.deps.getSettingsManager().getDefaultThinkingLevel() ?? DEFAULT_THINKING_LEVEL;\n\t\t}\n\t\treturn this.deps.getThinkingLevel();\n\t}\n\n\tprivate _clampThinkingLevel(level: ThinkingLevel, _availableLevels: ThinkingLevel[]): ThinkingLevel {\n\t\tconst model = this.deps.getModel();\n\t\treturn model ? (clampThinkingLevel(model, level) as ThinkingLevel) : \"off\";\n\t}\n}\n"]}
1
+ {"version":3,"file":"model-selection-controller.d.ts","sourceRoot":"","sources":["../../src/core/model-selection-controller.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * User-facing model + thinking-level selection.\n *\n * Extracted verbatim from agent-session.ts (god-file decomposition). Owns the manual/explicit model\n * switch and cycle paths and the thinking-level set/cycle/clamp — the deliberate human choices, as\n * opposed to the per-turn auto-routing owned by ModelRouterController. It mutates `agent.state.model`\n * / `agent.state.thinkingLevel` through deps and re-derives the capability-scoped tool surface, but the\n * session keeps the `model`/`thinkingLevel` GETTERS so the drive loop's reads are untouched. Model\n * choice is advisory-only: it warns on a bad-fit fitness probe or an oversized local model, never blocks.\n */\n\nimport { totalmem } from \"node:os\";\nimport type { Agent, ThinkingLevel } from \"@caupulican/pi-agent-core\";\nimport type { SessionManager } from \"@caupulican/pi-agent-core/node\";\nimport type { Api, Model } from \"@caupulican/pi-ai\";\nimport { clampThinkingLevel, getSupportedThinkingLevels, modelsAreEqual } from \"@caupulican/pi-ai\";\nimport type { AgentSessionEvent, ModelCycleResult } from \"./agent-session.ts\";\nimport { DEFAULT_THINKING_LEVEL } from \"./defaults.ts\";\nimport type { ExtensionRunner } from \"./extensions/index.ts\";\nimport type { ModelCapabilityProfile } from \"./model-capability.ts\";\nimport type { ModelRegistry } from \"./model-registry.ts\";\nimport { FitnessStore } from \"./models/fitness-store.ts\";\nimport { OLLAMA_PROVIDER } from \"./models/local-registration.ts\";\nimport type { OllamaRuntime } from \"./models/local-runtime.ts\";\nimport { matchesInstalledLocalModel } from \"./models/model-ref.ts\";\nimport { isProbeAllFailed } from \"./research/model-fitness.ts\";\nimport type { SettingsManager } from \"./settings-manager.ts\";\n\n/** Standard thinking levels (fallback set when the current model is unknown). */\nconst THINKING_LEVELS: ThinkingLevel[] = [\"off\", \"minimal\", \"low\", \"medium\", \"high\"];\n\nexport interface ModelSelectionControllerDeps {\n\tgetAgent(): Agent;\n\tgetModel(): Model<Api> | undefined;\n\tgetThinkingLevel(): ThinkingLevel;\n\tgetModelRegistry(): ModelRegistry;\n\tgetSessionManager(): SessionManager;\n\tgetSettingsManager(): SettingsManager;\n\tgetExtensionRunner(): ExtensionRunner;\n\tgetAgentDir(): string;\n\t/** Scoped models (--models flag), used by the cycle path. */\n\tgetScopedModels(): Array<{ model: Model<Api>; thinkingLevel?: ThinkingLevel }>;\n\t/** The user-requested active tool set, re-applied when the model's capability class changes. */\n\tgetRequestedActiveToolNames(): string[] | undefined;\n\tgetActiveToolNames(): string[];\n\tsetActiveToolsByName(toolNames: string[]): void;\n\tgetModelCapabilityProfile(): ModelCapabilityProfile;\n\t/** Rebuild the base prompt after a live orchestration-level change without altering requested tools. */\n\trefreshBaseSystemPrompt(): void;\n\t/** Session event emit (warnings + thinking_level_changed; model_select goes via the extension runner). */\n\temit(event: AgentSessionEvent): void;\n\t/** Context-window-usage warning check (session-owned, compaction-adjacent). */\n\tcheckContextWindowUsageWarning(): void;\n\t/** Resolve the Ollama server URL for a model base URL (local-runtime controller). */\n\tderiveOllamaServerUrl(modelBaseUrl: string): string;\n\t/** Local Ollama runtime for the given server URL (used for the oversized-model risk check). */\n\tgetLocalRuntime(serverUrl: string): OllamaRuntime;\n}\n\nexport class ModelSelectionController {\n\tprivate readonly deps: ModelSelectionControllerDeps;\n\n\tconstructor(deps: ModelSelectionControllerDeps) {\n\t\tthis.deps = deps;\n\t}\n\n\tprivate async _emitModelSelect(\n\t\tnextModel: Model<Api>,\n\t\tpreviousModel: Model<Api> | undefined,\n\t\tsource: \"set\" | \"cycle\" | \"restore\",\n\t): Promise<void> {\n\t\tif (modelsAreEqual(previousModel, nextModel)) return;\n\t\tawait this.deps.getExtensionRunner().emit({\n\t\t\ttype: \"model_select\",\n\t\t\tmodel: nextModel,\n\t\t\tpreviousModel,\n\t\t\tsource,\n\t\t});\n\t}\n\n\t/**\n\t * Set model directly.\n\t * Validates that auth is configured, saves to session and settings.\n\t * @throws Error if no auth is configured for the model\n\t */\n\tasync setModel(model: Model<Api>, options: { persistSettings?: boolean } = {}): Promise<void> {\n\t\tif (!this.deps.getModelRegistry().hasConfiguredAuth(model)) {\n\t\t\tthrow new Error(`No API key for ${model.provider}/${model.id}`);\n\t\t}\n\n\t\tconst persistSettings = options.persistSettings ?? true;\n\t\tconst previousModel = this.deps.getModel();\n\t\tconst thinkingLevel = this._getThinkingLevelForModelSwitch();\n\t\tthis.deps.getAgent().state.model = model;\n\t\tthis.deps.getSessionManager().appendModelChange(model.provider, model.id);\n\t\tif (persistSettings) {\n\t\t\tthis.deps.getSettingsManager().setDefaultModelAndProvider(model.provider, model.id);\n\t\t}\n\n\t\t// Re-clamp thinking level for new model's capabilities\n\t\tthis.setThinkingLevel(thinkingLevel, { persistSettings });\n\t\tthis._resyncToolSurfaceForModel();\n\n\t\tawait this._emitModelSelect(model, previousModel, \"set\");\n\t\tthis.deps.checkContextWindowUsageWarning();\n\t\tawait this._warnIfManualModelChoiceIsRisky(model);\n\t}\n\n\tprivate _resyncToolSurfaceForModel(): void {\n\t\t// Re-derive the model-capability tool surface for the new model (restores the full requested\n\t\t// set when moving small -> large, reduces it when moving large -> small). Every switch path,\n\t\t// including model cycling and RPC commands that delegate to it, must pass through this seam.\n\t\tconst requestedActiveToolNames = this.deps.getRequestedActiveToolNames();\n\t\tif (requestedActiveToolNames) {\n\t\t\tconst before = this.deps.getActiveToolNames().join(\",\");\n\t\t\tthis.deps.setActiveToolsByName(requestedActiveToolNames);\n\t\t\tconst capability = this.deps.getModelCapabilityProfile();\n\t\t\tif (capability.class !== \"full\" && this.deps.getActiveToolNames().join(\",\") !== before) {\n\t\t\t\tthis.deps.emit({\n\t\t\t\t\ttype: \"warning\",\n\t\t\t\t\tmessage: `Small-context model detected (${capability.contextWindow ?? \"unknown\"} tokens, class '${capability.class}'): active tools reduced to [${this.deps.getActiveToolNames().join(\", \")}]; background lanes ${capability.backgroundLanesEnabled ? \"enabled\" : \"disabled\"}.`,\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Manual model choice is a deliberate human decision, not an auto-adoption flow — it is\n\t * ADVISORY ONLY: warn on evidence the model is a bad fit, but never block and never prompt\n\t * (print/RPC modes only ever see plain warning text through the existing `warning` event, the\n\t * same channel `_checkContextWindowUsageWarning` above uses). Two independent checks, both\n\t * best-effort:\n\t * - a recorded all-lanes-failed fitness probe on THIS host (see `isProbeAllFailed`);\n\t * - for an Ollama-served local model, weights that exceed ~90% of total system memory, which\n\t * is the exact failure the OOM report reproduced (llama-server needs the whole model resident).\n\t */\n\tprivate async _warnIfManualModelChoiceIsRisky(model: Model<Api>): Promise<void> {\n\t\tconst canonicalRef = `${model.provider}/${model.id}`;\n\t\ttry {\n\t\t\tconst fitness = FitnessStore.forAgentDir(this.deps.getAgentDir())\n\t\t\t\t.getForHost()\n\t\t\t\t.find((entry) => entry.model === canonicalRef);\n\t\t\tif (fitness && isProbeAllFailed(fitness.report)) {\n\t\t\t\tthis.deps.emit({\n\t\t\t\t\ttype: \"warning\",\n\t\t\t\t\tmessage: `${canonicalRef} failed its fitness probe on all surfaces on this host (probed ${fitness.at}) — it is likely to fail in production too. Proceeding because you set it manually.`,\n\t\t\t\t});\n\t\t\t}\n\t\t} catch {\n\t\t\t// advisory only; a lookup failure must never block a manual model choice\n\t\t}\n\n\t\tif (model.provider !== OLLAMA_PROVIDER) return;\n\t\ttry {\n\t\t\tconst serverUrl = this.deps.deriveOllamaServerUrl(model.baseUrl);\n\t\t\tconst installed = await this.deps.getLocalRuntime(serverUrl).list();\n\t\t\tconst entry = installed.find((candidate) => matchesInstalledLocalModel(model.id, candidate.name));\n\t\t\tif (!entry) return;\n\t\t\tconst memoryBudget = totalmem() * 0.9;\n\t\t\tif (entry.sizeBytes > memoryBudget) {\n\t\t\t\tconst sizeGb = (entry.sizeBytes / 1e9).toFixed(1);\n\t\t\t\tconst totalGb = (totalmem() / 1e9).toFixed(1);\n\t\t\t\tthis.deps.emit({\n\t\t\t\t\ttype: \"warning\",\n\t\t\t\t\tmessage: `${canonicalRef} is ~${sizeGb}GB, over 90% of this machine's ${totalGb}GB RAM — llama-server is likely to OOM when it runs. Proceeding because you set it manually.`,\n\t\t\t\t});\n\t\t\t}\n\t\t} catch {\n\t\t\t// advisory only; an unreachable local server must never block a manual model choice\n\t\t}\n\t}\n\n\t/**\n\t * Cycle to next/previous model.\n\t * Uses scoped models (from --models flag) if available, otherwise all available models.\n\t * @param direction - \"forward\" (default) or \"backward\"\n\t * @returns The new model info, or undefined if only one model available\n\t */\n\tasync cycleModel(direction: \"forward\" | \"backward\" = \"forward\"): Promise<ModelCycleResult | undefined> {\n\t\tif (this.deps.getScopedModels().length > 0) {\n\t\t\treturn this._cycleScopedModel(direction);\n\t\t}\n\t\treturn this._cycleAvailableModel(direction);\n\t}\n\n\tprivate async _cycleScopedModel(direction: \"forward\" | \"backward\"): Promise<ModelCycleResult | undefined> {\n\t\tconst scopedModels = this.deps\n\t\t\t.getScopedModels()\n\t\t\t.filter((scoped) => this.deps.getModelRegistry().hasConfiguredAuth(scoped.model));\n\t\tif (scopedModels.length <= 1) return undefined;\n\n\t\tconst currentModel = this.deps.getModel();\n\t\tlet currentIndex = scopedModels.findIndex((sm) => modelsAreEqual(sm.model, currentModel));\n\n\t\tif (currentIndex === -1) currentIndex = 0;\n\t\tconst len = scopedModels.length;\n\t\tconst nextIndex = direction === \"forward\" ? (currentIndex + 1) % len : (currentIndex - 1 + len) % len;\n\t\tconst next = scopedModels[nextIndex];\n\t\tconst thinkingLevel = this._getThinkingLevelForModelSwitch(next.thinkingLevel);\n\n\t\t// Apply model\n\t\tthis.deps.getAgent().state.model = next.model;\n\t\tthis.deps.getSessionManager().appendModelChange(next.model.provider, next.model.id);\n\t\tthis.deps.getSettingsManager().setDefaultModelAndProvider(next.model.provider, next.model.id);\n\n\t\t// Apply thinking level.\n\t\t// - Explicit scoped model thinking level overrides current session level\n\t\t// - Undefined scoped model thinking level inherits the current session preference\n\t\t// setThinkingLevel clamps to model capabilities.\n\t\tthis.setThinkingLevel(thinkingLevel);\n\t\tthis._resyncToolSurfaceForModel();\n\n\t\tawait this._emitModelSelect(next.model, currentModel, \"cycle\");\n\n\t\tthis.deps.checkContextWindowUsageWarning();\n\t\tawait this._warnIfManualModelChoiceIsRisky(next.model);\n\n\t\treturn { model: next.model, thinkingLevel: this.deps.getThinkingLevel(), isScoped: true };\n\t}\n\n\tprivate async _cycleAvailableModel(direction: \"forward\" | \"backward\"): Promise<ModelCycleResult | undefined> {\n\t\tconst availableModels = await this.deps.getModelRegistry().getAvailable();\n\t\tif (availableModels.length <= 1) return undefined;\n\n\t\tconst currentModel = this.deps.getModel();\n\t\tlet currentIndex = availableModels.findIndex((m) => modelsAreEqual(m, currentModel));\n\n\t\tif (currentIndex === -1) currentIndex = 0;\n\t\tconst len = availableModels.length;\n\t\tconst nextIndex = direction === \"forward\" ? (currentIndex + 1) % len : (currentIndex - 1 + len) % len;\n\t\tconst nextModel = availableModels[nextIndex];\n\n\t\tconst thinkingLevel = this._getThinkingLevelForModelSwitch();\n\t\tthis.deps.getAgent().state.model = nextModel;\n\t\tthis.deps.getSessionManager().appendModelChange(nextModel.provider, nextModel.id);\n\t\tthis.deps.getSettingsManager().setDefaultModelAndProvider(nextModel.provider, nextModel.id);\n\n\t\t// Re-clamp thinking level for new model's capabilities\n\t\tthis.setThinkingLevel(thinkingLevel);\n\t\tthis._resyncToolSurfaceForModel();\n\n\t\tawait this._emitModelSelect(nextModel, currentModel, \"cycle\");\n\n\t\tthis.deps.checkContextWindowUsageWarning();\n\t\tawait this._warnIfManualModelChoiceIsRisky(nextModel);\n\n\t\treturn { model: nextModel, thinkingLevel: this.deps.getThinkingLevel(), isScoped: false };\n\t}\n\n\t/**\n\t * Set thinking level.\n\t * Clamps to model capabilities based on available thinking levels.\n\t * Saves to session and settings only if the level actually changes.\n\t */\n\tsetThinkingLevel(level: ThinkingLevel, options: { persistSettings?: boolean } = {}): void {\n\t\tconst availableLevels = this.getAvailableThinkingLevels();\n\t\tconst effectiveLevel = availableLevels.includes(level) ? level : this._clampThinkingLevel(level, availableLevels);\n\n\t\t// Only persist if actually changing\n\t\tconst previousLevel = this.deps.getAgent().state.thinkingLevel;\n\t\tconst isChanging = effectiveLevel !== previousLevel;\n\t\tconst persistSettings = options.persistSettings ?? true;\n\n\t\tthis.deps.getAgent().state.thinkingLevel = effectiveLevel;\n\n\t\tif (isChanging) {\n\t\t\tthis.deps.refreshBaseSystemPrompt();\n\t\t\tthis.deps.getSessionManager().appendThinkingLevelChange(effectiveLevel);\n\t\t\tif (persistSettings && (this.supportsThinking() || effectiveLevel !== \"off\")) {\n\t\t\t\tthis.deps.getSettingsManager().setDefaultThinkingLevel(effectiveLevel);\n\t\t\t}\n\t\t\tthis.deps.emit({ type: \"thinking_level_changed\", level: effectiveLevel });\n\t\t\tvoid this.deps.getExtensionRunner().emit({\n\t\t\t\ttype: \"thinking_level_select\",\n\t\t\t\tlevel: effectiveLevel,\n\t\t\t\tpreviousLevel,\n\t\t\t});\n\t\t}\n\t}\n\n\t/**\n\t * Cycle to next thinking level.\n\t * @returns New level, or undefined if model doesn't support thinking\n\t */\n\tcycleThinkingLevel(): ThinkingLevel | undefined {\n\t\tif (!this.supportsThinking()) return undefined;\n\n\t\tconst levels = this.getAvailableThinkingLevels();\n\t\tconst currentIndex = levels.indexOf(this.deps.getThinkingLevel());\n\t\tconst nextIndex = (currentIndex + 1) % levels.length;\n\t\tconst nextLevel = levels[nextIndex];\n\n\t\tthis.setThinkingLevel(nextLevel);\n\t\treturn nextLevel;\n\t}\n\n\t/**\n\t * Get available thinking levels for current model.\n\t * The provider will clamp to what the specific model supports internally.\n\t */\n\tgetAvailableThinkingLevels(): ThinkingLevel[] {\n\t\tconst model = this.deps.getModel();\n\t\tif (!model) return THINKING_LEVELS;\n\t\treturn getSupportedThinkingLevels(model) as ThinkingLevel[];\n\t}\n\n\t/**\n\t * Check if current model supports thinking/reasoning.\n\t */\n\tsupportsThinking(): boolean {\n\t\treturn !!this.deps.getModel()?.reasoning;\n\t}\n\n\tprivate _getThinkingLevelForModelSwitch(explicitLevel?: ThinkingLevel): ThinkingLevel {\n\t\tif (explicitLevel !== undefined) {\n\t\t\treturn explicitLevel;\n\t\t}\n\t\tif (!this.supportsThinking()) {\n\t\t\treturn this.deps.getSettingsManager().getDefaultThinkingLevel() ?? DEFAULT_THINKING_LEVEL;\n\t\t}\n\t\treturn this.deps.getThinkingLevel();\n\t}\n\n\tprivate _clampThinkingLevel(level: ThinkingLevel, _availableLevels: ThinkingLevel[]): ThinkingLevel {\n\t\tconst model = this.deps.getModel();\n\t\treturn model ? (clampThinkingLevel(model, level) as ThinkingLevel) : \"off\";\n\t}\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"model-selection-controller.js","sourceRoot":"","sources":["../../src/core/model-selection-controller.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * User-facing model + thinking-level selection.\n *\n * Extracted verbatim from agent-session.ts (god-file decomposition). Owns the manual/explicit model\n * switch and cycle paths and the thinking-level set/cycle/clamp — the deliberate human choices, as\n * opposed to the per-turn auto-routing owned by ModelRouterController. It mutates `agent.state.model`\n * / `agent.state.thinkingLevel` through deps and re-derives the capability-scoped tool surface, but the\n * session keeps the `model`/`thinkingLevel` GETTERS so the drive loop's reads are untouched. Model\n * choice is advisory-only: it warns on a bad-fit fitness probe or an oversized local model, never blocks.\n */\n\nimport { totalmem } from \"node:os\";\nimport type { Agent, ThinkingLevel } from \"@caupulican/pi-agent-core\";\nimport type { SessionManager } from \"@caupulican/pi-agent-core/node\";\nimport type { Api, Model } from \"@caupulican/pi-ai\";\nimport { clampThinkingLevel, getSupportedThinkingLevels, modelsAreEqual } from \"@caupulican/pi-ai\";\nimport type { AgentSessionEvent, ModelCycleResult } from \"./agent-session.ts\";\nimport { DEFAULT_THINKING_LEVEL } from \"./defaults.ts\";\nimport type { ExtensionRunner } from \"./extensions/index.ts\";\nimport type { ModelCapabilityProfile } from \"./model-capability.ts\";\nimport type { ModelRegistry } from \"./model-registry.ts\";\nimport { FitnessStore } from \"./models/fitness-store.ts\";\nimport { OLLAMA_PROVIDER } from \"./models/local-registration.ts\";\nimport type { OllamaRuntime } from \"./models/local-runtime.ts\";\nimport { matchesInstalledLocalModel } from \"./models/model-ref.ts\";\nimport { isProbeAllFailed } from \"./research/model-fitness.ts\";\nimport type { SettingsManager } from \"./settings-manager.ts\";\n\n/** Standard thinking levels (fallback set when the current model is unknown). */\nconst THINKING_LEVELS: ThinkingLevel[] = [\"off\", \"minimal\", \"low\", \"medium\", \"high\"];\n\nexport interface ModelSelectionControllerDeps {\n\tgetAgent(): Agent;\n\tgetModel(): Model<any> | undefined;\n\tgetThinkingLevel(): ThinkingLevel;\n\tgetModelRegistry(): ModelRegistry;\n\tgetSessionManager(): SessionManager;\n\tgetSettingsManager(): SettingsManager;\n\tgetExtensionRunner(): ExtensionRunner;\n\tgetAgentDir(): string;\n\t/** Scoped models (--models flag), used by the cycle path. */\n\tgetScopedModels(): Array<{ model: Model<any>; thinkingLevel?: ThinkingLevel }>;\n\t/** The user-requested active tool set, re-applied when the model's capability class changes. */\n\tgetRequestedActiveToolNames(): string[] | undefined;\n\tgetActiveToolNames(): string[];\n\tsetActiveToolsByName(toolNames: string[]): void;\n\tgetModelCapabilityProfile(): ModelCapabilityProfile;\n\t/** Rebuild the base prompt after a live orchestration-level change without altering requested tools. */\n\trefreshBaseSystemPrompt(): void;\n\t/** Session event emit (warnings + thinking_level_changed; model_select goes via the extension runner). */\n\temit(event: AgentSessionEvent): void;\n\t/** Context-window-usage warning check (session-owned, compaction-adjacent). */\n\tcheckContextWindowUsageWarning(): void;\n\t/** Resolve the Ollama server URL for a model base URL (local-runtime controller). */\n\tderiveOllamaServerUrl(modelBaseUrl: string): string;\n\t/** Local Ollama runtime for the given server URL (used for the oversized-model risk check). */\n\tgetLocalRuntime(serverUrl: string): OllamaRuntime;\n}\n\nexport class ModelSelectionController {\n\tprivate readonly deps: ModelSelectionControllerDeps;\n\n\tconstructor(deps: ModelSelectionControllerDeps) {\n\t\tthis.deps = deps;\n\t}\n\n\tprivate async _emitModelSelect(\n\t\tnextModel: Model<any>,\n\t\tpreviousModel: Model<any> | undefined,\n\t\tsource: \"set\" | \"cycle\" | \"restore\",\n\t): Promise<void> {\n\t\tif (modelsAreEqual(previousModel, nextModel)) return;\n\t\tawait this.deps.getExtensionRunner().emit({\n\t\t\ttype: \"model_select\",\n\t\t\tmodel: nextModel,\n\t\t\tpreviousModel,\n\t\t\tsource,\n\t\t});\n\t}\n\n\t/**\n\t * Set model directly.\n\t * Validates that auth is configured, saves to session and settings.\n\t * @throws Error if no auth is configured for the model\n\t */\n\tasync setModel(model: Model<any>, options: { persistSettings?: boolean } = {}): Promise<void> {\n\t\tif (!this.deps.getModelRegistry().hasConfiguredAuth(model)) {\n\t\t\tthrow new Error(`No API key for ${model.provider}/${model.id}`);\n\t\t}\n\n\t\tconst persistSettings = options.persistSettings ?? true;\n\t\tconst previousModel = this.deps.getModel();\n\t\tconst thinkingLevel = this._getThinkingLevelForModelSwitch();\n\t\tthis.deps.getAgent().state.model = model;\n\t\tthis.deps.getSessionManager().appendModelChange(model.provider, model.id);\n\t\tif (persistSettings) {\n\t\t\tthis.deps.getSettingsManager().setDefaultModelAndProvider(model.provider, model.id);\n\t\t}\n\n\t\t// Re-clamp thinking level for new model's capabilities\n\t\tthis.setThinkingLevel(thinkingLevel, { persistSettings });\n\t\tthis._resyncToolSurfaceForModel();\n\n\t\tawait this._emitModelSelect(model, previousModel, \"set\");\n\t\tthis.deps.checkContextWindowUsageWarning();\n\t\tawait this._warnIfManualModelChoiceIsRisky(model);\n\t}\n\n\tprivate _resyncToolSurfaceForModel(): void {\n\t\t// Re-derive the model-capability tool surface for the new model (restores the full requested\n\t\t// set when moving small -> large, reduces it when moving large -> small). Every switch path,\n\t\t// including model cycling and RPC commands that delegate to it, must pass through this seam.\n\t\tconst requestedActiveToolNames = this.deps.getRequestedActiveToolNames();\n\t\tif (requestedActiveToolNames) {\n\t\t\tconst before = this.deps.getActiveToolNames().join(\",\");\n\t\t\tthis.deps.setActiveToolsByName(requestedActiveToolNames);\n\t\t\tconst capability = this.deps.getModelCapabilityProfile();\n\t\t\tif (capability.class !== \"full\" && this.deps.getActiveToolNames().join(\",\") !== before) {\n\t\t\t\tthis.deps.emit({\n\t\t\t\t\ttype: \"warning\",\n\t\t\t\t\tmessage: `Small-context model detected (${capability.contextWindow ?? \"unknown\"} tokens, class '${capability.class}'): active tools reduced to [${this.deps.getActiveToolNames().join(\", \")}]; background lanes ${capability.backgroundLanesEnabled ? \"enabled\" : \"disabled\"}.`,\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Manual model choice is a deliberate human decision, not an auto-adoption flow — it is\n\t * ADVISORY ONLY: warn on evidence the model is a bad fit, but never block and never prompt\n\t * (print/RPC modes only ever see plain warning text through the existing `warning` event, the\n\t * same channel `_checkContextWindowUsageWarning` above uses). Two independent checks, both\n\t * best-effort:\n\t * - a recorded all-lanes-failed fitness probe on THIS host (see `isProbeAllFailed`);\n\t * - for an Ollama-served local model, weights that exceed ~90% of total system memory, which\n\t * is the exact failure the OOM report reproduced (llama-server needs the whole model resident).\n\t */\n\tprivate async _warnIfManualModelChoiceIsRisky(model: Model<Api>): Promise<void> {\n\t\tconst canonicalRef = `${model.provider}/${model.id}`;\n\t\ttry {\n\t\t\tconst fitness = FitnessStore.forAgentDir(this.deps.getAgentDir())\n\t\t\t\t.getForHost()\n\t\t\t\t.find((entry) => entry.model === canonicalRef);\n\t\t\tif (fitness && isProbeAllFailed(fitness.report)) {\n\t\t\t\tthis.deps.emit({\n\t\t\t\t\ttype: \"warning\",\n\t\t\t\t\tmessage: `${canonicalRef} failed its fitness probe on all surfaces on this host (probed ${fitness.at}) — it is likely to fail in production too. Proceeding because you set it manually.`,\n\t\t\t\t});\n\t\t\t}\n\t\t} catch {\n\t\t\t// advisory only; a lookup failure must never block a manual model choice\n\t\t}\n\n\t\tif (model.provider !== OLLAMA_PROVIDER) return;\n\t\ttry {\n\t\t\tconst serverUrl = this.deps.deriveOllamaServerUrl(model.baseUrl);\n\t\t\tconst installed = await this.deps.getLocalRuntime(serverUrl).list();\n\t\t\tconst entry = installed.find((candidate) => matchesInstalledLocalModel(model.id, candidate.name));\n\t\t\tif (!entry) return;\n\t\t\tconst memoryBudget = totalmem() * 0.9;\n\t\t\tif (entry.sizeBytes > memoryBudget) {\n\t\t\t\tconst sizeGb = (entry.sizeBytes / 1e9).toFixed(1);\n\t\t\t\tconst totalGb = (totalmem() / 1e9).toFixed(1);\n\t\t\t\tthis.deps.emit({\n\t\t\t\t\ttype: \"warning\",\n\t\t\t\t\tmessage: `${canonicalRef} is ~${sizeGb}GB, over 90% of this machine's ${totalGb}GB RAM — llama-server is likely to OOM when it runs. Proceeding because you set it manually.`,\n\t\t\t\t});\n\t\t\t}\n\t\t} catch {\n\t\t\t// advisory only; an unreachable local server must never block a manual model choice\n\t\t}\n\t}\n\n\t/**\n\t * Cycle to next/previous model.\n\t * Uses scoped models (from --models flag) if available, otherwise all available models.\n\t * @param direction - \"forward\" (default) or \"backward\"\n\t * @returns The new model info, or undefined if only one model available\n\t */\n\tasync cycleModel(direction: \"forward\" | \"backward\" = \"forward\"): Promise<ModelCycleResult | undefined> {\n\t\tif (this.deps.getScopedModels().length > 0) {\n\t\t\treturn this._cycleScopedModel(direction);\n\t\t}\n\t\treturn this._cycleAvailableModel(direction);\n\t}\n\n\tprivate async _cycleScopedModel(direction: \"forward\" | \"backward\"): Promise<ModelCycleResult | undefined> {\n\t\tconst scopedModels = this.deps\n\t\t\t.getScopedModels()\n\t\t\t.filter((scoped) => this.deps.getModelRegistry().hasConfiguredAuth(scoped.model));\n\t\tif (scopedModels.length <= 1) return undefined;\n\n\t\tconst currentModel = this.deps.getModel();\n\t\tlet currentIndex = scopedModels.findIndex((sm) => modelsAreEqual(sm.model, currentModel));\n\n\t\tif (currentIndex === -1) currentIndex = 0;\n\t\tconst len = scopedModels.length;\n\t\tconst nextIndex = direction === \"forward\" ? (currentIndex + 1) % len : (currentIndex - 1 + len) % len;\n\t\tconst next = scopedModels[nextIndex];\n\t\tconst thinkingLevel = this._getThinkingLevelForModelSwitch(next.thinkingLevel);\n\n\t\t// Apply model\n\t\tthis.deps.getAgent().state.model = next.model;\n\t\tthis.deps.getSessionManager().appendModelChange(next.model.provider, next.model.id);\n\t\tthis.deps.getSettingsManager().setDefaultModelAndProvider(next.model.provider, next.model.id);\n\n\t\t// Apply thinking level.\n\t\t// - Explicit scoped model thinking level overrides current session level\n\t\t// - Undefined scoped model thinking level inherits the current session preference\n\t\t// setThinkingLevel clamps to model capabilities.\n\t\tthis.setThinkingLevel(thinkingLevel);\n\t\tthis._resyncToolSurfaceForModel();\n\n\t\tawait this._emitModelSelect(next.model, currentModel, \"cycle\");\n\n\t\tthis.deps.checkContextWindowUsageWarning();\n\t\tawait this._warnIfManualModelChoiceIsRisky(next.model);\n\n\t\treturn { model: next.model, thinkingLevel: this.deps.getThinkingLevel(), isScoped: true };\n\t}\n\n\tprivate async _cycleAvailableModel(direction: \"forward\" | \"backward\"): Promise<ModelCycleResult | undefined> {\n\t\tconst availableModels = await this.deps.getModelRegistry().getAvailable();\n\t\tif (availableModels.length <= 1) return undefined;\n\n\t\tconst currentModel = this.deps.getModel();\n\t\tlet currentIndex = availableModels.findIndex((m) => modelsAreEqual(m, currentModel));\n\n\t\tif (currentIndex === -1) currentIndex = 0;\n\t\tconst len = availableModels.length;\n\t\tconst nextIndex = direction === \"forward\" ? (currentIndex + 1) % len : (currentIndex - 1 + len) % len;\n\t\tconst nextModel = availableModels[nextIndex];\n\n\t\tconst thinkingLevel = this._getThinkingLevelForModelSwitch();\n\t\tthis.deps.getAgent().state.model = nextModel;\n\t\tthis.deps.getSessionManager().appendModelChange(nextModel.provider, nextModel.id);\n\t\tthis.deps.getSettingsManager().setDefaultModelAndProvider(nextModel.provider, nextModel.id);\n\n\t\t// Re-clamp thinking level for new model's capabilities\n\t\tthis.setThinkingLevel(thinkingLevel);\n\t\tthis._resyncToolSurfaceForModel();\n\n\t\tawait this._emitModelSelect(nextModel, currentModel, \"cycle\");\n\n\t\tthis.deps.checkContextWindowUsageWarning();\n\t\tawait this._warnIfManualModelChoiceIsRisky(nextModel);\n\n\t\treturn { model: nextModel, thinkingLevel: this.deps.getThinkingLevel(), isScoped: false };\n\t}\n\n\t/**\n\t * Set thinking level.\n\t * Clamps to model capabilities based on available thinking levels.\n\t * Saves to session and settings only if the level actually changes.\n\t */\n\tsetThinkingLevel(level: ThinkingLevel, options: { persistSettings?: boolean } = {}): void {\n\t\tconst availableLevels = this.getAvailableThinkingLevels();\n\t\tconst effectiveLevel = availableLevels.includes(level) ? level : this._clampThinkingLevel(level, availableLevels);\n\n\t\t// Only persist if actually changing\n\t\tconst previousLevel = this.deps.getAgent().state.thinkingLevel;\n\t\tconst isChanging = effectiveLevel !== previousLevel;\n\t\tconst persistSettings = options.persistSettings ?? true;\n\n\t\tthis.deps.getAgent().state.thinkingLevel = effectiveLevel;\n\n\t\tif (isChanging) {\n\t\t\tthis.deps.refreshBaseSystemPrompt();\n\t\t\tthis.deps.getSessionManager().appendThinkingLevelChange(effectiveLevel);\n\t\t\tif (persistSettings && (this.supportsThinking() || effectiveLevel !== \"off\")) {\n\t\t\t\tthis.deps.getSettingsManager().setDefaultThinkingLevel(effectiveLevel);\n\t\t\t}\n\t\t\tthis.deps.emit({ type: \"thinking_level_changed\", level: effectiveLevel });\n\t\t\tvoid this.deps.getExtensionRunner().emit({\n\t\t\t\ttype: \"thinking_level_select\",\n\t\t\t\tlevel: effectiveLevel,\n\t\t\t\tpreviousLevel,\n\t\t\t});\n\t\t}\n\t}\n\n\t/**\n\t * Cycle to next thinking level.\n\t * @returns New level, or undefined if model doesn't support thinking\n\t */\n\tcycleThinkingLevel(): ThinkingLevel | undefined {\n\t\tif (!this.supportsThinking()) return undefined;\n\n\t\tconst levels = this.getAvailableThinkingLevels();\n\t\tconst currentIndex = levels.indexOf(this.deps.getThinkingLevel());\n\t\tconst nextIndex = (currentIndex + 1) % levels.length;\n\t\tconst nextLevel = levels[nextIndex];\n\n\t\tthis.setThinkingLevel(nextLevel);\n\t\treturn nextLevel;\n\t}\n\n\t/**\n\t * Get available thinking levels for current model.\n\t * The provider will clamp to what the specific model supports internally.\n\t */\n\tgetAvailableThinkingLevels(): ThinkingLevel[] {\n\t\tconst model = this.deps.getModel();\n\t\tif (!model) return THINKING_LEVELS;\n\t\treturn getSupportedThinkingLevels(model) as ThinkingLevel[];\n\t}\n\n\t/**\n\t * Check if current model supports thinking/reasoning.\n\t */\n\tsupportsThinking(): boolean {\n\t\treturn !!this.deps.getModel()?.reasoning;\n\t}\n\n\tprivate _getThinkingLevelForModelSwitch(explicitLevel?: ThinkingLevel): ThinkingLevel {\n\t\tif (explicitLevel !== undefined) {\n\t\t\treturn explicitLevel;\n\t\t}\n\t\tif (!this.supportsThinking()) {\n\t\t\treturn this.deps.getSettingsManager().getDefaultThinkingLevel() ?? DEFAULT_THINKING_LEVEL;\n\t\t}\n\t\treturn this.deps.getThinkingLevel();\n\t}\n\n\tprivate _clampThinkingLevel(level: ThinkingLevel, _availableLevels: ThinkingLevel[]): ThinkingLevel {\n\t\tconst model = this.deps.getModel();\n\t\treturn model ? (clampThinkingLevel(model, level) as ThinkingLevel) : \"off\";\n\t}\n}\n"]}
1
+ {"version":3,"file":"model-selection-controller.js","sourceRoot":"","sources":["../../src/core/model-selection-controller.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAInC,OAAO,EAAE,kBAAkB,EAAE,0BAA0B,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAEnG,OAAO,EAAE,sBAAsB,EAAE,MAAM,eAAe,CAAC;AAIvD,OAAO,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AACzD,OAAO,EAAE,eAAe,EAAE,MAAM,gCAAgC,CAAC;AAEjE,OAAO,EAAE,0BAA0B,EAAE,MAAM,uBAAuB,CAAC;AACnE,OAAO,EAAE,gBAAgB,EAAE,MAAM,6BAA6B,CAAC;AAG/D,iFAAiF;AACjF,MAAM,eAAe,GAAoB,CAAC,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAC;AA8BrF,MAAM,OAAO,wBAAwB;IACnB,IAAI,CAA+B;IAEpD,YAAY,IAAkC,EAAE;QAC/C,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IAAA,CACjB;IAEO,KAAK,CAAC,gBAAgB,CAC7B,SAAqB,EACrB,aAAqC,EACrC,MAAmC,EACnB;QAChB,IAAI,cAAc,CAAC,aAAa,EAAE,SAAS,CAAC;YAAE,OAAO;QACrD,MAAM,IAAI,CAAC,IAAI,CAAC,kBAAkB,EAAE,CAAC,IAAI,CAAC;YACzC,IAAI,EAAE,cAAc;YACpB,KAAK,EAAE,SAAS;YAChB,aAAa;YACb,MAAM;SACN,CAAC,CAAC;IAAA,CACH;IAED;;;;OAIG;IACH,KAAK,CAAC,QAAQ,CAAC,KAAiB,EAAE,OAAO,GAAkC,EAAE,EAAiB;QAC7F,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,gBAAgB,EAAE,CAAC,iBAAiB,CAAC,KAAK,CAAC,EAAE,CAAC;YAC5D,MAAM,IAAI,KAAK,CAAC,kBAAkB,KAAK,CAAC,QAAQ,IAAI,KAAK,CAAC,EAAE,EAAE,CAAC,CAAC;QACjE,CAAC;QAED,MAAM,eAAe,GAAG,OAAO,CAAC,eAAe,IAAI,IAAI,CAAC;QACxD,MAAM,aAAa,GAAG,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC;QAC3C,MAAM,aAAa,GAAG,IAAI,CAAC,+BAA+B,EAAE,CAAC;QAC7D,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC,KAAK,CAAC,KAAK,GAAG,KAAK,CAAC;QACzC,IAAI,CAAC,IAAI,CAAC,iBAAiB,EAAE,CAAC,iBAAiB,CAAC,KAAK,CAAC,QAAQ,EAAE,KAAK,CAAC,EAAE,CAAC,CAAC;QAC1E,IAAI,eAAe,EAAE,CAAC;YACrB,IAAI,CAAC,IAAI,CAAC,kBAAkB,EAAE,CAAC,0BAA0B,CAAC,KAAK,CAAC,QAAQ,EAAE,KAAK,CAAC,EAAE,CAAC,CAAC;QACrF,CAAC;QAED,uDAAuD;QACvD,IAAI,CAAC,gBAAgB,CAAC,aAAa,EAAE,EAAE,eAAe,EAAE,CAAC,CAAC;QAC1D,IAAI,CAAC,0BAA0B,EAAE,CAAC;QAElC,MAAM,IAAI,CAAC,gBAAgB,CAAC,KAAK,EAAE,aAAa,EAAE,KAAK,CAAC,CAAC;QACzD,IAAI,CAAC,IAAI,CAAC,8BAA8B,EAAE,CAAC;QAC3C,MAAM,IAAI,CAAC,+BAA+B,CAAC,KAAK,CAAC,CAAC;IAAA,CAClD;IAEO,0BAA0B,GAAS;QAC1C,6FAA6F;QAC7F,6FAA6F;QAC7F,6FAA6F;QAC7F,MAAM,wBAAwB,GAAG,IAAI,CAAC,IAAI,CAAC,2BAA2B,EAAE,CAAC;QACzE,IAAI,wBAAwB,EAAE,CAAC;YAC9B,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,kBAAkB,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YACxD,IAAI,CAAC,IAAI,CAAC,oBAAoB,CAAC,wBAAwB,CAAC,CAAC;YACzD,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,yBAAyB,EAAE,CAAC;YACzD,IAAI,UAAU,CAAC,KAAK,KAAK,MAAM,IAAI,IAAI,CAAC,IAAI,CAAC,kBAAkB,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,MAAM,EAAE,CAAC;gBACxF,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC;oBACd,IAAI,EAAE,SAAS;oBACf,OAAO,EAAE,iCAAiC,UAAU,CAAC,aAAa,IAAI,SAAS,mBAAmB,UAAU,CAAC,KAAK,gCAAgC,IAAI,CAAC,IAAI,CAAC,kBAAkB,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,uBAAuB,UAAU,CAAC,sBAAsB,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,UAAU,GAAG;iBAC/Q,CAAC,CAAC;YACJ,CAAC;QACF,CAAC;IAAA,CACD;IAED;;;;;;;;;OASG;IACK,KAAK,CAAC,+BAA+B,CAAC,KAAiB,EAAiB;QAC/E,MAAM,YAAY,GAAG,GAAG,KAAK,CAAC,QAAQ,IAAI,KAAK,CAAC,EAAE,EAAE,CAAC;QACrD,IAAI,CAAC;YACJ,MAAM,OAAO,GAAG,YAAY,CAAC,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC;iBAC/D,UAAU,EAAE;iBACZ,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,KAAK,KAAK,YAAY,CAAC,CAAC;YAChD,IAAI,OAAO,IAAI,gBAAgB,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;gBACjD,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC;oBACd,IAAI,EAAE,SAAS;oBACf,OAAO,EAAE,GAAG,YAAY,kEAAkE,OAAO,CAAC,EAAE,uFAAqF;iBACzL,CAAC,CAAC;YACJ,CAAC;QACF,CAAC;QAAC,MAAM,CAAC;YACR,yEAAyE;QAC1E,CAAC;QAED,IAAI,KAAK,CAAC,QAAQ,KAAK,eAAe;YAAE,OAAO;QAC/C,IAAI,CAAC;YACJ,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,qBAAqB,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;YACjE,MAAM,SAAS,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,eAAe,CAAC,SAAS,CAAC,CAAC,IAAI,EAAE,CAAC;YACpE,MAAM,KAAK,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,0BAA0B,CAAC,KAAK,CAAC,EAAE,EAAE,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC;YAClG,IAAI,CAAC,KAAK;gBAAE,OAAO;YACnB,MAAM,YAAY,GAAG,QAAQ,EAAE,GAAG,GAAG,CAAC;YACtC,IAAI,KAAK,CAAC,SAAS,GAAG,YAAY,EAAE,CAAC;gBACpC,MAAM,MAAM,GAAG,CAAC,KAAK,CAAC,SAAS,GAAG,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;gBAClD,MAAM,OAAO,GAAG,CAAC,QAAQ,EAAE,GAAG,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;gBAC9C,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC;oBACd,IAAI,EAAE,SAAS;oBACf,OAAO,EAAE,GAAG,YAAY,QAAQ,MAAM,kCAAkC,OAAO,gGAA8F;iBAC7K,CAAC,CAAC;YACJ,CAAC;QACF,CAAC;QAAC,MAAM,CAAC;YACR,oFAAoF;QACrF,CAAC;IAAA,CACD;IAED;;;;;OAKG;IACH,KAAK,CAAC,UAAU,CAAC,SAAS,GAA2B,SAAS,EAAyC;QACtG,IAAI,IAAI,CAAC,IAAI,CAAC,eAAe,EAAE,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC5C,OAAO,IAAI,CAAC,iBAAiB,CAAC,SAAS,CAAC,CAAC;QAC1C,CAAC;QACD,OAAO,IAAI,CAAC,oBAAoB,CAAC,SAAS,CAAC,CAAC;IAAA,CAC5C;IAEO,KAAK,CAAC,iBAAiB,CAAC,SAAiC,EAAyC;QACzG,MAAM,YAAY,GAAG,IAAI,CAAC,IAAI;aAC5B,eAAe,EAAE;aACjB,MAAM,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,gBAAgB,EAAE,CAAC,iBAAiB,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;QACnF,IAAI,YAAY,CAAC,MAAM,IAAI,CAAC;YAAE,OAAO,SAAS,CAAC;QAE/C,MAAM,YAAY,GAAG,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC;QAC1C,IAAI,YAAY,GAAG,YAAY,CAAC,SAAS,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,cAAc,CAAC,EAAE,CAAC,KAAK,EAAE,YAAY,CAAC,CAAC,CAAC;QAE1F,IAAI,YAAY,KAAK,CAAC,CAAC;YAAE,YAAY,GAAG,CAAC,CAAC;QAC1C,MAAM,GAAG,GAAG,YAAY,CAAC,MAAM,CAAC;QAChC,MAAM,SAAS,GAAG,SAAS,KAAK,SAAS,CAAC,CAAC,CAAC,CAAC,YAAY,GAAG,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,YAAY,GAAG,CAAC,GAAG,GAAG,CAAC,GAAG,GAAG,CAAC;QACtG,MAAM,IAAI,GAAG,YAAY,CAAC,SAAS,CAAC,CAAC;QACrC,MAAM,aAAa,GAAG,IAAI,CAAC,+BAA+B,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;QAE/E,cAAc;QACd,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC,KAAK,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC;QAC9C,IAAI,CAAC,IAAI,CAAC,iBAAiB,EAAE,CAAC,iBAAiB,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,EAAE,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;QACpF,IAAI,CAAC,IAAI,CAAC,kBAAkB,EAAE,CAAC,0BAA0B,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,EAAE,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;QAE9F,wBAAwB;QACxB,yEAAyE;QACzE,kFAAkF;QAClF,iDAAiD;QACjD,IAAI,CAAC,gBAAgB,CAAC,aAAa,CAAC,CAAC;QACrC,IAAI,CAAC,0BAA0B,EAAE,CAAC;QAElC,MAAM,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC,KAAK,EAAE,YAAY,EAAE,OAAO,CAAC,CAAC;QAE/D,IAAI,CAAC,IAAI,CAAC,8BAA8B,EAAE,CAAC;QAC3C,MAAM,IAAI,CAAC,+BAA+B,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAEvD,OAAO,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,aAAa,EAAE,IAAI,CAAC,IAAI,CAAC,gBAAgB,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IAAA,CAC1F;IAEO,KAAK,CAAC,oBAAoB,CAAC,SAAiC,EAAyC;QAC5G,MAAM,eAAe,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,gBAAgB,EAAE,CAAC,YAAY,EAAE,CAAC;QAC1E,IAAI,eAAe,CAAC,MAAM,IAAI,CAAC;YAAE,OAAO,SAAS,CAAC;QAElD,MAAM,YAAY,GAAG,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC;QAC1C,IAAI,YAAY,GAAG,eAAe,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,cAAc,CAAC,CAAC,EAAE,YAAY,CAAC,CAAC,CAAC;QAErF,IAAI,YAAY,KAAK,CAAC,CAAC;YAAE,YAAY,GAAG,CAAC,CAAC;QAC1C,MAAM,GAAG,GAAG,eAAe,CAAC,MAAM,CAAC;QACnC,MAAM,SAAS,GAAG,SAAS,KAAK,SAAS,CAAC,CAAC,CAAC,CAAC,YAAY,GAAG,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,YAAY,GAAG,CAAC,GAAG,GAAG,CAAC,GAAG,GAAG,CAAC;QACtG,MAAM,SAAS,GAAG,eAAe,CAAC,SAAS,CAAC,CAAC;QAE7C,MAAM,aAAa,GAAG,IAAI,CAAC,+BAA+B,EAAE,CAAC;QAC7D,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC,KAAK,CAAC,KAAK,GAAG,SAAS,CAAC;QAC7C,IAAI,CAAC,IAAI,CAAC,iBAAiB,EAAE,CAAC,iBAAiB,CAAC,SAAS,CAAC,QAAQ,EAAE,SAAS,CAAC,EAAE,CAAC,CAAC;QAClF,IAAI,CAAC,IAAI,CAAC,kBAAkB,EAAE,CAAC,0BAA0B,CAAC,SAAS,CAAC,QAAQ,EAAE,SAAS,CAAC,EAAE,CAAC,CAAC;QAE5F,uDAAuD;QACvD,IAAI,CAAC,gBAAgB,CAAC,aAAa,CAAC,CAAC;QACrC,IAAI,CAAC,0BAA0B,EAAE,CAAC;QAElC,MAAM,IAAI,CAAC,gBAAgB,CAAC,SAAS,EAAE,YAAY,EAAE,OAAO,CAAC,CAAC;QAE9D,IAAI,CAAC,IAAI,CAAC,8BAA8B,EAAE,CAAC;QAC3C,MAAM,IAAI,CAAC,+BAA+B,CAAC,SAAS,CAAC,CAAC;QAEtD,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,aAAa,EAAE,IAAI,CAAC,IAAI,CAAC,gBAAgB,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC;IAAA,CAC1F;IAED;;;;OAIG;IACH,gBAAgB,CAAC,KAAoB,EAAE,OAAO,GAAkC,EAAE,EAAQ;QACzF,MAAM,eAAe,GAAG,IAAI,CAAC,0BAA0B,EAAE,CAAC;QAC1D,MAAM,cAAc,GAAG,eAAe,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,mBAAmB,CAAC,KAAK,EAAE,eAAe,CAAC,CAAC;QAElH,oCAAoC;QACpC,MAAM,aAAa,GAAG,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC,KAAK,CAAC,aAAa,CAAC;QAC/D,MAAM,UAAU,GAAG,cAAc,KAAK,aAAa,CAAC;QACpD,MAAM,eAAe,GAAG,OAAO,CAAC,eAAe,IAAI,IAAI,CAAC;QAExD,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC,KAAK,CAAC,aAAa,GAAG,cAAc,CAAC;QAE1D,IAAI,UAAU,EAAE,CAAC;YAChB,IAAI,CAAC,IAAI,CAAC,uBAAuB,EAAE,CAAC;YACpC,IAAI,CAAC,IAAI,CAAC,iBAAiB,EAAE,CAAC,yBAAyB,CAAC,cAAc,CAAC,CAAC;YACxE,IAAI,eAAe,IAAI,CAAC,IAAI,CAAC,gBAAgB,EAAE,IAAI,cAAc,KAAK,KAAK,CAAC,EAAE,CAAC;gBAC9E,IAAI,CAAC,IAAI,CAAC,kBAAkB,EAAE,CAAC,uBAAuB,CAAC,cAAc,CAAC,CAAC;YACxE,CAAC;YACD,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,wBAAwB,EAAE,KAAK,EAAE,cAAc,EAAE,CAAC,CAAC;YAC1E,KAAK,IAAI,CAAC,IAAI,CAAC,kBAAkB,EAAE,CAAC,IAAI,CAAC;gBACxC,IAAI,EAAE,uBAAuB;gBAC7B,KAAK,EAAE,cAAc;gBACrB,aAAa;aACb,CAAC,CAAC;QACJ,CAAC;IAAA,CACD;IAED;;;OAGG;IACH,kBAAkB,GAA8B;QAC/C,IAAI,CAAC,IAAI,CAAC,gBAAgB,EAAE;YAAE,OAAO,SAAS,CAAC;QAE/C,MAAM,MAAM,GAAG,IAAI,CAAC,0BAA0B,EAAE,CAAC;QACjD,MAAM,YAAY,GAAG,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,gBAAgB,EAAE,CAAC,CAAC;QAClE,MAAM,SAAS,GAAG,CAAC,YAAY,GAAG,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,CAAC;QACrD,MAAM,SAAS,GAAG,MAAM,CAAC,SAAS,CAAC,CAAC;QAEpC,IAAI,CAAC,gBAAgB,CAAC,SAAS,CAAC,CAAC;QACjC,OAAO,SAAS,CAAC;IAAA,CACjB;IAED;;;OAGG;IACH,0BAA0B,GAAoB;QAC7C,MAAM,KAAK,GAAG,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC;QACnC,IAAI,CAAC,KAAK;YAAE,OAAO,eAAe,CAAC;QACnC,OAAO,0BAA0B,CAAC,KAAK,CAAoB,CAAC;IAAA,CAC5D;IAED;;OAEG;IACH,gBAAgB,GAAY;QAC3B,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,EAAE,SAAS,CAAC;IAAA,CACzC;IAEO,+BAA+B,CAAC,aAA6B,EAAiB;QACrF,IAAI,aAAa,KAAK,SAAS,EAAE,CAAC;YACjC,OAAO,aAAa,CAAC;QACtB,CAAC;QACD,IAAI,CAAC,IAAI,CAAC,gBAAgB,EAAE,EAAE,CAAC;YAC9B,OAAO,IAAI,CAAC,IAAI,CAAC,kBAAkB,EAAE,CAAC,uBAAuB,EAAE,IAAI,sBAAsB,CAAC;QAC3F,CAAC;QACD,OAAO,IAAI,CAAC,IAAI,CAAC,gBAAgB,EAAE,CAAC;IAAA,CACpC;IAEO,mBAAmB,CAAC,KAAoB,EAAE,gBAAiC,EAAiB;QACnG,MAAM,KAAK,GAAG,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC;QACnC,OAAO,KAAK,CAAC,CAAC,CAAE,kBAAkB,CAAC,KAAK,EAAE,KAAK,CAAmB,CAAC,CAAC,CAAC,KAAK,CAAC;IAAA,CAC3E;CACD","sourcesContent":["/**\n * User-facing model + thinking-level selection.\n *\n * Extracted verbatim from agent-session.ts (god-file decomposition). Owns the manual/explicit model\n * switch and cycle paths and the thinking-level set/cycle/clamp — the deliberate human choices, as\n * opposed to the per-turn auto-routing owned by ModelRouterController. It mutates `agent.state.model`\n * / `agent.state.thinkingLevel` through deps and re-derives the capability-scoped tool surface, but the\n * session keeps the `model`/`thinkingLevel` GETTERS so the drive loop's reads are untouched. Model\n * choice is advisory-only: it warns on a bad-fit fitness probe or an oversized local model, never blocks.\n */\n\nimport { totalmem } from \"node:os\";\nimport type { Agent, ThinkingLevel } from \"@caupulican/pi-agent-core\";\nimport type { SessionManager } from \"@caupulican/pi-agent-core/node\";\nimport type { Api, Model } from \"@caupulican/pi-ai\";\nimport { clampThinkingLevel, getSupportedThinkingLevels, modelsAreEqual } from \"@caupulican/pi-ai\";\nimport type { AgentSessionEvent, ModelCycleResult } from \"./agent-session.ts\";\nimport { DEFAULT_THINKING_LEVEL } from \"./defaults.ts\";\nimport type { ExtensionRunner } from \"./extensions/index.ts\";\nimport type { ModelCapabilityProfile } from \"./model-capability.ts\";\nimport type { ModelRegistry } from \"./model-registry.ts\";\nimport { FitnessStore } from \"./models/fitness-store.ts\";\nimport { OLLAMA_PROVIDER } from \"./models/local-registration.ts\";\nimport type { OllamaRuntime } from \"./models/local-runtime.ts\";\nimport { matchesInstalledLocalModel } from \"./models/model-ref.ts\";\nimport { isProbeAllFailed } from \"./research/model-fitness.ts\";\nimport type { SettingsManager } from \"./settings-manager.ts\";\n\n/** Standard thinking levels (fallback set when the current model is unknown). */\nconst THINKING_LEVELS: ThinkingLevel[] = [\"off\", \"minimal\", \"low\", \"medium\", \"high\"];\n\nexport interface ModelSelectionControllerDeps {\n\tgetAgent(): Agent;\n\tgetModel(): Model<Api> | undefined;\n\tgetThinkingLevel(): ThinkingLevel;\n\tgetModelRegistry(): ModelRegistry;\n\tgetSessionManager(): SessionManager;\n\tgetSettingsManager(): SettingsManager;\n\tgetExtensionRunner(): ExtensionRunner;\n\tgetAgentDir(): string;\n\t/** Scoped models (--models flag), used by the cycle path. */\n\tgetScopedModels(): Array<{ model: Model<Api>; thinkingLevel?: ThinkingLevel }>;\n\t/** The user-requested active tool set, re-applied when the model's capability class changes. */\n\tgetRequestedActiveToolNames(): string[] | undefined;\n\tgetActiveToolNames(): string[];\n\tsetActiveToolsByName(toolNames: string[]): void;\n\tgetModelCapabilityProfile(): ModelCapabilityProfile;\n\t/** Rebuild the base prompt after a live orchestration-level change without altering requested tools. */\n\trefreshBaseSystemPrompt(): void;\n\t/** Session event emit (warnings + thinking_level_changed; model_select goes via the extension runner). */\n\temit(event: AgentSessionEvent): void;\n\t/** Context-window-usage warning check (session-owned, compaction-adjacent). */\n\tcheckContextWindowUsageWarning(): void;\n\t/** Resolve the Ollama server URL for a model base URL (local-runtime controller). */\n\tderiveOllamaServerUrl(modelBaseUrl: string): string;\n\t/** Local Ollama runtime for the given server URL (used for the oversized-model risk check). */\n\tgetLocalRuntime(serverUrl: string): OllamaRuntime;\n}\n\nexport class ModelSelectionController {\n\tprivate readonly deps: ModelSelectionControllerDeps;\n\n\tconstructor(deps: ModelSelectionControllerDeps) {\n\t\tthis.deps = deps;\n\t}\n\n\tprivate async _emitModelSelect(\n\t\tnextModel: Model<Api>,\n\t\tpreviousModel: Model<Api> | undefined,\n\t\tsource: \"set\" | \"cycle\" | \"restore\",\n\t): Promise<void> {\n\t\tif (modelsAreEqual(previousModel, nextModel)) return;\n\t\tawait this.deps.getExtensionRunner().emit({\n\t\t\ttype: \"model_select\",\n\t\t\tmodel: nextModel,\n\t\t\tpreviousModel,\n\t\t\tsource,\n\t\t});\n\t}\n\n\t/**\n\t * Set model directly.\n\t * Validates that auth is configured, saves to session and settings.\n\t * @throws Error if no auth is configured for the model\n\t */\n\tasync setModel(model: Model<Api>, options: { persistSettings?: boolean } = {}): Promise<void> {\n\t\tif (!this.deps.getModelRegistry().hasConfiguredAuth(model)) {\n\t\t\tthrow new Error(`No API key for ${model.provider}/${model.id}`);\n\t\t}\n\n\t\tconst persistSettings = options.persistSettings ?? true;\n\t\tconst previousModel = this.deps.getModel();\n\t\tconst thinkingLevel = this._getThinkingLevelForModelSwitch();\n\t\tthis.deps.getAgent().state.model = model;\n\t\tthis.deps.getSessionManager().appendModelChange(model.provider, model.id);\n\t\tif (persistSettings) {\n\t\t\tthis.deps.getSettingsManager().setDefaultModelAndProvider(model.provider, model.id);\n\t\t}\n\n\t\t// Re-clamp thinking level for new model's capabilities\n\t\tthis.setThinkingLevel(thinkingLevel, { persistSettings });\n\t\tthis._resyncToolSurfaceForModel();\n\n\t\tawait this._emitModelSelect(model, previousModel, \"set\");\n\t\tthis.deps.checkContextWindowUsageWarning();\n\t\tawait this._warnIfManualModelChoiceIsRisky(model);\n\t}\n\n\tprivate _resyncToolSurfaceForModel(): void {\n\t\t// Re-derive the model-capability tool surface for the new model (restores the full requested\n\t\t// set when moving small -> large, reduces it when moving large -> small). Every switch path,\n\t\t// including model cycling and RPC commands that delegate to it, must pass through this seam.\n\t\tconst requestedActiveToolNames = this.deps.getRequestedActiveToolNames();\n\t\tif (requestedActiveToolNames) {\n\t\t\tconst before = this.deps.getActiveToolNames().join(\",\");\n\t\t\tthis.deps.setActiveToolsByName(requestedActiveToolNames);\n\t\t\tconst capability = this.deps.getModelCapabilityProfile();\n\t\t\tif (capability.class !== \"full\" && this.deps.getActiveToolNames().join(\",\") !== before) {\n\t\t\t\tthis.deps.emit({\n\t\t\t\t\ttype: \"warning\",\n\t\t\t\t\tmessage: `Small-context model detected (${capability.contextWindow ?? \"unknown\"} tokens, class '${capability.class}'): active tools reduced to [${this.deps.getActiveToolNames().join(\", \")}]; background lanes ${capability.backgroundLanesEnabled ? \"enabled\" : \"disabled\"}.`,\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Manual model choice is a deliberate human decision, not an auto-adoption flow — it is\n\t * ADVISORY ONLY: warn on evidence the model is a bad fit, but never block and never prompt\n\t * (print/RPC modes only ever see plain warning text through the existing `warning` event, the\n\t * same channel `_checkContextWindowUsageWarning` above uses). Two independent checks, both\n\t * best-effort:\n\t * - a recorded all-lanes-failed fitness probe on THIS host (see `isProbeAllFailed`);\n\t * - for an Ollama-served local model, weights that exceed ~90% of total system memory, which\n\t * is the exact failure the OOM report reproduced (llama-server needs the whole model resident).\n\t */\n\tprivate async _warnIfManualModelChoiceIsRisky(model: Model<Api>): Promise<void> {\n\t\tconst canonicalRef = `${model.provider}/${model.id}`;\n\t\ttry {\n\t\t\tconst fitness = FitnessStore.forAgentDir(this.deps.getAgentDir())\n\t\t\t\t.getForHost()\n\t\t\t\t.find((entry) => entry.model === canonicalRef);\n\t\t\tif (fitness && isProbeAllFailed(fitness.report)) {\n\t\t\t\tthis.deps.emit({\n\t\t\t\t\ttype: \"warning\",\n\t\t\t\t\tmessage: `${canonicalRef} failed its fitness probe on all surfaces on this host (probed ${fitness.at}) — it is likely to fail in production too. Proceeding because you set it manually.`,\n\t\t\t\t});\n\t\t\t}\n\t\t} catch {\n\t\t\t// advisory only; a lookup failure must never block a manual model choice\n\t\t}\n\n\t\tif (model.provider !== OLLAMA_PROVIDER) return;\n\t\ttry {\n\t\t\tconst serverUrl = this.deps.deriveOllamaServerUrl(model.baseUrl);\n\t\t\tconst installed = await this.deps.getLocalRuntime(serverUrl).list();\n\t\t\tconst entry = installed.find((candidate) => matchesInstalledLocalModel(model.id, candidate.name));\n\t\t\tif (!entry) return;\n\t\t\tconst memoryBudget = totalmem() * 0.9;\n\t\t\tif (entry.sizeBytes > memoryBudget) {\n\t\t\t\tconst sizeGb = (entry.sizeBytes / 1e9).toFixed(1);\n\t\t\t\tconst totalGb = (totalmem() / 1e9).toFixed(1);\n\t\t\t\tthis.deps.emit({\n\t\t\t\t\ttype: \"warning\",\n\t\t\t\t\tmessage: `${canonicalRef} is ~${sizeGb}GB, over 90% of this machine's ${totalGb}GB RAM — llama-server is likely to OOM when it runs. Proceeding because you set it manually.`,\n\t\t\t\t});\n\t\t\t}\n\t\t} catch {\n\t\t\t// advisory only; an unreachable local server must never block a manual model choice\n\t\t}\n\t}\n\n\t/**\n\t * Cycle to next/previous model.\n\t * Uses scoped models (from --models flag) if available, otherwise all available models.\n\t * @param direction - \"forward\" (default) or \"backward\"\n\t * @returns The new model info, or undefined if only one model available\n\t */\n\tasync cycleModel(direction: \"forward\" | \"backward\" = \"forward\"): Promise<ModelCycleResult | undefined> {\n\t\tif (this.deps.getScopedModels().length > 0) {\n\t\t\treturn this._cycleScopedModel(direction);\n\t\t}\n\t\treturn this._cycleAvailableModel(direction);\n\t}\n\n\tprivate async _cycleScopedModel(direction: \"forward\" | \"backward\"): Promise<ModelCycleResult | undefined> {\n\t\tconst scopedModels = this.deps\n\t\t\t.getScopedModels()\n\t\t\t.filter((scoped) => this.deps.getModelRegistry().hasConfiguredAuth(scoped.model));\n\t\tif (scopedModels.length <= 1) return undefined;\n\n\t\tconst currentModel = this.deps.getModel();\n\t\tlet currentIndex = scopedModels.findIndex((sm) => modelsAreEqual(sm.model, currentModel));\n\n\t\tif (currentIndex === -1) currentIndex = 0;\n\t\tconst len = scopedModels.length;\n\t\tconst nextIndex = direction === \"forward\" ? (currentIndex + 1) % len : (currentIndex - 1 + len) % len;\n\t\tconst next = scopedModels[nextIndex];\n\t\tconst thinkingLevel = this._getThinkingLevelForModelSwitch(next.thinkingLevel);\n\n\t\t// Apply model\n\t\tthis.deps.getAgent().state.model = next.model;\n\t\tthis.deps.getSessionManager().appendModelChange(next.model.provider, next.model.id);\n\t\tthis.deps.getSettingsManager().setDefaultModelAndProvider(next.model.provider, next.model.id);\n\n\t\t// Apply thinking level.\n\t\t// - Explicit scoped model thinking level overrides current session level\n\t\t// - Undefined scoped model thinking level inherits the current session preference\n\t\t// setThinkingLevel clamps to model capabilities.\n\t\tthis.setThinkingLevel(thinkingLevel);\n\t\tthis._resyncToolSurfaceForModel();\n\n\t\tawait this._emitModelSelect(next.model, currentModel, \"cycle\");\n\n\t\tthis.deps.checkContextWindowUsageWarning();\n\t\tawait this._warnIfManualModelChoiceIsRisky(next.model);\n\n\t\treturn { model: next.model, thinkingLevel: this.deps.getThinkingLevel(), isScoped: true };\n\t}\n\n\tprivate async _cycleAvailableModel(direction: \"forward\" | \"backward\"): Promise<ModelCycleResult | undefined> {\n\t\tconst availableModels = await this.deps.getModelRegistry().getAvailable();\n\t\tif (availableModels.length <= 1) return undefined;\n\n\t\tconst currentModel = this.deps.getModel();\n\t\tlet currentIndex = availableModels.findIndex((m) => modelsAreEqual(m, currentModel));\n\n\t\tif (currentIndex === -1) currentIndex = 0;\n\t\tconst len = availableModels.length;\n\t\tconst nextIndex = direction === \"forward\" ? (currentIndex + 1) % len : (currentIndex - 1 + len) % len;\n\t\tconst nextModel = availableModels[nextIndex];\n\n\t\tconst thinkingLevel = this._getThinkingLevelForModelSwitch();\n\t\tthis.deps.getAgent().state.model = nextModel;\n\t\tthis.deps.getSessionManager().appendModelChange(nextModel.provider, nextModel.id);\n\t\tthis.deps.getSettingsManager().setDefaultModelAndProvider(nextModel.provider, nextModel.id);\n\n\t\t// Re-clamp thinking level for new model's capabilities\n\t\tthis.setThinkingLevel(thinkingLevel);\n\t\tthis._resyncToolSurfaceForModel();\n\n\t\tawait this._emitModelSelect(nextModel, currentModel, \"cycle\");\n\n\t\tthis.deps.checkContextWindowUsageWarning();\n\t\tawait this._warnIfManualModelChoiceIsRisky(nextModel);\n\n\t\treturn { model: nextModel, thinkingLevel: this.deps.getThinkingLevel(), isScoped: false };\n\t}\n\n\t/**\n\t * Set thinking level.\n\t * Clamps to model capabilities based on available thinking levels.\n\t * Saves to session and settings only if the level actually changes.\n\t */\n\tsetThinkingLevel(level: ThinkingLevel, options: { persistSettings?: boolean } = {}): void {\n\t\tconst availableLevels = this.getAvailableThinkingLevels();\n\t\tconst effectiveLevel = availableLevels.includes(level) ? level : this._clampThinkingLevel(level, availableLevels);\n\n\t\t// Only persist if actually changing\n\t\tconst previousLevel = this.deps.getAgent().state.thinkingLevel;\n\t\tconst isChanging = effectiveLevel !== previousLevel;\n\t\tconst persistSettings = options.persistSettings ?? true;\n\n\t\tthis.deps.getAgent().state.thinkingLevel = effectiveLevel;\n\n\t\tif (isChanging) {\n\t\t\tthis.deps.refreshBaseSystemPrompt();\n\t\t\tthis.deps.getSessionManager().appendThinkingLevelChange(effectiveLevel);\n\t\t\tif (persistSettings && (this.supportsThinking() || effectiveLevel !== \"off\")) {\n\t\t\t\tthis.deps.getSettingsManager().setDefaultThinkingLevel(effectiveLevel);\n\t\t\t}\n\t\t\tthis.deps.emit({ type: \"thinking_level_changed\", level: effectiveLevel });\n\t\t\tvoid this.deps.getExtensionRunner().emit({\n\t\t\t\ttype: \"thinking_level_select\",\n\t\t\t\tlevel: effectiveLevel,\n\t\t\t\tpreviousLevel,\n\t\t\t});\n\t\t}\n\t}\n\n\t/**\n\t * Cycle to next thinking level.\n\t * @returns New level, or undefined if model doesn't support thinking\n\t */\n\tcycleThinkingLevel(): ThinkingLevel | undefined {\n\t\tif (!this.supportsThinking()) return undefined;\n\n\t\tconst levels = this.getAvailableThinkingLevels();\n\t\tconst currentIndex = levels.indexOf(this.deps.getThinkingLevel());\n\t\tconst nextIndex = (currentIndex + 1) % levels.length;\n\t\tconst nextLevel = levels[nextIndex];\n\n\t\tthis.setThinkingLevel(nextLevel);\n\t\treturn nextLevel;\n\t}\n\n\t/**\n\t * Get available thinking levels for current model.\n\t * The provider will clamp to what the specific model supports internally.\n\t */\n\tgetAvailableThinkingLevels(): ThinkingLevel[] {\n\t\tconst model = this.deps.getModel();\n\t\tif (!model) return THINKING_LEVELS;\n\t\treturn getSupportedThinkingLevels(model) as ThinkingLevel[];\n\t}\n\n\t/**\n\t * Check if current model supports thinking/reasoning.\n\t */\n\tsupportsThinking(): boolean {\n\t\treturn !!this.deps.getModel()?.reasoning;\n\t}\n\n\tprivate _getThinkingLevelForModelSwitch(explicitLevel?: ThinkingLevel): ThinkingLevel {\n\t\tif (explicitLevel !== undefined) {\n\t\t\treturn explicitLevel;\n\t\t}\n\t\tif (!this.supportsThinking()) {\n\t\t\treturn this.deps.getSettingsManager().getDefaultThinkingLevel() ?? DEFAULT_THINKING_LEVEL;\n\t\t}\n\t\treturn this.deps.getThinkingLevel();\n\t}\n\n\tprivate _clampThinkingLevel(level: ThinkingLevel, _availableLevels: ThinkingLevel[]): ThinkingLevel {\n\t\tconst model = this.deps.getModel();\n\t\treturn model ? (clampThinkingLevel(model, level) as ThinkingLevel) : \"off\";\n\t}\n}\n"]}
@@ -0,0 +1,23 @@
1
+ import { type Api, type Model, type SimpleStreamOptions } from "@caupulican/pi-ai";
2
+ import type { ModelAdaptationProfile } from "./models/adaptation-store.ts";
3
+ export declare const MODEL_TOOL_PROTOCOL_VERSION = 1;
4
+ export type ModelToolProtocolReasonCode = "settings_disabled" | "settings_enabled" | "model_enabled" | "probe_calibrated" | "probe_calibration_missing" | "probe_calibration_failed" | "probe_calibration_invalid" | "native_default";
5
+ export interface ModelToolProtocolResolution {
6
+ protocol: SimpleStreamOptions["textToolCallProtocol"];
7
+ reasonCode: ModelToolProtocolReasonCode;
8
+ variantsTried?: readonly string[];
9
+ }
10
+ /**
11
+ * Resolve the effective tool-call transport for any model and execution lane.
12
+ *
13
+ * Explicit settings win, followed by model metadata and then graded probe evidence. A probe-driven
14
+ * text protocol is usable only with a current, recognized calibration; otherwise the model stays on
15
+ * native calls for this turn. Callers use the reason code for diagnostics, but never reimplement the
16
+ * precedence or silently substitute the default dialect.
17
+ */
18
+ export declare function resolveModelToolProtocol(args: {
19
+ model: Pick<Model<Api>, "textToolCallProtocol">;
20
+ settingsOverride?: boolean;
21
+ adaptation: Pick<ModelAdaptationProfile, "protocol" | "toolProbe">;
22
+ }): ModelToolProtocolResolution;
23
+ //# sourceMappingURL=model-tool-protocol.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"model-tool-protocol.d.ts","sourceRoot":"","sources":["../../src/core/model-tool-protocol.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,GAAG,EAA6B,KAAK,KAAK,EAAE,KAAK,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAC9G,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,8BAA8B,CAAC;AAE3E,eAAO,MAAM,2BAA2B,IAAI,CAAC;AAE7C,MAAM,MAAM,2BAA2B,GACpC,mBAAmB,GACnB,kBAAkB,GAClB,eAAe,GACf,kBAAkB,GAClB,2BAA2B,GAC3B,0BAA0B,GAC1B,2BAA2B,GAC3B,gBAAgB,CAAC;AAEpB,MAAM,WAAW,2BAA2B;IAC3C,QAAQ,EAAE,mBAAmB,CAAC,sBAAsB,CAAC,CAAC;IACtD,UAAU,EAAE,2BAA2B,CAAC;IACxC,aAAa,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CAClC;AAED;;;;;;;GAOG;AACH,wBAAgB,wBAAwB,CAAC,IAAI,EAAE;IAC9C,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,sBAAsB,CAAC,CAAC;IAChD,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,UAAU,EAAE,IAAI,CAAC,sBAAsB,EAAE,UAAU,GAAG,WAAW,CAAC,CAAC;CACnE,GAAG,2BAA2B,CAgC9B","sourcesContent":["import { type Api, isTextToolProtocolVariant, type Model, type SimpleStreamOptions } from \"@caupulican/pi-ai\";\nimport type { ModelAdaptationProfile } from \"./models/adaptation-store.ts\";\n\nexport const MODEL_TOOL_PROTOCOL_VERSION = 1;\n\nexport type ModelToolProtocolReasonCode =\n\t| \"settings_disabled\"\n\t| \"settings_enabled\"\n\t| \"model_enabled\"\n\t| \"probe_calibrated\"\n\t| \"probe_calibration_missing\"\n\t| \"probe_calibration_failed\"\n\t| \"probe_calibration_invalid\"\n\t| \"native_default\";\n\nexport interface ModelToolProtocolResolution {\n\tprotocol: SimpleStreamOptions[\"textToolCallProtocol\"];\n\treasonCode: ModelToolProtocolReasonCode;\n\tvariantsTried?: readonly string[];\n}\n\n/**\n * Resolve the effective tool-call transport for any model and execution lane.\n *\n * Explicit settings win, followed by model metadata and then graded probe evidence. A probe-driven\n * text protocol is usable only with a current, recognized calibration; otherwise the model stays on\n * native calls for this turn. Callers use the reason code for diagnostics, but never reimplement the\n * precedence or silently substitute the default dialect.\n */\nexport function resolveModelToolProtocol(args: {\n\tmodel: Pick<Model<Api>, \"textToolCallProtocol\">;\n\tsettingsOverride?: boolean;\n\tadaptation: Pick<ModelAdaptationProfile, \"protocol\" | \"toolProbe\">;\n}): ModelToolProtocolResolution {\n\tif (args.settingsOverride === false) {\n\t\treturn { protocol: undefined, reasonCode: \"settings_disabled\" };\n\t}\n\tif (args.settingsOverride === true) {\n\t\treturn { protocol: true, reasonCode: \"settings_enabled\" };\n\t}\n\tif (args.model.textToolCallProtocol === true) {\n\t\treturn { protocol: true, reasonCode: \"model_enabled\" };\n\t}\n\tif (args.adaptation.toolProbe?.status !== \"text-protocol\") {\n\t\treturn { protocol: undefined, reasonCode: \"native_default\" };\n\t}\n\n\tconst calibration = args.adaptation.protocol;\n\tif (!calibration) {\n\t\treturn { protocol: undefined, reasonCode: \"probe_calibration_missing\" };\n\t}\n\tif (calibration.status === \"failed\") {\n\t\treturn {\n\t\t\tprotocol: undefined,\n\t\t\treasonCode: \"probe_calibration_failed\",\n\t\t\tvariantsTried: calibration.variantsTried,\n\t\t};\n\t}\n\tif (calibration.version !== MODEL_TOOL_PROTOCOL_VERSION || !isTextToolProtocolVariant(calibration.variant)) {\n\t\treturn { protocol: undefined, reasonCode: \"probe_calibration_invalid\" };\n\t}\n\treturn {\n\t\tprotocol: { variant: calibration.variant },\n\t\treasonCode: \"probe_calibrated\",\n\t};\n}\n"]}
@@ -0,0 +1,43 @@
1
+ import { isTextToolProtocolVariant } from "@caupulican/pi-ai";
2
+ export const MODEL_TOOL_PROTOCOL_VERSION = 1;
3
+ /**
4
+ * Resolve the effective tool-call transport for any model and execution lane.
5
+ *
6
+ * Explicit settings win, followed by model metadata and then graded probe evidence. A probe-driven
7
+ * text protocol is usable only with a current, recognized calibration; otherwise the model stays on
8
+ * native calls for this turn. Callers use the reason code for diagnostics, but never reimplement the
9
+ * precedence or silently substitute the default dialect.
10
+ */
11
+ export function resolveModelToolProtocol(args) {
12
+ if (args.settingsOverride === false) {
13
+ return { protocol: undefined, reasonCode: "settings_disabled" };
14
+ }
15
+ if (args.settingsOverride === true) {
16
+ return { protocol: true, reasonCode: "settings_enabled" };
17
+ }
18
+ if (args.model.textToolCallProtocol === true) {
19
+ return { protocol: true, reasonCode: "model_enabled" };
20
+ }
21
+ if (args.adaptation.toolProbe?.status !== "text-protocol") {
22
+ return { protocol: undefined, reasonCode: "native_default" };
23
+ }
24
+ const calibration = args.adaptation.protocol;
25
+ if (!calibration) {
26
+ return { protocol: undefined, reasonCode: "probe_calibration_missing" };
27
+ }
28
+ if (calibration.status === "failed") {
29
+ return {
30
+ protocol: undefined,
31
+ reasonCode: "probe_calibration_failed",
32
+ variantsTried: calibration.variantsTried,
33
+ };
34
+ }
35
+ if (calibration.version !== MODEL_TOOL_PROTOCOL_VERSION || !isTextToolProtocolVariant(calibration.variant)) {
36
+ return { protocol: undefined, reasonCode: "probe_calibration_invalid" };
37
+ }
38
+ return {
39
+ protocol: { variant: calibration.variant },
40
+ reasonCode: "probe_calibrated",
41
+ };
42
+ }
43
+ //# sourceMappingURL=model-tool-protocol.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"model-tool-protocol.js","sourceRoot":"","sources":["../../src/core/model-tool-protocol.ts"],"names":[],"mappings":"AAAA,OAAO,EAAY,yBAAyB,EAAwC,MAAM,mBAAmB,CAAC;AAG9G,MAAM,CAAC,MAAM,2BAA2B,GAAG,CAAC,CAAC;AAkB7C;;;;;;;GAOG;AACH,MAAM,UAAU,wBAAwB,CAAC,IAIxC,EAA+B;IAC/B,IAAI,IAAI,CAAC,gBAAgB,KAAK,KAAK,EAAE,CAAC;QACrC,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,UAAU,EAAE,mBAAmB,EAAE,CAAC;IACjE,CAAC;IACD,IAAI,IAAI,CAAC,gBAAgB,KAAK,IAAI,EAAE,CAAC;QACpC,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,UAAU,EAAE,kBAAkB,EAAE,CAAC;IAC3D,CAAC;IACD,IAAI,IAAI,CAAC,KAAK,CAAC,oBAAoB,KAAK,IAAI,EAAE,CAAC;QAC9C,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,UAAU,EAAE,eAAe,EAAE,CAAC;IACxD,CAAC;IACD,IAAI,IAAI,CAAC,UAAU,CAAC,SAAS,EAAE,MAAM,KAAK,eAAe,EAAE,CAAC;QAC3D,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,UAAU,EAAE,gBAAgB,EAAE,CAAC;IAC9D,CAAC;IAED,MAAM,WAAW,GAAG,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC;IAC7C,IAAI,CAAC,WAAW,EAAE,CAAC;QAClB,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,UAAU,EAAE,2BAA2B,EAAE,CAAC;IACzE,CAAC;IACD,IAAI,WAAW,CAAC,MAAM,KAAK,QAAQ,EAAE,CAAC;QACrC,OAAO;YACN,QAAQ,EAAE,SAAS;YACnB,UAAU,EAAE,0BAA0B;YACtC,aAAa,EAAE,WAAW,CAAC,aAAa;SACxC,CAAC;IACH,CAAC;IACD,IAAI,WAAW,CAAC,OAAO,KAAK,2BAA2B,IAAI,CAAC,yBAAyB,CAAC,WAAW,CAAC,OAAO,CAAC,EAAE,CAAC;QAC5G,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,UAAU,EAAE,2BAA2B,EAAE,CAAC;IACzE,CAAC;IACD,OAAO;QACN,QAAQ,EAAE,EAAE,OAAO,EAAE,WAAW,CAAC,OAAO,EAAE;QAC1C,UAAU,EAAE,kBAAkB;KAC9B,CAAC;AAAA,CACF","sourcesContent":["import { type Api, isTextToolProtocolVariant, type Model, type SimpleStreamOptions } from \"@caupulican/pi-ai\";\nimport type { ModelAdaptationProfile } from \"./models/adaptation-store.ts\";\n\nexport const MODEL_TOOL_PROTOCOL_VERSION = 1;\n\nexport type ModelToolProtocolReasonCode =\n\t| \"settings_disabled\"\n\t| \"settings_enabled\"\n\t| \"model_enabled\"\n\t| \"probe_calibrated\"\n\t| \"probe_calibration_missing\"\n\t| \"probe_calibration_failed\"\n\t| \"probe_calibration_invalid\"\n\t| \"native_default\";\n\nexport interface ModelToolProtocolResolution {\n\tprotocol: SimpleStreamOptions[\"textToolCallProtocol\"];\n\treasonCode: ModelToolProtocolReasonCode;\n\tvariantsTried?: readonly string[];\n}\n\n/**\n * Resolve the effective tool-call transport for any model and execution lane.\n *\n * Explicit settings win, followed by model metadata and then graded probe evidence. A probe-driven\n * text protocol is usable only with a current, recognized calibration; otherwise the model stays on\n * native calls for this turn. Callers use the reason code for diagnostics, but never reimplement the\n * precedence or silently substitute the default dialect.\n */\nexport function resolveModelToolProtocol(args: {\n\tmodel: Pick<Model<Api>, \"textToolCallProtocol\">;\n\tsettingsOverride?: boolean;\n\tadaptation: Pick<ModelAdaptationProfile, \"protocol\" | \"toolProbe\">;\n}): ModelToolProtocolResolution {\n\tif (args.settingsOverride === false) {\n\t\treturn { protocol: undefined, reasonCode: \"settings_disabled\" };\n\t}\n\tif (args.settingsOverride === true) {\n\t\treturn { protocol: true, reasonCode: \"settings_enabled\" };\n\t}\n\tif (args.model.textToolCallProtocol === true) {\n\t\treturn { protocol: true, reasonCode: \"model_enabled\" };\n\t}\n\tif (args.adaptation.toolProbe?.status !== \"text-protocol\") {\n\t\treturn { protocol: undefined, reasonCode: \"native_default\" };\n\t}\n\n\tconst calibration = args.adaptation.protocol;\n\tif (!calibration) {\n\t\treturn { protocol: undefined, reasonCode: \"probe_calibration_missing\" };\n\t}\n\tif (calibration.status === \"failed\") {\n\t\treturn {\n\t\t\tprotocol: undefined,\n\t\t\treasonCode: \"probe_calibration_failed\",\n\t\t\tvariantsTried: calibration.variantsTried,\n\t\t};\n\t}\n\tif (calibration.version !== MODEL_TOOL_PROTOCOL_VERSION || !isTextToolProtocolVariant(calibration.variant)) {\n\t\treturn { protocol: undefined, reasonCode: \"probe_calibration_invalid\" };\n\t}\n\treturn {\n\t\tprotocol: { variant: calibration.variant },\n\t\treasonCode: \"probe_calibrated\",\n\t};\n}\n"]}
@@ -1,4 +1,4 @@
1
- import { type HostFingerprint } from "./fitness-store.ts";
1
+ import { type HostFingerprint } from "./host-state-store.ts";
2
2
  import { type ModelPerfProfile, type ModelPerfSample } from "./perf-profile.ts";
3
3
  export interface ModelAdaptationRule {
4
4
  mode: string;
@@ -46,17 +46,22 @@ export interface StoredModelAdaptation {
46
46
  host: HostFingerprint;
47
47
  }
48
48
  export declare class ModelAdaptationStore {
49
- private readonly filePath;
50
- private readonly fingerprint;
49
+ private readonly storage;
51
50
  constructor(filePath: string, options?: {
52
51
  fingerprint?: () => HostFingerprint;
52
+ readOnly?: boolean;
53
53
  });
54
54
  static forAgentDir(agentDir: string, options?: {
55
55
  fingerprint?: () => HostFingerprint;
56
+ readOnly?: boolean;
56
57
  }): ModelAdaptationStore;
57
- private load;
58
- private write;
58
+ /**
59
+ * Load-mutate-write under a single exclusive lock so two concurrent stores (e.g. two sessions
60
+ * sharing an agentDir) can't both read the old file and clobber each other's write.
61
+ */
59
62
  private store;
63
+ /** Keep the complete profile read-modify-write transaction under the host-state lock. */
64
+ private mutateProfile;
60
65
  /** Persist the profile for a model on the CURRENT host. Best-effort, returns the entry. */
61
66
  save(model: string, profile: ModelAdaptationProfile, at?: string): StoredModelAdaptation;
62
67
  /** Profile for a model on the current host; prunes retired rules before returning. */
@@ -1 +1 @@
1
- {"version":3,"file":"adaptation-store.d.ts","sourceRoot":"","sources":["../../../src/core/models/adaptation-store.ts"],"names":[],"mappings":"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","sourcesContent":["import { existsSync, mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { dirname, join } from \"node:path\";\nimport { currentHostFingerprint, type HostFingerprint } from \"./fitness-store.ts\";\nimport {\n\thasUsableModelPerfSample,\n\tisModelPerfProfile,\n\ttype ModelPerfProfile,\n\ttype ModelPerfSample,\n\tupdateModelPerfProfile,\n} from \"./perf-profile.ts\";\n\nconst STORE_VERSION = 1;\nconst MAX_RULES_PER_MODEL = 5;\nconst RETIRE_AFTER_MS = 30 * 24 * 60 * 60 * 1000;\n\nexport interface ModelAdaptationRule {\n\tmode: string;\n\ttext: string;\n\taddedAt: string;\n\tlastFiredAt: string;\n}\n\nexport type ModelProtocolCalibration =\n\t| {\n\t\t\tversion: number;\n\t\t\tstatus?: \"calibrated\";\n\t\t\tvariant: string;\n\t\t\tcalibratedAt: string;\n\t }\n\t| {\n\t\t\tversion: number;\n\t\t\tstatus: \"failed\";\n\t\t\tattemptedAt: string;\n\t\t\tvariantsTried: string[];\n\t };\n\nexport type ModelToolProbeVerdict = \"native\" | \"text-protocol\" | \"none\";\nexport type NativeToolProbeGrade = \"task\" | \"echo-only\" | \"absent\";\n\nexport interface ModelToolProbe {\n\tversion: number;\n\tstatus: ModelToolProbeVerdict;\n\tprobedAt: string;\n\tvariant?: string;\n\tnativeGrade?: NativeToolProbeGrade;\n\tdiagnostic?: string;\n}\n\nexport interface ModelTeachStats {\n\ttaught: number;\n\trecurrenceBefore: number;\n\trecurrenceAfter: number;\n}\n\nexport interface ModelAdaptationProfile {\n\trules: ModelAdaptationRule[];\n\tprotocol?: ModelProtocolCalibration;\n\ttoolProbe?: ModelToolProbe;\n\tperf?: ModelPerfProfile;\n\tteachStats: Record<string, ModelTeachStats>;\n}\n\nexport interface StoredModelAdaptation {\n\tmodel: string;\n\tprofile: ModelAdaptationProfile;\n\tat: string;\n\thost: HostFingerprint;\n}\n\ninterface AdaptationStoreFile {\n\tversion: 1;\n\t/** hostId -> modelRef -> latest stored adaptation profile. */\n\thosts: Record<string, Record<string, StoredModelAdaptation>>;\n}\n\nfunction emptyProfile(): ModelAdaptationProfile {\n\treturn { rules: [], teachStats: {} };\n}\n\nfunction normalizeProfile(profile: Partial<ModelAdaptationProfile> | undefined): ModelAdaptationProfile {\n\treturn {\n\t\trules: Array.isArray(profile?.rules) ? profile.rules.filter(isRule) : [],\n\t\t...(isProtocol(profile?.protocol) && { protocol: profile.protocol }),\n\t\t...(isToolProbe(profile?.toolProbe) && { toolProbe: profile.toolProbe }),\n\t\t...(isModelPerfProfile(profile?.perf) && { perf: profile.perf }),\n\t\tteachStats: isRecord(profile?.teachStats) ? filterTeachStats(profile.teachStats) : {},\n\t};\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n\treturn typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction isRule(value: unknown): value is ModelAdaptationRule {\n\treturn (\n\t\tisRecord(value) &&\n\t\ttypeof value.mode === \"string\" &&\n\t\ttypeof value.text === \"string\" &&\n\t\ttypeof value.addedAt === \"string\" &&\n\t\ttypeof value.lastFiredAt === \"string\"\n\t);\n}\n\nfunction isProtocol(value: unknown): value is ModelProtocolCalibration {\n\tif (!isRecord(value) || typeof value.version !== \"number\") return false;\n\tif (value.status === \"failed\") {\n\t\treturn (\n\t\t\ttypeof value.attemptedAt === \"string\" &&\n\t\t\tArray.isArray(value.variantsTried) &&\n\t\t\tvalue.variantsTried.every((variant) => typeof variant === \"string\")\n\t\t);\n\t}\n\treturn (\n\t\t(value.status === undefined || value.status === \"calibrated\") &&\n\t\ttypeof value.variant === \"string\" &&\n\t\ttypeof value.calibratedAt === \"string\"\n\t);\n}\n\nfunction isToolProbe(value: unknown): value is ModelToolProbe {\n\treturn (\n\t\tisRecord(value) &&\n\t\ttypeof value.version === \"number\" &&\n\t\t(value.status === \"native\" || value.status === \"text-protocol\" || value.status === \"none\") &&\n\t\ttypeof value.probedAt === \"string\" &&\n\t\t(value.variant === undefined || typeof value.variant === \"string\") &&\n\t\t(value.nativeGrade === undefined ||\n\t\t\tvalue.nativeGrade === \"task\" ||\n\t\t\tvalue.nativeGrade === \"echo-only\" ||\n\t\t\tvalue.nativeGrade === \"absent\") &&\n\t\t(value.diagnostic === undefined || typeof value.diagnostic === \"string\")\n\t);\n}\n\nfunction isTeachStats(value: unknown): value is ModelTeachStats {\n\treturn (\n\t\tisRecord(value) &&\n\t\ttypeof value.taught === \"number\" &&\n\t\ttypeof value.recurrenceBefore === \"number\" &&\n\t\ttypeof value.recurrenceAfter === \"number\"\n\t);\n}\n\nfunction filterTeachStats(value: Record<string, unknown>): Record<string, ModelTeachStats> {\n\treturn Object.fromEntries(\n\t\tObject.entries(value).filter((entry): entry is [string, ModelTeachStats] => isTeachStats(entry[1])),\n\t);\n}\n\nfunction ruleRecency(rule: ModelAdaptationRule): number {\n\tconst lastFired = Date.parse(rule.lastFiredAt);\n\tif (Number.isFinite(lastFired)) return lastFired;\n\tconst added = Date.parse(rule.addedAt);\n\treturn Number.isFinite(added) ? added : 0;\n}\n\nfunction pruneRetiredRules(rules: readonly ModelAdaptationRule[], now: Date): ModelAdaptationRule[] {\n\tconst cutoff = now.getTime() - RETIRE_AFTER_MS;\n\treturn rules.filter((rule) => ruleRecency(rule) >= cutoff);\n}\n\nfunction enforceRuleCap(rules: readonly ModelAdaptationRule[]): ModelAdaptationRule[] {\n\tif (rules.length <= MAX_RULES_PER_MODEL) return [...rules];\n\treturn [...rules].sort((a, b) => ruleRecency(b) - ruleRecency(a)).slice(0, MAX_RULES_PER_MODEL);\n}\n\nfunction mergeRule(rules: readonly ModelAdaptationRule[], rule: ModelAdaptationRule): ModelAdaptationRule[] {\n\tconst withoutSameMode = rules.filter((existing) => existing.mode !== rule.mode);\n\treturn enforceRuleCap([...withoutSameMode, rule]);\n}\n\nexport class ModelAdaptationStore {\n\tprivate readonly filePath: string;\n\tprivate readonly fingerprint: () => HostFingerprint;\n\n\tconstructor(filePath: string, options?: { fingerprint?: () => HostFingerprint }) {\n\t\tthis.filePath = filePath;\n\t\tthis.fingerprint = options?.fingerprint ?? currentHostFingerprint;\n\t}\n\n\tstatic forAgentDir(agentDir: string, options?: { fingerprint?: () => HostFingerprint }): ModelAdaptationStore {\n\t\treturn new ModelAdaptationStore(join(agentDir, \"state\", \"model-adaptation.json\"), options);\n\t}\n\n\tprivate load(): AdaptationStoreFile {\n\t\ttry {\n\t\t\tif (!existsSync(this.filePath)) return { version: STORE_VERSION, hosts: {} };\n\t\t\tconst parsed = JSON.parse(readFileSync(this.filePath, \"utf-8\")) as AdaptationStoreFile;\n\t\t\tif (parsed && parsed.version === STORE_VERSION && parsed.hosts && typeof parsed.hosts === \"object\") {\n\t\t\t\treturn parsed;\n\t\t\t}\n\t\t} catch {\n\t\t\t// Unreadable/corrupt store: start fresh in memory; the next save rewrites the file.\n\t\t}\n\t\treturn { version: STORE_VERSION, hosts: {} };\n\t}\n\n\tprivate write(file: AdaptationStoreFile): void {\n\t\tmkdirSync(dirname(this.filePath), { recursive: true });\n\t\twriteFileSync(this.filePath, `${JSON.stringify(file, null, \"\\t\")}\\n`, \"utf-8\");\n\t}\n\n\tprivate store(model: string, profile: ModelAdaptationProfile, at: string): StoredModelAdaptation {\n\t\tconst host = this.fingerprint();\n\t\tconst entry: StoredModelAdaptation = { model, profile: normalizeProfile(profile), at, host };\n\t\tconst file = this.load();\n\t\tfile.hosts[host.id] = { ...(file.hosts[host.id] ?? {}), [model]: entry };\n\t\tthis.write(file);\n\t\treturn entry;\n\t}\n\n\t/** Persist the profile for a model on the CURRENT host. Best-effort, returns the entry. */\n\tsave(model: string, profile: ModelAdaptationProfile, at?: string): StoredModelAdaptation {\n\t\treturn this.store(model, profile, at ?? new Date().toISOString());\n\t}\n\n\t/** Profile for a model on the current host; prunes retired rules before returning. */\n\tget(model: string, now: Date = new Date()): ModelAdaptationProfile {\n\t\tconst host = this.fingerprint();\n\t\tconst file = this.load();\n\t\tconst entry = file.hosts[host.id]?.[model];\n\t\tif (!entry) return emptyProfile();\n\t\tconst profile = normalizeProfile(entry.profile);\n\t\tconst prunedRules = pruneRetiredRules(profile.rules, now);\n\t\tif (prunedRules.length !== profile.rules.length) {\n\t\t\treturn this.store(model, { ...profile, rules: prunedRules }, now.toISOString()).profile;\n\t\t}\n\t\treturn profile;\n\t}\n\n\t/** Add or replace one standing rule, enforcing the per-model cap. */\n\taddRule(\n\t\tmodel: string,\n\t\trule: { mode: string; text: string; addedAt?: string; lastFiredAt?: string },\n\t\tnow = new Date(),\n\t): StoredModelAdaptation {\n\t\tconst profile = this.get(model, now);\n\t\tconst at = now.toISOString();\n\t\tconst nextRule: ModelAdaptationRule = {\n\t\t\tmode: rule.mode,\n\t\t\ttext: rule.text,\n\t\t\taddedAt: rule.addedAt ?? at,\n\t\t\tlastFiredAt: rule.lastFiredAt ?? at,\n\t\t};\n\t\treturn this.store(model, { ...profile, rules: mergeRule(profile.rules, nextRule) }, at);\n\t}\n\n\tremoveRule(model: string, mode: string, at = new Date()): boolean {\n\t\tconst profile = this.get(model, at);\n\t\tconst rules = profile.rules.filter((rule) => rule.mode !== mode);\n\t\tif (rules.length === profile.rules.length) return false;\n\t\tthis.store(model, { ...profile, rules }, at.toISOString());\n\t\treturn true;\n\t}\n\n\t/** Update last-fired recency for an existing rule. No-op when absent. */\n\tmarkRuleFired(model: string, mode: string, at = new Date()): StoredModelAdaptation | undefined {\n\t\tconst profile = this.get(model, at);\n\t\tconst rules = profile.rules.map((rule) =>\n\t\t\trule.mode === mode ? { ...rule, lastFiredAt: at.toISOString() } : rule,\n\t\t);\n\t\tif (rules.every((rule, index) => rule === profile.rules[index])) return undefined;\n\t\treturn this.store(model, { ...profile, rules }, at.toISOString());\n\t}\n\n\tsetProtocol(model: string, protocol: ModelProtocolCalibration, at?: string): StoredModelAdaptation {\n\t\tconst now = at ?? (protocol.status === \"failed\" ? protocol.attemptedAt : protocol.calibratedAt);\n\t\tconst profile = this.get(model, new Date(now));\n\t\treturn this.store(model, { ...profile, protocol }, now);\n\t}\n\n\tsetToolProbe(model: string, toolProbe: ModelToolProbe, at?: string): StoredModelAdaptation {\n\t\tconst now = at ?? toolProbe.probedAt;\n\t\tconst profile = this.get(model, new Date(now));\n\t\treturn this.store(model, { ...profile, toolProbe }, now);\n\t}\n\n\trecordPerfSample(model: string, sample: ModelPerfSample, at?: string): StoredModelAdaptation | undefined {\n\t\tif (!hasUsableModelPerfSample(sample)) return undefined;\n\t\tconst now = at ?? sample.at ?? new Date().toISOString();\n\t\tconst profile = this.get(model, new Date(now));\n\t\tconst perf = updateModelPerfProfile(profile.perf, sample, now);\n\t\tif (!perf) return undefined;\n\t\treturn this.store(model, { ...profile, perf }, now);\n\t}\n\n\tremoveProtocol(model: string, at = new Date()): boolean {\n\t\tconst profile = this.get(model, at);\n\t\tif (!profile.protocol) return false;\n\t\tconst { protocol: _protocol, ...rest } = profile;\n\t\tthis.store(model, rest, at.toISOString());\n\t\treturn true;\n\t}\n\n\tsetTeachStats(model: string, mode: string, stats: ModelTeachStats, at?: string): StoredModelAdaptation {\n\t\tconst now = at ?? new Date().toISOString();\n\t\tconst profile = this.get(model, new Date(now));\n\t\treturn this.store(model, { ...profile, teachStats: { ...profile.teachStats, [mode]: stats } }, now);\n\t}\n\n\t/** Profiles for the current host (default) or an explicit host id. */\n\tgetForHost(hostId?: string): StoredModelAdaptation[] {\n\t\tconst file = this.load();\n\t\treturn Object.values(file.hosts[hostId ?? this.fingerprint().id] ?? {}).map((entry) => ({\n\t\t\t...entry,\n\t\t\tprofile: normalizeProfile(entry.profile),\n\t\t}));\n\t}\n\n\t/** Every stored profile across all hosts (for cross-machine comparisons). */\n\tgetAll(): StoredModelAdaptation[] {\n\t\tconst file = this.load();\n\t\treturn Object.values(file.hosts).flatMap((models) =>\n\t\t\tObject.values(models).map((entry) => ({ ...entry, profile: normalizeProfile(entry.profile) })),\n\t\t);\n\t}\n}\n"]}
1
+ {"version":3,"file":"adaptation-store.d.ts","sourceRoot":"","sources":["../../../src/core/models/adaptation-store.ts"],"names":[],"mappings":"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","sourcesContent":["import { stateFile } from \"../agent-paths.ts\";\nimport { isWorkerSession } from \"../session-role.ts\";\nimport { isRecordObject } from \"../util/value-guards.ts\";\nimport { type HostFingerprint, HostStateStore, isHostFingerprint } from \"./host-state-store.ts\";\nimport {\n\thasUsableModelPerfSample,\n\tisModelPerfProfile,\n\ttype ModelPerfProfile,\n\ttype ModelPerfSample,\n\tupdateModelPerfProfile,\n} from \"./perf-profile.ts\";\n\nconst STORE_VERSION = 1;\nconst MAX_RULES_PER_MODEL = 5;\nconst RETIRE_AFTER_MS = 30 * 24 * 60 * 60 * 1000;\n\nexport interface ModelAdaptationRule {\n\tmode: string;\n\ttext: string;\n\taddedAt: string;\n\tlastFiredAt: string;\n}\n\nexport type ModelProtocolCalibration =\n\t| {\n\t\t\tversion: number;\n\t\t\tstatus?: \"calibrated\";\n\t\t\tvariant: string;\n\t\t\tcalibratedAt: string;\n\t }\n\t| {\n\t\t\tversion: number;\n\t\t\tstatus: \"failed\";\n\t\t\tattemptedAt: string;\n\t\t\tvariantsTried: string[];\n\t };\n\nexport type ModelToolProbeVerdict = \"native\" | \"text-protocol\" | \"none\";\nexport type NativeToolProbeGrade = \"task\" | \"echo-only\" | \"absent\";\n\nexport interface ModelToolProbe {\n\tversion: number;\n\tstatus: ModelToolProbeVerdict;\n\tprobedAt: string;\n\tvariant?: string;\n\tnativeGrade?: NativeToolProbeGrade;\n\tdiagnostic?: string;\n}\n\nexport interface ModelTeachStats {\n\ttaught: number;\n\trecurrenceBefore: number;\n\trecurrenceAfter: number;\n}\n\nexport interface ModelAdaptationProfile {\n\trules: ModelAdaptationRule[];\n\tprotocol?: ModelProtocolCalibration;\n\ttoolProbe?: ModelToolProbe;\n\tperf?: ModelPerfProfile;\n\tteachStats: Record<string, ModelTeachStats>;\n}\n\nexport interface StoredModelAdaptation {\n\tmodel: string;\n\tprofile: ModelAdaptationProfile;\n\tat: string;\n\thost: HostFingerprint;\n}\n\ninterface ProfileMutation {\n\tprofile: ModelAdaptationProfile;\n\tentry?: StoredModelAdaptation;\n\tapplied: boolean;\n}\n\nfunction emptyProfile(): ModelAdaptationProfile {\n\treturn { rules: [], teachStats: {} };\n}\n\nfunction normalizeProfile(profile: Partial<ModelAdaptationProfile> | undefined): ModelAdaptationProfile {\n\treturn {\n\t\trules: Array.isArray(profile?.rules) ? profile.rules.filter(isRule) : [],\n\t\t...(isProtocol(profile?.protocol) && { protocol: profile.protocol }),\n\t\t...(isToolProbe(profile?.toolProbe) && { toolProbe: profile.toolProbe }),\n\t\t...(isModelPerfProfile(profile?.perf) && { perf: profile.perf }),\n\t\tteachStats: isRecordObject(profile?.teachStats) ? filterTeachStats(profile.teachStats) : {},\n\t};\n}\n\nfunction isRule(value: unknown): value is ModelAdaptationRule {\n\treturn (\n\t\tisRecordObject(value) &&\n\t\ttypeof value.mode === \"string\" &&\n\t\ttypeof value.text === \"string\" &&\n\t\ttypeof value.addedAt === \"string\" &&\n\t\ttypeof value.lastFiredAt === \"string\"\n\t);\n}\n\nfunction isProtocol(value: unknown): value is ModelProtocolCalibration {\n\tif (!isRecordObject(value) || typeof value.version !== \"number\") return false;\n\tif (value.status === \"failed\") {\n\t\treturn (\n\t\t\ttypeof value.attemptedAt === \"string\" &&\n\t\t\tArray.isArray(value.variantsTried) &&\n\t\t\tvalue.variantsTried.every((variant) => typeof variant === \"string\")\n\t\t);\n\t}\n\treturn (\n\t\t(value.status === undefined || value.status === \"calibrated\") &&\n\t\ttypeof value.variant === \"string\" &&\n\t\ttypeof value.calibratedAt === \"string\"\n\t);\n}\n\nfunction isToolProbe(value: unknown): value is ModelToolProbe {\n\treturn (\n\t\tisRecordObject(value) &&\n\t\ttypeof value.version === \"number\" &&\n\t\t(value.status === \"native\" || value.status === \"text-protocol\" || value.status === \"none\") &&\n\t\ttypeof value.probedAt === \"string\" &&\n\t\t(value.variant === undefined || typeof value.variant === \"string\") &&\n\t\t(value.nativeGrade === undefined ||\n\t\t\tvalue.nativeGrade === \"task\" ||\n\t\t\tvalue.nativeGrade === \"echo-only\" ||\n\t\t\tvalue.nativeGrade === \"absent\") &&\n\t\t(value.diagnostic === undefined || typeof value.diagnostic === \"string\")\n\t);\n}\n\nfunction isTeachStats(value: unknown): value is ModelTeachStats {\n\treturn (\n\t\tisRecordObject(value) &&\n\t\ttypeof value.taught === \"number\" &&\n\t\ttypeof value.recurrenceBefore === \"number\" &&\n\t\ttypeof value.recurrenceAfter === \"number\"\n\t);\n}\n\nfunction filterTeachStats(value: Record<string, unknown>): Record<string, ModelTeachStats> {\n\treturn Object.fromEntries(\n\t\tObject.entries(value).filter((entry): entry is [string, ModelTeachStats] => isTeachStats(entry[1])),\n\t);\n}\n\nfunction ruleRecency(rule: ModelAdaptationRule): number {\n\tconst lastFired = Date.parse(rule.lastFiredAt);\n\tif (Number.isFinite(lastFired)) return lastFired;\n\tconst added = Date.parse(rule.addedAt);\n\treturn Number.isFinite(added) ? added : 0;\n}\n\n/**\n * Minimum post-rule recurrences before efficacy can retire a rule early — a single relapse is\n * noise, not proof the rule stopped working.\n */\nconst MIN_RECURRENCE_AFTER_FOR_EFFICACY = 2;\n\n/**\n * Efficacy-based early retirement. `agent-session.ts`'s `_handleModelAdaptationTelemetry` bumps\n * `recurrenceBefore` each time a failure mode recurs BEFORE a standing rule exists for it (the\n * baseline that earned the rule), then switches to bumping `recurrenceAfter` each time the SAME mode\n * recurs AFTER the rule fired. So a rule that is actually working keeps `recurrenceAfter` low relative\n * to its own pre-rule baseline; a rule that has caught up to (or exceeded) that baseline has\n * demonstrably stopped reducing recurrence and should retire before the time-based outer bound.\n */\nfunction isRuleEffective(stats: ModelTeachStats | undefined): boolean {\n\tif (!stats || stats.recurrenceAfter < MIN_RECURRENCE_AFTER_FOR_EFFICACY) return true; // not enough post-rule evidence yet\n\treturn stats.recurrenceAfter < stats.recurrenceBefore;\n}\n\n/**\n * Prune rules past the time-based outer bound (`RETIRE_AFTER_MS`, always enforced) OR past efficacy\n * (a rule that stopped reducing recurrence retires early; a rule that keeps helping persists until\n * the outer bound).\n */\nfunction pruneRetiredRules(\n\trules: readonly ModelAdaptationRule[],\n\tteachStats: Record<string, ModelTeachStats>,\n\tnow: Date,\n): ModelAdaptationRule[] {\n\tconst cutoff = now.getTime() - RETIRE_AFTER_MS;\n\treturn rules.filter((rule) => ruleRecency(rule) >= cutoff && isRuleEffective(teachStats[rule.mode]));\n}\n\nfunction enforceRuleCap(rules: readonly ModelAdaptationRule[]): ModelAdaptationRule[] {\n\tif (rules.length <= MAX_RULES_PER_MODEL) return [...rules];\n\treturn [...rules].sort((a, b) => ruleRecency(b) - ruleRecency(a)).slice(0, MAX_RULES_PER_MODEL);\n}\n\nfunction mergeRule(rules: readonly ModelAdaptationRule[], rule: ModelAdaptationRule): ModelAdaptationRule[] {\n\tconst withoutSameMode = rules.filter((existing) => existing.mode !== rule.mode);\n\treturn enforceRuleCap([...withoutSameMode, rule]);\n}\n\nfunction parseAdaptationHost(value: unknown, hostId: string): Record<string, StoredModelAdaptation> | undefined {\n\tif (!isRecordObject(value)) return undefined;\n\tconst profiles: Record<string, StoredModelAdaptation> = {};\n\tfor (const [model, candidate] of Object.entries(value)) {\n\t\tif (\n\t\t\t!isRecordObject(candidate) ||\n\t\t\tcandidate.model !== model ||\n\t\t\ttypeof candidate.at !== \"string\" ||\n\t\t\t!isHostFingerprint(candidate.host) ||\n\t\t\tcandidate.host.id !== hostId ||\n\t\t\t!isRecordObject(candidate.profile)\n\t\t)\n\t\t\tcontinue;\n\t\tprofiles[model] = {\n\t\t\tmodel,\n\t\t\tprofile: normalizeProfile(candidate.profile),\n\t\t\tat: candidate.at,\n\t\t\thost: candidate.host,\n\t\t};\n\t}\n\treturn profiles;\n}\n\nexport class ModelAdaptationStore {\n\tprivate readonly storage: HostStateStore<Record<string, StoredModelAdaptation>>;\n\n\tconstructor(filePath: string, options?: { fingerprint?: () => HostFingerprint; readOnly?: boolean }) {\n\t\tthis.storage = new HostStateStore({\n\t\t\tfilePath,\n\t\t\tversion: STORE_VERSION,\n\t\t\tfingerprint: options?.fingerprint,\n\t\t\treadOnly: options?.readOnly ?? isWorkerSession(),\n\t\t\tparseHost: parseAdaptationHost,\n\t\t});\n\t}\n\n\tstatic forAgentDir(\n\t\tagentDir: string,\n\t\toptions?: { fingerprint?: () => HostFingerprint; readOnly?: boolean },\n\t): ModelAdaptationStore {\n\t\treturn new ModelAdaptationStore(stateFile(agentDir, \"model-adaptation.json\"), options);\n\t}\n\n\t/**\n\t * Load-mutate-write under a single exclusive lock so two concurrent stores (e.g. two sessions\n\t * sharing an agentDir) can't both read the old file and clobber each other's write.\n\t */\n\tprivate store(model: string, profile: ModelAdaptationProfile, at: string): StoredModelAdaptation {\n\t\treturn this.storage.mutateCurrentHost(\n\t\t\t() => ({}),\n\t\t\t(profiles, host) => {\n\t\t\t\tconst entry: StoredModelAdaptation = { model, profile: normalizeProfile(profile), at, host };\n\t\t\t\tprofiles[model] = entry;\n\t\t\t\treturn { result: entry, changed: true };\n\t\t\t},\n\t\t);\n\t}\n\n\t/** Keep the complete profile read-modify-write transaction under the host-state lock. */\n\tprivate mutateProfile(\n\t\tmodel: string,\n\t\tnow: Date,\n\t\tmutate: (profile: ModelAdaptationProfile) => ModelAdaptationProfile | undefined,\n\t\tstoredAt = now.toISOString(),\n\t): ProfileMutation {\n\t\treturn this.storage.mutateCurrentHost<ProfileMutation>(\n\t\t\t() => ({}),\n\t\t\t(profiles, host) => {\n\t\t\t\tconst current = normalizeProfile(profiles[model]?.profile);\n\t\t\t\tconst activeRules = pruneRetiredRules(current.rules, current.teachStats, now);\n\t\t\t\tconst pruned = activeRules.length !== current.rules.length;\n\t\t\t\tconst active = pruned ? { ...current, rules: activeRules } : current;\n\t\t\t\tconst requested = mutate(active);\n\t\t\t\tconst applied = requested !== undefined;\n\t\t\t\tif (!applied && !pruned) {\n\t\t\t\t\treturn { result: { profile: active, applied: false }, changed: false };\n\t\t\t\t}\n\t\t\t\tconst profile = normalizeProfile(requested ?? active);\n\t\t\t\tconst entry: StoredModelAdaptation = { model, profile, at: storedAt, host };\n\t\t\t\tprofiles[model] = entry;\n\t\t\t\treturn { result: { profile, entry, applied }, changed: true };\n\t\t\t},\n\t\t);\n\t}\n\n\t/** Persist the profile for a model on the CURRENT host. Best-effort, returns the entry. */\n\tsave(model: string, profile: ModelAdaptationProfile, at?: string): StoredModelAdaptation {\n\t\treturn this.store(model, profile, at ?? new Date().toISOString());\n\t}\n\n\t/** Profile for a model on the current host; prunes retired rules before returning. */\n\tget(model: string, now: Date = new Date()): ModelAdaptationProfile {\n\t\tconst entry = this.storage.getHost()?.[model];\n\t\tif (!entry) return emptyProfile();\n\t\tconst profile = normalizeProfile(entry.profile);\n\t\tconst prunedRules = pruneRetiredRules(profile.rules, profile.teachStats, now);\n\t\tif (prunedRules.length !== profile.rules.length) {\n\t\t\treturn this.mutateProfile(model, now, () => undefined).profile;\n\t\t}\n\t\treturn profile;\n\t}\n\n\t/** Add or replace one standing rule, enforcing the per-model cap. */\n\taddRule(\n\t\tmodel: string,\n\t\trule: { mode: string; text: string; addedAt?: string; lastFiredAt?: string },\n\t\tnow = new Date(),\n\t): StoredModelAdaptation {\n\t\tconst at = now.toISOString();\n\t\tconst nextRule: ModelAdaptationRule = {\n\t\t\tmode: rule.mode,\n\t\t\ttext: rule.text,\n\t\t\taddedAt: rule.addedAt ?? at,\n\t\t\tlastFiredAt: rule.lastFiredAt ?? at,\n\t\t};\n\t\tconst result = this.mutateProfile(model, now, (profile) => ({\n\t\t\t...profile,\n\t\t\trules: mergeRule(profile.rules, nextRule),\n\t\t}));\n\t\treturn result.entry!;\n\t}\n\n\tremoveRule(model: string, mode: string, at = new Date()): boolean {\n\t\treturn this.mutateProfile(model, at, (profile) => {\n\t\t\tconst rules = profile.rules.filter((rule) => rule.mode !== mode);\n\t\t\treturn rules.length === profile.rules.length ? undefined : { ...profile, rules };\n\t\t}).applied;\n\t}\n\n\t/** Update last-fired recency for an existing rule. No-op when absent. */\n\tmarkRuleFired(model: string, mode: string, at = new Date()): StoredModelAdaptation | undefined {\n\t\tconst result = this.mutateProfile(model, at, (profile) => {\n\t\t\tconst rules = profile.rules.map((rule) =>\n\t\t\t\trule.mode === mode ? { ...rule, lastFiredAt: at.toISOString() } : rule,\n\t\t\t);\n\t\t\treturn rules.every((rule, index) => rule === profile.rules[index]) ? undefined : { ...profile, rules };\n\t\t});\n\t\treturn result.applied ? result.entry : undefined;\n\t}\n\n\tsetProtocol(model: string, protocol: ModelProtocolCalibration, at?: string): StoredModelAdaptation {\n\t\tconst now = at ?? (protocol.status === \"failed\" ? protocol.attemptedAt : protocol.calibratedAt);\n\t\treturn this.mutateProfile(model, new Date(now), (profile) => ({ ...profile, protocol }), now).entry!;\n\t}\n\n\tsetToolProbe(model: string, toolProbe: ModelToolProbe, at?: string): StoredModelAdaptation {\n\t\tconst now = at ?? toolProbe.probedAt;\n\t\treturn this.mutateProfile(model, new Date(now), (profile) => ({ ...profile, toolProbe }), now).entry!;\n\t}\n\n\trecordPerfSample(model: string, sample: ModelPerfSample, at?: string): StoredModelAdaptation | undefined {\n\t\tif (!hasUsableModelPerfSample(sample)) return undefined;\n\t\tconst now = at ?? sample.at ?? new Date().toISOString();\n\t\tconst result = this.mutateProfile(\n\t\t\tmodel,\n\t\t\tnew Date(now),\n\t\t\t(profile) => {\n\t\t\t\tconst perf = updateModelPerfProfile(profile.perf, sample, now);\n\t\t\t\treturn perf ? { ...profile, perf } : undefined;\n\t\t\t},\n\t\t\tnow,\n\t\t);\n\t\treturn result.applied ? result.entry : undefined;\n\t}\n\n\tremoveProtocol(model: string, at = new Date()): boolean {\n\t\treturn this.mutateProfile(model, at, (profile) => {\n\t\t\tif (!profile.protocol) return undefined;\n\t\t\tconst { protocol: _protocol, ...rest } = profile;\n\t\t\treturn rest;\n\t\t}).applied;\n\t}\n\n\tsetTeachStats(model: string, mode: string, stats: ModelTeachStats, at?: string): StoredModelAdaptation {\n\t\tconst now = at ?? new Date().toISOString();\n\t\treturn this.mutateProfile(\n\t\t\tmodel,\n\t\t\tnew Date(now),\n\t\t\t(profile) => ({\n\t\t\t\t...profile,\n\t\t\t\tteachStats: { ...profile.teachStats, [mode]: stats },\n\t\t\t}),\n\t\t\tnow,\n\t\t).entry!;\n\t}\n\n\t/** Profiles for the current host (default) or an explicit host id. */\n\tgetForHost(hostId?: string): StoredModelAdaptation[] {\n\t\treturn Object.values(this.storage.getHost(hostId) ?? {}).map((entry) => ({\n\t\t\t...entry,\n\t\t\tprofile: normalizeProfile(entry.profile),\n\t\t}));\n\t}\n\n\t/** Every stored profile across all hosts (for cross-machine comparisons). */\n\tgetAll(): StoredModelAdaptation[] {\n\t\treturn this.storage\n\t\t\t.getAllHosts()\n\t\t\t.flatMap((models) =>\n\t\t\t\tObject.values(models).map((entry) => ({ ...entry, profile: normalizeProfile(entry.profile) })),\n\t\t\t);\n\t}\n}\n"]}