@caupulican/pi-adaptative 0.80.103 → 0.81.0

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 (401) hide show
  1. package/CHANGELOG.md +120 -0
  2. package/dist/cli/session-picker.d.ts +1 -1
  3. package/dist/cli/session-picker.d.ts.map +1 -1
  4. package/dist/cli/session-picker.js.map +1 -1
  5. package/dist/cli.d.ts.map +1 -1
  6. package/dist/cli.js +7 -1
  7. package/dist/cli.js.map +1 -1
  8. package/dist/core/agent-session-runtime.d.ts +1 -1
  9. package/dist/core/agent-session-runtime.d.ts.map +1 -1
  10. package/dist/core/agent-session-runtime.js +6 -6
  11. package/dist/core/agent-session-runtime.js.map +1 -1
  12. package/dist/core/agent-session-services.d.ts +1 -1
  13. package/dist/core/agent-session-services.d.ts.map +1 -1
  14. package/dist/core/agent-session-services.js.map +1 -1
  15. package/dist/core/agent-session.d.ts +152 -512
  16. package/dist/core/agent-session.d.ts.map +1 -1
  17. package/dist/core/agent-session.js +635 -4590
  18. package/dist/core/agent-session.js.map +1 -1
  19. package/dist/core/autonomy/session-lane-record.d.ts +1 -1
  20. package/dist/core/autonomy/session-lane-record.d.ts.map +1 -1
  21. package/dist/core/autonomy/session-lane-record.js.map +1 -1
  22. package/dist/core/autonomy/status.d.ts +1 -0
  23. package/dist/core/autonomy/status.d.ts.map +1 -1
  24. package/dist/core/autonomy/status.js +1 -0
  25. package/dist/core/autonomy/status.js.map +1 -1
  26. package/dist/core/autonomy-telemetry.d.ts +80 -0
  27. package/dist/core/autonomy-telemetry.d.ts.map +1 -0
  28. package/dist/core/autonomy-telemetry.js +266 -0
  29. package/dist/core/autonomy-telemetry.js.map +1 -0
  30. package/dist/core/background-lane-controller.d.ts +186 -0
  31. package/dist/core/background-lane-controller.d.ts.map +1 -0
  32. package/dist/core/background-lane-controller.js +691 -0
  33. package/dist/core/background-lane-controller.js.map +1 -0
  34. package/dist/core/bash-execution-controller.d.ts +51 -0
  35. package/dist/core/bash-execution-controller.d.ts.map +1 -0
  36. package/dist/core/bash-execution-controller.js +96 -0
  37. package/dist/core/bash-execution-controller.js.map +1 -0
  38. package/dist/core/bash-executor.d.ts.map +1 -1
  39. package/dist/core/bash-executor.js +9 -2
  40. package/dist/core/bash-executor.js.map +1 -1
  41. package/dist/core/compaction-support.d.ts +56 -0
  42. package/dist/core/compaction-support.d.ts.map +1 -0
  43. package/dist/core/compaction-support.js +99 -0
  44. package/dist/core/compaction-support.js.map +1 -0
  45. package/dist/core/context/artifact-retrieval.d.ts +1 -1
  46. package/dist/core/context/artifact-retrieval.d.ts.map +1 -1
  47. package/dist/core/context/artifact-retrieval.js +1 -1
  48. package/dist/core/context/artifact-retrieval.js.map +1 -1
  49. package/dist/core/context/context-composition.d.ts.map +1 -1
  50. package/dist/core/context/context-composition.js +1 -1
  51. package/dist/core/context/context-composition.js.map +1 -1
  52. package/dist/core/context/tool-output-packer.d.ts +1 -1
  53. package/dist/core/context/tool-output-packer.d.ts.map +1 -1
  54. package/dist/core/context/tool-output-packer.js +1 -1
  55. package/dist/core/context/tool-output-packer.js.map +1 -1
  56. package/dist/core/context-gc.d.ts.map +1 -1
  57. package/dist/core/context-gc.js +1 -1
  58. package/dist/core/context-gc.js.map +1 -1
  59. package/dist/core/context-pipeline.d.ts +223 -0
  60. package/dist/core/context-pipeline.d.ts.map +1 -0
  61. package/dist/core/context-pipeline.js +594 -0
  62. package/dist/core/context-pipeline.js.map +1 -0
  63. package/dist/core/cost/daily-usage.d.ts +1 -1
  64. package/dist/core/cost/daily-usage.d.ts.map +1 -1
  65. package/dist/core/cost/daily-usage.js +1 -1
  66. package/dist/core/cost/daily-usage.js.map +1 -1
  67. package/dist/core/cost/session-usage.d.ts +1 -1
  68. package/dist/core/cost/session-usage.d.ts.map +1 -1
  69. package/dist/core/cost/session-usage.js +1 -1
  70. package/dist/core/cost/session-usage.js.map +1 -1
  71. package/dist/core/delegation/session-worker-result.d.ts +1 -1
  72. package/dist/core/delegation/session-worker-result.d.ts.map +1 -1
  73. package/dist/core/delegation/session-worker-result.js.map +1 -1
  74. package/dist/core/exec.d.ts +2 -0
  75. package/dist/core/exec.d.ts.map +1 -1
  76. package/dist/core/exec.js +15 -9
  77. package/dist/core/exec.js.map +1 -1
  78. package/dist/core/export-html/index.d.ts +1 -1
  79. package/dist/core/export-html/index.d.ts.map +1 -1
  80. package/dist/core/export-html/index.js +2 -2
  81. package/dist/core/export-html/index.js.map +1 -1
  82. package/dist/core/extensions/builtin.d.ts.map +1 -1
  83. package/dist/core/extensions/builtin.js +2 -2
  84. package/dist/core/extensions/builtin.js.map +1 -1
  85. package/dist/core/extensions/runner.d.ts +1 -1
  86. package/dist/core/extensions/runner.d.ts.map +1 -1
  87. package/dist/core/extensions/runner.js.map +1 -1
  88. package/dist/core/extensions/types.d.ts +2 -4
  89. package/dist/core/extensions/types.d.ts.map +1 -1
  90. package/dist/core/extensions/types.js.map +1 -1
  91. package/dist/core/goal-loop-controller.d.ts +24 -0
  92. package/dist/core/goal-loop-controller.d.ts.map +1 -0
  93. package/dist/core/goal-loop-controller.js +83 -0
  94. package/dist/core/goal-loop-controller.js.map +1 -0
  95. package/dist/core/goals/goal-runtime-snapshot.d.ts +1 -1
  96. package/dist/core/goals/goal-runtime-snapshot.d.ts.map +1 -1
  97. package/dist/core/goals/goal-runtime-snapshot.js.map +1 -1
  98. package/dist/core/goals/session-goal-state.d.ts +1 -1
  99. package/dist/core/goals/session-goal-state.d.ts.map +1 -1
  100. package/dist/core/goals/session-goal-state.js.map +1 -1
  101. package/dist/core/index.d.ts +1 -1
  102. package/dist/core/index.d.ts.map +1 -1
  103. package/dist/core/index.js.map +1 -1
  104. package/dist/core/learning/learning-audit.d.ts +1 -1
  105. package/dist/core/learning/learning-audit.d.ts.map +1 -1
  106. package/dist/core/learning/learning-audit.js.map +1 -1
  107. package/dist/core/learning/session-learning-decision.d.ts +1 -1
  108. package/dist/core/learning/session-learning-decision.d.ts.map +1 -1
  109. package/dist/core/learning/session-learning-decision.js.map +1 -1
  110. package/dist/core/local-runtime-controller.d.ts +99 -0
  111. package/dist/core/local-runtime-controller.d.ts.map +1 -0
  112. package/dist/core/local-runtime-controller.js +214 -0
  113. package/dist/core/local-runtime-controller.js.map +1 -0
  114. package/dist/core/memory/providers/transcript-recall.d.ts.map +1 -1
  115. package/dist/core/memory/providers/transcript-recall.js +1 -1
  116. package/dist/core/memory/providers/transcript-recall.js.map +1 -1
  117. package/dist/core/memory-controller.d.ts +139 -0
  118. package/dist/core/memory-controller.d.ts.map +1 -0
  119. package/dist/core/memory-controller.js +323 -0
  120. package/dist/core/memory-controller.js.map +1 -0
  121. package/dist/core/model-router/status.d.ts +1 -1
  122. package/dist/core/model-router/status.d.ts.map +1 -1
  123. package/dist/core/model-router/status.js.map +1 -1
  124. package/dist/core/model-router-controller.d.ts +140 -0
  125. package/dist/core/model-router-controller.d.ts.map +1 -0
  126. package/dist/core/model-router-controller.js +623 -0
  127. package/dist/core/model-router-controller.js.map +1 -0
  128. package/dist/core/model-selection-controller.d.ts +95 -0
  129. package/dist/core/model-selection-controller.d.ts.map +1 -0
  130. package/dist/core/model-selection-controller.js +252 -0
  131. package/dist/core/model-selection-controller.js.map +1 -0
  132. package/dist/core/models/model-ref.d.ts +13 -0
  133. package/dist/core/models/model-ref.d.ts.map +1 -1
  134. package/dist/core/models/model-ref.js +22 -0
  135. package/dist/core/models/model-ref.js.map +1 -1
  136. package/dist/core/profile-filter-controller.d.ts +77 -0
  137. package/dist/core/profile-filter-controller.d.ts.map +1 -0
  138. package/dist/core/profile-filter-controller.js +159 -0
  139. package/dist/core/profile-filter-controller.js.map +1 -0
  140. package/dist/core/reflection-controller.d.ts +111 -0
  141. package/dist/core/reflection-controller.d.ts.map +1 -0
  142. package/dist/core/reflection-controller.js +412 -0
  143. package/dist/core/reflection-controller.js.map +1 -0
  144. package/dist/core/research/model-fitness.d.ts +12 -0
  145. package/dist/core/research/model-fitness.d.ts.map +1 -1
  146. package/dist/core/research/model-fitness.js +21 -0
  147. package/dist/core/research/model-fitness.js.map +1 -1
  148. package/dist/core/research/session-evidence-bundle.d.ts +1 -1
  149. package/dist/core/research/session-evidence-bundle.d.ts.map +1 -1
  150. package/dist/core/research/session-evidence-bundle.js.map +1 -1
  151. package/dist/core/runtime-builder.d.ts +222 -0
  152. package/dist/core/runtime-builder.d.ts.map +1 -0
  153. package/dist/core/runtime-builder.js +640 -0
  154. package/dist/core/runtime-builder.js.map +1 -0
  155. package/dist/core/sdk.d.ts +2 -2
  156. package/dist/core/sdk.d.ts.map +1 -1
  157. package/dist/core/sdk.js +3 -4
  158. package/dist/core/sdk.js.map +1 -1
  159. package/dist/core/session-analytics.d.ts +96 -0
  160. package/dist/core/session-analytics.d.ts.map +1 -0
  161. package/dist/core/session-analytics.js +331 -0
  162. package/dist/core/session-analytics.js.map +1 -0
  163. package/dist/core/session-manager-factory.d.ts +18 -0
  164. package/dist/core/session-manager-factory.d.ts.map +1 -0
  165. package/dist/core/session-manager-factory.js +32 -0
  166. package/dist/core/session-manager-factory.js.map +1 -0
  167. package/dist/core/session-tree-navigator.d.ts +68 -0
  168. package/dist/core/session-tree-navigator.d.ts.map +1 -0
  169. package/dist/core/session-tree-navigator.js +217 -0
  170. package/dist/core/session-tree-navigator.js.map +1 -0
  171. package/dist/core/system-prompt-builder.d.ts +63 -0
  172. package/dist/core/system-prompt-builder.d.ts.map +1 -0
  173. package/dist/core/system-prompt-builder.js +181 -0
  174. package/dist/core/system-prompt-builder.js.map +1 -0
  175. package/dist/core/tool-gate-controller.d.ts +36 -0
  176. package/dist/core/tool-gate-controller.d.ts.map +1 -0
  177. package/dist/core/tool-gate-controller.js +94 -0
  178. package/dist/core/tool-gate-controller.js.map +1 -0
  179. package/dist/core/tools/artifact-retrieve.d.ts.map +1 -1
  180. package/dist/core/tools/artifact-retrieve.js +1 -1
  181. package/dist/core/tools/artifact-retrieve.js.map +1 -1
  182. package/dist/core/tools/bash.d.ts +4 -2
  183. package/dist/core/tools/bash.d.ts.map +1 -1
  184. package/dist/core/tools/bash.js +49 -7
  185. package/dist/core/tools/bash.js.map +1 -1
  186. package/dist/core/tools/file-mutation-queue.d.ts +6 -0
  187. package/dist/core/tools/file-mutation-queue.d.ts.map +1 -1
  188. package/dist/core/tools/file-mutation-queue.js +61 -1
  189. package/dist/core/tools/file-mutation-queue.js.map +1 -1
  190. package/dist/core/tools/find.d.ts +1 -1
  191. package/dist/core/tools/find.d.ts.map +1 -1
  192. package/dist/core/tools/find.js +1 -1
  193. package/dist/core/tools/find.js.map +1 -1
  194. package/dist/core/tools/grep.d.ts +1 -1
  195. package/dist/core/tools/grep.d.ts.map +1 -1
  196. package/dist/core/tools/grep.js +1 -1
  197. package/dist/core/tools/grep.js.map +1 -1
  198. package/dist/core/tools/index.d.ts +1 -1
  199. package/dist/core/tools/index.d.ts.map +1 -1
  200. package/dist/core/tools/index.js +1 -1
  201. package/dist/core/tools/index.js.map +1 -1
  202. package/dist/core/tools/ls.d.ts +1 -1
  203. package/dist/core/tools/ls.d.ts.map +1 -1
  204. package/dist/core/tools/ls.js +1 -1
  205. package/dist/core/tools/ls.js.map +1 -1
  206. package/dist/core/tools/output-accumulator.d.ts +1 -1
  207. package/dist/core/tools/output-accumulator.d.ts.map +1 -1
  208. package/dist/core/tools/output-accumulator.js +1 -1
  209. package/dist/core/tools/output-accumulator.js.map +1 -1
  210. package/dist/core/tools/read.d.ts +1 -1
  211. package/dist/core/tools/read.d.ts.map +1 -1
  212. package/dist/core/tools/read.js +1 -1
  213. package/dist/core/tools/read.js.map +1 -1
  214. package/dist/core/tools/render-utils.d.ts.map +1 -1
  215. package/dist/core/tools/render-utils.js +1 -1
  216. package/dist/core/tools/render-utils.js.map +1 -1
  217. package/dist/index.d.ts +2 -3
  218. package/dist/index.d.ts.map +1 -1
  219. package/dist/index.js +3 -4
  220. package/dist/index.js.map +1 -1
  221. package/dist/main.d.ts.map +1 -1
  222. package/dist/main.js +13 -12
  223. package/dist/main.js.map +1 -1
  224. package/dist/modes/interactive/auth-dialogs-controller.d.ts +59 -0
  225. package/dist/modes/interactive/auth-dialogs-controller.d.ts.map +1 -0
  226. package/dist/modes/interactive/auth-dialogs-controller.js +398 -0
  227. package/dist/modes/interactive/auth-dialogs-controller.js.map +1 -0
  228. package/dist/modes/interactive/auto-learn-controller.d.ts +168 -0
  229. package/dist/modes/interactive/auto-learn-controller.d.ts.map +1 -0
  230. package/dist/modes/interactive/auto-learn-controller.js +1239 -0
  231. package/dist/modes/interactive/auto-learn-controller.js.map +1 -0
  232. package/dist/modes/interactive/autocomplete-provider.d.ts +26 -0
  233. package/dist/modes/interactive/autocomplete-provider.d.ts.map +1 -0
  234. package/dist/modes/interactive/autocomplete-provider.js +103 -0
  235. package/dist/modes/interactive/autocomplete-provider.js.map +1 -0
  236. package/dist/modes/interactive/autonomy-commands.d.ts +38 -0
  237. package/dist/modes/interactive/autonomy-commands.d.ts.map +1 -0
  238. package/dist/modes/interactive/autonomy-commands.js +142 -0
  239. package/dist/modes/interactive/autonomy-commands.js.map +1 -0
  240. package/dist/modes/interactive/clipboard-input.d.ts +37 -0
  241. package/dist/modes/interactive/clipboard-input.d.ts.map +1 -0
  242. package/dist/modes/interactive/clipboard-input.js +59 -0
  243. package/dist/modes/interactive/clipboard-input.js.map +1 -0
  244. package/dist/modes/interactive/compaction-queue.d.ts +30 -0
  245. package/dist/modes/interactive/compaction-queue.d.ts.map +1 -0
  246. package/dist/modes/interactive/compaction-queue.js +91 -0
  247. package/dist/modes/interactive/compaction-queue.js.map +1 -0
  248. package/dist/modes/interactive/components/bash-execution.d.ts +1 -1
  249. package/dist/modes/interactive/components/bash-execution.d.ts.map +1 -1
  250. package/dist/modes/interactive/components/bash-execution.js +1 -1
  251. package/dist/modes/interactive/components/bash-execution.js.map +1 -1
  252. package/dist/modes/interactive/components/branch-summary-message.d.ts +1 -1
  253. package/dist/modes/interactive/components/branch-summary-message.d.ts.map +1 -1
  254. package/dist/modes/interactive/components/branch-summary-message.js.map +1 -1
  255. package/dist/modes/interactive/components/compaction-summary-message.d.ts +1 -1
  256. package/dist/modes/interactive/components/compaction-summary-message.d.ts.map +1 -1
  257. package/dist/modes/interactive/components/compaction-summary-message.js.map +1 -1
  258. package/dist/modes/interactive/components/custom-message.d.ts +1 -1
  259. package/dist/modes/interactive/components/custom-message.d.ts.map +1 -1
  260. package/dist/modes/interactive/components/custom-message.js.map +1 -1
  261. package/dist/modes/interactive/components/session-selector-search.d.ts +1 -1
  262. package/dist/modes/interactive/components/session-selector-search.d.ts.map +1 -1
  263. package/dist/modes/interactive/components/session-selector-search.js.map +1 -1
  264. package/dist/modes/interactive/components/session-selector.d.ts +1 -1
  265. package/dist/modes/interactive/components/session-selector.d.ts.map +1 -1
  266. package/dist/modes/interactive/components/session-selector.js.map +1 -1
  267. package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  268. package/dist/modes/interactive/components/tool-execution.js +2 -2
  269. package/dist/modes/interactive/components/tool-execution.js.map +1 -1
  270. package/dist/modes/interactive/components/tree-selector.d.ts +1 -1
  271. package/dist/modes/interactive/components/tree-selector.d.ts.map +1 -1
  272. package/dist/modes/interactive/components/tree-selector.js.map +1 -1
  273. package/dist/modes/interactive/config-backup.d.ts +27 -0
  274. package/dist/modes/interactive/config-backup.d.ts.map +1 -0
  275. package/dist/modes/interactive/config-backup.js +146 -0
  276. package/dist/modes/interactive/config-backup.js.map +1 -0
  277. package/dist/modes/interactive/editor-overlay-host.d.ts +48 -0
  278. package/dist/modes/interactive/editor-overlay-host.d.ts.map +1 -0
  279. package/dist/modes/interactive/editor-overlay-host.js +43 -0
  280. package/dist/modes/interactive/editor-overlay-host.js.map +1 -0
  281. package/dist/modes/interactive/extension-ui-host.d.ts +165 -0
  282. package/dist/modes/interactive/extension-ui-host.d.ts.map +1 -0
  283. package/dist/modes/interactive/extension-ui-host.js +610 -0
  284. package/dist/modes/interactive/extension-ui-host.js.map +1 -0
  285. package/dist/modes/interactive/external-editor.d.ts +18 -0
  286. package/dist/modes/interactive/external-editor.d.ts.map +1 -0
  287. package/dist/modes/interactive/external-editor.js +107 -0
  288. package/dist/modes/interactive/external-editor.js.map +1 -0
  289. package/dist/modes/interactive/history-reload-math.d.ts +24 -0
  290. package/dist/modes/interactive/history-reload-math.d.ts.map +1 -0
  291. package/dist/modes/interactive/history-reload-math.js +129 -0
  292. package/dist/modes/interactive/history-reload-math.js.map +1 -0
  293. package/dist/modes/interactive/interactive-mode.d.ts +22 -238
  294. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  295. package/dist/modes/interactive/interactive-mode.js +714 -5910
  296. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  297. package/dist/modes/interactive/key-handlers.d.ts +46 -0
  298. package/dist/modes/interactive/key-handlers.d.ts.map +1 -0
  299. package/dist/modes/interactive/key-handlers.js +80 -0
  300. package/dist/modes/interactive/key-handlers.js.map +1 -0
  301. package/dist/modes/interactive/local-model-commands.d.ts +68 -0
  302. package/dist/modes/interactive/local-model-commands.d.ts.map +1 -0
  303. package/dist/modes/interactive/local-model-commands.js +288 -0
  304. package/dist/modes/interactive/local-model-commands.js.map +1 -0
  305. package/dist/modes/interactive/profile-menu-controller.d.ts +77 -0
  306. package/dist/modes/interactive/profile-menu-controller.d.ts.map +1 -0
  307. package/dist/modes/interactive/profile-menu-controller.js +917 -0
  308. package/dist/modes/interactive/profile-menu-controller.js.map +1 -0
  309. package/dist/modes/interactive/report-commands.d.ts +46 -0
  310. package/dist/modes/interactive/report-commands.d.ts.map +1 -0
  311. package/dist/modes/interactive/report-commands.js +255 -0
  312. package/dist/modes/interactive/report-commands.js.map +1 -0
  313. package/dist/modes/interactive/resource-display.d.ts +73 -0
  314. package/dist/modes/interactive/resource-display.d.ts.map +1 -0
  315. package/dist/modes/interactive/resource-display.js +278 -0
  316. package/dist/modes/interactive/resource-display.js.map +1 -0
  317. package/dist/modes/interactive/resource-shell-commands.d.ts +64 -0
  318. package/dist/modes/interactive/resource-shell-commands.d.ts.map +1 -0
  319. package/dist/modes/interactive/resource-shell-commands.js +202 -0
  320. package/dist/modes/interactive/resource-shell-commands.js.map +1 -0
  321. package/dist/modes/interactive/session-flow-commands.d.ts +139 -0
  322. package/dist/modes/interactive/session-flow-commands.d.ts.map +1 -0
  323. package/dist/modes/interactive/session-flow-commands.js +458 -0
  324. package/dist/modes/interactive/session-flow-commands.js.map +1 -0
  325. package/dist/modes/interactive/session-io-commands.d.ts +73 -0
  326. package/dist/modes/interactive/session-io-commands.d.ts.map +1 -0
  327. package/dist/modes/interactive/session-io-commands.js +202 -0
  328. package/dist/modes/interactive/session-io-commands.js.map +1 -0
  329. package/dist/modes/interactive/settings-selector-flow.d.ts +42 -0
  330. package/dist/modes/interactive/settings-selector-flow.d.ts.map +1 -0
  331. package/dist/modes/interactive/settings-selector-flow.js +287 -0
  332. package/dist/modes/interactive/settings-selector-flow.js.map +1 -0
  333. package/dist/modes/interactive/signal-lifecycle.d.ts +55 -0
  334. package/dist/modes/interactive/signal-lifecycle.d.ts.map +1 -0
  335. package/dist/modes/interactive/signal-lifecycle.js +177 -0
  336. package/dist/modes/interactive/signal-lifecycle.js.map +1 -0
  337. package/dist/modes/interactive/startup-checks.d.ts +38 -0
  338. package/dist/modes/interactive/startup-checks.d.ts.map +1 -0
  339. package/dist/modes/interactive/startup-checks.js +189 -0
  340. package/dist/modes/interactive/startup-checks.js.map +1 -0
  341. package/dist/modes/rpc/rpc-client.d.ts +1 -1
  342. package/dist/modes/rpc/rpc-client.d.ts.map +1 -1
  343. package/dist/modes/rpc/rpc-client.js.map +1 -1
  344. package/dist/modes/rpc/rpc-types.d.ts +1 -1
  345. package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
  346. package/dist/modes/rpc/rpc-types.js.map +1 -1
  347. package/dist/utils/paths.d.ts +2 -14
  348. package/dist/utils/paths.d.ts.map +1 -1
  349. package/dist/utils/paths.js +5 -30
  350. package/dist/utils/paths.js.map +1 -1
  351. package/dist/utils/process-memory.d.ts +8 -0
  352. package/dist/utils/process-memory.d.ts.map +1 -0
  353. package/dist/utils/process-memory.js +11 -0
  354. package/dist/utils/process-memory.js.map +1 -0
  355. package/dist/utils/shell.d.ts +0 -9
  356. package/dist/utils/shell.d.ts.map +1 -1
  357. package/dist/utils/shell.js +0 -37
  358. package/dist/utils/shell.js.map +1 -1
  359. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  360. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  361. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  362. package/examples/extensions/sandbox/package-lock.json +2 -2
  363. package/examples/extensions/sandbox/package.json +1 -1
  364. package/examples/extensions/with-deps/package-lock.json +2 -2
  365. package/examples/extensions/with-deps/package.json +1 -1
  366. package/examples/sdk/11-sessions.ts +8 -8
  367. package/examples/sdk/13-session-runtime.ts +1 -1
  368. package/npm-shrinkwrap.json +12 -12
  369. package/package.json +4 -4
  370. package/dist/core/compaction/branch-summarization.d.ts +0 -88
  371. package/dist/core/compaction/branch-summarization.d.ts.map +0 -1
  372. package/dist/core/compaction/branch-summarization.js +0 -243
  373. package/dist/core/compaction/branch-summarization.js.map +0 -1
  374. package/dist/core/compaction/compaction.d.ts +0 -143
  375. package/dist/core/compaction/compaction.d.ts.map +0 -1
  376. package/dist/core/compaction/compaction.js +0 -659
  377. package/dist/core/compaction/compaction.js.map +0 -1
  378. package/dist/core/compaction/index.d.ts +0 -7
  379. package/dist/core/compaction/index.d.ts.map +0 -1
  380. package/dist/core/compaction/index.js +0 -7
  381. package/dist/core/compaction/index.js.map +0 -1
  382. package/dist/core/compaction/utils.d.ts +0 -38
  383. package/dist/core/compaction/utils.d.ts.map +0 -1
  384. package/dist/core/compaction/utils.js +0 -153
  385. package/dist/core/compaction/utils.js.map +0 -1
  386. package/dist/core/message-retention.d.ts +0 -26
  387. package/dist/core/message-retention.d.ts.map +0 -1
  388. package/dist/core/message-retention.js +0 -95
  389. package/dist/core/message-retention.js.map +0 -1
  390. package/dist/core/messages.d.ts +0 -77
  391. package/dist/core/messages.d.ts.map +0 -1
  392. package/dist/core/messages.js +0 -123
  393. package/dist/core/messages.js.map +0 -1
  394. package/dist/core/session-manager.d.ts +0 -337
  395. package/dist/core/session-manager.d.ts.map +0 -1
  396. package/dist/core/session-manager.js +0 -1328
  397. package/dist/core/session-manager.js.map +0 -1
  398. package/dist/core/tools/truncate.d.ts +0 -70
  399. package/dist/core/tools/truncate.d.ts.map +0 -1
  400. package/dist/core/tools/truncate.js +0 -215
  401. package/dist/core/tools/truncate.js.map +0 -1
