@hoilab/ada-cli 0.84.21 → 0.84.22

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 (344) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/README.es.md +163 -0
  3. package/README.md +99 -652
  4. package/dist/cli/args.d.ts +3 -0
  5. package/dist/cli/args.d.ts.map +1 -1
  6. package/dist/cli/args.js +11 -0
  7. package/dist/cli/args.js.map +1 -1
  8. package/dist/cli/file-processor.d.ts.map +1 -1
  9. package/dist/cli/file-processor.js +2 -1
  10. package/dist/cli/file-processor.js.map +1 -1
  11. package/dist/cli/home-screen.d.ts.map +1 -1
  12. package/dist/cli/home-screen.js +2 -2
  13. package/dist/cli/home-screen.js.map +1 -1
  14. package/dist/cli/startup-ui.d.ts.map +1 -1
  15. package/dist/cli/startup-ui.js +9 -3
  16. package/dist/cli/startup-ui.js.map +1 -1
  17. package/dist/config.d.ts.map +1 -1
  18. package/dist/config.js +2 -1
  19. package/dist/config.js.map +1 -1
  20. package/dist/core/agent-session.d.ts +57 -7
  21. package/dist/core/agent-session.d.ts.map +1 -1
  22. package/dist/core/agent-session.js +449 -73
  23. package/dist/core/agent-session.js.map +1 -1
  24. package/dist/core/auth-storage.d.ts.map +1 -1
  25. package/dist/core/auth-storage.js +4 -3
  26. package/dist/core/auth-storage.js.map +1 -1
  27. package/dist/core/compaction/branch-summarization.d.ts.map +1 -1
  28. package/dist/core/compaction/branch-summarization.js +3 -0
  29. package/dist/core/compaction/branch-summarization.js.map +1 -1
  30. package/dist/core/compaction/compaction.d.ts +4 -3
  31. package/dist/core/compaction/compaction.d.ts.map +1 -1
  32. package/dist/core/compaction/compaction.js +41 -32
  33. package/dist/core/compaction/compaction.js.map +1 -1
  34. package/dist/core/defaults.d.ts +1 -0
  35. package/dist/core/defaults.d.ts.map +1 -1
  36. package/dist/core/defaults.js +9 -0
  37. package/dist/core/defaults.js.map +1 -1
  38. package/dist/core/engine-personality.d.ts +12 -0
  39. package/dist/core/engine-personality.d.ts.map +1 -0
  40. package/dist/core/engine-personality.js +53 -0
  41. package/dist/core/engine-personality.js.map +1 -0
  42. package/dist/core/extensions/index.d.ts +1 -1
  43. package/dist/core/extensions/index.d.ts.map +1 -1
  44. package/dist/core/extensions/index.js.map +1 -1
  45. package/dist/core/extensions/loader.d.ts.map +1 -1
  46. package/dist/core/extensions/loader.js +10 -5
  47. package/dist/core/extensions/loader.js.map +1 -1
  48. package/dist/core/extensions/types.d.ts +23 -4
  49. package/dist/core/extensions/types.d.ts.map +1 -1
  50. package/dist/core/extensions/types.js.map +1 -1
  51. package/dist/core/keybindings.d.ts.map +1 -1
  52. package/dist/core/keybindings.js +2 -1
  53. package/dist/core/keybindings.js.map +1 -1
  54. package/dist/core/memory-engine/audit-export.d.ts +54 -0
  55. package/dist/core/memory-engine/audit-export.d.ts.map +1 -0
  56. package/dist/core/memory-engine/audit-export.js +85 -0
  57. package/dist/core/memory-engine/audit-export.js.map +1 -0
  58. package/dist/core/memory-engine/audit.d.ts.map +1 -1
  59. package/dist/core/memory-engine/audit.js +7 -8
  60. package/dist/core/memory-engine/audit.js.map +1 -1
  61. package/dist/core/memory-engine/engine.d.ts +2 -3
  62. package/dist/core/memory-engine/engine.d.ts.map +1 -1
  63. package/dist/core/memory-engine/engine.js +148 -50
  64. package/dist/core/memory-engine/engine.js.map +1 -1
  65. package/dist/core/memory-engine/inverted-index.d.ts.map +1 -1
  66. package/dist/core/memory-engine/inverted-index.js +67 -10
  67. package/dist/core/memory-engine/inverted-index.js.map +1 -1
  68. package/dist/core/memory-engine/keychain.d.ts.map +1 -1
  69. package/dist/core/memory-engine/keychain.js +1 -6
  70. package/dist/core/memory-engine/keychain.js.map +1 -1
  71. package/dist/core/memory-engine/observation-store.d.ts +1 -0
  72. package/dist/core/memory-engine/observation-store.d.ts.map +1 -1
  73. package/dist/core/memory-engine/observation-store.js +37 -8
  74. package/dist/core/memory-engine/observation-store.js.map +1 -1
  75. package/dist/core/memory-engine/scanner.d.ts.map +1 -1
  76. package/dist/core/memory-engine/scanner.js +25 -5
  77. package/dist/core/memory-engine/scanner.js.map +1 -1
  78. package/dist/core/memory-engine/security.d.ts.map +1 -1
  79. package/dist/core/memory-engine/security.js +131 -23
  80. package/dist/core/memory-engine/security.js.map +1 -1
  81. package/dist/core/memory-engine/session-indexer.d.ts.map +1 -1
  82. package/dist/core/memory-engine/session-indexer.js +8 -2
  83. package/dist/core/memory-engine/session-indexer.js.map +1 -1
  84. package/dist/core/memory-engine/tools.d.ts +2 -2
  85. package/dist/core/memory-engine/tools.d.ts.map +1 -1
  86. package/dist/core/memory-engine/tools.js +14 -7
  87. package/dist/core/memory-engine/tools.js.map +1 -1
  88. package/dist/core/memory-engine/vault.d.ts.map +1 -1
  89. package/dist/core/memory-engine/vault.js +2 -2
  90. package/dist/core/memory-engine/vault.js.map +1 -1
  91. package/dist/core/model-config.d.ts.map +1 -1
  92. package/dist/core/model-config.js +2 -1
  93. package/dist/core/model-config.js.map +1 -1
  94. package/dist/core/model-resolver.d.ts +1 -0
  95. package/dist/core/model-resolver.d.ts.map +1 -1
  96. package/dist/core/model-resolver.js +12 -6
  97. package/dist/core/model-resolver.js.map +1 -1
  98. package/dist/core/models-store.d.ts.map +1 -1
  99. package/dist/core/models-store.js +2 -1
  100. package/dist/core/models-store.js.map +1 -1
  101. package/dist/core/package-manager.d.ts.map +1 -1
  102. package/dist/core/package-manager.js +11 -6
  103. package/dist/core/package-manager.js.map +1 -1
  104. package/dist/core/pi-manifest.d.ts.map +1 -1
  105. package/dist/core/pi-manifest.js +2 -1
  106. package/dist/core/pi-manifest.js.map +1 -1
  107. package/dist/core/prompt-optimizer.d.ts +53 -0
  108. package/dist/core/prompt-optimizer.d.ts.map +1 -0
  109. package/dist/core/prompt-optimizer.js +138 -0
  110. package/dist/core/prompt-optimizer.js.map +1 -0
  111. package/dist/core/provider-attribution.d.ts.map +1 -1
  112. package/dist/core/provider-attribution.js +1 -1
  113. package/dist/core/provider-attribution.js.map +1 -1
  114. package/dist/core/remote-catalog-provider.d.ts.map +1 -1
  115. package/dist/core/remote-catalog-provider.js +2 -1
  116. package/dist/core/remote-catalog-provider.js.map +1 -1
  117. package/dist/core/resource-loader.d.ts +50 -0
  118. package/dist/core/resource-loader.d.ts.map +1 -1
  119. package/dist/core/resource-loader.js +233 -7
  120. package/dist/core/resource-loader.js.map +1 -1
  121. package/dist/core/sdk.d.ts +8 -0
  122. package/dist/core/sdk.d.ts.map +1 -1
  123. package/dist/core/sdk.js +11 -1
  124. package/dist/core/sdk.js.map +1 -1
  125. package/dist/core/session-manager.d.ts.map +1 -1
  126. package/dist/core/session-manager.js +2 -1
  127. package/dist/core/session-manager.js.map +1 -1
  128. package/dist/core/settings-diagnostics.d.ts +9 -0
  129. package/dist/core/settings-diagnostics.d.ts.map +1 -0
  130. package/dist/core/settings-diagnostics.js +21 -0
  131. package/dist/core/settings-diagnostics.js.map +1 -0
  132. package/dist/core/settings-manager.d.ts +52 -0
  133. package/dist/core/settings-manager.d.ts.map +1 -1
  134. package/dist/core/settings-manager.js +204 -10
  135. package/dist/core/settings-manager.js.map +1 -1
  136. package/dist/core/skills.d.ts.map +1 -1
  137. package/dist/core/skills.js +51 -33
  138. package/dist/core/skills.js.map +1 -1
  139. package/dist/core/slash-commands.d.ts.map +1 -1
  140. package/dist/core/slash-commands.js +2 -1
  141. package/dist/core/slash-commands.js.map +1 -1
  142. package/dist/core/tools/edit-diff.d.ts +0 -5
  143. package/dist/core/tools/edit-diff.d.ts.map +1 -1
  144. package/dist/core/tools/edit-diff.js +2 -5
  145. package/dist/core/tools/edit-diff.js.map +1 -1
  146. package/dist/core/tools/edit.d.ts.map +1 -1
  147. package/dist/core/tools/edit.js +20 -4
  148. package/dist/core/tools/edit.js.map +1 -1
  149. package/dist/core/tools/index.d.ts.map +1 -1
  150. package/dist/core/tools/index.js +2 -2
  151. package/dist/core/tools/index.js.map +1 -1
  152. package/dist/core/tools/project-memory-tool.d.ts.map +1 -1
  153. package/dist/core/tools/project-memory-tool.js +4 -1
  154. package/dist/core/tools/project-memory-tool.js.map +1 -1
  155. package/dist/core/trust-manager.d.ts.map +1 -1
  156. package/dist/core/trust-manager.js +2 -1
  157. package/dist/core/trust-manager.js.map +1 -1
  158. package/dist/core/workflows/agent-runner.d.ts +86 -0
  159. package/dist/core/workflows/agent-runner.d.ts.map +1 -0
  160. package/dist/core/workflows/agent-runner.js +225 -0
  161. package/dist/core/workflows/agent-runner.js.map +1 -0
  162. package/dist/core/workflows/builtins.d.ts +41 -0
  163. package/dist/core/workflows/builtins.d.ts.map +1 -0
  164. package/dist/core/workflows/builtins.js +282 -0
  165. package/dist/core/workflows/builtins.js.map +1 -0
  166. package/dist/core/workflows/goal-engine/engine.d.ts +134 -0
  167. package/dist/core/workflows/goal-engine/engine.d.ts.map +1 -0
  168. package/dist/core/workflows/goal-engine/engine.js +462 -0
  169. package/dist/core/workflows/goal-engine/engine.js.map +1 -0
  170. package/dist/core/workflows/goal-engine/errors.d.ts +7 -0
  171. package/dist/core/workflows/goal-engine/errors.d.ts.map +1 -0
  172. package/dist/core/workflows/goal-engine/errors.js +9 -0
  173. package/dist/core/workflows/goal-engine/errors.js.map +1 -0
  174. package/dist/core/workflows/goal-engine/index.d.ts +13 -0
  175. package/dist/core/workflows/goal-engine/index.d.ts.map +1 -0
  176. package/dist/core/workflows/goal-engine/index.js +8 -0
  177. package/dist/core/workflows/goal-engine/index.js.map +1 -0
  178. package/dist/core/workflows/goal-engine/persistence.d.ts +31 -0
  179. package/dist/core/workflows/goal-engine/persistence.d.ts.map +1 -0
  180. package/dist/core/workflows/goal-engine/persistence.js +145 -0
  181. package/dist/core/workflows/goal-engine/persistence.js.map +1 -0
  182. package/dist/core/workflows/goal-engine/prompts.d.ts +37 -0
  183. package/dist/core/workflows/goal-engine/prompts.d.ts.map +1 -0
  184. package/dist/core/workflows/goal-engine/prompts.js +131 -0
  185. package/dist/core/workflows/goal-engine/prompts.js.map +1 -0
  186. package/dist/core/workflows/goal-engine/state.d.ts +41 -0
  187. package/dist/core/workflows/goal-engine/state.d.ts.map +1 -0
  188. package/dist/core/workflows/goal-engine/state.js +397 -0
  189. package/dist/core/workflows/goal-engine/state.js.map +1 -0
  190. package/dist/core/workflows/goal-engine/tools.d.ts +31 -0
  191. package/dist/core/workflows/goal-engine/tools.d.ts.map +1 -0
  192. package/dist/core/workflows/goal-engine/tools.js +211 -0
  193. package/dist/core/workflows/goal-engine/tools.js.map +1 -0
  194. package/dist/core/workflows/goal-engine/types.d.ts +286 -0
  195. package/dist/core/workflows/goal-engine/types.d.ts.map +1 -0
  196. package/dist/core/workflows/goal-engine/types.js +17 -0
  197. package/dist/core/workflows/goal-engine/types.js.map +1 -0
  198. package/dist/core/workflows/goal-engine/worktree-fingerprint.d.ts +23 -0
  199. package/dist/core/workflows/goal-engine/worktree-fingerprint.d.ts.map +1 -0
  200. package/dist/core/workflows/goal-engine/worktree-fingerprint.js +72 -0
  201. package/dist/core/workflows/goal-engine/worktree-fingerprint.js.map +1 -0
  202. package/dist/core/workflows/graph-sync.d.ts +67 -0
  203. package/dist/core/workflows/graph-sync.d.ts.map +1 -0
  204. package/dist/core/workflows/graph-sync.js +188 -0
  205. package/dist/core/workflows/graph-sync.js.map +1 -0
  206. package/dist/core/workflows/graph.d.ts +143 -0
  207. package/dist/core/workflows/graph.d.ts.map +1 -0
  208. package/dist/core/workflows/graph.js +319 -0
  209. package/dist/core/workflows/graph.js.map +1 -0
  210. package/dist/core/workflows/index.d.ts +18 -0
  211. package/dist/core/workflows/index.d.ts.map +1 -0
  212. package/dist/core/workflows/index.js +14 -0
  213. package/dist/core/workflows/index.js.map +1 -0
  214. package/dist/core/workflows/journal.d.ts +68 -0
  215. package/dist/core/workflows/journal.d.ts.map +1 -0
  216. package/dist/core/workflows/journal.js +83 -0
  217. package/dist/core/workflows/journal.js.map +1 -0
  218. package/dist/core/workflows/manager.d.ts +112 -0
  219. package/dist/core/workflows/manager.d.ts.map +1 -0
  220. package/dist/core/workflows/manager.js +327 -0
  221. package/dist/core/workflows/manager.js.map +1 -0
  222. package/dist/core/workflows/resolve-script.d.ts +35 -0
  223. package/dist/core/workflows/resolve-script.d.ts.map +1 -0
  224. package/dist/core/workflows/resolve-script.js +66 -0
  225. package/dist/core/workflows/resolve-script.js.map +1 -0
  226. package/dist/core/workflows/runtime.d.ts +49 -0
  227. package/dist/core/workflows/runtime.d.ts.map +1 -0
  228. package/dist/core/workflows/runtime.js +301 -0
  229. package/dist/core/workflows/runtime.js.map +1 -0
  230. package/dist/core/workflows/shared-store.d.ts +31 -0
  231. package/dist/core/workflows/shared-store.d.ts.map +1 -0
  232. package/dist/core/workflows/shared-store.js +80 -0
  233. package/dist/core/workflows/shared-store.js.map +1 -0
  234. package/dist/core/workflows/subagent-host.d.ts +50 -0
  235. package/dist/core/workflows/subagent-host.d.ts.map +1 -0
  236. package/dist/core/workflows/subagent-host.js +225 -0
  237. package/dist/core/workflows/subagent-host.js.map +1 -0
  238. package/dist/core/workflows/tool-workflow.d.ts +22 -0
  239. package/dist/core/workflows/tool-workflow.d.ts.map +1 -0
  240. package/dist/core/workflows/tool-workflow.js +120 -0
  241. package/dist/core/workflows/tool-workflow.js.map +1 -0
  242. package/dist/core/workflows/types.d.ts +142 -0
  243. package/dist/core/workflows/types.d.ts.map +1 -0
  244. package/dist/core/workflows/types.js +26 -0
  245. package/dist/core/workflows/types.js.map +1 -0
  246. package/dist/core/workflows/worktree.d.ts +42 -0
  247. package/dist/core/workflows/worktree.d.ts.map +1 -0
  248. package/dist/core/workflows/worktree.js +107 -0
  249. package/dist/core/workflows/worktree.js.map +1 -0
  250. package/dist/extensions/llama/index.d.ts.map +1 -1
  251. package/dist/extensions/llama/index.js +2 -0
  252. package/dist/extensions/llama/index.js.map +1 -1
  253. package/dist/extensions/llama/provider.d.ts.map +1 -1
  254. package/dist/extensions/llama/provider.js +6 -2
  255. package/dist/extensions/llama/provider.js.map +1 -1
  256. package/dist/index.d.ts +16 -12
  257. package/dist/index.d.ts.map +1 -1
  258. package/dist/index.js +14 -11
  259. package/dist/index.js.map +1 -1
  260. package/dist/main.d.ts +4 -0
  261. package/dist/main.d.ts.map +1 -1
  262. package/dist/main.js +20 -15
  263. package/dist/main.js.map +1 -1
  264. package/dist/migrations.d.ts.map +1 -1
  265. package/dist/migrations.js +4 -3
  266. package/dist/migrations.js.map +1 -1
  267. package/dist/modes/interactive/components/home-screen.d.ts.map +1 -1
  268. package/dist/modes/interactive/components/home-screen.js +2 -2
  269. package/dist/modes/interactive/components/home-screen.js.map +1 -1
  270. package/dist/modes/interactive/components/model-selector.d.ts +2 -3
  271. package/dist/modes/interactive/components/model-selector.d.ts.map +1 -1
  272. package/dist/modes/interactive/components/model-selector.js +16 -6
  273. package/dist/modes/interactive/components/model-selector.js.map +1 -1
  274. package/dist/modes/interactive/components/prompt-optimization.d.ts +16 -0
  275. package/dist/modes/interactive/components/prompt-optimization.d.ts.map +1 -0
  276. package/dist/modes/interactive/components/prompt-optimization.js +46 -0
  277. package/dist/modes/interactive/components/prompt-optimization.js.map +1 -0
  278. package/dist/modes/interactive/components/settings-selector.d.ts +19 -1
  279. package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  280. package/dist/modes/interactive/components/settings-selector.js +247 -46
  281. package/dist/modes/interactive/components/settings-selector.js.map +1 -1
  282. package/dist/modes/interactive/components/settings-submenu.d.ts +72 -0
  283. package/dist/modes/interactive/components/settings-submenu.d.ts.map +1 -0
  284. package/dist/modes/interactive/components/settings-submenu.js +165 -0
  285. package/dist/modes/interactive/components/settings-submenu.js.map +1 -0
  286. package/dist/modes/interactive/components/thinking-selector.d.ts +8 -3
  287. package/dist/modes/interactive/components/thinking-selector.d.ts.map +1 -1
  288. package/dist/modes/interactive/components/thinking-selector.js +29 -3
  289. package/dist/modes/interactive/components/thinking-selector.js.map +1 -1
  290. package/dist/modes/interactive/external-editor.d.ts.map +1 -1
  291. package/dist/modes/interactive/external-editor.js +2 -1
  292. package/dist/modes/interactive/external-editor.js.map +1 -1
  293. package/dist/modes/interactive/interactive-mode.d.ts +24 -0
  294. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  295. package/dist/modes/interactive/interactive-mode.js +677 -34
  296. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  297. package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  298. package/dist/modes/interactive/theme/theme.js +4 -3
  299. package/dist/modes/interactive/theme/theme.js.map +1 -1
  300. package/dist/modes/json-event.d.ts +16 -13
  301. package/dist/modes/json-event.d.ts.map +1 -1
  302. package/dist/modes/json-event.js +20 -6
  303. package/dist/modes/json-event.js.map +1 -1
  304. package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
  305. package/dist/modes/rpc/rpc-mode.js +173 -0
  306. package/dist/modes/rpc/rpc-mode.js.map +1 -1
  307. package/dist/modes/rpc/rpc-types.d.ts +29 -0
  308. package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
  309. package/dist/modes/rpc/rpc-types.js.map +1 -1
  310. package/dist/package-manager-cli.d.ts.map +1 -1
  311. package/dist/package-manager-cli.js +1 -1
  312. package/dist/package-manager-cli.js.map +1 -1
  313. package/dist/skills/ada-engine/SKILL.md +153 -0
  314. package/dist/utils/frontmatter.d.ts.map +1 -1
  315. package/dist/utils/frontmatter.js +2 -1
  316. package/dist/utils/frontmatter.js.map +1 -1
  317. package/dist/utils/management-http.d.ts +5 -3
  318. package/dist/utils/management-http.d.ts.map +1 -1
  319. package/dist/utils/management-http.js +14 -10
  320. package/dist/utils/management-http.js.map +1 -1
  321. package/dist/utils/text.d.ts +8 -0
  322. package/dist/utils/text.d.ts.map +1 -0
  323. package/dist/utils/text.js +9 -0
  324. package/dist/utils/text.js.map +1 -0
  325. package/docs/compaction.md +15 -0
  326. package/docs/custom-provider.md +5 -2
  327. package/docs/extensions.md +11 -2
  328. package/docs/json.md +7 -2
  329. package/docs/llama-cpp.md +2 -0
  330. package/docs/models.md +8 -2
  331. package/docs/rpc.md +9 -3
  332. package/docs/settings.md +2 -2
  333. package/docs/skills.md +4 -3
  334. package/examples/extensions/border-status-editor.ts +1 -1
  335. package/examples/extensions/git-checkpoint.ts +2 -2
  336. package/examples/extensions/notify.ts +3 -1
  337. package/examples/extensions/subagent/README.md +1 -1
  338. package/examples/extensions/subagent/index.ts +6 -1
  339. package/examples/extensions/titlebar-spinner.ts +1 -1
  340. package/examples/rpc-extension-ui.ts +1 -1
  341. package/examples/sdk/06-extensions.ts +1 -1
  342. package/examples/sdk/README.md +1 -1
  343. package/npm-shrinkwrap.json +2 -12
  344. package/package.json +3 -3
