@selesai/code 0.12.0 → 0.13.1

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 (466) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/dist/core/agent-session.d.ts +1 -1
  3. package/dist/core/agent-session.js +1 -1
  4. package/dist/core/sdk.d.ts +2 -2
  5. package/dist/core/sdk.js +1 -1
  6. package/dist/core/system-prompt.d.ts +1 -1
  7. package/dist/core/system-prompt.js +1 -1
  8. package/dist/core/usage-totals.d.ts +2 -0
  9. package/dist/core/usage-totals.js +10 -0
  10. package/dist/extensions/ascii.txt +9 -0
  11. package/dist/extensions/package.json +1 -2
  12. package/dist/extensions/pi-hermes-memory/CHANGELOG.md +371 -0
  13. package/dist/extensions/pi-hermes-memory/LICENSE +21 -0
  14. package/dist/extensions/pi-hermes-memory/README.md +643 -0
  15. package/dist/extensions/pi-hermes-memory/docs/0.1/TASKS.md +197 -0
  16. package/dist/extensions/pi-hermes-memory/docs/0.2/PLAN.md +290 -0
  17. package/dist/extensions/pi-hermes-memory/docs/0.2/TASKS.md +134 -0
  18. package/dist/extensions/pi-hermes-memory/docs/0.2/TEST-PLAN.md +216 -0
  19. package/dist/extensions/pi-hermes-memory/docs/0.3/PLAN.md +330 -0
  20. package/dist/extensions/pi-hermes-memory/docs/0.3/TASKS.md +125 -0
  21. package/dist/extensions/pi-hermes-memory/docs/0.4/PLAN.md +160 -0
  22. package/dist/extensions/pi-hermes-memory/docs/0.4/TASKS.md +113 -0
  23. package/dist/extensions/pi-hermes-memory/docs/0.6/CHANGELOG.md +57 -0
  24. package/dist/extensions/pi-hermes-memory/docs/0.6/PLAN.md +202 -0
  25. package/dist/extensions/pi-hermes-memory/docs/0.6/TASKS.md +144 -0
  26. package/dist/extensions/pi-hermes-memory/docs/0.7/PLAN.md +349 -0
  27. package/dist/extensions/pi-hermes-memory/docs/0.7/TASKS.md +110 -0
  28. package/dist/extensions/pi-hermes-memory/docs/PUBLISHING.md +149 -0
  29. package/dist/extensions/pi-hermes-memory/docs/ROADMAP.md +468 -0
  30. package/dist/extensions/pi-hermes-memory/docs/images/memory-architecture.svg +1 -0
  31. package/dist/extensions/pi-hermes-memory/docs/images/pi-logo.svg +22 -0
  32. package/dist/extensions/pi-hermes-memory/docs/images/pi_memory.png +0 -0
  33. package/dist/extensions/pi-hermes-memory/docs/images/pi_memory.svg +19 -0
  34. package/dist/extensions/pi-hermes-memory/docs/images/security-flow.svg +1 -0
  35. package/dist/extensions/pi-hermes-memory/docs/images/session-lifecycle.svg +1 -0
  36. package/dist/extensions/pi-hermes-memory/docs/images/source-architecture.svg +1 -0
  37. package/dist/extensions/pi-hermes-memory/docs/mermaid/memory-architecture.mmd +49 -0
  38. package/dist/extensions/pi-hermes-memory/docs/mermaid/security-flow.mmd +40 -0
  39. package/dist/extensions/pi-hermes-memory/docs/mermaid/session-lifecycle.mmd +56 -0
  40. package/dist/extensions/pi-hermes-memory/docs/mermaid/source-architecture.mmd +54 -0
  41. package/dist/extensions/pi-hermes-memory/package-lock.json +3799 -0
  42. package/dist/extensions/pi-hermes-memory/package.json +66 -0
  43. package/dist/extensions/pi-hermes-memory/scripts/check-min-sdk.mjs +99 -0
  44. package/dist/extensions/pi-hermes-memory/scripts/ensure-dev.mjs +57 -0
  45. package/dist/extensions/pi-hermes-memory/src/auto-consolidation-warning.ts +6 -0
  46. package/dist/extensions/pi-hermes-memory/src/config.ts +178 -0
  47. package/dist/extensions/pi-hermes-memory/src/constants.ts +424 -0
  48. package/dist/extensions/pi-hermes-memory/src/extension-root-migration.ts +639 -0
  49. package/dist/extensions/pi-hermes-memory/src/handlers/auto-consolidate.ts +401 -0
  50. package/dist/extensions/pi-hermes-memory/src/handlers/background-review.ts +297 -0
  51. package/dist/extensions/pi-hermes-memory/src/handlers/child-process-watchdog.mjs +90 -0
  52. package/dist/extensions/pi-hermes-memory/src/handlers/correction-detector.ts +300 -0
  53. package/dist/extensions/pi-hermes-memory/src/handlers/index-sessions.ts +86 -0
  54. package/dist/extensions/pi-hermes-memory/src/handlers/insights.ts +78 -0
  55. package/dist/extensions/pi-hermes-memory/src/handlers/interview.ts +37 -0
  56. package/dist/extensions/pi-hermes-memory/src/handlers/learn-memory.ts +186 -0
  57. package/dist/extensions/pi-hermes-memory/src/handlers/message-parts.ts +27 -0
  58. package/dist/extensions/pi-hermes-memory/src/handlers/pi-child-process.ts +485 -0
  59. package/dist/extensions/pi-hermes-memory/src/handlers/preview-context.ts +105 -0
  60. package/dist/extensions/pi-hermes-memory/src/handlers/review-memory-ops.ts +506 -0
  61. package/dist/extensions/pi-hermes-memory/src/handlers/session-backfill.ts +145 -0
  62. package/dist/extensions/pi-hermes-memory/src/handlers/session-flush.ts +151 -0
  63. package/dist/extensions/pi-hermes-memory/src/handlers/session-live-index.ts +94 -0
  64. package/dist/extensions/pi-hermes-memory/src/handlers/skills-command.ts +1334 -0
  65. package/dist/extensions/pi-hermes-memory/src/handlers/standing-pin.ts +111 -0
  66. package/dist/extensions/pi-hermes-memory/src/handlers/switch-project.ts +75 -0
  67. package/dist/extensions/pi-hermes-memory/src/handlers/sync-markdown-memories.ts +266 -0
  68. package/dist/extensions/pi-hermes-memory/src/index.ts +378 -0
  69. package/dist/extensions/pi-hermes-memory/src/lifecycle-timing.ts +54 -0
  70. package/dist/extensions/pi-hermes-memory/src/paths.ts +61 -0
  71. package/dist/extensions/pi-hermes-memory/src/project-context.ts +13 -0
  72. package/dist/extensions/pi-hermes-memory/src/project-memory-migration.ts +93 -0
  73. package/dist/extensions/pi-hermes-memory/src/project.ts +157 -0
  74. package/dist/extensions/pi-hermes-memory/src/prompt-context.ts +55 -0
  75. package/dist/extensions/pi-hermes-memory/src/store/atomic-lock-coordinator.ts +375 -0
  76. package/dist/extensions/pi-hermes-memory/src/store/canonical-storage-path.ts +54 -0
  77. package/dist/extensions/pi-hermes-memory/src/store/content-scanner.ts +101 -0
  78. package/dist/extensions/pi-hermes-memory/src/store/db.ts +1180 -0
  79. package/dist/extensions/pi-hermes-memory/src/store/fts-query.ts +101 -0
  80. package/dist/extensions/pi-hermes-memory/src/store/markdown-mutation-lock.ts +38 -0
  81. package/dist/extensions/pi-hermes-memory/src/store/memory-lookup.ts +24 -0
  82. package/dist/extensions/pi-hermes-memory/src/store/memory-store.ts +1191 -0
  83. package/dist/extensions/pi-hermes-memory/src/store/recovery-maintenance.ts +66 -0
  84. package/dist/extensions/pi-hermes-memory/src/store/schema.ts +118 -0
  85. package/dist/extensions/pi-hermes-memory/src/store/session-anchor-search.ts +472 -0
  86. package/dist/extensions/pi-hermes-memory/src/store/session-indexer.ts +511 -0
  87. package/dist/extensions/pi-hermes-memory/src/store/session-parser.ts +213 -0
  88. package/dist/extensions/pi-hermes-memory/src/store/session-search.ts +234 -0
  89. package/dist/extensions/pi-hermes-memory/src/store/skill-store.ts +950 -0
  90. package/dist/extensions/pi-hermes-memory/src/store/skill-utils.ts +133 -0
  91. package/dist/extensions/pi-hermes-memory/src/store/sqlite-memory-store.ts +1002 -0
  92. package/dist/extensions/pi-hermes-memory/src/store/sqlite-native.ts +241 -0
  93. package/dist/extensions/pi-hermes-memory/src/store/standing-instructions.ts +247 -0
  94. package/dist/extensions/pi-hermes-memory/src/tools/memory-search-tool.ts +95 -0
  95. package/dist/extensions/pi-hermes-memory/src/tools/memory-tool.ts +469 -0
  96. package/dist/extensions/pi-hermes-memory/src/tools/session-search-tool.ts +246 -0
  97. package/dist/extensions/pi-hermes-memory/src/tools/shared-output-view.ts +170 -0
  98. package/dist/extensions/pi-hermes-memory/src/tools/skill-tool.ts +333 -0
  99. package/dist/extensions/pi-hermes-memory/src/tools/tool-result-views.ts +105 -0
  100. package/dist/extensions/pi-hermes-memory/src/types.ts +214 -0
  101. package/dist/extensions/pi-hermes-memory/tests/config.test.ts +434 -0
  102. package/dist/extensions/pi-hermes-memory/tests/extension-root-migration.test.ts +368 -0
  103. package/dist/extensions/pi-hermes-memory/tests/handlers/auto-consolidate.test.ts +984 -0
  104. package/dist/extensions/pi-hermes-memory/tests/handlers/background-review.test.ts +950 -0
  105. package/dist/extensions/pi-hermes-memory/tests/handlers/correction-detector.test.ts +671 -0
  106. package/dist/extensions/pi-hermes-memory/tests/handlers/insights.test.ts +157 -0
  107. package/dist/extensions/pi-hermes-memory/tests/handlers/interview.test.ts +127 -0
  108. package/dist/extensions/pi-hermes-memory/tests/handlers/pi-child-process.test.ts +905 -0
  109. package/dist/extensions/pi-hermes-memory/tests/handlers/preview-context.test.ts +152 -0
  110. package/dist/extensions/pi-hermes-memory/tests/handlers/prompt-context.test.ts +166 -0
  111. package/dist/extensions/pi-hermes-memory/tests/handlers/resources-discover.test.ts +71 -0
  112. package/dist/extensions/pi-hermes-memory/tests/handlers/review-memory-ops.test.ts +614 -0
  113. package/dist/extensions/pi-hermes-memory/tests/handlers/session-backfill.test.ts +192 -0
  114. package/dist/extensions/pi-hermes-memory/tests/handlers/session-flush.test.ts +677 -0
  115. package/dist/extensions/pi-hermes-memory/tests/handlers/session-live-index.test.ts +225 -0
  116. package/dist/extensions/pi-hermes-memory/tests/handlers/skills-command.test.ts +827 -0
  117. package/dist/extensions/pi-hermes-memory/tests/handlers/standing-pin.test.ts +134 -0
  118. package/dist/extensions/pi-hermes-memory/tests/handlers/sync-markdown-memories.test.ts +491 -0
  119. package/dist/extensions/pi-hermes-memory/tests/handlers/system-prompt.test.ts +199 -0
  120. package/dist/extensions/pi-hermes-memory/tests/handlers/thinking-max.test.ts +64 -0
  121. package/dist/extensions/pi-hermes-memory/tests/index.test.ts +30 -0
  122. package/dist/extensions/pi-hermes-memory/tests/integration/flow.test.ts +126 -0
  123. package/dist/extensions/pi-hermes-memory/tests/lifecycle-timing.test.ts +76 -0
  124. package/dist/extensions/pi-hermes-memory/tests/paths.test.ts +23 -0
  125. package/dist/extensions/pi-hermes-memory/tests/project-memory-migration.test.ts +80 -0
  126. package/dist/extensions/pi-hermes-memory/tests/project-rebinding.test.ts +115 -0
  127. package/dist/extensions/pi-hermes-memory/tests/project.test.ts +120 -0
  128. package/dist/extensions/pi-hermes-memory/tests/run-all-timeout.test.ts +107 -0
  129. package/dist/extensions/pi-hermes-memory/tests/run-all.sh +45 -0
  130. package/dist/extensions/pi-hermes-memory/tests/store/atomic-lock-coordinator.test.ts +381 -0
  131. package/dist/extensions/pi-hermes-memory/tests/store/content-scanner.test.ts +388 -0
  132. package/dist/extensions/pi-hermes-memory/tests/store/db.test.ts +1046 -0
  133. package/dist/extensions/pi-hermes-memory/tests/store/markdown-mutation-lock.test.ts +34 -0
  134. package/dist/extensions/pi-hermes-memory/tests/store/memory-store.test.ts +2150 -0
  135. package/dist/extensions/pi-hermes-memory/tests/store/recovery-maintenance.test.ts +170 -0
  136. package/dist/extensions/pi-hermes-memory/tests/store/session-anchor-search.test.ts +210 -0
  137. package/dist/extensions/pi-hermes-memory/tests/store/session-indexer.test.ts +643 -0
  138. package/dist/extensions/pi-hermes-memory/tests/store/session-parser.test.ts +282 -0
  139. package/dist/extensions/pi-hermes-memory/tests/store/session-search.test.ts +287 -0
  140. package/dist/extensions/pi-hermes-memory/tests/store/skill-store.test.ts +678 -0
  141. package/dist/extensions/pi-hermes-memory/tests/store/skill-utils.test.ts +21 -0
  142. package/dist/extensions/pi-hermes-memory/tests/store/sqlite-lazy-load.test.ts +78 -0
  143. package/dist/extensions/pi-hermes-memory/tests/store/sqlite-memory-store.test.ts +543 -0
  144. package/dist/extensions/pi-hermes-memory/tests/store/sqlite-native.test.ts +164 -0
  145. package/dist/extensions/pi-hermes-memory/tests/store/standing-instructions.test.ts +171 -0
  146. package/dist/extensions/pi-hermes-memory/tests/tools/memory-search-tool.test.ts +84 -0
  147. package/dist/extensions/pi-hermes-memory/tests/tools/memory-tool.test.ts +709 -0
  148. package/dist/extensions/pi-hermes-memory/tests/tools/session-search-tool.test.ts +345 -0
  149. package/dist/extensions/pi-hermes-memory/tests/tools/shared-output-view.test.ts +362 -0
  150. package/dist/extensions/pi-hermes-memory/tests/tools/skill-tool.test.ts +478 -0
  151. package/dist/extensions/pi-hermes-memory/tests/tools/tool-result-renderer-wiring.test.ts +40 -0
  152. package/dist/extensions/pi-hermes-memory/tsconfig.json +19 -0
  153. package/dist/extensions/pi-subagents/.oxlintrc.json +43 -0
  154. package/dist/extensions/pi-subagents/AGENTS.md +5 -0
  155. package/dist/extensions/pi-subagents/CHANGELOG.md +157 -0
  156. package/dist/extensions/pi-subagents/README.md +12 -6
  157. package/dist/extensions/pi-subagents/VISION.md +81 -0
  158. package/dist/extensions/pi-subagents/agents/claude-code-writer.md +15 -0
  159. package/dist/extensions/pi-subagents/agents/claude-code.md +15 -0
  160. package/dist/extensions/pi-subagents/agents/codex-exec-writer.md +15 -0
  161. package/dist/extensions/pi-subagents/agents/codex-exec.md +15 -0
  162. package/dist/extensions/pi-subagents/agents/cursor-agent-writer.md +14 -0
  163. package/dist/extensions/pi-subagents/agents/cursor-agent.md +14 -0
  164. package/dist/extensions/pi-subagents/agents/reviewer.md +1 -3
  165. package/dist/extensions/pi-subagents/docs/agents.md +488 -0
  166. package/dist/extensions/pi-subagents/docs/configuration.md +46 -9
  167. package/dist/extensions/pi-subagents/docs/extension-api.md +42 -3
  168. package/dist/extensions/pi-subagents/docs/models.md +27 -4
  169. package/dist/extensions/pi-subagents/docs/observability.md +10 -9
  170. package/dist/extensions/pi-subagents/docs/tool-reference.md +102 -6
  171. package/dist/extensions/pi-subagents/docs/workflows.md +135 -0
  172. package/dist/extensions/pi-subagents/package-lock.json +389 -2
  173. package/dist/extensions/pi-subagents/package.json +3 -1
  174. package/dist/extensions/pi-subagents/prompts/review-loop.md +2 -2
  175. package/dist/extensions/pi-subagents/skills/council-mode/SKILL.md +1 -1
  176. package/dist/extensions/pi-subagents/skills/pi-subagents/SKILL.md +3 -1
  177. package/dist/extensions/pi-subagents/skills/pi-subagents/references/execution-controls.md +7 -3
  178. package/dist/extensions/pi-subagents/skills/pi-subagents/references/management-authoring-rpc.md +2 -2
  179. package/dist/extensions/pi-subagents/skills/pi-subagents/references/prompting-and-roles.md +26 -5
  180. package/dist/extensions/pi-subagents/src/agents/agent-management.ts +41 -22
  181. package/dist/extensions/pi-subagents/src/agents/agent-refinements.ts +4 -4
  182. package/dist/extensions/pi-subagents/src/agents/agent-serializer.ts +9 -2
  183. package/dist/extensions/pi-subagents/src/agents/agents.ts +322 -75
  184. package/dist/extensions/pi-subagents/src/agents/builtin-names.ts +6 -0
  185. package/dist/extensions/pi-subagents/src/agents/runtime-agent-events.ts +70 -0
  186. package/dist/extensions/pi-subagents/src/agents/runtime-agent-registry.ts +27 -20
  187. package/dist/extensions/pi-subagents/src/api/agents.ts +10 -5
  188. package/dist/extensions/pi-subagents/src/api/background-work.ts +5 -1
  189. package/dist/extensions/pi-subagents/src/api/delegation.ts +0 -7
  190. package/dist/extensions/pi-subagents/src/api/preflight.ts +34 -12
  191. package/dist/extensions/pi-subagents/src/extension/config.ts +70 -0
  192. package/dist/extensions/pi-subagents/src/extension/doctor.ts +3 -3
  193. package/dist/extensions/pi-subagents/src/extension/fanout-child.ts +5 -3
  194. package/dist/extensions/pi-subagents/src/extension/index.ts +83 -30
  195. package/dist/extensions/pi-subagents/src/extension/public-execution.ts +44 -15
  196. package/dist/extensions/pi-subagents/src/extension/rpc.ts +55 -19
  197. package/dist/extensions/pi-subagents/src/extension/schemas.ts +41 -15
  198. package/dist/extensions/pi-subagents/src/extension/tool-description.ts +28 -31
  199. package/dist/extensions/pi-subagents/src/inspectors/herdr/actions.ts +2 -1
  200. package/dist/extensions/pi-subagents/src/inspectors/herdr/inspector-runner.ts +2 -10
  201. package/dist/extensions/pi-subagents/src/inspectors/herdr/session-roots-codec.ts +42 -0
  202. package/dist/extensions/pi-subagents/src/integrations/herdr-status.ts +51 -3
  203. package/dist/extensions/pi-subagents/src/intercom/result-intercom.ts +2 -0
  204. package/dist/extensions/pi-subagents/src/profiles/profiles.ts +5 -6
  205. package/dist/extensions/pi-subagents/src/runs/background/active-async-capacity.ts +77 -10
  206. package/dist/extensions/pi-subagents/src/runs/background/async-execution.ts +176 -53
  207. package/dist/extensions/pi-subagents/src/runs/background/async-job-tracker.ts +19 -12
  208. package/dist/extensions/pi-subagents/src/runs/background/async-resume.ts +29 -27
  209. package/dist/extensions/pi-subagents/src/runs/background/async-retention.ts +20 -3
  210. package/dist/extensions/pi-subagents/src/runs/background/async-status-snapshot.ts +23 -261
  211. package/dist/extensions/pi-subagents/src/runs/background/async-status.ts +73 -7
  212. package/dist/extensions/pi-subagents/src/runs/background/chain-append.ts +8 -3
  213. package/dist/extensions/pi-subagents/src/runs/background/chain-root-attachment.ts +74 -8
  214. package/dist/extensions/pi-subagents/src/runs/background/fleet-view.ts +37 -21
  215. package/dist/extensions/pi-subagents/src/runs/background/inspect-rpc.ts +8 -8
  216. package/dist/extensions/pi-subagents/src/runs/background/notify.ts +184 -9
  217. package/dist/extensions/pi-subagents/src/runs/background/result-delivery-ownership.ts +45 -0
  218. package/dist/extensions/pi-subagents/src/runs/background/result-files.ts +28 -14
  219. package/dist/extensions/pi-subagents/src/runs/background/result-watcher.ts +38 -15
  220. package/dist/extensions/pi-subagents/src/runs/background/resume-guidance.ts +1 -1
  221. package/dist/extensions/pi-subagents/src/runs/background/retained-children.ts +1 -1
  222. package/dist/extensions/pi-subagents/src/runs/background/run-status.ts +50 -13
  223. package/dist/extensions/pi-subagents/src/runs/background/scheduled-runs.ts +93 -8
  224. package/dist/extensions/pi-subagents/src/runs/background/stale-run-reconciler.ts +10 -4
  225. package/dist/extensions/pi-subagents/src/runs/background/steering.ts +4 -14
  226. package/dist/extensions/pi-subagents/src/runs/background/subagent-runner.ts +549 -377
  227. package/dist/extensions/pi-subagents/src/runs/background/subagent-wait.ts +58 -10
  228. package/dist/extensions/pi-subagents/src/runs/background/terminal-run-index.ts +1 -1
  229. package/dist/extensions/pi-subagents/src/runs/background/wait-completions.ts +26 -1
  230. package/dist/extensions/pi-subagents/src/runs/background/wait-config.ts +23 -9
  231. package/dist/extensions/pi-subagents/src/runs/background/wait-tool.ts +9 -2
  232. package/dist/extensions/pi-subagents/src/runs/foreground/async-steering-action.ts +2 -2
  233. package/dist/extensions/pi-subagents/src/runs/foreground/execution.ts +204 -129
  234. package/dist/extensions/pi-subagents/src/runs/foreground/foreground-control.ts +9 -0
  235. package/dist/extensions/pi-subagents/src/runs/foreground/foreground-history.ts +23 -1
  236. package/dist/extensions/pi-subagents/src/runs/foreground/subagent-executor.ts +773 -311
  237. package/dist/extensions/pi-subagents/src/runs/foreground/workflow-detach-reconcile.ts +140 -115
  238. package/dist/extensions/pi-subagents/src/runs/shared/abort-recovery.ts +119 -0
  239. package/dist/extensions/pi-subagents/src/runs/shared/async-status-projection.ts +463 -0
  240. package/dist/extensions/pi-subagents/src/runs/shared/child-identity.ts +19 -4
  241. package/dist/extensions/pi-subagents/src/runs/shared/child-launch-plan.ts +151 -0
  242. package/dist/extensions/pi-subagents/src/runs/shared/child-protocol.ts +21 -7
  243. package/dist/extensions/pi-subagents/src/runs/shared/claude-code-adapter.ts +129 -0
  244. package/dist/extensions/pi-subagents/src/runs/shared/codex-exec-adapter.ts +129 -0
  245. package/dist/extensions/pi-subagents/src/runs/shared/completion-evidence.ts +89 -0
  246. package/dist/extensions/pi-subagents/src/runs/shared/completion-guard.ts +5 -4
  247. package/dist/extensions/pi-subagents/src/runs/shared/cursor-agent-adapter.ts +114 -0
  248. package/dist/extensions/pi-subagents/src/runs/shared/dynamic-fanout.ts +3 -3
  249. package/dist/extensions/pi-subagents/src/runs/shared/external-cli-contract.ts +167 -0
  250. package/dist/extensions/pi-subagents/src/runs/shared/external-cli-preflight.ts +122 -0
  251. package/dist/extensions/pi-subagents/src/runs/shared/external-cli-runner.ts +348 -55
  252. package/dist/extensions/pi-subagents/src/runs/shared/fast-mode-extension.ts +5 -5
  253. package/dist/extensions/pi-subagents/src/runs/shared/host-step-status.ts +230 -0
  254. package/dist/extensions/pi-subagents/src/runs/shared/lane-metadata.ts +105 -0
  255. package/dist/extensions/pi-subagents/src/runs/shared/launch-cwd.ts +16 -0
  256. package/dist/extensions/pi-subagents/src/runs/shared/long-running-guard.ts +2 -1
  257. package/dist/extensions/pi-subagents/src/runs/shared/mcp-config-sources.ts +422 -0
  258. package/dist/extensions/pi-subagents/src/runs/shared/mcp-direct-tool-allowlist.ts +221 -158
  259. package/dist/extensions/pi-subagents/src/runs/shared/mcp-direct-tool-grant.ts +197 -0
  260. package/dist/extensions/pi-subagents/src/runs/shared/model-exclusions.ts +69 -7
  261. package/dist/extensions/pi-subagents/src/runs/shared/model-fallback.ts +47 -6
  262. package/dist/extensions/pi-subagents/src/runs/shared/mutation-evidence.ts +7 -2
  263. package/dist/extensions/pi-subagents/src/runs/shared/nested-events.ts +9 -3
  264. package/dist/extensions/pi-subagents/src/runs/shared/nested-render.ts +9 -5
  265. package/dist/extensions/pi-subagents/src/runs/shared/parallel-handoff.ts +419 -7
  266. package/dist/extensions/pi-subagents/src/runs/shared/parallel-utils.ts +15 -1
  267. package/dist/extensions/pi-subagents/src/runs/shared/pi-args.ts +81 -8
  268. package/dist/extensions/pi-subagents/src/runs/shared/process-signal.ts +13 -0
  269. package/dist/extensions/pi-subagents/src/runs/shared/run-history.ts +21 -1
  270. package/dist/extensions/pi-subagents/src/runs/shared/single-output.ts +44 -4
  271. package/dist/extensions/pi-subagents/src/runs/shared/subagent-prompt-runtime.ts +98 -13
  272. package/dist/extensions/pi-subagents/src/runs/shared/subagent-startup-retry.ts +1 -1
  273. package/dist/extensions/pi-subagents/src/runs/shared/worktree-cleanup-plan.ts +847 -0
  274. package/dist/extensions/pi-subagents/src/runs/shared/worktree.ts +18 -0
  275. package/dist/extensions/pi-subagents/src/shared/child-session-name.ts +46 -0
  276. package/dist/extensions/pi-subagents/src/shared/extension-context.ts +24 -0
  277. package/dist/extensions/pi-subagents/src/shared/fork-context.ts +21 -0
  278. package/dist/extensions/pi-subagents/src/shared/formatters.ts +18 -3
  279. package/dist/extensions/pi-subagents/src/shared/launch-contract.ts +5 -1
  280. package/dist/extensions/pi-subagents/src/shared/pruned-fork.ts +450 -0
  281. package/dist/extensions/pi-subagents/src/shared/session-file-trust.ts +19 -0
  282. package/dist/extensions/pi-subagents/src/shared/session-tokens.ts +14 -3
  283. package/dist/extensions/pi-subagents/src/shared/settings.ts +19 -105
  284. package/dist/extensions/pi-subagents/src/shared/shortcuts.ts +17 -0
  285. package/dist/extensions/pi-subagents/src/shared/types.ts +359 -40
  286. package/dist/extensions/pi-subagents/src/shared/utils.ts +41 -84
  287. package/dist/extensions/pi-subagents/src/shared/workflow-child-permit.ts +116 -0
  288. package/dist/extensions/pi-subagents/src/slash/delegation-adapters.ts +1 -9
  289. package/dist/extensions/pi-subagents/src/slash/delegation-request.ts +0 -4
  290. package/dist/extensions/pi-subagents/src/slash/slash-bridge.ts +1 -2
  291. package/dist/extensions/pi-subagents/src/slash/slash-commands.ts +378 -88
  292. package/dist/extensions/pi-subagents/src/slash/slash-live-state.ts +22 -11
  293. package/dist/extensions/pi-subagents/src/slash/subagents-admin.ts +3 -0
  294. package/dist/extensions/pi-subagents/src/tui/fleet-status.ts +147 -79
  295. package/dist/extensions/pi-subagents/src/tui/fleet-transcript.ts +11 -5
  296. package/dist/extensions/pi-subagents/src/tui/fleet.ts +39 -18
  297. package/dist/extensions/pi-subagents/src/tui/render.ts +369 -46
  298. package/dist/extensions/pi-subagents/src/watchdog/turn-delta.ts +1 -1
  299. package/dist/extensions/pi-subagents/src/workflows/chat-progress.ts +6 -3
  300. package/dist/extensions/pi-subagents/src/workflows/host-command.ts +230 -0
  301. package/dist/extensions/pi-subagents/src/workflows/scripted-workflow.ts +740 -58
  302. package/dist/extensions/pi-subagents/src/workflows/workflow-child-summary.ts +121 -0
  303. package/dist/extensions/pi-subagents/src/workflows/workflow-preflight.ts +270 -0
  304. package/dist/extensions/pi-subagents/src/workflows/workflow-receipt.ts +195 -6
  305. package/dist/extensions/pi-subagents/src/workflows/workflow-settlement.ts +246 -0
  306. package/dist/extensions/pi-subagents/test/e2e/real-session-subagent.test.ts +2 -2
  307. package/dist/extensions/pi-subagents/test/integration/async-execution.test.ts +1114 -260
  308. package/dist/extensions/pi-subagents/test/integration/async-job-tracker.test.ts +34 -0
  309. package/dist/extensions/pi-subagents/test/integration/async-status.test.ts +150 -3
  310. package/dist/extensions/pi-subagents/test/integration/claude-code-smoke.test.ts +52 -0
  311. package/dist/extensions/pi-subagents/test/integration/claude-code-writer-smoke.test.ts +55 -0
  312. package/dist/extensions/pi-subagents/test/integration/codex-exec-smoke.test.ts +53 -0
  313. package/dist/extensions/pi-subagents/test/integration/codex-exec-writer-smoke.test.ts +57 -0
  314. package/dist/extensions/pi-subagents/test/integration/cursor-agent-smoke.test.ts +59 -0
  315. package/dist/extensions/pi-subagents/test/integration/cursor-agent-writer-smoke.test.ts +62 -0
  316. package/dist/extensions/pi-subagents/test/integration/error-handling.test.ts +57 -1
  317. package/dist/extensions/pi-subagents/test/integration/external-cli-runner.test.ts +1 -0
  318. package/dist/extensions/pi-subagents/test/integration/fork-context-execution.test.ts +20 -0
  319. package/dist/extensions/pi-subagents/test/integration/intercom-result-delivery.test.ts +4 -3
  320. package/dist/extensions/pi-subagents/test/integration/orca-progress-tabs.test.ts +2 -2
  321. package/dist/extensions/pi-subagents/test/integration/render-fork-badge.test.ts +54 -4
  322. package/dist/extensions/pi-subagents/test/integration/render-widget.test.ts +482 -4
  323. package/dist/extensions/pi-subagents/test/integration/result-watcher.test.ts +157 -9
  324. package/dist/extensions/pi-subagents/test/integration/session-tokens.test.ts +5 -5
  325. package/dist/extensions/pi-subagents/test/integration/single-execution.test.ts +1508 -291
  326. package/dist/extensions/pi-subagents/test/integration/slash-commands.test.ts +423 -1
  327. package/dist/extensions/pi-subagents/test/integration/template-resolution.test.ts +8 -0
  328. package/dist/extensions/pi-subagents/test/support/cursor-smoke-inputs.ts +51 -0
  329. package/dist/extensions/pi-subagents/test/support/helpers.ts +2 -0
  330. package/dist/extensions/pi-subagents/test/support/mock-pi.ts +5 -5
  331. package/dist/extensions/pi-subagents/test/support/real-session-runner.ts +6 -6
  332. package/dist/extensions/pi-subagents/test/unit/abort-recovery.test.ts +152 -0
  333. package/dist/extensions/pi-subagents/test/unit/active-async-capacity.test.ts +48 -0
  334. package/dist/extensions/pi-subagents/test/unit/agent-frontmatter.test.ts +184 -6
  335. package/dist/extensions/pi-subagents/test/unit/agent-management.test.ts +66 -6
  336. package/dist/extensions/pi-subagents/test/unit/agent-overrides.test.ts +77 -0
  337. package/dist/extensions/pi-subagents/test/unit/agent-refinements.test.ts +18 -0
  338. package/dist/extensions/pi-subagents/test/unit/anti-slop-oxlint.test.ts +86 -0
  339. package/dist/extensions/pi-subagents/test/unit/artifacts.test.ts +1 -1
  340. package/dist/extensions/pi-subagents/test/unit/async-execution.test.ts +101 -1
  341. package/dist/extensions/pi-subagents/test/unit/async-interrupt-action.test.ts +121 -0
  342. package/dist/extensions/pi-subagents/test/unit/async-recovery-descriptor.test.ts +28 -0
  343. package/dist/extensions/pi-subagents/test/unit/async-resume.test.ts +14 -7
  344. package/dist/extensions/pi-subagents/test/unit/async-retention.test.ts +53 -0
  345. package/dist/extensions/pi-subagents/test/unit/async-status-projection.test.ts +190 -0
  346. package/dist/extensions/pi-subagents/test/unit/async-status-snapshot.test.ts +42 -0
  347. package/dist/extensions/pi-subagents/test/unit/auto-drain.test.ts +8 -4
  348. package/dist/extensions/pi-subagents/test/unit/background-work.test.ts +23 -0
  349. package/dist/extensions/pi-subagents/test/unit/chain-root-attachment.test.ts +78 -1
  350. package/dist/extensions/pi-subagents/test/unit/child-launch-plan.test.ts +94 -0
  351. package/dist/extensions/pi-subagents/test/unit/child-protocol.test.ts +21 -0
  352. package/dist/extensions/pi-subagents/test/unit/child-session-name.test.ts +50 -0
  353. package/dist/extensions/pi-subagents/test/unit/claude-code-adapter.test.ts +245 -0
  354. package/dist/extensions/pi-subagents/test/unit/codex-exec-adapter.test.ts +192 -0
  355. package/dist/extensions/pi-subagents/test/unit/compaction-resume.test.ts +27 -0
  356. package/dist/extensions/pi-subagents/test/unit/completion-evidence.test.ts +150 -0
  357. package/dist/extensions/pi-subagents/test/unit/completion-guard.test.ts +21 -0
  358. package/dist/extensions/pi-subagents/test/unit/cursor-agent-adapter.test.ts +259 -0
  359. package/dist/extensions/pi-subagents/test/unit/cursor-smoke-inputs.test.ts +77 -0
  360. package/dist/extensions/pi-subagents/test/unit/delegation-api.test.ts +1 -4
  361. package/dist/extensions/pi-subagents/test/unit/doctor.test.ts +1 -1
  362. package/dist/extensions/pi-subagents/test/unit/extension-context.test.ts +27 -0
  363. package/dist/extensions/pi-subagents/test/unit/external-cli-runner.test.ts +223 -0
  364. package/dist/extensions/pi-subagents/test/unit/fast-mode-extension.test.ts +27 -0
  365. package/dist/extensions/pi-subagents/test/unit/fleet-status.test.ts +208 -2
  366. package/dist/extensions/pi-subagents/test/unit/fleet-transcript.test.ts +29 -1
  367. package/dist/extensions/pi-subagents/test/unit/fleet.test.ts +110 -1
  368. package/dist/extensions/pi-subagents/test/unit/foreground-control.test.ts +10 -0
  369. package/dist/extensions/pi-subagents/test/unit/formatters.test.ts +14 -0
  370. package/dist/extensions/pi-subagents/test/unit/herdr-inspector.test.ts +18 -9
  371. package/dist/extensions/pi-subagents/test/unit/herdr-session-roots-codec.test.ts +63 -0
  372. package/dist/extensions/pi-subagents/test/unit/herdr-shell-command.test.ts +2 -2
  373. package/dist/extensions/pi-subagents/test/unit/herdr-status-bridge.test.ts +132 -0
  374. package/dist/extensions/pi-subagents/test/unit/host-command.test.ts +156 -0
  375. package/dist/extensions/pi-subagents/test/unit/host-step-status.test.ts +98 -0
  376. package/dist/extensions/pi-subagents/test/unit/index-child-registration.test.ts +178 -26
  377. package/dist/extensions/pi-subagents/test/unit/index-segment.test.ts +2 -1
  378. package/dist/extensions/pi-subagents/test/unit/inspect-rpc.test.ts +21 -0
  379. package/dist/extensions/pi-subagents/test/unit/mcp-direct-tool-grant.test.ts +138 -0
  380. package/dist/extensions/pi-subagents/test/unit/mission-lifecycle.test.ts +1 -1
  381. package/dist/extensions/pi-subagents/test/unit/model-exclusions.test.ts +91 -0
  382. package/dist/extensions/pi-subagents/test/unit/model-fallback.test.ts +72 -7
  383. package/dist/extensions/pi-subagents/test/unit/mutation-evidence.test.ts +21 -0
  384. package/dist/extensions/pi-subagents/test/unit/nested-control.test.ts +1 -0
  385. package/dist/extensions/pi-subagents/test/unit/nested-events.test.ts +20 -2
  386. package/dist/extensions/pi-subagents/test/unit/notify.test.ts +129 -1
  387. package/dist/extensions/pi-subagents/test/unit/orca-progress-tabs.test.ts +2 -2
  388. package/dist/extensions/pi-subagents/test/unit/package-manifest.test.ts +4 -0
  389. package/dist/extensions/pi-subagents/test/unit/parallel-handoff.test.ts +358 -0
  390. package/dist/extensions/pi-subagents/test/unit/pi-args.test.ts +574 -21
  391. package/dist/extensions/pi-subagents/test/unit/pi-coding-agent-dir.test.ts +109 -1
  392. package/dist/extensions/pi-subagents/test/unit/preflight.test.ts +71 -1
  393. package/dist/extensions/pi-subagents/test/unit/profiles.test.ts +7 -7
  394. package/dist/extensions/pi-subagents/test/unit/pruned-fork.test.ts +214 -0
  395. package/dist/extensions/pi-subagents/test/unit/public-execution.test.ts +29 -8
  396. package/dist/extensions/pi-subagents/test/unit/render-helpers.test.ts +53 -0
  397. package/dist/extensions/pi-subagents/test/unit/result-delivery-ownership.test.ts +34 -0
  398. package/dist/extensions/pi-subagents/test/unit/result-files.test.ts +101 -3
  399. package/dist/extensions/pi-subagents/test/unit/retained-children.test.ts +1 -0
  400. package/dist/extensions/pi-subagents/test/unit/rpc.test.ts +10 -8
  401. package/dist/extensions/pi-subagents/test/unit/run-fanout-budget.test.ts +71 -1
  402. package/dist/extensions/pi-subagents/test/unit/run-status.test.ts +41 -7
  403. package/dist/extensions/pi-subagents/test/unit/runtime-agent-registration.test.ts +136 -2
  404. package/dist/extensions/pi-subagents/test/unit/scheduled-runs.test.ts +137 -0
  405. package/dist/extensions/pi-subagents/test/unit/schemas.test.ts +33 -12
  406. package/dist/extensions/pi-subagents/test/unit/scripted-workflow.test.ts +504 -1
  407. package/dist/extensions/pi-subagents/test/unit/single-output.test.ts +8 -0
  408. package/dist/extensions/pi-subagents/test/unit/skills-fallback.test.ts +1 -1
  409. package/dist/extensions/pi-subagents/test/unit/stale-run-reconciler.test.ts +12 -1
  410. package/dist/extensions/pi-subagents/test/unit/steering-action.test.ts +3 -5
  411. package/dist/extensions/pi-subagents/test/unit/steering.test.ts +5 -6
  412. package/dist/extensions/pi-subagents/test/unit/subagent-action-recovery.test.ts +0 -8
  413. package/dist/extensions/pi-subagents/test/unit/subagent-guide.test.ts +5 -0
  414. package/dist/extensions/pi-subagents/test/unit/subagent-prompt-runtime.test.ts +157 -2
  415. package/dist/extensions/pi-subagents/test/unit/subagent-startup-retry.test.ts +1 -1
  416. package/dist/extensions/pi-subagents/test/unit/subagent-wait.test.ts +63 -4
  417. package/dist/extensions/pi-subagents/test/unit/tool-description.test.ts +27 -23
  418. package/dist/extensions/pi-subagents/test/unit/wait-completions.test.ts +44 -0
  419. package/dist/extensions/pi-subagents/test/unit/wait-subscriptions.test.ts +1 -0
  420. package/dist/extensions/pi-subagents/test/unit/widget-nested-render.test.ts +7 -5
  421. package/dist/extensions/pi-subagents/test/unit/workflow-chat-progress.test.ts +106 -0
  422. package/dist/extensions/pi-subagents/test/unit/workflow-detach-reconcile.test.ts +484 -9
  423. package/dist/extensions/pi-subagents/test/unit/workflow-launch-params.test.ts +38 -4
  424. package/dist/extensions/pi-subagents/test/unit/workflow-preflight.test.ts +155 -0
  425. package/dist/extensions/pi-subagents/test/unit/workflow-receipt.test.ts +318 -0
  426. package/dist/extensions/pi-subagents/test/unit/workflow-resume-hint.test.ts +365 -0
  427. package/dist/extensions/pi-subagents/test/unit/worktree-cleanup-plan.test.ts +349 -0
  428. package/dist/extensions/pi-subagents/test/unit/worktree.test.ts +21 -0
  429. package/dist/extensions/pi-subagents/test/unit/writer-budget-guidance.test.ts +2 -7
  430. package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/effect/index.ts +13 -0
  431. package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/effect/rules/no-service-constructor-imports.ts +52 -0
  432. package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/index.ts +41 -0
  433. package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/rules/no-chained-type-assertions.ts +77 -0
  434. package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/rules/no-conditional-empty-object-spread.ts +49 -0
  435. package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/rules/no-known-value-widening.ts +247 -0
  436. package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/rules/no-module-mocking.ts +91 -0
  437. package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/rules/no-object-parameters.ts +126 -0
  438. package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/rules/no-reflect-apply.ts +28 -0
  439. package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/rules/no-reflect-get.ts +28 -0
  440. package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/rules/no-runtime-typeof.ts +67 -0
  441. package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/rules/no-shape-in-symbol-names.ts +39 -0
  442. package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/rules/no-unknown-parameters.ts +83 -0
  443. package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/rules/no-unknown-returns.ts +115 -0
  444. package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/rules/no-unknown-type-aliases.ts +72 -0
  445. package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/rules/no-unsafe-dictionary-type.ts +134 -0
  446. package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/rules/no-widen-then-assert.ts +366 -0
  447. package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/rules/require-safety-comment-for-type-assertion.ts +62 -0
  448. package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/shared/dictionary-types.ts +502 -0
  449. package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/shared/lexical-type-parameters.ts +60 -0
  450. package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/shared/reflect-method.ts +35 -0
  451. package/dist/extensions/pi-tool-display/tests/capabilities.test.ts +1 -1
  452. package/dist/extensions/pi-tool-display/tests/settings-inspector.test.ts +2 -2
  453. package/dist/extensions/pi-tool-display/tests/tool-overrides-mcp.test.ts +1 -1
  454. package/dist/extensions/pi-zentui/extensions/zentui/format.ts +2 -1
  455. package/dist/extensions/pi-zentui/test/format.test.ts +15 -1
  456. package/dist/modes/interactive/components/footer.js +2 -2
  457. package/dist/modes/interactive/interactive-mode.js +11 -11
  458. package/dist/skills/workflow/SKILL.md +65 -50
  459. package/package.json +112 -110
  460. package/dist/extensions/enable-readonly-tools.test.ts +0 -63
  461. package/dist/extensions/enable-readonly-tools.ts +0 -24
  462. package/dist/extensions/model-prompt-injector/config.json +0 -16
  463. package/dist/extensions/model-prompt-injector/index.test.ts +0 -102
  464. package/dist/extensions/model-prompt-injector/index.ts +0 -148
  465. package/dist/extensions/pi-subagents/src/runs/shared/turn-budget.ts +0 -98
  466. package/dist/extensions/pi-subagents/test/unit/turn-budget.test.ts +0 -250
