@selesai/code 0.13.15 → 0.13.18

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 (473) hide show
  1. package/CHANGELOG.md +37 -0
  2. package/README.md +2 -14
  3. package/dist/bun/cli.d.ts +3 -1
  4. package/dist/bun/cli.js +3 -9
  5. package/dist/bun/{register-bedrock.js → runtime-setup.js} +5 -0
  6. package/dist/bun/sandbox-env-setup.d.ts +1 -0
  7. package/dist/bun/sandbox-env-setup.js +3 -0
  8. package/dist/cli/auth-command.d.ts +23 -0
  9. package/dist/cli/auth-command.js +102 -0
  10. package/dist/cli/config-selector.js +2 -1
  11. package/dist/cli/setup.d.ts +1 -0
  12. package/dist/cli/setup.js +11 -0
  13. package/dist/cli.js +2 -14
  14. package/dist/config.d.ts +3 -2
  15. package/dist/config.js +9 -4
  16. package/dist/core/agent-session-runtime.js +15 -7
  17. package/dist/core/agent-session.d.ts +2 -6
  18. package/dist/core/agent-session.js +15 -4
  19. package/dist/core/compaction/branch-summarization.d.ts +1 -1
  20. package/dist/core/compaction/branch-summarization.js +2 -1
  21. package/dist/core/extensions/loader.js +3 -0
  22. package/dist/core/extensions/types.d.ts +16 -2
  23. package/dist/core/http-dispatcher.js +2 -0
  24. package/dist/core/keybindings.d.ts +5 -0
  25. package/dist/core/keybindings.js +4 -0
  26. package/dist/core/messages.d.ts +1 -1
  27. package/dist/core/model-config.d.ts +15 -0
  28. package/dist/core/model-config.js +3 -0
  29. package/dist/core/model-runtime.d.ts +1 -0
  30. package/dist/core/model-runtime.js +5 -2
  31. package/dist/core/provider-composer.js +8 -2
  32. package/dist/core/session-export.d.ts +3 -0
  33. package/dist/core/session-export.js +31 -0
  34. package/dist/core/session-manager.d.ts +3 -2
  35. package/dist/core/session-manager.js +40 -13
  36. package/dist/core/skills.d.ts +1 -1
  37. package/dist/core/skills.js +4 -2
  38. package/dist/core/system-prompt.js +10 -10
  39. package/dist/core/tools/bash.js +3 -117
  40. package/dist/core/tools/edit.d.ts +1 -12
  41. package/dist/core/tools/edit.js +5 -163
  42. package/dist/core/tools/find.js +4 -55
  43. package/dist/core/tools/grep.js +4 -60
  44. package/dist/core/tools/ls.js +4 -49
  45. package/dist/core/tools/read.d.ts +4 -0
  46. package/dist/core/tools/read.js +10 -122
  47. package/dist/core/tools/renderers/bash.d.ts +11 -0
  48. package/dist/core/tools/renderers/bash.js +129 -0
  49. package/dist/core/tools/renderers/edit.d.ts +22 -0
  50. package/dist/core/tools/renderers/edit.js +166 -0
  51. package/dist/core/tools/renderers/find.d.ts +9 -0
  52. package/dist/core/tools/renderers/find.js +63 -0
  53. package/dist/core/tools/renderers/grep.d.ts +9 -0
  54. package/dist/core/tools/renderers/grep.js +68 -0
  55. package/dist/core/tools/renderers/index.d.ts +27 -0
  56. package/dist/core/tools/renderers/index.js +46 -0
  57. package/dist/core/tools/renderers/ls.d.ts +9 -0
  58. package/dist/core/tools/renderers/ls.js +57 -0
  59. package/dist/core/tools/renderers/read.d.ts +10 -0
  60. package/dist/core/tools/renderers/read.js +128 -0
  61. package/dist/core/tools/renderers/write.d.ts +9 -0
  62. package/dist/core/tools/renderers/write.js +152 -0
  63. package/dist/core/tools/write.js +5 -146
  64. package/dist/extensions/capability-gateway/index.ts +12 -1
  65. package/dist/extensions/capability-gateway/integration.test.ts +23 -0
  66. package/dist/extensions/pi-hermes-memory/CHANGELOG.md +18 -0
  67. package/dist/extensions/pi-hermes-memory/README.md +16 -12
  68. package/dist/extensions/pi-hermes-memory/docs/ROADMAP.md +1 -1
  69. package/dist/extensions/pi-hermes-memory/package-lock.json +587 -62
  70. package/dist/extensions/pi-hermes-memory/package.json +4 -4
  71. package/dist/extensions/pi-hermes-memory/src/config.ts +32 -0
  72. package/dist/extensions/pi-hermes-memory/src/constants.ts +12 -2
  73. package/dist/extensions/pi-hermes-memory/src/handlers/background-review.ts +109 -51
  74. package/dist/extensions/pi-hermes-memory/src/handlers/index-sessions.ts +9 -3
  75. package/dist/extensions/pi-hermes-memory/src/handlers/review-memory-ops.ts +257 -98
  76. package/dist/extensions/pi-hermes-memory/src/handlers/session-backfill.ts +9 -2
  77. package/dist/extensions/pi-hermes-memory/src/handlers/sync-markdown-memories.ts +5 -0
  78. package/dist/extensions/pi-hermes-memory/src/index.ts +22 -2
  79. package/dist/extensions/pi-hermes-memory/src/paths.ts +1 -1
  80. package/dist/extensions/pi-hermes-memory/src/store/db.ts +6 -0
  81. package/dist/extensions/pi-hermes-memory/src/store/fts-query.ts +58 -1
  82. package/dist/extensions/pi-hermes-memory/src/store/memory-store.ts +60 -8
  83. package/dist/extensions/pi-hermes-memory/src/store/session-indexer.ts +169 -7
  84. package/dist/extensions/pi-hermes-memory/src/store/session-search.ts +7 -21
  85. package/dist/extensions/pi-hermes-memory/src/store/sqlite-memory-store.ts +134 -15
  86. package/dist/extensions/pi-hermes-memory/src/tools/memory-search-tool.ts +4 -2
  87. package/dist/extensions/pi-hermes-memory/src/tools/memory-tool.ts +24 -9
  88. package/dist/extensions/pi-hermes-memory/src/types.ts +11 -0
  89. package/dist/extensions/pi-hermes-memory/tests/config.test.ts +42 -0
  90. package/dist/extensions/pi-hermes-memory/tests/handlers/background-review.test.ts +159 -3
  91. package/dist/extensions/pi-hermes-memory/tests/handlers/review-memory-ops.test.ts +452 -0
  92. package/dist/extensions/pi-hermes-memory/tests/handlers/session-backfill.test.ts +39 -0
  93. package/dist/extensions/pi-hermes-memory/tests/integration/corruption-recovery-integration.test.ts +91 -0
  94. package/dist/extensions/pi-hermes-memory/tests/store/db.test.ts +29 -0
  95. package/dist/extensions/pi-hermes-memory/tests/store/fts-query.test.ts +116 -0
  96. package/dist/extensions/pi-hermes-memory/tests/store/memory-store.test.ts +163 -0
  97. package/dist/extensions/pi-hermes-memory/tests/store/session-indexer.test.ts +233 -0
  98. package/dist/extensions/pi-hermes-memory/tests/store/session-search.test.ts +12 -0
  99. package/dist/extensions/pi-hermes-memory/tests/store/sqlite-memory-store.test.ts +175 -0
  100. package/dist/extensions/pi-hermes-memory/tests/store/sqlite-native.test.ts +2 -2
  101. package/dist/extensions/pi-hermes-memory/tests/tools/memory-search-tool.test.ts +22 -0
  102. package/dist/extensions/pi-hermes-memory/tests/tools/memory-tool.test.ts +37 -1
  103. package/dist/extensions/pi-intercom/CHANGELOG.md +13 -0
  104. package/dist/extensions/pi-intercom/README.md +16 -2
  105. package/dist/extensions/pi-intercom/broker/spawn.test.ts +19 -2
  106. package/dist/extensions/pi-intercom/broker/spawn.ts +4 -4
  107. package/dist/extensions/pi-intercom/index.ts +1 -1
  108. package/dist/extensions/pi-intercom/intercom.integration.test.ts +121 -2
  109. package/dist/extensions/pi-intercom/package.json +1 -1
  110. package/dist/extensions/pi-intercom/project-agent.test.ts +1 -1
  111. package/dist/extensions/pi-intercom/project-agent.ts +1 -6
  112. package/dist/extensions/pi-intercom/skills/pi-intercom/SKILL.md +7 -6
  113. package/dist/extensions/pi-rewind-hook/index.test.ts +7 -0
  114. package/dist/extensions/pi-subagents/.github/workflows/test.yml +36 -4
  115. package/dist/extensions/pi-subagents/CHANGELOG.md +120 -0
  116. package/dist/extensions/pi-subagents/README.md +3 -3
  117. package/dist/extensions/pi-subagents/agents/researcher.md +23 -13
  118. package/dist/extensions/pi-subagents/agents/reviewer.md +1 -1
  119. package/dist/extensions/pi-subagents/agents/scout.md +1 -1
  120. package/dist/extensions/pi-subagents/docs/agents.md +32 -16
  121. package/dist/extensions/pi-subagents/docs/configuration.md +41 -15
  122. package/dist/extensions/pi-subagents/docs/extension-api.md +109 -6
  123. package/dist/extensions/pi-subagents/docs/missions.md +2 -0
  124. package/dist/extensions/pi-subagents/docs/models.md +58 -1
  125. package/dist/extensions/pi-subagents/docs/observability.md +50 -12
  126. package/dist/extensions/pi-subagents/docs/tool-reference.md +17 -10
  127. package/dist/extensions/pi-subagents/docs/watchdog.md +1 -1
  128. package/dist/extensions/pi-subagents/docs/workflows.md +26 -9
  129. package/dist/extensions/pi-subagents/package-lock.json +747 -177
  130. package/dist/extensions/pi-subagents/package.json +6 -4
  131. package/dist/extensions/pi-subagents/runner-server-preload.mjs +13 -0
  132. package/dist/extensions/pi-subagents/skills/pi-subagents/SKILL.md +2 -1
  133. package/dist/extensions/pi-subagents/skills/pi-subagents/references/constraints-and-recipes.md +1 -1
  134. package/dist/extensions/pi-subagents/skills/pi-subagents/references/execution-controls.md +21 -4
  135. package/dist/extensions/pi-subagents/skills/pi-subagents/references/management-authoring-rpc.md +2 -1
  136. package/dist/extensions/pi-subagents/skills/pi-subagents/references/multi-lane-orchestration.md +2 -0
  137. package/dist/extensions/pi-subagents/src/agents/advertised-agent-prompt.ts +63 -0
  138. package/dist/extensions/pi-subagents/src/agents/agent-management.ts +61 -16
  139. package/dist/extensions/pi-subagents/src/agents/agent-serializer.ts +2 -0
  140. package/dist/extensions/pi-subagents/src/agents/agents.ts +8 -0
  141. package/dist/extensions/pi-subagents/src/api/capability-ceiling.ts +0 -1
  142. package/dist/extensions/pi-subagents/src/api/{pi-args.ts → child-tool-plan.ts} +1 -1
  143. package/dist/extensions/pi-subagents/src/api/preflight.ts +7 -4
  144. package/dist/extensions/pi-subagents/src/api/shared-types.ts +2 -2
  145. package/dist/extensions/pi-subagents/src/api/workflow-resources.ts +6 -0
  146. package/dist/extensions/pi-subagents/src/extension/config.ts +4 -2
  147. package/dist/extensions/pi-subagents/src/extension/doctor.ts +2 -10
  148. package/dist/extensions/pi-subagents/src/extension/fanout-child.ts +9 -11
  149. package/dist/extensions/pi-subagents/src/extension/index.ts +99 -10
  150. package/dist/extensions/pi-subagents/src/extension/public-execution.ts +13 -0
  151. package/dist/extensions/pi-subagents/src/extension/rpc.ts +7 -23
  152. package/dist/extensions/pi-subagents/src/extension/schemas.ts +11 -8
  153. package/dist/extensions/pi-subagents/src/extension/tool-description.ts +25 -7
  154. package/dist/extensions/pi-subagents/src/integrations/pi-web-session-liveness.ts +73 -0
  155. package/dist/extensions/pi-subagents/src/intercom/native-supervisor-channel.ts +228 -136
  156. package/dist/extensions/pi-subagents/src/intercom/supervisor-ui.ts +244 -0
  157. package/dist/extensions/pi-subagents/src/missions/workflow-state.ts +37 -16
  158. package/dist/extensions/pi-subagents/src/runs/background/active-async-capacity.ts +18 -18
  159. package/dist/extensions/pi-subagents/src/runs/background/async-execution.ts +51 -25
  160. package/dist/extensions/pi-subagents/src/runs/background/async-job-tracker.ts +46 -3
  161. package/dist/extensions/pi-subagents/src/runs/background/async-resume.ts +14 -3
  162. package/dist/extensions/pi-subagents/src/runs/background/async-retention.ts +9 -0
  163. package/dist/extensions/pi-subagents/src/runs/background/async-status-snapshot.ts +10 -12
  164. package/dist/extensions/pi-subagents/src/runs/background/async-status.ts +18 -15
  165. package/dist/extensions/pi-subagents/src/runs/background/auto-drain.ts +40 -29
  166. package/dist/extensions/pi-subagents/src/runs/background/chain-root-attachment.ts +8 -0
  167. package/dist/extensions/pi-subagents/src/runs/background/control-channel.ts +79 -247
  168. package/dist/extensions/pi-subagents/src/runs/background/notify.ts +88 -12
  169. package/dist/extensions/pi-subagents/src/runs/background/owned-process-tree.ts +6 -6
  170. package/dist/extensions/pi-subagents/src/runs/background/process-terminal.ts +24 -24
  171. package/dist/extensions/pi-subagents/src/runs/background/retained-nested-route-tracker.ts +96 -0
  172. package/dist/extensions/pi-subagents/src/runs/background/run-child-session.ts +642 -0
  173. package/dist/extensions/pi-subagents/src/runs/background/run-status.ts +75 -6
  174. package/dist/extensions/pi-subagents/src/runs/background/runner-aliases.ts +163 -0
  175. package/dist/extensions/pi-subagents/src/runs/background/runner-child-launch.ts +86 -0
  176. package/dist/extensions/pi-subagents/src/runs/background/runner-child-sessions.ts +31 -0
  177. package/dist/extensions/pi-subagents/src/runs/background/scheduled-runs.ts +18 -4
  178. package/dist/extensions/pi-subagents/src/runs/background/stale-run-reconciler.ts +3 -1
  179. package/dist/extensions/pi-subagents/src/runs/background/subagent-runner.ts +597 -1092
  180. package/dist/extensions/pi-subagents/src/runs/background/subagent-wait.ts +3 -0
  181. package/dist/extensions/pi-subagents/src/runs/background/wait-completions.ts +4 -0
  182. package/dist/extensions/pi-subagents/src/runs/foreground/async-steering-action.ts +20 -17
  183. package/dist/extensions/pi-subagents/src/runs/foreground/execution.ts +296 -394
  184. package/dist/extensions/pi-subagents/src/runs/foreground/foreground-control.ts +4 -0
  185. package/dist/extensions/pi-subagents/src/runs/foreground/foreground-history.ts +3 -1
  186. package/dist/extensions/pi-subagents/src/runs/foreground/prompt-audit.ts +9 -5
  187. package/dist/extensions/pi-subagents/src/runs/foreground/subagent-executor.ts +630 -283
  188. package/dist/extensions/pi-subagents/src/runs/foreground/workflow-detach-reconcile.ts +8 -5
  189. package/dist/extensions/pi-subagents/src/runs/foreground/workflow-foreground-steering.ts +78 -98
  190. package/dist/extensions/pi-subagents/src/runs/shared/abort-recovery.ts +3 -3
  191. package/dist/extensions/pi-subagents/src/runs/shared/acceptance.ts +16 -3
  192. package/dist/extensions/pi-subagents/src/runs/shared/agent-contract.ts +1 -1
  193. package/dist/extensions/pi-subagents/src/runs/shared/async-status-projection.ts +47 -47
  194. package/dist/extensions/pi-subagents/src/runs/shared/capability-ceiling.ts +1 -2
  195. package/dist/extensions/pi-subagents/src/runs/shared/child-hooks.ts +174 -0
  196. package/dist/extensions/pi-subagents/src/runs/shared/child-identity.ts +14 -3
  197. package/dist/extensions/pi-subagents/src/runs/shared/child-launch.ts +319 -0
  198. package/dist/extensions/pi-subagents/src/runs/shared/child-lifecycle.ts +25 -0
  199. package/dist/extensions/pi-subagents/src/runs/shared/child-runtime-config.ts +126 -0
  200. package/dist/extensions/pi-subagents/src/runs/shared/child-session.ts +373 -0
  201. package/dist/extensions/pi-subagents/src/runs/shared/child-tool-plan.ts +530 -0
  202. package/dist/extensions/pi-subagents/src/runs/shared/completion-evidence.ts +2 -2
  203. package/dist/extensions/pi-subagents/src/runs/shared/completion-guard.ts +2 -1
  204. package/dist/extensions/pi-subagents/src/runs/shared/external-cli-preflight.ts +16 -0
  205. package/dist/extensions/pi-subagents/src/runs/shared/host-step-status.ts +11 -11
  206. package/dist/extensions/pi-subagents/src/runs/shared/llm-intent-arbiter.ts +30 -20
  207. package/dist/extensions/pi-subagents/src/runs/shared/mcp-direct-tool-allowlist.ts +5 -4
  208. package/dist/extensions/pi-subagents/src/runs/shared/model-exclusions.ts +84 -15
  209. package/dist/extensions/pi-subagents/src/runs/shared/model-fallback.ts +88 -12
  210. package/dist/extensions/pi-subagents/src/runs/shared/nested-events.ts +37 -53
  211. package/dist/extensions/pi-subagents/src/runs/shared/nested-path.ts +1 -1
  212. package/dist/extensions/pi-subagents/src/runs/shared/orca-progress-tabs.ts +18 -7
  213. package/dist/extensions/pi-subagents/src/runs/shared/parallel-handoff.ts +57 -12
  214. package/dist/extensions/pi-subagents/src/runs/shared/parallel-utils.ts +3 -4
  215. package/dist/extensions/pi-subagents/src/runs/shared/permissions.ts +0 -11
  216. package/dist/extensions/pi-subagents/src/runs/shared/process-signal.ts +4 -1
  217. package/dist/extensions/pi-subagents/src/runs/shared/readonly-drain-observation.ts +42 -0
  218. package/dist/extensions/pi-subagents/src/runs/shared/readonly-model-continuation.ts +69 -0
  219. package/dist/extensions/pi-subagents/src/runs/shared/readonly-session-evidence.ts +307 -0
  220. package/dist/extensions/pi-subagents/src/runs/shared/run-fanout-budget.ts +8 -21
  221. package/dist/extensions/pi-subagents/src/runs/shared/runtime-acknowledged-extensions.ts +3 -30
  222. package/dist/extensions/pi-subagents/src/runs/shared/structured-output.ts +17 -4
  223. package/dist/extensions/pi-subagents/src/runs/shared/subagent-control.ts +2 -0
  224. package/dist/extensions/pi-subagents/src/runs/shared/subagent-prompt-runtime.ts +99 -386
  225. package/dist/extensions/pi-subagents/src/runs/shared/tool-availability.ts +18 -62
  226. package/dist/extensions/pi-subagents/src/runs/shared/tool-budget.ts +0 -14
  227. package/dist/extensions/pi-subagents/src/runs/shared/worktree-cleanup-plan.ts +31 -9
  228. package/dist/extensions/pi-subagents/src/runs/shared/worktree-setup-command.ts +190 -0
  229. package/dist/extensions/pi-subagents/src/runs/shared/worktree.ts +506 -226
  230. package/dist/extensions/pi-subagents/src/shared/child-session-name.ts +1 -1
  231. package/dist/extensions/pi-subagents/src/shared/jsonl-writer.ts +11 -0
  232. package/dist/extensions/pi-subagents/src/shared/model-response-aliases.ts +13 -0
  233. package/dist/extensions/pi-subagents/src/shared/thinking-ceiling.ts +0 -6
  234. package/dist/extensions/pi-subagents/src/shared/types.ts +145 -89
  235. package/dist/extensions/pi-subagents/src/shared/utils.ts +13 -6
  236. package/dist/extensions/pi-subagents/src/shared/watch-strategy.ts +2 -0
  237. package/dist/extensions/pi-subagents/src/shared/workflow-child-permit.ts +18 -13
  238. package/dist/extensions/pi-subagents/src/slash/slash-commands.ts +0 -6
  239. package/dist/extensions/pi-subagents/src/tui/fleet-status.ts +1 -1
  240. package/dist/extensions/pi-subagents/src/tui/fleet.ts +11 -6
  241. package/dist/extensions/pi-subagents/src/tui/render.ts +277 -46
  242. package/dist/extensions/pi-subagents/src/watchdog/child-status.ts +0 -1
  243. package/dist/extensions/pi-subagents/src/watchdog/register-child.ts +12 -12
  244. package/dist/extensions/pi-subagents/src/workflows/chat-progress.ts +3 -3
  245. package/dist/extensions/pi-subagents/src/workflows/scripted-workflow.ts +116 -15
  246. package/dist/extensions/pi-subagents/src/workflows/workflow-checklist.ts +15 -18
  247. package/dist/extensions/pi-subagents/src/workflows/workflow-child-summary.ts +57 -8
  248. package/dist/extensions/pi-subagents/src/workflows/workflow-preflight.ts +19 -19
  249. package/dist/extensions/pi-subagents/src/workflows/workflow-receipt.ts +3 -3
  250. package/dist/extensions/pi-subagents/src/workflows/workflow-resources.ts +97 -22
  251. package/dist/extensions/pi-subagents/src/workflows/workflow-settlement.ts +3 -0
  252. package/dist/extensions/pi-subagents/test/integration/acceptance-file-report.test.ts +336 -21
  253. package/dist/extensions/pi-subagents/test/integration/async-execution.part-1.test.ts +1181 -0
  254. package/dist/extensions/pi-subagents/test/integration/async-execution.part-2.test.ts +2240 -0
  255. package/dist/extensions/pi-subagents/test/integration/async-execution.part-3.test.ts +2268 -0
  256. package/dist/extensions/pi-subagents/test/integration/async-execution.part-4.test.ts +1512 -0
  257. package/dist/extensions/pi-subagents/test/integration/async-job-tracker.test.ts +231 -16
  258. package/dist/extensions/pi-subagents/test/integration/error-handling.test.ts +4 -18
  259. package/dist/extensions/pi-subagents/test/integration/foreign-workflow-steering.test.ts +95 -0
  260. package/dist/extensions/pi-subagents/test/integration/fork-context-execution.test.ts +9 -15
  261. package/dist/extensions/pi-subagents/test/integration/in-process-child.test.ts +374 -0
  262. package/dist/extensions/pi-subagents/test/integration/intercom-result-delivery.test.ts +20 -10
  263. package/dist/extensions/pi-subagents/test/integration/orca-progress-tabs.test.ts +18 -6
  264. package/dist/extensions/pi-subagents/test/integration/recovered-assistant-error.test.ts +151 -0
  265. package/dist/extensions/pi-subagents/test/integration/render-fork-badge.test.ts +42 -1
  266. package/dist/extensions/pi-subagents/test/integration/render-widget.test.ts +150 -25
  267. package/dist/extensions/pi-subagents/test/integration/result-publication.test.ts +354 -0
  268. package/dist/extensions/pi-subagents/test/integration/shared-cwd-terminal-evidence.test.ts +117 -0
  269. package/dist/extensions/pi-subagents/test/integration/single-execution.part-1.test.ts +3678 -0
  270. package/dist/extensions/pi-subagents/test/integration/{single-execution.test.ts → single-execution.part-2.test.ts} +500 -4207
  271. package/dist/extensions/pi-subagents/test/integration/slash-commands.test.ts +1 -2
  272. package/dist/extensions/pi-subagents/test/integration/workflow-result-publication.test.ts +127 -0
  273. package/dist/extensions/pi-subagents/test/integration/workflow-steer-inbox.test.ts +365 -0
  274. package/dist/extensions/pi-subagents/test/smoke/pi085-child.ts +31 -0
  275. package/dist/extensions/pi-subagents/test/smoke/pi085-clean-install.mjs +74 -0
  276. package/dist/extensions/pi-subagents/test/smoke/pi085-extension.ts +16 -0
  277. package/dist/extensions/pi-subagents/test/support/async-execution-fixture.ts +699 -0
  278. package/dist/extensions/pi-subagents/test/support/fake-child-session.ts +418 -0
  279. package/dist/extensions/pi-subagents/test/support/foreign-workflow-steer-host.mjs +13 -0
  280. package/dist/extensions/pi-subagents/test/support/mock-pi.ts +30 -76
  281. package/dist/extensions/pi-subagents/test/support/notify-diagnostics-fixture.ts +84 -0
  282. package/dist/extensions/pi-subagents/test/support/result-publication-capacity-preload.mjs +70 -0
  283. package/dist/extensions/pi-subagents/test/support/runner-child-session-factory.ts +17 -0
  284. package/dist/extensions/pi-subagents/test/support/single-execution-fixture.ts +390 -0
  285. package/dist/extensions/pi-subagents/test/unit/acceptance-compaction.test.ts +120 -0
  286. package/dist/extensions/pi-subagents/test/unit/acceptance.test.ts +24 -3
  287. package/dist/extensions/pi-subagents/test/unit/advertised-agent-prompt.test.ts +87 -0
  288. package/dist/extensions/pi-subagents/test/unit/advertised-agent-refresh.test.ts +163 -0
  289. package/dist/extensions/pi-subagents/test/unit/agent-frontmatter.test.ts +70 -31
  290. package/dist/extensions/pi-subagents/test/unit/agent-management.test.ts +78 -3
  291. package/dist/extensions/pi-subagents/test/unit/agent-overrides.test.ts +2 -8
  292. package/dist/extensions/pi-subagents/test/unit/async-execution.test.ts +1 -1
  293. package/dist/extensions/pi-subagents/test/unit/async-interrupt-action.test.ts +73 -66
  294. package/dist/extensions/pi-subagents/test/unit/async-recovery-descriptor.test.ts +51 -1
  295. package/dist/extensions/pi-subagents/test/unit/async-retention.test.ts +49 -1
  296. package/dist/extensions/pi-subagents/test/unit/async-spawn-preload.test.ts +103 -0
  297. package/dist/extensions/pi-subagents/test/unit/async-status-projection.test.ts +3 -3
  298. package/dist/extensions/pi-subagents/test/unit/capability-ceiling-agent-allowlist.test.ts +9 -8
  299. package/dist/extensions/pi-subagents/test/unit/capability-ceiling.test.ts +10 -7
  300. package/dist/extensions/pi-subagents/test/unit/chain-root-attachment.test.ts +48 -6
  301. package/dist/extensions/pi-subagents/test/unit/child-runtime-config.test.ts +90 -0
  302. package/dist/extensions/pi-subagents/test/unit/{pi-args-permission-system.test.ts → child-tool-plan-permission-system.test.ts} +48 -86
  303. package/dist/extensions/pi-subagents/test/unit/child-tool-plan.test.ts +33 -0
  304. package/dist/extensions/pi-subagents/test/unit/compaction-resume.test.ts +18 -18
  305. package/dist/extensions/pi-subagents/test/unit/completion-evidence.test.ts +9 -9
  306. package/dist/extensions/pi-subagents/test/unit/completion-guard.test.ts +8 -7
  307. package/dist/extensions/pi-subagents/test/unit/control-channel.test.ts +225 -65
  308. package/dist/extensions/pi-subagents/test/unit/extension-bindings.test.ts +17 -22
  309. package/dist/extensions/pi-subagents/test/unit/fleet-status.test.ts +11 -1
  310. package/dist/extensions/pi-subagents/test/unit/fleet.test.ts +47 -3
  311. package/dist/extensions/pi-subagents/test/unit/foreign-workflow-steering.test.ts +74 -0
  312. package/dist/extensions/pi-subagents/test/unit/fork-cache-key.test.ts +23 -29
  313. package/dist/extensions/pi-subagents/test/unit/host-peer-runtime-imports.test.ts +96 -4
  314. package/dist/extensions/pi-subagents/test/unit/host-step-status.test.ts +4 -4
  315. package/dist/extensions/pi-subagents/test/unit/index-child-registration.test.ts +116 -55
  316. package/dist/extensions/pi-subagents/test/unit/index-segment.test.ts +58 -1
  317. package/dist/extensions/pi-subagents/test/unit/llm-intent-arbiter.test.ts +104 -3
  318. package/dist/extensions/pi-subagents/test/unit/mission-lifecycle.test.ts +10 -1
  319. package/dist/extensions/pi-subagents/test/unit/mission-store.test.ts +31 -1
  320. package/dist/extensions/pi-subagents/test/unit/model-exclusions.test.ts +102 -51
  321. package/dist/extensions/pi-subagents/test/unit/model-fallback.test.ts +196 -6
  322. package/dist/extensions/pi-subagents/test/unit/native-supervisor-channel.test.ts +181 -38
  323. package/dist/extensions/pi-subagents/test/unit/nested-control.test.ts +62 -70
  324. package/dist/extensions/pi-subagents/test/unit/nested-events.test.ts +58 -53
  325. package/dist/extensions/pi-subagents/test/unit/notify-diagnostics.test.ts +42 -0
  326. package/dist/extensions/pi-subagents/test/unit/notify.test.ts +84 -0
  327. package/dist/extensions/pi-subagents/test/unit/orca-progress-tabs.test.ts +13 -5
  328. package/dist/extensions/pi-subagents/test/unit/package-manifest.test.ts +21 -5
  329. package/dist/extensions/pi-subagents/test/unit/parallel-handoff.test.ts +5 -4
  330. package/dist/extensions/pi-subagents/test/unit/permissions.test.ts +2 -6
  331. package/dist/extensions/pi-subagents/test/unit/pi-coding-agent-dir.test.ts +28 -2
  332. package/dist/extensions/pi-subagents/test/unit/pi-web-session-liveness.test.ts +209 -0
  333. package/dist/extensions/pi-subagents/test/unit/preflight.test.ts +38 -1
  334. package/dist/extensions/pi-subagents/test/unit/prompt-audit-optional-peers.test.ts +43 -0
  335. package/dist/extensions/pi-subagents/test/unit/public-execution.test.ts +24 -2
  336. package/dist/extensions/pi-subagents/test/unit/readonly-drain-observation.test.ts +174 -0
  337. package/dist/extensions/pi-subagents/test/unit/readonly-model-continuation.test.ts +94 -0
  338. package/dist/extensions/pi-subagents/test/unit/readonly-session-evidence.test.ts +1329 -0
  339. package/dist/extensions/pi-subagents/test/unit/recursion-guard.test.ts +29 -80
  340. package/dist/extensions/pi-subagents/test/unit/render-helpers.test.ts +11 -8
  341. package/dist/extensions/pi-subagents/test/unit/result-files.test.ts +2 -2
  342. package/dist/extensions/pi-subagents/test/unit/retained-nested-route-tracker.test.ts +175 -0
  343. package/dist/extensions/pi-subagents/test/unit/rpc.test.ts +38 -12
  344. package/dist/extensions/pi-subagents/test/unit/run-fanout-budget.test.ts +2 -5
  345. package/dist/extensions/pi-subagents/test/unit/run-status.test.ts +89 -1
  346. package/dist/extensions/pi-subagents/test/unit/runtime-acknowledged-extensions.test.ts +2 -16
  347. package/dist/extensions/pi-subagents/test/unit/scheduled-runs.test.ts +27 -3
  348. package/dist/extensions/pi-subagents/test/unit/scheduled-supervisor-demand.test.ts +215 -0
  349. package/dist/extensions/pi-subagents/test/unit/schemas.test.ts +15 -11
  350. package/dist/extensions/pi-subagents/test/unit/scripted-workflow.test.ts +237 -1
  351. package/dist/extensions/pi-subagents/test/unit/steering-action.test.ts +2 -5
  352. package/dist/extensions/pi-subagents/test/unit/streamed-progress-bounds.test.ts +2 -2
  353. package/dist/extensions/pi-subagents/test/unit/subagent-guide.test.ts +14 -1
  354. package/dist/extensions/pi-subagents/test/unit/subagent-prompt-runtime.test.ts +130 -739
  355. package/dist/extensions/pi-subagents/test/unit/supervisor-ask-registration.test.ts +682 -0
  356. package/dist/extensions/pi-subagents/test/unit/supervisor-ui.test.ts +137 -0
  357. package/dist/extensions/pi-subagents/test/unit/tool-budget.test.ts +1 -11
  358. package/dist/extensions/pi-subagents/test/unit/tool-description.test.ts +23 -3
  359. package/dist/extensions/pi-subagents/test/unit/wait-completions.test.ts +8 -1
  360. package/dist/extensions/pi-subagents/test/unit/watchdog-tool-actions.test.ts +19 -1
  361. package/dist/extensions/pi-subagents/test/unit/workflow-chat-progress.test.ts +72 -4
  362. package/dist/extensions/pi-subagents/test/unit/workflow-checklist.test.ts +17 -3
  363. package/dist/extensions/pi-subagents/test/unit/workflow-child-summary.test.ts +34 -0
  364. package/dist/extensions/pi-subagents/test/unit/workflow-detach-reconcile.test.ts +12 -2
  365. package/dist/extensions/pi-subagents/test/unit/workflow-launch-params.test.ts +46 -6
  366. package/dist/extensions/pi-subagents/test/unit/workflow-receipt.test.ts +3 -3
  367. package/dist/extensions/pi-subagents/test/unit/workflow-resources.test.ts +133 -1
  368. package/dist/extensions/pi-subagents/test/unit/workflow-resume-hint.test.ts +53 -2
  369. package/dist/extensions/pi-subagents/test/unit/worktree-cleanup-plan.test.ts +222 -14
  370. package/dist/extensions/pi-subagents/test/unit/worktree.test.ts +526 -85
  371. package/dist/extensions/pi-web-agent/src/backends/config.ts +1 -0
  372. package/dist/extensions/pi-web-agent/src/backends/settings-reader.ts +5 -3
  373. package/dist/extensions/pi-web-agent/src/search/tokenin.ts +7 -3
  374. package/dist/extensions/pi-web-agent/src/types.ts +1 -1
  375. package/dist/extensions/pi-zentui/README.md +17 -0
  376. package/dist/extensions/pi-zentui/assets/screenshots/footer-status-hyperlink.png +0 -0
  377. package/dist/extensions/pi-zentui/docs/configuration.md +71 -2
  378. package/dist/extensions/pi-zentui/extensions/zentui/config.ts +11 -0
  379. package/dist/extensions/pi-zentui/extensions/zentui/editor-transfer.ts +14 -0
  380. package/dist/extensions/pi-zentui/extensions/zentui/extension-status.ts +29 -13
  381. package/dist/extensions/pi-zentui/extensions/zentui/footer-layout.ts +5 -4
  382. package/dist/extensions/pi-zentui/extensions/zentui/footer-text.ts +11 -0
  383. package/dist/extensions/pi-zentui/extensions/zentui/footer.ts +11 -10
  384. package/dist/extensions/pi-zentui/extensions/zentui/index.ts +77 -3
  385. package/dist/extensions/pi-zentui/extensions/zentui/presets.ts +77 -0
  386. package/dist/extensions/pi-zentui/extensions/zentui/settings-command.ts +103 -2
  387. package/dist/extensions/pi-zentui/extensions/zentui/working-line-extension-segments.ts +115 -0
  388. package/dist/extensions/pi-zentui/extensions/zentui/working-line.ts +87 -5
  389. package/dist/extensions/pi-zentui/package-lock.json +277 -517
  390. package/dist/extensions/pi-zentui/package.json +7 -4
  391. package/dist/extensions/pi-zentui/test/config-presets.test.ts +238 -0
  392. package/dist/extensions/pi-zentui/test/editor-transfer.test.ts +24 -1
  393. package/dist/extensions/pi-zentui/test/extension-compliance.test.ts +570 -2
  394. package/dist/extensions/pi-zentui/test/extension-status.test.ts +90 -0
  395. package/dist/extensions/pi-zentui/test/footer-hyperlink.test.ts +243 -0
  396. package/dist/extensions/pi-zentui/test/package-contents.mjs +1 -0
  397. package/dist/extensions/pi-zentui/test/presets.test.ts +121 -0
  398. package/dist/extensions/pi-zentui/test/settings-command.test.ts +205 -1
  399. package/dist/extensions/pi-zentui/test/working-line-extension-segments.test.ts +139 -0
  400. package/dist/extensions/pi-zentui/test/working-line-lifecycle.integration.test.ts +237 -3
  401. package/dist/extensions/pi-zentui/test/working-line.test.ts +94 -2
  402. package/dist/index.d.ts +1 -1
  403. package/dist/main.js +4 -1
  404. package/dist/modes/interactive/chat-viewport.d.ts +19 -0
  405. package/dist/modes/interactive/chat-viewport.js +27 -0
  406. package/dist/modes/interactive/components/assistant-message.d.ts +6 -2
  407. package/dist/modes/interactive/components/assistant-message.js +30 -13
  408. package/dist/modes/interactive/components/custom-editor.d.ts +10 -1
  409. package/dist/modes/interactive/components/custom-editor.js +39 -1
  410. package/dist/modes/interactive/components/first-time-setup.js +1 -1
  411. package/dist/modes/interactive/components/index.d.ts +1 -1
  412. package/dist/modes/interactive/components/markdown-transform.d.ts +2 -0
  413. package/dist/modes/interactive/components/markdown-transform.js +18 -0
  414. package/dist/modes/interactive/components/model-selector.js +19 -17
  415. package/dist/modes/interactive/components/scoped-models-selector.js +21 -23
  416. package/dist/modes/interactive/components/settings-selector.d.ts +7 -1
  417. package/dist/modes/interactive/components/settings-selector.js +128 -20
  418. package/dist/modes/interactive/components/settings-submenu.d.ts +71 -0
  419. package/dist/modes/interactive/components/settings-submenu.js +164 -0
  420. package/dist/modes/interactive/components/status-indicator.d.ts +3 -1
  421. package/dist/modes/interactive/components/status-indicator.js +10 -3
  422. package/dist/modes/interactive/components/thinking-selector.d.ts +15 -3
  423. package/dist/modes/interactive/components/thinking-selector.js +75 -18
  424. package/dist/modes/interactive/components/tool-execution.d.ts +21 -4
  425. package/dist/modes/interactive/components/tool-execution.js +35 -33
  426. package/dist/modes/interactive/components/trust-selector.js +2 -2
  427. package/dist/modes/interactive/interactive-mode.d.ts +13 -15
  428. package/dist/modes/interactive/interactive-mode.js +144 -80
  429. package/dist/modes/interactive/session-share.d.ts +15 -0
  430. package/dist/modes/interactive/session-share.js +183 -0
  431. package/dist/modes/interactive/theme/dark.json +2 -1
  432. package/dist/modes/interactive/theme/light.json +2 -1
  433. package/dist/modes/interactive/theme/theme-controller.d.ts +1 -0
  434. package/dist/modes/interactive/theme/theme-controller.js +5 -0
  435. package/dist/modes/interactive/theme/theme-json.d.ts +83 -0
  436. package/dist/modes/interactive/theme/theme-json.js +129 -0
  437. package/dist/modes/interactive/theme/theme-schema.json +9 -1
  438. package/dist/modes/interactive/theme/theme.d.ts +14 -5
  439. package/dist/modes/interactive/theme/theme.js +18 -119
  440. package/dist/modes/interactive/tui-renderer.d.ts +20 -0
  441. package/dist/modes/interactive/tui-renderer.js +65 -0
  442. package/dist/utils/exif-orientation.js +2 -3
  443. package/dist/utils/management-http.d.ts +24 -0
  444. package/dist/utils/management-http.js +53 -0
  445. package/dist/utils/text.d.ts +7 -0
  446. package/dist/utils/text.js +8 -0
  447. package/dist/utils/tools-manager.d.ts +1 -0
  448. package/dist/utils/tools-manager.js +43 -21
  449. package/docs/docs.json +0 -4
  450. package/package.json +4 -4
  451. package/dist/extensions/pi-subagents/src/runs/shared/child-protocol.ts +0 -415
  452. package/dist/extensions/pi-subagents/src/runs/shared/pi-args.ts +0 -1059
  453. package/dist/extensions/pi-subagents/src/runs/shared/subagent-startup-retry.ts +0 -116
  454. package/dist/extensions/pi-subagents/src/shared/post-exit-stdio-guard.ts +0 -85
  455. package/dist/extensions/pi-subagents/test/e2e/real-session-subagent.test.ts +0 -224
  456. package/dist/extensions/pi-subagents/test/integration/async-execution.test.ts +0 -6986
  457. package/dist/extensions/pi-subagents/test/support/mock-pi-script.mjs +0 -447
  458. package/dist/extensions/pi-subagents/test/support/real-session-child-cli.mjs +0 -185
  459. package/dist/extensions/pi-subagents/test/support/real-session-runner.ts +0 -311
  460. package/dist/extensions/pi-subagents/test/unit/capability-ceiling-pi-args.test.ts +0 -84
  461. package/dist/extensions/pi-subagents/test/unit/child-protocol.test.ts +0 -202
  462. package/dist/extensions/pi-subagents/test/unit/close-grace-timer.test.ts +0 -101
  463. package/dist/extensions/pi-subagents/test/unit/pi-args.test.ts +0 -2347
  464. package/dist/extensions/pi-subagents/test/unit/subagent-startup-retry.test.ts +0 -104
  465. package/dist/extensions/pi-subagents/test/unit/windows-hide-spawn.test.ts +0 -49
  466. package/dist/skills/imagegen-frontend-mobile/SKILL.md +0 -1465
  467. package/dist/skills/imagegen-frontend-web/SKILL.md +0 -987
  468. package/dist/skills/implanger/SKILL.md +0 -70
  469. package/dist/skills/output/SKILL.md +0 -49
  470. package/dist/skills/planger/SKILL.md +0 -167
  471. package/dist/skills/workflow/SKILL.md +0 -95
  472. package/docs/workflows.md +0 -18
  473. /package/dist/bun/{register-bedrock.d.ts → runtime-setup.d.ts} +0 -0
