@nghyane/arcane 0.1.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 (738) hide show
  1. package/CHANGELOG.md +3 -0
  2. package/README.md +12 -0
  3. package/examples/README.md +21 -0
  4. package/examples/custom-tools/README.md +109 -0
  5. package/examples/custom-tools/hello/index.ts +20 -0
  6. package/examples/custom-tools/todo/index.ts +206 -0
  7. package/examples/extensions/README.md +143 -0
  8. package/examples/extensions/api-demo.ts +89 -0
  9. package/examples/extensions/chalk-logger.ts +25 -0
  10. package/examples/extensions/hello.ts +32 -0
  11. package/examples/extensions/pirate.ts +43 -0
  12. package/examples/extensions/plan-mode.ts +550 -0
  13. package/examples/extensions/reload-runtime.ts +37 -0
  14. package/examples/extensions/todo.ts +296 -0
  15. package/examples/extensions/tools.ts +144 -0
  16. package/examples/extensions/with-deps/index.ts +35 -0
  17. package/examples/extensions/with-deps/package-lock.json +31 -0
  18. package/examples/extensions/with-deps/package.json +16 -0
  19. package/examples/hooks/README.md +56 -0
  20. package/examples/hooks/auto-commit-on-exit.ts +48 -0
  21. package/examples/hooks/confirm-destructive.ts +58 -0
  22. package/examples/hooks/custom-compaction.ts +116 -0
  23. package/examples/hooks/dirty-repo-guard.ts +51 -0
  24. package/examples/hooks/file-trigger.ts +40 -0
  25. package/examples/hooks/git-checkpoint.ts +52 -0
  26. package/examples/hooks/handoff.ts +150 -0
  27. package/examples/hooks/permission-gate.ts +33 -0
  28. package/examples/hooks/protected-paths.ts +29 -0
  29. package/examples/hooks/qna.ts +119 -0
  30. package/examples/hooks/status-line.ts +39 -0
  31. package/examples/sdk/01-minimal.ts +21 -0
  32. package/examples/sdk/02-custom-model.ts +49 -0
  33. package/examples/sdk/03-custom-prompt.ts +43 -0
  34. package/examples/sdk/04-skills.ts +43 -0
  35. package/examples/sdk/06-extensions.ts +80 -0
  36. package/examples/sdk/06-hooks.ts +61 -0
  37. package/examples/sdk/07-context-files.ts +35 -0
  38. package/examples/sdk/08-prompt-templates.ts +36 -0
  39. package/examples/sdk/08-slash-commands.ts +41 -0
  40. package/examples/sdk/09-api-keys-and-oauth.ts +54 -0
  41. package/examples/sdk/11-sessions.ts +47 -0
  42. package/examples/sdk/README.md +150 -0
  43. package/package.json +464 -0
  44. package/scripts/format-prompts.ts +184 -0
  45. package/scripts/generate-docs-index.ts +40 -0
  46. package/scripts/generate-template.ts +32 -0
  47. package/src/bun-imports.d.ts +22 -0
  48. package/src/capability/context-file.ts +39 -0
  49. package/src/capability/extension-module.ts +33 -0
  50. package/src/capability/extension.ts +47 -0
  51. package/src/capability/fs.ts +89 -0
  52. package/src/capability/hook.ts +39 -0
  53. package/src/capability/index.ts +432 -0
  54. package/src/capability/instruction.ts +36 -0
  55. package/src/capability/mcp.ts +60 -0
  56. package/src/capability/prompt.ts +34 -0
  57. package/src/capability/rule.ts +223 -0
  58. package/src/capability/settings.ts +34 -0
  59. package/src/capability/skill.ts +48 -0
  60. package/src/capability/slash-command.ts +39 -0
  61. package/src/capability/ssh.ts +41 -0
  62. package/src/capability/system-prompt.ts +34 -0
  63. package/src/capability/tool.ts +37 -0
  64. package/src/capability/types.ts +156 -0
  65. package/src/cli/args.ts +259 -0
  66. package/src/cli/config-cli.ts +357 -0
  67. package/src/cli/file-processor.ts +124 -0
  68. package/src/cli/grep-cli.ts +152 -0
  69. package/src/cli/jupyter-cli.ts +106 -0
  70. package/src/cli/list-models.ts +103 -0
  71. package/src/cli/plugin-cli.ts +661 -0
  72. package/src/cli/session-picker.ts +42 -0
  73. package/src/cli/setup-cli.ts +376 -0
  74. package/src/cli/shell-cli.ts +174 -0
  75. package/src/cli/ssh-cli.ts +179 -0
  76. package/src/cli/stats-cli.ts +197 -0
  77. package/src/cli/update-cli.ts +286 -0
  78. package/src/cli/web-search-cli.ts +143 -0
  79. package/src/cli.ts +65 -0
  80. package/src/commands/commit.ts +36 -0
  81. package/src/commands/config.ts +51 -0
  82. package/src/commands/grep.ts +41 -0
  83. package/src/commands/jupyter.ts +32 -0
  84. package/src/commands/launch.ts +139 -0
  85. package/src/commands/plugin.ts +70 -0
  86. package/src/commands/setup.ts +42 -0
  87. package/src/commands/shell.ts +29 -0
  88. package/src/commands/ssh.ts +60 -0
  89. package/src/commands/stats.ts +29 -0
  90. package/src/commands/update.ts +21 -0
  91. package/src/commands/web-search.ts +42 -0
  92. package/src/commit/agentic/agent.ts +311 -0
  93. package/src/commit/agentic/fallback.ts +96 -0
  94. package/src/commit/agentic/index.ts +359 -0
  95. package/src/commit/agentic/prompts/analyze-file.md +22 -0
  96. package/src/commit/agentic/prompts/session-user.md +25 -0
  97. package/src/commit/agentic/prompts/split-confirm.md +1 -0
  98. package/src/commit/agentic/prompts/system.md +38 -0
  99. package/src/commit/agentic/state.ts +69 -0
  100. package/src/commit/agentic/tools/analyze-file.ts +118 -0
  101. package/src/commit/agentic/tools/git-file-diff.ts +194 -0
  102. package/src/commit/agentic/tools/git-hunk.ts +50 -0
  103. package/src/commit/agentic/tools/git-overview.ts +84 -0
  104. package/src/commit/agentic/tools/index.ts +56 -0
  105. package/src/commit/agentic/tools/propose-changelog.ts +128 -0
  106. package/src/commit/agentic/tools/propose-commit.ts +154 -0
  107. package/src/commit/agentic/tools/recent-commits.ts +81 -0
  108. package/src/commit/agentic/tools/split-commit.ts +280 -0
  109. package/src/commit/agentic/topo-sort.ts +44 -0
  110. package/src/commit/agentic/trivial.ts +51 -0
  111. package/src/commit/agentic/validation.ts +200 -0
  112. package/src/commit/analysis/conventional.ts +165 -0
  113. package/src/commit/analysis/index.ts +4 -0
  114. package/src/commit/analysis/scope.ts +242 -0
  115. package/src/commit/analysis/summary.ts +112 -0
  116. package/src/commit/analysis/validation.ts +66 -0
  117. package/src/commit/changelog/detect.ts +37 -0
  118. package/src/commit/changelog/generate.ts +110 -0
  119. package/src/commit/changelog/index.ts +234 -0
  120. package/src/commit/changelog/parse.ts +44 -0
  121. package/src/commit/cli.ts +93 -0
  122. package/src/commit/git/diff.ts +148 -0
  123. package/src/commit/git/errors.ts +9 -0
  124. package/src/commit/git/index.ts +211 -0
  125. package/src/commit/git/operations.ts +54 -0
  126. package/src/commit/index.ts +5 -0
  127. package/src/commit/map-reduce/index.ts +64 -0
  128. package/src/commit/map-reduce/map-phase.ts +178 -0
  129. package/src/commit/map-reduce/reduce-phase.ts +145 -0
  130. package/src/commit/map-reduce/utils.ts +9 -0
  131. package/src/commit/message.ts +11 -0
  132. package/src/commit/model-selection.ts +69 -0
  133. package/src/commit/pipeline.ts +243 -0
  134. package/src/commit/prompts/analysis-system.md +148 -0
  135. package/src/commit/prompts/analysis-user.md +38 -0
  136. package/src/commit/prompts/changelog-system.md +50 -0
  137. package/src/commit/prompts/changelog-user.md +18 -0
  138. package/src/commit/prompts/file-observer-system.md +24 -0
  139. package/src/commit/prompts/file-observer-user.md +8 -0
  140. package/src/commit/prompts/reduce-system.md +50 -0
  141. package/src/commit/prompts/reduce-user.md +17 -0
  142. package/src/commit/prompts/summary-retry.md +3 -0
  143. package/src/commit/prompts/summary-system.md +38 -0
  144. package/src/commit/prompts/summary-user.md +13 -0
  145. package/src/commit/prompts/types-description.md +2 -0
  146. package/src/commit/types.ts +109 -0
  147. package/src/commit/utils/exclusions.ts +42 -0
  148. package/src/config/file-lock.ts +121 -0
  149. package/src/config/keybindings.ts +280 -0
  150. package/src/config/model-registry.ts +1140 -0
  151. package/src/config/model-resolver.ts +812 -0
  152. package/src/config/prompt-templates.ts +526 -0
  153. package/src/config/resolve-config-value.ts +92 -0
  154. package/src/config/settings-schema.ts +1236 -0
  155. package/src/config/settings.ts +706 -0
  156. package/src/config.ts +414 -0
  157. package/src/cursor.ts +239 -0
  158. package/src/debug/index.ts +431 -0
  159. package/src/debug/log-formatting.ts +60 -0
  160. package/src/debug/log-viewer.ts +903 -0
  161. package/src/debug/profiler.ts +158 -0
  162. package/src/debug/report-bundle.ts +366 -0
  163. package/src/debug/system-info.ts +112 -0
  164. package/src/discovery/agents-md.ts +68 -0
  165. package/src/discovery/agents.ts +199 -0
  166. package/src/discovery/builtin.ts +815 -0
  167. package/src/discovery/claude-plugins.ts +205 -0
  168. package/src/discovery/claude.ts +506 -0
  169. package/src/discovery/cline.ts +83 -0
  170. package/src/discovery/codex.ts +532 -0
  171. package/src/discovery/cursor.ts +218 -0
  172. package/src/discovery/gemini.ts +395 -0
  173. package/src/discovery/github.ts +117 -0
  174. package/src/discovery/helpers.ts +698 -0
  175. package/src/discovery/index.ts +89 -0
  176. package/src/discovery/mcp-json.ts +156 -0
  177. package/src/discovery/opencode.ts +394 -0
  178. package/src/discovery/ssh.ts +160 -0
  179. package/src/discovery/vscode.ts +103 -0
  180. package/src/discovery/windsurf.ts +145 -0
  181. package/src/exa/company.ts +57 -0
  182. package/src/exa/index.ts +62 -0
  183. package/src/exa/linkedin.ts +57 -0
  184. package/src/exa/mcp-client.ts +289 -0
  185. package/src/exa/render.ts +244 -0
  186. package/src/exa/researcher.ts +89 -0
  187. package/src/exa/search.ts +330 -0
  188. package/src/exa/types.ts +166 -0
  189. package/src/exa/websets.ts +247 -0
  190. package/src/exec/bash-executor.ts +184 -0
  191. package/src/exec/exec.ts +53 -0
  192. package/src/export/custom-share.ts +65 -0
  193. package/src/export/html/index.ts +162 -0
  194. package/src/export/html/template.css +889 -0
  195. package/src/export/html/template.generated.ts +2 -0
  196. package/src/export/html/template.html +45 -0
  197. package/src/export/html/template.js +1329 -0
  198. package/src/export/html/template.macro.ts +24 -0
  199. package/src/export/html/vendor/highlight.min.js +1213 -0
  200. package/src/export/html/vendor/marked.min.js +6 -0
  201. package/src/export/ttsr.ts +434 -0
  202. package/src/extensibility/custom-commands/bundled/review/index.ts +433 -0
  203. package/src/extensibility/custom-commands/index.ts +15 -0
  204. package/src/extensibility/custom-commands/loader.ts +231 -0
  205. package/src/extensibility/custom-commands/types.ts +111 -0
  206. package/src/extensibility/custom-tools/index.ts +22 -0
  207. package/src/extensibility/custom-tools/loader.ts +235 -0
  208. package/src/extensibility/custom-tools/types.ts +226 -0
  209. package/src/extensibility/custom-tools/wrapper.ts +45 -0
  210. package/src/extensibility/extensions/index.ts +136 -0
  211. package/src/extensibility/extensions/loader.ts +520 -0
  212. package/src/extensibility/extensions/runner.ts +774 -0
  213. package/src/extensibility/extensions/types.ts +1293 -0
  214. package/src/extensibility/extensions/wrapper.ts +188 -0
  215. package/src/extensibility/hooks/index.ts +16 -0
  216. package/src/extensibility/hooks/loader.ts +273 -0
  217. package/src/extensibility/hooks/runner.ts +441 -0
  218. package/src/extensibility/hooks/tool-wrapper.ts +106 -0
  219. package/src/extensibility/hooks/types.ts +817 -0
  220. package/src/extensibility/plugins/doctor.ts +65 -0
  221. package/src/extensibility/plugins/git-url.ts +281 -0
  222. package/src/extensibility/plugins/index.ts +33 -0
  223. package/src/extensibility/plugins/installer.ts +192 -0
  224. package/src/extensibility/plugins/loader.ts +338 -0
  225. package/src/extensibility/plugins/manager.ts +716 -0
  226. package/src/extensibility/plugins/parser.ts +105 -0
  227. package/src/extensibility/plugins/types.ts +190 -0
  228. package/src/extensibility/skills.ts +385 -0
  229. package/src/extensibility/slash-commands.ts +287 -0
  230. package/src/extensibility/tool-proxy.ts +25 -0
  231. package/src/index.ts +275 -0
  232. package/src/internal-urls/agent-protocol.ts +136 -0
  233. package/src/internal-urls/artifact-protocol.ts +97 -0
  234. package/src/internal-urls/docs-index.generated.ts +54 -0
  235. package/src/internal-urls/docs-protocol.ts +84 -0
  236. package/src/internal-urls/index.ts +31 -0
  237. package/src/internal-urls/json-query.ts +126 -0
  238. package/src/internal-urls/memory-protocol.ts +133 -0
  239. package/src/internal-urls/router.ts +70 -0
  240. package/src/internal-urls/rule-protocol.ts +55 -0
  241. package/src/internal-urls/skill-protocol.ts +111 -0
  242. package/src/internal-urls/types.ts +52 -0
  243. package/src/ipy/executor.ts +556 -0
  244. package/src/ipy/gateway-coordinator.ts +426 -0
  245. package/src/ipy/kernel.ts +892 -0
  246. package/src/ipy/modules.ts +109 -0
  247. package/src/ipy/prelude.py +831 -0
  248. package/src/ipy/prelude.ts +3 -0
  249. package/src/ipy/runtime.ts +222 -0
  250. package/src/lsp/client.ts +867 -0
  251. package/src/lsp/clients/biome-client.ts +202 -0
  252. package/src/lsp/clients/index.ts +50 -0
  253. package/src/lsp/clients/lsp-linter-client.ts +93 -0
  254. package/src/lsp/clients/swiftlint-client.ts +120 -0
  255. package/src/lsp/config.ts +397 -0
  256. package/src/lsp/defaults.json +464 -0
  257. package/src/lsp/edits.ts +109 -0
  258. package/src/lsp/index.ts +1268 -0
  259. package/src/lsp/lspmux.ts +250 -0
  260. package/src/lsp/render.ts +689 -0
  261. package/src/lsp/types.ts +414 -0
  262. package/src/lsp/utils.ts +549 -0
  263. package/src/main.ts +773 -0
  264. package/src/mcp/client.ts +239 -0
  265. package/src/mcp/config-writer.ts +215 -0
  266. package/src/mcp/config.ts +363 -0
  267. package/src/mcp/index.ts +55 -0
  268. package/src/mcp/json-rpc.ts +84 -0
  269. package/src/mcp/loader.ts +124 -0
  270. package/src/mcp/manager.ts +490 -0
  271. package/src/mcp/oauth-discovery.ts +274 -0
  272. package/src/mcp/oauth-flow.ts +229 -0
  273. package/src/mcp/render.ts +123 -0
  274. package/src/mcp/tool-bridge.ts +372 -0
  275. package/src/mcp/tool-cache.ts +121 -0
  276. package/src/mcp/transports/http.ts +332 -0
  277. package/src/mcp/transports/index.ts +6 -0
  278. package/src/mcp/transports/stdio.ts +281 -0
  279. package/src/mcp/types.ts +248 -0
  280. package/src/memories/index.ts +1099 -0
  281. package/src/memories/storage.ts +563 -0
  282. package/src/modes/components/agent-dashboard.ts +1130 -0
  283. package/src/modes/components/assistant-message.ts +144 -0
  284. package/src/modes/components/bash-execution.ts +218 -0
  285. package/src/modes/components/bordered-loader.ts +41 -0
  286. package/src/modes/components/branch-summary-message.ts +45 -0
  287. package/src/modes/components/codemode-group.ts +369 -0
  288. package/src/modes/components/compaction-summary-message.ts +51 -0
  289. package/src/modes/components/countdown-timer.ts +46 -0
  290. package/src/modes/components/custom-editor.ts +181 -0
  291. package/src/modes/components/custom-message.ts +91 -0
  292. package/src/modes/components/diff.ts +186 -0
  293. package/src/modes/components/dynamic-border.ts +25 -0
  294. package/src/modes/components/extensions/extension-dashboard.ts +325 -0
  295. package/src/modes/components/extensions/extension-list.ts +484 -0
  296. package/src/modes/components/extensions/index.ts +9 -0
  297. package/src/modes/components/extensions/inspector-panel.ts +321 -0
  298. package/src/modes/components/extensions/state-manager.ts +586 -0
  299. package/src/modes/components/extensions/types.ts +191 -0
  300. package/src/modes/components/footer.ts +315 -0
  301. package/src/modes/components/history-search.ts +157 -0
  302. package/src/modes/components/hook-editor.ts +101 -0
  303. package/src/modes/components/hook-input.ts +72 -0
  304. package/src/modes/components/hook-message.ts +100 -0
  305. package/src/modes/components/hook-selector.ts +155 -0
  306. package/src/modes/components/index.ts +41 -0
  307. package/src/modes/components/keybinding-hints.ts +65 -0
  308. package/src/modes/components/login-dialog.ts +164 -0
  309. package/src/modes/components/mcp-add-wizard.ts +1295 -0
  310. package/src/modes/components/model-selector.ts +625 -0
  311. package/src/modes/components/oauth-selector.ts +210 -0
  312. package/src/modes/components/plugin-settings.ts +477 -0
  313. package/src/modes/components/python-execution.ts +196 -0
  314. package/src/modes/components/queue-mode-selector.ts +56 -0
  315. package/src/modes/components/read-tool-group.ts +119 -0
  316. package/src/modes/components/session-selector.ts +242 -0
  317. package/src/modes/components/settings-defs.ts +340 -0
  318. package/src/modes/components/settings-selector.ts +529 -0
  319. package/src/modes/components/show-images-selector.ts +45 -0
  320. package/src/modes/components/skill-message.ts +90 -0
  321. package/src/modes/components/status-line/index.ts +4 -0
  322. package/src/modes/components/status-line/presets.ts +94 -0
  323. package/src/modes/components/status-line/segments.ts +352 -0
  324. package/src/modes/components/status-line/separators.ts +55 -0
  325. package/src/modes/components/status-line/types.ts +75 -0
  326. package/src/modes/components/status-line-segment-editor.ts +354 -0
  327. package/src/modes/components/status-line.ts +421 -0
  328. package/src/modes/components/theme-selector.ts +63 -0
  329. package/src/modes/components/thinking-selector.ts +64 -0
  330. package/src/modes/components/todo-display.ts +115 -0
  331. package/src/modes/components/todo-reminder.ts +40 -0
  332. package/src/modes/components/tool-execution.ts +703 -0
  333. package/src/modes/components/tree-selector.ts +904 -0
  334. package/src/modes/components/ttsr-notification.ts +80 -0
  335. package/src/modes/components/user-message-selector.ts +146 -0
  336. package/src/modes/components/user-message.ts +22 -0
  337. package/src/modes/components/visual-truncate.ts +63 -0
  338. package/src/modes/components/welcome.ts +247 -0
  339. package/src/modes/controllers/command-controller.ts +1120 -0
  340. package/src/modes/controllers/event-controller.ts +479 -0
  341. package/src/modes/controllers/extension-ui-controller.ts +778 -0
  342. package/src/modes/controllers/input-controller.ts +671 -0
  343. package/src/modes/controllers/mcp-command-controller.ts +1315 -0
  344. package/src/modes/controllers/selector-controller.ts +712 -0
  345. package/src/modes/controllers/ssh-command-controller.ts +452 -0
  346. package/src/modes/index.ts +15 -0
  347. package/src/modes/interactive-mode.ts +1027 -0
  348. package/src/modes/print-mode.ts +191 -0
  349. package/src/modes/rpc/rpc-client.ts +583 -0
  350. package/src/modes/rpc/rpc-mode.ts +700 -0
  351. package/src/modes/rpc/rpc-types.ts +236 -0
  352. package/src/modes/theme/dark.json +95 -0
  353. package/src/modes/theme/defaults/alabaster.json +93 -0
  354. package/src/modes/theme/defaults/amethyst.json +96 -0
  355. package/src/modes/theme/defaults/anthracite.json +93 -0
  356. package/src/modes/theme/defaults/basalt.json +91 -0
  357. package/src/modes/theme/defaults/birch.json +95 -0
  358. package/src/modes/theme/defaults/dark-abyss.json +91 -0
  359. package/src/modes/theme/defaults/dark-arctic.json +104 -0
  360. package/src/modes/theme/defaults/dark-aurora.json +95 -0
  361. package/src/modes/theme/defaults/dark-catppuccin.json +107 -0
  362. package/src/modes/theme/defaults/dark-cavern.json +91 -0
  363. package/src/modes/theme/defaults/dark-copper.json +95 -0
  364. package/src/modes/theme/defaults/dark-cosmos.json +90 -0
  365. package/src/modes/theme/defaults/dark-cyberpunk.json +102 -0
  366. package/src/modes/theme/defaults/dark-dracula.json +98 -0
  367. package/src/modes/theme/defaults/dark-eclipse.json +91 -0
  368. package/src/modes/theme/defaults/dark-ember.json +95 -0
  369. package/src/modes/theme/defaults/dark-equinox.json +90 -0
  370. package/src/modes/theme/defaults/dark-forest.json +96 -0
  371. package/src/modes/theme/defaults/dark-github.json +105 -0
  372. package/src/modes/theme/defaults/dark-gruvbox.json +112 -0
  373. package/src/modes/theme/defaults/dark-lavender.json +95 -0
  374. package/src/modes/theme/defaults/dark-lunar.json +89 -0
  375. package/src/modes/theme/defaults/dark-midnight.json +95 -0
  376. package/src/modes/theme/defaults/dark-monochrome.json +94 -0
  377. package/src/modes/theme/defaults/dark-monokai.json +98 -0
  378. package/src/modes/theme/defaults/dark-nebula.json +90 -0
  379. package/src/modes/theme/defaults/dark-nord.json +97 -0
  380. package/src/modes/theme/defaults/dark-ocean.json +101 -0
  381. package/src/modes/theme/defaults/dark-one.json +100 -0
  382. package/src/modes/theme/defaults/dark-rainforest.json +91 -0
  383. package/src/modes/theme/defaults/dark-reef.json +91 -0
  384. package/src/modes/theme/defaults/dark-retro.json +92 -0
  385. package/src/modes/theme/defaults/dark-rose-pine.json +96 -0
  386. package/src/modes/theme/defaults/dark-sakura.json +95 -0
  387. package/src/modes/theme/defaults/dark-slate.json +95 -0
  388. package/src/modes/theme/defaults/dark-solarized.json +97 -0
  389. package/src/modes/theme/defaults/dark-solstice.json +90 -0
  390. package/src/modes/theme/defaults/dark-starfall.json +91 -0
  391. package/src/modes/theme/defaults/dark-sunset.json +99 -0
  392. package/src/modes/theme/defaults/dark-swamp.json +90 -0
  393. package/src/modes/theme/defaults/dark-synthwave.json +103 -0
  394. package/src/modes/theme/defaults/dark-taiga.json +91 -0
  395. package/src/modes/theme/defaults/dark-terminal.json +95 -0
  396. package/src/modes/theme/defaults/dark-tokyo-night.json +101 -0
  397. package/src/modes/theme/defaults/dark-tundra.json +91 -0
  398. package/src/modes/theme/defaults/dark-twilight.json +91 -0
  399. package/src/modes/theme/defaults/dark-volcanic.json +91 -0
  400. package/src/modes/theme/defaults/graphite.json +92 -0
  401. package/src/modes/theme/defaults/index.ts +195 -0
  402. package/src/modes/theme/defaults/light-arctic.json +107 -0
  403. package/src/modes/theme/defaults/light-aurora-day.json +91 -0
  404. package/src/modes/theme/defaults/light-canyon.json +91 -0
  405. package/src/modes/theme/defaults/light-catppuccin.json +106 -0
  406. package/src/modes/theme/defaults/light-cirrus.json +90 -0
  407. package/src/modes/theme/defaults/light-coral.json +95 -0
  408. package/src/modes/theme/defaults/light-cyberpunk.json +96 -0
  409. package/src/modes/theme/defaults/light-dawn.json +90 -0
  410. package/src/modes/theme/defaults/light-dunes.json +91 -0
  411. package/src/modes/theme/defaults/light-eucalyptus.json +95 -0
  412. package/src/modes/theme/defaults/light-forest.json +100 -0
  413. package/src/modes/theme/defaults/light-frost.json +95 -0
  414. package/src/modes/theme/defaults/light-github.json +115 -0
  415. package/src/modes/theme/defaults/light-glacier.json +91 -0
  416. package/src/modes/theme/defaults/light-gruvbox.json +108 -0
  417. package/src/modes/theme/defaults/light-haze.json +90 -0
  418. package/src/modes/theme/defaults/light-honeycomb.json +95 -0
  419. package/src/modes/theme/defaults/light-lagoon.json +91 -0
  420. package/src/modes/theme/defaults/light-lavender.json +95 -0
  421. package/src/modes/theme/defaults/light-meadow.json +91 -0
  422. package/src/modes/theme/defaults/light-mint.json +95 -0
  423. package/src/modes/theme/defaults/light-monochrome.json +101 -0
  424. package/src/modes/theme/defaults/light-ocean.json +99 -0
  425. package/src/modes/theme/defaults/light-one.json +99 -0
  426. package/src/modes/theme/defaults/light-opal.json +91 -0
  427. package/src/modes/theme/defaults/light-orchard.json +91 -0
  428. package/src/modes/theme/defaults/light-paper.json +95 -0
  429. package/src/modes/theme/defaults/light-prism.json +90 -0
  430. package/src/modes/theme/defaults/light-retro.json +98 -0
  431. package/src/modes/theme/defaults/light-sand.json +95 -0
  432. package/src/modes/theme/defaults/light-savanna.json +91 -0
  433. package/src/modes/theme/defaults/light-solarized.json +102 -0
  434. package/src/modes/theme/defaults/light-soleil.json +90 -0
  435. package/src/modes/theme/defaults/light-sunset.json +99 -0
  436. package/src/modes/theme/defaults/light-synthwave.json +98 -0
  437. package/src/modes/theme/defaults/light-tokyo-night.json +111 -0
  438. package/src/modes/theme/defaults/light-wetland.json +91 -0
  439. package/src/modes/theme/defaults/light-zenith.json +89 -0
  440. package/src/modes/theme/defaults/limestone.json +94 -0
  441. package/src/modes/theme/defaults/mahogany.json +97 -0
  442. package/src/modes/theme/defaults/marble.json +93 -0
  443. package/src/modes/theme/defaults/obsidian.json +91 -0
  444. package/src/modes/theme/defaults/onyx.json +91 -0
  445. package/src/modes/theme/defaults/pearl.json +93 -0
  446. package/src/modes/theme/defaults/porcelain.json +91 -0
  447. package/src/modes/theme/defaults/quartz.json +96 -0
  448. package/src/modes/theme/defaults/sandstone.json +95 -0
  449. package/src/modes/theme/defaults/titanium.json +90 -0
  450. package/src/modes/theme/light.json +93 -0
  451. package/src/modes/theme/mermaid-cache.ts +111 -0
  452. package/src/modes/theme/theme-schema.json +429 -0
  453. package/src/modes/theme/theme.ts +2333 -0
  454. package/src/modes/types.ts +216 -0
  455. package/src/modes/utils/ui-helpers.ts +529 -0
  456. package/src/patch/applicator.ts +1482 -0
  457. package/src/patch/diff.ts +425 -0
  458. package/src/patch/fuzzy.ts +784 -0
  459. package/src/patch/hashline.ts +972 -0
  460. package/src/patch/index.ts +964 -0
  461. package/src/patch/normalize.ts +397 -0
  462. package/src/patch/normative.ts +72 -0
  463. package/src/patch/parser.ts +532 -0
  464. package/src/patch/shared.ts +400 -0
  465. package/src/patch/types.ts +292 -0
  466. package/src/priority.json +35 -0
  467. package/src/prompts/agents/explore.md +48 -0
  468. package/src/prompts/agents/frontmatter.md +9 -0
  469. package/src/prompts/agents/init.md +36 -0
  470. package/src/prompts/agents/librarian.md +53 -0
  471. package/src/prompts/agents/oracle.md +51 -0
  472. package/src/prompts/agents/reviewer.md +70 -0
  473. package/src/prompts/agents/task.md +14 -0
  474. package/src/prompts/compaction/branch-summary-context.md +5 -0
  475. package/src/prompts/compaction/branch-summary-preamble.md +2 -0
  476. package/src/prompts/compaction/branch-summary.md +30 -0
  477. package/src/prompts/compaction/compaction-short-summary.md +9 -0
  478. package/src/prompts/compaction/compaction-summary-context.md +5 -0
  479. package/src/prompts/compaction/compaction-summary.md +38 -0
  480. package/src/prompts/compaction/compaction-turn-prefix.md +17 -0
  481. package/src/prompts/compaction/compaction-update-summary.md +45 -0
  482. package/src/prompts/memories/consolidation.md +30 -0
  483. package/src/prompts/memories/read_path.md +11 -0
  484. package/src/prompts/memories/stage_one_input.md +6 -0
  485. package/src/prompts/memories/stage_one_system.md +21 -0
  486. package/src/prompts/review-request.md +64 -0
  487. package/src/prompts/system/agent-creation-architect.md +65 -0
  488. package/src/prompts/system/agent-creation-user.md +6 -0
  489. package/src/prompts/system/custom-system-prompt.md +68 -0
  490. package/src/prompts/system/file-operations.md +10 -0
  491. package/src/prompts/system/subagent-submit-reminder.md +11 -0
  492. package/src/prompts/system/subagent-system-prompt.md +31 -0
  493. package/src/prompts/system/subagent-user-prompt.md +8 -0
  494. package/src/prompts/system/summarization-system.md +3 -0
  495. package/src/prompts/system/system-prompt.md +300 -0
  496. package/src/prompts/system/title-system.md +2 -0
  497. package/src/prompts/system/ttsr-interrupt.md +7 -0
  498. package/src/prompts/system/web-search.md +28 -0
  499. package/src/prompts/tools/ask.md +44 -0
  500. package/src/prompts/tools/bash.md +24 -0
  501. package/src/prompts/tools/browser.md +33 -0
  502. package/src/prompts/tools/calculator.md +12 -0
  503. package/src/prompts/tools/explore.md +29 -0
  504. package/src/prompts/tools/fetch.md +16 -0
  505. package/src/prompts/tools/find.md +18 -0
  506. package/src/prompts/tools/gemini-image.md +23 -0
  507. package/src/prompts/tools/grep.md +28 -0
  508. package/src/prompts/tools/hashline.md +232 -0
  509. package/src/prompts/tools/librarian.md +24 -0
  510. package/src/prompts/tools/lsp.md +28 -0
  511. package/src/prompts/tools/oracle.md +26 -0
  512. package/src/prompts/tools/patch.md +74 -0
  513. package/src/prompts/tools/python.md +66 -0
  514. package/src/prompts/tools/read.md +36 -0
  515. package/src/prompts/tools/replace.md +38 -0
  516. package/src/prompts/tools/reviewer.md +41 -0
  517. package/src/prompts/tools/ssh.md +51 -0
  518. package/src/prompts/tools/task-summary.md +28 -0
  519. package/src/prompts/tools/task.md +275 -0
  520. package/src/prompts/tools/todo-write.md +65 -0
  521. package/src/prompts/tools/undo-edit.md +7 -0
  522. package/src/prompts/tools/web-search.md +19 -0
  523. package/src/prompts/tools/write.md +18 -0
  524. package/src/sdk.ts +1287 -0
  525. package/src/secrets/index.ts +116 -0
  526. package/src/secrets/obfuscator.ts +269 -0
  527. package/src/secrets/regex.ts +21 -0
  528. package/src/session/agent-session.ts +4669 -0
  529. package/src/session/agent-storage.ts +621 -0
  530. package/src/session/artifacts.ts +132 -0
  531. package/src/session/auth-storage.ts +1433 -0
  532. package/src/session/blob-store.ts +103 -0
  533. package/src/session/compaction/branch-summarization.ts +315 -0
  534. package/src/session/compaction/compaction.ts +864 -0
  535. package/src/session/compaction/index.ts +7 -0
  536. package/src/session/compaction/pruning.ts +91 -0
  537. package/src/session/compaction/utils.ts +171 -0
  538. package/src/session/history-storage.ts +170 -0
  539. package/src/session/messages.ts +317 -0
  540. package/src/session/session-manager.ts +2276 -0
  541. package/src/session/session-storage.ts +342 -0
  542. package/src/session/streaming-output.ts +565 -0
  543. package/src/slash-commands/builtin-registry.ts +439 -0
  544. package/src/ssh/config-writer.ts +183 -0
  545. package/src/ssh/connection-manager.ts +444 -0
  546. package/src/ssh/ssh-executor.ts +127 -0
  547. package/src/ssh/sshfs-mount.ts +135 -0
  548. package/src/stt/downloader.ts +71 -0
  549. package/src/stt/index.ts +3 -0
  550. package/src/stt/recorder.ts +351 -0
  551. package/src/stt/setup.ts +52 -0
  552. package/src/stt/stt-controller.ts +160 -0
  553. package/src/stt/transcribe.py +70 -0
  554. package/src/stt/transcriber.ts +91 -0
  555. package/src/system-prompt.ts +685 -0
  556. package/src/task/agents.ts +155 -0
  557. package/src/task/batch.ts +102 -0
  558. package/src/task/commands.ts +134 -0
  559. package/src/task/discovery.ts +126 -0
  560. package/src/task/executor.ts +908 -0
  561. package/src/task/index.ts +223 -0
  562. package/src/task/output-manager.ts +107 -0
  563. package/src/task/parallel.ts +84 -0
  564. package/src/task/render.ts +326 -0
  565. package/src/task/subprocess-tool-registry.ts +88 -0
  566. package/src/task/template.ts +32 -0
  567. package/src/task/types.ts +144 -0
  568. package/src/tools/ask.ts +523 -0
  569. package/src/tools/bash-interactive.ts +419 -0
  570. package/src/tools/bash-interceptor.ts +105 -0
  571. package/src/tools/bash-normalize.ts +107 -0
  572. package/src/tools/bash-skill-urls.ts +177 -0
  573. package/src/tools/bash.ts +347 -0
  574. package/src/tools/browser.ts +1374 -0
  575. package/src/tools/calculator.ts +537 -0
  576. package/src/tools/context.ts +39 -0
  577. package/src/tools/explore.ts +23 -0
  578. package/src/tools/fetch.ts +1091 -0
  579. package/src/tools/find.ts +540 -0
  580. package/src/tools/fs-cache-invalidation.ts +28 -0
  581. package/src/tools/gemini-image.ts +907 -0
  582. package/src/tools/grep.ts +489 -0
  583. package/src/tools/index.ts +337 -0
  584. package/src/tools/json-tree.ts +231 -0
  585. package/src/tools/jtd-to-json-schema.ts +247 -0
  586. package/src/tools/jtd-to-typescript.ts +198 -0
  587. package/src/tools/librarian.ts +33 -0
  588. package/src/tools/list-limit.ts +40 -0
  589. package/src/tools/notebook.ts +287 -0
  590. package/src/tools/oracle.ts +40 -0
  591. package/src/tools/output-meta.ts +459 -0
  592. package/src/tools/output-utils.ts +63 -0
  593. package/src/tools/path-utils.ts +116 -0
  594. package/src/tools/puppeteer/00_stealth_tampering.txt +63 -0
  595. package/src/tools/puppeteer/01_stealth_activity.txt +20 -0
  596. package/src/tools/puppeteer/02_stealth_hairline.txt +11 -0
  597. package/src/tools/puppeteer/03_stealth_botd.txt +384 -0
  598. package/src/tools/puppeteer/04_stealth_iframe.txt +81 -0
  599. package/src/tools/puppeteer/05_stealth_webgl.txt +75 -0
  600. package/src/tools/puppeteer/06_stealth_screen.txt +72 -0
  601. package/src/tools/puppeteer/07_stealth_fonts.txt +97 -0
  602. package/src/tools/puppeteer/08_stealth_audio.txt +51 -0
  603. package/src/tools/puppeteer/09_stealth_locale.txt +46 -0
  604. package/src/tools/puppeteer/10_stealth_plugins.txt +206 -0
  605. package/src/tools/puppeteer/11_stealth_hardware.txt +8 -0
  606. package/src/tools/puppeteer/12_stealth_codecs.txt +40 -0
  607. package/src/tools/puppeteer/13_stealth_worker.txt +74 -0
  608. package/src/tools/python.ts +1118 -0
  609. package/src/tools/read.ts +1193 -0
  610. package/src/tools/render-utils.ts +680 -0
  611. package/src/tools/renderers.ts +60 -0
  612. package/src/tools/reviewer-tool.ts +41 -0
  613. package/src/tools/ssh.ts +326 -0
  614. package/src/tools/subagent-tool.ts +169 -0
  615. package/src/tools/submit-result.ts +152 -0
  616. package/src/tools/todo-write.ts +255 -0
  617. package/src/tools/tool-errors.ts +92 -0
  618. package/src/tools/tool-result.ts +86 -0
  619. package/src/tools/undo-edit.ts +145 -0
  620. package/src/tools/undo-history.ts +22 -0
  621. package/src/tools/write.ts +274 -0
  622. package/src/tui/code-cell.ts +108 -0
  623. package/src/tui/file-list.ts +47 -0
  624. package/src/tui/index.ts +11 -0
  625. package/src/tui/output-block.ts +144 -0
  626. package/src/tui/status-line.ts +39 -0
  627. package/src/tui/tree-list.ts +53 -0
  628. package/src/tui/types.ts +16 -0
  629. package/src/tui/utils.ts +116 -0
  630. package/src/utils/changelog.ts +98 -0
  631. package/src/utils/event-bus.ts +33 -0
  632. package/src/utils/external-editor.ts +59 -0
  633. package/src/utils/file-display-mode.ts +36 -0
  634. package/src/utils/file-mentions.ts +384 -0
  635. package/src/utils/frontmatter.ts +101 -0
  636. package/src/utils/fuzzy.ts +108 -0
  637. package/src/utils/ignore-files.ts +119 -0
  638. package/src/utils/image-convert.ts +27 -0
  639. package/src/utils/image-resize.ts +236 -0
  640. package/src/utils/mime.ts +30 -0
  641. package/src/utils/open.ts +20 -0
  642. package/src/utils/shell-snapshot.ts +199 -0
  643. package/src/utils/timings.ts +26 -0
  644. package/src/utils/title-generator.ts +167 -0
  645. package/src/utils/tools-manager.ts +362 -0
  646. package/src/web/scrapers/artifacthub.ts +215 -0
  647. package/src/web/scrapers/arxiv.ts +88 -0
  648. package/src/web/scrapers/aur.ts +175 -0
  649. package/src/web/scrapers/biorxiv.ts +141 -0
  650. package/src/web/scrapers/bluesky.ts +284 -0
  651. package/src/web/scrapers/brew.ts +177 -0
  652. package/src/web/scrapers/cheatsh.ts +78 -0
  653. package/src/web/scrapers/chocolatey.ts +158 -0
  654. package/src/web/scrapers/choosealicense.ts +110 -0
  655. package/src/web/scrapers/cisa-kev.ts +100 -0
  656. package/src/web/scrapers/clojars.ts +180 -0
  657. package/src/web/scrapers/coingecko.ts +184 -0
  658. package/src/web/scrapers/crates-io.ts +128 -0
  659. package/src/web/scrapers/crossref.ts +149 -0
  660. package/src/web/scrapers/devto.ts +177 -0
  661. package/src/web/scrapers/discogs.ts +307 -0
  662. package/src/web/scrapers/discourse.ts +221 -0
  663. package/src/web/scrapers/dockerhub.ts +160 -0
  664. package/src/web/scrapers/fdroid.ts +158 -0
  665. package/src/web/scrapers/firefox-addons.ts +214 -0
  666. package/src/web/scrapers/flathub.ts +239 -0
  667. package/src/web/scrapers/github-gist.ts +68 -0
  668. package/src/web/scrapers/github.ts +490 -0
  669. package/src/web/scrapers/gitlab.ts +456 -0
  670. package/src/web/scrapers/go-pkg.ts +275 -0
  671. package/src/web/scrapers/hackage.ts +94 -0
  672. package/src/web/scrapers/hackernews.ts +208 -0
  673. package/src/web/scrapers/hex.ts +121 -0
  674. package/src/web/scrapers/huggingface.ts +385 -0
  675. package/src/web/scrapers/iacr.ts +86 -0
  676. package/src/web/scrapers/index.ts +249 -0
  677. package/src/web/scrapers/jetbrains-marketplace.ts +169 -0
  678. package/src/web/scrapers/lemmy.ts +220 -0
  679. package/src/web/scrapers/lobsters.ts +186 -0
  680. package/src/web/scrapers/mastodon.ts +310 -0
  681. package/src/web/scrapers/maven.ts +152 -0
  682. package/src/web/scrapers/mdn.ts +172 -0
  683. package/src/web/scrapers/metacpan.ts +253 -0
  684. package/src/web/scrapers/musicbrainz.ts +272 -0
  685. package/src/web/scrapers/npm.ts +114 -0
  686. package/src/web/scrapers/nuget.ts +205 -0
  687. package/src/web/scrapers/nvd.ts +243 -0
  688. package/src/web/scrapers/ollama.ts +265 -0
  689. package/src/web/scrapers/open-vsx.ts +119 -0
  690. package/src/web/scrapers/opencorporates.ts +275 -0
  691. package/src/web/scrapers/openlibrary.ts +319 -0
  692. package/src/web/scrapers/orcid.ts +298 -0
  693. package/src/web/scrapers/osv.ts +192 -0
  694. package/src/web/scrapers/packagist.ts +174 -0
  695. package/src/web/scrapers/pub-dev.ts +185 -0
  696. package/src/web/scrapers/pubmed.ts +177 -0
  697. package/src/web/scrapers/pypi.ts +129 -0
  698. package/src/web/scrapers/rawg.ts +124 -0
  699. package/src/web/scrapers/readthedocs.ts +125 -0
  700. package/src/web/scrapers/reddit.ts +104 -0
  701. package/src/web/scrapers/repology.ts +262 -0
  702. package/src/web/scrapers/rfc.ts +209 -0
  703. package/src/web/scrapers/rubygems.ts +117 -0
  704. package/src/web/scrapers/searchcode.ts +217 -0
  705. package/src/web/scrapers/sec-edgar.ts +274 -0
  706. package/src/web/scrapers/semantic-scholar.ts +190 -0
  707. package/src/web/scrapers/snapcraft.ts +200 -0
  708. package/src/web/scrapers/sourcegraph.ts +373 -0
  709. package/src/web/scrapers/spdx.ts +121 -0
  710. package/src/web/scrapers/spotify.ts +217 -0
  711. package/src/web/scrapers/stackoverflow.ts +124 -0
  712. package/src/web/scrapers/terraform.ts +304 -0
  713. package/src/web/scrapers/tldr.ts +51 -0
  714. package/src/web/scrapers/twitter.ts +97 -0
  715. package/src/web/scrapers/types.ts +200 -0
  716. package/src/web/scrapers/utils.ts +142 -0
  717. package/src/web/scrapers/vimeo.ts +152 -0
  718. package/src/web/scrapers/vscode-marketplace.ts +195 -0
  719. package/src/web/scrapers/w3c.ts +163 -0
  720. package/src/web/scrapers/wikidata.ts +357 -0
  721. package/src/web/scrapers/wikipedia.ts +95 -0
  722. package/src/web/scrapers/youtube.ts +312 -0
  723. package/src/web/search/auth.ts +178 -0
  724. package/src/web/search/index.ts +598 -0
  725. package/src/web/search/provider.ts +77 -0
  726. package/src/web/search/providers/anthropic.ts +284 -0
  727. package/src/web/search/providers/base.ts +22 -0
  728. package/src/web/search/providers/brave.ts +165 -0
  729. package/src/web/search/providers/codex.ts +377 -0
  730. package/src/web/search/providers/exa.ts +158 -0
  731. package/src/web/search/providers/gemini.ts +437 -0
  732. package/src/web/search/providers/jina.ts +99 -0
  733. package/src/web/search/providers/kimi.ts +196 -0
  734. package/src/web/search/providers/perplexity.ts +546 -0
  735. package/src/web/search/providers/synthetic.ts +136 -0
  736. package/src/web/search/providers/zai.ts +352 -0
  737. package/src/web/search/render.ts +299 -0
  738. package/src/web/search/types.ts +437 -0
