@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
@@ -1 +1 @@
1
- {"version":3,"file":"learning-audit.js","sourceRoot":"","sources":["../../../src/core/learning/learning-audit.ts"],"names":[],"mappings":"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","sourcesContent":["import type { SessionEntry, SessionManager } from \"@caupulican/pi-agent-core/node\";\nimport type { LearningDecision } from \"../autonomy/contracts.ts\";\nimport type { DurableChangeLayer, DurableChangeProposal } from \"./learning-gate.ts\";\nimport type { ReflectionWrite } from \"./reflection-engine.ts\";\n\n/**\n * Audit + rollback metadata for durable learning changes. Every reflection-sourced write that the\n * learning policy applies (or converts to a proposal) leaves one of these records in the session\n * log, so `/autonomy diagnostics` can explain what changed, why, and how to undo it — and\n * `rollbackLearningWrite` can execute the inverse operation.\n */\n\nexport type LearningRollbackKind = \"memory_remove\" | \"memory_restore\" | \"memory_add\" | \"archive_skill\";\n\nexport interface LearningRollbackPlan {\n\tkind: LearningRollbackKind;\n\t/** Text currently present because of the change (to remove/replace), or the skill name to archive. */\n\ttarget?: string;\n\t/** Original text to restore (memory_restore/memory_add). */\n\tprevious?: string;\n\tinstructions: string;\n}\n\n// \"apply_failed\" is distinct from \"apply\": the gate decided to apply, but the underlying write tool\n// (e.g. the memory tool) refused it (budget exceeded, drift, threat) without throwing. It must never\n// be treated as \"apply\" by a rollback-eligibility check — there is nothing durable to undo.\nexport type LearningAuditAction = \"apply\" | \"apply_failed\" | \"propose\" | \"rollback\";\n\n/** reasonCode for an \"apply_failed\" audit — distinct from the gate's own reasonCode (e.g.\n * \"eligible_auto_apply\") so the record is honest about WHERE it stopped: the gate approved the\n * write, but the write tool refused it after the fact. */\nexport const APPLY_WRITE_REFUSED_REASON_CODE = \"apply_write_refused\";\n\nexport interface LearningAuditRecord {\n\tid: string;\n\tproposalId: string;\n\tlayer: DurableChangeLayer;\n\taction: LearningAuditAction;\n\tsummary: string;\n\treasonCode: string;\n\tdecision: LearningDecision;\n\trollback?: LearningRollbackPlan;\n\t/** For action \"rollback\": the audit id of the applied change this record undoes. */\n\trollbackOf?: string;\n\tcreatedAt: string;\n}\n\nfunction describeWrite(write: ReflectionWrite): string {\n\tswitch (write.kind) {\n\t\tcase \"memory_add\":\n\t\t\treturn `Add ${write.section} memory: ${write.text}`;\n\t\tcase \"memory_replace\":\n\t\t\treturn `Replace memory \"${write.target}\" with \"${write.text}\"`;\n\t\tcase \"memory_remove\":\n\t\t\treturn `Remove memory: ${write.target}`;\n\t\tcase \"promote_skill\":\n\t\t\treturn `Promote skill \"${write.name}\": ${write.description}`;\n\t}\n}\n\nexport function proposalFromReflectionWrite(write: ReflectionWrite, proposalId: string): DurableChangeProposal {\n\treturn {\n\t\tid: proposalId,\n\t\tlayer: write.kind === \"promote_skill\" ? \"skill\" : \"memory\",\n\t\tsummary: describeWrite(write),\n\t\trollbackPlan: rollbackPlanForReflectionWrite(write).instructions,\n\t};\n}\n\n/**\n * Contradiction count a reflection write carries against existing durable knowledge. A\n * `memory_replace`/`memory_remove` is only emitted when the reflection engine CONFRONTS an existing\n * fact (it supersedes or deletes it) — that supersession is the gate's contradiction signal, so such\n * a write must route through approval rather than silently overwriting prior memory. A `memory_add`\n * or `promote_skill` is purely additive and contradicts nothing.\n */\nexport function contradictionsForReflectionWrite(write: ReflectionWrite): number {\n\tswitch (write.kind) {\n\t\tcase \"memory_replace\":\n\t\tcase \"memory_remove\":\n\t\t\treturn 1;\n\t\tcase \"memory_add\":\n\t\tcase \"promote_skill\":\n\t\t\treturn 0;\n\t}\n}\n\nexport function rollbackPlanForReflectionWrite(write: ReflectionWrite): LearningRollbackPlan {\n\tswitch (write.kind) {\n\t\tcase \"memory_add\":\n\t\t\treturn {\n\t\t\t\tkind: \"memory_remove\",\n\t\t\t\ttarget: write.text,\n\t\t\t\tinstructions: `Remove the added ${write.section} memory text.`,\n\t\t\t};\n\t\tcase \"memory_replace\":\n\t\t\treturn {\n\t\t\t\tkind: \"memory_restore\",\n\t\t\t\ttarget: write.text,\n\t\t\t\tprevious: write.target,\n\t\t\t\tinstructions: \"Replace the new memory text with the original text it overwrote.\",\n\t\t\t};\n\t\tcase \"memory_remove\":\n\t\t\treturn {\n\t\t\t\tkind: \"memory_add\",\n\t\t\t\tprevious: write.target,\n\t\t\t\tinstructions: \"Re-add the removed text to the MEMORY file (it may originally have lived in USER).\",\n\t\t\t};\n\t\tcase \"promote_skill\":\n\t\t\treturn {\n\t\t\t\tkind: \"archive_skill\",\n\t\t\t\ttarget: write.name,\n\t\t\t\tinstructions: \"Archive the promoted skill so it no longer loads.\",\n\t\t\t};\n\t}\n}\n\nconst AUDIT_ACTIONS: readonly string[] = [\"apply\", \"apply_failed\", \"propose\", \"rollback\"];\nconst ROLLBACK_KINDS: readonly string[] = [\"memory_remove\", \"memory_restore\", \"memory_add\", \"archive_skill\"];\nconst LAYERS: readonly string[] = [\"memory\", \"skill\", \"prompt\", \"extension\", \"tool\", \"script\", \"settings\", \"source\"];\n\nfunction isOptionalString(value: unknown): boolean {\n\treturn value === undefined || typeof value === \"string\";\n}\n\nfunction isLearningRollbackPlan(value: unknown): value is LearningRollbackPlan {\n\tif (!value || typeof value !== \"object\" || Array.isArray(value)) return false;\n\tconst plan = value as Record<string, unknown>;\n\tif (typeof plan.kind !== \"string\" || !ROLLBACK_KINDS.includes(plan.kind)) return false;\n\tif (!isOptionalString(plan.target) || !isOptionalString(plan.previous)) return false;\n\treturn typeof plan.instructions === \"string\";\n}\n\nfunction isLearningDecisionShape(value: unknown): value is LearningDecision {\n\tif (!value || typeof value !== \"object\" || Array.isArray(value)) return false;\n\tconst decision = value as Record<string, unknown>;\n\treturn (\n\t\t(decision.kind === \"no-op\" || decision.kind === \"proposal\" || decision.kind === \"apply\") &&\n\t\ttypeof decision.reasonCode === \"string\" &&\n\t\ttypeof decision.confidence === \"number\" &&\n\t\ttypeof decision.summary === \"string\" &&\n\t\ttypeof decision.requiresApproval === \"boolean\"\n\t);\n}\n\nexport function isLearningAuditRecord(value: unknown): value is LearningAuditRecord {\n\tif (!value || typeof value !== \"object\" || Array.isArray(value)) return false;\n\tconst record = value as Record<string, unknown>;\n\tif (typeof record.id !== \"string\" || record.id.length === 0) return false;\n\tif (typeof record.proposalId !== \"string\") return false;\n\tif (typeof record.layer !== \"string\" || !LAYERS.includes(record.layer)) return false;\n\tif (typeof record.action !== \"string\" || !AUDIT_ACTIONS.includes(record.action)) return false;\n\tif (typeof record.summary !== \"string\" || typeof record.reasonCode !== \"string\") return false;\n\tif (!isLearningDecisionShape(record.decision)) return false;\n\tif (record.rollback !== undefined && !isLearningRollbackPlan(record.rollback)) return false;\n\tif (!isOptionalString(record.rollbackOf)) return false;\n\treturn typeof record.createdAt === \"string\";\n}\n\nexport function cloneLearningAuditRecordForStorage(record: LearningAuditRecord): LearningAuditRecord {\n\treturn {\n\t\t...record,\n\t\tdecision: { ...record.decision },\n\t\t...(record.rollback ? { rollback: { ...record.rollback } } : {}),\n\t};\n}\n\nexport const LEARNING_AUDIT_CUSTOM_TYPE = \"learning_audit\";\n\nexport interface LearningAuditSnapshotPayload {\n\tversion: 1;\n\trecord: LearningAuditRecord;\n}\n\nexport function appendLearningAuditSnapshot(\n\tsessionManager: Pick<SessionManager, \"appendCustomEntry\">,\n\trecord: LearningAuditRecord,\n): string {\n\tconst payload: LearningAuditSnapshotPayload = {\n\t\tversion: 1,\n\t\trecord: cloneLearningAuditRecordForStorage(record),\n\t};\n\treturn sessionManager.appendCustomEntry(LEARNING_AUDIT_CUSTOM_TYPE, payload);\n}\n\nfunction isPlainRecord(value: unknown): value is Record<string, unknown> {\n\tif (!value || typeof value !== \"object\" || Array.isArray(value)) return false;\n\tconst prototype = Object.getPrototypeOf(value);\n\treturn prototype === Object.prototype || prototype === null;\n}\n\nexport function getLearningAuditSnapshots(entries: readonly SessionEntry[]): LearningAuditRecord[] {\n\tconst records: LearningAuditRecord[] = [];\n\n\tfor (const entry of entries) {\n\t\tif (entry.type !== \"custom\" || entry.customType !== LEARNING_AUDIT_CUSTOM_TYPE) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst payload = entry.data;\n\t\tif (!isPlainRecord(payload)) continue;\n\t\tif (payload.version !== 1) continue;\n\t\tif (!(\"record\" in payload)) continue;\n\t\tconst record = payload.record;\n\t\tif (isLearningAuditRecord(record)) {\n\t\t\trecords.push(cloneLearningAuditRecordForStorage(record));\n\t\t}\n\t}\n\n\treturn records;\n}\n"]}
1
+ {"version":3,"file":"learning-audit.js","sourceRoot":"","sources":["../../../src/core/learning/learning-audit.ts"],"names":[],"mappings":"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","sourcesContent":["import type { SessionEntry, SessionManager } from \"@caupulican/pi-agent-core/node\";\nimport type { LearningDecision } from \"../autonomy/contracts.ts\";\nimport {\n\tappendSessionSnapshot,\n\tgetSessionSnapshots,\n\ttype SessionSnapshotCodec,\n\ttype SessionSnapshotPayload,\n} from \"../session-snapshot.ts\";\nimport type { DurableChangeLayer, DurableChangeProposal } from \"./learning-gate.ts\";\nimport type { ReflectionWrite } from \"./reflection-engine.ts\";\n\n/**\n * Audit + rollback metadata for durable learning changes. Every reflection-sourced write that the\n * learning policy applies (or converts to a proposal) leaves one of these records in the session\n * log, so `/autonomy diagnostics` can explain what changed, why, and how to undo it — and\n * `rollbackLearningWrite` can execute the inverse operation.\n */\n\nexport type LearningRollbackKind = \"memory_remove\" | \"memory_restore\" | \"memory_add\" | \"archive_skill\";\n\nexport interface LearningRollbackPlan {\n\tkind: LearningRollbackKind;\n\t/** Text currently present because of the change (to remove/replace), or the skill name to archive. */\n\ttarget?: string;\n\t/** Original text to restore (memory_restore/memory_add). */\n\tprevious?: string;\n\tinstructions: string;\n}\n\n// \"apply_failed\" is distinct from \"apply\": the gate decided to apply, but the underlying write tool\n// (e.g. the memory tool) refused it (budget exceeded, drift, threat) without throwing. It must never\n// be treated as \"apply\" by a rollback-eligibility check — there is nothing durable to undo.\nexport type LearningAuditAction = \"apply\" | \"apply_failed\" | \"propose\" | \"rollback\";\n\n/** reasonCode for an \"apply_failed\" audit — distinct from the gate's own reasonCode (e.g.\n * \"eligible_auto_apply\") so the record is honest about WHERE it stopped: the gate approved the\n * write, but the write tool refused it after the fact. */\nexport const APPLY_WRITE_REFUSED_REASON_CODE = \"apply_write_refused\";\n\nexport interface LearningAuditRecord {\n\tid: string;\n\tproposalId: string;\n\tlayer: DurableChangeLayer;\n\taction: LearningAuditAction;\n\tsummary: string;\n\treasonCode: string;\n\tdecision: LearningDecision;\n\trollback?: LearningRollbackPlan;\n\t/** For action \"rollback\": the audit id of the applied change this record undoes. */\n\trollbackOf?: string;\n\tcreatedAt: string;\n}\n\nfunction describeWrite(write: ReflectionWrite): string {\n\tswitch (write.kind) {\n\t\tcase \"memory_add\":\n\t\t\treturn `Add ${write.section} memory: ${write.text}`;\n\t\tcase \"memory_replace\":\n\t\t\treturn `Replace memory \"${write.target}\" with \"${write.text}\"`;\n\t\tcase \"memory_remove\":\n\t\t\treturn `Remove memory: ${write.target}`;\n\t\tcase \"promote_skill\":\n\t\t\treturn `Promote skill \"${write.name}\": ${write.description}`;\n\t}\n}\n\nexport function proposalFromReflectionWrite(write: ReflectionWrite, proposalId: string): DurableChangeProposal {\n\treturn {\n\t\tid: proposalId,\n\t\tlayer: write.kind === \"promote_skill\" ? \"skill\" : \"memory\",\n\t\tsummary: describeWrite(write),\n\t\trollbackPlan: rollbackPlanForReflectionWrite(write).instructions,\n\t};\n}\n\n/**\n * Contradiction count a reflection write carries against existing durable knowledge. A\n * `memory_replace`/`memory_remove` is only emitted when the reflection engine CONFRONTS an existing\n * fact (it supersedes or deletes it) — that supersession is the gate's contradiction signal, so such\n * a write must route through approval rather than silently overwriting prior memory. A `memory_add`\n * or `promote_skill` is purely additive and contradicts nothing.\n */\nexport function contradictionsForReflectionWrite(write: ReflectionWrite): number {\n\tswitch (write.kind) {\n\t\tcase \"memory_replace\":\n\t\tcase \"memory_remove\":\n\t\t\treturn 1;\n\t\tcase \"memory_add\":\n\t\tcase \"promote_skill\":\n\t\t\treturn 0;\n\t}\n}\n\nexport function rollbackPlanForReflectionWrite(write: ReflectionWrite): LearningRollbackPlan {\n\tswitch (write.kind) {\n\t\tcase \"memory_add\":\n\t\t\treturn {\n\t\t\t\tkind: \"memory_remove\",\n\t\t\t\ttarget: write.text,\n\t\t\t\tinstructions: `Remove the added ${write.section} memory text.`,\n\t\t\t};\n\t\tcase \"memory_replace\":\n\t\t\treturn {\n\t\t\t\tkind: \"memory_restore\",\n\t\t\t\ttarget: write.text,\n\t\t\t\tprevious: write.target,\n\t\t\t\tinstructions: \"Replace the new memory text with the original text it overwrote.\",\n\t\t\t};\n\t\tcase \"memory_remove\":\n\t\t\treturn {\n\t\t\t\tkind: \"memory_add\",\n\t\t\t\tprevious: write.target,\n\t\t\t\tinstructions: \"Re-add the removed text to the MEMORY file (it may originally have lived in USER).\",\n\t\t\t};\n\t\tcase \"promote_skill\":\n\t\t\treturn {\n\t\t\t\tkind: \"archive_skill\",\n\t\t\t\ttarget: write.name,\n\t\t\t\tinstructions: \"Archive the promoted skill so it no longer loads.\",\n\t\t\t};\n\t}\n}\n\nconst AUDIT_ACTIONS: readonly string[] = [\"apply\", \"apply_failed\", \"propose\", \"rollback\"];\nconst ROLLBACK_KINDS: readonly string[] = [\"memory_remove\", \"memory_restore\", \"memory_add\", \"archive_skill\"];\nconst LAYERS: readonly string[] = [\"memory\", \"skill\", \"prompt\", \"extension\", \"tool\", \"script\", \"settings\", \"source\"];\n\nfunction isOptionalString(value: unknown): boolean {\n\treturn value === undefined || typeof value === \"string\";\n}\n\nfunction isLearningRollbackPlan(value: unknown): value is LearningRollbackPlan {\n\tif (!value || typeof value !== \"object\" || Array.isArray(value)) return false;\n\tconst plan = value as Record<string, unknown>;\n\tif (typeof plan.kind !== \"string\" || !ROLLBACK_KINDS.includes(plan.kind)) return false;\n\tif (!isOptionalString(plan.target) || !isOptionalString(plan.previous)) return false;\n\treturn typeof plan.instructions === \"string\";\n}\n\nfunction isLearningDecisionShape(value: unknown): value is LearningDecision {\n\tif (!value || typeof value !== \"object\" || Array.isArray(value)) return false;\n\tconst decision = value as Record<string, unknown>;\n\treturn (\n\t\t(decision.kind === \"no-op\" || decision.kind === \"proposal\" || decision.kind === \"apply\") &&\n\t\ttypeof decision.reasonCode === \"string\" &&\n\t\ttypeof decision.confidence === \"number\" &&\n\t\ttypeof decision.summary === \"string\" &&\n\t\ttypeof decision.requiresApproval === \"boolean\"\n\t);\n}\n\nexport function isLearningAuditRecord(value: unknown): value is LearningAuditRecord {\n\tif (!value || typeof value !== \"object\" || Array.isArray(value)) return false;\n\tconst record = value as Record<string, unknown>;\n\tif (typeof record.id !== \"string\" || record.id.length === 0) return false;\n\tif (typeof record.proposalId !== \"string\") return false;\n\tif (typeof record.layer !== \"string\" || !LAYERS.includes(record.layer)) return false;\n\tif (typeof record.action !== \"string\" || !AUDIT_ACTIONS.includes(record.action)) return false;\n\tif (typeof record.summary !== \"string\" || typeof record.reasonCode !== \"string\") return false;\n\tif (!isLearningDecisionShape(record.decision)) return false;\n\tif (record.rollback !== undefined && !isLearningRollbackPlan(record.rollback)) return false;\n\tif (!isOptionalString(record.rollbackOf)) return false;\n\treturn typeof record.createdAt === \"string\";\n}\n\nexport function cloneLearningAuditRecordForStorage(record: LearningAuditRecord): LearningAuditRecord {\n\treturn {\n\t\t...record,\n\t\tdecision: { ...record.decision },\n\t\t...(record.rollback ? { rollback: { ...record.rollback } } : {}),\n\t};\n}\n\nexport const LEARNING_AUDIT_CUSTOM_TYPE = \"learning_audit\";\n\nexport type LearningAuditSnapshotPayload = SessionSnapshotPayload<\"record\", LearningAuditRecord>;\n\nconst LEARNING_AUDIT_SNAPSHOT_CODEC: SessionSnapshotCodec<LearningAuditRecord, \"record\"> = {\n\tcustomType: LEARNING_AUDIT_CUSTOM_TYPE,\n\tvalueKey: \"record\",\n\tisValue: isLearningAuditRecord,\n\tclone: cloneLearningAuditRecordForStorage,\n};\n\nexport function appendLearningAuditSnapshot(\n\tsessionManager: Pick<SessionManager, \"appendCustomEntry\">,\n\trecord: LearningAuditRecord,\n): string {\n\treturn appendSessionSnapshot(sessionManager, LEARNING_AUDIT_SNAPSHOT_CODEC, record);\n}\n\nexport function getLearningAuditSnapshots(entries: readonly SessionEntry[]): LearningAuditRecord[] {\n\treturn getSessionSnapshots(entries, LEARNING_AUDIT_SNAPSHOT_CODEC);\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"learning-gate.d.ts","sourceRoot":"","sources":["../../../src/core/learning/learning-gate.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAEjE,MAAM,MAAM,kBAAkB,GAC3B,QAAQ,GACR,OAAO,GACP,QAAQ,GACR,WAAW,GACX,MAAM,GACN,QAAQ,GACR,UAAU,GACV,QAAQ,CAAC;AAEZ,MAAM,WAAW,qBAAqB;IACrC,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,kBAAkB,CAAC;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAChC,YAAY,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,oBAAoB;IACpC,OAAO,EAAE,OAAO,CAAC;IACjB,gBAAgB,EAAE,OAAO,CAAC;IAC1B,mBAAmB,EAAE,MAAM,CAAC;IAC5B,eAAe,EAAE,MAAM,CAAC;IACxB,sBAAsB,EAAE,SAAS,kBAAkB,EAAE,CAAC;IACtD,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B;2FACuF;IACvF,sBAAsB,CAAC,EAAE,OAAO,CAAC;CACjC;AA2BD,wBAAgB,wBAAwB,CAAC,IAAI,EAAE;IAC9C,QAAQ,EAAE,qBAAqB,CAAC;IAChC,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,cAAc,EAAE,MAAM,CAAC;IACvB,QAAQ,EAAE,oBAAoB,CAAC;IAC/B,GAAG,CAAC,EAAE,MAAM,CAAC;CACb,GAAG,gBAAgB,CAoHnB;AAQD,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,gBAAgB,CAc5E;AAED,wBAAgB,+BAA+B,CAAC,QAAQ,EAAE,gBAAgB,GAAG,gBAAgB,CAS5F","sourcesContent":["import type { LearningDecision } from \"../autonomy/contracts.ts\";\n\nexport type DurableChangeLayer =\n\t| \"memory\"\n\t| \"skill\"\n\t| \"prompt\"\n\t| \"extension\"\n\t| \"tool\"\n\t| \"script\"\n\t| \"settings\"\n\t| \"source\";\n\nexport interface DurableChangeProposal {\n\tid: string;\n\tlayer: DurableChangeLayer;\n\tsummary: string;\n\tevidenceIds?: readonly string[];\n\trollbackPlan?: string;\n}\n\nexport interface LearningGateSettings {\n\tenabled: boolean;\n\tautoApplyEnabled: boolean;\n\tconfidenceThreshold: number;\n\tminObservations: number;\n\tallowedAutoApplyLayers: readonly DurableChangeLayer[];\n\trequireRollbackPlan?: boolean;\n\trequireEvidence?: boolean;\n\t/** default: false — a memory_replace/memory_remove (supersedes/deletes an existing fact) stays an\n\t * approval-gated proposal even when otherwise eligible, unless explicitly opted in. */\n\tautoApplySupersessions?: boolean;\n}\n\nconst MAX_DECISION_SUMMARY_LENGTH = 240;\n\nfunction boundedDecisionSummary(summary: string): string {\n\tif (summary.length <= MAX_DECISION_SUMMARY_LENGTH) return summary;\n\treturn `${summary.slice(0, MAX_DECISION_SUMMARY_LENGTH - 1)}…`;\n}\n\nfunction learningDecision(args: {\n\tkind: LearningDecision[\"kind\"];\n\treasonCode: string;\n\tconfidence: number;\n\tsummary: string;\n\trequiresApproval: boolean;\n\tcreatedAt?: string;\n}): LearningDecision {\n\treturn {\n\t\tkind: args.kind,\n\t\treasonCode: args.reasonCode,\n\t\tconfidence: args.confidence,\n\t\tsummary: boundedDecisionSummary(args.summary),\n\t\trequiresApproval: args.requiresApproval,\n\t\tcreatedAt: args.createdAt,\n\t};\n}\n\nexport function evaluateLearningDecision(args: {\n\tproposal: DurableChangeProposal;\n\tconfidence: number;\n\tobservations: number;\n\tcontradictions: number;\n\tsettings: LearningGateSettings;\n\tnow?: string;\n}): LearningDecision {\n\tconst { proposal, confidence, observations, contradictions, settings, now } = args;\n\n\tif (!settings.enabled) {\n\t\treturn learningDecision({\n\t\t\tkind: \"no-op\",\n\t\t\treasonCode: \"learning_disabled\",\n\t\t\tconfidence,\n\t\t\tsummary: proposal.summary,\n\t\t\trequiresApproval: false,\n\t\t\tcreatedAt: now,\n\t\t});\n\t}\n\n\t// A replace/remove supersedes or deletes an existing fact — the reflection engine's\n\t// confront-before-write conflict signal — so by default it routes through approval rather than\n\t// silently overwriting prior memory. With `autoApplySupersessions` opted in, that signal no longer\n\t// short-circuits the decision outright: it FALLS THROUGH to the standard eligibility chain below,\n\t// so a supersession auto-applies only when everything else (threshold/observations/evidence/\n\t// rollback/autoApplyEnabled/layer) also passes.\n\tif (contradictions > 0 && !settings.autoApplySupersessions) {\n\t\treturn learningDecision({\n\t\t\tkind: \"proposal\",\n\t\t\treasonCode: \"contradictions_present\",\n\t\t\tconfidence,\n\t\t\tsummary: proposal.summary,\n\t\t\trequiresApproval: true,\n\t\t\tcreatedAt: now,\n\t\t});\n\t}\n\n\tif (confidence < settings.confidenceThreshold) {\n\t\t// Below-threshold confidence degrades to an approval-gated proposal, never a silent no-op:\n\t\t// learning stays fail-closed (it can only auto-apply above the threshold) while remaining\n\t\t// VISIBLE and audited. A silent no-op here disabled learning entirely under stock settings\n\t\t// (reflectionSourceConfidence < confidenceThreshold, reflection writes carrying no evidenceIds).\n\t\treturn learningDecision({\n\t\t\tkind: \"proposal\",\n\t\t\treasonCode: \"below_confidence_threshold\",\n\t\t\tconfidence,\n\t\t\tsummary: proposal.summary,\n\t\t\trequiresApproval: true,\n\t\t\tcreatedAt: now,\n\t\t});\n\t}\n\n\tif (observations < settings.minObservations) {\n\t\treturn learningDecision({\n\t\t\tkind: \"proposal\",\n\t\t\treasonCode: \"insufficient_observations\",\n\t\t\tconfidence,\n\t\t\tsummary: proposal.summary,\n\t\t\trequiresApproval: true,\n\t\t\tcreatedAt: now,\n\t\t});\n\t}\n\n\tif (settings.requireEvidence) {\n\t\tconst hasEvidence = !!(proposal.evidenceIds && proposal.evidenceIds.length > 0);\n\t\tif (!hasEvidence) {\n\t\t\treturn learningDecision({\n\t\t\t\tkind: \"proposal\",\n\t\t\t\treasonCode: \"missing_evidence\",\n\t\t\t\tconfidence,\n\t\t\t\tsummary: proposal.summary,\n\t\t\t\trequiresApproval: true,\n\t\t\t\tcreatedAt: now,\n\t\t\t});\n\t\t}\n\t}\n\n\tif (settings.requireRollbackPlan) {\n\t\tconst hasRollback = proposal.rollbackPlan !== undefined && proposal.rollbackPlan.trim().length > 0;\n\t\tif (!hasRollback) {\n\t\t\treturn learningDecision({\n\t\t\t\tkind: \"proposal\",\n\t\t\t\treasonCode: \"missing_rollback_plan\",\n\t\t\t\tconfidence,\n\t\t\t\tsummary: proposal.summary,\n\t\t\t\trequiresApproval: true,\n\t\t\t\tcreatedAt: now,\n\t\t\t});\n\t\t}\n\t}\n\n\tif (!settings.autoApplyEnabled) {\n\t\treturn learningDecision({\n\t\t\tkind: \"proposal\",\n\t\t\treasonCode: \"auto_apply_disabled\",\n\t\t\tconfidence,\n\t\t\tsummary: proposal.summary,\n\t\t\trequiresApproval: true,\n\t\t\tcreatedAt: now,\n\t\t});\n\t}\n\n\tconst isAllowed = settings.allowedAutoApplyLayers.includes(proposal.layer);\n\tif (!isAllowed) {\n\t\treturn learningDecision({\n\t\t\tkind: \"proposal\",\n\t\t\treasonCode: \"layer_not_allowed_for_auto_apply\",\n\t\t\tconfidence,\n\t\t\tsummary: proposal.summary,\n\t\t\trequiresApproval: true,\n\t\t\tcreatedAt: now,\n\t\t});\n\t}\n\n\treturn learningDecision({\n\t\tkind: \"apply\",\n\t\treasonCode: \"eligible_auto_apply\",\n\t\tconfidence,\n\t\tsummary: proposal.summary,\n\t\trequiresApproval: false,\n\t\tcreatedAt: now,\n\t});\n}\n\nfunction isPlainRecord(value: unknown): value is Record<string, unknown> {\n\tif (!value || typeof value !== \"object\" || Array.isArray(value)) return false;\n\tconst prototype = Object.getPrototypeOf(value);\n\treturn prototype === Object.prototype || prototype === null;\n}\n\nexport function isLearningDecision(value: unknown): value is LearningDecision {\n\tif (!isPlainRecord(value)) return false;\n\n\tif (typeof value.kind !== \"string\" || ![\"no-op\", \"proposal\", \"apply\"].includes(value.kind)) {\n\t\treturn false;\n\t}\n\n\tif (typeof value.reasonCode !== \"string\") return false;\n\tif (typeof value.confidence !== \"number\" || !Number.isFinite(value.confidence)) return false;\n\tif (typeof value.summary !== \"string\") return false;\n\tif (typeof value.requiresApproval !== \"boolean\") return false;\n\tif (value.createdAt !== undefined && typeof value.createdAt !== \"string\") return false;\n\n\treturn true;\n}\n\nexport function cloneLearningDecisionForStorage(decision: LearningDecision): LearningDecision {\n\treturn {\n\t\tkind: decision.kind,\n\t\treasonCode: decision.reasonCode,\n\t\tconfidence: decision.confidence,\n\t\tsummary: decision.summary,\n\t\trequiresApproval: decision.requiresApproval,\n\t\tcreatedAt: decision.createdAt,\n\t};\n}\n"]}
1
+ {"version":3,"file":"learning-gate.d.ts","sourceRoot":"","sources":["../../../src/core/learning/learning-gate.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAGjE,MAAM,MAAM,kBAAkB,GAC3B,QAAQ,GACR,OAAO,GACP,QAAQ,GACR,WAAW,GACX,MAAM,GACN,QAAQ,GACR,UAAU,GACV,QAAQ,CAAC;AAEZ,MAAM,WAAW,qBAAqB;IACrC,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,kBAAkB,CAAC;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAChC,YAAY,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,oBAAoB;IACpC,OAAO,EAAE,OAAO,CAAC;IACjB,gBAAgB,EAAE,OAAO,CAAC;IAC1B,mBAAmB,EAAE,MAAM,CAAC;IAC5B,eAAe,EAAE,MAAM,CAAC;IACxB,sBAAsB,EAAE,SAAS,kBAAkB,EAAE,CAAC;IACtD,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B;2FACuF;IACvF,sBAAsB,CAAC,EAAE,OAAO,CAAC;CACjC;AA2BD,wBAAgB,wBAAwB,CAAC,IAAI,EAAE;IAC9C,QAAQ,EAAE,qBAAqB,CAAC;IAChC,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,cAAc,EAAE,MAAM,CAAC;IACvB,QAAQ,EAAE,oBAAoB,CAAC;IAC/B,GAAG,CAAC,EAAE,MAAM,CAAC;CACb,GAAG,gBAAgB,CAoHnB;AAED,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,gBAAgB,CAc5E;AAED,wBAAgB,+BAA+B,CAAC,QAAQ,EAAE,gBAAgB,GAAG,gBAAgB,CAS5F","sourcesContent":["import type { LearningDecision } from \"../autonomy/contracts.ts\";\nimport { isPlainRecord } from \"../util/value-guards.ts\";\n\nexport type DurableChangeLayer =\n\t| \"memory\"\n\t| \"skill\"\n\t| \"prompt\"\n\t| \"extension\"\n\t| \"tool\"\n\t| \"script\"\n\t| \"settings\"\n\t| \"source\";\n\nexport interface DurableChangeProposal {\n\tid: string;\n\tlayer: DurableChangeLayer;\n\tsummary: string;\n\tevidenceIds?: readonly string[];\n\trollbackPlan?: string;\n}\n\nexport interface LearningGateSettings {\n\tenabled: boolean;\n\tautoApplyEnabled: boolean;\n\tconfidenceThreshold: number;\n\tminObservations: number;\n\tallowedAutoApplyLayers: readonly DurableChangeLayer[];\n\trequireRollbackPlan?: boolean;\n\trequireEvidence?: boolean;\n\t/** default: false — a memory_replace/memory_remove (supersedes/deletes an existing fact) stays an\n\t * approval-gated proposal even when otherwise eligible, unless explicitly opted in. */\n\tautoApplySupersessions?: boolean;\n}\n\nconst MAX_DECISION_SUMMARY_LENGTH = 240;\n\nfunction boundedDecisionSummary(summary: string): string {\n\tif (summary.length <= MAX_DECISION_SUMMARY_LENGTH) return summary;\n\treturn `${summary.slice(0, MAX_DECISION_SUMMARY_LENGTH - 1)}…`;\n}\n\nfunction learningDecision(args: {\n\tkind: LearningDecision[\"kind\"];\n\treasonCode: string;\n\tconfidence: number;\n\tsummary: string;\n\trequiresApproval: boolean;\n\tcreatedAt?: string;\n}): LearningDecision {\n\treturn {\n\t\tkind: args.kind,\n\t\treasonCode: args.reasonCode,\n\t\tconfidence: args.confidence,\n\t\tsummary: boundedDecisionSummary(args.summary),\n\t\trequiresApproval: args.requiresApproval,\n\t\tcreatedAt: args.createdAt,\n\t};\n}\n\nexport function evaluateLearningDecision(args: {\n\tproposal: DurableChangeProposal;\n\tconfidence: number;\n\tobservations: number;\n\tcontradictions: number;\n\tsettings: LearningGateSettings;\n\tnow?: string;\n}): LearningDecision {\n\tconst { proposal, confidence, observations, contradictions, settings, now } = args;\n\n\tif (!settings.enabled) {\n\t\treturn learningDecision({\n\t\t\tkind: \"no-op\",\n\t\t\treasonCode: \"learning_disabled\",\n\t\t\tconfidence,\n\t\t\tsummary: proposal.summary,\n\t\t\trequiresApproval: false,\n\t\t\tcreatedAt: now,\n\t\t});\n\t}\n\n\t// A replace/remove supersedes or deletes an existing fact — the reflection engine's\n\t// confront-before-write conflict signal — so by default it routes through approval rather than\n\t// silently overwriting prior memory. With `autoApplySupersessions` opted in, that signal no longer\n\t// short-circuits the decision outright: it FALLS THROUGH to the standard eligibility chain below,\n\t// so a supersession auto-applies only when everything else (threshold/observations/evidence/\n\t// rollback/autoApplyEnabled/layer) also passes.\n\tif (contradictions > 0 && !settings.autoApplySupersessions) {\n\t\treturn learningDecision({\n\t\t\tkind: \"proposal\",\n\t\t\treasonCode: \"contradictions_present\",\n\t\t\tconfidence,\n\t\t\tsummary: proposal.summary,\n\t\t\trequiresApproval: true,\n\t\t\tcreatedAt: now,\n\t\t});\n\t}\n\n\tif (confidence < settings.confidenceThreshold) {\n\t\t// Below-threshold confidence degrades to an approval-gated proposal, never a silent no-op:\n\t\t// learning stays fail-closed (it can only auto-apply above the threshold) while remaining\n\t\t// VISIBLE and audited. A silent no-op here disabled learning entirely under stock settings\n\t\t// (reflectionSourceConfidence < confidenceThreshold, reflection writes carrying no evidenceIds).\n\t\treturn learningDecision({\n\t\t\tkind: \"proposal\",\n\t\t\treasonCode: \"below_confidence_threshold\",\n\t\t\tconfidence,\n\t\t\tsummary: proposal.summary,\n\t\t\trequiresApproval: true,\n\t\t\tcreatedAt: now,\n\t\t});\n\t}\n\n\tif (observations < settings.minObservations) {\n\t\treturn learningDecision({\n\t\t\tkind: \"proposal\",\n\t\t\treasonCode: \"insufficient_observations\",\n\t\t\tconfidence,\n\t\t\tsummary: proposal.summary,\n\t\t\trequiresApproval: true,\n\t\t\tcreatedAt: now,\n\t\t});\n\t}\n\n\tif (settings.requireEvidence) {\n\t\tconst hasEvidence = !!(proposal.evidenceIds && proposal.evidenceIds.length > 0);\n\t\tif (!hasEvidence) {\n\t\t\treturn learningDecision({\n\t\t\t\tkind: \"proposal\",\n\t\t\t\treasonCode: \"missing_evidence\",\n\t\t\t\tconfidence,\n\t\t\t\tsummary: proposal.summary,\n\t\t\t\trequiresApproval: true,\n\t\t\t\tcreatedAt: now,\n\t\t\t});\n\t\t}\n\t}\n\n\tif (settings.requireRollbackPlan) {\n\t\tconst hasRollback = proposal.rollbackPlan !== undefined && proposal.rollbackPlan.trim().length > 0;\n\t\tif (!hasRollback) {\n\t\t\treturn learningDecision({\n\t\t\t\tkind: \"proposal\",\n\t\t\t\treasonCode: \"missing_rollback_plan\",\n\t\t\t\tconfidence,\n\t\t\t\tsummary: proposal.summary,\n\t\t\t\trequiresApproval: true,\n\t\t\t\tcreatedAt: now,\n\t\t\t});\n\t\t}\n\t}\n\n\tif (!settings.autoApplyEnabled) {\n\t\treturn learningDecision({\n\t\t\tkind: \"proposal\",\n\t\t\treasonCode: \"auto_apply_disabled\",\n\t\t\tconfidence,\n\t\t\tsummary: proposal.summary,\n\t\t\trequiresApproval: true,\n\t\t\tcreatedAt: now,\n\t\t});\n\t}\n\n\tconst isAllowed = settings.allowedAutoApplyLayers.includes(proposal.layer);\n\tif (!isAllowed) {\n\t\treturn learningDecision({\n\t\t\tkind: \"proposal\",\n\t\t\treasonCode: \"layer_not_allowed_for_auto_apply\",\n\t\t\tconfidence,\n\t\t\tsummary: proposal.summary,\n\t\t\trequiresApproval: true,\n\t\t\tcreatedAt: now,\n\t\t});\n\t}\n\n\treturn learningDecision({\n\t\tkind: \"apply\",\n\t\treasonCode: \"eligible_auto_apply\",\n\t\tconfidence,\n\t\tsummary: proposal.summary,\n\t\trequiresApproval: false,\n\t\tcreatedAt: now,\n\t});\n}\n\nexport function isLearningDecision(value: unknown): value is LearningDecision {\n\tif (!isPlainRecord(value)) return false;\n\n\tif (typeof value.kind !== \"string\" || ![\"no-op\", \"proposal\", \"apply\"].includes(value.kind)) {\n\t\treturn false;\n\t}\n\n\tif (typeof value.reasonCode !== \"string\") return false;\n\tif (typeof value.confidence !== \"number\" || !Number.isFinite(value.confidence)) return false;\n\tif (typeof value.summary !== \"string\") return false;\n\tif (typeof value.requiresApproval !== \"boolean\") return false;\n\tif (value.createdAt !== undefined && typeof value.createdAt !== \"string\") return false;\n\n\treturn true;\n}\n\nexport function cloneLearningDecisionForStorage(decision: LearningDecision): LearningDecision {\n\treturn {\n\t\tkind: decision.kind,\n\t\treasonCode: decision.reasonCode,\n\t\tconfidence: decision.confidence,\n\t\tsummary: decision.summary,\n\t\trequiresApproval: decision.requiresApproval,\n\t\tcreatedAt: decision.createdAt,\n\t};\n}\n"]}
@@ -1,3 +1,4 @@
1
+ import { isPlainRecord } from "../util/value-guards.js";
1
2
  const MAX_DECISION_SUMMARY_LENGTH = 240;
2
3
  function boundedDecisionSummary(summary) {
3
4
  if (summary.length <= MAX_DECISION_SUMMARY_LENGTH)
@@ -122,12 +123,6 @@ export function evaluateLearningDecision(args) {
122
123
  createdAt: now,
123
124
  });
124
125
  }