@@ -24,24 +24,29 @@ import { normalizeToolResultImages } from "../utils/tool-result-images.js";
24
24
  import { formatNoApiKeyFoundMessage, formatNoModelSelectedMessage } from "./auth-guidance.js";
25
25
  import { executeBashWithOperations } from "./bash-executor.js";
26
26
  import { calculateContextTokens, collectEntriesForBranchSummary, compact, estimateContextTokens, estimateTokens, generateBranchSummary, prepareCompaction, shouldCompact, } from "./compaction/index.js";
27
- import { DEFAULT_THINKING_LEVEL } from "./defaults.js";
27
+ import { DEFAULT_THINKING_LEVEL, THINKING_LEVEL_OPTIONS } from "./defaults.js";
28
+ import { buildEnginePersonalityBlock } from "./engine-personality.js";
28
29
  import { exportSessionToHtml } from "./export-html/index.js";
29
30
  import { createToolHtmlRenderer } from "./export-html/tool-renderer.js";
30
31
  import { ExtensionRunner, wrapRegisteredTools, } from "./extensions/index.js";
31
32
  import { emitSessionShutdownEvent } from "./extensions/runner.js";
33
+ import { redactSensitive } from "./memory-engine/security.js";
34
+ import { createMemoryEngineTools } from "./memory-engine/tools.js";
35
+ import { expandVaultRefs } from "./memory-engine/vault.js";
32
36
  import { ModelRegistry } from "./model-registry.js";