@@ -0,0 +1,1099 @@
1
+ import type { Database } from "bun:sqlite";
2
+ import type * as fsNode from "node:fs";
3
+ import * as fs from "node:fs/promises";
4
+ import * as path from "node:path";
5
+ import type { AgentMessage } from "@nghyane/arcane-agent";
6
+ import { completeSimple, type Model } from "@nghyane/arcane-ai";
7
+ import { logger, parseJsonlLenient } from "@nghyane/arcane-utils";
8
+ import { getAgentDbPath } from "@nghyane/arcane-utils/dirs";
9
+ import type { ModelRegistry } from "../config/model-registry";
10
+ import { parseModelString } from "../config/model-resolver";
11
+ import { renderPromptTemplate } from "../config/prompt-templates";
12
+ import type { Settings } from "../config/settings";
13
+ import consolidationTemplate from "../prompts/memories/consolidation.md" with { type: "text" };
14
+ import readPathTemplate from "../prompts/memories/read_path.md" with { type: "text" };
15
+ import stageOneInputTemplate from "../prompts/memories/stage_one_input.md" with { type: "text" };
16
+ import stageOneSystemTemplate from "../prompts/memories/stage_one_system.md" with { type: "text" };
17
+ import type { AgentSession } from "../session/agent-session";
18
+ import {
19
+ claimStage1Jobs,
20
+ clearMemoryData as clearMemoryDataInDb,
21
+ closeMemoryDb,
22
+ enqueueGlobalWatermark,
23
+ heartbeatGlobalJob,
24
+ listStage1OutputsForGlobal,
25
+ type MemoryThread,
26
+ markGlobalPhase2Failed,
27
+ markGlobalPhase2FailedUnowned,
28
+ markGlobalPhase2Succeeded,
29
+ markStage1Failed,
30
+ markStage1SucceededNoOutput,
31
+ markStage1SucceededWithOutput,
32
+ openMemoryDb,
33
+ type Stage1Claim,
34
+ type Stage1OutputRow,
35
+ tryClaimGlobalPhase2Job,
36
+ upsertThreads,
37
+ } from "./storage";
38
+
39
+ interface MemoryRuntimeConfig {
40
+ enabled: boolean;
41
+ maxRolloutsPerStartup: number;
42
+ maxRolloutAgeDays: number;
43
+ minRolloutIdleHours: number;
44
+ threadScanLimit: number;
45
+ maxRawMemoriesForGlobal: number;
46
+ stage1Concurrency: number;
47
+ stage1LeaseSeconds: number;
48
+ stage1RetryDelaySeconds: number;
49
+ phase2LeaseSeconds: number;
50
+ phase2RetryDelaySeconds: number;
51
+ phase2HeartbeatSeconds: number;
52
+ rolloutPayloadPercent: number;
53
+ fallbackTokenLimit: number;
54
+ summaryInjectionTokenLimit: number;
55
+ }
56
+
57
+ const DEFAULTS: MemoryRuntimeConfig = {
58
+ enabled: false,
59
+ maxRolloutsPerStartup: 64,
60
+ maxRolloutAgeDays: 30,
61
+ minRolloutIdleHours: 12,
62
+ threadScanLimit: 300,
63
+ maxRawMemoriesForGlobal: 200,
64
+ stage1Concurrency: 8,
65
+ stage1LeaseSeconds: 120,
66
+ stage1RetryDelaySeconds: 120,
67
+ phase2LeaseSeconds: 180,
68
+ phase2RetryDelaySeconds: 180,
69
+ phase2HeartbeatSeconds: 30,
70
+ rolloutPayloadPercent: 0.7,
71
+ fallbackTokenLimit: 16_000,
72
+ summaryInjectionTokenLimit: 5_000,
73
+ };
74
+
75
+ interface Stage1Stats {
76
+ claimed: number;
77
+ succeeded: number;
78
+ succeededNoOutput: number;
79
+ failed: number;
80
+ produced: number;
81
+ usage: {
82
+ input: number;
83
+ output: number;
84
+ cacheRead: number;
85
+ cacheWrite: number;
86
+ total: number;
87
+ };
88
+ }
89
+
90
+ interface Stage1OutputSchema {
91
+ raw_memory: string;
92
+ rollout_summary: string;
93
+ rollout_slug: string | null;
94
+ }
95
+
96
+ interface ConsolidationSkillFileSchema {
97
+ path: string;
98
+ content: string;
99
+ }
100
+
101
+ interface ConsolidationSkillSchema {
102
+ name: string;
103
+ content?: string;
104
+ scripts?: ConsolidationSkillFileSchema[];
105
+ templates?: ConsolidationSkillFileSchema[];
106
+ examples?: ConsolidationSkillFileSchema[];
107
+ }
108
+ interface ConsolidationOutputSchema {
109
+ memory_md: string;
110
+ memory_summary: string;
111
+ skills: ConsolidationSkillSchema[];
112
+ }
113
+
114
+ /**
115
+ * Start the background memory startup pipeline.
116
+ *
117
+ * Skips for ephemeral sessions, subagent sessions, disabled settings, or DB failures.
118
+ */
119
+ export function startMemoryStartupTask(options: {
120
+ session: AgentSession;
121
+ settings: Settings;
122
+ modelRegistry: ModelRegistry;
123
+ agentDir: string;
124
+ taskDepth: number;
125
+ }): void {
126
+ const { session, settings, modelRegistry, agentDir, taskDepth } = options;
127
+ const cfg = loadMemoryConfig(settings);
128
+ if (!cfg.enabled) return;
129
+ if (taskDepth > 0) return;
130
+ if (!session.sessionManager.getSessionFile()) return;
131
+
132
+ const dbPath = getAgentDbPath(agentDir);
133
+ try {
134
+ const db = openMemoryDb(dbPath);
135
+ closeMemoryDb(db);
136
+ } catch (error) {
137
+ logger.debug("Memory startup skipped: state DB unavailable", { error: String(error) });
138
+ return;
139
+ }
140
+
141
+ void runMemoryStartup({ session, settings, modelRegistry, agentDir, config: cfg }).catch(error => {
142
+ logger.warn("Memory startup failed", { error: String(error) });
143
+ });
144
+ }
145
+
146
+ /**
147
+ * Build memory usage instructions for prompt injection.
148
+ */
149
+ export async function buildMemoryToolDeveloperInstructions(
150
+ agentDir: string,
151
+ settings: Settings,
152
+ ): Promise<string | undefined> {
153
+ const cfg = loadMemoryConfig(settings);
154
+ if (!cfg.enabled) return undefined;
155
+ const memoryRoot = getMemoryRoot(agentDir, settings.getCwd());
156
+ const summaryPath = path.join(memoryRoot, "memory_summary.md");
157
+
158
+ let text: string;
159
+ try {
160
+ text = await Bun.file(summaryPath).text();
161
+ } catch {
162
+ return undefined;
163
+ }
164
+
165
+ const summary = text.trim();
166
+ if (!summary) return undefined;
167
+ const truncated = truncateByApproxTokens(summary, cfg.summaryInjectionTokenLimit);
168
+ if (!truncated.trim()) return undefined;
169
+
170
+ return renderPromptTemplate(readPathTemplate, {
171
+ memory_summary: truncated,
172
+ });
173
+ }
174
+
175
+ /**
176
+ * Clear all persisted memory state and generated artifacts.
177
+ */
178
+ export async function clearMemoryData(agentDir: string, cwd: string): Promise<void> {
179
+ const db = openMemoryDb(getAgentDbPath(agentDir));
180
+ try {
181
+ clearMemoryDataInDb(db);
182
+ } finally {
183
+ closeMemoryDb(db);
184
+ }
185
+ await fs.rm(getMemoryRoot(agentDir, cwd), { recursive: true, force: true });
186
+ }
187
+
188
+ /**
189
+ * Force-enqueue global consolidation maintenance work.
190
+ */
191
+ export function enqueueMemoryConsolidation(agentDir: string, sourceUpdatedAt = unixNow()): void {
192
+ const db = openMemoryDb(getAgentDbPath(agentDir));
193
+ try {
194
+ enqueueGlobalWatermark(db, sourceUpdatedAt, { forceDirtyWhenNotAdvanced: true });
195
+ } finally {
196
+ closeMemoryDb(db);
197
+ }
198
+ }
199
+
200
+ async function runMemoryStartup(options: {
201
+ session: AgentSession;
202
+ settings: Settings;
203
+ modelRegistry: ModelRegistry;
204
+ agentDir: string;
205
+ config: MemoryRuntimeConfig;
206
+ }): Promise<void> {
207
+ await runPhase1(options);
208
+ await runPhase2(options);
209
+ await options.session.refreshBaseSystemPrompt?.();
210
+ }
211
+
212
+ async function runPhase1(options: {
213
+ session: AgentSession;
214
+ settings: Settings;
215
+ modelRegistry: ModelRegistry;
216
+ agentDir: string;
217
+ config: MemoryRuntimeConfig;
218
+ }): Promise<void> {
219
+ const { session, modelRegistry, agentDir, config } = options;
220
+ const db = openMemoryDb(getAgentDbPath(agentDir));
221
+ const nowSec = unixNow();
222
+ const workerId = `memory-${process.pid}`;
223
+ const memoryRoot = getMemoryRoot(agentDir, session.sessionManager.getCwd());
224
+ const currentThreadId = session.sessionManager.getSessionId();
225
+
226
+ try {
227
+ const threads = await collectThreads(session, currentThreadId);
228
+ upsertThreads(db, threads);
229
+
230
+ const phase1Model = await resolveMemoryModel({
231
+ modelRegistry,
232
+ session,
233
+ fallbackRole: "default",
234
+ });
235
+ if (!phase1Model) {
236
+ logger.debug("Phase1 skipped: no model available");
237
+ return;
238
+ }
239
+ const phase1ApiKey = await modelRegistry.getApiKey(phase1Model, session.sessionManager.getSessionId());
240
+ if (!phase1ApiKey) {
241
+ logger.debug("Phase1 skipped: no API key for phase1 model", {
242
+ provider: phase1Model.provider,
243
+ model: phase1Model.id,
244
+ });
245
+ return;
246
+ }
247
+
248
+ const claims = claimStage1Jobs(db, {
249
+ nowSec,
250
+ threadScanLimit: config.threadScanLimit,
251
+ maxRolloutsPerStartup: config.maxRolloutsPerStartup,
252
+ maxRolloutAgeDays: config.maxRolloutAgeDays,
253
+ minRolloutIdleHours: config.minRolloutIdleHours,
254
+ leaseSeconds: config.stage1LeaseSeconds,
255
+ runningConcurrencyCap: config.stage1Concurrency,
256
+ workerId,
257
+ excludeThreadIds: currentThreadId ? [currentThreadId] : [],
258
+ });
259
+ if (claims.length === 0) return;
260
+
261
+ const stats: Stage1Stats = {
262
+ claimed: claims.length,
263
+ succeeded: 0,
264
+ succeededNoOutput: 0,
265
+ failed: 0,
266
+ produced: 0,
267
+ usage: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },
268
+ };
269
+
270
+ await runWithConcurrency(claims, config.stage1Concurrency, async claim => {
271
+ const result = await runStage1Job({
272
+ claim,
273
+ model: phase1Model,
274
+ apiKey: phase1ApiKey,
275
+ modelMaxTokens: computeModelTokenBudget(phase1Model, config),
276
+ config,
277
+ });
278
+
279
+ if (result.kind === "failed") {
280
+ markStage1Failed(db, {
281
+ threadId: claim.threadId,
282
+ ownershipToken: claim.ownershipToken,
283
+ retryDelaySeconds: config.stage1RetryDelaySeconds,
284
+ reason: result.reason,
285
+ nowSec: unixNow(),
286
+ });
287
+ stats.failed += 1;
288
+ return;
289
+ }
290
+
291
+ if (result.kind === "no_output") {
292
+ markStage1SucceededNoOutput(db, {
293
+ threadId: claim.threadId,
294
+ ownershipToken: claim.ownershipToken,
295
+ sourceUpdatedAt: claim.sourceUpdatedAt,
296
+ nowSec: unixNow(),
297
+ });
298
+ stats.succeededNoOutput += 1;
299
+ return;
300
+ }
301
+
302
+ markStage1SucceededWithOutput(db, {
303
+ threadId: claim.threadId,
304
+ ownershipToken: claim.ownershipToken,
305
+ sourceUpdatedAt: claim.sourceUpdatedAt,
306
+ rawMemory: result.output.rawMemory,
307
+ rolloutSummary: result.output.rolloutSummary,
308
+ rolloutSlug: result.output.rolloutSlug,
309
+ nowSec: unixNow(),
310
+ });
311
+ stats.succeeded += 1;
312
+ stats.produced += 1;
313
+ if (result.usage) {
314
+ stats.usage.input += result.usage.input;
315
+ stats.usage.output += result.usage.output;
316
+ stats.usage.cacheRead += result.usage.cacheRead;
317
+ stats.usage.cacheWrite += result.usage.cacheWrite;
318
+ stats.usage.total += result.usage.totalTokens || 0;
319
+ }
320
+ });
321
+
322
+ logger.debug("Memory phase1 completed", {
323
+ memoryRoot,
324
+ claimed: stats.claimed,
325
+ succeeded: stats.succeeded,
326
+ succeededNoOutput: stats.succeededNoOutput,
327
+ failed: stats.failed,
328
+ produced: stats.produced,
329
+ usage: stats.usage,
330
+ });
331
+ } finally {
332
+ closeMemoryDb(db);
333
+ }
334
+ }
335
+
336
+ async function runPhase2(options: {
337
+ session: AgentSession;
338
+ settings: Settings;
339
+ modelRegistry: ModelRegistry;
340
+ agentDir: string;
341
+ config: MemoryRuntimeConfig;
342
+ }): Promise<void> {
343
+ const { session, modelRegistry, agentDir, config } = options;
344
+ const db = openMemoryDb(getAgentDbPath(agentDir));
345
+ const nowSec = unixNow();
346
+ const workerId = `memory-${process.pid}`;
347
+ const memoryRoot = getMemoryRoot(agentDir, session.sessionManager.getCwd());
348
+
349
+ try {
350
+ const claimResult = tryClaimGlobalPhase2Job(db, {
351
+ workerId,
352
+ leaseSeconds: config.phase2LeaseSeconds,
353
+ nowSec,
354
+ });
355
+ if (claimResult.kind !== "claimed") return;
356
+
357
+ const claim = claimResult.claim;
358
+ const outputs = listStage1OutputsForGlobal(db, config.maxRawMemoriesForGlobal);
359
+ const newWatermark = computeCompletionWatermark(claim.inputWatermark, outputs);
360
+
361
+ await syncPhase2Artifacts(memoryRoot, outputs);
362
+ if (outputs.length === 0) {
363
+ await cleanupConsolidatedArtifacts(memoryRoot);
364
+ const marked = markGlobalPhase2Succeeded(db, {
365
+ ownershipToken: claim.ownershipToken,
366
+ newWatermark,
367
+ nowSec: unixNow(),
368
+ });
369
+ if (!marked) {
370
+ logger.warn("Phase2 empty-input completion lost ownership", { memoryRoot });
371
+ }
372
+ return;
373
+ }
374
+
375
+ const phase2Model = await resolveMemoryModel({
376
+ modelRegistry,
377
+ session,
378
+ fallbackRole: "fast",
379
+ });
380
+ if (!phase2Model) {
381
+ markPhase2FailureWithFallback(db, {
382
+ claim,
383
+ retryDelaySeconds: config.phase2RetryDelaySeconds,
384
+ reason: "No model available for phase2",
385
+ memoryRoot,
386
+ });
387
+ return;
388
+ }
389
+ const phase2ApiKey = await modelRegistry.getApiKey(phase2Model, session.sessionManager.getSessionId());
390
+ if (!phase2ApiKey) {
391
+ markPhase2FailureWithFallback(db, {
392
+ claim,
393
+ retryDelaySeconds: config.phase2RetryDelaySeconds,
394
+ reason: "No API key available for phase2",
395
+ memoryRoot,
396
+ });
397
+ return;
398
+ }
399
+
400
+ let heartbeatLostOwnership = false;
401
+ const heartbeat = setInterval(() => {
402
+ const ok = heartbeatGlobalJob(db, {
403
+ ownershipToken: claim.ownershipToken,
404
+ leaseSeconds: config.phase2LeaseSeconds,
405
+ nowSec: unixNow(),
406
+ });
407
+ if (!ok) {
408
+ heartbeatLostOwnership = true;
409
+ clearInterval(heartbeat);
410
+ }
411
+ }, config.phase2HeartbeatSeconds * 1000);
412
+
413
+ try {
414
+ const consolidated = await runConsolidationModel({
415
+ memoryRoot,
416
+ model: phase2Model,
417
+ apiKey: phase2ApiKey,
418
+ });
419
+ await applyConsolidation(memoryRoot, consolidated);
420
+ if (heartbeatLostOwnership) {
421
+ throw new Error("Phase2 lease ownership lost before completion");
422
+ }
423
+ const marked = markGlobalPhase2Succeeded(db, {
424
+ ownershipToken: claim.ownershipToken,
425
+ newWatermark,
426
+ nowSec: unixNow(),
427
+ });
428
+ if (!marked) {
429
+ throw new Error("Phase2 could not mark success: ownership lost");
430
+ }
431
+ } catch (error) {
432
+ markPhase2FailureWithFallback(db, {
433
+ claim,
434
+ retryDelaySeconds: config.phase2RetryDelaySeconds,
435
+ reason: String(error),
436
+ memoryRoot,
437
+ error,
438
+ });
439
+ } finally {
440
+ clearInterval(heartbeat);
441
+ }
442
+ } finally {
443
+ closeMemoryDb(db);
444
+ }
445
+ }
446
+
447
+ function markPhase2FailureWithFallback(
448
+ db: Database,
449
+ params: {
450
+ claim: { ownershipToken: string; inputWatermark: number };
451
+ retryDelaySeconds: number;
452
+ reason: string;
453
+ memoryRoot: string;
454
+ error?: unknown;
455
+ },
456
+ ): void {
457
+ const { claim, retryDelaySeconds, reason, memoryRoot, error } = params;
458
+ const nowSec = unixNow();
459
+ const strictFailed = markGlobalPhase2Failed(db, {
460
+ ownershipToken: claim.ownershipToken,
461
+ retryDelaySeconds,
462
+ reason,
463
+ nowSec,
464
+ });
465
+ if (strictFailed) return;
466
+
467
+ const unownedFailed = markGlobalPhase2FailedUnowned(db, {
468
+ retryDelaySeconds,
469
+ reason,
470
+ nowSec,
471
+ });
472
+ if (!unownedFailed) {
473
+ logger.warn("Phase2 could not mark failure (ownership lost and unowned fallback skipped)", {
474
+ error: error ? String(error) : undefined,
475
+ memoryRoot,
476
+ reason,
477
+ inputWatermark: claim.inputWatermark,
478
+ });
479
+ }
480
+ }
481
+
482
+ async function collectThreads(session: AgentSession, currentThreadId?: string): Promise<MemoryThread[]> {
483
+ const sessionDir = session.sessionManager.getSessionDir();
484
+ const files = await fs.readdir(sessionDir);
485
+ const threads: MemoryThread[] = [];
486
+ for (const name of files) {
487
+ if (!name.endsWith(".jsonl")) continue;
488
+ const fullPath = path.join(sessionDir, name);
489
+ let stat: fsNode.Stats;
490
+ try {
491
+ stat = await fs.stat(fullPath);
492
+ } catch {
493
+ continue;
494
+ }
495
+ let cwd = "";
496
+ let id = name.slice(0, -6);
497
+ try {
498
+ const fileText = await Bun.file(fullPath).text();
499
+ const firstLine = fileText.split("\n", 1)[0] ?? "";
500
+ const parsed = parseJsonlLenient(firstLine);
501
+ const header = Array.isArray(parsed) && parsed.length > 0 ? (parsed[0] as Record<string, unknown>) : undefined;
502
+ if (header && header.type === "session") {
503
+ if (typeof header.cwd === "string") cwd = header.cwd;
504
+ if (typeof header.id === "string") id = header.id;
505
+ }
506
+ } catch {
507
+ // ignore malformed session files
508
+ }
509
+
510
+ if (currentThreadId && id === currentThreadId) continue;
511
+ threads.push({
512
+ id,
513
+ updatedAt: Math.floor(stat.mtimeMs / 1000),
514
+ rolloutPath: fullPath,
515
+ cwd,
516
+ sourceKind: "cli",
517
+ });
518
+ }
519
+ return threads;
520
+ }
521
+
522
+ function shouldPersistResponseItemForMemories(message: AgentMessage): boolean {
523
+ const role = (message as { role: string }).role;
524
+ if (role === "system" || role === "developer" || role === "user" || role === "assistant") {
525
+ return true;
526
+ }
527
+ if (role !== "toolResult") return false;
528
+ const toolName = (message as { toolName?: string }).toolName;
529
+ if (toolName === "bash" || toolName === "python" || toolName === "read" || toolName === "grep") {
530
+ const text = extractMessageText(message);
531
+ return text.length > 0 && text.length <= 32_000;
532
+ }
533
+ return false;
534
+ }
535
+
536
+ function extractPersistableMessages(payload: string): AgentMessage[] {
537
+ const rows = parseJsonlLenient(payload);
538
+ if (!Array.isArray(rows)) return [];
539
+ const messages: AgentMessage[] = [];
540
+ for (const row of rows) {
541
+ if (!row || typeof row !== "object") continue;
542
+ const entry = row as Record<string, unknown>;
543
+ if (entry.type !== "message") continue;
544
+ const maybeMessage = entry.message;
545
+ if (!maybeMessage || typeof maybeMessage !== "object") continue;
546
+ const message = maybeMessage as AgentMessage;
547
+ if (shouldPersistResponseItemForMemories(message)) {
548
+ messages.push(message);
549
+ }
550
+ }
551
+ return messages;
552
+ }
553
+
554
+ async function runStage1Job(options: {
555
+ claim: Stage1Claim;
556
+ model: Model;
557
+ apiKey: string;
558
+ modelMaxTokens: number;
559
+ config: MemoryRuntimeConfig;
560
+ }): Promise<
561
+ | {
562
+ kind: "output";
563
+ output: { rawMemory: string; rolloutSummary: string; rolloutSlug: string | null };
564
+ usage?: { input: number; output: number; cacheRead: number; cacheWrite: number; totalTokens?: number };
565
+ }
566
+ | { kind: "no_output" }
567
+ | { kind: "failed"; reason: string }
568
+ > {
569
+ const { claim, model, apiKey, modelMaxTokens, config } = options;
570
+ try {
571
+ const rolloutRaw = await Bun.file(claim.rolloutPath).text();
572
+ const persisted = extractPersistableMessages(rolloutRaw);
573
+ const serializedItems = JSON.stringify(persisted);
574
+ const budgetTokens = Math.floor(modelMaxTokens * config.rolloutPayloadPercent);
575
+ const truncatedItems = truncateByApproxTokens(serializedItems, budgetTokens);
576
+ const inputPrompt = renderPromptTemplate(stageOneInputTemplate, {
577
+ thread_id: claim.threadId,
578
+ response_items_json: truncatedItems,
579
+ });
580
+
581
+ const response = await completeSimple(
582
+ model,
583
+ {
584
+ systemPrompt: stageOneSystemTemplate,
585
+ messages: [{ role: "user", content: [{ type: "text", text: inputPrompt }], timestamp: Date.now() }],
586
+ },
587
+ { apiKey, maxTokens: Math.max(1024, Math.min(4096, Math.floor(modelMaxTokens * 0.2))), reasoning: "low" },
588
+ );
589
+
590
+ if (response.stopReason === "error") {
591
+ return { kind: "failed", reason: response.errorMessage || "stage1 model error" };
592
+ }
593
+ const text = response.content
594
+ .filter((c): c is { type: "text"; text: string } => c.type === "text")
595
+ .map(c => c.text)
596
+ .join("\n")
597
+ .trim();
598
+ const parsed = parseJsonObject(text);
599
+ if (!parsed) {
600
+ return { kind: "failed", reason: "stage1 JSON parse failure" };
601
+ }
602
+ const schemaOutput = parseStage1OutputSchema(parsed);
603
+ if (!schemaOutput) {
604
+ return { kind: "failed", reason: "stage1 JSON schema validation failure" };
605
+ }
606
+
607
+ const rawMemory = redactSecrets(schemaOutput.raw_memory).trim();
608
+ const rolloutSummary = redactSecrets(schemaOutput.rollout_summary).trim();
609
+ const rolloutSlug = schemaOutput.rollout_slug === null ? null : redactSecrets(schemaOutput.rollout_slug).trim();
610
+ if (!rawMemory || !rolloutSummary) {
611
+ return { kind: "no_output" };
612
+ }
613
+ return {
614
+ kind: "output",
615
+ output: {
616
+ rawMemory,
617
+ rolloutSummary,
618
+ rolloutSlug: rolloutSlug || null,
619
+ },
620
+ usage: response.usage,
621
+ };
622
+ } catch (error) {
623
+ return { kind: "failed", reason: String(error) };
624
+ }
625
+ }
626
+
627
+ async function syncPhase2Artifacts(memoryRoot: string, outputs: Stage1OutputRow[]): Promise<void> {
628
+ const summariesDir = path.join(memoryRoot, "rollout_summaries");
629
+ await fs.mkdir(summariesDir, { recursive: true });
630
+
631
+ const keepFiles = new Set<string>();
632
+ for (const row of outputs) {
633
+ const stem = formatRolloutFilename(row.threadId, row.rolloutSlug);
634
+ const filename = `${stem}.md`;
635
+ keepFiles.add(filename);
636
+ const body = [`thread_id: ${row.threadId}`, `updated_at: ${row.sourceUpdatedAt}`, "", row.rolloutSummary].join(
637
+ "\n",
638
+ );
639
+ await Bun.write(path.join(summariesDir, filename), `${body.trim()}\n`);
640
+ }
641
+
642
+ const currentFiles = await fs.readdir(summariesDir).catch(() => [] as string[]);
643
+ for (const file of currentFiles) {
644
+ if (!file.endsWith(".md")) continue;
645
+ if (keepFiles.has(file)) continue;
646
+ await fs.rm(path.join(summariesDir, file), { force: true });
647
+ }
648
+
649
+ const rawBody = buildRawMemoriesMarkdown(outputs);
650
+ await Bun.write(path.join(memoryRoot, "raw_memories.md"), rawBody);
651
+ }
652
+
653
+ async function cleanupConsolidatedArtifacts(memoryRoot: string): Promise<void> {
654
+ await fs.rm(path.join(memoryRoot, "MEMORY.md"), { force: true });
655
+ await fs.rm(path.join(memoryRoot, "memory_summary.md"), { force: true });
656
+ await fs.rm(path.join(memoryRoot, "skills"), { recursive: true, force: true });
657
+ }
658
+
659
+ function buildRawMemoriesMarkdown(outputs: Stage1OutputRow[]): string {
660
+ if (outputs.length === 0) {
661
+ return "# Raw Memories\n\nNo raw memories yet.\n";
662
+ }
663
+
664
+ const blocks = outputs.map(row => {
665
+ const header = [`## ${row.threadId}`, `updated_at: ${row.sourceUpdatedAt}`, ""].join("\n");
666
+ return `${header}${row.rawMemory.trim()}\n`;
667
+ });
668
+ return `# Raw Memories\n\n${blocks.join("\n")}`;
669
+ }
670
+
671
+ async function readRolloutSummaries(memoryRoot: string): Promise<string> {
672
+ const summariesDir = path.join(memoryRoot, "rollout_summaries");
673
+ const names = await fs.readdir(summariesDir).catch(() => [] as string[]);
674
+ const summaryNames = names.filter(name => name.endsWith(".md")).sort((a, b) => a.localeCompare(b));
675
+ if (summaryNames.length === 0) return "No rollout summaries yet.";
676
+
677
+ const blocks: string[] = [];
678
+ for (const name of summaryNames) {
679
+ const text = await Bun.file(path.join(summariesDir, name))
680
+ .text()
681
+ .catch(() => "");
682
+ if (!text.trim()) continue;
683
+ blocks.push(`--- ${name} ---\n${text.trim()}`);
684
+ }
685
+ if (blocks.length === 0) return "No rollout summaries yet.";
686
+ return blocks.join("\n\n");
687
+ }
688
+
689
+ async function runConsolidationModel(options: { memoryRoot: string; model: Model; apiKey: string }): Promise<{
690
+ memoryMd: string;
691
+ memorySummary: string;
692
+ skills: Array<{
693
+ name: string;
694
+ content: string;
695
+ scripts: ConsolidationSkillFileSchema[];
696
+ templates: ConsolidationSkillFileSchema[];
697
+ examples: ConsolidationSkillFileSchema[];
698
+ }>;
699
+ }> {
700
+ const { memoryRoot, model, apiKey } = options;
701
+ const rawMemories = await Bun.file(path.join(memoryRoot, "raw_memories.md")).text();
702
+ const rolloutSummaries = await readRolloutSummaries(memoryRoot);
703
+ const input = renderPromptTemplate(consolidationTemplate, {
704
+ raw_memories: truncateByApproxTokens(rawMemories, 20_000),
705
+ rollout_summaries: truncateByApproxTokens(rolloutSummaries, 12_000),
706
+ });
707
+
708
+ const response = await completeSimple(
709
+ model,
710
+ {
711
+ messages: [{ role: "user", content: [{ type: "text", text: input }], timestamp: Date.now() }],
712
+ },
713
+ { apiKey, maxTokens: 8192, reasoning: "medium" },
714
+ );
715
+ if (response.stopReason === "error") {
716
+ throw new Error(response.errorMessage || "phase2 model error");
717
+ }
718
+ const text = response.content
719
+ .filter((c): c is { type: "text"; text: string } => c.type === "text")
720
+ .map(c => c.text)
721
+ .join("\n")
722
+ .trim();
723
+ const parsed = parseJsonObject(text);
724
+ if (!parsed) throw new Error("phase2 JSON parse failure");
725
+ const schemaOutput = parseConsolidationOutputSchema(parsed);
726
+ if (!schemaOutput) throw new Error("phase2 JSON schema validation failure");
727
+ const memoryMd = redactSecrets(schemaOutput.memory_md).trim();
728
+ const memorySummary = redactSecrets(schemaOutput.memory_summary).trim();
729
+ const skills = schemaOutput.skills
730
+ .map(item => {
731
+ const name = sanitizeSkillName(item.name.trim());
732
+ const content = redactSecrets(item.content ?? "").trim();
733
+ if (!name || !content) return null;
734
+ return {
735
+ name,
736
+ content,
737
+ scripts: sanitizeConsolidationSkillFiles(item.scripts, "scripts"),
738
+ templates: sanitizeConsolidationSkillFiles(item.templates, "templates"),
739
+ examples: sanitizeConsolidationSkillFiles(item.examples, "examples"),
740
+ };
741
+ })
742
+ .filter(
743
+ (
744
+ item,
745
+ ): item is {
746
+ name: string;
747
+ content: string;
748
+ scripts: ConsolidationSkillFileSchema[];
749
+ templates: ConsolidationSkillFileSchema[];
750
+ examples: ConsolidationSkillFileSchema[];
751
+ } => item !== null,
752
+ );
753
+ if (!memoryMd || !memorySummary) {
754
+ throw new Error("phase2 returned empty consolidated memory");
755
+ }
756
+ return { memoryMd, memorySummary, skills };
757
+ }
758
+
759
+ async function applyConsolidation(
760
+ memoryRoot: string,
761
+ consolidated: {
762
+ memoryMd: string;
763
+ memorySummary: string;
764
+ skills: Array<{
765
+ name: string;
766
+ content: string;
767
+ scripts: ConsolidationSkillFileSchema[];
768
+ templates: ConsolidationSkillFileSchema[];
769
+ examples: ConsolidationSkillFileSchema[];
770
+ }>;
771
+ },
772
+ ): Promise<void> {
773
+ await Bun.write(path.join(memoryRoot, "MEMORY.md"), `${consolidated.memoryMd.trim()}\n`);
774
+ await Bun.write(path.join(memoryRoot, "memory_summary.md"), `${consolidated.memorySummary.trim()}\n`);
775
+ const skillsDir = path.join(memoryRoot, "skills");
776
+ await fs.mkdir(skillsDir, { recursive: true });
777
+ const keep = new Set<string>();
778
+ for (const skill of consolidated.skills) {
779
+ const dir = path.join(skillsDir, skill.name);
780
+ keep.add(skill.name);
781
+ await fs.mkdir(dir, { recursive: true });
782
+ const files = new Map<string, string>();
783
+ files.set("SKILL.md", `${skill.content.trim()}\n`);
784
+ for (const item of skill.scripts) {
785
+ files.set(path.posix.join("scripts", item.path), `${item.content.trim()}\n`);
786
+ }
787
+ for (const item of skill.templates) {
788
+ files.set(path.posix.join("templates", item.path), `${item.content.trim()}\n`);
789
+ }
790
+ for (const item of skill.examples) {
791
+ files.set(path.posix.join("examples", item.path), `${item.content.trim()}\n`);
792
+ }
793
+
794
+ for (const [relativePath, content] of [...files.entries()].sort(([a], [b]) => a.localeCompare(b))) {
795
+ await Bun.write(path.join(dir, ...relativePath.split("/")), content);
796
+ }
797
+
798
+ const keepFiles = new Set(files.keys());
799
+ const existingFiles = await listRelativeFiles(dir);
800
+ for (const relativePath of existingFiles) {
801
+ if (keepFiles.has(relativePath)) continue;
802
+ await fs.rm(path.join(dir, ...relativePath.split("/")), { force: true });
803
+ }
804
+ await pruneEmptyDirectories(dir);
805
+ }
806
+ const dirs = await fs.readdir(skillsDir, { withFileTypes: true }).catch(() => []);
807
+ for (const dirent of dirs) {
808
+ if (!dirent.isDirectory()) continue;
809
+ if (keep.has(dirent.name)) continue;
810
+ await fs.rm(path.join(skillsDir, dirent.name), { recursive: true, force: true });
811
+ }
812
+ }
813
+
814
+ async function listRelativeFiles(rootDir: string, prefix = ""): Promise<string[]> {
815
+ const entries = await fs.readdir(rootDir, { withFileTypes: true }).catch(() => []);
816
+ const files: string[] = [];
817
+ for (const entry of entries) {
818
+ const relative = prefix ? `${prefix}/${entry.name}` : entry.name;
819
+ if (entry.isDirectory()) {
820
+ files.push(...(await listRelativeFiles(path.join(rootDir, entry.name), relative)));
821
+ continue;
822
+ }
823
+ if (entry.isFile()) files.push(relative);
824
+ }
825
+ return files;
826
+ }
827
+
828
+ async function pruneEmptyDirectories(rootDir: string): Promise<void> {
829
+ const entries = await fs.readdir(rootDir, { withFileTypes: true }).catch(() => []);
830
+ for (const entry of entries) {
831
+ if (!entry.isDirectory()) continue;
832
+ const child = path.join(rootDir, entry.name);
833
+ await pruneEmptyDirectories(child);
834
+ const childEntries = await fs.readdir(child).catch(() => []);
835
+ if (childEntries.length === 0) {
836
+ await fs.rm(child, { recursive: true, force: true });
837
+ }
838
+ }
839
+ }
840
+
841
+ function computeCompletionWatermark(claimedInputWatermark: number, outputs: Stage1OutputRow[]): number {
842
+ const maxOutputWatermark = outputs.reduce((max, row) => Math.max(max, row.sourceUpdatedAt), claimedInputWatermark);
843
+ return Math.max(claimedInputWatermark, maxOutputWatermark);
844
+ }
845
+
846
+ function formatRolloutFilename(threadId: string, rolloutSlug: string | null): string {
847
+ if (!rolloutSlug) return threadId;
848
+ const normalized = rolloutSlug
849
+ .toLowerCase()
850
+ .replace(/[^a-z0-9]+/g, "_")
851
+ .replace(/_+$/g, "")
852
+ .slice(0, 20);
853
+ if (!normalized) return threadId;
854
+ return `${threadId}-${normalized}`;
855
+ }
856
+
857
+ function parseJsonObject(text: string): Record<string, unknown> | undefined {
858
+ if (!text) return undefined;
859
+ try {
860
+ const parsed = JSON.parse(text) as unknown;
861
+ if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
862
+ return parsed as Record<string, unknown>;
863
+ }
864
+ } catch {
865
+ const match = text.match(/\{[\s\S]*\}/);
866
+ if (!match) return undefined;
867
+ try {
868
+ const parsed = JSON.parse(match[0]) as unknown;
869
+ if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
870
+ return parsed as Record<string, unknown>;
871
+ }
872
+ } catch {
873
+ return undefined;
874
+ }
875
+ }
876
+ return undefined;
877
+ }
878
+
879
+ function parseStage1OutputSchema(value: Record<string, unknown>): Stage1OutputSchema | undefined {
880
+ if (!hasExactKeys(value, ["rollout_summary", "rollout_slug", "raw_memory"])) return undefined;
881
+ if (typeof value.rollout_summary !== "string") return undefined;
882
+ if (!(typeof value.rollout_slug === "string" || value.rollout_slug === null)) return undefined;
883
+ if (typeof value.raw_memory !== "string") return undefined;
884
+ return {
885
+ rollout_summary: value.rollout_summary,
886
+ rollout_slug: value.rollout_slug,
887
+ raw_memory: value.raw_memory,
888
+ };
889
+ }
890
+
891
+ function parseConsolidationOutputSchema(value: Record<string, unknown>): ConsolidationOutputSchema | undefined {
892
+ if (!hasExactKeys(value, ["memory_md", "memory_summary", "skills"])) return undefined;
893
+ if (typeof value.memory_md !== "string") return undefined;
894
+ if (typeof value.memory_summary !== "string") return undefined;
895
+ if (!Array.isArray(value.skills)) return undefined;
896
+ const skills: ConsolidationSkillSchema[] = [];
897
+ for (const item of value.skills) {
898
+ if (!item || typeof item !== "object" || Array.isArray(item)) return undefined;
899
+ const data = item as Record<string, unknown>;
900
+ if (!hasExactKeys(data, ["name", "content", "scripts", "templates", "examples"], true)) return undefined;
901
+ if (typeof data.name !== "string") return undefined;
902
+ if (!(typeof data.content === "string" || data.content === undefined)) return undefined;
903
+ const scripts = parseConsolidationSkillFileArray(data.scripts);
904
+ const templates = parseConsolidationSkillFileArray(data.templates);
905
+ const examples = parseConsolidationSkillFileArray(data.examples);
906
+ if (!scripts || !templates || !examples) return undefined;
907
+ skills.push({
908
+ name: data.name,
909
+ content: data.content,
910
+ scripts,
911
+ templates,
912
+ examples,
913
+ });
914
+ }
915
+ return {
916
+ memory_md: value.memory_md,
917
+ memory_summary: value.memory_summary,
918
+ skills,
919
+ };
920
+ }
921
+
922
+ function hasExactKeys(value: Record<string, unknown>, expectedKeys: string[], allowMissing = false): boolean {
923
+ const sortedKeys = Object.keys(value).sort();
924
+ const sortedExpected = [...expectedKeys].sort();
925
+ if (!allowMissing && sortedKeys.length !== sortedExpected.length) return false;
926
+ for (const key of sortedKeys) {
927
+ if (!sortedExpected.includes(key)) return false;
928
+ }
929
+ if (allowMissing) return true;
930
+ for (let i = 0; i < sortedExpected.length; i += 1) {
931
+ if (sortedKeys[i] !== sortedExpected[i]) return false;
932
+ }
933
+ return true;
934
+ }
935
+
936
+ function redactSecrets(input: string): string {
937
+ let out = input;
938
+ const patterns = [
939
+ /(?:sk|pk|rk|tok|key|secret|token|password)[-_A-Za-z0-9]{12,}/g,
940
+ /[A-Za-z0-9_-]{16,}\.[A-Za-z0-9_-]{16,}\.[A-Za-z0-9_-]{16,}/g,
941
+ /(?:AKIA|ASIA)[A-Z0-9]{16}/g,
942
+ ];
943
+ for (const pattern of patterns) {
944
+ out = out.replace(pattern, "[REDACTED]");
945
+ }
946
+ return out;
947
+ }
948
+
949
+ function sanitizeSkillName(name: string): string {
950
+ return name
951
+ .toLowerCase()
952
+ .replace(/[^a-z0-9_-]+/g, "-")
953
+ .replace(/^-+|-+$/g, "")
954
+ .slice(0, 64);
955
+ }
956
+
957
+ function parseConsolidationSkillFileArray(value: unknown): ConsolidationSkillFileSchema[] | undefined {
958
+ if (value === undefined) return [];
959
+ if (!Array.isArray(value)) return undefined;
960
+ const files: ConsolidationSkillFileSchema[] = [];
961
+ for (const item of value) {
962
+ if (!item || typeof item !== "object" || Array.isArray(item)) return undefined;
963
+ const data = item as Record<string, unknown>;
964
+ if (!hasExactKeys(data, ["path", "content"])) return undefined;
965
+ if (typeof data.path !== "string" || typeof data.content !== "string") return undefined;
966
+ files.push({ path: data.path, content: data.content });
967
+ }
968
+ return files;
969
+ }
970
+
971
+ function sanitizeConsolidationSkillFiles(
972
+ files: ConsolidationSkillFileSchema[] | undefined,
973
+ bucket: "scripts" | "templates" | "examples",
974
+ ): ConsolidationSkillFileSchema[] {
975
+ if (!files || files.length === 0) return [];
976
+ const sanitized = new Map<string, string>();
977
+ for (const file of files) {
978
+ const relativePath = sanitizeSkillRelativePath(file.path);
979
+ if (!relativePath) continue;
980
+ const content = redactSecrets(file.content).trim();
981
+ if (!content) continue;
982
+ sanitized.set(path.posix.join(bucket, relativePath), content);
983
+ }
984
+ return [...sanitized.entries()]
985
+ .sort(([a], [b]) => a.localeCompare(b))
986
+ .map(([fullPath, content]) => ({
987
+ path: fullPath.slice(bucket.length + 1),
988
+ content,
989
+ }));
990
+ }
991
+
992
+ function sanitizeSkillRelativePath(rawPath: string): string | undefined {
993
+ const normalized = rawPath.replace(/\\/g, "/").trim();
994
+ if (!normalized) return undefined;
995
+ if (normalized.startsWith("/")) return undefined;
996
+ if (normalized.includes("\0")) return undefined;
997
+ if (normalized.includes(":")) return undefined;
998
+ const parts = normalized.split("/").filter(Boolean);
999
+ if (parts.length === 0) return undefined;
1000
+ for (const part of parts) {
1001
+ if (part === "." || part === "..") return undefined;
1002
+ if (!/^[A-Za-z0-9._-]+$/.test(part)) return undefined;
1003
+ }
1004
+ return parts.join("/");
1005
+ }
1006
+
1007
+ function extractMessageText(message: AgentMessage): string {
1008
+ const content = (message as { content?: unknown }).content;
1009
+ if (typeof content === "string") return content;
1010
+ if (!Array.isArray(content)) return "";
1011
+ return content
1012
+ .map(item => {
1013
+ if (item.type === "text") return item.text;
1014
+ if (item.type === "toolCall") return `${item.toolName} ${JSON.stringify(item.arguments)}`;
1015
+ return "";
1016
+ })
1017
+ .join("\n");
1018
+ }
1019
+
1020
+ function truncateByApproxTokens(text: string, tokenLimit: number): string {
1021
+ if (tokenLimit <= 0) return "";
1022
+ const maxChars = tokenLimit * 4;
1023
+ if (text.length <= maxChars) return text;
1024
+ const head = Math.floor(maxChars * 0.6);
1025
+ const tail = maxChars - head;
1026
+ return `${text.slice(0, head)}\n\n...[truncated]...\n\n${text.slice(-tail)}`;
1027
+ }
1028
+
1029
+ function computeModelTokenBudget(model: Model, config: MemoryRuntimeConfig): number {
1030
+ const maxTokens =
1031
+ Number.isFinite(model.contextWindow) && model.contextWindow > 0 ? model.contextWindow : config.fallbackTokenLimit;
1032
+ return Math.max(2048, Math.floor(maxTokens));
1033
+ }
1034
+
1035
+ async function resolveMemoryModel(options: {
1036
+ modelRegistry: ModelRegistry;
1037
+ session: AgentSession;
1038
+ fallbackRole: string;
1039
+ }): Promise<Model | undefined> {
1040
+ const { modelRegistry, session, fallbackRole } = options;
1041
+ const requestedModel = session.settings.getModelRole(fallbackRole) || session.settings.getModelRole("default");
1042
+ if (requestedModel) {
1043
+ const parsed = parseModelString(requestedModel);
1044
+ if (parsed) {
1045
+ const found = modelRegistry.find(parsed.provider, parsed.id);
1046
+ if (found) return found;
1047
+ }
1048
+ }
1049
+ return session.model ?? modelRegistry.getAll()[0];
1050
+ }
1051
+
1052
+ function loadMemoryConfig(settings: Settings): MemoryRuntimeConfig {
1053
+ return {
1054
+ enabled: settings.get("memories.enabled") ?? DEFAULTS.enabled,
1055
+ maxRolloutsPerStartup: settings.get("memories.maxRolloutsPerStartup") ?? DEFAULTS.maxRolloutsPerStartup,
1056
+ maxRolloutAgeDays: settings.get("memories.maxRolloutAgeDays") ?? DEFAULTS.maxRolloutAgeDays,
1057
+ minRolloutIdleHours: settings.get("memories.minRolloutIdleHours") ?? DEFAULTS.minRolloutIdleHours,
1058
+ threadScanLimit: settings.get("memories.threadScanLimit") ?? DEFAULTS.threadScanLimit,
1059
+ maxRawMemoriesForGlobal: settings.get("memories.maxRawMemoriesForGlobal") ?? DEFAULTS.maxRawMemoriesForGlobal,
1060
+ stage1Concurrency: settings.get("memories.stage1Concurrency") ?? DEFAULTS.stage1Concurrency,
1061
+ stage1LeaseSeconds: settings.get("memories.stage1LeaseSeconds") ?? DEFAULTS.stage1LeaseSeconds,
1062
+ stage1RetryDelaySeconds: settings.get("memories.stage1RetryDelaySeconds") ?? DEFAULTS.stage1RetryDelaySeconds,
1063
+ phase2LeaseSeconds: settings.get("memories.phase2LeaseSeconds") ?? DEFAULTS.phase2LeaseSeconds,
1064
+ phase2RetryDelaySeconds: settings.get("memories.phase2RetryDelaySeconds") ?? DEFAULTS.phase2RetryDelaySeconds,
1065
+ phase2HeartbeatSeconds: settings.get("memories.phase2HeartbeatSeconds") ?? DEFAULTS.phase2HeartbeatSeconds,
1066
+ rolloutPayloadPercent: settings.get("memories.rolloutPayloadPercent") ?? DEFAULTS.rolloutPayloadPercent,
1067
+ fallbackTokenLimit: settings.get("memories.fallbackTokenLimit") ?? DEFAULTS.fallbackTokenLimit,
1068
+ summaryInjectionTokenLimit:
1069
+ settings.get("memories.summaryInjectionTokenLimit") ?? DEFAULTS.summaryInjectionTokenLimit,
1070
+ };
1071
+ }
1072
+
1073
+ export function getMemoryRoot(agentDir: string, cwd: string): string {
1074
+ return path.join(agentDir, "memories", encodeProjectPath(cwd));
1075
+ }
1076
+
1077
+ function encodeProjectPath(cwd: string): string {
1078
+ return `--${cwd.replace(/^[/\\]/, "").replace(/[/\\:]/g, "-")}--`;
1079
+ }
1080
+
1081
+ function unixNow(): number {
1082
+ return Math.floor(Date.now() / 1000);
1083
+ }
1084
+
1085
+ async function runWithConcurrency<T>(
1086
+ items: T[],
1087
+ concurrency: number,
1088
+ worker: (item: T) => Promise<void>,
1089
+ ): Promise<void> {
1090
+ const queue = [...items];
1091
+ const workers = new Array(Math.max(1, concurrency)).fill(0).map(async () => {
1092
+ while (queue.length > 0) {
1093
+ const item = queue.shift();
1094
+ if (!item) return;
1095
+ await worker(item);
1096
+ }
1097
+ });
1098
+ await Promise.all(workers);
1099
+ }