125
- function isPlainRecord(value) {
126
- if (!value || typeof value !== "object" || Array.isArray(value))
127
- return false;
128
- const prototype = Object.getPrototypeOf(value);
129
- return prototype === Object.prototype || prototype === null;
130
- }
131
126
  export function isLearningDecision(value) {
132
127
  if (!isPlainRecord(value))
133
128
  return false;
@@ -1 +1 @@
1
- {"version":3,"file":"learning-gate.js","sourceRoot":"","sources":["../../../src/core/learning/learning-gate.ts"],"names":[],"mappings":"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","sourcesContent":["import type { LearningDecision } from \"../autonomy/contracts.ts\";\n\nexport type DurableChangeLayer =\n\t| \"memory\"\n\t| \"skill\"\n\t| \"prompt\"\n\t| \"extension\"\n\t| \"tool\"\n\t| \"script\"\n\t| \"settings\"\n\t| \"source\";\n\nexport interface DurableChangeProposal {\n\tid: string;\n\tlayer: DurableChangeLayer;\n\tsummary: string;\n\tevidenceIds?: readonly string[];\n\trollbackPlan?: string;\n}\n\nexport interface LearningGateSettings {\n\tenabled: boolean;\n\tautoApplyEnabled: boolean;\n\tconfidenceThreshold: number;\n\tminObservations: number;\n\tallowedAutoApplyLayers: readonly DurableChangeLayer[];\n\trequireRollbackPlan?: boolean;\n\trequireEvidence?: boolean;\n\t/** default: false — a memory_replace/memory_remove (supersedes/deletes an existing fact) stays an\n\t * approval-gated proposal even when otherwise eligible, unless explicitly opted in. */\n\tautoApplySupersessions?: boolean;\n}\n\nconst MAX_DECISION_SUMMARY_LENGTH = 240;\n\nfunction boundedDecisionSummary(summary: string): string {\n\tif (summary.length <= MAX_DECISION_SUMMARY_LENGTH) return summary;\n\treturn `${summary.slice(0, MAX_DECISION_SUMMARY_LENGTH - 1)}…`;\n}\n\nfunction learningDecision(args: {\n\tkind: LearningDecision[\"kind\"];\n\treasonCode: string;\n\tconfidence: number;\n\tsummary: string;\n\trequiresApproval: boolean;\n\tcreatedAt?: string;\n}): LearningDecision {\n\treturn {\n\t\tkind: args.kind,\n\t\treasonCode: args.reasonCode,\n\t\tconfidence: args.confidence,\n\t\tsummary: boundedDecisionSummary(args.summary),\n\t\trequiresApproval: args.requiresApproval,\n\t\tcreatedAt: args.createdAt,\n\t};\n}\n\nexport function evaluateLearningDecision(args: {\n\tproposal: DurableChangeProposal;\n\tconfidence: number;\n\tobservations: number;\n\tcontradictions: number;\n\tsettings: LearningGateSettings;\n\tnow?: string;\n}): LearningDecision {\n\tconst { proposal, confidence, observations, contradictions, settings, now } = args;\n\n\tif (!settings.enabled) {\n\t\treturn learningDecision({\n\t\t\tkind: \"no-op\",\n\t\t\treasonCode: \"learning_disabled\",\n\t\t\tconfidence,\n\t\t\tsummary: proposal.summary,\n\t\t\trequiresApproval: false,\n\t\t\tcreatedAt: now,\n\t\t});\n\t}\n\n\t// A replace/remove supersedes or deletes an existing fact — the reflection engine's\n\t// confront-before-write conflict signal — so by default it routes through approval rather than\n\t// silently overwriting prior memory. With `autoApplySupersessions` opted in, that signal no longer\n\t// short-circuits the decision outright: it FALLS THROUGH to the standard eligibility chain below,\n\t// so a supersession auto-applies only when everything else (threshold/observations/evidence/\n\t// rollback/autoApplyEnabled/layer) also passes.\n\tif (contradictions > 0 && !settings.autoApplySupersessions) {\n\t\treturn learningDecision({\n\t\t\tkind: \"proposal\",\n\t\t\treasonCode: \"contradictions_present\",\n\t\t\tconfidence,\n\t\t\tsummary: proposal.summary,\n\t\t\trequiresApproval: true,\n\t\t\tcreatedAt: now,\n\t\t});\n\t}\n\n\tif (confidence < settings.confidenceThreshold) {\n\t\t// Below-threshold confidence degrades to an approval-gated proposal, never a silent no-op:\n\t\t// learning stays fail-closed (it can only auto-apply above the threshold) while remaining\n\t\t// VISIBLE and audited. A silent no-op here disabled learning entirely under stock settings\n\t\t// (reflectionSourceConfidence < confidenceThreshold, reflection writes carrying no evidenceIds).\n\t\treturn learningDecision({\n\t\t\tkind: \"proposal\",\n\t\t\treasonCode: \"below_confidence_threshold\",\n\t\t\tconfidence,\n\t\t\tsummary: proposal.summary,\n\t\t\trequiresApproval: true,\n\t\t\tcreatedAt: now,\n\t\t});\n\t}\n\n\tif (observations < settings.minObservations) {\n\t\treturn learningDecision({\n\t\t\tkind: \"proposal\",\n\t\t\treasonCode: \"insufficient_observations\",\n\t\t\tconfidence,\n\t\t\tsummary: proposal.summary,\n\t\t\trequiresApproval: true,\n\t\t\tcreatedAt: now,\n\t\t});\n\t}\n\n\tif (settings.requireEvidence) {\n\t\tconst hasEvidence = !!(proposal.evidenceIds && proposal.evidenceIds.length > 0);\n\t\tif (!hasEvidence) {\n\t\t\treturn learningDecision({\n\t\t\t\tkind: \"proposal\",\n\t\t\t\treasonCode: \"missing_evidence\",\n\t\t\t\tconfidence,\n\t\t\t\tsummary: proposal.summary,\n\t\t\t\trequiresApproval: true,\n\t\t\t\tcreatedAt: now,\n\t\t\t});\n\t\t}\n\t}\n\n\tif (settings.requireRollbackPlan) {\n\t\tconst hasRollback = proposal.rollbackPlan !== undefined && proposal.rollbackPlan.trim().length > 0;\n\t\tif (!hasRollback) {\n\t\t\treturn learningDecision({\n\t\t\t\tkind: \"proposal\",\n\t\t\t\treasonCode: \"missing_rollback_plan\",\n\t\t\t\tconfidence,\n\t\t\t\tsummary: proposal.summary,\n\t\t\t\trequiresApproval: true,\n\t\t\t\tcreatedAt: now,\n\t\t\t});\n\t\t}\n\t}\n\n\tif (!settings.autoApplyEnabled) {\n\t\treturn learningDecision({\n\t\t\tkind: \"proposal\",\n\t\t\treasonCode: \"auto_apply_disabled\",\n\t\t\tconfidence,\n\t\t\tsummary: proposal.summary,\n\t\t\trequiresApproval: true,\n\t\t\tcreatedAt: now,\n\t\t});\n\t}\n\n\tconst isAllowed = settings.allowedAutoApplyLayers.includes(proposal.layer);\n\tif (!isAllowed) {\n\t\treturn learningDecision({\n\t\t\tkind: \"proposal\",\n\t\t\treasonCode: \"layer_not_allowed_for_auto_apply\",\n\t\t\tconfidence,\n\t\t\tsummary: proposal.summary,\n\t\t\trequiresApproval: true,\n\t\t\tcreatedAt: now,\n\t\t});\n\t}\n\n\treturn learningDecision({\n\t\tkind: \"apply\",\n\t\treasonCode: \"eligible_auto_apply\",\n\t\tconfidence,\n\t\tsummary: proposal.summary,\n\t\trequiresApproval: false,\n\t\tcreatedAt: now,\n\t});\n}\n\nfunction isPlainRecord(value: unknown): value is Record<string, unknown> {\n\tif (!value || typeof value !== \"object\" || Array.isArray(value)) return false;\n\tconst prototype = Object.getPrototypeOf(value);\n\treturn prototype === Object.prototype || prototype === null;\n}\n\nexport function isLearningDecision(value: unknown): value is LearningDecision {\n\tif (!isPlainRecord(value)) return false;\n\n\tif (typeof value.kind !== \"string\" || ![\"no-op\", \"proposal\", \"apply\"].includes(value.kind)) {\n\t\treturn false;\n\t}\n\n\tif (typeof value.reasonCode !== \"string\") return false;\n\tif (typeof value.confidence !== \"number\" || !Number.isFinite(value.confidence)) return false;\n\tif (typeof value.summary !== \"string\") return false;\n\tif (typeof value.requiresApproval !== \"boolean\") return false;\n\tif (value.createdAt !== undefined && typeof value.createdAt !== \"string\") return false;\n\n\treturn true;\n}\n\nexport function cloneLearningDecisionForStorage(decision: LearningDecision): LearningDecision {\n\treturn {\n\t\tkind: decision.kind,\n\t\treasonCode: decision.reasonCode,\n\t\tconfidence: decision.confidence,\n\t\tsummary: decision.summary,\n\t\trequiresApproval: decision.requiresApproval,\n\t\tcreatedAt: decision.createdAt,\n\t};\n}\n"]}
1
+ {"version":3,"file":"learning-gate.js","sourceRoot":"","sources":["../../../src/core/learning/learning-gate.ts"],"names":[],"mappings":"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","sourcesContent":["import type { LearningDecision } from \"../autonomy/contracts.ts\";\nimport { isPlainRecord } from \"../util/value-guards.ts\";\n\nexport type DurableChangeLayer =\n\t| \"memory\"\n\t| \"skill\"\n\t| \"prompt\"\n\t| \"extension\"\n\t| \"tool\"\n\t| \"script\"\n\t| \"settings\"\n\t| \"source\";\n\nexport interface DurableChangeProposal {\n\tid: string;\n\tlayer: DurableChangeLayer;\n\tsummary: string;\n\tevidenceIds?: readonly string[];\n\trollbackPlan?: string;\n}\n\nexport interface LearningGateSettings {\n\tenabled: boolean;\n\tautoApplyEnabled: boolean;\n\tconfidenceThreshold: number;\n\tminObservations: number;\n\tallowedAutoApplyLayers: readonly DurableChangeLayer[];\n\trequireRollbackPlan?: boolean;\n\trequireEvidence?: boolean;\n\t/** default: false — a memory_replace/memory_remove (supersedes/deletes an existing fact) stays an\n\t * approval-gated proposal even when otherwise eligible, unless explicitly opted in. */\n\tautoApplySupersessions?: boolean;\n}\n\nconst MAX_DECISION_SUMMARY_LENGTH = 240;\n\nfunction boundedDecisionSummary(summary: string): string {\n\tif (summary.length <= MAX_DECISION_SUMMARY_LENGTH) return summary;\n\treturn `${summary.slice(0, MAX_DECISION_SUMMARY_LENGTH - 1)}…`;\n}\n\nfunction learningDecision(args: {\n\tkind: LearningDecision[\"kind\"];\n\treasonCode: string;\n\tconfidence: number;\n\tsummary: string;\n\trequiresApproval: boolean;\n\tcreatedAt?: string;\n}): LearningDecision {\n\treturn {\n\t\tkind: args.kind,\n\t\treasonCode: args.reasonCode,\n\t\tconfidence: args.confidence,\n\t\tsummary: boundedDecisionSummary(args.summary),\n\t\trequiresApproval: args.requiresApproval,\n\t\tcreatedAt: args.createdAt,\n\t};\n}\n\nexport function evaluateLearningDecision(args: {\n\tproposal: DurableChangeProposal;\n\tconfidence: number;\n\tobservations: number;\n\tcontradictions: number;\n\tsettings: LearningGateSettings;\n\tnow?: string;\n}): LearningDecision {\n\tconst { proposal, confidence, observations, contradictions, settings, now } = args;\n\n\tif (!settings.enabled) {\n\t\treturn learningDecision({\n\t\t\tkind: \"no-op\",\n\t\t\treasonCode: \"learning_disabled\",\n\t\t\tconfidence,\n\t\t\tsummary: proposal.summary,\n\t\t\trequiresApproval: false,\n\t\t\tcreatedAt: now,\n\t\t});\n\t}\n\n\t// A replace/remove supersedes or deletes an existing fact — the reflection engine's\n\t// confront-before-write conflict signal — so by default it routes through approval rather than\n\t// silently overwriting prior memory. With `autoApplySupersessions` opted in, that signal no longer\n\t// short-circuits the decision outright: it FALLS THROUGH to the standard eligibility chain below,\n\t// so a supersession auto-applies only when everything else (threshold/observations/evidence/\n\t// rollback/autoApplyEnabled/layer) also passes.\n\tif (contradictions > 0 && !settings.autoApplySupersessions) {\n\t\treturn learningDecision({\n\t\t\tkind: \"proposal\",\n\t\t\treasonCode: \"contradictions_present\",\n\t\t\tconfidence,\n\t\t\tsummary: proposal.summary,\n\t\t\trequiresApproval: true,\n\t\t\tcreatedAt: now,\n\t\t});\n\t}\n\n\tif (confidence < settings.confidenceThreshold) {\n\t\t// Below-threshold confidence degrades to an approval-gated proposal, never a silent no-op:\n\t\t// learning stays fail-closed (it can only auto-apply above the threshold) while remaining\n\t\t// VISIBLE and audited. A silent no-op here disabled learning entirely under stock settings\n\t\t// (reflectionSourceConfidence < confidenceThreshold, reflection writes carrying no evidenceIds).\n\t\treturn learningDecision({\n\t\t\tkind: \"proposal\",\n\t\t\treasonCode: \"below_confidence_threshold\",\n\t\t\tconfidence,\n\t\t\tsummary: proposal.summary,\n\t\t\trequiresApproval: true,\n\t\t\tcreatedAt: now,\n\t\t});\n\t}\n\n\tif (observations < settings.minObservations) {\n\t\treturn learningDecision({\n\t\t\tkind: \"proposal\",\n\t\t\treasonCode: \"insufficient_observations\",\n\t\t\tconfidence,\n\t\t\tsummary: proposal.summary,\n\t\t\trequiresApproval: true,\n\t\t\tcreatedAt: now,\n\t\t});\n\t}\n\n\tif (settings.requireEvidence) {\n\t\tconst hasEvidence = !!(proposal.evidenceIds && proposal.evidenceIds.length > 0);\n\t\tif (!hasEvidence) {\n\t\t\treturn learningDecision({\n\t\t\t\tkind: \"proposal\",\n\t\t\t\treasonCode: \"missing_evidence\",\n\t\t\t\tconfidence,\n\t\t\t\tsummary: proposal.summary,\n\t\t\t\trequiresApproval: true,\n\t\t\t\tcreatedAt: now,\n\t\t\t});\n\t\t}\n\t}\n\n\tif (settings.requireRollbackPlan) {\n\t\tconst hasRollback = proposal.rollbackPlan !== undefined && proposal.rollbackPlan.trim().length > 0;\n\t\tif (!hasRollback) {\n\t\t\treturn learningDecision({\n\t\t\t\tkind: \"proposal\",\n\t\t\t\treasonCode: \"missing_rollback_plan\",\n\t\t\t\tconfidence,\n\t\t\t\tsummary: proposal.summary,\n\t\t\t\trequiresApproval: true,\n\t\t\t\tcreatedAt: now,\n\t\t\t});\n\t\t}\n\t}\n\n\tif (!settings.autoApplyEnabled) {\n\t\treturn learningDecision({\n\t\t\tkind: \"proposal\",\n\t\t\treasonCode: \"auto_apply_disabled\",\n\t\t\tconfidence,\n\t\t\tsummary: proposal.summary,\n\t\t\trequiresApproval: true,\n\t\t\tcreatedAt: now,\n\t\t});\n\t}\n\n\tconst isAllowed = settings.allowedAutoApplyLayers.includes(proposal.layer);\n\tif (!isAllowed) {\n\t\treturn learningDecision({\n\t\t\tkind: \"proposal\",\n\t\t\treasonCode: \"layer_not_allowed_for_auto_apply\",\n\t\t\tconfidence,\n\t\t\tsummary: proposal.summary,\n\t\t\trequiresApproval: true,\n\t\t\tcreatedAt: now,\n\t\t});\n\t}\n\n\treturn learningDecision({\n\t\tkind: \"apply\",\n\t\treasonCode: \"eligible_auto_apply\",\n\t\tconfidence,\n\t\tsummary: proposal.summary,\n\t\trequiresApproval: false,\n\t\tcreatedAt: now,\n\t});\n}\n\nexport function isLearningDecision(value: unknown): value is LearningDecision {\n\tif (!isPlainRecord(value)) return false;\n\n\tif (typeof value.kind !== \"string\" || ![\"no-op\", \"proposal\", \"apply\"].includes(value.kind)) {\n\t\treturn false;\n\t}\n\n\tif (typeof value.reasonCode !== \"string\") return false;\n\tif (typeof value.confidence !== \"number\" || !Number.isFinite(value.confidence)) return false;\n\tif (typeof value.summary !== \"string\") return false;\n\tif (typeof value.requiresApproval !== \"boolean\") return false;\n\tif (value.createdAt !== undefined && typeof value.createdAt !== \"string\") return false;\n\n\treturn true;\n}\n\nexport function cloneLearningDecisionForStorage(decision: LearningDecision): LearningDecision {\n\treturn {\n\t\tkind: decision.kind,\n\t\treasonCode: decision.reasonCode,\n\t\tconfidence: decision.confidence,\n\t\tsummary: decision.summary,\n\t\trequiresApproval: decision.requiresApproval,\n\t\tcreatedAt: decision.createdAt,\n\t};\n}\n"]}
@@ -6,13 +6,22 @@
6
6
  export declare function observationKey(layer: string, summary: string): string;
7
7
  export declare class ObservationStore {
8
8
  private readonly filePath;
9
- constructor(filePath: string);
10
- static forAgentDir(agentDir: string): ObservationStore;
9
+ private readonly readOnly;
10
+ constructor(filePath: string, options?: {
11
+ readOnly?: boolean;
12
+ });
13
+ static forAgentDir(agentDir: string, options?: {
14
+ readOnly?: boolean;
15
+ }): ObservationStore;
11
16
  private load;
12
17
  private save;
13
18
  /** Evict least-recently-incremented keys until the store is back within {@link MAX_KEYS}. */
14
19
  private evict;
15
- /** Record one more observation of `key` and return the new (capped) count. */
20
+ /**
21
+ * Record one more observation of `key` and return the new (capped) count. Load-mutate-save runs
22
+ * under a single exclusive lock so two concurrent increments (e.g. two reflection passes across
23
+ * sessions sharing an agentDir) can't both read the old count and drop one increment.
24
+ */
16
25
  increment(key: string, at?: string): number;
17
26
  /** Current observation count for `key` (0 if never observed). */
18
27
  get(key: string): number;
@@ -1 +1 @@
1
- {"version":3,"file":"observation-store.d.ts","sourceRoot":"","sources":["../../../src/core/learning/observation-store.ts"],"names":[],"mappings":"AAgCA;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAGrE;AAED,qBAAa,gBAAgB;IAC5B,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAElC,YAAY,QAAQ,EAAE,MAAM,EAE3B;IAED,MAAM,CAAC,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,gBAAgB,CAErD;IAED,OAAO,CAAC,IAAI;IAoCZ,OAAO,CAAC,IAAI;IAKZ,6FAA6F;IAC7F,OAAO,CAAC,KAAK;IAab,8EAA8E;IAC9E,SAAS,CAAC,GAAG,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAQ1C;IAED,iEAAiE;IACjE,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAEvB;CACD","sourcesContent":["import { createHash } from \"node:crypto\";\nimport { existsSync, mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { dirname, join } from \"node:path\";\n\n/**\n * Durable, BOUNDED evidence-strength store for the learning gate (G6). The gate auto-applies a\n * durable change only once it has been *observed* enough times; a single reflection pass sees a\n * lesson once, so without persistence every proposal would look brand-new and never accumulate the\n * repeated evidence the gate requires. This store counts how many times the SAME lesson (keyed by\n * its durable layer + normalized summary) has been proposed across passes and sessions.\n *\n * File layout mirrors {@link ../models/fitness-store.ts}: a versioned JSON object under\n * `<agentDir>/state/`, best-effort writes, and a corrupt/missing file recovers as a fresh store.\n */\n\n/** Cap per-key counts so a hot lesson can't grow unbounded (the gate only needs a small threshold). */\nconst MAX_COUNT = 100;\n/** Cap the number of tracked keys; least-recently-incremented keys are evicted past this bound. */\nconst MAX_KEYS = 500;\n\ninterface ObservationEntry {\n\tcount: number;\n\t/** ISO timestamp of the most recent increment; drives least-recently-incremented eviction. */\n\tlastAt: string;\n}\n\ninterface ObservationStoreFile {\n\tversion: 1;\n\t/** observationKey -> accumulated evidence for that lesson. */\n\tobservations: Record<string, ObservationEntry>;\n}\n\n/**\n * Stable evidence key for a durable-change proposal: sha256 of the layer plus its summary,\n * lowercased and whitespace-collapsed, truncated to 24 hex chars. Normalization means the same\n * lesson re-observed with reworded whitespace/casing accumulates onto one key instead of scattering.\n */\nexport function observationKey(layer: string, summary: string): string {\n\tconst normalized = summary.toLowerCase().replace(/\\s+/g, \" \").trim();\n\treturn createHash(\"sha256\").update(`${layer}\\n${normalized}`).digest(\"hex\").slice(0, 24);\n}\n\nexport class ObservationStore {\n\tprivate readonly filePath: string;\n\n\tconstructor(filePath: string) {\n\t\tthis.filePath = filePath;\n\t}\n\n\tstatic forAgentDir(agentDir: string): ObservationStore {\n\t\treturn new ObservationStore(join(agentDir, \"state\", \"learning-observations.json\"));\n\t}\n\n\tprivate load(): ObservationStoreFile {\n\t\ttry {\n\t\t\tif (!existsSync(this.filePath)) return { version: 1, observations: {} };\n\t\t\tconst parsed = JSON.parse(readFileSync(this.filePath, \"utf-8\")) as ObservationStoreFile;\n\t\t\tif (\n\t\t\t\tparsed &&\n\t\t\t\tparsed.version === 1 &&\n\t\t\t\tparsed.observations &&\n\t\t\t\ttypeof parsed.observations === \"object\" &&\n\t\t\t\t!Array.isArray(parsed.observations)\n\t\t\t) {\n\t\t\t\t// Sanitize per-entry so a partially-mangled file still yields a usable store rather than\n\t\t\t\t// leaking NaN/undefined counts into the gate.\n\t\t\t\tconst clean: ObservationStoreFile = { version: 1, observations: {} };\n\t\t\t\tfor (const [key, value] of Object.entries(parsed.observations)) {\n\t\t\t\t\tif (\n\t\t\t\t\t\tvalue &&\n\t\t\t\t\t\ttypeof value === \"object\" &&\n\t\t\t\t\t\ttypeof (value as ObservationEntry).count === \"number\" &&\n\t\t\t\t\t\tNumber.isFinite((value as ObservationEntry).count) &&\n\t\t\t\t\t\ttypeof (value as ObservationEntry).lastAt === \"string\"\n\t\t\t\t\t) {\n\t\t\t\t\t\tclean.observations[key] = {\n\t\t\t\t\t\t\tcount: Math.min(Math.max(0, Math.floor((value as ObservationEntry).count)), MAX_COUNT),\n\t\t\t\t\t\t\tlastAt: (value as ObservationEntry).lastAt,\n\t\t\t\t\t\t};\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn clean;\n\t\t\t}\n\t\t} catch {\n\t\t\t// Unreadable/corrupt store: start fresh in memory; the next increment rewrites the file.\n\t\t}\n\t\treturn { version: 1, observations: {} };\n\t}\n\n\tprivate save(file: ObservationStoreFile): 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\t/** Evict least-recently-incremented keys until the store is back within {@link MAX_KEYS}. */\n\tprivate evict(file: ObservationStoreFile): void {\n\t\tconst keys = Object.keys(file.observations);\n\t\tif (keys.length <= MAX_KEYS) return;\n\t\tkeys.sort((a, b) => {\n\t\t\tconst la = file.observations[a]!.lastAt;\n\t\t\tconst lb = file.observations[b]!.lastAt;\n\t\t\treturn la < lb ? -1 : la > lb ? 1 : 0;\n\t\t});\n\t\tfor (const key of keys.slice(0, keys.length - MAX_KEYS)) {\n\t\t\tdelete file.observations[key];\n\t\t}\n\t}\n\n\t/** Record one more observation of `key` and return the new (capped) count. */\n\tincrement(key: string, at?: string): number {\n\t\tconst file = this.load();\n\t\tconst now = at ?? new Date().toISOString();\n\t\tconst count = Math.min((file.observations[key]?.count ?? 0) + 1, MAX_COUNT);\n\t\tfile.observations[key] = { count, lastAt: now };\n\t\tthis.evict(file);\n\t\tthis.save(file);\n\t\treturn count;\n\t}\n\n\t/** Current observation count for `key` (0 if never observed). */\n\tget(key: string): number {\n\t\treturn this.load().observations[key]?.count ?? 0;\n\t}\n}\n"]}
1
+ {"version":3,"file":"observation-store.d.ts","sourceRoot":"","sources":["../../../src/core/learning/observation-store.ts"],"names":[],"mappings":"AAkCA;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAGrE;AAED,qBAAa,gBAAgB;IAC5B,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAU;IAEnC,YAAY,QAAQ,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,QAAQ,CAAC,EAAE,OAAO,CAAA;KAAE,EAG7D;IAED,MAAM,CAAC,WAAW,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,QAAQ,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,gBAAgB,CAEvF;IAED,OAAO,CAAC,IAAI;IAoCZ,OAAO,CAAC,IAAI;IAIZ,6FAA6F;IAC7F,OAAO,CAAC,KAAK;IAab;;;;OAIG;IACH,SAAS,CAAC,GAAG,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAgB1C;IAED,iEAAiE;IACjE,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAEvB;CACD","sourcesContent":["import { createHash } from \"node:crypto\";\nimport { existsSync, readFileSync } from \"node:fs\";\nimport { stateFile } from \"../agent-paths.ts\";\nimport { isWorkerSession } from \"../session-role.ts\";\nimport { withFileLockSync, writeFileAtomicSync } from \"../util/atomic-file.ts\";\n\n/**\n * Durable, BOUNDED evidence-strength store for the learning gate. The gate auto-applies a\n * durable change only once it has been *observed* enough times; a single reflection pass sees a\n * lesson once, so without persistence every proposal would look brand-new and never accumulate the\n * repeated evidence the gate requires. This store counts how many times the SAME lesson (keyed by\n * its durable layer + normalized summary) has been proposed across passes and sessions.\n *\n * File layout mirrors {@link ../models/fitness-store.ts}: a versioned JSON object under\n * `<agentDir>/state/`, best-effort writes, and a corrupt/missing file recovers as a fresh store.\n */\n\n/** Cap per-key counts so a hot lesson can't grow unbounded (the gate only needs a small threshold). */\nconst MAX_COUNT = 100;\n/** Cap the number of tracked keys; least-recently-incremented keys are evicted past this bound. */\nconst MAX_KEYS = 500;\n\ninterface ObservationEntry {\n\tcount: number;\n\t/** ISO timestamp of the most recent increment; drives least-recently-incremented eviction. */\n\tlastAt: string;\n}\n\ninterface ObservationStoreFile {\n\tversion: 1;\n\t/** observationKey -> accumulated evidence for that lesson. */\n\tobservations: Record<string, ObservationEntry>;\n}\n\n/**\n * Stable evidence key for a durable-change proposal: sha256 of the layer plus its summary,\n * lowercased and whitespace-collapsed, truncated to 24 hex chars. Normalization means the same\n * lesson re-observed with reworded whitespace/casing accumulates onto one key instead of scattering.\n */\nexport function observationKey(layer: string, summary: string): string {\n\tconst normalized = summary.toLowerCase().replace(/\\s+/g, \" \").trim();\n\treturn createHash(\"sha256\").update(`${layer}\\n${normalized}`).digest(\"hex\").slice(0, 24);\n}\n\nexport class ObservationStore {\n\tprivate readonly filePath: string;\n\tprivate readonly readOnly: boolean;\n\n\tconstructor(filePath: string, options?: { readOnly?: boolean }) {\n\t\tthis.filePath = filePath;\n\t\tthis.readOnly = options?.readOnly ?? isWorkerSession();\n\t}\n\n\tstatic forAgentDir(agentDir: string, options?: { readOnly?: boolean }): ObservationStore {\n\t\treturn new ObservationStore(stateFile(agentDir, \"learning-observations.json\"), options);\n\t}\n\n\tprivate load(): ObservationStoreFile {\n\t\ttry {\n\t\t\tif (!existsSync(this.filePath)) return { version: 1, observations: {} };\n\t\t\tconst parsed = JSON.parse(readFileSync(this.filePath, \"utf-8\")) as ObservationStoreFile;\n\t\t\tif (\n\t\t\t\tparsed &&\n\t\t\t\tparsed.version === 1 &&\n\t\t\t\tparsed.observations &&\n\t\t\t\ttypeof parsed.observations === \"object\" &&\n\t\t\t\t!Array.isArray(parsed.observations)\n\t\t\t) {\n\t\t\t\t// Sanitize per-entry so a partially-mangled file still yields a usable store rather than\n\t\t\t\t// leaking NaN/undefined counts into the gate.\n\t\t\t\tconst clean: ObservationStoreFile = { version: 1, observations: {} };\n\t\t\t\tfor (const [key, value] of Object.entries(parsed.observations)) {\n\t\t\t\t\tif (\n\t\t\t\t\t\tvalue &&\n\t\t\t\t\t\ttypeof value === \"object\" &&\n\t\t\t\t\t\ttypeof (value as ObservationEntry).count === \"number\" &&\n\t\t\t\t\t\tNumber.isFinite((value as ObservationEntry).count) &&\n\t\t\t\t\t\ttypeof (value as ObservationEntry).lastAt === \"string\"\n\t\t\t\t\t) {\n\t\t\t\t\t\tclean.observations[key] = {\n\t\t\t\t\t\t\tcount: Math.min(Math.max(0, Math.floor((value as ObservationEntry).count)), MAX_COUNT),\n\t\t\t\t\t\t\tlastAt: (value as ObservationEntry).lastAt,\n\t\t\t\t\t\t};\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn clean;\n\t\t\t}\n\t\t} catch {\n\t\t\t// Unreadable/corrupt store: start fresh in memory; the next increment rewrites the file.\n\t\t}\n\t\treturn { version: 1, observations: {} };\n\t}\n\n\tprivate save(file: ObservationStoreFile): void {\n\t\twriteFileAtomicSync(this.filePath, `${JSON.stringify(file, null, \"\\t\")}\\n`);\n\t}\n\n\t/** Evict least-recently-incremented keys until the store is back within {@link MAX_KEYS}. */\n\tprivate evict(file: ObservationStoreFile): void {\n\t\tconst keys = Object.keys(file.observations);\n\t\tif (keys.length <= MAX_KEYS) return;\n\t\tkeys.sort((a, b) => {\n\t\t\tconst la = file.observations[a]!.lastAt;\n\t\t\tconst lb = file.observations[b]!.lastAt;\n\t\t\treturn la < lb ? -1 : la > lb ? 1 : 0;\n\t\t});\n\t\tfor (const key of keys.slice(0, keys.length - MAX_KEYS)) {\n\t\t\tdelete file.observations[key];\n\t\t}\n\t}\n\n\t/**\n\t * Record one more observation of `key` and return the new (capped) count. Load-mutate-save runs\n\t * under a single exclusive lock so two concurrent increments (e.g. two reflection passes across\n\t * sessions sharing an agentDir) can't both read the old count and drop one increment.\n\t */\n\tincrement(key: string, at?: string): number {\n\t\tif (this.readOnly) {\n\t\t\t// Zero-footprint (worker session): no lock, no dir, no write -- just the pure-read\n\t\t\t// equivalent of what a real increment would have returned.\n\t\t\tconst file = this.load();\n\t\t\treturn Math.min((file.observations[key]?.count ?? 0) + 1, MAX_COUNT);\n\t\t}\n\t\tconst now = at ?? new Date().toISOString();\n\t\treturn withFileLockSync(this.filePath, () => {\n\t\t\tconst file = this.load();\n\t\t\tconst count = Math.min((file.observations[key]?.count ?? 0) + 1, MAX_COUNT);\n\t\t\tfile.observations[key] = { count, lastAt: now };\n\t\t\tthis.evict(file);\n\t\t\tthis.save(file);\n\t\t\treturn count;\n\t\t});\n\t}\n\n\t/** Current observation count for `key` (0 if never observed). */\n\tget(key: string): number {\n\t\treturn this.load().observations[key]?.count ?? 0;\n\t}\n}\n"]}
@@ -1,8 +1,10 @@
1
1
  import { createHash } from "node:crypto";
2
- import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
3
- import { dirname, join } from "node:path";
2
+ import { existsSync, readFileSync } from "node:fs";
3
+ import { stateFile } from "../agent-paths.js";
4
+ import { isWorkerSession } from "../session-role.js";
5
+ import { withFileLockSync, writeFileAtomicSync } from "../util/atomic-file.js";
4
6
  /**
5
- * Durable, BOUNDED evidence-strength store for the learning gate (G6). The gate auto-applies a
7
+ * Durable, BOUNDED evidence-strength store for the learning gate. The gate auto-applies a
6
8
  * durable change only once it has been *observed* enough times; a single reflection pass sees a
7
9
  * lesson once, so without persistence every proposal would look brand-new and never accumulate the
8
10
  * repeated evidence the gate requires. This store counts how many times the SAME lesson (keyed by
@@ -26,11 +28,13 @@ export function observationKey(layer, summary) {
26
28
  }
27
29
  export class ObservationStore {
28
30
  filePath;
29
- constructor(filePath) {
31
+ readOnly;
32
+ constructor(filePath, options) {
30
33
  this.filePath = filePath;
34
+ this.readOnly = options?.readOnly ?? isWorkerSession();
31
35
  }
32
- static forAgentDir(agentDir) {
33
- return new ObservationStore(join(agentDir, "state", "learning-observations.json"));
36
+ static forAgentDir(agentDir, options) {
37
+ return new ObservationStore(stateFile(agentDir, "learning-observations.json"), options);
34
38
  }
35
39
  load() {
36
40
  try {
@@ -66,8 +70,7 @@ export class ObservationStore {
66
70
  return { version: 1, observations: {} };
67
71
  }
68
72
  save(file) {
69
- mkdirSync(dirname(this.filePath), { recursive: true });
70
- writeFileSync(this.filePath, `${JSON.stringify(file, null, "\t")}\n`, "utf-8");
73
+ writeFileAtomicSync(this.filePath, `${JSON.stringify(file, null, "\t")}\n`);
71
74
  }
72
75
  /** Evict least-recently-incremented keys until the store is back within {@link MAX_KEYS}. */
73
76
  evict(file) {
@@ -83,15 +86,27 @@ export class ObservationStore {
83
86
  delete file.observations[key];
84
87
  }
85
88
  }
86
- /** Record one more observation of `key` and return the new (capped) count. */
89
+ /**
90
+ * Record one more observation of `key` and return the new (capped) count. Load-mutate-save runs
91
+ * under a single exclusive lock so two concurrent increments (e.g. two reflection passes across
92
+ * sessions sharing an agentDir) can't both read the old count and drop one increment.
93
+ */
87
94
  increment(key, at) {
88
- const file = this.load();
95
+ if (this.readOnly) {
96
+ // Zero-footprint (worker session): no lock, no dir, no write -- just the pure-read
97
+ // equivalent of what a real increment would have returned.
98
+ const file = this.load();
99
+ return Math.min((file.observations[key]?.count ?? 0) + 1, MAX_COUNT);
100
+ }
89
101
  const now = at ?? new Date().toISOString();
90
- const count = Math.min((file.observations[key]?.count ?? 0) + 1, MAX_COUNT);
91
- file.observations[key] = { count, lastAt: now };
92
- this.evict(file);
93
- this.save(file);
94
- return count;
102
+ return withFileLockSync(this.filePath, () => {
103
+ const file = this.load();
104
+ const count = Math.min((file.observations[key]?.count ?? 0) + 1, MAX_COUNT);
105
+ file.observations[key] = { count, lastAt: now };
106
+ this.evict(file);
107
+ this.save(file);
108
+ return count;
109
+ });
95
110
  }
96
111
  /** Current observation count for `key` (0 if never observed). */
97
112
  get(key) {
@@ -1 +1 @@
1
- {"version":3,"file":"observation-store.js","sourceRoot":"","sources":["../../../src/core/learning/observation-store.ts"],"names":[],"mappings":"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","sourcesContent":["import { createHash } from \"node:crypto\";\nimport { existsSync, mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { dirname, join } from \"node:path\";\n\n/**\n * Durable, BOUNDED evidence-strength store for the learning gate (G6). The gate auto-applies a\n * durable change only once it has been *observed* enough times; a single reflection pass sees a\n * lesson once, so without persistence every proposal would look brand-new and never accumulate the\n * repeated evidence the gate requires. This store counts how many times the SAME lesson (keyed by\n * its durable layer + normalized summary) has been proposed across passes and sessions.\n *\n * File layout mirrors {@link ../models/fitness-store.ts}: a versioned JSON object under\n * `<agentDir>/state/`, best-effort writes, and a corrupt/missing file recovers as a fresh store.\n */\n\n/** Cap per-key counts so a hot lesson can't grow unbounded (the gate only needs a small threshold). */\nconst MAX_COUNT = 100;\n/** Cap the number of tracked keys; least-recently-incremented keys are evicted past this bound. */\nconst MAX_KEYS = 500;\n\ninterface ObservationEntry {\n\tcount: number;\n\t/** ISO timestamp of the most recent increment; drives least-recently-incremented eviction. */\n\tlastAt: string;\n}\n\ninterface ObservationStoreFile {\n\tversion: 1;\n\t/** observationKey -> accumulated evidence for that lesson. */\n\tobservations: Record<string, ObservationEntry>;\n}\n\n/**\n * Stable evidence key for a durable-change proposal: sha256 of the layer plus its summary,\n * lowercased and whitespace-collapsed, truncated to 24 hex chars. Normalization means the same\n * lesson re-observed with reworded whitespace/casing accumulates onto one key instead of scattering.\n */\nexport function observationKey(layer: string, summary: string): string {\n\tconst normalized = summary.toLowerCase().replace(/\\s+/g, \" \").trim();\n\treturn createHash(\"sha256\").update(`${layer}\\n${normalized}`).digest(\"hex\").slice(0, 24);\n}\n\nexport class ObservationStore {\n\tprivate readonly filePath: string;\n\n\tconstructor(filePath: string) {\n\t\tthis.filePath = filePath;\n\t}\n\n\tstatic forAgentDir(agentDir: string): ObservationStore {\n\t\treturn new ObservationStore(join(agentDir, \"state\", \"learning-observations.json\"));\n\t}\n\n\tprivate load(): ObservationStoreFile {\n\t\ttry {\n\t\t\tif (!existsSync(this.filePath)) return { version: 1, observations: {} };\n\t\t\tconst parsed = JSON.parse(readFileSync(this.filePath, \"utf-8\")) as ObservationStoreFile;\n\t\t\tif (\n\t\t\t\tparsed &&\n\t\t\t\tparsed.version === 1 &&\n\t\t\t\tparsed.observations &&\n\t\t\t\ttypeof parsed.observations === \"object\" &&\n\t\t\t\t!Array.isArray(parsed.observations)\n\t\t\t) {\n\t\t\t\t// Sanitize per-entry so a partially-mangled file still yields a usable store rather than\n\t\t\t\t// leaking NaN/undefined counts into the gate.\n\t\t\t\tconst clean: ObservationStoreFile = { version: 1, observations: {} };\n\t\t\t\tfor (const [key, value] of Object.entries(parsed.observations)) {\n\t\t\t\t\tif (\n\t\t\t\t\t\tvalue &&\n\t\t\t\t\t\ttypeof value === \"object\" &&\n\t\t\t\t\t\ttypeof (value as ObservationEntry).count === \"number\" &&\n\t\t\t\t\t\tNumber.isFinite((value as ObservationEntry).count) &&\n\t\t\t\t\t\ttypeof (value as ObservationEntry).lastAt === \"string\"\n\t\t\t\t\t) {\n\t\t\t\t\t\tclean.observations[key] = {\n\t\t\t\t\t\t\tcount: Math.min(Math.max(0, Math.floor((value as ObservationEntry).count)), MAX_COUNT),\n\t\t\t\t\t\t\tlastAt: (value as ObservationEntry).lastAt,\n\t\t\t\t\t\t};\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn clean;\n\t\t\t}\n\t\t} catch {\n\t\t\t// Unreadable/corrupt store: start fresh in memory; the next increment rewrites the file.\n\t\t}\n\t\treturn { version: 1, observations: {} };\n\t}\n\n\tprivate save(file: ObservationStoreFile): 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\t/** Evict least-recently-incremented keys until the store is back within {@link MAX_KEYS}. */\n\tprivate evict(file: ObservationStoreFile): void {\n\t\tconst keys = Object.keys(file.observations);\n\t\tif (keys.length <= MAX_KEYS) return;\n\t\tkeys.sort((a, b) => {\n\t\t\tconst la = file.observations[a]!.lastAt;\n\t\t\tconst lb = file.observations[b]!.lastAt;\n\t\t\treturn la < lb ? -1 : la > lb ? 1 : 0;\n\t\t});\n\t\tfor (const key of keys.slice(0, keys.length - MAX_KEYS)) {\n\t\t\tdelete file.observations[key];\n\t\t}\n\t}\n\n\t/** Record one more observation of `key` and return the new (capped) count. */\n\tincrement(key: string, at?: string): number {\n\t\tconst file = this.load();\n\t\tconst now = at ?? new Date().toISOString();\n\t\tconst count = Math.min((file.observations[key]?.count ?? 0) + 1, MAX_COUNT);\n\t\tfile.observations[key] = { count, lastAt: now };\n\t\tthis.evict(file);\n\t\tthis.save(file);\n\t\treturn count;\n\t}\n\n\t/** Current observation count for `key` (0 if never observed). */\n\tget(key: string): number {\n\t\treturn this.load().observations[key]?.count ?? 0;\n\t}\n}\n"]}
1
+ {"version":3,"file":"observation-store.js","sourceRoot":"","sources":["../../../src/core/learning/observation-store.ts"],"names":[],"mappings":"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","sourcesContent":["import { createHash } from \"node:crypto\";\nimport { existsSync, readFileSync } from \"node:fs\";\nimport { stateFile } from \"../agent-paths.ts\";\nimport { isWorkerSession } from \"../session-role.ts\";\nimport { withFileLockSync, writeFileAtomicSync } from \"../util/atomic-file.ts\";\n\n/**\n * Durable, BOUNDED evidence-strength store for the learning gate. The gate auto-applies a\n * durable change only once it has been *observed* enough times; a single reflection pass sees a\n * lesson once, so without persistence every proposal would look brand-new and never accumulate the\n * repeated evidence the gate requires. This store counts how many times the SAME lesson (keyed by\n * its durable layer + normalized summary) has been proposed across passes and sessions.\n *\n * File layout mirrors {@link ../models/fitness-store.ts}: a versioned JSON object under\n * `<agentDir>/state/`, best-effort writes, and a corrupt/missing file recovers as a fresh store.\n */\n\n/** Cap per-key counts so a hot lesson can't grow unbounded (the gate only needs a small threshold). */\nconst MAX_COUNT = 100;\n/** Cap the number of tracked keys; least-recently-incremented keys are evicted past this bound. */\nconst MAX_KEYS = 500;\n\ninterface ObservationEntry {\n\tcount: number;\n\t/** ISO timestamp of the most recent increment; drives least-recently-incremented eviction. */\n\tlastAt: string;\n}\n\ninterface ObservationStoreFile {\n\tversion: 1;\n\t/** observationKey -> accumulated evidence for that lesson. */\n\tobservations: Record<string, ObservationEntry>;\n}\n\n/**\n * Stable evidence key for a durable-change proposal: sha256 of the layer plus its summary,\n * lowercased and whitespace-collapsed, truncated to 24 hex chars. Normalization means the same\n * lesson re-observed with reworded whitespace/casing accumulates onto one key instead of scattering.\n */\nexport function observationKey(layer: string, summary: string): string {\n\tconst normalized = summary.toLowerCase().replace(/\\s+/g, \" \").trim();\n\treturn createHash(\"sha256\").update(`${layer}\\n${normalized}`).digest(\"hex\").slice(0, 24);\n}\n\nexport class ObservationStore {\n\tprivate readonly filePath: string;\n\tprivate readonly readOnly: boolean;\n\n\tconstructor(filePath: string, options?: { readOnly?: boolean }) {\n\t\tthis.filePath = filePath;\n\t\tthis.readOnly = options?.readOnly ?? isWorkerSession();\n\t}\n\n\tstatic forAgentDir(agentDir: string, options?: { readOnly?: boolean }): ObservationStore {\n\t\treturn new ObservationStore(stateFile(agentDir, \"learning-observations.json\"), options);\n\t}\n\n\tprivate load(): ObservationStoreFile {\n\t\ttry {\n\t\t\tif (!existsSync(this.filePath)) return { version: 1, observations: {} };\n\t\t\tconst parsed = JSON.parse(readFileSync(this.filePath, \"utf-8\")) as ObservationStoreFile;\n\t\t\tif (\n\t\t\t\tparsed &&\n\t\t\t\tparsed.version === 1 &&\n\t\t\t\tparsed.observations &&\n\t\t\t\ttypeof parsed.observations === \"object\" &&\n\t\t\t\t!Array.isArray(parsed.observations)\n\t\t\t) {\n\t\t\t\t// Sanitize per-entry so a partially-mangled file still yields a usable store rather than\n\t\t\t\t// leaking NaN/undefined counts into the gate.\n\t\t\t\tconst clean: ObservationStoreFile = { version: 1, observations: {} };\n\t\t\t\tfor (const [key, value] of Object.entries(parsed.observations)) {\n\t\t\t\t\tif (\n\t\t\t\t\t\tvalue &&\n\t\t\t\t\t\ttypeof value === \"object\" &&\n\t\t\t\t\t\ttypeof (value as ObservationEntry).count === \"number\" &&\n\t\t\t\t\t\tNumber.isFinite((value as ObservationEntry).count) &&\n\t\t\t\t\t\ttypeof (value as ObservationEntry).lastAt === \"string\"\n\t\t\t\t\t) {\n\t\t\t\t\t\tclean.observations[key] = {\n\t\t\t\t\t\t\tcount: Math.min(Math.max(0, Math.floor((value as ObservationEntry).count)), MAX_COUNT),\n\t\t\t\t\t\t\tlastAt: (value as ObservationEntry).lastAt,\n\t\t\t\t\t\t};\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn clean;\n\t\t\t}\n\t\t} catch {\n\t\t\t// Unreadable/corrupt store: start fresh in memory; the next increment rewrites the file.\n\t\t}\n\t\treturn { version: 1, observations: {} };\n\t}\n\n\tprivate save(file: ObservationStoreFile): void {\n\t\twriteFileAtomicSync(this.filePath, `${JSON.stringify(file, null, \"\\t\")}\\n`);\n\t}\n\n\t/** Evict least-recently-incremented keys until the store is back within {@link MAX_KEYS}. */\n\tprivate evict(file: ObservationStoreFile): void {\n\t\tconst keys = Object.keys(file.observations);\n\t\tif (keys.length <= MAX_KEYS) return;\n\t\tkeys.sort((a, b) => {\n\t\t\tconst la = file.observations[a]!.lastAt;\n\t\t\tconst lb = file.observations[b]!.lastAt;\n\t\t\treturn la < lb ? -1 : la > lb ? 1 : 0;\n\t\t});\n\t\tfor (const key of keys.slice(0, keys.length - MAX_KEYS)) {\n\t\t\tdelete file.observations[key];\n\t\t}\n\t}\n\n\t/**\n\t * Record one more observation of `key` and return the new (capped) count. Load-mutate-save runs\n\t * under a single exclusive lock so two concurrent increments (e.g. two reflection passes across\n\t * sessions sharing an agentDir) can't both read the old count and drop one increment.\n\t */\n\tincrement(key: string, at?: string): number {\n\t\tif (this.readOnly) {\n\t\t\t// Zero-footprint (worker session): no lock, no dir, no write -- just the pure-read\n\t\t\t// equivalent of what a real increment would have returned.\n\t\t\tconst file = this.load();\n\t\t\treturn Math.min((file.observations[key]?.count ?? 0) + 1, MAX_COUNT);\n\t\t}\n\t\tconst now = at ?? new Date().toISOString();\n\t\treturn withFileLockSync(this.filePath, () => {\n\t\t\tconst file = this.load();\n\t\t\tconst count = Math.min((file.observations[key]?.count ?? 0) + 1, MAX_COUNT);\n\t\t\tfile.observations[key] = { count, lastAt: now };\n\t\t\tthis.evict(file);\n\t\t\tthis.save(file);\n\t\t\treturn count;\n\t\t});\n\t}\n\n\t/** Current observation count for `key` (0 if never observed). */\n\tget(key: string): number {\n\t\treturn this.load().observations[key]?.count ?? 0;\n\t}\n}\n"]}
@@ -1,10 +1,8 @@
1
1
  import type { SessionEntry, SessionManager } from "@caupulican/pi-agent-core/node";
2
2
  import type { LearningDecision } from "../autonomy/contracts.ts";
3
+ import { type SessionSnapshotPayload } from "../session-snapshot.ts";
3
4
  export declare const LEARNING_DECISION_CUSTOM_TYPE = "learning_decision";
4
- export interface LearningDecisionSnapshotPayload {
5
- version: 1;
6
- decision: LearningDecision;
7
- }
5
+ export type LearningDecisionSnapshotPayload = SessionSnapshotPayload<"decision", LearningDecision>;
8
6
  export declare function appendLearningDecisionSnapshot(sessionManager: Pick<SessionManager, "appendCustomEntry">, decision: LearningDecision): string;
9
7
  export declare function getLearningDecisionSnapshots(entries: readonly SessionEntry[]): LearningDecision[];
10
8
  //# sourceMappingURL=session-learning-decision.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"session-learning-decision.d.ts","sourceRoot":"","sources":["../../../src/core/learning/session-learning-decision.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAC;AACnF,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAGjE,eAAO,MAAM,6BAA6B,sBAAsB,CAAC;AAEjE,MAAM,WAAW,+BAA+B;IAC/C,OAAO,EAAE,CAAC,CAAC;IACX,QAAQ,EAAE,gBAAgB,CAAC;CAC3B;AAED,wBAAgB,8BAA8B,CAC7C,cAAc,EAAE,IAAI,CAAC,cAAc,EAAE,mBAAmB,CAAC,EACzD,QAAQ,EAAE,gBAAgB,GACxB,MAAM,CAMR;AAQD,wBAAgB,4BAA4B,CAAC,OAAO,EAAE,SAAS,YAAY,EAAE,GAAG,gBAAgB,EAAE,CAmBjG","sourcesContent":["import type { SessionEntry, SessionManager } from \"@caupulican/pi-agent-core/node\";\nimport type { LearningDecision } from \"../autonomy/contracts.ts\";\nimport { cloneLearningDecisionForStorage, isLearningDecision } from \"./learning-gate.ts\";\n\nexport const LEARNING_DECISION_CUSTOM_TYPE = \"learning_decision\";\n\nexport interface LearningDecisionSnapshotPayload {\n\tversion: 1;\n\tdecision: LearningDecision;\n}\n\nexport function appendLearningDecisionSnapshot(\n\tsessionManager: Pick<SessionManager, \"appendCustomEntry\">,\n\tdecision: LearningDecision,\n): string {\n\tconst payload: LearningDecisionSnapshotPayload = {\n\t\tversion: 1,\n\t\tdecision: cloneLearningDecisionForStorage(decision),\n\t};\n\treturn sessionManager.appendCustomEntry(LEARNING_DECISION_CUSTOM_TYPE, payload);\n}\n\nfunction isPlainRecord(value: unknown): value is Record<string, unknown> {\n\tif (!value || typeof value !== \"object\" || Array.isArray(value)) return false;\n\tconst prototype = Object.getPrototypeOf(value);\n\treturn prototype === Object.prototype || prototype === null;\n}\n\nexport function getLearningDecisionSnapshots(entries: readonly SessionEntry[]): LearningDecision[] {\n\tconst results: LearningDecision[] = [];\n\n\tfor (const entry of entries) {\n\t\tif (entry.type !== \"custom\" || entry.customType !== LEARNING_DECISION_CUSTOM_TYPE) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst payload = entry.data;\n\t\tif (!isPlainRecord(payload)) continue;\n\t\tif (payload.version !== 1) continue;\n\t\tif (!(\"decision\" in payload)) continue;\n\t\tconst decision = payload.decision;\n\t\tif (isLearningDecision(decision)) {\n\t\t\tresults.push(cloneLearningDecisionForStorage(decision));\n\t\t}\n\t}\n\n\treturn results;\n}\n"]}
1
+ {"version":3,"file":"session-learning-decision.d.ts","sourceRoot":"","sources":["../../../src/core/learning/session-learning-decision.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAC;AACnF,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AACjE,OAAO,EAIN,KAAK,sBAAsB,EAC3B,MAAM,wBAAwB,CAAC;AAGhC,eAAO,MAAM,6BAA6B,sBAAsB,CAAC;AAEjE,MAAM,MAAM,+BAA+B,GAAG,sBAAsB,CAAC,UAAU,EAAE,gBAAgB,CAAC,CAAC;AASnG,wBAAgB,8BAA8B,CAC7C,cAAc,EAAE,IAAI,CAAC,cAAc,EAAE,mBAAmB,CAAC,EACzD,QAAQ,EAAE,gBAAgB,GACxB,MAAM,CAER;AAED,wBAAgB,4BAA4B,CAAC,OAAO,EAAE,SAAS,YAAY,EAAE,GAAG,gBAAgB,EAAE,CAEjG","sourcesContent":["import type { SessionEntry, SessionManager } from \"@caupulican/pi-agent-core/node\";\nimport type { LearningDecision } from \"../autonomy/contracts.ts\";\nimport {\n\tappendSessionSnapshot,\n\tgetSessionSnapshots,\n\ttype SessionSnapshotCodec,\n\ttype SessionSnapshotPayload,\n} from \"../session-snapshot.ts\";\nimport { cloneLearningDecisionForStorage, isLearningDecision } from \"./learning-gate.ts\";\n\nexport const LEARNING_DECISION_CUSTOM_TYPE = \"learning_decision\";\n\nexport type LearningDecisionSnapshotPayload = SessionSnapshotPayload<\"decision\", LearningDecision>;\n\nconst LEARNING_DECISION_SNAPSHOT_CODEC: SessionSnapshotCodec<LearningDecision, \"decision\"> = {\n\tcustomType: LEARNING_DECISION_CUSTOM_TYPE,\n\tvalueKey: \"decision\",\n\tisValue: isLearningDecision,\n\tclone: cloneLearningDecisionForStorage,\n};\n\nexport function appendLearningDecisionSnapshot(\n\tsessionManager: Pick<SessionManager, \"appendCustomEntry\">,\n\tdecision: LearningDecision,\n): string {\n\treturn appendSessionSnapshot(sessionManager, LEARNING_DECISION_SNAPSHOT_CODEC, decision);\n}\n\nexport function getLearningDecisionSnapshots(entries: readonly SessionEntry[]): LearningDecision[] {\n\treturn getSessionSnapshots(entries, LEARNING_DECISION_SNAPSHOT_CODEC);\n}\n"]}
@@ -1,36 +1,16 @@
1
+ import { appendSessionSnapshot, getSessionSnapshots, } from "../session-snapshot.js";
1
2
  import { cloneLearningDecisionForStorage, isLearningDecision } from "./learning-gate.js";
2
3
  export const LEARNING_DECISION_CUSTOM_TYPE = "learning_decision";
4
+ const LEARNING_DECISION_SNAPSHOT_CODEC = {
5
+ customType: LEARNING_DECISION_CUSTOM_TYPE,
6
+ valueKey: "decision",
7
+ isValue: isLearningDecision,
8
+ clone: cloneLearningDecisionForStorage,
9
+ };
3
10
  export function appendLearningDecisionSnapshot(sessionManager, decision) {
4
- const payload = {
5
- version: 1,
6
- decision: cloneLearningDecisionForStorage(decision),
7
- };
8
- return sessionManager.appendCustomEntry(LEARNING_DECISION_CUSTOM_TYPE, payload);
9
- }
10
- function isPlainRecord(value) {
11
- if (!value || typeof value !== "object" || Array.isArray(value))
12
- return false;
13
- const prototype = Object.getPrototypeOf(value);
14
- return prototype === Object.prototype || prototype === null;
11
+ return appendSessionSnapshot(sessionManager, LEARNING_DECISION_SNAPSHOT_CODEC, decision);
15
12
  }
16
13
  export function getLearningDecisionSnapshots(entries) {
17
- const results = [];
18
- for (const entry of entries) {
19
- if (entry.type !== "custom" || entry.customType !== LEARNING_DECISION_CUSTOM_TYPE) {
20
- continue;
21
- }
22
- const payload = entry.data;
23
- if (!isPlainRecord(payload))
24
- continue;
25
- if (payload.version !== 1)
26
- continue;
27
- if (!("decision" in payload))
28
- continue;
29
- const decision = payload.decision;
30
- if (isLearningDecision(decision)) {
31
- results.push(cloneLearningDecisionForStorage(decision));
32
- }
33
- }
34
- return results;
14
+ return getSessionSnapshots(entries, LEARNING_DECISION_SNAPSHOT_CODEC);
35
15
  }
36
16
  //# sourceMappingURL=session-learning-decision.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"session-learning-decision.js","sourceRoot":"","sources":["../../../src/core/learning/session-learning-decision.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,+BAA+B,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAEzF,MAAM,CAAC,MAAM,6BAA6B,GAAG,mBAAmB,CAAC;AAOjE,MAAM,UAAU,8BAA8B,CAC7C,cAAyD,EACzD,QAA0B,EACjB;IACT,MAAM,OAAO,GAAoC;QAChD,OAAO,EAAE,CAAC;QACV,QAAQ,EAAE,+BAA+B,CAAC,QAAQ,CAAC;KACnD,CAAC;IACF,OAAO,cAAc,CAAC,iBAAiB,CAAC,6BAA6B,EAAE,OAAO,CAAC,CAAC;AAAA,CAChF;AAED,SAAS,aAAa,CAAC,KAAc,EAAoC;IACxE,IAAI,CAAC,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC;IAC9E,MAAM,SAAS,GAAG,MAAM,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC;IAC/C,OAAO,SAAS,KAAK,MAAM,CAAC,SAAS,IAAI,SAAS,KAAK,IAAI,CAAC;AAAA,CAC5D;AAED,MAAM,UAAU,4BAA4B,CAAC,OAAgC,EAAsB;IAClG,MAAM,OAAO,GAAuB,EAAE,CAAC;IAEvC,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;QAC7B,IAAI,KAAK,CAAC,IAAI,KAAK,QAAQ,IAAI,KAAK,CAAC,UAAU,KAAK,6BAA6B,EAAE,CAAC;YACnF,SAAS;QACV,CAAC;QAED,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,CAAC;QAC3B,IAAI,CAAC,aAAa,CAAC,OAAO,CAAC;YAAE,SAAS;QACtC,IAAI,OAAO,CAAC,OAAO,KAAK,CAAC;YAAE,SAAS;QACpC,IAAI,CAAC,CAAC,UAAU,IAAI,OAAO,CAAC;YAAE,SAAS;QACvC,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC;QAClC,IAAI,kBAAkB,CAAC,QAAQ,CAAC,EAAE,CAAC;YAClC,OAAO,CAAC,IAAI,CAAC,+BAA+B,CAAC,QAAQ,CAAC,CAAC,CAAC;QACzD,CAAC;IACF,CAAC;IAED,OAAO,OAAO,CAAC;AAAA,CACf","sourcesContent":["import type { SessionEntry, SessionManager } from \"@caupulican/pi-agent-core/node\";\nimport type { LearningDecision } from \"../autonomy/contracts.ts\";\nimport { cloneLearningDecisionForStorage, isLearningDecision } from \"./learning-gate.ts\";\n\nexport const LEARNING_DECISION_CUSTOM_TYPE = \"learning_decision\";\n\nexport interface LearningDecisionSnapshotPayload {\n\tversion: 1;\n\tdecision: LearningDecision;\n}\n\nexport function appendLearningDecisionSnapshot(\n\tsessionManager: Pick<SessionManager, \"appendCustomEntry\">,\n\tdecision: LearningDecision,\n): string {\n\tconst payload: LearningDecisionSnapshotPayload = {\n\t\tversion: 1,\n\t\tdecision: cloneLearningDecisionForStorage(decision),\n\t};\n\treturn sessionManager.appendCustomEntry(LEARNING_DECISION_CUSTOM_TYPE, payload);\n}\n\nfunction isPlainRecord(value: unknown): value is Record<string, unknown> {\n\tif (!value || typeof value !== \"object\" || Array.isArray(value)) return false;\n\tconst prototype = Object.getPrototypeOf(value);\n\treturn prototype === Object.prototype || prototype === null;\n}\n\nexport function getLearningDecisionSnapshots(entries: readonly SessionEntry[]): LearningDecision[] {\n\tconst results: LearningDecision[] = [];\n\n\tfor (const entry of entries) {\n\t\tif (entry.type !== \"custom\" || entry.customType !== LEARNING_DECISION_CUSTOM_TYPE) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst payload = entry.data;\n\t\tif (!isPlainRecord(payload)) continue;\n\t\tif (payload.version !== 1) continue;\n\t\tif (!(\"decision\" in payload)) continue;\n\t\tconst decision = payload.decision;\n\t\tif (isLearningDecision(decision)) {\n\t\t\tresults.push(cloneLearningDecisionForStorage(decision));\n\t\t}\n\t}\n\n\treturn results;\n}\n"]}
1
+ {"version":3,"file":"session-learning-decision.js","sourceRoot":"","sources":["../../../src/core/learning/session-learning-decision.ts"],"names":[],"mappings":"AAEA,OAAO,EACN,qBAAqB,EACrB,mBAAmB,GAGnB,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAE,+BAA+B,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAEzF,MAAM,CAAC,MAAM,6BAA6B,GAAG,mBAAmB,CAAC;AAIjE,MAAM,gCAAgC,GAAuD;IAC5F,UAAU,EAAE,6BAA6B;IACzC,QAAQ,EAAE,UAAU;IACpB,OAAO,EAAE,kBAAkB;IAC3B,KAAK,EAAE,+BAA+B;CACtC,CAAC;AAEF,MAAM,UAAU,8BAA8B,CAC7C,cAAyD,EACzD,QAA0B,EACjB;IACT,OAAO,qBAAqB,CAAC,cAAc,EAAE,gCAAgC,EAAE,QAAQ,CAAC,CAAC;AAAA,CACzF;AAED,MAAM,UAAU,4BAA4B,CAAC,OAAgC,EAAsB;IAClG,OAAO,mBAAmB,CAAC,OAAO,EAAE,gCAAgC,CAAC,CAAC;AAAA,CACtE","sourcesContent":["import type { SessionEntry, SessionManager } from \"@caupulican/pi-agent-core/node\";\nimport type { LearningDecision } from \"../autonomy/contracts.ts\";\nimport {\n\tappendSessionSnapshot,\n\tgetSessionSnapshots,\n\ttype SessionSnapshotCodec,\n\ttype SessionSnapshotPayload,\n} from \"../session-snapshot.ts\";\nimport { cloneLearningDecisionForStorage, isLearningDecision } from \"./learning-gate.ts\";\n\nexport const LEARNING_DECISION_CUSTOM_TYPE = \"learning_decision\";\n\nexport type LearningDecisionSnapshotPayload = SessionSnapshotPayload<\"decision\", LearningDecision>;\n\nconst LEARNING_DECISION_SNAPSHOT_CODEC: SessionSnapshotCodec<LearningDecision, \"decision\"> = {\n\tcustomType: LEARNING_DECISION_CUSTOM_TYPE,\n\tvalueKey: \"decision\",\n\tisValue: isLearningDecision,\n\tclone: cloneLearningDecisionForStorage,\n};\n\nexport function appendLearningDecisionSnapshot(\n\tsessionManager: Pick<SessionManager, \"appendCustomEntry\">,\n\tdecision: LearningDecision,\n): string {\n\treturn appendSessionSnapshot(sessionManager, LEARNING_DECISION_SNAPSHOT_CODEC, decision);\n}\n\nexport function getLearningDecisionSnapshots(entries: readonly SessionEntry[]): LearningDecision[] {\n\treturn getSessionSnapshots(entries, LEARNING_DECISION_SNAPSHOT_CODEC);\n}\n"]}
@@ -54,7 +54,11 @@ export declare class SkillCurator {
54
54
  private readonly archiveDir;
55
55
  private readonly usageFile;
56
56
  constructor(skillsDir: string);
57
- /** Record that a promoted skill was loaded/used (bumps count + last-used). Best-effort. */
57
+ /**
58
+ * Record that a promoted skill was loaded/used (bumps count + last-used). Best-effort.
59
+ * Load-mutate-write runs under a single exclusive lock so two concurrent uses (e.g. two sessions
60
+ * loading the same skill around the same time) can't both read the old usage map and drop a count.
61
+ */
58
62
  recordUse(name: string, now: number): void;
59
63
  /** Build the proposals from the current promoted-skill corpus. */
60
64
  proposeCuration(now: number, options?: Partial<Omit<CuratorOptions, "now">>): CurationProposals;
@@ -1 +1 @@
1
- {"version":3,"file":"skill-curator.d.ts","sourceRoot":"","sources":["../../../src/core/learning/skill-curator.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAOH,uFAAqF;AACrF,MAAM,WAAW,iBAAiB;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,yGAAyG;IACzG,SAAS,EAAE,MAAM,CAAC;IAClB,uEAAuE;IACvE,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,gEAAgE;IAChE,QAAQ,EAAE,MAAM,EAAE,CAAC;CACnB;AAED,MAAM,WAAW,cAAc;IAC9B,kGAAkG;IAClG,SAAS,EAAE,MAAM,CAAC;IAClB,oGAAkG;IAClG,gBAAgB,EAAE,MAAM,CAAC;IACzB,mFAAmF;IACnF,GAAG,EAAE,MAAM,CAAC;CACZ;AAED,eAAO,MAAM,uBAAuB,EAAE,IAAI,CAAC,cAAc,EAAE,KAAK,CAG/D,CAAC;AAEF,MAAM,WAAW,iBAAiB;IACjC,+EAA+E;IAC/E,OAAO,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACjD,sGAAsG;IACtG,WAAW,EAAE,KAAK,CAAC;QAAE,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACjE;AAED;;;GAGG;AACH,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,iBAAiB,EAAE,EAAE,IAAI,EAAE,cAAc,GAAG,iBAAiB,CAgC7G;AAWD;;;;GAIG;AACH,qBAAa,YAAY;IACxB,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IAEnC,YAAY,SAAS,EAAE,MAAM,EAI5B;IAED,2FAA2F;IAC3F,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI,CASzC;IAED,kEAAkE;IAClE,eAAe,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,GAAE,OAAO,CAAC,IAAI,CAAC,cAAc,EAAE,KAAK,CAAC,CAAM,GAAG,iBAAiB,CAOlG;IAED;;;;;OAKG;IACG,gBAAgB,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,GAAE,OAAO,CAAC,IAAI,CAAC,cAAc,EAAE,KAAK,CAAC,CAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAezG;IAED,qFAAqF;IACrF,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAUlC;IAED,2FAA2F;IAC3F,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAUlC;IAED,kBAAkB,IAAI,iBAAiB,EAAE,CA+BxC;IAED,OAAO,CAAC,UAAU;IAQlB,OAAO,CAAC,SAAS;CAOjB;AAED,8FAA8F;AAC9F,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAI9D","sourcesContent":["/**\n * Skill curator (Hermes-parity #32). Reflection (R7) promotes recurring procedures into SKILL.md files;\n * without curation they accumulate forever, bloating tool/context and raising per-turn cost. The curator\n * tracks usage of PROMOTED skills (frontmatter `promoted: true`) and PROPOSES — never auto-applies —\n * archiving stale ones and consolidating overlapping ones. Hand-authored user skills are never touched.\n *\n * Design (locked with agy): propose-only, session-start + idle triggers (not per-turn), restorable\n * archive (non-destructive), and consolidation is a flagged suggestion (never an auto-merge).\n */\n\nimport { existsSync, mkdirSync, readdirSync, readFileSync, renameSync, statSync, writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport lockfile from \"proper-lockfile\";\nimport { jaccard, tokenize } from \"../tools/skill-audit.ts\";\n\n/** Per-promoted-skill signal the proposal logic reasons over. Pure data — no I/O. */\nexport interface PromotedSkillInfo {\n\tname: string;\n\t/** When the skill file was created (ms epoch); guards a freshly-promoted skill from instant archival. */\n\tcreatedMs: number;\n\t/** Last time the skill was loaded/used (ms epoch); 0 if never used. */\n\tlastUsedMs: number;\n\tuseCount: number;\n\t/** Tokens from name+description+body, for overlap detection. */\n\tkeywords: string[];\n}\n\nexport interface CuratorOptions {\n\t/** A promoted skill unused and older than this many days is proposed for archival. Default 30. */\n\tstaleDays: number;\n\t/** Token-Jaccard ≥ this between two promoted skills flags them for consolidation. Default 0.5. */\n\toverlapThreshold: number;\n\t/** Current time (ms epoch); injected so the proposal logic stays pure/testable. */\n\tnow: number;\n}\n\nexport const DEFAULT_CURATOR_OPTIONS: Omit<CuratorOptions, \"now\"> = {\n\tstaleDays: 30,\n\toverlapThreshold: 0.5,\n};\n\nexport interface CurationProposals {\n\t/** Promoted skills proposed for (restorable) archival, with a human reason. */\n\tarchive: Array<{ name: string; reason: string }>;\n\t/** Pairs of promoted skills that overlap enough to consider merging (flag only, never auto-merge). */\n\tconsolidate: Array<{ names: [string, string]; overlap: number }>;\n}\n\n/**\n * Pure proposal logic: decide which promoted skills to PROPOSE archiving (stale + unused) and which pairs\n * overlap enough to PROPOSE consolidating. Returns suggestions only; the caller applies them on approval.\n */\nexport function computeCurationProposals(skills: PromotedSkillInfo[], opts: CuratorOptions): CurationProposals {\n\tconst staleMs = opts.staleDays * 86_400_000;\n\tconst archive: CurationProposals[\"archive\"] = [];\n\tfor (const s of skills) {\n\t\t// \"Stale\" = never recently used AND not freshly promoted: measure age from the most recent of\n\t\t// last-use / creation so a brand-new skill isn't archived before it has had a chance to be used.\n\t\tconst lastSeen = Math.max(s.lastUsedMs, s.createdMs);\n\t\tconst ageMs = opts.now - lastSeen;\n\t\tif (ageMs > staleMs) {\n\t\t\tconst days = Math.floor(ageMs / 86_400_000);\n\t\t\tarchive.push({\n\t\t\t\tname: s.name,\n\t\t\t\treason: s.useCount === 0 ? `never used, ${days}d old` : `unused for ${days}d (${s.useCount} total uses)`,\n\t\t\t});\n\t\t}\n\t}\n\n\tconst consolidate: CurationProposals[\"consolidate\"] = [];\n\tconst archiving = new Set(archive.map((a) => a.name));\n\tfor (let i = 0; i < skills.length; i++) {\n\t\tfor (let j = i + 1; j < skills.length; j++) {\n\t\t\tconst a = skills[i];\n\t\t\tconst b = skills[j];\n\t\t\t// Don't propose consolidating something already proposed for archival.\n\t\t\tif (archiving.has(a.name) || archiving.has(b.name)) continue;\n\t\t\tconst overlap = jaccard(a.keywords, b.keywords);\n\t\t\tif (overlap >= opts.overlapThreshold) {\n\t\t\t\tconsolidate.push({ names: [a.name, b.name], overlap });\n\t\t\t}\n\t\t}\n\t}\n\treturn { archive, consolidate };\n}\n\ninterface UsageRecord {\n\tlastUsedMs: number;\n\tuseCount: number;\n}\ntype UsageMap = Record<string, UsageRecord>;\n\n/** Cap on how much of a skill body feeds keyword extraction (keeps overlap detection cheap). */\nconst KEYWORD_SOURCE_CAP = 4000;\n\n/**\n * Filesystem layer over {@link computeCurationProposals}: reads promoted SKILL.md files + the usage\n * sidecar, and archives/restores skills non-destructively. The current time is injected so callers (and\n * tests) control \"now\".\n */\nexport class SkillCurator {\n\tprivate readonly skillsDir: string;\n\tprivate readonly archiveDir: string;\n\tprivate readonly usageFile: string;\n\n\tconstructor(skillsDir: string) {\n\t\tthis.skillsDir = skillsDir;\n\t\tthis.archiveDir = join(skillsDir, \".archive\");\n\t\tthis.usageFile = join(skillsDir, \".usage.json\");\n\t}\n\n\t/** Record that a promoted skill was loaded/used (bumps count + last-used). Best-effort. */\n\trecordUse(name: string, now: number): void {\n\t\ttry {\n\t\t\tconst usage = this.loadUsage();\n\t\t\tconst prev = usage[name] ?? { lastUsedMs: 0, useCount: 0 };\n\t\t\tusage[name] = { lastUsedMs: now, useCount: prev.useCount + 1 };\n\t\t\twriteFileSync(this.usageFile, JSON.stringify(usage, null, 2), \"utf-8\");\n\t\t} catch {\n\t\t\t// usage tracking must never disrupt a turn\n\t\t}\n\t}\n\n\t/** Build the proposals from the current promoted-skill corpus. */\n\tproposeCuration(now: number, options: Partial<Omit<CuratorOptions, \"now\">> = {}): CurationProposals {\n\t\tconst skills = this.loadPromotedSkills();\n\t\treturn computeCurationProposals(skills, {\n\t\t\tnow,\n\t\t\tstaleDays: options.staleDays ?? DEFAULT_CURATOR_OPTIONS.staleDays,\n\t\t\toverlapThreshold: options.overlapThreshold ?? DEFAULT_CURATOR_OPTIONS.overlapThreshold,\n\t\t});\n\t}\n\n\t/**\n\t * Auto-archive every stale promoted skill in ONE locked batch (#32 default-on). The lock serializes\n\t * curation across sessions sharing this `agentDir` so a concurrent session can't archive a folder\n\t * mid-rename (agy's race mitigation). Returns the names archived (for the host to announce). Never\n\t * throws; on lock contention it simply does nothing this run.\n\t */\n\tasync autoArchiveStale(now: number, options: Partial<Omit<CuratorOptions, \"now\">> = {}): Promise<string[]> {\n\t\tif (!existsSync(this.skillsDir)) return [];\n\t\tlet release: (() => Promise<void>) | undefined;\n\t\ttry {\n\t\t\trelease = await lockfile.lock(this.skillsDir, { realpath: false, retries: 2 });\n\t\t\tconst archived: string[] = [];\n\t\t\tfor (const a of this.proposeCuration(now, options).archive) {\n\t\t\t\tif (this.archiveSkill(a.name)) archived.push(a.name);\n\t\t\t}\n\t\t\treturn archived;\n\t\t} catch {\n\t\t\treturn []; // another session holds the lock, or fs error — skip this run\n\t\t} finally {\n\t\t\tif (release) await release().catch(() => {});\n\t\t}\n\t}\n\n\t/** Move a promoted skill into `.archive/` (restorable). Returns true if archived. */\n\tarchiveSkill(name: string): boolean {\n\t\ttry {\n\t\t\tconst from = join(this.skillsDir, name);\n\t\t\tif (!existsSync(join(from, \"SKILL.md\")) || !this.isPromoted(name)) return false;\n\t\t\tmkdirSync(this.archiveDir, { recursive: true });\n\t\t\trenameSync(from, join(this.archiveDir, name));\n\t\t\treturn true;\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t/** Restore an archived skill back into the active skills dir. Returns true if restored. */\n\trestoreSkill(name: string): boolean {\n\t\ttry {\n\t\t\tconst from = join(this.archiveDir, name);\n\t\t\tconst to = join(this.skillsDir, name);\n\t\t\tif (!existsSync(join(from, \"SKILL.md\")) || existsSync(to)) return false;\n\t\t\trenameSync(from, to);\n\t\t\treturn true;\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tloadPromotedSkills(): PromotedSkillInfo[] {\n\t\tconst out: PromotedSkillInfo[] = [];\n\t\tlet entries: string[];\n\t\ttry {\n\t\t\tentries = readdirSync(this.skillsDir);\n\t\t} catch {\n\t\t\treturn out;\n\t\t}\n\t\tconst usage = this.loadUsage();\n\t\tfor (const name of entries) {\n\t\t\tif (name.startsWith(\".\")) continue; // skip .archive, .usage.json\n\t\t\tconst file = join(this.skillsDir, name, \"SKILL.md\");\n\t\t\tlet raw: string;\n\t\t\tlet createdMs = 0;\n\t\t\ttry {\n\t\t\t\traw = readFileSync(file, \"utf-8\");\n\t\t\t\tcreatedMs = statSync(file).birthtimeMs || statSync(file).mtimeMs;\n\t\t\t} catch {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (!isPromotedFrontmatter(raw)) continue;\n\t\t\tconst u = usage[name] ?? { lastUsedMs: 0, useCount: 0 };\n\t\t\tout.push({\n\t\t\t\tname,\n\t\t\t\tcreatedMs,\n\t\t\t\tlastUsedMs: u.lastUsedMs,\n\t\t\t\tuseCount: u.useCount,\n\t\t\t\tkeywords: tokenize(raw.slice(0, KEYWORD_SOURCE_CAP)),\n\t\t\t});\n\t\t}\n\t\treturn out;\n\t}\n\n\tprivate isPromoted(name: string): boolean {\n\t\ttry {\n\t\t\treturn isPromotedFrontmatter(readFileSync(join(this.skillsDir, name, \"SKILL.md\"), \"utf-8\"));\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tprivate loadUsage(): UsageMap {\n\t\ttry {\n\t\t\treturn JSON.parse(readFileSync(this.usageFile, \"utf-8\")) as UsageMap;\n\t\t} catch {\n\t\t\treturn {};\n\t\t}\n\t}\n}\n\n/** True if a SKILL.md's YAML frontmatter declares `promoted: true` (reflection-generated). */\nexport function isPromotedFrontmatter(content: string): boolean {\n\tconst fm = content.match(/^---\\n([\\s\\S]*?)\\n---/);\n\tif (!fm) return false;\n\treturn /^\\s*promoted\\s*:\\s*true\\s*$/im.test(fm[1]);\n}\n"]}
1
+ {"version":3,"file":"skill-curator.d.ts","sourceRoot":"","sources":["../../../src/core/learning/skill-curator.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAOH,uFAAqF;AACrF,MAAM,WAAW,iBAAiB;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,yGAAyG;IACzG,SAAS,EAAE,MAAM,CAAC;IAClB,uEAAuE;IACvE,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,gEAAgE;IAChE,QAAQ,EAAE,MAAM,EAAE,CAAC;CACnB;AAED,MAAM,WAAW,cAAc;IAC9B,kGAAkG;IAClG,SAAS,EAAE,MAAM,CAAC;IAClB,oGAAkG;IAClG,gBAAgB,EAAE,MAAM,CAAC;IACzB,mFAAmF;IACnF,GAAG,EAAE,MAAM,CAAC;CACZ;AAED,eAAO,MAAM,uBAAuB,EAAE,IAAI,CAAC,cAAc,EAAE,KAAK,CAG/D,CAAC;AAEF,MAAM,WAAW,iBAAiB;IACjC,+EAA+E;IAC/E,OAAO,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACjD,sGAAsG;IACtG,WAAW,EAAE,KAAK,CAAC;QAAE,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACjE;AAED;;;GAGG;AACH,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,iBAAiB,EAAE,EAAE,IAAI,EAAE,cAAc,GAAG,iBAAiB,CAgC7G;AAWD;;;;GAIG;AACH,qBAAa,YAAY;IACxB,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IAEnC,YAAY,SAAS,EAAE,MAAM,EAI5B;IAED;;;;OAIG;IACH,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI,CAWzC;IAED,kEAAkE;IAClE,eAAe,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,GAAE,OAAO,CAAC,IAAI,CAAC,cAAc,EAAE,KAAK,CAAC,CAAM,GAAG,iBAAiB,CAOlG;IAED;;;;;OAKG;IACG,gBAAgB,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,GAAE,OAAO,CAAC,IAAI,CAAC,cAAc,EAAE,KAAK,CAAC,CAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAiBzG;IAED,qFAAqF;IACrF,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAUlC;IAED,2FAA2F;IAC3F,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAUlC;IAED,kBAAkB,IAAI,iBAAiB,EAAE,CA+BxC;IAED,OAAO,CAAC,UAAU;IAQlB,OAAO,CAAC,SAAS;CAOjB;AAED,8FAA8F;AAC9F,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAI9D","sourcesContent":["/**\n * Skill curator (Hermes-parity #32). Reflection (R7) promotes recurring procedures into SKILL.md files;\n * without curation they accumulate forever, bloating tool/context and raising per-turn cost. The curator\n * tracks usage of PROMOTED skills (frontmatter `promoted: true`) and PROPOSES — never auto-applies —\n * archiving stale ones and consolidating overlapping ones. Hand-authored user skills are never touched.\n *\n * Design (locked with agy): propose-only, session-start + idle triggers (not per-turn), restorable\n * archive (non-destructive), and consolidation is a flagged suggestion (never an auto-merge).\n */\n\nimport { existsSync, mkdirSync, readdirSync, readFileSync, renameSync, statSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { jaccard, tokenize } from \"../tools/skill-audit.ts\";\nimport { withFileLock, withFileLockSync, writeFileAtomicSync } from \"../util/atomic-file.ts\";\n\n/** Per-promoted-skill signal the proposal logic reasons over. Pure data — no I/O. */\nexport interface PromotedSkillInfo {\n\tname: string;\n\t/** When the skill file was created (ms epoch); guards a freshly-promoted skill from instant archival. */\n\tcreatedMs: number;\n\t/** Last time the skill was loaded/used (ms epoch); 0 if never used. */\n\tlastUsedMs: number;\n\tuseCount: number;\n\t/** Tokens from name+description+body, for overlap detection. */\n\tkeywords: string[];\n}\n\nexport interface CuratorOptions {\n\t/** A promoted skill unused and older than this many days is proposed for archival. Default 30. */\n\tstaleDays: number;\n\t/** Token-Jaccard ≥ this between two promoted skills flags them for consolidation. Default 0.5. */\n\toverlapThreshold: number;\n\t/** Current time (ms epoch); injected so the proposal logic stays pure/testable. */\n\tnow: number;\n}\n\nexport const DEFAULT_CURATOR_OPTIONS: Omit<CuratorOptions, \"now\"> = {\n\tstaleDays: 30,\n\toverlapThreshold: 0.5,\n};\n\nexport interface CurationProposals {\n\t/** Promoted skills proposed for (restorable) archival, with a human reason. */\n\tarchive: Array<{ name: string; reason: string }>;\n\t/** Pairs of promoted skills that overlap enough to consider merging (flag only, never auto-merge). */\n\tconsolidate: Array<{ names: [string, string]; overlap: number }>;\n}\n\n/**\n * Pure proposal logic: decide which promoted skills to PROPOSE archiving (stale + unused) and which pairs\n * overlap enough to PROPOSE consolidating. Returns suggestions only; the caller applies them on approval.\n */\nexport function computeCurationProposals(skills: PromotedSkillInfo[], opts: CuratorOptions): CurationProposals {\n\tconst staleMs = opts.staleDays * 86_400_000;\n\tconst archive: CurationProposals[\"archive\"] = [];\n\tfor (const s of skills) {\n\t\t// \"Stale\" = never recently used AND not freshly promoted: measure age from the most recent of\n\t\t// last-use / creation so a brand-new skill isn't archived before it has had a chance to be used.\n\t\tconst lastSeen = Math.max(s.lastUsedMs, s.createdMs);\n\t\tconst ageMs = opts.now - lastSeen;\n\t\tif (ageMs > staleMs) {\n\t\t\tconst days = Math.floor(ageMs / 86_400_000);\n\t\t\tarchive.push({\n\t\t\t\tname: s.name,\n\t\t\t\treason: s.useCount === 0 ? `never used, ${days}d old` : `unused for ${days}d (${s.useCount} total uses)`,\n\t\t\t});\n\t\t}\n\t}\n\n\tconst consolidate: CurationProposals[\"consolidate\"] = [];\n\tconst archiving = new Set(archive.map((a) => a.name));\n\tfor (let i = 0; i < skills.length; i++) {\n\t\tfor (let j = i + 1; j < skills.length; j++) {\n\t\t\tconst a = skills[i];\n\t\t\tconst b = skills[j];\n\t\t\t// Don't propose consolidating something already proposed for archival.\n\t\t\tif (archiving.has(a.name) || archiving.has(b.name)) continue;\n\t\t\tconst overlap = jaccard(a.keywords, b.keywords);\n\t\t\tif (overlap >= opts.overlapThreshold) {\n\t\t\t\tconsolidate.push({ names: [a.name, b.name], overlap });\n\t\t\t}\n\t\t}\n\t}\n\treturn { archive, consolidate };\n}\n\ninterface UsageRecord {\n\tlastUsedMs: number;\n\tuseCount: number;\n}\ntype UsageMap = Record<string, UsageRecord>;\n\n/** Cap on how much of a skill body feeds keyword extraction (keeps overlap detection cheap). */\nconst KEYWORD_SOURCE_CAP = 4000;\n\n/**\n * Filesystem layer over {@link computeCurationProposals}: reads promoted SKILL.md files + the usage\n * sidecar, and archives/restores skills non-destructively. The current time is injected so callers (and\n * tests) control \"now\".\n */\nexport class SkillCurator {\n\tprivate readonly skillsDir: string;\n\tprivate readonly archiveDir: string;\n\tprivate readonly usageFile: string;\n\n\tconstructor(skillsDir: string) {\n\t\tthis.skillsDir = skillsDir;\n\t\tthis.archiveDir = join(skillsDir, \".archive\");\n\t\tthis.usageFile = join(skillsDir, \".usage.json\");\n\t}\n\n\t/**\n\t * Record that a promoted skill was loaded/used (bumps count + last-used). Best-effort.\n\t * Load-mutate-write runs under a single exclusive lock so two concurrent uses (e.g. two sessions\n\t * loading the same skill around the same time) can't both read the old usage map and drop a count.\n\t */\n\trecordUse(name: string, now: number): void {\n\t\ttry {\n\t\t\twithFileLockSync(this.usageFile, () => {\n\t\t\t\tconst usage = this.loadUsage();\n\t\t\t\tconst prev = usage[name] ?? { lastUsedMs: 0, useCount: 0 };\n\t\t\t\tusage[name] = { lastUsedMs: now, useCount: prev.useCount + 1 };\n\t\t\t\twriteFileAtomicSync(this.usageFile, JSON.stringify(usage, null, 2));\n\t\t\t});\n\t\t} catch {\n\t\t\t// usage tracking must never disrupt a turn\n\t\t}\n\t}\n\n\t/** Build the proposals from the current promoted-skill corpus. */\n\tproposeCuration(now: number, options: Partial<Omit<CuratorOptions, \"now\">> = {}): CurationProposals {\n\t\tconst skills = this.loadPromotedSkills();\n\t\treturn computeCurationProposals(skills, {\n\t\t\tnow,\n\t\t\tstaleDays: options.staleDays ?? DEFAULT_CURATOR_OPTIONS.staleDays,\n\t\t\toverlapThreshold: options.overlapThreshold ?? DEFAULT_CURATOR_OPTIONS.overlapThreshold,\n\t\t});\n\t}\n\n\t/**\n\t * Auto-archive every stale promoted skill in ONE locked batch (#32 default-on). The lock serializes\n\t * curation across sessions sharing this `agentDir` so a concurrent session can't archive a folder\n\t * mid-rename (agy's race mitigation). Returns the names archived (for the host to announce). Never\n\t * throws; on lock contention it simply does nothing this run.\n\t */\n\tasync autoArchiveStale(now: number, options: Partial<Omit<CuratorOptions, \"now\">> = {}): Promise<string[]> {\n\t\tif (!existsSync(this.skillsDir)) return [];\n\t\ttry {\n\t\t\treturn await withFileLock(\n\t\t\t\tthis.skillsDir,\n\t\t\t\t() => {\n\t\t\t\t\tconst archived: string[] = [];\n\t\t\t\t\tfor (const a of this.proposeCuration(now, options).archive) {\n\t\t\t\t\t\tif (this.archiveSkill(a.name)) archived.push(a.name);\n\t\t\t\t\t}\n\t\t\t\t\treturn archived;\n\t\t\t\t},\n\t\t\t\t{ retries: 2 },\n\t\t\t);\n\t\t} catch {\n\t\t\treturn []; // another session holds the lock, or fs error — skip this run\n\t\t}\n\t}\n\n\t/** Move a promoted skill into `.archive/` (restorable). Returns true if archived. */\n\tarchiveSkill(name: string): boolean {\n\t\ttry {\n\t\t\tconst from = join(this.skillsDir, name);\n\t\t\tif (!existsSync(join(from, \"SKILL.md\")) || !this.isPromoted(name)) return false;\n\t\t\tmkdirSync(this.archiveDir, { recursive: true });\n\t\t\trenameSync(from, join(this.archiveDir, name));\n\t\t\treturn true;\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t/** Restore an archived skill back into the active skills dir. Returns true if restored. */\n\trestoreSkill(name: string): boolean {\n\t\ttry {\n\t\t\tconst from = join(this.archiveDir, name);\n\t\t\tconst to = join(this.skillsDir, name);\n\t\t\tif (!existsSync(join(from, \"SKILL.md\")) || existsSync(to)) return false;\n\t\t\trenameSync(from, to);\n\t\t\treturn true;\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tloadPromotedSkills(): PromotedSkillInfo[] {\n\t\tconst out: PromotedSkillInfo[] = [];\n\t\tlet entries: string[];\n\t\ttry {\n\t\t\tentries = readdirSync(this.skillsDir);\n\t\t} catch {\n\t\t\treturn out;\n\t\t}\n\t\tconst usage = this.loadUsage();\n\t\tfor (const name of entries) {\n\t\t\tif (name.startsWith(\".\")) continue; // skip .archive, .usage.json\n\t\t\tconst file = join(this.skillsDir, name, \"SKILL.md\");\n\t\t\tlet raw: string;\n\t\t\tlet createdMs = 0;\n\t\t\ttry {\n\t\t\t\traw = readFileSync(file, \"utf-8\");\n\t\t\t\tcreatedMs = statSync(file).birthtimeMs || statSync(file).mtimeMs;\n\t\t\t} catch {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (!isPromotedFrontmatter(raw)) continue;\n\t\t\tconst u = usage[name] ?? { lastUsedMs: 0, useCount: 0 };\n\t\t\tout.push({\n\t\t\t\tname,\n\t\t\t\tcreatedMs,\n\t\t\t\tlastUsedMs: u.lastUsedMs,\n\t\t\t\tuseCount: u.useCount,\n\t\t\t\tkeywords: tokenize(raw.slice(0, KEYWORD_SOURCE_CAP)),\n\t\t\t});\n\t\t}\n\t\treturn out;\n\t}\n\n\tprivate isPromoted(name: string): boolean {\n\t\ttry {\n\t\t\treturn isPromotedFrontmatter(readFileSync(join(this.skillsDir, name, \"SKILL.md\"), \"utf-8\"));\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tprivate loadUsage(): UsageMap {\n\t\ttry {\n\t\t\treturn JSON.parse(readFileSync(this.usageFile, \"utf-8\")) as UsageMap;\n\t\t} catch {\n\t\t\treturn {};\n\t\t}\n\t}\n}\n\n/** True if a SKILL.md's YAML frontmatter declares `promoted: true` (reflection-generated). */\nexport function isPromotedFrontmatter(content: string): boolean {\n\tconst fm = content.match(/^---\\n([\\s\\S]*?)\\n---/);\n\tif (!fm) return false;\n\treturn /^\\s*promoted\\s*:\\s*true\\s*$/im.test(fm[1]);\n}\n"]}