37
+ import { loadProjectMemorySync, saveProjectMemory } from "./project-memory.js";
38
+ import { optimizeUserPrompt } from "./prompt-optimizer.js";
33
39
  import { expandPromptTemplate } from "./prompt-templates.js";
34
40
  import { CURRENT_SESSION_VERSION, getLatestCompactionEntry } from "./session-manager.js";
35
- import { loadProjectMemorySync, saveProjectMemory } from "./project-memory.js";
36
41
  import { createSyntheticSourceInfo } from "./source-info.js";
37
42
  import { buildSystemPrompt } from "./system-prompt.js";
38
43
  import { createLocalBashOperations } from "./tools/bash.js";
39
44
  import { createAllToolDefinitions } from "./tools/index.js";
40
- import { createMemoryEngineTools } from "./memory-engine/tools.js";
41
- import { redactSensitive } from "./memory-engine/security.js";
42
- import { expandVaultRefs } from "./memory-engine/vault.js";
43
45
  import { createToolDefinitionFromAgentTool } from "./tools/tool-definition-wrapper.js";
44
46
  import { addUsageToTotals, createUsageTotals } from "./usage-totals.js";
47
+ import { goalSessionTools } from "./workflows/goal-engine/tools.js";
48
+ import { createGraphTool, GraphSync } from "./workflows/graph-sync.js";
49
+ import { createWorkflowTool } from "./workflows/tool-workflow.js";
45
50
  /**
46
51
  * Parse a skill block from message text.
47
52
  * Returns null if the text doesn't contain a skill block.
@@ -75,8 +80,6 @@ function estimateMessagesTokens(messages) {
75
80
  // ============================================================================
76
81
  // Constants
77
82
  // ============================================================================
78
- /** Standard thinking levels */
79
- const THINKING_LEVELS = ["off", "minimal", "low", "medium", "high"];
80
83
  // ============================================================================
81
84
  // AgentSession Class
82
85
  // ============================================================================