@@ -0,0 +1,1002 @@
1
+ import { DatabaseManager } from './db.js';
2
+ import {
3
+ buildFallbackFts5Query,
4
+ buildNaturalLanguageFallbackQuery,
5
+ isFts5QueryError,
6
+ normalizeFts5Query,
7
+ normalizeNaturalLanguageFts5Query,
8
+ } from './fts-query.js';
9
+ import { normalizeMemoryLookupText } from './memory-lookup.js';
10
+ import type { MemoryCategory } from '../types.js';
11
+
12
+ const MEMORY_SELECT_COLUMNS = `
13
+ id,
14
+ project,
15
+ target,
16
+ category,
17
+ content,
18
+ failure_reason,
19
+ tool_state,
20
+ corrected_to,
21
+ created,
22
+ last_referenced
23
+ `;
24
+
25
+ const FAILURE_CATEGORY_SET = new Set<MemoryCategory>([
26
+ 'failure',
27
+ 'correction',
28
+ 'insight',
29
+ 'preference',
30
+ 'convention',
31
+ 'tool-quirk',
32
+ ]);
33
+
34
+ /**
35
+ * A memory entry stored in SQLite.
36
+ */
37
+ export interface SqliteMemoryEntry {
38
+ id: number;
39
+ project: string | null;
40
+ target: 'memory' | 'user' | 'failure';
41
+ category: MemoryCategory | null;
42
+ content: string;
43
+ failureReason: string | null;
44
+ toolState: string | null;
45
+ correctedTo: string | null;
46
+ created: string;
47
+ lastReferenced: string;
48
+ }
49
+
50
+ export interface SqliteMemorySyncInput {
51
+ content: string;
52
+ target: 'memory' | 'user' | 'failure';
53
+ project?: string | null;
54
+ category?: MemoryCategory | null;
55
+ failureReason?: string | null;
56
+ toolState?: string | null;
57
+ correctedTo?: string | null;
58
+ created?: string | null;
59
+ lastReferenced?: string | null;
60
+ }
61
+
62
+ export interface SqliteMemorySyncResult {
63
+ action: 'inserted' | 'existing';
64
+ entry: SqliteMemoryEntry;
65
+ }
66
+
67
+ export interface SqliteMemoryUpdateResult {
68
+ matched: number;
69
+ updated: number;
70
+ entries: SqliteMemoryEntry[];
71
+ }
72
+
73
+ export interface SqliteMemoryRemoveResult {
74
+ matched: number;
75
+ removed: number;
76
+ }
77
+
78
+ export interface SqliteMemoryRemoveOptions {
79
+ target: 'memory' | 'user' | 'failure';
80
+ project?: string | null;
81
+ }
82
+
83
+ export interface MarkdownMemoryReconcileResult {
84
+ inserted: number;
85
+ existing: number;
86
+ removed: number;
87
+ }
88
+
89
+ export interface ParsedMarkdownMemoryEntry extends SqliteMemorySyncInput {}
90
+
91
+ function today(): string {
92
+ return new Date().toISOString().split('T')[0];
93
+ }
94
+
95
+ function normalizeNullable(value?: string | null): string | null {
96
+ if (value == null) return null;
97
+ const trimmed = value.trim();
98
+ return trimmed.length > 0 ? trimmed : null;
99
+ }
100
+
101
+ function normalizeCategory(value?: MemoryCategory | null): MemoryCategory | null {
102
+ return value ?? null;
103
+ }
104
+
105
+ function mapRow(row: {
106
+ id: number;
107
+ project: string | null;
108
+ target: string;
109
+ category: string | null;
110
+ content: string;
111
+ failure_reason: string | null;
112
+ tool_state: string | null;
113
+ corrected_to: string | null;
114
+ created: string;
115
+ last_referenced: string;
116
+ }): SqliteMemoryEntry {
117
+ return {
118
+ id: row.id,
119
+ project: row.project,
120
+ target: row.target as 'memory' | 'user' | 'failure',
121
+ category: row.category as MemoryCategory | null,
122
+ content: row.content,
123
+ failureReason: row.failure_reason,
124
+ toolState: row.tool_state,
125
+ correctedTo: row.corrected_to,
126
+ created: row.created,
127
+ lastReferenced: row.last_referenced,
128
+ };
129
+ }
130
+
131
+ function buildScopeConditions(params: unknown[], target?: string, project?: string | null, category?: MemoryCategory | null): string[] {
132
+ const conditions: string[] = [];
133
+
134
+ if (target) {
135
+ conditions.push('target = ?');
136
+ params.push(target);
137
+ }
138
+
139
+ if (project !== undefined) {
140
+ if (project === null) {
141
+ conditions.push('project IS NULL');
142
+ } else {
143
+ conditions.push('project = ?');
144
+ params.push(project);
145
+ }
146
+ }
147
+
148
+ if (category !== undefined) {
149
+ if (category === null) {
150
+ conditions.push('category IS NULL');
151
+ } else {
152
+ conditions.push('category = ?');
153
+ params.push(category);
154
+ }
155
+ }
156
+
157
+ return conditions;
158
+ }
159
+
160
+ function getMemoryById(dbManager: DatabaseManager, id: number): SqliteMemoryEntry | null {
161
+ const db = dbManager.getDb();
162
+ const row = db.prepare(`
163
+ SELECT ${MEMORY_SELECT_COLUMNS}
164
+ FROM memories
165
+ WHERE id = ?
166
+ `).get(id) as {
167
+ id: number;
168
+ project: string | null;
169
+ target: string;
170
+ category: string | null;
171
+ content: string;
172
+ failure_reason: string | null;
173
+ tool_state: string | null;
174
+ corrected_to: string | null;
175
+ created: string;
176
+ last_referenced: string;
177
+ } | undefined;
178
+
179
+ return row ? mapRow(row) : null;
180
+ }
181
+
182
+ function minDate(a: string, b: string): string {
183
+ return a <= b ? a : b;
184
+ }
185
+
186
+ function maxDate(a: string, b: string): string {
187
+ return a >= b ? a : b;
188
+ }
189
+
190
+ function escapeLikePattern(text: string): string {
191
+ return text.replace(/[\\%_]/g, '\\$&');
192
+ }
193
+
194
+ function isShortCjkLiteralQuery(query: string): boolean {
195
+ const trimmed = query.trim();
196
+ return [...trimmed].length <= 2
197
+ && /^[\p{Script=Han}\p{Script=Hiragana}\p{Script=Katakana}\p{Script=Hangul}]+$/u.test(trimmed);
198
+ }
199
+
200
+ function parseMetadataComment(raw: string): { text: string; created: string; lastReferenced: string; project: string | null } {
201
+ const match = raw.match(/^(.*?)\s*<!--\s*created=([^,]+),\s*last=([^,>]+)(?:,\s*project64=([A-Za-z0-9_-]+))?\s*-->\s*$/);
202
+ if (match) {
203
+ let project: string | null = null;
204
+ if (match[4]) {
205
+ try { project = Buffer.from(match[4], 'base64url').toString('utf-8').trim() || null; } catch {}
206
+ }
207
+ return {
208
+ text: match[1].trim(),
209
+ created: match[2].trim(),
210
+ lastReferenced: match[3].trim(),
211
+ project,
212
+ };
213
+ }
214
+
215
+ const fallback = today();
216
+ return {
217
+ text: raw.trim(),
218
+ created: fallback,
219
+ lastReferenced: fallback,
220
+ project: null,
221
+ };
222
+ }
223
+
224
+ /**
225
+ * Add a memory entry to the SQLite store.
226
+ */
227
+ export function addMemory(
228
+ dbManager: DatabaseManager,
229
+ content: string,
230
+ target: 'memory' | 'user' | 'failure' = 'memory',
231
+ project: string | null = null,
232
+ category: MemoryCategory | null = null,
233
+ failureReason: string | null = null,
234
+ toolState: string | null = null,
235
+ correctedTo: string | null = null,
236
+ created = today(),
237
+ lastReferenced = created
238
+ ): SqliteMemoryEntry {
239
+ const db = dbManager.getDb();
240
+
241
+ const result = db.prepare(`
242
+ INSERT INTO memories (project, target, category, content, failure_reason, tool_state, corrected_to, created, last_referenced)
243
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
244
+ `).run(project, target, category, content, failureReason, toolState, correctedTo, created, lastReferenced);
245
+
246
+ return {
247
+ id: Number(result.lastInsertRowid),
248
+ project,
249
+ target,
250
+ category,
251
+ content,
252
+ failureReason,
253
+ toolState,
254
+ correctedTo,
255
+ created,
256
+ lastReferenced,
257
+ };
258
+ }
259
+
260
+ /**
261
+ * Build the visible failure-memory text stored in Markdown.
262
+ */
263
+ export function formatFailureMemoryContent(
264
+ content: string,
265
+ options: {
266
+ category: MemoryCategory;
267
+ failureReason?: string | null;
268
+ toolState?: string | null;
269
+ correctedTo?: string | null;
270
+ project?: string | null;
271
+ }
272
+ ): string {
273
+ const categoryTag = `[${options.category}]`;
274
+ const parts = [`${categoryTag} ${content.trim()}`.trim()];
275
+ if (options.failureReason) parts.push(`Failed: ${options.failureReason}`);
276
+ if (options.toolState) parts.push(`Tool state: ${options.toolState}`);
277
+ if (options.correctedTo) parts.push(`Corrected to: ${options.correctedTo}`);
278
+ return parts.join(' — ');
279
+ }
280
+
281
+ /**
282
+ * Parse a Markdown memory entry into SQLite sync fields.
283
+ * Best-effort only: if failure metadata cannot be fully reconstructed,
284
+ * content is still imported and available for search.
285
+ */
286
+ export function parseMarkdownMemoryEntry(
287
+ rawEntry: string,
288
+ target: 'memory' | 'user' | 'failure',
289
+ project: string | null = null,
290
+ ): ParsedMarkdownMemoryEntry {
291
+ const metadata = parseMetadataComment(rawEntry);
292
+ const { text, created, lastReferenced } = metadata;
293
+ const parsedProject = normalizeNullable(project);
294
+
295
+ if (target !== 'failure') {
296
+ return {
297
+ content: text,
298
+ target,
299
+ project: parsedProject,
300
+ created,
301
+ lastReferenced,
302
+ };
303
+ }
304
+
305
+ let category: MemoryCategory | null = null;
306
+ let failureReason: string | null = null;
307
+ let toolState: string | null = null;
308
+ let correctedTo: string | null = null;
309
+
310
+ const categoryMatch = text.match(/^\[([^\]]+)\]\s+/);
311
+ if (categoryMatch && FAILURE_CATEGORY_SET.has(categoryMatch[1] as MemoryCategory)) {
312
+ category = categoryMatch[1] as MemoryCategory;
313
+ }
314
+
315
+ const segments = text.split(' — ');
316
+ for (const segment of segments.slice(1)) {
317
+ if (segment.startsWith('Failed: ') && !failureReason) {
318
+ failureReason = segment.slice('Failed: '.length).trim() || null;
319
+ continue;
320
+ }
321
+ if (segment.startsWith('Tool state: ') && !toolState) {
322
+ toolState = segment.slice('Tool state: '.length).trim() || null;
323
+ continue;
324
+ }
325
+ if (segment.startsWith('Corrected to: ') && !correctedTo) {
326
+ correctedTo = segment.slice('Corrected to: '.length).trim() || null;
327
+ }
328
+ }
329
+
330
+ return {
331
+ content: text,
332
+ target: 'failure',
333
+ project: parsedProject,
334
+ category,
335
+ failureReason,
336
+ toolState,
337
+ correctedTo,
338
+ created,
339
+ lastReferenced,
340
+ };
341
+ }
342
+
343
+ /**
344
+ * Idempotently sync a Markdown-backed memory entry into SQLite.
345
+ * Duplicate identity is exact: project + target + category + content.
346
+ */
347
+ export function syncMemoryEntry(
348
+ dbManager: DatabaseManager,
349
+ input: SqliteMemorySyncInput,
350
+ ): SqliteMemorySyncResult {
351
+ const db = dbManager.getDb();
352
+ const content = input.content.trim();
353
+ const project = normalizeNullable(input.project);
354
+ const category = normalizeCategory(input.category);
355
+ const failureReason = normalizeNullable(input.failureReason);
356
+ const toolState = normalizeNullable(input.toolState);
357
+ const correctedTo = normalizeNullable(input.correctedTo);
358
+ const created = input.created?.trim() || today();
359
+ const lastReferenced = input.lastReferenced?.trim() || created;
360
+
361
+ const params: unknown[] = [];
362
+ const conditions = buildScopeConditions(params, input.target, project, category);
363
+ conditions.push('content = ?');
364
+ params.push(content);
365
+
366
+ const existing = db.prepare(`
367
+ SELECT ${MEMORY_SELECT_COLUMNS}
368
+ FROM memories
369
+ WHERE ${conditions.join(' AND ')}
370
+ ORDER BY id ASC
371
+ LIMIT 1
372
+ `).get(...params) as {
373
+ id: number;
374
+ project: string | null;
375
+ target: string;
376
+ category: string | null;
377
+ content: string;
378
+ failure_reason: string | null;
379
+ tool_state: string | null;
380
+ corrected_to: string | null;
381
+ created: string;
382
+ last_referenced: string;
383
+ } | undefined;
384
+
385
+ if (!existing) {
386
+ return {
387
+ action: 'inserted',
388
+ entry: addMemory(
389
+ dbManager,
390
+ content,
391
+ input.target,
392
+ project,
393
+ category,
394
+ failureReason,
395
+ toolState,
396
+ correctedTo,
397
+ created,
398
+ lastReferenced,
399
+ ),
400
+ };
401
+ }
402
+
403
+ const updatedCreated = minDate(existing.created, created);
404
+ const updatedLastReferenced = maxDate(existing.last_referenced, lastReferenced);
405
+ const updatedCategory = (existing.category as MemoryCategory | null) ?? category;
406
+ const updatedFailureReason = existing.failure_reason ?? failureReason;
407
+ const updatedToolState = existing.tool_state ?? toolState;
408
+ const updatedCorrectedTo = existing.corrected_to ?? correctedTo;
409
+
410
+ db.prepare(`
411
+ UPDATE memories
412
+ SET category = ?, failure_reason = ?, tool_state = ?, corrected_to = ?, created = ?, last_referenced = ?
413
+ WHERE id = ?
414
+ `).run(
415
+ updatedCategory,
416
+ updatedFailureReason,
417
+ updatedToolState,
418
+ updatedCorrectedTo,
419
+ updatedCreated,
420
+ updatedLastReferenced,
421
+ existing.id,
422
+ );
423
+
424
+ return {
425
+ action: 'existing',
426
+ entry: getMemoryById(dbManager, existing.id)!,
427
+ };
428
+ }
429
+
430
+ /**
431
+ * Make one exact Markdown target/project scope authoritative in SQLite.
432
+ * Upserts and orphan deletion are committed together when transactions are
433
+ * supported by the active SQLite driver.
434
+ */
435
+ export function reconcileMarkdownMemoryScope(
436
+ dbManager: DatabaseManager,
437
+ rawEntries: string[],
438
+ target: 'memory' | 'user' | 'failure',
439
+ project: string | null = null,
440
+ ): MarkdownMemoryReconcileResult {
441
+ const db = dbManager.getDb();
442
+ const normalizedProject = normalizeNullable(project);
443
+
444
+ const reconcile = (): MarkdownMemoryReconcileResult => {
445
+ let inserted = 0;
446
+ let existing = 0;
447
+ const desiredIdentities = new Set<string>();
448
+
449
+ for (const rawEntry of rawEntries) {
450
+ const parsed = parseMarkdownMemoryEntry(rawEntry, target, normalizedProject);
451
+ desiredIdentities.add(JSON.stringify([
452
+ normalizeCategory(parsed.category),
453
+ parsed.content.trim(),
454
+ ]));
455
+ const result = syncMemoryEntry(dbManager, parsed);
456
+ if (result.action === 'inserted') inserted++;
457
+ else existing++;
458
+ }
459
+
460
+ const params: unknown[] = [];
461
+ const conditions = buildScopeConditions(params, target, normalizedProject);
462
+ const scopedRows = db.prepare(`
463
+ SELECT id, content, category
464
+ FROM memories
465
+ WHERE ${conditions.join(' AND ')}
466
+ ORDER BY id ASC
467
+ `).all(...params) as Array<{ id: number; content: string; category: MemoryCategory | null }>;
468
+ const retainedIdentities = new Set<string>();
469
+ const orphanIds: number[] = [];
470
+ for (const row of scopedRows) {
471
+ const identity = JSON.stringify([normalizeCategory(row.category), row.content.trim()]);
472
+ if (!desiredIdentities.has(identity) || retainedIdentities.has(identity)) {
473
+ orphanIds.push(row.id);
474
+ } else {
475
+ retainedIdentities.add(identity);
476
+ }
477
+ }
478
+
479
+ let removed = 0;
480
+ if (orphanIds.length > 0) {
481
+ const placeholders = orphanIds.map(() => '?').join(', ');
482
+ removed = db.prepare(`DELETE FROM memories WHERE id IN (${placeholders})`).run(...orphanIds).changes;
483
+ }
484
+
485
+ return { inserted, existing, removed };
486
+ };
487
+
488
+ const transactional = db.transaction?.(reconcile);
489
+ return transactional ? transactional() : reconcile();
490
+ }
491
+
492
+ function failureProject(rawEntry: string): string | null {
493
+ return parseMetadataComment(rawEntry).project;
494
+ }
495
+
496
+ export function reconcileMarkdownFailureScopes(
497
+ dbManager: DatabaseManager,
498
+ rawEntries: string[],
499
+ ): MarkdownMemoryReconcileResult {
500
+ const entriesByProject = new Map<string | null, string[]>();
501
+ for (const rawEntry of rawEntries) {
502
+ const project = failureProject(rawEntry);
503
+ const entries = entriesByProject.get(project) ?? [];
504
+ entries.push(rawEntry);
505
+ entriesByProject.set(project, entries);
506
+ }
507
+
508
+ const mirroredProjects = dbManager.getDb().prepare(`
509
+ SELECT DISTINCT project
510
+ FROM memories
511
+ WHERE target = 'failure'
512
+ `).all() as Array<{ project: string | null }>;
513
+ const projects = new Set<string | null>([
514
+ null,
515
+ ...entriesByProject.keys(),
516
+ ...mirroredProjects.map(({ project }) => normalizeNullable(project)),
517
+ ]);
518
+ const total: MarkdownMemoryReconcileResult = { inserted: 0, existing: 0, removed: 0 };
519
+
520
+ for (const project of projects) {
521
+ const result = reconcileMarkdownMemoryScope(
522
+ dbManager,
523
+ entriesByProject.get(project) ?? [],
524
+ 'failure',
525
+ project,
526
+ );
527
+ total.inserted += result.inserted;
528
+ total.existing += result.existing;
529
+ total.removed += result.removed;
530
+ }
531
+
532
+ return total;
533
+ }
534
+
535
+ /**
536
+ * Best-effort substring replacement for SQLite-backed memory sync.
537
+ * Updates all matches in the scoped slice to recover from prior duplicate rows.
538
+ */
539
+ export function replaceSyncedMemories(
540
+ dbManager: DatabaseManager,
541
+ oldText: string,
542
+ updates: {
543
+ content: string;
544
+ target: 'memory' | 'user' | 'failure';
545
+ project?: string | null;
546
+ category?: MemoryCategory | null;
547
+ failureReason?: string | null;
548
+ toolState?: string | null;
549
+ correctedTo?: string | null;
550
+ lastReferenced?: string | null;
551
+ },
552
+ ): SqliteMemoryUpdateResult {
553
+ const db = dbManager.getDb();
554
+ const normalizedOldText = normalizeMemoryLookupText(oldText);
555
+ if (!normalizedOldText) return { matched: 0, updated: 0, entries: [] };
556
+ const params: unknown[] = [];
557
+ const conditions = buildScopeConditions(params, updates.target, updates.project ?? undefined);
558
+ conditions.push(`content LIKE ? ESCAPE '\\'`);
559
+ params.push(`%${escapeLikePattern(normalizedOldText)}%`);
560
+
561
+ const rows = db.prepare(`
562
+ SELECT ${MEMORY_SELECT_COLUMNS}
563
+ FROM memories
564
+ WHERE ${conditions.join(' AND ')}
565
+ ORDER BY id ASC
566
+ `).all(...params) as Array<{
567
+ id: number;
568
+ project: string | null;
569
+ target: string;
570
+ category: string | null;
571
+ content: string;
572
+ failure_reason: string | null;
573
+ tool_state: string | null;
574
+ corrected_to: string | null;
575
+ created: string;
576
+ last_referenced: string;
577
+ }>;
578
+
579
+ if (rows.length === 0) {
580
+ return { matched: 0, updated: 0, entries: [] };
581
+ }
582
+
583
+ const nextLastReferenced = updates.lastReferenced?.trim() || today();
584
+
585
+ for (const row of rows) {
586
+ db.prepare(`
587
+ UPDATE memories
588
+ SET content = ?,
589
+ category = ?,
590
+ failure_reason = ?,
591
+ tool_state = ?,
592
+ corrected_to = ?,
593
+ last_referenced = ?
594
+ WHERE id = ?
595
+ `).run(
596
+ updates.content.trim(),
597
+ updates.category === undefined ? row.category : updates.category,
598
+ updates.failureReason === undefined ? row.failure_reason : normalizeNullable(updates.failureReason),
599
+ updates.toolState === undefined ? row.tool_state : normalizeNullable(updates.toolState),
600
+ updates.correctedTo === undefined ? row.corrected_to : normalizeNullable(updates.correctedTo),
601
+ nextLastReferenced,
602
+ row.id,
603
+ );
604
+ }
605
+
606
+ return {
607
+ matched: rows.length,
608
+ updated: rows.length,
609
+ entries: rows
610
+ .map((row) => getMemoryById(dbManager, row.id))
611
+ .filter((entry): entry is SqliteMemoryEntry => entry !== null),
612
+ };
613
+ }
614
+
615
+ /**
616
+ * Best-effort substring removal for SQLite-backed memory sync.
617
+ * Deletes all matches in the scoped slice to recover from prior duplicate rows.
618
+ */
619
+ export function removeSyncedMemories(
620
+ dbManager: DatabaseManager,
621
+ oldText: string,
622
+ options: SqliteMemoryRemoveOptions,
623
+ ): SqliteMemoryRemoveResult {
624
+ const db = dbManager.getDb();
625
+ const normalizedOldText = normalizeMemoryLookupText(oldText);
626
+ if (!normalizedOldText) return { matched: 0, removed: 0 };
627
+ const params: unknown[] = [];
628
+ const conditions = buildScopeConditions(params, options.target, options.project ?? undefined);
629
+ conditions.push(`content LIKE ? ESCAPE '\\'`);
630
+ params.push(`%${escapeLikePattern(normalizedOldText)}%`);
631
+
632
+ const matchingIds = db.prepare(`
633
+ SELECT id
634
+ FROM memories
635
+ WHERE ${conditions.join(' AND ')}
636
+ `).all(...params) as Array<{ id: number }>;
637
+
638
+ if (matchingIds.length === 0) {
639
+ return { matched: 0, removed: 0 };
640
+ }
641
+
642
+ const deleteParams = matchingIds.map((row) => row.id);
643
+ const placeholders = deleteParams.map(() => '?').join(', ');
644
+ const result = db.prepare(`DELETE FROM memories WHERE id IN (${placeholders})`).run(...deleteParams);
645
+
646
+ return {
647
+ matched: matchingIds.length,
648
+ removed: result.changes,
649
+ };
650
+ }
651
+
652
+ /**
653
+ * Exact removal for Markdown entries whose full content is known.
654
+ * Used for FIFO eviction cleanup, where substring matching could remove
655
+ * unrelated SQLite mirror rows that merely contain the evicted text.
656
+ */
657
+ export function removeExactSyncedMemories(
658
+ dbManager: DatabaseManager,
659
+ content: string,
660
+ options: SqliteMemoryRemoveOptions,
661
+ ): SqliteMemoryRemoveResult {
662
+ const db = dbManager.getDb();
663
+ const params: unknown[] = [];
664
+ const conditions = buildScopeConditions(params, options.target, options.project ?? undefined);
665
+ conditions.push('content = ?');
666
+ params.push(content.trim());
667
+
668
+ const matchingIds = db.prepare(`
669
+ SELECT id
670
+ FROM memories
671
+ WHERE ${conditions.join(' AND ')}
672
+ `).all(...params) as Array<{ id: number }>;
673
+
674
+ if (matchingIds.length === 0) {
675
+ return { matched: 0, removed: 0 };
676
+ }
677
+
678
+ const deleteParams = matchingIds.map((row) => row.id);
679
+ const placeholders = deleteParams.map(() => '?').join(', ');
680
+ const result = db.prepare(`DELETE FROM memories WHERE id IN (${placeholders})`).run(...deleteParams);
681
+
682
+ return {
683
+ matched: matchingIds.length,
684
+ removed: result.changes,
685
+ };
686
+ }
687
+
688
+ /**
689
+ * Search memories using FTS5.
690
+ */
691
+ export function searchMemories(
692
+ dbManager: DatabaseManager,
693
+ query: string,
694
+ options: { project?: string; target?: string; category?: MemoryCategory; limit?: number } = {}
695
+ ): SqliteMemoryEntry[] {
696
+ if (query.trim().length === 0) {
697
+ return [];
698
+ }
699
+
700
+ const db = dbManager.getDb();
701
+ const { project, target, category, limit = 10 } = options;
702
+
703
+ const conditions: string[] = [];
704
+ const params: unknown[] = [];
705
+
706
+ // FTS5 match via subquery with escaped query
707
+ const normalizedQuery = normalizeFts5Query(query);
708
+ if (normalizedQuery.length === 0) {
709
+ return [];
710
+ }
711
+
712
+ let ftsParseError = false;
713
+
714
+ const runSearch = (matchQuery: string): SqliteMemoryEntry[] => {
715
+ const conditions: string[] = [];
716
+ const params: unknown[] = [];
717
+
718
+ conditions.push('m.id IN (SELECT rowid FROM memory_fts WHERE memory_fts MATCH ?)');
719
+ params.push(matchQuery);
720
+
721
+ if (project !== undefined) {
722
+ if (project === null) {
723
+ conditions.push('m.project IS NULL');
724
+ } else {
725
+ conditions.push('m.project = ?');
726
+ params.push(project);
727
+ }
728
+ }
729
+
730
+ if (target) {
731
+ conditions.push('m.target = ?');
732
+ params.push(target);
733
+ }
734
+
735
+ if (category) {
736
+ conditions.push('m.category = ?');
737
+ params.push(category);
738
+ }
739
+
740
+ const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(' AND ')}` : '';
741
+
742
+ const sql = `
743
+ SELECT ${MEMORY_SELECT_COLUMNS}
744
+ FROM memories m
745
+ ${whereClause}
746
+ ORDER BY m.last_referenced DESC
747
+ LIMIT ?
748
+ `;
749
+
750
+ try {
751
+ const rows = db.prepare(sql).all(...params, limit) as Array<{
752
+ id: number;
753
+ project: string | null;
754
+ target: string;
755
+ category: string | null;
756
+ content: string;
757
+ failure_reason: string | null;
758
+ tool_state: string | null;
759
+ corrected_to: string | null;
760
+ created: string;
761
+ last_referenced: string;
762
+ }>;
763
+
764
+ return rows.map(mapRow);
765
+ } catch (err) {
766
+ if (isFts5QueryError(err)) {
767
+ ftsParseError = true;
768
+ return [];
769
+ }
770
+ throw err;
771
+ }
772
+ };
773
+
774
+ // FTS5's trigram tokenizer cannot match one- and two-character CJK terms.
775
+ // Use a scoped literal fallback only for those terms so FTS operators and
776
+ // normal tokenized searches retain their existing semantics.
777
+ const runShortCjkFallback = (): SqliteMemoryEntry[] => {
778
+ const conditions: string[] = ["m.content LIKE ? ESCAPE '\\'"];
779
+ const params: unknown[] = [`%${escapeLikePattern(query.trim())}%`];
780
+
781
+ if (project !== undefined) {
782
+ if (project === null) {
783
+ conditions.push('m.project IS NULL');
784
+ } else {
785
+ conditions.push('m.project = ?');
786
+ params.push(project);
787
+ }
788
+ }
789
+ if (target) {
790
+ conditions.push('m.target = ?');
791
+ params.push(target);
792
+ }
793
+ if (category) {
794
+ conditions.push('m.category = ?');
795
+ params.push(category);
796
+ }
797
+
798
+ const rows = db.prepare(`
799
+ SELECT ${MEMORY_SELECT_COLUMNS}
800
+ FROM memories m
801
+ WHERE ${conditions.join(' AND ')}
802
+ ORDER BY m.last_referenced DESC
803
+ LIMIT ?
804
+ `).all(...params, limit) as Array<{
805
+ id: number;
806
+ project: string | null;
807
+ target: string;
808
+ category: string | null;
809
+ content: string;
810
+ failure_reason: string | null;
811
+ tool_state: string | null;
812
+ corrected_to: string | null;
813
+ created: string;
814
+ last_referenced: string;
815
+ }>;
816
+ return rows.map(mapRow);
817
+ };
818
+
819
+ const exactResults = runSearch(normalizedQuery);
820
+ if (exactResults.length > 0) {
821
+ return exactResults;
822
+ }
823
+
824
+ if (isShortCjkLiteralQuery(query)) {
825
+ return runShortCjkFallback();
826
+ }
827
+
828
+ // A query with uppercase operator words (e.g. "DO NOT USE FIND /") passes
829
+ // through as raw FTS5 syntax; when that fails to parse, retry it as natural
830
+ // language instead of silently returning nothing. Valid operator queries
831
+ // that legitimately match nothing keep their exact semantics.
832
+ if (ftsParseError) {
833
+ const nlQuery = normalizeNaturalLanguageFts5Query(query);
834
+ if (nlQuery.length === 0 || nlQuery === normalizedQuery) {
835
+ return [];
836
+ }
837
+ const nlResults = runSearch(nlQuery);
838
+ if (nlResults.length > 0) {
839
+ return nlResults;
840
+ }
841
+ const nlFallback = buildNaturalLanguageFallbackQuery(query);
842
+ if (nlFallback && nlFallback !== nlQuery) {
843
+ return runSearch(nlFallback);
844
+ }
845
+ return nlResults;
846
+ }
847
+
848
+ const fallbackQuery = buildFallbackFts5Query(query);
849
+ if (!fallbackQuery || fallbackQuery === normalizedQuery) {
850
+ return exactResults;
851
+ }
852
+
853
+ return runSearch(fallbackQuery);
854
+ }
855
+
856
+ /**
857
+ * Get all memories, optionally filtered.
858
+ */
859
+ export function getMemories(
860
+ dbManager: DatabaseManager,
861
+ options: { project?: string | null; target?: string; category?: MemoryCategory } = {}
862
+ ): SqliteMemoryEntry[] {
863
+ const db = dbManager.getDb();
864
+ const { project, target, category } = options;
865
+
866
+ const conditions: string[] = [];
867
+ const params: unknown[] = [];
868
+
869
+ if (project !== undefined) {
870
+ if (project === null) {
871
+ conditions.push('project IS NULL');
872
+ } else {
873
+ conditions.push('project = ?');
874
+ params.push(project);
875
+ }
876
+ }
877
+
878
+ if (target) {
879
+ conditions.push('target = ?');
880
+ params.push(target);
881
+ }
882
+
883
+ if (category) {
884
+ conditions.push('category = ?');
885
+ params.push(category);
886
+ }
887
+
888
+ const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(' AND ')}` : '';
889
+
890
+ const rows = db.prepare(`
891
+ SELECT ${MEMORY_SELECT_COLUMNS}
892
+ FROM memories
893
+ ${whereClause}
894
+ ORDER BY last_referenced DESC
895
+ `).all(...params) as Array<{
896
+ id: number;
897
+ project: string | null;
898
+ target: string;
899
+ category: string | null;
900
+ content: string;
901
+ failure_reason: string | null;
902
+ tool_state: string | null;
903
+ corrected_to: string | null;
904
+ created: string;
905
+ last_referenced: string;
906
+ }>;
907
+
908
+ return rows.map(mapRow);
909
+ }
910
+
911
+ /**
912
+ * Remove a memory by ID.
913
+ */
914
+ export function removeMemory(dbManager: DatabaseManager, id: number): boolean {
915
+ const db = dbManager.getDb();
916
+ const result = db.prepare('DELETE FROM memories WHERE id = ?').run(id);
917
+ return result.changes > 0;
918
+ }
919
+
920
+ /**
921
+ * Get recent failure memories (last N days).
922
+ */
923
+ export function getRecentFailures(
924
+ dbManager: DatabaseManager,
925
+ maxAgeDays = 7,
926
+ project?: string | null
927
+ ): SqliteMemoryEntry[] {
928
+ const db = dbManager.getDb();
929
+ const cutoff = new Date();
930
+ cutoff.setDate(cutoff.getDate() - maxAgeDays);
931
+ const cutoffStr = cutoff.toISOString().split('T')[0];
932
+
933
+ const conditions: string[] = ['target = ?', 'created >= ?'];
934
+ const params: unknown[] = ['failure', cutoffStr];
935
+
936
+ if (project !== undefined) {
937
+ if (project === null) {
938
+ conditions.push('project IS NULL');
939
+ } else {
940
+ conditions.push('(project = ? OR project IS NULL)');
941
+ params.push(project);
942
+ }
943
+ }
944
+
945
+ const rows = db.prepare(`
946
+ SELECT ${MEMORY_SELECT_COLUMNS}
947
+ FROM memories
948
+ WHERE ${conditions.join(' AND ')}
949
+ ORDER BY created DESC
950
+ LIMIT 5
951
+ `).all(...params) as Array<{
952
+ id: number;
953
+ project: string | null;
954
+ target: string;
955
+ category: string | null;
956
+ content: string;
957
+ failure_reason: string | null;
958
+ tool_state: string | null;
959
+ corrected_to: string | null;
960
+ created: string;
961
+ last_referenced: string;
962
+ }>;
963
+
964
+ return rows.map(mapRow);
965
+ }
966
+
967
+ /**
968
+ * Update a memory's last_referenced date.
969
+ */
970
+ export function touchMemory(dbManager: DatabaseManager, id: number): void {
971
+ const db = dbManager.getDb();
972
+ db.prepare('UPDATE memories SET last_referenced = ? WHERE id = ?').run(today(), id);
973
+ }
974
+
975
+ /**
976
+ * Get memory statistics.
977
+ */
978
+ export function getMemoryStats(dbManager: DatabaseManager): {
979
+ total: number;
980
+ byProject: { project: string | null; count: number }[];
981
+ byTarget: { target: string; count: number }[];
982
+ } {
983
+ const db = dbManager.getDb();
984
+
985
+ const total = (db.prepare('SELECT COUNT(*) as count FROM memories').get() as { count: number }).count;
986
+
987
+ const byProject = db.prepare(`
988
+ SELECT project, COUNT(*) as count
989
+ FROM memories
990
+ GROUP BY project
991
+ ORDER BY count DESC
992
+ `).all() as { project: string | null; count: number }[];
993
+
994
+ const byTarget = db.prepare(`
995
+ SELECT target, COUNT(*) as count
996
+ FROM memories
997
+ GROUP BY target
998
+ ORDER BY count DESC
999
+ `).all() as { target: string; count: number }[];
1000
+
1001
+ return { total, byProject, byTarget };
1002
+ }