@@ -2,6 +2,7 @@ import { DatabaseManager } from './db.js';
2
2
  import {
3
3
  buildFallbackFts5Query,
4
4
  buildNaturalLanguageFallbackQuery,
5
+ collectLikeTerms,
5
6
  isFts5QueryError,
6
7
  normalizeFts5Query,
7
8
  normalizeNaturalLanguageFts5Query,
@@ -9,6 +10,8 @@ import {
9
10
  import { normalizeMemoryLookupText } from './memory-lookup.js';
10
11
  import type { MemoryCategory } from '../types.js';
11
12
 
13
+ export { isFts5QueryError };
14
+
12
15
  const MEMORY_SELECT_COLUMNS = `
13
16
  id,
14
17
  project,
@@ -22,6 +25,13 @@ const MEMORY_SELECT_COLUMNS = `
22
25
  last_referenced
23
26
  `;
24
27
 
28
+ // The BM25-ranked search joins memory_fts, which also has a `content` column,
29
+ // so that one query needs the same list qualified with the `memories` alias.
30
+ const MEMORY_SELECT_COLUMNS_M = MEMORY_SELECT_COLUMNS
31
+ .split(',')
32
+ .map((column) => `m.${column.trim()}`)
33
+ .join(',\n ');
34
+
25
35
  const FAILURE_CATEGORY_SET = new Set<MemoryCategory>([
26
36
  'failure',
27
37
  'correction',
@@ -84,6 +94,15 @@ export interface MarkdownMemoryReconcileResult {
84
94
  inserted: number;
85
95
  existing: number;
86
96
  removed: number;
97
+ /**
98
+ * True when the scope was left un-reconciled because of a genuine FTS5
99
+ * search-index error. Markdown remains the source of truth and the write
100
+ * did not fail; search may be stale until /memory-sync-markdown rebuilds
101
+ * the index. Callers should surface that repair guidance to the user.
102
+ */
103
+ degraded?: boolean;
104
+ /** Human-readable reason for the degraded state, when degraded is true. */
105
+ degradedReason?: string;
87
106
  }
88
107
 
89
108
  export interface ParsedMarkdownMemoryEntry extends SqliteMemorySyncInput {}
@@ -157,6 +176,21 @@ function buildScopeConditions(params: unknown[], target?: string, project?: stri
157
176
  return conditions;
158
177
  }
159
178
 
179
+ /** Maps memory_search target filters onto SQLite columns (search paths only). */
180
+ function buildSearchTargetConditions(params: unknown[], target: string | undefined, tablePrefix: string): string[] {
181
+ const conditions: string[] = [];
182
+
183
+ if (target === 'project') {
184
+ conditions.push(`${tablePrefix}.target = 'memory'`);
185
+ conditions.push(`${tablePrefix}.project IS NOT NULL`);
186
+ } else if (target) {
187
+ conditions.push(`${tablePrefix}.target = ?`);
188
+ params.push(target);
189
+ }
190
+
191
+ return conditions;
192
+ }
193
+
160
194
  function getMemoryById(dbManager: DatabaseManager, id: number): SqliteMemoryEntry | null {
161
195
  const db = dbManager.getDb();
162
196
  const row = db.prepare(`
@@ -438,12 +472,17 @@ export function reconcileMarkdownMemoryScope(
438
472
  target: 'memory' | 'user' | 'failure',
439
473
  project: string | null = null,
440
474
  ): MarkdownMemoryReconcileResult {
441
- const db = dbManager.getDb();
442
475
  const normalizedProject = normalizeNullable(project);
443
476
 
477
+ // The full reconcile runs against the CURRENT database handle. It must fetch
478
+ // it via dbManager.getDb() rather than a pre-fetched handle: after corruption
479
+ // recovery quarantines the old file and rebuilds a fresh one, the retry has
480
+ // to run on the new handle.
444
481
  const reconcile = (): MarkdownMemoryReconcileResult => {
482
+ const db = dbManager.getDb();
445
483
  let inserted = 0;
446
484
  let existing = 0;
485
+ let removed = 0;
447
486
  const desiredIdentities = new Set<string>();
448
487
 
449
488
  for (const rawEntry of rawEntries) {
@@ -476,7 +515,6 @@ export function reconcileMarkdownMemoryScope(
476
515
  }
477
516
  }
478
517
 
479
- let removed = 0;
480
518
  if (orphanIds.length > 0) {
481
519
  const placeholders = orphanIds.map(() => '?').join(', ');
482
520
  removed = db.prepare(`DELETE FROM memories WHERE id IN (${placeholders})`).run(...orphanIds).changes;
@@ -485,8 +523,32 @@ export function reconcileMarkdownMemoryScope(
485
523
  return { inserted, existing, removed };
486
524
  };
487
525
 
488
- const transactional = db.transaction?.(reconcile);
489
- return transactional ? transactional() : reconcile();
526
+ const run = (): MarkdownMemoryReconcileResult => {
527
+ const db = dbManager.getDb();
528
+ const transactional = db.transaction?.(reconcile);
529
+ return transactional ? transactional() : reconcile();
530
+ };
531
+
532
+ try {
533
+ // Corruption errors (e.g. "database disk image is malformed") are NOT
534
+ // search-index problems: they must stay on the recovery path so
535
+ // DatabaseManager quarantines the corrupt file, rebuilds a fresh one, and
536
+ // retries this sync (#186). Swallowing them would hide corruption and
537
+ // leave a corrupt database in place.
538
+ return dbManager.withCorruptionRecovery(run);
539
+ } catch (err) {
540
+ if (isFts5QueryError(err)) {
541
+ // A genuine FTS5 query/index error means the search index is stale or
542
+ // broken, not that the database is corrupt. Markdown is the source of
543
+ // truth, so the write must not fail: warn in the log and report a
544
+ // DEGRADED result so the caller can surface the /memory-sync-markdown
545
+ // repair guidance to the user.
546
+ const detail = err instanceof Error ? err.message : String(err);
547
+ console.warn(`[pi-hermes-memory] FTS5 search index error during markdown sync (search may be stale; run /memory-sync-markdown): ${detail}`);
548
+ return { inserted: 0, existing: 0, removed: 0, degraded: true, degradedReason: detail };
549
+ }
550
+ throw err;
551
+ }
490
552
  }
491
553
 
492
554
  function failureProject(rawEntry: string): string | null {
@@ -527,6 +589,12 @@ export function reconcileMarkdownFailureScopes(
527
589
  total.inserted += result.inserted;
528
590
  total.existing += result.existing;
529
591
  total.removed += result.removed;
592
+ if (result.degraded && !total.degraded) {
593
+ // Surface the first degraded scope: repair guidance applies to the whole
594
+ // sync run, and the first reason is the one to act on.
595
+ total.degraded = true;
596
+ total.degradedReason = result.degradedReason;
597
+ }
530
598
  }
531
599
 
532
600
  return total;
@@ -703,11 +771,8 @@ export function searchMemories(
703
771
  const conditions: string[] = [];
704
772
  const params: unknown[] = [];
705
773
 
706
- // FTS5 match via subquery with escaped query
774
+ // FTS5 match via JOIN with BM25 ranking
707
775
  const normalizedQuery = normalizeFts5Query(query);
708
- if (normalizedQuery.length === 0) {
709
- return [];
710
- }
711
776
 
712
777
  let ftsParseError = false;
713
778
 
@@ -715,7 +780,7 @@ export function searchMemories(
715
780
  const conditions: string[] = [];
716
781
  const params: unknown[] = [];
717
782
 
718
- conditions.push('m.id IN (SELECT rowid FROM memory_fts WHERE memory_fts MATCH ?)');
783
+ conditions.push('memory_fts MATCH ?');
719
784
  params.push(matchQuery);
720
785
 
721
786
  if (project !== undefined) {
@@ -727,10 +792,7 @@ export function searchMemories(
727
792
  }
728
793
  }
729
794
 
730
- if (target) {
731
- conditions.push('m.target = ?');
732
- params.push(target);
733
- }
795
+ conditions.push(...buildSearchTargetConditions(params, target, 'm'));
734
796
 
735
797
  if (category) {
736
798
  conditions.push('m.category = ?');
@@ -740,10 +802,13 @@ export function searchMemories(
740
802
  const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(' AND ')}` : '';
741
803
 
742
804
  const sql = `
743
- SELECT ${MEMORY_SELECT_COLUMNS}
805
+ SELECT
806
+ ${MEMORY_SELECT_COLUMNS_M},
807
+ bm25(memory_fts) AS rank_score
744
808
  FROM memories m
809
+ JOIN memory_fts ON memory_fts.rowid = m.id
745
810
  ${whereClause}
746
- ORDER BY m.last_referenced DESC
811
+ ORDER BY rank_score ASC, m.last_referenced DESC
747
812
  LIMIT ?
748
813
  `;
749
814
 
@@ -759,6 +824,7 @@ export function searchMemories(
759
824
  corrected_to: string | null;
760
825
  created: string;
761
826
  last_referenced: string;
827
+ rank_score: number;
762
828
  }>;
763
829
 
764
830
  return rows.map(mapRow);
@@ -778,6 +844,55 @@ export function searchMemories(
778
844
  const conditions: string[] = ["m.content LIKE ? ESCAPE '\\'"];
779
845
  const params: unknown[] = [`%${escapeLikePattern(query.trim())}%`];
780
846
 
847
+ if (project !== undefined) {
848
+ if (project === null) {
849
+ conditions.push('m.project IS NULL');
850
+ } else {
851
+ conditions.push('m.project = ?');
852
+ params.push(project);
853
+ }
854
+ }
855
+ conditions.push(...buildSearchTargetConditions(params, target, 'm'));
856
+
857
+ if (category) {
858
+ conditions.push('m.category = ?');
859
+ params.push(category);
860
+ }
861
+
862
+ const rows = db.prepare(`
863
+ SELECT ${MEMORY_SELECT_COLUMNS}
864
+ FROM memories m
865
+ WHERE ${conditions.join(' AND ')}
866
+ ORDER BY m.last_referenced DESC
867
+ LIMIT ?
868
+ `).all(...params, limit) as Array<{
869
+ id: number;
870
+ project: string | null;
871
+ target: string;
872
+ category: string | null;
873
+ content: string;
874
+ failure_reason: string | null;
875
+ tool_state: string | null;
876
+ corrected_to: string | null;
877
+ created: string;
878
+ last_referenced: string;
879
+ }>;
880
+ return rows.map(mapRow);
881
+ };
882
+
883
+ // Every term a stop word or connector leaves FTS5 nothing to match.
884
+ // Degrade to a scoped literal substring search (OR over the raw terms) —
885
+ // the same fallback session search uses for this case — instead of a hard
886
+ // [].
887
+ const runLiteralLikeFallback = (): SqliteMemoryEntry[] => {
888
+ const terms = collectLikeTerms(query);
889
+ if (terms.length === 0) return [];
890
+
891
+ const conditions: string[] = [
892
+ `(${terms.map(() => "m.content LIKE ? ESCAPE '\\'").join(' OR ')})`,
893
+ ];
894
+ const params: unknown[] = terms.map((term) => `%${escapeLikePattern(term.trim())}%`);
895
+
781
896
  if (project !== undefined) {
782
897
  if (project === null) {
783
898
  conditions.push('m.project IS NULL');
@@ -816,6 +931,10 @@ export function searchMemories(
816
931
  return rows.map(mapRow);
817
932
  };
818
933
 
934
+ if (normalizedQuery.length === 0) {
935
+ return runLiteralLikeFallback();
936
+ }
937
+
819
938
  const exactResults = runSearch(normalizedQuery);
820
939
  if (exactResults.length > 0) {
821
940
  return exactResults;
@@ -36,6 +36,8 @@ Use cases:
36
36
  - Find user preferences: "What are the user's testing preferences?"
37
37
  - Search for past failures: "memory_search('auth', category='failure')"
38
38
 
39
+ target="project" returns only project-attributed memory entries (the ones labeled [target=project]); combine with project to search a named project.
40
+
39
41
  Returns matching memory entries with their mutation target, scope, and dates. The displayed target is the value required by memory_replace and memory_remove.`,
40
42
  promptSnippet: 'Search extended memory store (unlimited capacity)',
41
43
  promptGuidelines: [
@@ -47,11 +49,11 @@ Returns matching memory entries with their mutation target, scope, and dates. Th
47
49
  parameters: Type.Object({
48
50
  query: Type.String({ description: 'Search query. Use natural language or specific terms.' }),
49
51
  project: Type.Optional(Type.String({ description: 'Filter by project name. Pass null for global memories only.' })),
50
- target: Type.Optional(StringEnum(['memory', 'user', 'failure'] as const, { description: 'Filter by target type (memory, user, or failure).' })),
52
+ target: Type.Optional(StringEnum(['memory', 'user', 'failure', 'project'] as const, { description: 'Filter by target type: memory, user, failure, or project-attributed memories.' })),
51
53
  category: Type.Optional(StringEnum(['failure', 'correction', 'insight', 'preference', 'convention', 'tool-quirk'] as const, { description: 'Filter by memory category.' })),
52
54
  limit: Type.Optional(Type.Number({ description: 'Maximum results to return (default: 10, max: 20).' })),
53
55
  }),
54
- execute: async (_id: string, args: { query: string; project?: string; target?: string; category?: string; limit?: number }) => {
56
+ execute: async (_id: string, args: { query: string; project?: string; target?: 'memory' | 'user' | 'failure' | 'project'; category?: string; limit?: number }) => {
55
57
  const query = args.query;
56
58
  const project = args.project;
57
59
  const target = args.target;
@@ -17,6 +17,7 @@ import {
17
17
  removeSyncedMemories,
18
18
  replaceSyncedMemories,
19
19
  syncMemoryEntry,
20
+ isFts5QueryError,
20
21
  } from "../store/sqlite-memory-store.js";
21
22
  import { MEMORY_TOOL_DESCRIPTION } from "../constants.js";
22
23
  import { resolveProjectName, resolveProjectStore, type ProjectNameRef, type ProjectStoreRef } from "../project-context.js";
@@ -238,22 +239,36 @@ async function reconcileStoreScope(
238
239
  if (!dbManager) return undefined;
239
240
  try {
240
241
  if (rawTarget === "failure") {
241
- reconcileMarkdownFailureScopes(dbManager, entries);
242
- return null;
242
+ return degradedRepairMessage(reconcileMarkdownFailureScopes(dbManager, entries));
243
243
  }
244
244
  const target = sqliteTargetFor(rawTarget);
245
- reconcileMarkdownMemoryScope(
246
- dbManager,
247
- entries,
248
- target,
249
- sqliteProjectFor(rawTarget, projectName) ?? null,
245
+ return degradedRepairMessage(
246
+ reconcileMarkdownMemoryScope(
247
+ dbManager,
248
+ entries,
249
+ target,
250
+ sqliteProjectFor(rawTarget, projectName) ?? null,
251
+ ),
250
252
  );
251
- return null;
252
253
  } catch (err) {
253
- return `Saved to Markdown, but SQLite search reconciliation failed: ${err instanceof Error ? err.message : String(err)}`;
254
+ const detail = err instanceof Error ? err.message : String(err);
255
+ if (isFts5QueryError(err)) {
256
+ return degradedRepairMessage({ degraded: true, degradedReason: detail });
257
+ }
258
+ return `Saved to Markdown, but SQLite search reconciliation failed: ${detail}`;
254
259
  }
255
260
  }
256
261
 
262
+ // A degraded reconcile result means the Markdown write succeeded but the
263
+ // SQLite search mirror could not be updated (genuine FTS5 index error). The
264
+ // caller must tell the user how to repair the index instead of looking like a
265
+ // plain success.
266
+ function degradedRepairMessage(result: { degraded?: boolean; degradedReason?: string }): string | null {
267
+ return result.degraded
268
+ ? `Saved to Markdown. Search may be temporarily unavailable (FTS5 index error: ${result.degradedReason}). Run /memory-sync-markdown to rebuild the search index.`
269
+ : null;
270
+ }
271
+
257
272
  type MemoryAction = "add" | "replace" | "remove";
258
273
 
259
274
  type MemoryToolParams = {
@@ -52,8 +52,12 @@ export interface MemoryConfig {
52
52
  projectsMemoryDir?: string;
53
53
  /** Session search configuration. Default: { variant: "legacy" } */
54
54
  sessionSearch?: SessionSearchConfig;
55
+ /** Run a full SQLite quick_check asynchronously after opening. Default: true */
56
+ quickCheckOnOpen?: boolean;
55
57
  /** Override model used for child pi -p subprocess LLM calls. Default: unset */
56
58
  llmModelOverride?: string;
59
+ /** Fallback model chain tried in order when the primary review model fails (rate limit, 401/403, 404, 500/503, invalid response). Default: unset */
60
+ llmFallbackModels?: string[];
57
61
  /** Override thinking level used for child pi -p subprocess LLM calls. Default: unset */
58
62
  llmThinkingOverride?: ThinkingLevel;
59
63
  /** Trusted Pi extension sources required by child processes, such as custom providers or auth adapters. */
@@ -88,6 +92,13 @@ export interface MemoryConfig {
88
92
  autoConsolidationWarnOnFailure: boolean;
89
93
  /** Inject pinned STANDING.md instructions into every session. Default: true */
90
94
  standingInstructionsEnabled: boolean;
95
+ /**
96
+ * Session retention window in days. A positive value opts in to pruning
97
+ * sessions (and their messages) older than the window on startup; `0`/omitted
98
+ * disables pruning so no existing searchable history is silently deleted.
99
+ * Default: 0 (disabled).
100
+ */
101
+ sessionRetentionDays?: number;
91
102
  }
92
103
 
93
104
  export type MemoryCategory =
@@ -41,6 +41,10 @@ describe("loadConfig", () => {
41
41
  assert.strictEqual(config.llmModelOverride, undefined);
42
42
  assert.strictEqual(config.llmThinkingOverride, undefined);
43
43
  assert.strictEqual(config.standingInstructionsEnabled, true);
44
+ // Retention is disabled by default so existing history is never silently
45
+ // deleted; a positive value opts in, 0/omitted disables.
46
+ assert.strictEqual(config.sessionRetentionDays, 0);
47
+ assert.strictEqual(config.quickCheckOnOpen, true);
44
48
  });
45
49
 
46
50
  it("honors a configured consolidationTimeoutMs, warning only when it is below the default", () => {
@@ -85,6 +89,7 @@ describe("loadConfig", () => {
85
89
  llmModelOverride: " openrouter/deepseek/deepseek-v4-flash ",
86
90
  llmThinkingOverride: "minimal",
87
91
  autoConsolidationWarnOnFailure: false,
92
+ quickCheckOnOpen: false,
88
93
  }));
89
94
  const config = loadConfig(TEST_CONFIG_PATH);
90
95
  assert.strictEqual(config.memoryMode, "legacy-inject");
@@ -101,11 +106,48 @@ describe("loadConfig", () => {
101
106
  assert.strictEqual(config.projectsMemoryDir, "my-memory");
102
107
  assert.strictEqual(config.llmModelOverride, "openrouter/deepseek/deepseek-v4-flash");
103
108
  assert.strictEqual(config.llmThinkingOverride, "minimal");
109
+ assert.strictEqual(config.quickCheckOnOpen, false);
104
110
  // Unset values use defaults
105
111
  assert.strictEqual(config.userCharLimit, 5000);
106
112
  assert.strictEqual(config.reviewEnabled, true);
107
113
  });
108
114
 
115
+ it("only accepts boolean quickCheckOnOpen overrides", () => {
116
+ fs.mkdirSync(path.dirname(TEST_CONFIG_PATH), { recursive: true });
117
+ fs.writeFileSync(TEST_CONFIG_PATH, JSON.stringify({ quickCheckOnOpen: "false" }));
118
+ assert.strictEqual(loadConfig(TEST_CONFIG_PATH).quickCheckOnOpen, true);
119
+ });
120
+
121
+ it("merges array-form override tails with explicit llmFallbackModels", () => {
122
+ fs.mkdirSync(path.dirname(TEST_CONFIG_PATH), { recursive: true });
123
+ fs.writeFileSync(TEST_CONFIG_PATH, JSON.stringify({
124
+ llmModelOverride: ["p1/m1", "p2/m2"],
125
+ llmFallbackModels: ["p3/m3"],
126
+ }));
127
+ const config = loadConfig(TEST_CONFIG_PATH);
128
+ assert.strictEqual(config.llmModelOverride, "p1/m1");
129
+ assert.deepStrictEqual(config.llmFallbackModels, ["p2/m2", "p3/m3"]);
130
+ });
131
+
132
+
133
+ it("parses sessionRetentionDays as opt-in, accepting explicit 0 to disable", () => {
134
+ fs.mkdirSync(path.dirname(TEST_CONFIG_PATH), { recursive: true });
135
+
136
+ // Positive value opts in to retention pruning.
137
+ fs.writeFileSync(TEST_CONFIG_PATH, JSON.stringify({ sessionRetentionDays: 45 }));
138
+ assert.strictEqual(loadConfig(TEST_CONFIG_PATH).sessionRetentionDays, 45);
139
+
140
+ // Explicit 0 disables retention (even after a prior positive value).
141
+ fs.writeFileSync(TEST_CONFIG_PATH, JSON.stringify({ sessionRetentionDays: 0 }));
142
+ assert.strictEqual(loadConfig(TEST_CONFIG_PATH).sessionRetentionDays, 0);
143
+
144
+ // Negative / non-numeric values are ignored, keeping the (disabled) default.
145
+ fs.writeFileSync(TEST_CONFIG_PATH, JSON.stringify({ sessionRetentionDays: -5 }));
146
+ assert.strictEqual(loadConfig(TEST_CONFIG_PATH).sessionRetentionDays, 0);
147
+ fs.writeFileSync(TEST_CONFIG_PATH, JSON.stringify({ sessionRetentionDays: "30" }));
148
+ assert.strictEqual(loadConfig(TEST_CONFIG_PATH).sessionRetentionDays, 0);
149
+ });
150
+
109
151
  it("handles partial config (missing keys use defaults)", () => {
110
152
  fs.mkdirSync(path.dirname(TEST_CONFIG_PATH), { recursive: true });
111
153
  fs.writeFileSync(TEST_CONFIG_PATH, JSON.stringify({ reviewEnabled: false }));
@@ -771,7 +771,7 @@ describe("setupBackgroundReview", () => {
771
771
  assert.strictEqual(directCalls.length, 1, "direct review should be attempted first");
772
772
  assert.strictEqual(execCalls.length, 1, "subprocess should run as fallback");
773
773
  });
774
- it("inherits the active session model and execution context for subprocess fallback", async () => {
774
+ it("inherits the active session model and cwd without coupling fallback to the agent run signal", async () => {
775
775
  const pi = createMockPi();
776
776
  setupWithDirectDeps(pi, { ok: false, appliedCount: 0, fallbackReason: "no_auth" }, {
777
777
  ...defaultConfig,
@@ -781,14 +781,15 @@ describe("setupBackgroundReview", () => {
781
781
  fireMessageEnd("user");
782
782
  fireMessageEnd("user");
783
783
  fireMessageEnd("user");
784
- const signal = new AbortController().signal;
784
+ const controller = new AbortController();
785
785
  for (let i = 0; i < 10; i++) {
786
786
  fireTurnEnd(makeBranch(10), {
787
787
  cwd: "/tmp/local-session",
788
788
  model: { provider: "local-llama", id: "local-9b" },
789
- signal,
789
+ signal: controller.signal,
790
790
  });
791
791
  }
792
+ controller.abort();
792
793
  await reviewSettledSignal.promise;
793
794
 
794
795
  assert.deepStrictEqual(logicalChildArgs(0).slice(0, 5), [
@@ -919,6 +920,161 @@ describe("setupBackgroundReview", () => {
919
920
  assert.ok(directPrompt.includes("monorepo layout"));
920
921
  });
921
922
 
923
+ it("does not forward the turn abort signal to the child review", async () => {
924
+ let capturedSignal: AbortSignal | undefined;
925
+ const childGate = Promise.withResolvers<void>();
926
+ const pi = createMockPi();
927
+ setup(pi, defaultConfig, {
928
+ execChildPrompt: async (_api, _prompt, _config, options) => {
929
+ capturedSignal = options.signal;
930
+ await childGate.promise;
931
+ return { code: 0, stdout: "Saved memory", stderr: "" };
932
+ },
933
+ });
934
+
935
+ fireMessageEnd("user");
936
+ fireMessageEnd("user");
937
+ fireMessageEnd("user");
938
+ const turnAbort = new AbortController();
939
+ for (let i = 0; i < 10; i++) {
940
+ fireTurnEnd(makeBranch(10), { signal: turnAbort.signal });
941
+ }
942
+ await settle(5);
943
+
944
+ assert.ok(capturedSignal, "child review must receive a session signal");
945
+ assert.notStrictEqual(capturedSignal, turnAbort.signal);
946
+ turnAbort.abort();
947
+ await settle(5);
948
+ assert.equal(capturedSignal.aborted, false, "turn abort must not cancel session review");
949
+
950
+ childGate.resolve();
951
+ await reviewSettledSignal.promise;
952
+ });
953
+
954
+ it("session_shutdown aborts the child signal, waits for review, and stays silent", async () => {
955
+ let capturedSignal: AbortSignal | undefined;
956
+ const childGate = Promise.withResolvers<void>();
957
+ let childStarted = 0;
958
+ const pi = createMockPi();
959
+ setup(pi, defaultConfig, {
960
+ execChildPrompt: async (_api, _prompt, _config, options) => {
961
+ childStarted++;
962
+ capturedSignal = options.signal;
963
+ await childGate.promise;
964
+ return { code: 0, stdout: "Saved memory", stderr: "" };
965
+ },
966
+ });
967
+
968
+ fireMessageEnd("user");
969
+ fireMessageEnd("user");
970
+ fireMessageEnd("user");
971
+ for (let i = 0; i < 10; i++) {
972
+ fireTurnEnd();
973
+ }
974
+ await settle(5);
975
+ assert.ok(capturedSignal);
976
+ assert.equal(capturedSignal.aborted, false);
977
+
978
+ const shutdownHandlers = handlers["session_shutdown"];
979
+ assert.ok(shutdownHandlers?.length, "session_shutdown handler must be registered");
980
+ const first = shutdownHandlers[0]({}, makeCtx());
981
+ const second = shutdownHandlers[0]({}, makeCtx());
982
+ assert.strictEqual(first, second, "repeated session_shutdown must reuse the same promise");
983
+ assert.equal(capturedSignal.aborted, true);
984
+
985
+ let shutdownDone = false;
986
+ void Promise.resolve(first).then(() => {
987
+ shutdownDone = true;
988
+ });
989
+ await settle(5);
990
+ assert.equal(shutdownDone, false, "shutdown must wait for the in-flight review");
991
+
992
+ childGate.resolve();
993
+ await first;
994
+ await reviewSettledSignal.promise;
995
+ assert.equal(shutdownDone, true);
996
+ assert.equal(
997
+ notifyCalls.some((n) => n.msg.includes("Memory auto-reviewed") || n.level === "warning"),
998
+ false,
999
+ "cancelled review must not notify success or failure",
1000
+ );
1001
+
1002
+ resetReviewSettledSignal();
1003
+ for (let i = 0; i < 10; i++) {
1004
+ fireTurnEnd();
1005
+ }
1006
+ await settle(10);
1007
+ assert.equal(childStarted, 1, "cancelled session must not start another review");
1008
+ });
1009
+
1010
+ it("does not start fallback after session cancellation", async () => {
1011
+ const directGate = Promise.withResolvers<void>();
1012
+ let childStarted = 0;
1013
+ const pi = createMockPi();
1014
+ setup(pi, { ...defaultConfig, reviewTransport: "direct" } as MemoryConfig, {
1015
+ runDirectReview: async () => {
1016
+ await directGate.promise;
1017
+ return { ok: true, appliedCount: 1 };
1018
+ },
1019
+ execChildPrompt: async () => {
1020
+ childStarted++;
1021
+ return { code: 0, stdout: "Saved memory", stderr: "" };
1022
+ },
1023
+ });
1024
+
1025
+ fireMessageEnd("user");
1026
+ fireMessageEnd("user");
1027
+ fireMessageEnd("user");
1028
+ for (let i = 0; i < 10; i++) {
1029
+ fireTurnEnd();
1030
+ }
1031
+ await settle(5);
1032
+
1033
+ const shutdown = handlers["session_shutdown"][0]({}, makeCtx());
1034
+ directGate.resolve();
1035
+ await shutdown;
1036
+ await reviewSettledSignal.promise;
1037
+
1038
+ assert.equal(childStarted, 0, "cancelled session must not start subprocess fallback");
1039
+ assert.equal(
1040
+ notifyCalls.some((n) => n.msg.includes("Memory auto-reviewed") || n.level === "warning"),
1041
+ false,
1042
+ );
1043
+ });
1044
+
1045
+ it("session_shutdown resolves after the grace bound when review ignores abort", async () => {
1046
+ let started = false;
1047
+ const pi = createMockPi();
1048
+ setup(pi, { ...defaultConfig, reviewTransport: "direct" } as MemoryConfig, {
1049
+ shutdownGraceMs: 20,
1050
+ runDirectReview: async () => {
1051
+ started = true;
1052
+ await new Promise(() => {});
1053
+ return { ok: true, appliedCount: 1 };
1054
+ },
1055
+ });
1056
+
1057
+ fireMessageEnd("user");
1058
+ fireMessageEnd("user");
1059
+ fireMessageEnd("user");
1060
+ for (let i = 0; i < 10; i++) {
1061
+ fireTurnEnd();
1062
+ }
1063
+ await settle(5);
1064
+ assert.equal(started, true);
1065
+
1066
+ const first = handlers["session_shutdown"][0]({}, makeCtx());
1067
+ const second = handlers["session_shutdown"][0]({}, makeCtx());
1068
+ assert.strictEqual(first, second);
1069
+ const startedAt = Date.now();
1070
+ await first;
1071
+ assert.ok(Date.now() - startedAt < 200, "shutdown must not wait for a review that ignores abort");
1072
+ assert.equal(
1073
+ notifyCalls.some((n) => n.msg.includes("Memory auto-reviewed") || n.level === "warning"),
1074
+ false,
1075
+ );
1076
+ });
1077
+
922
1078
  it("falls back gracefully if getBranch throws", async () => {
923
1079
  const config = { ...defaultConfig, nudgeToolCalls: 3 };
924
1080
  const pi = createMockPi();