@@ -0,0 +1,1239 @@
1
+ /**
2
+ * Auto-Learn / Native-Reflection controller.
3
+ *
4
+ * Extracted verbatim from interactive-mode.ts (god-file decomposition). Owns the disk-backed
5
+ * Auto Learn run-state machine (state.json + lockfile), the background-learner spawn/prune
6
+ * lifecycle, and the in-process native reflection pass. It takes narrow deps (a live session
7
+ * accessor, a self-modification-source resolver that stays in the host, and a small UI callback
8
+ * surface) rather than the whole InteractiveMode instance.
9
+ */
10
+ import * as crypto from "node:crypto";
11
+ import * as fs from "node:fs";
12
+ import * as path from "node:path";
13
+ import { isAutoLearnSessionId } from "@caupulican/pi-agent-core/node";
14
+ import { spawn } from "child_process";
15
+ import lockfile from "proper-lockfile";
16
+ import { getAgentDir } from "../../config.js";
17
+ import { readAutoLearnSessionIdFromFile, reportCompletedAutoLearnUsageHelper } from "../../core/cost/session-usage.js";
18
+ import { resolveCliModel } from "../../core/model-resolver.js";
19
+ import { getPendingReloadBlockers } from "../../core/reload-blockers.js";
20
+ import { theme } from "./theme/theme.js";
21
+ const AUTO_LEARN_DEFAULTS = {
22
+ enabled: false,
23
+ model: "active",
24
+ thinkingLevel: "low",
25
+ longSessionMessages: 32,
26
+ longSessionContextPercent: 70,
27
+ cooldownMinutes: 24 * 60,
28
+ leaseMinutes: 90,
29
+ maxConcurrentLearners: 1,
30
+ applyHighConfidence: false,
31
+ reflectionReview: true,
32
+ reflectionMinToolCalls: 12,
33
+ reflectionCooldownMinutes: 24 * 60,
34
+ complexTaskToolCalls: 12,
35
+ };
36
+ const AUTONOMY_AUTO_LEARN_PRESETS = {
37
+ off: { ...AUTO_LEARN_DEFAULTS, enabled: false, reflectionReview: false },
38
+ safe: {
39
+ ...AUTO_LEARN_DEFAULTS,
40
+ enabled: false,
41
+ longSessionMessages: 64,
42
+ longSessionContextPercent: 85,
43
+ cooldownMinutes: 24 * 60,
44
+ leaseMinutes: 60,
45
+ maxConcurrentLearners: 1,
46
+ applyHighConfidence: false,
47
+ reflectionReview: false,
48
+ reflectionMinToolCalls: 12,
49
+ reflectionCooldownMinutes: 24 * 60,
50
+ complexTaskToolCalls: 12,
51
+ },
52
+ balanced: {
53
+ ...AUTO_LEARN_DEFAULTS,
54
+ enabled: false,
55
+ longSessionMessages: 64,
56
+ longSessionContextPercent: 85,
57
+ cooldownMinutes: 24 * 60,
58
+ leaseMinutes: 90,
59
+ maxConcurrentLearners: 1,
60
+ applyHighConfidence: false,
61
+ reflectionReview: false,
62
+ reflectionMinToolCalls: 12,
63
+ reflectionCooldownMinutes: 24 * 60,
64
+ complexTaskToolCalls: 12,
65
+ },
66
+ full: {
67
+ ...AUTO_LEARN_DEFAULTS,
68
+ enabled: true,
69
+ longSessionMessages: 64,
70
+ longSessionContextPercent: 85,
71
+ cooldownMinutes: 24 * 60,
72
+ leaseMinutes: 90,
73
+ maxConcurrentLearners: 1,
74
+ applyHighConfidence: true,
75
+ reflectionReview: true,
76
+ reflectionMinToolCalls: 12,
77
+ reflectionCooldownMinutes: 24 * 60,
78
+ complexTaskToolCalls: 12,
79
+ },
80
+ };
81
+ export const AUTONOMY_MODES = ["off", "safe", "balanced", "full"];
82
+ const AUTO_LEARN_RESERVATION_MS = 2 * 60 * 1000;
83
+ export const AUTO_LEARN_HISTORY_RETENTION_MS = 7 * 24 * 60 * 60 * 1000;
84
+ function definedStringSet(values) {
85
+ const set = new Set();
86
+ if (!values)
87
+ return set;
88
+ for (const value of values) {
89
+ if (typeof value === "string" && value.length > 0)
90
+ set.add(value);
91
+ }
92
+ return set;
93
+ }
94
+ function sanitizeAutoLearnPathPart(input, fallback) {
95
+ const cleaned = (input || fallback)
96
+ .replace(/[^a-zA-Z0-9._-]+/g, "-")
97
+ .replace(/-+/g, "-")
98
+ .replace(/^-+|-+$/g, "")
99
+ .slice(0, 80);
100
+ return cleaned || fallback;
101
+ }
102
+ function isOldAutoLearnArtifact(filePath, now, retentionMs) {
103
+ const stats = fs.lstatSync(filePath);
104
+ return stats.isFile() && now - stats.mtimeMs > retentionMs;
105
+ }
106
+ function removeOldAutoLearnArtifact(filePath, result, counter) {
107
+ try {
108
+ fs.rmSync(filePath, { force: true });
109
+ result[counter]++;
110
+ }
111
+ catch {
112
+ result.errors++;
113
+ }
114
+ }
115
+ function isPathInside(target, root) {
116
+ const resolvedTarget = path.resolve(target);
117
+ const resolvedRoot = path.resolve(root);
118
+ return resolvedTarget === resolvedRoot || resolvedTarget.startsWith(`${resolvedRoot}${path.sep}`);
119
+ }
120
+ function removeAutoLearnArtifactPath(filePath, root) {
121
+ if (!isPathInside(filePath, root))
122
+ return false;
123
+ try {
124
+ fs.rmSync(filePath, { recursive: true, force: true });
125
+ return true;
126
+ }
127
+ catch {
128
+ return false;
129
+ }
130
+ }
131
+ function getAutoLearnSessionIdFromFileName(fileName) {
132
+ return fileName.match(/_(auto-learn-[A-Za-z0-9._-]+)\.jsonl$/)?.[1];
133
+ }
134
+ function pruneAutoLearnSessionFiles(dir, activeSessionIds, now, retentionMs, result) {
135
+ let entries;
136
+ try {
137
+ entries = fs.readdirSync(dir, { withFileTypes: true });
138
+ }
139
+ catch {
140
+ return;
141
+ }
142
+ for (const entry of entries) {
143
+ const filePath = path.join(dir, entry.name);
144
+ if (entry.isDirectory()) {
145
+ pruneAutoLearnSessionFiles(filePath, activeSessionIds, now, retentionMs, result);
146
+ continue;
147
+ }
148
+ if (!entry.isFile() || !entry.name.endsWith(".jsonl"))
149
+ continue;
150
+ let shouldPrune = false;
151
+ try {
152
+ shouldPrune = isOldAutoLearnArtifact(filePath, now, retentionMs);
153
+ }
154
+ catch {
155
+ result.errors++;
156
+ continue;
157
+ }
158
+ if (!shouldPrune)
159
+ continue;
160
+ const sessionId = readAutoLearnSessionIdFromFile(filePath) ?? getAutoLearnSessionIdFromFileName(entry.name);
161
+ if (!sessionId || !isAutoLearnSessionId(sessionId) || activeSessionIds.has(sessionId))
162
+ continue;
163
+ removeOldAutoLearnArtifact(filePath, result, "sessionFiles");
164
+ }
165
+ }
166
+ function pruneAutoLearnRunArtifacts(dir, activeRunIds, now, retentionMs, result) {
167
+ let entries;
168
+ try {
169
+ entries = fs.readdirSync(dir, { withFileTypes: true });
170
+ }
171
+ catch {
172
+ return;
173
+ }
174
+ for (const entry of entries) {
175
+ const filePath = path.join(dir, entry.name);
176
+ if (entry.isDirectory()) {
177
+ pruneAutoLearnRunArtifacts(filePath, activeRunIds, now, retentionMs, result);
178
+ continue;
179
+ }
180
+ if (!entry.isFile())
181
+ continue;
182
+ const promptRunId = entry.name.endsWith(".prompt.md") ? entry.name.slice(0, -".prompt.md".length) : undefined;
183
+ const logRunId = entry.name.endsWith(".log") ? entry.name.slice(0, -".log".length) : undefined;
184
+ const runId = promptRunId ?? logRunId;
185
+ if (!runId || activeRunIds.has(runId))
186
+ continue;
187
+ let shouldPrune = false;
188
+ try {
189
+ shouldPrune = isOldAutoLearnArtifact(filePath, now, retentionMs);
190
+ }
191
+ catch {
192
+ result.errors++;
193
+ continue;
194
+ }
195
+ if (!shouldPrune)
196
+ continue;
197
+ removeOldAutoLearnArtifact(filePath, result, promptRunId ? "promptFiles" : "logFiles");
198
+ }
199
+ }
200
+ export function pruneAutoLearnConversationHistory(options) {
201
+ const result = { promptFiles: 0, logFiles: 0, sessionFiles: 0, errors: 0 };
202
+ const dataDir = path.resolve(options.dataDir);
203
+ const now = options.now ?? Date.now();
204
+ const retentionMs = options.retentionMs ?? AUTO_LEARN_HISTORY_RETENTION_MS;
205
+ const activeRunIds = definedStringSet(options.activeRunIds);
206
+ const activeSessionIds = definedStringSet(options.activeSessionIds);
207
+ if (retentionMs <= 0 || !fs.existsSync(dataDir))
208
+ return result;
209
+ pruneAutoLearnRunArtifacts(dataDir, activeRunIds, now, retentionMs, result);
210
+ pruneAutoLearnSessionFiles(dataDir, activeSessionIds, now, retentionMs, result);
211
+ return result;
212
+ }
213
+ export function buildAutoLearnSpawnArgs(spawnTarget, options) {
214
+ return [
215
+ ...spawnTarget.argsPrefix,
216
+ "--print",
217
+ "--name",
218
+ options.name,
219
+ "--model",
220
+ options.modelPattern,
221
+ ...(options.thinkingLevel ? ["--thinking", options.thinkingLevel] : []),
222
+ "--session-dir",
223
+ options.sessionDir,
224
+ "--session-id",
225
+ options.sessionId,
226
+ `@${options.promptPath}`,
227
+ ];
228
+ }
229
+ export function findAutoLearnSpawnNullByteInput(command, args) {
230
+ if (command.includes("\0"))
231
+ return "command";
232
+ const argIndex = args.findIndex((arg) => arg.includes("\0"));
233
+ return argIndex === -1 ? undefined : `args[${argIndex}]`;
234
+ }
235
+ export class AutoLearnController {
236
+ // Native-reflection debounce: prevents back-to-back/overlapping background reflection passes (cost
237
+ // guard). `_nativeReflectionInFlight` blocks a second pass while one runs; `_lastNativeReflectionAt`
238
+ // enforces a minimum gap between passes. A debounce-skipped turn's text is BUFFERED in
239
+ // `_pendingReflectionText` (not dropped) and folded into the next pass, so no corrective feedback is
240
+ // lost — reflection sees only the current turn's messages, so dropping a skipped turn would lose its
241
+ // learning entirely (bug #29).
242
+ _nativeReflectionInFlight = false;
243
+ _lastNativeReflectionAt = 0;
244
+ _pendingReflectionText = [];
245
+ static NATIVE_REFLECTION_MIN_INTERVAL_MS = 45_000;
246
+ static PENDING_REFLECTION_MAX_CHARS = 12_000;
247
+ deps;
248
+ constructor(deps) {
249
+ this.deps = deps;
250
+ }
251
+ get session() {
252
+ return this.deps.getSession();
253
+ }
254
+ get sessionManager() {
255
+ return this.deps.getSession().sessionManager;
256
+ }
257
+ get settingsManager() {
258
+ return this.deps.getSession().settingsManager;
259
+ }
260
+ get ui() {
261
+ return this.deps.ui;
262
+ }
263
+ getAutoLearnModelAuthPriority(model) {
264
+ if (this.session.model && model.provider === this.session.model.provider && model.id === this.session.model.id) {
265
+ return 0;
266
+ }
267
+ const credential = this.session.modelRegistry.authStorage.get(model.provider);
268
+ if (credential?.type === "oauth")
269
+ return 1;
270
+ if (credential?.type === "api_key")
271
+ return 2;
272
+ const authStatus = this.session.modelRegistry.getProviderAuthStatus(model.provider);
273
+ switch (authStatus.source) {
274
+ case "runtime":
275
+ return 3;
276
+ case "environment":
277
+ return 4;
278
+ case "models_json_key":
279
+ case "models_json_command":
280
+ case "fallback":
281
+ return 5;
282
+ default:
283
+ return 6;
284
+ }
285
+ }
286
+ getAutoLearnModelAuthLabel(model) {
287
+ const credential = this.session.modelRegistry.authStorage.get(model.provider);
288
+ if (credential?.type === "oauth")
289
+ return "subscription";
290
+ if (credential?.type === "api_key")
291
+ return "API key";
292
+ const authStatus = this.session.modelRegistry.getProviderAuthStatus(model.provider);
293
+ switch (authStatus.source) {
294
+ case "runtime":
295
+ return authStatus.label ? `runtime ${authStatus.label}` : "runtime API key";
296
+ case "environment":
297
+ return authStatus.label ? `env ${authStatus.label}` : "environment API key";
298
+ case "models_json_key":
299
+ return "models.json API key";
300
+ case "models_json_command":
301
+ return "models.json command";
302
+ case "fallback":
303
+ return authStatus.label ?? "custom provider config";
304
+ default:
305
+ return "configured";
306
+ }
307
+ }
308
+ getAutoLearnModelOptions() {
309
+ this.session.modelRegistry.refresh();
310
+ const availableModels = this.session.modelRegistry.getAvailable();
311
+ const sortedModels = [...availableModels].sort((a, b) => {
312
+ const priorityDelta = this.getAutoLearnModelAuthPriority(a) - this.getAutoLearnModelAuthPriority(b);
313
+ if (priorityDelta !== 0)
314
+ return priorityDelta;
315
+ const providerDelta = this.session.modelRegistry
316
+ .getProviderDisplayName(a.provider)
317
+ .localeCompare(this.session.modelRegistry.getProviderDisplayName(b.provider));
318
+ if (providerDelta !== 0)
319
+ return providerDelta;
320
+ return a.id.localeCompare(b.id);
321
+ });
322
+ return sortedModels.map((model) => {
323
+ const providerName = this.session.modelRegistry.getProviderDisplayName(model.provider);
324
+ const authLabel = this.getAutoLearnModelAuthLabel(model);
325
+ const modelPattern = `${model.provider}/${model.id}`;
326
+ const currentLabel = this.session.model && model.provider === this.session.model.provider && model.id === this.session.model.id
327
+ ? " · current"
328
+ : "";
329
+ const displayName = model.name && model.name !== model.id ? ` · ${model.name}` : "";
330
+ return {
331
+ value: modelPattern,
332
+ label: modelPattern,
333
+ description: `${providerName} · ${authLabel}${currentLabel}${displayName}`,
334
+ };
335
+ });
336
+ }
337
+ getAutoLearnDataDir() {
338
+ return path.join(getAgentDir(), "auto-learn");
339
+ }
340
+ getAutoLearnStatePath() {
341
+ return path.join(this.getAutoLearnDataDir(), "state.json");
342
+ }
343
+ ensureAutoLearnStateFile() {
344
+ const dir = this.getAutoLearnDataDir();
345
+ fs.mkdirSync(dir, { recursive: true });
346
+ const statePath = this.getAutoLearnStatePath();
347
+ if (!fs.existsSync(statePath)) {
348
+ fs.writeFileSync(statePath, "{}\n", "utf-8");
349
+ }
350
+ }
351
+ readAutoLearnState() {
352
+ try {
353
+ const statePath = this.getAutoLearnStatePath();
354
+ if (!fs.existsSync(statePath))
355
+ return {};
356
+ return JSON.parse(fs.readFileSync(statePath, "utf-8"));
357
+ }
358
+ catch {
359
+ return {};
360
+ }
361
+ }
362
+ writeAutoLearnState(state) {
363
+ const dir = this.getAutoLearnDataDir();
364
+ fs.mkdirSync(dir, { recursive: true });
365
+ fs.writeFileSync(this.getAutoLearnStatePath(), `${JSON.stringify(state, null, 2)}\n`, "utf-8");
366
+ }
367
+ acquireAutoLearnStateLock() {
368
+ this.ensureAutoLearnStateFile();
369
+ const statePath = this.getAutoLearnStatePath();
370
+ const maxAttempts = 20;
371
+ const delayMs = 25;
372
+ let lastError;
373
+ for (let attempt = 1; attempt <= maxAttempts; attempt++) {
374
+ try {
375
+ return lockfile.lockSync(statePath, { realpath: false, stale: 30000 });
376
+ }
377
+ catch (error) {
378
+ const code = error && typeof error === "object" && "code" in error ? String(error.code) : "";
379
+ if (code !== "ELOCKED" || attempt === maxAttempts) {
380
+ throw error;
381
+ }
382
+ lastError = error;
383
+ const start = Date.now();
384
+ while (Date.now() - start < delayMs) {
385
+ // Synchronous callers need a synchronous lock retry loop.
386
+ }
387
+ }
388
+ }
389
+ throw lastError instanceof Error ? lastError : new Error("Failed to acquire Auto Learn state lock");
390
+ }
391
+ withAutoLearnStateLock(fn) {
392
+ let release;
393
+ try {
394
+ release = this.acquireAutoLearnStateLock();
395
+ const { result, next } = fn(this.readAutoLearnState());
396
+ if (next !== undefined) {
397
+ this.writeAutoLearnState(next);
398
+ }
399
+ return result;
400
+ }
401
+ finally {
402
+ release?.();
403
+ }
404
+ }
405
+ appendAutoLearnLog(logPath, message) {
406
+ try {
407
+ fs.appendFileSync(logPath, `${message}\n`, "utf-8");
408
+ }
409
+ catch {
410
+ // Logging must never turn a background learner startup failure into an interactive crash.
411
+ }
412
+ }
413
+ isAutoLearnPidAlive(pid) {
414
+ if (typeof pid !== "number" || pid <= 0)
415
+ return false;
416
+ try {
417
+ process.kill(pid, 0);
418
+ return true;
419
+ }
420
+ catch (error) {
421
+ const code = error && typeof error === "object" && "code" in error ? String(error.code) : "";
422
+ return code === "EPERM";
423
+ }
424
+ }
425
+ pruneAutoLearnState(state, now = Date.now()) {
426
+ const runs = { ...(state.runs ?? {}) };
427
+ for (const [id, run] of Object.entries(runs)) {
428
+ if (run.expiresAt <= now) {
429
+ delete runs[id];
430
+ continue;
431
+ }
432
+ if (run.status === "reserved" && run.pid === undefined) {
433
+ continue;
434
+ }
435
+ if (!this.isAutoLearnPidAlive(run.pid)) {
436
+ delete runs[id];
437
+ }
438
+ }
439
+ return { ...state, runs };
440
+ }
441
+ pruneAutoLearnHistoryFromState(state, now = Date.now()) {
442
+ const prunedState = this.pruneAutoLearnState(state, now);
443
+ pruneAutoLearnConversationHistory({
444
+ dataDir: this.getAutoLearnDataDir(),
445
+ now,
446
+ activeRunIds: Object.keys(prunedState.runs ?? {}),
447
+ activeSessionIds: Object.values(prunedState.runs ?? {}).map((run) => run.sessionId),
448
+ });
449
+ return prunedState;
450
+ }
451
+ getPrunedAutoLearnState() {
452
+ return this.withAutoLearnStateLock((current) => {
453
+ const state = this.pruneAutoLearnHistoryFromState(current);
454
+ return { result: state, next: state };
455
+ });
456
+ }
457
+ getAutoLearnPresetForAutonomyMode(mode, current = {}) {
458
+ const preset = AUTONOMY_AUTO_LEARN_PRESETS[mode] ?? AUTONOMY_AUTO_LEARN_PRESETS.off;
459
+ return { ...preset, model: current.model?.trim() || preset.model };
460
+ }
461
+ getEffectiveAutoLearnSettings() {
462
+ const settings = this.settingsManager.getAutoLearnSettings();
463
+ const preset = this.getAutoLearnPresetForAutonomyMode(this.settingsManager.getAutonomySettings().mode, settings);
464
+ return {
465
+ enabled: settings.enabled ?? preset.enabled,
466
+ model: settings.model?.trim() || preset.model,
467
+ longSessionMessages: settings.longSessionMessages ?? preset.longSessionMessages,
468
+ longSessionContextPercent: settings.longSessionContextPercent ?? preset.longSessionContextPercent,
469
+ cooldownMinutes: settings.cooldownMinutes ?? preset.cooldownMinutes,
470
+ leaseMinutes: settings.leaseMinutes ?? preset.leaseMinutes,
471
+ maxConcurrentLearners: settings.maxConcurrentLearners ?? preset.maxConcurrentLearners,
472
+ applyHighConfidence: settings.applyHighConfidence ?? preset.applyHighConfidence,
473
+ reflectionReview: settings.reflectionReview ?? preset.reflectionReview,
474
+ reflectionMinToolCalls: settings.reflectionMinToolCalls ?? preset.reflectionMinToolCalls,
475
+ reflectionCooldownMinutes: settings.reflectionCooldownMinutes ?? preset.reflectionCooldownMinutes,
476
+ complexTaskToolCalls: settings.complexTaskToolCalls ?? preset.complexTaskToolCalls,
477
+ thinkingLevel: settings.thinkingLevel ?? preset.thinkingLevel,
478
+ };
479
+ }
480
+ getCurrentAutoLearnSettings() {
481
+ return this.getEffectiveAutoLearnSettings();
482
+ }
483
+ getAutoLearnTenantKey() {
484
+ return `${this.sessionManager.getCwd()}::${this.session.sessionId}`;
485
+ }
486
+ getAutoLearnTenantId() {
487
+ const cwdHash = crypto.createHash("sha256").update(this.sessionManager.getCwd()).digest("hex").slice(0, 8);
488
+ const sessionPart = sanitizeAutoLearnPathPart(this.session.sessionId, "session");
489
+ return `${sessionPart}-${cwdHash}`;
490
+ }
491
+ getAutoLearnTenantDataDir() {
492
+ return path.join(this.getAutoLearnDataDir(), "tenants", this.getAutoLearnTenantId());
493
+ }
494
+ getAutoLearnMessageCount() {
495
+ return this.sessionManager.getBranch().filter((entry) => entry.type === "message").length;
496
+ }
497
+ buildAutoLearnDecisionFromState(state, settings, force = false) {
498
+ const now = Date.now();
499
+ const tenant = this.getAutoLearnTenantKey();
500
+ const runningCount = Object.values(state.runs ?? {}).filter((run) => run.tenant === tenant).length;
501
+ const lastLaunch = state.lastLaunchByTenant?.[tenant] ?? 0;
502
+ const cooldownMs = settings.cooldownMinutes * 60 * 1000;
503
+ const cooldownRemainingMs = Math.max(0, lastLaunch + cooldownMs - now);
504
+ const messageCount = this.getAutoLearnMessageCount();
505
+ const contextPercent = this.session.getContextUsage()?.percent ?? null;
506
+ if (!settings.enabled && !force) {
507
+ return {
508
+ shouldRun: false,
509
+ reason: "disabled",
510
+ messageCount,
511
+ contextPercent,
512
+ cooldownRemainingMs,
513
+ runningCount,
514
+ };
515
+ }
516
+ if (runningCount >= settings.maxConcurrentLearners) {
517
+ return {
518
+ shouldRun: false,
519
+ reason: `max tenant learners running (${runningCount}/${settings.maxConcurrentLearners})`,
520
+ messageCount,
521
+ contextPercent,
522
+ cooldownRemainingMs,
523
+ runningCount,
524
+ };
525
+ }
526
+ if (!force && cooldownRemainingMs > 0) {
527
+ return {
528
+ shouldRun: false,
529
+ reason: "cooldown",
530
+ messageCount,
531
+ contextPercent,
532
+ cooldownRemainingMs,
533
+ runningCount,
534
+ };
535
+ }
536
+ if (force) {
537
+ return { shouldRun: true, reason: "manual", messageCount, contextPercent, cooldownRemainingMs, runningCount };
538
+ }
539
+ if (messageCount >= settings.longSessionMessages) {
540
+ return {
541
+ shouldRun: true,
542
+ reason: `message trigger (${messageCount}/${settings.longSessionMessages})`,
543
+ messageCount,
544
+ contextPercent,
545
+ cooldownRemainingMs,
546
+ runningCount,
547
+ };
548
+ }
549
+ if (contextPercent !== null && contextPercent >= settings.longSessionContextPercent) {
550
+ return {
551
+ shouldRun: true,
552
+ reason: `context trigger (${contextPercent.toFixed(1)}%/${settings.longSessionContextPercent}%)`,
553
+ messageCount,
554
+ contextPercent,
555
+ cooldownRemainingMs,
556
+ runningCount,
557
+ };
558
+ }
559
+ return {
560
+ shouldRun: false,
561
+ reason: "thresholds not met",
562
+ messageCount,
563
+ contextPercent,
564
+ cooldownRemainingMs,
565
+ runningCount,
566
+ };
567
+ }
568
+ resolveAutoLearnModelPattern(settings) {
569
+ if (settings.model === "active") {
570
+ return this.session.model ? `${this.session.model.provider}/${this.session.model.id}` : undefined;
571
+ }
572
+ return settings.model;
573
+ }
574
+ getAutoLearnSpawnTarget() {
575
+ const overridePath = process.env.PI_AUTO_LEARN_CLI_PATH?.trim();
576
+ if (overridePath) {
577
+ return { command: overridePath, argsPrefix: [] };
578
+ }
579
+ const execBase = path.basename(process.execPath).toLowerCase();
580
+ const isScriptRuntime = execBase === "node" || execBase === "node.exe" || execBase === "bun" || execBase === "bun.exe";
581
+ if (!isScriptRuntime) {
582
+ return { command: process.execPath, argsPrefix: [] };
583
+ }
584
+ const cliPath = process.argv[1];
585
+ if (!cliPath || cliPath.startsWith("-")) {
586
+ return undefined;
587
+ }
588
+ return { command: process.execPath, argsPrefix: [cliPath] };
589
+ }
590
+ validateAutoLearnModelValue(value) {
591
+ const modelValue = value?.trim();
592
+ if (!modelValue || modelValue === "active")
593
+ return undefined;
594
+ const available = this.session.modelRegistry.getAvailable();
595
+ if (modelValue.includes("/")) {
596
+ const [provider, modelId] = modelValue.split("/", 2);
597
+ if (available.some((model) => model.provider === provider && model.id === modelId))
598
+ return undefined;
599
+ return `Auto Learn model "${modelValue}" is not in configured subscription/API models; saved as manual/unverified.`;
600
+ }
601
+ if (available.some((model) => model.id === modelValue))
602
+ return undefined;
603
+ return `Auto Learn model "${modelValue}" is not in configured subscription/API models; saved as manual/unverified.`;
604
+ }
605
+ evaluateAutoLearn(force = false) {
606
+ const settings = this.getEffectiveAutoLearnSettings();
607
+ return this.withAutoLearnStateLock((current) => {
608
+ const state = this.pruneAutoLearnHistoryFromState(current);
609
+ return { result: this.buildAutoLearnDecisionFromState(state, settings, force), next: state };
610
+ });
611
+ }
612
+ buildAutonomyAuthorityPrompt() {
613
+ const autonomy = this.settingsManager.getAutonomySettings();
614
+ const selfModification = this.settingsManager.getSelfModificationSettings();
615
+ if (autonomy.mode !== "full") {
616
+ return [
617
+ "Authority mode: proposal-gated.",
618
+ "- You may autonomously query memory and run bounded learning tools.",
619
+ "- Durable memory writes require the configured high-confidence policy; skills, extensions, source, settings, publishing, tagging, and releases remain proposals unless the foreground user explicitly asks.",
620
+ ].join("\n");
621
+ }
622
+ const selfModificationSource = selfModification.enabled
623
+ ? this.deps.resolveSelfModificationSource(selfModification)
624
+ : undefined;
625
+ const sourceAuthority = selfModification.enabled && selfModificationSource
626
+ ? `- Pi source: standing authority to edit the authorized selfModification source (${selfModificationSource}) for self-evolution improvements; inspect git status first, preserve unrelated user changes, run focused validation, and leave a clear rollback/test summary.`
627
+ : "- Pi source: no standing source-edit authority until selfModification.enabled and selfModification.sourcePaths are set.";
628
+ return [
629
+ "Authority mode: FULL AUTONOMOUS standing grant.",
630
+ "- Memory: may apply high-confidence durable Automata memory/corrections after duplicate and corroboration checks.",
631
+ "- Skills: may create or patch user/project-owned procedural skills and support files; prefer updating existing umbrella skills; do not delete skills without making a recoverable archive/backup.",
632
+ "- User/project extensions and tools: may create or patch small scoped extensions/tools under Pi user/project roots when they address repeated workflow/tooling gaps; validate and keep changes auditable.",
633
+ "- Settings: may auto-tune autonomy/autoLearn settings to reduce bottlenecks; do not modify credentials, provider auth, package sources, or unrelated user preferences.",
634
+ sourceAuthority,
635
+ "- Hard stops without explicit foreground approval: publish, npm release, git push, tag creation, credential changes, destructive user-data deletion, network-exposed services, or authority expansion beyond this policy.",
636
+ "- Audit: final output must list changed paths/settings, commands/tests run, evidence, residual risks, and rollback guidance. If no safe validation is possible, leave the change as a proposal instead of applying it.",
637
+ ].join("\n");
638
+ }
639
+ buildAutoLearnPrompt(reason, settings, options = {}) {
640
+ const authorityBlock = this.buildAutonomyAuthorityPrompt();
641
+ const reflectionBlock = options.kind === "reflection" && options.turnDigest
642
+ ? `\n\nLatest completed turn digest (bounded; use only as current-session evidence, not as longitudinal proof):\n<turn_digest>\n${options.turnDigest}\n</turn_digest>`
643
+ : "";
644
+ const objective = options.kind === "reflection"
645
+ ? "review the latest completed turn for durable memory, skill, validation, tooling, and code-baked self-improvement cues, then run one bounded continuous-learning pass if the learning tools are available"
646
+ : "run one bounded continuous-learning pass for this Pi tenant";
647
+ return `You are Pi Auto Learn running as a background learner.\n\nObjective: ${objective}.\nTrigger: ${reason}.\n\n${authorityBlock}\n\nRequired workflow:\n1. Query existing durable memory/rules first when tools allow it. Memory confrontation is mandatory before accepting, merging, upgrading, or rejecting learning candidates.\n2. Run the available Auto Learn tooling, preferably learning_run_auto, with applyHighConfidence=${settings.applyHighConfidence}. Process candidate validation in vectorized chunks/batches; avoid scalar per-candidate memory queries except for final selected writes.\n3. Apply the learning validation tree to each candidate chunk: (a) Why is this good for the user? (b) Is it unique, or similar to existing memory/skills/agents so it should merge or upgrade existing knowledge? (c) Will this make Pi a better agent? Candidates that cannot answer all three are noise.\n4. Hermes-style learning cycle: after a complex task (${settings.complexTaskToolCalls}+ tool calls), user correction, repeated steering pattern, non-trivial fix/workaround/debugging path, loaded-skill defect, trigger gap, tool gap, or harness workflow defect, actively create or update durable learning artifacts. Memory stores compact facts/preferences/state; skills/prompts/agents/extensions/source store procedural behavior. When a lesson changes how Pi should act on a future class of task, memory alone is not completion.\n5. Skill update preference order: (1) patch the currently loaded or consulted skill that governed the task; (2) patch an existing class-level umbrella skill/agent/prompt; (3) add a support file under references/, templates/, or scripts/ and add a SKILL.md pointer; (4) create a new class-level umbrella skill only when no existing artifact fits. Never create one-off PR/error/codename/session skills.\n6. Behavioral self-improvement is code-baked by default: prefer the lowest durable executable layer that fixes the behavior — patch an existing skill/prompt/agent/extension/tool, tune an approved setting, or edit the authorized Pi source when source authority is available. Use Automata only for concise facts/evidence pointers that support the baked change.\n7. Do not harden transient or environment-dependent failures into durable behavior: missing binaries, fresh-install package gaps, credentials not configured, path mismatches, one-off task narratives, or negative tool-broken claims should become setup/troubleshooting fixes only when the fix itself is reusable.\n8. Treat the latest-turn digest as current-session evidence only; do not auto-commit one-off cues unless deterministic tooling and memory confrontation corroborate them.\n9. In mode=full, apply safe memory/skill/user-extension/authorized-source improvements under the standing grant above; otherwise keep them proposal-gated.\n10. Never cross hard-stop boundaries from the authority policy.\n11. If the learning tools are unavailable, report BLOCKED with the missing tool names and do not improvise.\n12. Finish with PASS, BLOCKED, or FAIL and concise evidence, including chunk counts, merge/upgrade/code-bake decisions, changed paths/settings, validation, and cleanup/purge status.${reflectionBlock}`;
648
+ }
649
+ reserveAutoLearnRun(params) {
650
+ return this.withAutoLearnStateLock((current) => {
651
+ const now = Date.now();
652
+ const state = this.pruneAutoLearnHistoryFromState(current, now);
653
+ const tenant = this.getAutoLearnTenantKey();
654
+ if (params.cooldownKind === "reflection" && !params.bypassReflectionCooldown) {
655
+ const lastReflection = state.lastReflectionByTenant?.[tenant] ?? 0;
656
+ const cooldownMs = params.settings.reflectionCooldownMinutes * 60 * 1000;
657
+ if (Math.max(0, lastReflection + cooldownMs - now) > 0) {
658
+ return { result: { ok: false, reason: "reflection cooldown" }, next: state };
659
+ }
660
+ }
661
+ const decision = this.buildAutoLearnDecisionFromState(state, params.settings, params.force);
662
+ if (!decision.shouldRun) {
663
+ return { result: { ok: false, reason: decision.reason }, next: state };
664
+ }
665
+ const run = {
666
+ tenant,
667
+ model: params.modelPattern,
668
+ reason: params.reason,
669
+ startedAt: now,
670
+ expiresAt: now + AUTO_LEARN_RESERVATION_MS,
671
+ cwd: this.sessionManager.getCwd(),
672
+ logPath: params.logPath,
673
+ sessionDir: params.sessionDir,
674
+ sessionId: params.sessionId,
675
+ promptPath: params.promptPath,
676
+ kind: params.kind,
677
+ autonomyMode: this.settingsManager.getAutonomySettings().mode,
678
+ authority: this.settingsManager.getAutonomySettings().mode === "full"
679
+ ? "standing-full-autonomous"
680
+ : "proposal-gated",
681
+ status: "reserved",
682
+ };
683
+ const next = {
684
+ ...state,
685
+ runs: { ...(state.runs ?? {}), [params.runId]: run },
686
+ };
687
+ if (params.cooldownKind === "reflection") {
688
+ next.lastReflectionByTenant = { ...(state.lastReflectionByTenant ?? {}), [tenant]: now };
689
+ }
690
+ else {
691
+ next.lastLaunchByTenant = { ...(state.lastLaunchByTenant ?? {}), [tenant]: now };
692
+ }
693
+ return { result: { ok: true, reservation: { runId: params.runId, startedAt: now } }, next };
694
+ });
695
+ }
696
+ releaseAutoLearnReservation(reservation, cooldownKind) {
697
+ this.withAutoLearnStateLock((current) => {
698
+ const state = this.pruneAutoLearnHistoryFromState(current);
699
+ const tenant = this.getAutoLearnTenantKey();
700
+ const runs = { ...(state.runs ?? {}) };
701
+ delete runs[reservation.runId];
702
+ const next = { ...state, runs };
703
+ if (cooldownKind === "reflection" && next.lastReflectionByTenant?.[tenant] === reservation.startedAt) {
704
+ next.lastReflectionByTenant = { ...next.lastReflectionByTenant };
705
+ delete next.lastReflectionByTenant[tenant];
706
+ }
707
+ else if (cooldownKind !== "reflection" && next.lastLaunchByTenant?.[tenant] === reservation.startedAt) {
708
+ next.lastLaunchByTenant = { ...next.lastLaunchByTenant };
709
+ delete next.lastLaunchByTenant[tenant];
710
+ }
711
+ return { result: undefined, next };
712
+ });
713
+ }
714
+ reportCompletedAutoLearnUsage(runId, sessionDir, sessionId, logPath) {
715
+ try {
716
+ reportCompletedAutoLearnUsageHelper({
717
+ runId,
718
+ sessionDir,
719
+ sessionId,
720
+ logPath,
721
+ parentSession: this.session,
722
+ appendLog: (p, msg) => this.appendAutoLearnLog(p, msg),
723
+ });
724
+ }
725
+ catch (error) {
726
+ const message = error instanceof Error ? error.message : String(error);
727
+ this.appendAutoLearnLog(logPath, `Auto Learn usage report failed: ${message}`);
728
+ }
729
+ }
730
+ cleanupCompletedAutoLearnRun(runId, options) {
731
+ this.reportCompletedAutoLearnUsage(runId, options.sessionDir, options.sessionId, options.logPath);
732
+ const dataDir = this.getAutoLearnDataDir();
733
+ for (const filePath of options.artifactPaths)
734
+ removeAutoLearnArtifactPath(filePath, dataDir);
735
+ this.withAutoLearnStateLock((current) => {
736
+ const state = this.pruneAutoLearnState(current);
737
+ const runs = { ...(state.runs ?? {}) };
738
+ delete runs[runId];
739
+ return { result: undefined, next: { ...state, runs } };
740
+ });
741
+ this.updateAutoLearnFooter();
742
+ }
743
+ markAutoLearnReservationRunning(reservation, pid, settings) {
744
+ this.withAutoLearnStateLock((current) => {
745
+ const now = Date.now();
746
+ const state = this.pruneAutoLearnHistoryFromState(current, now);
747
+ const run = state.runs?.[reservation.runId];
748
+ if (!run) {
749
+ return { result: undefined, next: state };
750
+ }
751
+ return {
752
+ result: undefined,
753
+ next: {
754
+ ...state,
755
+ runs: {
756
+ ...(state.runs ?? {}),
757
+ [reservation.runId]: {
758
+ ...run,
759
+ pid,
760
+ expiresAt: now + settings.leaseMinutes * 60 * 1000,
761
+ status: "running",
762
+ },
763
+ },
764
+ },
765
+ };
766
+ });
767
+ }
768
+ launchAutoLearn(reason, force = false, options = {}) {
769
+ const settings = this.getEffectiveAutoLearnSettings();
770
+ const modelPattern = this.resolveAutoLearnModelPattern(settings);
771
+ if (!modelPattern) {
772
+ return "Auto Learn not started: no active model is available for model=active.";
773
+ }
774
+ const spawnTarget = this.getAutoLearnSpawnTarget();
775
+ if (!spawnTarget) {
776
+ return "Auto Learn not started: could not resolve current pi CLI path.";
777
+ }
778
+ const dir = this.getAutoLearnTenantDataDir();
779
+ fs.mkdirSync(dir, { recursive: true });
780
+ const runId = `${Date.now()}-${crypto.randomUUID().slice(0, 8)}`;
781
+ const logPath = path.join(dir, `${runId}.log`);
782
+ const promptPath = path.join(dir, `${runId}.prompt.md`);
783
+ const kind = options.promptKind ?? "auto";
784
+ const sessionDir = path.join(dir, "sessions", runId);
785
+ const sessionId = `auto-learn-${kind}-${this.getAutoLearnTenantId()}-${runId}`;
786
+ fs.mkdirSync(sessionDir, { recursive: true });
787
+ const prompt = this.buildAutoLearnPrompt(reason, settings, {
788
+ kind,
789
+ turnDigest: options.turnDigest,
790
+ });
791
+ const args = buildAutoLearnSpawnArgs(spawnTarget, {
792
+ name: `Auto Learn ${runId}`,
793
+ modelPattern,
794
+ thinkingLevel: settings.thinkingLevel ?? "low",
795
+ sessionDir,
796
+ sessionId,
797
+ promptPath,
798
+ });
799
+ const invalidSpawnInput = findAutoLearnSpawnNullByteInput(spawnTarget.command, args);
800
+ if (invalidSpawnInput) {
801
+ const message = `Auto Learn not started: ${invalidSpawnInput} contains a null byte.`;
802
+ this.appendAutoLearnLog(logPath, message);
803
+ this.updateAutoLearnFooter();
804
+ return `${message} Log: ${logPath}`;
805
+ }
806
+ const reservationResult = this.reserveAutoLearnRun({
807
+ settings,
808
+ force,
809
+ cooldownKind: options.cooldownKind,
810
+ bypassReflectionCooldown: options.bypassReflectionCooldown,
811
+ runId,
812
+ modelPattern,
813
+ reason,
814
+ logPath,
815
+ sessionDir,
816
+ sessionId,
817
+ promptPath,
818
+ kind,
819
+ });
820
+ if (!reservationResult.ok) {
821
+ return `Auto Learn not started: ${reservationResult.reason}`;
822
+ }
823
+ const { reservation } = reservationResult;
824
+ try {
825
+ fs.writeFileSync(promptPath, prompt, "utf-8");
826
+ }
827
+ catch (error) {
828
+ const message = error instanceof Error ? error.message : String(error);
829
+ this.releaseAutoLearnReservation(reservation, options.cooldownKind);
830
+ this.appendAutoLearnLog(logPath, `Auto Learn failed to write prompt file: ${message}`);
831
+ this.updateAutoLearnFooter();
832
+ return `Auto Learn not started: failed to write prompt file (${message}). Log: ${logPath}`;
833
+ }
834
+ let child;
835
+ let outFd;
836
+ try {
837
+ outFd = fs.openSync(logPath, "a");
838
+ const sourceSessionFile = this.sessionManager.getSessionFile();
839
+ child = spawn(spawnTarget.command, args, {
840
+ cwd: this.sessionManager.getCwd(),
841
+ detached: true,
842
+ stdio: ["ignore", outFd, outFd],
843
+ env: {
844
+ ...process.env,
845
+ PI_AUTO_LEARN_CHILD: "1",
846
+ ...(sourceSessionFile ? { PI_AUTO_LEARN_SOURCE_SESSION_FILE: sourceSessionFile } : {}),
847
+ },
848
+ });
849
+ child.once("error", (error) => {
850
+ const message = error instanceof Error ? error.message : String(error);
851
+ this.appendAutoLearnLog(logPath, `Auto Learn failed to start: ${message}`);
852
+ });
853
+ }
854
+ catch (error) {
855
+ const message = error instanceof Error ? error.message : String(error);
856
+ this.releaseAutoLearnReservation(reservation, options.cooldownKind);
857
+ this.appendAutoLearnLog(logPath, `Auto Learn failed to start: ${message}`);
858
+ this.updateAutoLearnFooter();
859
+ return `Auto Learn not started: failed to spawn background learner (${message}). Log: ${logPath}`;
860
+ }
861
+ finally {
862
+ if (outFd !== undefined) {
863
+ try {
864
+ fs.closeSync(outFd);
865
+ }
866
+ catch {
867
+ // The child has already been spawned or startup has already failed; ignore close errors here.
868
+ }
869
+ }
870
+ }
871
+ if (!child || typeof child.pid !== "number" || child.pid <= 0) {
872
+ this.releaseAutoLearnReservation(reservation, options.cooldownKind);
873
+ this.updateAutoLearnFooter();
874
+ return `Auto Learn not started: failed to spawn background learner. Log: ${logPath}`;
875
+ }
876
+ const childPid = child.pid;
877
+ child.once("exit", (code) => {
878
+ if (code === 0) {
879
+ this.cleanupCompletedAutoLearnRun(reservation.runId, {
880
+ artifactPaths: [promptPath, logPath, sessionDir],
881
+ sessionDir,
882
+ sessionId,
883
+ logPath,
884
+ });
885
+ }
886
+ });
887
+ child.unref();
888
+ this.markAutoLearnReservationRunning(reservation, childPid, settings);
889
+ this.updateAutoLearnFooter();
890
+ return `Auto Learn started. Log: ${logPath}`;
891
+ }
892
+ sanitizeAutoLearnDigestText(text) {
893
+ return text
894
+ .replace(/-----BEGIN [A-Z ]*(?:PRIVATE|OPENSSH|RSA|DSA|EC) KEY-----[\s\S]*?-----END [A-Z ]*(?:PRIVATE|OPENSSH|RSA|DSA|EC) KEY-----/g, "[redacted-private-key]")
895
+ .replace(/\b(?:sk|pk)-(?:proj-)?[A-Za-z0-9_-]{12,}/g, "[redacted-api-key]")
896
+ .replace(/\bsk-ant-[A-Za-z0-9_-]{12,}/g, "[redacted-api-key]")
897
+ .replace(/\b(?:ghp|gho|ghu|ghs|github_pat)_[A-Za-z0-9_]{20,}/g, "[redacted-github-token]")
898
+ .replace(/\b(?:AKIA|ASIA)[0-9A-Z]{16}\b/g, "[redacted-aws-access-key]")
899
+ .replace(/(?:Bearer\s+)[A-Za-z0-9._-]{16,}/gi, "Bearer [redacted]")
900
+ .replace(/([?&](?:key|token|api_key|access_token|secret|password)=)[^&\s]+/gi, "$1[redacted]")
901
+ .replace(/((?:access|refresh|token|apiKey|api_key|password|secret|authorization|auth)\s*[:=]\s*)[^\s,'"}]{8,}/gi, "$1[redacted]");
902
+ }
903
+ capAutoLearnDigestText(text, maxChars) {
904
+ const compact = this.sanitizeAutoLearnDigestText(text).replace(/\s+/g, " ").trim();
905
+ if (compact.length <= maxChars)
906
+ return compact;
907
+ return `${compact.slice(0, Math.max(0, maxChars - 20)).trimEnd()} …[truncated]`;
908
+ }
909
+ getAgentMessagePlainText(message) {
910
+ const raw = message;
911
+ const content = raw.content;
912
+ if (typeof content === "string")
913
+ return content;
914
+ if (!Array.isArray(content))
915
+ return "";
916
+ const parts = [];
917
+ for (const block of content) {
918
+ if (!block || typeof block !== "object")
919
+ continue;
920
+ const item = block;
921
+ if (item.type === "text" && typeof item.text === "string")
922
+ parts.push(item.text);
923
+ if (item.type === "toolCall" && typeof item.name === "string")
924
+ parts.push(`[tool call: ${item.name}]`);
925
+ }
926
+ return parts.join("\n");
927
+ }
928
+ countAgentToolCalls(messages) {
929
+ let toolCalls = 0;
930
+ let toolResults = 0;
931
+ for (const message of messages) {
932
+ const raw = message;
933
+ const role = String(raw.role ?? "");
934
+ if (role === "toolResult" || role === "bashExecution")
935
+ toolResults++;
936
+ const content = raw.content;
937
+ if (!Array.isArray(content))
938
+ continue;
939
+ for (const block of content) {
940
+ if (block && typeof block === "object" && block.type === "toolCall") {
941
+ toolCalls++;
942
+ }
943
+ }
944
+ }
945
+ return Math.max(toolCalls, toolResults);
946
+ }
947
+ buildAutonomyReviewDigest(messages) {
948
+ const lines = [];
949
+ for (const message of messages.slice(-18)) {
950
+ const raw = message;
951
+ const role = String(raw.role ?? "message");
952
+ const label = role === "toolResult" && typeof raw.toolName === "string" ? `toolResult:${raw.toolName}` : role;
953
+ const text = this.capAutoLearnDigestText(this.getAgentMessagePlainText(message), 700);
954
+ if (text)
955
+ lines.push(`${label}: ${text}`);
956
+ }
957
+ const digest = lines.join("\n---\n");
958
+ return this.capAutoLearnDigestText(digest || "[No textual turn digest available.]", 6000);
959
+ }
960
+ evaluateAutonomyReview(messages) {
961
+ const settings = this.getEffectiveAutoLearnSettings();
962
+ const state = this.withAutoLearnStateLock((current) => {
963
+ const pruned = this.pruneAutoLearnHistoryFromState(current);
964
+ return { result: pruned, next: pruned };
965
+ });
966
+ const now = Date.now();
967
+ const tenant = this.getAutoLearnTenantKey();
968
+ const runningCount = Object.values(state.runs ?? {}).filter((run) => run.tenant === tenant).length;
969
+ const lastReflection = state.lastReflectionByTenant?.[tenant] ?? 0;
970
+ const cooldownMs = settings.reflectionCooldownMinutes * 60 * 1000;
971
+ const cooldownRemainingMs = Math.max(0, lastReflection + cooldownMs - now);
972
+ const messageCount = this.getAutoLearnMessageCount();
973
+ const contextPercent = this.session.getContextUsage()?.percent ?? null;
974
+ const toolCalls = this.countAgentToolCalls(messages);
975
+ const userText = messages
976
+ .filter((message) => String(message.role ?? "") === "user")
977
+ .map((message) => this.getAgentMessagePlainText(message))
978
+ .join("\n");
979
+ const correctionSignal = /\b(next time|for future|from now on|remember this|don't|do not|avoid|instead|you should|should have|you forgot|you missed|not what i asked|wrong again)\b/i.test(userText);
980
+ const behavioralSelfImprovementSignal = /\b(harness|pi|agent|autonomy|autonomous|self[- ]?improv(?:e|ement|ing)?|steer(?:ing)?|trigger(?:s)?|skill(?:s)?|code[- ]?bak(?:e|ed)|bake(?:d)? into code|not (?:automata|memory)|reference agent|hermes)\b/i.test(userText) &&
981
+ /\b(improve|automatic(?:ally)?|autonomous|trigger|fire|skill|steer|self[- ]?improv(?:e|ement|ing)?|code[- ]?bak(?:e|ed)|bake(?:d)?|too much|less)\b/i.test(userText);
982
+ const complexTaskThreshold = Math.max(1, settings.complexTaskToolCalls ?? 12);
983
+ const complexTaskSignal = toolCalls >= complexTaskThreshold;
984
+ const bypassCooldown = correctionSignal || behavioralSelfImprovementSignal || complexTaskSignal;
985
+ const base = { messageCount, contextPercent, cooldownRemainingMs, runningCount, toolCalls };
986
+ if (!settings.enabled)
987
+ return { ...base, shouldRun: false, reason: "disabled" };
988
+ if (!settings.reflectionReview)
989
+ return { ...base, shouldRun: false, reason: "reflection disabled" };
990
+ if (runningCount >= settings.maxConcurrentLearners) {
991
+ return {
992
+ ...base,
993
+ shouldRun: false,
994
+ reason: `max tenant learners running (${runningCount}/${settings.maxConcurrentLearners})`,
995
+ };
996
+ }
997
+ if (cooldownRemainingMs > 0 && !bypassCooldown) {
998
+ return { ...base, shouldRun: false, reason: "reflection cooldown" };
999
+ }
1000
+ if (behavioralSelfImprovementSignal) {
1001
+ return {
1002
+ ...base,
1003
+ shouldRun: true,
1004
+ reason: "reflection behavioral self-improvement signal",
1005
+ digest: this.buildAutonomyReviewDigest(messages),
1006
+ bypassCooldown: true,
1007
+ };
1008
+ }
1009
+ if (correctionSignal) {
1010
+ return {
1011
+ ...base,
1012
+ shouldRun: true,
1013
+ reason: "reflection correction signal",
1014
+ digest: this.buildAutonomyReviewDigest(messages),
1015
+ bypassCooldown: true,
1016
+ };
1017
+ }
1018
+ if (complexTaskSignal) {
1019
+ return {
1020
+ ...base,
1021
+ shouldRun: true,
1022
+ reason: `reflection complex task learning signal (${toolCalls}/${complexTaskThreshold} tool calls)`,
1023
+ digest: this.buildAutonomyReviewDigest(messages),
1024
+ bypassCooldown: true,
1025
+ };
1026
+ }
1027
+ // Full autonomy expands allowed action scope for triggered reviews; it does not make every turn a review trigger.
1028
+ if (toolCalls >= settings.reflectionMinToolCalls) {
1029
+ return {
1030
+ ...base,
1031
+ shouldRun: true,
1032
+ reason: `reflection tool trigger (${toolCalls}/${settings.reflectionMinToolCalls})`,
1033
+ digest: this.buildAutonomyReviewDigest(messages),
1034
+ };
1035
+ }
1036
+ return { ...base, shouldRun: false, reason: "reflection thresholds not met" };
1037
+ }
1038
+ /**
1039
+ * Native reflection (R2) is the in-process replacement for the buggy `continuous-learning`
1040
+ * subprocess. It runs when auto-learn is enabled and is not killed via `PI_NATIVE_REFLECTION=0`.
1041
+ */
1042
+ isNativeReflectionEnabled() {
1043
+ if (process.env.PI_NATIVE_REFLECTION === "0")
1044
+ return false;
1045
+ if (process.env.PI_AUTO_LEARN_CHILD === "1")
1046
+ return false;
1047
+ return this.getEffectiveAutoLearnSettings().enabled;
1048
+ }
1049
+ /** Heuristic: does the user's turn text read like a correction/steer worth learning from? */
1050
+ hasCorrectionSignal(userText) {
1051
+ return /\b(next time|for future|from now on|remember this|don't|do not|avoid|instead|you should|should have|you forgot|you missed|not what i asked|wrong again)\b/i.test(userText);
1052
+ }
1053
+ /**
1054
+ * End-of-loop native reflection: demand-gate the just-finished turn (zero-I/O) and, when
1055
+ * warranted, run the in-process {@link AgentSession.runReflectionPass} as a fire-and-forget
1056
+ * background microtask. No subprocess, no blocking of the UI.
1057
+ */
1058
+ /**
1059
+ * Resolve the model + thinking level the native reflection pass should use, from auto-learn
1060
+ * settings (`model`, `thinkingLevel`). The configured model is honored only when its provider is
1061
+ * AVAILABLE (api key / logged in) — otherwise we fall back to the session model (undefined). This
1062
+ * lets the user pick a balanced/cheaper reflection model without risking an unusable one.
1063
+ */
1064
+ _resolveReflectionModel(settings) {
1065
+ let model;
1066
+ if (settings.model && settings.model !== "active") {
1067
+ const resolved = resolveCliModel({ cliModel: settings.model, modelRegistry: this.session.modelRegistry });
1068
+ if (resolved.model && this.session.modelRegistry.hasConfiguredAuth(resolved.model)) {
1069
+ model = resolved.model;
1070
+ }
1071
+ }
1072
+ const thinkingLevel = settings.thinkingLevel ?? "low";
1073
+ return { model, thinkingLevel };
1074
+ }
1075
+ /** Buffer a debounce-skipped turn's text so its learning is folded into the next pass (bug #29). */
1076
+ _bufferPendingReflection(text) {
1077
+ const t = text.trim();
1078
+ if (!t)
1079
+ return;
1080
+ this._pendingReflectionText.push(t);
1081
+ // Bound the buffer so a long skipped streak can't grow unbounded; drop oldest past the budget
1082
+ // (the most recent corrections matter most).
1083
+ let total = this._pendingReflectionText.reduce((n, s) => n + s.length + 1, 0);
1084
+ while (this._pendingReflectionText.length > 1 && total > AutoLearnController.PENDING_REFLECTION_MAX_CHARS) {
1085
+ total -= (this._pendingReflectionText.shift()?.length ?? 0) + 1;
1086
+ }
1087
+ }
1088
+ _drainPendingReflection() {
1089
+ if (this._pendingReflectionText.length === 0)
1090
+ return "";
1091
+ const joined = this._pendingReflectionText.join("\n");
1092
+ this._pendingReflectionText = [];
1093
+ return joined;
1094
+ }
1095
+ maybeRunNativeReflection(messages) {
1096
+ if (!this.isNativeReflectionEnabled())
1097
+ return;
1098
+ const settings = this.getEffectiveAutoLearnSettings();
1099
+ const toolCallCount = this.countAgentToolCalls(messages);
1100
+ const contextPercent = this.session.getContextUsage()?.percent ?? 0;
1101
+ const contextHeadroomPct = Math.max(0, 100 - contextPercent);
1102
+ const userText = messages
1103
+ .filter((m) => String(m.role ?? "") === "user")
1104
+ .map((m) => this.getAgentMessagePlainText(m))
1105
+ .join("\n");
1106
+ const hadCorrection = this.hasCorrectionSignal(userText);
1107
+ // A correction is worth learning from even on a short turn; otherwise require a complex turn.
1108
+ const trigger = hadCorrection
1109
+ ? "corrective"
1110
+ : toolCallCount >= Math.max(1, settings.complexTaskToolCalls ?? 12)
1111
+ ? "complex"
1112
+ : "none";
1113
+ if (trigger === "none")
1114
+ return;
1115
+ const recentTurnText = messages
1116
+ .map((m) => `${String(m.role ?? "")}: ${this.getAgentMessagePlainText(m)}`.trim())
1117
+ .filter(Boolean)
1118
+ .join("\n");
1119
+ // Debounce (cost guard): never run two background reflection passes at once, and never start one
1120
+ // within the min interval of the last — a multi-turn correction session would otherwise spawn
1121
+ // overlapping passes that re-reason the same task. A skipped turn is NOT dropped: its text is
1122
+ // buffered and folded into the next pass, so the corrective feedback is still learned (bug #29).
1123
+ const now = Date.now();
1124
+ const debounced = this._nativeReflectionInFlight ||
1125
+ now - this._lastNativeReflectionAt < AutoLearnController.NATIVE_REFLECTION_MIN_INTERVAL_MS;
1126
+ if (debounced) {
1127
+ this._bufferPendingReflection(recentTurnText);
1128
+ return;
1129
+ }
1130
+ // Fold any buffered (previously debounced) turns into this pass so nothing learned is lost.
1131
+ const pending = this._drainPendingReflection();
1132
+ const reflectionText = pending ? `${pending}\n${recentTurnText}` : recentTurnText;
1133
+ // Stable per-turn id so a duplicate scheduling/retry can't double-count the reflection cost.
1134
+ // Messages carry no `id` on the real path (only timestamps), so derive the key from the last
1135
+ // message's timestamp + the turn size — present on every real turn, stable across a retry of the
1136
+ // same agent_end, and distinct between turns. Falls back to a content signature if needed.
1137
+ const last = messages[messages.length - 1];
1138
+ const turnKey = last?.id ?? (last?.timestamp != null ? `${last.timestamp}:${recentTurnText.length}` : undefined);
1139
+ const reportId = turnKey ? `reflection:${turnKey}` : undefined;
1140
+ // User-configurable reflection model + thinking (auto-learn settings), restricted to AVAILABLE
1141
+ // (authed) models — falls back to the session model when unset or unavailable.
1142
+ const { model, thinkingLevel } = this._resolveReflectionModel(settings);
1143
+ this._nativeReflectionInFlight = true;
1144
+ this._lastNativeReflectionAt = now;
1145
+ void this.session
1146
+ .runReflectionPass({
1147
+ signals: { trigger, toolCallCount, hadCorrection, contextHeadroomPct, usefulLately: 0 },
1148
+ recentTurnText: reflectionText,
1149
+ reportId,
1150
+ model,
1151
+ thinkingLevel,
1152
+ })
1153
+ .catch(() => {
1154
+ // best-effort background learning; never disrupt the session
1155
+ })
1156
+ .finally(() => {
1157
+ this._nativeReflectionInFlight = false;
1158
+ });
1159
+ }
1160
+ maybeStartAutoLearn() {
1161
+ if (process.env.PI_AUTO_LEARN_CHILD === "1")
1162
+ return false;
1163
+ const decision = this.evaluateAutoLearn(false);
1164
+ if (!decision.shouldRun) {
1165
+ this.updateAutoLearnFooter();
1166
+ return false;
1167
+ }
1168
+ const message = this.launchAutoLearn(decision.reason, false);
1169
+ if (!message.startsWith("Auto Learn started"))
1170
+ this.ui.showStatus(message);
1171
+ return message.startsWith("Auto Learn started");
1172
+ }
1173
+ maybeStartAutonomyReview(messages) {
1174
+ if (process.env.PI_AUTO_LEARN_CHILD === "1")
1175
+ return false;
1176
+ const decision = this.evaluateAutonomyReview(messages);
1177
+ if (!decision.shouldRun)
1178
+ return false;
1179
+ const message = this.launchAutoLearn(decision.reason, true, {
1180
+ cooldownKind: "reflection",
1181
+ promptKind: "reflection",
1182
+ turnDigest: decision.digest,
1183
+ bypassReflectionCooldown: decision.bypassCooldown,
1184
+ });
1185
+ if (!message.startsWith("Auto Learn started"))
1186
+ this.ui.showStatus(message);
1187
+ return message.startsWith("Auto Learn started");
1188
+ }
1189
+ updateAutoLearnFooter() {
1190
+ const settings = this.getEffectiveAutoLearnSettings();
1191
+ if (!settings.enabled) {
1192
+ this.ui.footerDataProvider.setExtensionStatus("auto-learn", undefined);
1193
+ return;
1194
+ }
1195
+ const tenant = this.getAutoLearnTenantKey();
1196
+ const state = this.getPrunedAutoLearnState();
1197
+ const hasActiveRun = Object.values(state.runs ?? {}).some((run) => run.tenant === tenant && this.isAutoLearnPidAlive(run.pid));
1198
+ this.ui.footerDataProvider.setExtensionStatus("auto-learn", hasActiveRun ? theme.fg("warning", "(learning)") : undefined);
1199
+ this.ui.invalidateFooter();
1200
+ this.ui.requestRender();
1201
+ }
1202
+ formatAutoLearnStatus() {
1203
+ const settings = this.getEffectiveAutoLearnSettings();
1204
+ const decision = this.evaluateAutoLearn(false);
1205
+ const state = this.getPrunedAutoLearnState();
1206
+ const tenant = this.getAutoLearnTenantKey();
1207
+ const runs = Object.entries(state.runs ?? {}).filter(([, run]) => run.tenant === tenant);
1208
+ const otherTenantRuns = Object.values(state.runs ?? {}).filter((run) => run.tenant !== tenant).length;
1209
+ const contextText = decision.contextPercent === null ? "unknown" : `${decision.contextPercent.toFixed(1)}%`;
1210
+ const cooldownText = decision.cooldownRemainingMs > 0 ? `${Math.ceil(decision.cooldownRemainingMs / 60000)}m remaining` : "ready";
1211
+ const runLines = runs.length
1212
+ ? runs
1213
+ .map(([id, run]) => {
1214
+ const session = [
1215
+ run.sessionId ? `session=${run.sessionId}` : "",
1216
+ run.sessionDir ? `sessionDir=${run.sessionDir}` : "",
1217
+ ]
1218
+ .filter(Boolean)
1219
+ .join(", ");
1220
+ const sessionText = session ? `, ${session}` : "";
1221
+ return `- ${id}: ${run.model}, kind=${run.kind ?? "auto"}, status=${run.status ?? "running"}, authority=${run.authority ?? "unknown"}, pid=${run.pid ?? "?"}${sessionText}, log=${run.logPath}`;
1222
+ })
1223
+ .join("\n")
1224
+ : "- none";
1225
+ const reloadBlockers = getPendingReloadBlockers({
1226
+ ownPid: process.pid,
1227
+ ownSessionId: this.sessionManager.getSessionId(),
1228
+ ownSessionFile: this.sessionManager.getSessionFile(),
1229
+ });
1230
+ const reloadBlockerLines = reloadBlockers.pending
1231
+ ? reloadBlockers.descriptions.map((description) => `- ${description}`).join("\n")
1232
+ : "- none";
1233
+ const reflectionLast = state.lastReflectionByTenant?.[this.getAutoLearnTenantKey()] ?? 0;
1234
+ const reflectionCooldownRemainingMs = Math.max(0, reflectionLast + settings.reflectionCooldownMinutes * 60 * 1000 - Date.now());
1235
+ const reflectionCooldownText = reflectionCooldownRemainingMs > 0 ? `${Math.ceil(reflectionCooldownRemainingMs / 60000)}m remaining` : "ready";
1236
+ return `Auto Learn status\nEnabled: ${settings.enabled}\nModel: ${settings.model}\nNext decision: ${decision.shouldRun ? "ready" : decision.reason}\nMessages: ${decision.messageCount}/${settings.longSessionMessages}\nContext: ${contextText}/${settings.longSessionContextPercent}%\nCooldown: ${cooldownText}\nReflection review: ${settings.reflectionReview ? "enabled" : "disabled"} (tool trigger ${settings.reflectionMinToolCalls}, cooldown ${reflectionCooldownText})\nHistory retention: 7 days for internal Auto Learn prompts/logs/sessions\nRunning tenant leases: ${runs.length}/${settings.maxConcurrentLearners}\nOther tenant leases: ${otherTenantRuns}\nTenant artifact dir: ${this.getAutoLearnTenantDataDir()}\nPi auto-reload blockers: ${reloadBlockers.pending ? reloadBlockers.reason : "none"}\n${reloadBlockerLines}\nRuns:\n${runLines}`;
1237
+ }
1238
+ }
1239
+ //# sourceMappingURL=auto-learn-controller.js.map