@@ -114,6 +117,8 @@ export class AgentSession {
114
117
  _turnIndex = 0;
115
118
  _resourceLoader;
116
119
  _customTools;
120
+ _isSubagentSession = false;
121
+ _graphSync;
117
122
  _baseToolDefinitions = new Map();
118
123
  _cwd;
119
124
  _extensionRunnerRef;
@@ -130,6 +135,10 @@ export class AgentSession {
130
135
  _extensionErrorListener;
131
136
  _extensionErrorUnsubscriber;
132
137
  _modelRuntime;
138
+ /** Optimizador autónomo de prompts: "off" | "auto" | "clarify". */
139
+ _promptOptimizerMode = "off";
140
+ /** Clarificación pendiente (modo "clarify"): original + preguntas ya emitidas. */
141
+ _pendingClarification;
133
142
  // Tool registry for extension getTools/setTools
134
143
  _toolRegistry = new Map();
135
144
  _toolDefinitions = new Map();
@@ -146,6 +155,7 @@ export class AgentSession {
146
155
  this._scopedModels = config.scopedModels ?? [];
147
156
  this._resourceLoader = config.resourceLoader;
148
157
  this._customTools = config.customTools ?? [];
158
+ this._isSubagentSession = config.isSubagent === true;
149
159
  this._cwd = config.cwd;
150
160
  this._modelRuntime = config.modelRuntime;
151
161
  this._extensionRunnerRef = config.extensionRunnerRef;
@@ -163,6 +173,23 @@ export class AgentSession {
163
173
  activeToolNames: this._initialActiveToolNames,
164
174
  includeAllExtensionTools: true,
165
175
  });
176
+ // Los subagentes (goals/workflows) HEREDAN el modelo de la sesión principal:
177
+ // misma provider/modelId → misma auth → misma API key del chat.
178
+ this._resourceLoader.setSessionModelGetter?.(() => {
179
+ const m = this.model;
180
+ return m
181
+ ? {
182
+ provider: m.provider,
183
+ modelId: m.modelId ?? m.id ?? "",
184
+ }
185
+ : undefined;
186
+ });
187
+ // Cuando un run de workflow arranca (tool del agente o sub-tarea del
188
+ // goal), la sesión emite workflow_started — el desktop lo usa para
189
+ // abrir el panel Engine automáticamente.
190
+ this._resourceLoader.setOnWorkflowRunStarted?.((runId, name) => {
191
+ this._emit({ type: "workflow_started", runId, name });
192
+ });
166
193
  // Ada Memory Engine: lazy-initialize (stores, migration, session index)
167
194
  // and wire the LLM callback used by review/correction/flush. Best-effort
168
195
  // and never blocks startup.
@@ -190,8 +217,7 @@ export class AgentSession {
190
217
  content: [
191
218
  {
192
219
  type: "text",
193
- text: "Has introducido un dato sensible que no se almacenará en memoria. " +
194
- reason,
220
+ text: `Has introducido un dato sensible que no se almacenará en memoria. ${reason}`,
195
221
  },
196
222
  ],
197
223
  display: true,
@@ -285,6 +311,20 @@ export class AgentSession {
285
311
  }
286
312
  };
287
313
  this.agent.afterToolCall = async ({ toolCall, args, result, isError }) => {
314
+ // Auto-registro del grafo (best-effort, nunca bloquea el agente).
315
+ try {
316
+ if (!this._graphSync) {
317
+ this._graphSync = new GraphSync({
318
+ graph: () => this._resourceLoader.getGraph?.(),
319
+ cwd: this._cwd,
320
+ activeGoal: () => null, // resuelto en agent_settled vía syncGoal
321
+ });
322
+ }
323
+ void this._graphSync.onToolResult(toolCall.name, args, result, isError);
324
+ }
325
+ catch {
326
+ // best-effort
327
+ }
288
328
  const runner = this._extensionRunner;
289
329
  const hookResult = runner.hasHandlers("tool_result")
290
330
  ? await runner.emitToolResult({
@@ -350,6 +390,11 @@ export class AgentSession {
350
390
  followUp: [...this._followUpMessages],
351
391
  });
352
392
  }
393
+ async _emitSessionCompactFailed(event) {
394
+ if (this._extensionRunner.hasHandlers("session_compact_failed")) {
395
+ await this._extensionRunner.emit({ type: "session_compact_failed", ...event });
396
+ }
397
+ }
353
398
  _getIdleWaitPromise() {
354
399
  if (!this._idleWaitPromise) {
355
400
  this._idleWaitPromise = new Promise((resolve) => {
@@ -372,11 +417,88 @@ export class AgentSession {
372
417
  try {
373
418
  await this._extensionRunner.emit({ type: "agent_settled" });
374
419
  this._emit({ type: "agent_settled" });
420
+ // Ada Intelligence Engine: el GoalEngine es el ÚNICO dueño de la
421
+ // continuación automática del goal (pi-goal). No compite con retry/
422
+ // compaction/steer. Fire-and-forget; nunca bloquea ni rompe el chat.
423
+ try {
424
+ const goalEngine = this._isSubagentSession ? null : this._resourceLoader.getGoalEngine?.();
425
+ if (goalEngine) {
426
+ void this._maybeContinueGoal(goalEngine);
427
+ // Spine del grafo: sync automático del goal activo.
428
+ if (this._graphSync) {
429
+ void (async () => {
430
+ const goals = await goalEngine.list().catch(() => []);
431
+ const active = goals.find((g) => g.status === "active");
432
+ if (active) {
433
+ const full = await goalEngine.get(active.id).catch(() => null);
434
+ if (full) {
435
+ await this._graphSync?.syncGoal({
436
+ id: full.id,
437
+ goal: full.goal,
438
+ phase: full.phase,
439
+ tasks: full.tasks ?? [],
440
+ doneCriteria: full.doneCriteria ?? [],
441
+ });
442
+ }
443
+ }
444
+ })();
445
+ }
446
+ }
447
+ }
448
+ catch {
449
+ // best-effort
450
+ }
375
451
  }
376
452
  finally {
377
453
  this._resolveIdleWaitIfIdle();
378
454
  }
379
455
  }
456
+ /**
457
+ * Confirmación fail-closed del lanzamiento de workflows: emite un evento y
458
+ * espera respuesta humana hasta 15s; sin respuesta → NO lanzar.
459
+ */
460
+ _confirmWorkflowLaunch(prompt) {
461
+ return new Promise((resolve) => {
462
+ const timeout = setTimeout(() => {
463
+ this._confirmResolvers.delete(prompt);
464
+ resolve(false);
465
+ }, 15_000);
466
+ this._confirmResolvers.set(prompt, (ok) => {
467
+ clearTimeout(timeout);
468
+ this._confirmResolvers.delete(prompt);
469
+ resolve(ok);
470
+ });
471
+ this._emit({ type: "notification", message: `[workflow] ${prompt}`, level: "warning" });
472
+ });
473
+ }
474
+ /** Respuesta a una confirmación pendiente (la UI la llama). */
475
+ resolveWorkflowConfirmation(prompt, ok) {
476
+ const resolver = this._confirmResolvers.get(prompt);
477
+ if (!resolver)
478
+ return false;
479
+ resolver(ok);
480
+ return true;
481
+ }
482
+ /** Si hay un goal activo aprobado, avanza un turno del bucle. */
483
+ async _maybeContinueGoal(goalEngine) {
484
+ const goals = await goalEngine.list().catch(() => []);
485
+ const active = goals.find((g) => g.status === "active");
486
+ if (!active)
487
+ return;
488
+ // Freno por defecto del engine: si está desactivado no continúa.
489
+ if (!this.settingsManager.getGoalEngineEnabled())
490
+ return;
491
+ // markers anti-duplicados: solo avanzar si no hay un turno ya en curso.
492
+ if (this._isAgentRunActive)
493
+ return;
494
+ this._isAgentRunActive = true;
495
+ try {
496
+ await goalEngine.tick(active.id).catch(() => { });
497
+ }
498
+ finally {
499
+ this._isAgentRunActive = false;
500
+ }
501
+ }
380
502
  // =========================================================================
381
503
  // Automatic project memory (claude-mem style)
382
504
  // =========================================================================
@@ -488,6 +610,7 @@ Task: given the CURRENT MEMORY and the LATEST CONVERSATION TURN, produce the COM
488
610
  // Track last assistant message for auto-compaction check
489
611
  _lastAssistantMessage = undefined;
490
612
  _lastAutoMemorizeAt = 0;
613
+ _confirmResolvers = new Map();
491
614
  /** Vault expansion redactions by user-message id (frontend + persistence). */
492
615
  _vaultRedactions = new Map();
493
616
  _userMessageSeq = 0;
@@ -922,7 +1045,19 @@ Task: given the CURRENT MEMORY and the LATEST CONVERSATION TURN, produce the COM
922
1045
  }
923
1046
  const loaderSystemPrompt = this._resourceLoader.getSystemPrompt();
924
1047
  const loaderAppendSystemPrompt = this._resourceLoader.getAppendSystemPrompt();
925
- const appendSystemPrompt = loaderAppendSystemPrompt.length > 0 ? loaderAppendSystemPrompt.join("\n\n") : undefined;
1048
+ // Ada Intelligence Engine: PERSONALIDAD SIEMPRE presente (sesión principal).
1049
+ // No depende de que el modelo lea la skill: el árbol de decisión y los
1050
+ // procedimientos van inline en el prompt, cada turno.
1051
+ let enginePrompt;
1052
+ if (!this._isSubagentSession &&
1053
+ (this.settingsManager.getGoalEngineEnabled() || this.settingsManager.getWorkflowsEnabled())) {
1054
+ enginePrompt = buildEnginePersonalityBlock();
1055
+ }
1056
+ const appendSystemPrompt = enginePrompt
1057
+ ? [enginePrompt, ...(loaderAppendSystemPrompt.length > 0 ? [loaderAppendSystemPrompt.join("\n\n")] : [])].join("\n\n")
1058
+ : loaderAppendSystemPrompt.length > 0
1059
+ ? loaderAppendSystemPrompt.join("\n\n")
1060
+ : undefined;
926
1061
  const loadedSkills = this._resourceLoader.getSkills().skills;
927
1062
  const loadedContextFiles = this._resourceLoader.getAgentsFiles().agentsFiles;
928
1063
  this._baseSystemPromptOptions = {
@@ -1382,13 +1517,135 @@ Task: given the CURRENT MEMORY and the LATEST CONVERSATION TURN, produce the COM
1382
1517
  if (images.length === 0)
1383
1518
  images = undefined;
1384
1519
  }
1385
- await this.prompt(text, {
1520
+ // ── Optimizador autónomo de prompt ───────────────────────────────────
1521
+ // Mejora la petición del usuario (claridad, contexto, formato, criterios,
1522
+ // alcance) ANTES de que llegue al modelo. Fail-open: cualquier error o
1523
+ // timeout usa el prompt original — nunca degrada la experiencia.
1524
+ let finalText = text;
1525
+ if (this._promptOptimizerMode !== "off" && typeof text === "string" && !text.startsWith("/")) {
1526
+ try {
1527
+ const result = await this._runPromptOptimizer(text);
1528
+ if (result) {
1529
+ if (result.decision === "clarify" && this._promptOptimizerMode === "clarify") {
1530
+ // Preguntar primero y esperar respuesta; no enviamos aún.
1531
+ this._pendingClarification = { original: text, questions: result.questions };
1532
+ this._emit({
1533
+ type: "prompt_optimization",
1534
+ decision: "clarify",
1535
+ original: text,
1536
+ questions: result.questions,
1537
+ summary: result.summary,
1538
+ });
1539
+ const questions = result.questions.map((q, i) => `${i + 1}. ${q}`).join("\n");
1540
+ await this.sendCustomMessage({
1541
+ customType: "clarify-questions",
1542
+ content: [
1543
+ {
1544
+ type: "text",
1545
+ text: `🔎 Para dejar tu petición sin huecos, necesito aclarar:\n${questions}\n\nResponde a las preguntas y continuaré.`,
1546
+ },
1547
+ ],
1548
+ display: true,
1549
+ details: { q: result.questions },
1550
+ }, { triggerTurn: false });
1551
+ return;
1552
+ }
1553
+ if (result.enhanced && result.enhanced !== text) {
1554
+ finalText = result.enhanced;
1555
+ this._emit({
1556
+ type: "prompt_optimization",
1557
+ decision: "enhance",
1558
+ original: text,
1559
+ enhanced: result.enhanced,
1560
+ summary: result.summary,
1561
+ });
1562
+ // Mensaje persistente en el chat (CLI + desktop): muestra el
1563
+ // antes → después para que el usuario vea la mejora.
1564
+ void this.sendCustomMessage({
1565
+ customType: "prompt-optimization",
1566
+ content: [{ type: "text", text: result.enhanced }],
1567
+ display: true,
1568
+ details: { original: text, summary: result.summary },
1569
+ }, { triggerTurn: false });
1570
+ }
1571
+ }
1572
+ }
1573
+ catch {
1574
+ // fail-open: continuamos con el prompt original
1575
+ }
1576
+ }
1577
+ await this.prompt(finalText, {
1386
1578
  expandPromptTemplates: options?.expandPromptTemplates ?? false,
1387
1579
  streamingBehavior: options?.deliverAs,
1388
1580
  images,
1389
1581
  source: "extension",
1390
1582
  });
1391
1583
  }
1584
+ /**
1585
+ * Ejecuta el optimizador con el modelo de la sesión. Timeout de 12s,
1586
+ * nunca lanza (devuelve undefined → fail-open).
1587
+ */
1588
+ async _runPromptOptimizer(text) {
1589
+ const model = this.model;
1590
+ if (!model)
1591
+ return undefined;
1592
+ try {
1593
+ const answers = this._pendingClarification?.answers;
1594
+ const result = await Promise.race([
1595
+ optimizeUserPrompt({
1596
+ text,
1597
+ // En modo auto nunca bloqueamos al usuario: si el LLM pide
1598
+ // clarificar, forzamos una mejora con supuestos razonables.
1599
+ forceEnhance: this._promptOptimizerMode === "auto",
1600
+ context: {
1601
+ projectDir: this._cwd,
1602
+ // Si el usuario ya respondió a las preguntas de la ronda
1603
+ // anterior, se las pasamos al optimizador como contexto.
1604
+ recentMessages: answers?.length
1605
+ ? [...answers, ...(this._recentUserPrompts(3) ?? [])]
1606
+ : this._recentUserPrompts(3),
1607
+ },
1608
+ }, async (systemPrompt, userContent) => {
1609
+ const completed = await this._modelRuntime.completeSimple(model, {
1610
+ systemPrompt,
1611
+ messages: [{ role: "user", content: userContent }],
1612
+ signal: AbortSignal.timeout(20_000),
1613
+ });
1614
+ return contentText(completed.content, "").trim();
1615
+ }),
1616
+ new Promise((resolve) => setTimeout(() => resolve(undefined), 25_000)),
1617
+ ]);
1618
+ if (!result)
1619
+ return undefined;
1620
+ this._pendingClarification = undefined;
1621
+ return result;
1622
+ }
1623
+ catch {
1624
+ return undefined;
1625
+ }
1626
+ }
1627
+ /** Últimos prompts de usuario (para dar contexto al optimizador). */
1628
+ _recentUserPrompts(max) {
1629
+ const out = [];
1630
+ const messages = this.agent.state.messages;
1631
+ for (let i = messages.length - 1; i >= 0 && out.length < max; i--) {
1632
+ const m = messages[i];
1633
+ if (m.role === "user") {
1634
+ const t = contentText(m.content, "");
1635
+ if (t.trim())
1636
+ out.unshift(t.slice(0, 500));
1637
+ }
1638
+ }
1639
+ return out;
1640
+ }
1641
+ /** Ajusta el modo del optimizador autónomo de prompts. */
1642
+ setPromptOptimizerMode(mode) {
1643
+ this._promptOptimizerMode = mode;
1644
+ }
1645
+ /** Modo actual del optimizador autónomo de prompts. */
1646
+ getPromptOptimizerMode() {
1647
+ return this._promptOptimizerMode;
1648
+ }
1392
1649
  /**
1393
1650
  * Clear all queued messages and return them.
1394
1651
  * Useful for restoring to editor when user aborts.
@@ -1447,19 +1704,24 @@ Task: given the CURRENT MEMORY and the LATEST CONVERSATION TURN, produce the COM
1447
1704
  }
1448
1705
  /**
1449
1706
  * Set model directly.
1450
- * Validates that auth is configured, saves to session and settings.
1707
+ * Validates that auth is configured and saves to the session transcript.
1708
+ * Persists to global defaults only when options.persist is true.
1451
1709
  * @throws Error if no auth is configured for the model
1452
1710
  */
1453
- async setModel(model) {
1711
+ async setModel(model, options = {}) {
1454
1712
  if (!(await this._modelRuntime.checkAuth(model.provider))) {
1455
1713
  throw new Error(`No API key for ${model.provider}/${model.id}`);
1456
1714
  }
1457
1715
  const previousModel = this.model;
1458
- const thinkingLevel = this._getThinkingLevelForModelSwitch();
1716
+ const thinkingLevel = this._getThinkingLevelForModelSwitch(model);
1459
1717
  this.agent.state.model = model;
1460
1718
  this.sessionManager.appendModelChange(model.provider, model.id);
1461
- this.settingsManager.setDefaultModelAndProvider(model.provider, model.id);
1462
- // Re-clamp thinking level for new model's capabilities
1719
+ if (options.persist) {
1720
+ this.settingsManager.setDefaultModelAndProvider(model.provider, model.id);
1721
+ }
1722
+ // Re-clamp session thinking level for new model's capabilities.
1723
+ // Per-model thinking level overrides take priority over session carry-over.
1724
+ // Model persistence does not implicitly rewrite the global thinking default.
1463
1725
  this.setThinkingLevel(thinkingLevel);
1464
1726
  await this._emitModelSelect(model, previousModel, "set");
1465
1727
  }
@@ -1469,13 +1731,13 @@ Task: given the CURRENT MEMORY and the LATEST CONVERSATION TURN, produce the COM
1469
1731
  * @param direction - "forward" (default) or "backward"
1470
1732
  * @returns The new model info, or undefined if only one model available
1471
1733
  */
1472
- async cycleModel(direction = "forward") {
1734
+ async cycleModel(direction = "forward", options = {}) {
1473
1735
  if (this._scopedModels.length > 0) {
1474
- return this._cycleScopedModel(direction);
1736
+ return this._cycleScopedModel(direction, options);
1475
1737
  }
1476
- return this._cycleAvailableModel(direction);
1738
+ return this._cycleAvailableModel(direction, options);
1477
1739
  }
1478
- async _cycleScopedModel(direction) {
1740
+ async _cycleScopedModel(direction, options) {
1479
1741
  const availableIds = new Set(this._modelRuntime.getAvailableSnapshot().map((model) => `${model.provider}\0${model.id}`));
1480
1742
  const scopedModels = this._scopedModels.filter((scoped) => availableIds.has(`${scoped.model.provider}\0${scoped.model.id}`));
1481
1743
  if (scopedModels.length <= 1)
@@ -1487,20 +1749,23 @@ Task: given the CURRENT MEMORY and the LATEST CONVERSATION TURN, produce the COM
1487
1749
  const len = scopedModels.length;
1488
1750
  const nextIndex = direction === "forward" ? (currentIndex + 1) % len : (currentIndex - 1 + len) % len;
1489
1751
  const next = scopedModels[nextIndex];
1490
- const thinkingLevel = this._getThinkingLevelForModelSwitch(next.thinkingLevel);
1752
+ const thinkingLevel = this._getThinkingLevelForModelSwitch(next.model, next.thinkingLevel);
1491
1753
  // Apply model
1492
1754
  this.agent.state.model = next.model;
1493
1755
  this.sessionManager.appendModelChange(next.model.provider, next.model.id);
1494
- this.settingsManager.setDefaultModelAndProvider(next.model.provider, next.model.id);
1495
- // Apply thinking level.
1756
+ if (options.persist) {
1757
+ this.settingsManager.setDefaultModelAndProvider(next.model.provider, next.model.id);
1758
+ }
1759
+ // Apply session thinking level.
1496
1760
  // - Explicit scoped model thinking level overrides current session level
1497
1761
  // - Undefined scoped model thinking level inherits the current session preference
1498
1762
  // setThinkingLevel clamps to model capabilities.
1763
+ // Model persistence does not implicitly rewrite the global thinking default.
1499
1764
  this.setThinkingLevel(thinkingLevel);
1500
1765
  await this._emitModelSelect(next.model, currentModel, "cycle");
1501
1766
  return { model: next.model, thinkingLevel: this.thinkingLevel, isScoped: true };
1502
1767
  }
1503
- async _cycleAvailableModel(direction) {
1768
+ async _cycleAvailableModel(direction, options) {
1504
1769
  const availableModels = this._modelRuntime.getAvailableSnapshot();
1505
1770
  if (availableModels.length <= 1)
1506
1771
  return undefined;
@@ -1511,11 +1776,14 @@ Task: given the CURRENT MEMORY and the LATEST CONVERSATION TURN, produce the COM
1511
1776
  const len = availableModels.length;
1512
1777
  const nextIndex = direction === "forward" ? (currentIndex + 1) % len : (currentIndex - 1 + len) % len;
1513
1778
  const nextModel = availableModels[nextIndex];
1514
- const thinkingLevel = this._getThinkingLevelForModelSwitch();
1779
+ const thinkingLevel = this._getThinkingLevelForModelSwitch(nextModel);
1515
1780
  this.agent.state.model = nextModel;
1516
1781
  this.sessionManager.appendModelChange(nextModel.provider, nextModel.id);
1517
- this.settingsManager.setDefaultModelAndProvider(nextModel.provider, nextModel.id);
1518
- // Re-clamp thinking level for new model's capabilities
1782
+ if (options.persist) {
1783
+ this.settingsManager.setDefaultModelAndProvider(nextModel.provider, nextModel.id);
1784
+ }
1785
+ // Re-clamp session thinking level for new model's capabilities.
1786
+ // Model persistence does not implicitly rewrite the global thinking default.
1519
1787
  this.setThinkingLevel(thinkingLevel);
1520
1788
  await this._emitModelSelect(nextModel, currentModel, "cycle");
1521
1789
  return { model: nextModel, thinkingLevel: this.thinkingLevel, isScoped: false };
@@ -1526,20 +1794,21 @@ Task: given the CURRENT MEMORY and the LATEST CONVERSATION TURN, produce the COM
1526
1794
  /**
1527
1795
  * Set thinking level.
1528
1796
  * Clamps to model capabilities based on available thinking levels.
1529
- * Saves to session and settings only if the level actually changes.
1797
+ * Saves the clamped level to the session transcript only if the level actually changes.
1798
+ * Persists the requested level to global defaults only when options.persist is true.
1530
1799
  */
1531
- setThinkingLevel(level) {
1800
+ setThinkingLevel(level, options = {}) {
1532
1801
  const availableLevels = this.getAvailableThinkingLevels();
1533
1802
  const effectiveLevel = availableLevels.includes(level) ? level : this._clampThinkingLevel(level, availableLevels);
1534
1803
  // Only persist if actually changing
1535
1804
  const previousLevel = this.agent.state.thinkingLevel;
1536
1805
  const isChanging = effectiveLevel !== previousLevel;
1537
1806
  this.agent.state.thinkingLevel = effectiveLevel;
1807
+ if (options.persist) {
1808
+ this.settingsManager.setDefaultThinkingLevel(level);
1809
+ }
1538
1810
  if (isChanging) {
1539
1811
  this.sessionManager.appendThinkingLevelChange(effectiveLevel);
1540
- if (this.supportsThinking() || effectiveLevel !== "off") {
1541
- this.settingsManager.setDefaultThinkingLevel(effectiveLevel);
1542
- }
1543
1812
  this._emit({ type: "thinking_level_changed", level: effectiveLevel });
1544
1813
  void this._extensionRunner.emit({
1545
1814
  type: "thinking_level_select",
@@ -1552,14 +1821,14 @@ Task: given the CURRENT MEMORY and the LATEST CONVERSATION TURN, produce the COM
1552
1821
  * Cycle to next thinking level.
1553
1822
  * @returns New level, or undefined if model doesn't support thinking
1554
1823
  */
1555
- cycleThinkingLevel() {
1824
+ cycleThinkingLevel(options = {}) {
1556
1825
  if (!this.supportsThinking())
1557
1826
  return undefined;
1558
1827
  const levels = this.getAvailableThinkingLevels();
1559
1828
  const currentIndex = levels.indexOf(this.thinkingLevel);
1560
1829
  const nextIndex = (currentIndex + 1) % levels.length;
1561
1830
  const nextLevel = levels[nextIndex];
1562
- this.setThinkingLevel(nextLevel);
1831
+ this.setThinkingLevel(nextLevel, options);
1563
1832
  return nextLevel;
1564
1833
  }
1565
1834
  /**
@@ -1568,7 +1837,7 @@ Task: given the CURRENT MEMORY and the LATEST CONVERSATION TURN, produce the COM
1568
1837
  */
1569
1838
  getAvailableThinkingLevels() {
1570
1839
  if (!this.model)
1571
- return THINKING_LEVELS;
1840
+ return [...THINKING_LEVEL_OPTIONS];
1572
1841
  return getSupportedThinkingLevels(this.model);
1573
1842
  }
1574
1843
  /**
@@ -1577,10 +1846,17 @@ Task: given the CURRENT MEMORY and the LATEST CONVERSATION TURN, produce the COM
1577
1846
  supportsThinking() {
1578
1847
  return !!this.model?.reasoning;
1579
1848
  }
1580
- _getThinkingLevelForModelSwitch(explicitLevel) {
1849
+ _getThinkingLevelForModelSwitch(targetModel, explicitLevel) {
1581
1850
  if (explicitLevel !== undefined) {
1582
1851
  return explicitLevel;
1583
1852
  }
1853
+ // Per-model default takes priority when switching to a model that has one
1854
+ if (targetModel) {
1855
+ const perModel = this.settingsManager.getModelThinkingLevel(targetModel.provider, targetModel.id);
1856
+ if (perModel !== undefined) {
1857
+ return perModel;
1858
+ }
1859
+ }
1584
1860
  if (!this.supportsThinking()) {
1585
1861
  return this.settingsManager.getDefaultThinkingLevel() ?? DEFAULT_THINKING_LEVEL;
1586
1862
  }
@@ -1615,15 +1891,30 @@ Task: given the CURRENT MEMORY and the LATEST CONVERSATION TURN, produce the COM
1615
1891
  // =========================================================================
1616
1892
  // Compaction
1617
1893
  // =========================================================================
1894
+ /** Generate Pi's built-in compaction summary for manual and automatic compaction. */
1895
+ async _runDefaultCompaction(preparation, requestModel, apiKey, headers, customInstructions, signal, env, reason) {
1896
+ return compact(preparation, requestModel, apiKey, headers, customInstructions, signal, this.thinkingLevel, this.agent.streamFunction, env, this.settingsManager.getRetrySettings(), this._summarizationRetryCallbacks({ source: "compaction", reason }), undefined);
1897
+ }
1618
1898
  /**
1619
1899
  * Manually compact the session context.
1620
- * Aborts current agent operation first.
1900
+ *
1901
+ * This is the manual entry point used by `/compact`, RPC, and extensions. It is
1902
+ * separate from automatic threshold/overflow compaction, which enters through
1903
+ * `_checkCompaction()` and `_runAutoCompaction()`. After preparation and the
1904
+ * `session_before_compact` hook, both paths call the lower-level `compact()`
1905
+ * function imported from `./compaction/index.ts`, unless the hook cancels or
1906
+ * supplies a custom result.
1907
+ *
1908
+ * Aborts the current agent operation first. Manual compaction never retries or
1909
+ * continues the interrupted agent turn.
1910
+ *
1621
1911
  * @param customInstructions Optional instructions for the compaction summary
1622
1912
  */
1623
1913
  async compact(customInstructions) {
1624
1914
  await this.abort();
1625
1915
  this._compactionAbortController = new AbortController();
1626
1916
  this._emit({ type: "compaction_start", reason: "manual" });
1917
+ let fromExtension = false;
1627
1918
  try {
1628
1919
  if (!this.model) {
1629
1920
  throw new Error(formatNoModelSelectedMessage());
@@ -1641,7 +1932,6 @@ Task: given the CURRENT MEMORY and the LATEST CONVERSATION TURN, produce the COM
1641
1932
  throw new Error("Nothing to compact (session too small)");
1642
1933
  }
1643
1934
  let extensionCompaction;
1644
- let fromExtension = false;
1645
1935
  if (this._extensionRunner.hasHandlers("session_before_compact")) {
1646
1936
  const result = (await this._extensionRunner.emit({
1647
1937
  type: "session_before_compact",
@@ -1674,8 +1964,8 @@ Task: given the CURRENT MEMORY and the LATEST CONVERSATION TURN, produce the COM
1674
1964
  details = extensionCompaction.details;
1675
1965
  }
1676
1966
  else {
1677
- // Generate compaction result
1678
- const result = await compact(preparation, requestModel, apiKey, headers, customInstructions, this._compactionAbortController.signal, this.thinkingLevel, this.agent.streamFunction, env, this.settingsManager.getRetrySettings(), this._summarizationRetryCallbacks({ source: "compaction", reason: "manual" }));
1967
+ // Shared default summary generator, also used by automatic compaction.
1968
+ const result = await this._runDefaultCompaction(preparation, requestModel, apiKey, headers, customInstructions, this._compactionAbortController.signal, env, "manual");
1679
1969
  summary = result.summary;
1680
1970
  firstKeptEntryId = result.firstKeptEntryId;
1681
1971
  tokensBefore = result.tokensBefore;
@@ -1723,6 +2013,7 @@ Task: given the CURRENT MEMORY and the LATEST CONVERSATION TURN, produce the COM
1723
2013
  catch (error) {
1724
2014
  const message = error instanceof Error ? error.message : String(error);
1725
2015
  const aborted = message === "Compaction cancelled" || (error instanceof Error && error.name === "AbortError");
2016
+ const errorMessage = aborted ? undefined : `Compaction failed: ${message}`;
1726
2017
  this._compactionAbortController = undefined;
1727
2018
  this._emit({
1728
2019
  type: "compaction_end",
@@ -1730,7 +2021,14 @@ Task: given the CURRENT MEMORY and the LATEST CONVERSATION TURN, produce the COM
1730
2021
  result: undefined,
1731
2022
  aborted,
1732
2023
  willRetry: false,
1733
- errorMessage: aborted ? undefined : `Compaction failed: ${message}`,
2024
+ errorMessage,
2025
+ });
2026
+ await this._emitSessionCompactFailed({
2027
+ reason: "manual",
2028
+ errorMessage,
2029
+ aborted,
2030
+ willRetry: false,
2031
+ fromExtension,
1734
2032
  });
1735
2033
  throw error;
1736
2034
  }
@@ -1752,16 +2050,25 @@ Task: given the CURRENT MEMORY and the LATEST CONVERSATION TURN, produce the COM
1752
2050
  this._branchSummaryAbortController?.abort();
1753
2051
  }
1754
2052
  /**
1755
- * Check if compaction is needed and run it.
1756
- * Called after agent_end and before prompt submission.
2053
+ * Dispatch automatic compaction after `agent_end` or before prompt submission.
2054
+ * Manual compaction does not call this method; it enters through `compact()`.
1757
2055
  *
1758
- * Two cases:
1759
- * 1. Recoverable failure: LLM returned context overflow or stopped below its desired output limit;
1760
- * remove the assistant message, compact, and auto-retry once
1761
- * 2. Threshold: Context over threshold, compact, NO auto-retry (user continues manually)
2056
+ * Automatic cases:
2057
+ * 1. Overflow with retry: a context-overflow error or recoverable length stop;
2058
+ * remove the failed assistant message, compact, and retry the turn once.
2059
+ * 2. Overflow without retry: a successful response exceeded the configured
2060
+ * context window; compact but preserve the completed response.
2061
+ * 3. Threshold without retry: valid or estimated context usage crossed the
2062
+ * configured threshold; compact without retrying the completed response.
2063
+ *
2064
+ * Each case calls `_runAutoCompaction()`. After preparation and the
2065
+ * `session_before_compact` hook, that method calls the lower-level `compact()`
2066
+ * function imported from `./compaction/index.ts`, unless the hook cancels or
2067
+ * supplies a custom result.
1762
2068
  *
1763
2069
  * @param assistantMessage The assistant message to check
1764
2070
  * @param skipAbortedCheck If false, include aborted messages (for pre-prompt check). Default: true
2071
+ * @returns Whether the post-run loop should call `agent.continue()` for overflow recovery or queued messages
1765
2072
  */
1766
2073
  async _checkCompaction(assistantMessage, skipAbortedCheck = true) {
1767
2074
  const settings = this.settingsManager.getCompactionSettings();
@@ -1784,38 +2091,49 @@ Task: given the CURRENT MEMORY and the LATEST CONVERSATION TURN, produce the COM
1784
2091
  if (assistantIsFromBeforeCompaction) {
1785
2092
  return false;
1786
2093
  }
1787
- // Case 1: Recoverable failure. Explicit/silent context overflow still uses context metadata.
2094
+ // Automatic cases 1 and 2: context overflow.
1788
2095
  // A length stop is recoverable when output ended below the model's original desired limit,
1789
2096
  // independent of the configured context size or any context-clamped provider request limit.
1790
- // A successful response over the configured window should compact but must not retry: the
1791
- // assistant answer already completed and agent.continue() cannot continue from an assistant.
2097
+ const contextOverflow = sameModel && isContextOverflow(assistantMessage, contextWindow);
1792
2098
  const recoverableLength = sameModel && isRecoverableLength(assistantMessage, this.model?.maxTokens ?? 0);
1793
- if (sameModel && (isContextOverflow(assistantMessage, contextWindow) || recoverableLength)) {
2099
+ if (contextOverflow || recoverableLength) {
1794
2100
  const willRetry = assistantMessage.stopReason !== "stop";
2101
+ // Case 2: the response completed successfully. Compact, but do not retry because
2102
+ // agent.continue() cannot continue from a completed assistant response.
1795
2103
  if (!willRetry) {
1796
2104
  return await this._runAutoCompaction("overflow", false);
1797
2105
  }
1798
2106
  if (this._overflowRecoveryAttempted) {
2107
+ const errorMessage = contextOverflow
2108
+ ? "Context overflow recovery failed after one compact-and-retry attempt. Try reducing context or switching to a larger-context model."
2109
+ : "Truncated response recovery failed after one compact-and-retry attempt.";
1799
2110
  this._emit({
1800
2111
  type: "compaction_end",
1801
2112
  reason: "overflow",
1802
2113
  result: undefined,
1803
2114
  aborted: false,
1804
2115
  willRetry: false,
1805
- errorMessage: "Context overflow recovery failed after one compact-and-retry attempt. Try reducing context or switching to a larger-context model.",
2116
+ errorMessage,
2117
+ });
2118
+ await this._emitSessionCompactFailed({
2119
+ reason: "overflow",
2120
+ errorMessage,
2121
+ aborted: false,
2122
+ willRetry: false,
2123
+ fromExtension: false,
1806
2124
  });
1807
2125
  return false;
1808
2126
  }
2127
+ // Case 1: remove the failed or truncated message from agent state, compact, and
2128
+ // retry once. The message remains in session history but is excluded from retry context.
1809
2129
  this._overflowRecoveryAttempted = true;
1810
- // Remove the failed or truncated message from agent state. It remains in session history,
1811
- // but must not be included in the compact-and-retry context.
1812
2130
  const messages = this.agent.state.messages;
1813
2131
  if (messages.length > 0 && messages[messages.length - 1].role === "assistant") {
1814
2132
  this.agent.state.messages = messages.slice(0, -1);
1815
2133
  }
1816
2134
  return await this._runAutoCompaction("overflow", willRetry);
1817
2135
  }
1818
- // Case 2: Threshold - context is getting large
2136
+ // Case 3: threshold compaction without retry.
1819
2137
  // For error messages or all-zero usage messages, estimate from the last valid response.
1820
2138
  // This ensures sessions that hit persistent API errors (e.g. 529) or malformed zero-usage
1821
2139
  // responses can still compact and do not reset context accounting.
@@ -1824,16 +2142,18 @@ Task: given the CURRENT MEMORY and the LATEST CONVERSATION TURN, produce the COM
1824
2142
  if (assistantMessage.stopReason === "error" || directContextTokens === 0) {
1825
2143
  const messages = this.agent.state.messages;
1826
2144
  const estimate = estimateContextTokens(messages);
1827
- if (estimate.lastUsageIndex === null)
1828
- return false; // No usage data at all
1829
- // Verify the usage source is post-compaction. Kept pre-compaction messages
1830
- // have stale usage reflecting the old (larger) context and would falsely
1831
- // trigger compaction right after one just finished.
1832
- const usageMsg = messages[estimate.lastUsageIndex];
1833
- if (compactionEntry &&
1834
- usageMsg.role === "assistant" &&
1835
- usageMsg.timestamp <= new Date(compactionEntry.timestamp).getTime()) {
1836
- return false;
2145
+ // Without provider usage, estimate.tokens is the pure message-size estimate.
2146
+ // Only usage-backed estimates need the stale pre-compaction check.
2147
+ if (estimate.lastUsageIndex !== null) {
2148
+ // Verify the usage source is post-compaction. Kept pre-compaction messages
2149
+ // have stale usage reflecting the old (larger) context and would falsely
2150
+ // trigger compaction right after one just finished.
2151
+ const usageMsg = messages[estimate.lastUsageIndex];
2152
+ if (compactionEntry &&
2153
+ usageMsg.role === "assistant" &&
2154
+ usageMsg.timestamp <= new Date(compactionEntry.timestamp).getTime()) {
2155
+ return false;
2156
+ }
1837
2157
  }
1838
2158
  contextTokens = estimate.tokens;
1839
2159
  }
@@ -1846,11 +2166,19 @@ Task: given the CURRENT MEMORY and the LATEST CONVERSATION TURN, produce the COM
1846
2166
  return false;
1847
2167
  }
1848
2168
  /**
1849
- * Internal: Run auto-compaction with events.
2169
+ * Execute threshold or overflow compaction. Manual compaction uses
2170
+ * `AgentSession.compact()` instead. Both paths call the lower-level `compact()`
2171
+ * function imported from `./compaction/index.ts` after preparation and extension
2172
+ * interception.
2173
+ *
2174
+ * @param reason Automatic trigger selected by `_checkCompaction()`
2175
+ * @param willRetry Whether to continue the interrupted turn after overflow compaction
2176
+ * @returns Whether the post-run loop should call `agent.continue()`
1850
2177
  */
1851
2178
  async _runAutoCompaction(reason, willRetry) {
1852
2179
  const settings = this.settingsManager.getCompactionSettings();
1853
2180
  let started = false;
2181
+ let fromExtension = false;
1854
2182
  try {
1855
2183
  if (!this.model) {
1856
2184
  return false;
@@ -1865,7 +2193,6 @@ Task: given the CURRENT MEMORY and the LATEST CONVERSATION TURN, produce the COM
1865
2193
  this._autoCompactionAbortController = new AbortController();
1866
2194
  started = true;
1867
2195
  let extensionCompaction;
1868
- let fromExtension = false;
1869
2196
  if (this._extensionRunner.hasHandlers("session_before_compact")) {
1870
2197
  const extensionResult = (await this._extensionRunner.emit({
1871
2198
  type: "session_before_compact",
@@ -1884,6 +2211,12 @@ Task: given the CURRENT MEMORY and the LATEST CONVERSATION TURN, produce the COM
1884
2211
  aborted: true,
1885
2212
  willRetry: false,
1886
2213
  });
2214
+ await this._emitSessionCompactFailed({
2215
+ reason,
2216
+ aborted: true,
2217
+ willRetry: false,
2218
+ fromExtension: false,
2219
+ });
1887
2220
  return false;
1888
2221
  }
1889
2222
  if (extensionResult?.compaction) {
@@ -1905,8 +2238,8 @@ Task: given the CURRENT MEMORY and the LATEST CONVERSATION TURN, produce the COM
1905
2238
  details = extensionCompaction.details;
1906
2239
  }
1907
2240
  else {
1908
- // Generate compaction result
1909
- const compactResult = await compact(preparation, requestModel, apiKey, headers, undefined, this._autoCompactionAbortController.signal, this.thinkingLevel, this.agent.streamFunction, env, this.settingsManager.getRetrySettings(), this._summarizationRetryCallbacks({ source: "compaction", reason }));
2241
+ // Shared default summary generator, also used by manual compaction.
2242
+ const compactResult = await this._runDefaultCompaction(preparation, requestModel, apiKey, headers, undefined, this._autoCompactionAbortController.signal, env, reason);
1910
2243
  summary = compactResult.summary;
1911
2244
  firstKeptEntryId = compactResult.firstKeptEntryId;
1912
2245
  tokensBefore = compactResult.tokensBefore;
@@ -1921,6 +2254,12 @@ Task: given the CURRENT MEMORY and the LATEST CONVERSATION TURN, produce the COM
1921
2254
  aborted: true,
1922
2255
  willRetry: false,
1923
2256
  });
2257
+ await this._emitSessionCompactFailed({
2258
+ reason,
2259
+ aborted: true,
2260
+ willRetry: false,
2261
+ fromExtension,
2262
+ });
1924
2263
  return false;
1925
2264
  }
1926
2265
  this.sessionManager.appendCompaction(summary, firstKeptEntryId, tokensBefore, details, fromExtension, usage);
@@ -1967,15 +2306,23 @@ Task: given the CURRENT MEMORY and the LATEST CONVERSATION TURN, produce the COM
1967
2306
  catch (error) {
1968
2307
  const errorMessage = error instanceof Error ? error.message : "compaction failed";
1969
2308
  if (started) {
2309
+ const formattedErrorMessage = reason === "overflow"
2310
+ ? `Context overflow recovery failed: ${errorMessage}`
2311
+ : `Auto-compaction failed: ${errorMessage}`;
1970
2312
  this._emit({
1971
2313
  type: "compaction_end",
1972
2314
  reason,
1973
2315
  result: undefined,
1974
2316
  aborted: false,
1975
2317
  willRetry: false,
1976
- errorMessage: reason === "overflow"
1977
- ? `Context overflow recovery failed: ${errorMessage}`
1978
- : `Auto-compaction failed: ${errorMessage}`,
2318
+ errorMessage: formattedErrorMessage,
2319
+ });
2320
+ await this._emitSessionCompactFailed({
2321
+ reason,
2322
+ errorMessage: formattedErrorMessage,
2323
+ aborted: false,
2324
+ willRetry: false,
2325
+ fromExtension,
1979
2326
  });
1980
2327
  }
1981
2328
  return false;
@@ -2290,13 +2637,42 @@ Task: given the CURRENT MEMORY and the LATEST CONVERSATION TURN, produce the COM
2290
2637
  }
2291
2638
  // Ada Memory Engine tools (memory_add/replace/remove/search,
2292
2639
  // session_search, scratchpad, memory_status). Exposed when enabled.
2640
+ // Solo en la sesión principal: un subagente no debe poder mutar la
2641
+ // memoria durable del usuario (anti-recursión, igual que goal_* y
2642
+ // workflow abajo). Hasta ahora la propiedad se sostenía solo por la
2643
+ // allowlist de tools del call site, no por una guardia aquí.
2293
2644
  const memoryEngine = this._resourceLoader.getMemoryEngine();
2294
- if (memoryEngine && this.settingsManager.getProjectMemoryEnabled()) {
2645
+ if (memoryEngine && !this._isSubagentSession && this.settingsManager.getProjectMemoryEnabled()) {
2295
2646
  const engineTools = createMemoryEngineTools(memoryEngine);
2296
2647
  for (const [name, tool] of Object.entries(engineTools)) {
2297
2648
  this._baseToolDefinitions.set(name, tool);
2298
2649
  }
2299
2650
  }
2651
+ // Ada Intelligence Engine: tools del GoalEngine (resuelven el goal
2652
+ // activo por llamada). Registradas si el GoalEngine está habilitado.
2653
+ // Los subagentes NO reciben goal_*/workflow tools: solo el agente
2654
+ // principal orquesta el engine (anti-recursión y control de presupuesto).
2655
+ const goalEngine = this._isSubagentSession ? null : this._resourceLoader.getGoalEngine?.();
2656
+ if (goalEngine && this.settingsManager.getGoalEngineEnabled()) {
2657
+ for (const tool of goalSessionTools(goalEngine, () => this.sessionManager.getSessionId())) {
2658
+ this._baseToolDefinitions.set(tool.name, tool);
2659
+ }
2660
+ // graph_add_nodes: registros puntuales (QA_FINDING/RISK/DECISION).
2661
+ // El spine se auto-registra vía GraphSync — esto es complemento.
2662
+ if (this.settingsManager.getGraphEnabled()) {
2663
+ this._baseToolDefinitions.set("graph_add_nodes", createGraphTool(() => this._resourceLoader.getGraph?.()));
2664
+ }
2665
+ }
2666
+ // Tool `workflow` SOLO en la sesión principal: los subagentes no
2667
+ // orquestan workflows (anti-anidamiento y control de presupuesto).
2668
+ if (!this._isSubagentSession) {
2669
+ this._baseToolDefinitions.set("workflow", createWorkflowTool({
2670
+ getManager: () => this._resourceLoader.getWorkflowManager?.(),
2671
+ confirm: (prompt) => this._confirmWorkflowLaunch(prompt),
2672
+ agentDir: this._resourceLoader.getAgentDir(),
2673
+ sessionId: () => this.sessionManager.getSessionId(),
2674
+ }));
2675
+ }
2300
2676
  const extensionsResult = this._resourceLoader.getExtensions();
2301
2677
  if (options.flagValues) {
2302
2678
  for (const [name, value] of options.flagValues) {