gsd-pi 2.80.0-dev.c5f2443b3 → 2.80.0-dev.e51d2c88c

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 (434) hide show
  1. package/README.md +4 -2
  2. package/dist/resources/.managed-resources-content-hash +1 -1
  3. package/dist/resources/GSD-WORKFLOW.md +2 -2
  4. package/dist/resources/extensions/github-sync/templates.js +39 -8
  5. package/dist/resources/extensions/gsd/auto/loop.js +48 -10
  6. package/dist/resources/extensions/gsd/auto/phases.js +66 -45
  7. package/dist/resources/extensions/gsd/auto/resolve.js +17 -0
  8. package/dist/resources/extensions/gsd/auto/run-unit.js +32 -16
  9. package/dist/resources/extensions/gsd/auto-dashboard.js +51 -15
  10. package/dist/resources/extensions/gsd/auto-dispatch.js +10 -0
  11. package/dist/resources/extensions/gsd/auto-post-unit.js +10 -10
  12. package/dist/resources/extensions/gsd/auto-prompts.js +124 -2
  13. package/dist/resources/extensions/gsd/auto-recovery.js +197 -9
  14. package/dist/resources/extensions/gsd/auto-start.js +2 -3
  15. package/dist/resources/extensions/gsd/auto-supervisor.js +8 -1
  16. package/dist/resources/extensions/gsd/auto-timeout-recovery.js +2 -2
  17. package/dist/resources/extensions/gsd/auto.js +75 -5
  18. package/dist/resources/extensions/gsd/bootstrap/agent-end-recovery.js +36 -3
  19. package/dist/resources/extensions/gsd/bootstrap/exec-tools.js +27 -20
  20. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +32 -1
  21. package/dist/resources/extensions/gsd/bootstrap/write-gate.js +129 -1
  22. package/dist/resources/extensions/gsd/clean-root-preflight.js +42 -4
  23. package/dist/resources/extensions/gsd/commands/dispatcher.js +5 -0
  24. package/dist/resources/extensions/gsd/commands-extract-learnings.js +17 -12
  25. package/dist/resources/extensions/gsd/context-budget.js +37 -2
  26. package/dist/resources/extensions/gsd/crash-recovery.js +56 -10
  27. package/dist/resources/extensions/gsd/custom-workflow-engine.js +22 -2
  28. package/dist/resources/extensions/gsd/db/unit-dispatches.js +39 -0
  29. package/dist/resources/extensions/gsd/db-base-schema.js +18 -2
  30. package/dist/resources/extensions/gsd/db-migration-steps.js +22 -0
  31. package/dist/resources/extensions/gsd/detection.js +106 -0
  32. package/dist/resources/extensions/gsd/graph.js +9 -3
  33. package/dist/resources/extensions/gsd/gsd-db.js +146 -13
  34. package/dist/resources/extensions/gsd/guided-flow.js +82 -16
  35. package/dist/resources/extensions/gsd/memory-store.js +69 -12
  36. package/dist/resources/extensions/gsd/migrate/command.js +40 -1
  37. package/dist/resources/extensions/gsd/migration-auto-check.js +87 -0
  38. package/dist/resources/extensions/gsd/planning-path-scope.js +26 -0
  39. package/dist/resources/extensions/gsd/pr-evidence.js +57 -16
  40. package/dist/resources/extensions/gsd/prompt-loader.js +28 -2
  41. package/dist/resources/extensions/gsd/prompts/complete-milestone.md +19 -19
  42. package/dist/resources/extensions/gsd/prompts/parallel-research-slices.md +1 -1
  43. package/dist/resources/extensions/gsd/prompts/plan-milestone.md +3 -1
  44. package/dist/resources/extensions/gsd/prompts/plan-slice.md +1 -1
  45. package/dist/resources/extensions/gsd/prompts/quick-task.md +1 -5
  46. package/dist/resources/extensions/gsd/prompts/validate-milestone.md +2 -2
  47. package/dist/resources/extensions/gsd/quick.js +34 -2
  48. package/dist/resources/extensions/gsd/safety/evidence-collector.js +10 -2
  49. package/dist/resources/extensions/gsd/tools/context-mode-tool-result.js +15 -0
  50. package/dist/resources/extensions/gsd/tools/exec-search-tool.js +5 -0
  51. package/dist/resources/extensions/gsd/tools/exec-tool.js +3 -15
  52. package/dist/resources/extensions/gsd/tools/memory-tools.js +1 -0
  53. package/dist/resources/extensions/gsd/tools/plan-slice.js +9 -0
  54. package/dist/resources/extensions/gsd/tools/plan-task.js +9 -0
  55. package/dist/resources/extensions/gsd/tools/resume-tool.js +5 -0
  56. package/dist/resources/extensions/gsd/tools/workflow-tool-executors.js +1 -1
  57. package/dist/resources/extensions/gsd/unit-context-composer.js +12 -3
  58. package/dist/resources/extensions/gsd/unit-runtime.js +22 -0
  59. package/dist/resources/extensions/gsd/working-output-messages.js +64 -0
  60. package/dist/resources/extensions/gsd/worktree-manager.js +16 -14
  61. package/dist/tsconfig.extensions.tsbuildinfo +1 -1
  62. package/dist/web/standalone/.next/BUILD_ID +1 -1
  63. package/dist/web/standalone/.next/app-path-routes-manifest.json +11 -11
  64. package/dist/web/standalone/.next/build-manifest.json +3 -3
  65. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  66. package/dist/web/standalone/.next/react-loadable-manifest.json +1 -1
  67. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  68. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  69. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  70. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  71. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  72. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  73. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  74. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  75. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  76. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  77. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  78. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  79. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  80. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  81. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  82. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  83. package/dist/web/standalone/.next/server/app/api/onboarding/route.js +1 -1
  84. package/dist/web/standalone/.next/server/app/index.html +1 -1
  85. package/dist/web/standalone/.next/server/app/index.rsc +1 -1
  86. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  87. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
  88. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  89. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  90. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  91. package/dist/web/standalone/.next/server/app-paths-manifest.json +11 -11
  92. package/dist/web/standalone/.next/server/chunks/6897.js +3 -3
  93. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  94. package/dist/web/standalone/.next/server/middleware-react-loadable-manifest.js +1 -1
  95. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  96. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  97. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  98. package/dist/web/standalone/.next/static/chunks/{8336.6f6f30e410419aff.js → 8336.631939fb583761fa.js} +1 -1
  99. package/dist/web/standalone/.next/static/chunks/{webpack-d82dbee6356c1733.js → webpack-0481f1221120a7c6.js} +1 -1
  100. package/package.json +12 -8
  101. package/packages/contracts/package.json +1 -1
  102. package/packages/mcp-server/dist/workflow-tools.d.ts.map +1 -1
  103. package/packages/mcp-server/dist/workflow-tools.js +22 -17
  104. package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
  105. package/packages/mcp-server/src/workflow-tools.test.ts +75 -2
  106. package/packages/mcp-server/src/workflow-tools.ts +30 -16
  107. package/packages/mcp-server/tsconfig.tsbuildinfo +1 -1
  108. package/packages/native/tsconfig.tsbuildinfo +1 -1
  109. package/packages/pi-ai/dist/models/fake-model.d.ts +12 -0
  110. package/packages/pi-ai/dist/models/fake-model.d.ts.map +1 -0
  111. package/packages/pi-ai/dist/models/fake-model.js +27 -0
  112. package/packages/pi-ai/dist/models/fake-model.js.map +1 -0
  113. package/packages/pi-ai/dist/models/index.d.ts.map +1 -1
  114. package/packages/pi-ai/dist/models/index.js +8 -0
  115. package/packages/pi-ai/dist/models/index.js.map +1 -1
  116. package/packages/pi-ai/dist/providers/fake.d.ts +42 -0
  117. package/packages/pi-ai/dist/providers/fake.d.ts.map +1 -0
  118. package/packages/pi-ai/dist/providers/fake.js +319 -0
  119. package/packages/pi-ai/dist/providers/fake.js.map +1 -0
  120. package/packages/pi-ai/dist/providers/register-builtins.d.ts.map +1 -1
  121. package/packages/pi-ai/dist/providers/register-builtins.js +24 -0
  122. package/packages/pi-ai/dist/providers/register-builtins.js.map +1 -1
  123. package/packages/pi-ai/src/models/fake-model.ts +30 -0
  124. package/packages/pi-ai/src/models/index.ts +9 -0
  125. package/packages/pi-ai/src/providers/fake.ts +376 -0
  126. package/packages/pi-ai/src/providers/register-builtins.ts +23 -0
  127. package/packages/pi-ai/tsconfig.tsbuildinfo +1 -1
  128. package/packages/pi-coding-agent/dist/core/agent-session-abort-order.test.js +32 -0
  129. package/packages/pi-coding-agent/dist/core/agent-session-abort-order.test.js.map +1 -1
  130. package/packages/pi-coding-agent/dist/core/agent-session.d.ts.map +1 -1
  131. package/packages/pi-coding-agent/dist/core/agent-session.js +8 -0
  132. package/packages/pi-coding-agent/dist/core/agent-session.js.map +1 -1
  133. package/packages/pi-coding-agent/dist/core/chat-controller-ordering.test.js +76 -0
  134. package/packages/pi-coding-agent/dist/core/chat-controller-ordering.test.js.map +1 -1
  135. package/packages/pi-coding-agent/dist/core/compaction/compaction.d.ts +11 -0
  136. package/packages/pi-coding-agent/dist/core/compaction/compaction.d.ts.map +1 -1
  137. package/packages/pi-coding-agent/dist/core/compaction/compaction.js +9 -0
  138. package/packages/pi-coding-agent/dist/core/compaction/compaction.js.map +1 -1
  139. package/packages/pi-coding-agent/dist/core/compaction-threshold.test.d.ts +2 -0
  140. package/packages/pi-coding-agent/dist/core/compaction-threshold.test.d.ts.map +1 -0
  141. package/packages/pi-coding-agent/dist/core/compaction-threshold.test.js +103 -0
  142. package/packages/pi-coding-agent/dist/core/compaction-threshold.test.js.map +1 -0
  143. package/packages/pi-coding-agent/dist/core/db-snapshot.d.ts +15 -0
  144. package/packages/pi-coding-agent/dist/core/db-snapshot.d.ts.map +1 -0
  145. package/packages/pi-coding-agent/dist/core/db-snapshot.js +66 -0
  146. package/packages/pi-coding-agent/dist/core/db-snapshot.js.map +1 -0
  147. package/packages/pi-coding-agent/dist/core/db-snapshot.test.d.ts +2 -0
  148. package/packages/pi-coding-agent/dist/core/db-snapshot.test.d.ts.map +1 -0
  149. package/packages/pi-coding-agent/dist/core/db-snapshot.test.js +24 -0
  150. package/packages/pi-coding-agent/dist/core/db-snapshot.test.js.map +1 -0
  151. package/packages/pi-coding-agent/dist/core/extensions/runner.d.ts +3 -0
  152. package/packages/pi-coding-agent/dist/core/extensions/runner.d.ts.map +1 -1
  153. package/packages/pi-coding-agent/dist/core/extensions/runner.js +17 -1
  154. package/packages/pi-coding-agent/dist/core/extensions/runner.js.map +1 -1
  155. package/packages/pi-coding-agent/dist/core/extensions/runner.test.js +99 -0
  156. package/packages/pi-coding-agent/dist/core/extensions/runner.test.js.map +1 -1
  157. package/packages/pi-coding-agent/dist/core/extensions/types.d.ts +7 -0
  158. package/packages/pi-coding-agent/dist/core/extensions/types.d.ts.map +1 -1
  159. package/packages/pi-coding-agent/dist/core/extensions/types.js.map +1 -1
  160. package/packages/pi-coding-agent/dist/core/model-registry.d.ts.map +1 -1
  161. package/packages/pi-coding-agent/dist/core/model-registry.js +5 -0
  162. package/packages/pi-coding-agent/dist/core/model-registry.js.map +1 -1
  163. package/packages/pi-coding-agent/dist/core/settings-manager.d.ts +24 -0
  164. package/packages/pi-coding-agent/dist/core/settings-manager.d.ts.map +1 -1
  165. package/packages/pi-coding-agent/dist/core/settings-manager.js +33 -0
  166. package/packages/pi-coding-agent/dist/core/settings-manager.js.map +1 -1
  167. package/packages/pi-coding-agent/dist/core/slash-commands.d.ts.map +1 -1
  168. package/packages/pi-coding-agent/dist/core/slash-commands.js +1 -0
  169. package/packages/pi-coding-agent/dist/core/slash-commands.js.map +1 -1
  170. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/chat-frame-compaction-tone.test.js +6 -4
  171. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/chat-frame-compaction-tone.test.js.map +1 -1
  172. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/tool-execution.test.js +54 -15
  173. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/tool-execution.test.js.map +1 -1
  174. package/packages/pi-coding-agent/dist/modes/interactive/components/adaptive-layout.d.ts +26 -0
  175. package/packages/pi-coding-agent/dist/modes/interactive/components/adaptive-layout.d.ts.map +1 -0
  176. package/packages/pi-coding-agent/dist/modes/interactive/components/adaptive-layout.js +112 -0
  177. package/packages/pi-coding-agent/dist/modes/interactive/components/adaptive-layout.js.map +1 -0
  178. package/packages/pi-coding-agent/dist/modes/interactive/components/adaptive-layout.test.d.ts +2 -0
  179. package/packages/pi-coding-agent/dist/modes/interactive/components/adaptive-layout.test.d.ts.map +1 -0
  180. package/packages/pi-coding-agent/dist/modes/interactive/components/adaptive-layout.test.js +51 -0
  181. package/packages/pi-coding-agent/dist/modes/interactive/components/adaptive-layout.test.js.map +1 -0
  182. package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
  183. package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.js.map +1 -1
  184. package/packages/pi-coding-agent/dist/modes/interactive/components/chat-frame.d.ts.map +1 -1
  185. package/packages/pi-coding-agent/dist/modes/interactive/components/chat-frame.js +10 -9
  186. package/packages/pi-coding-agent/dist/modes/interactive/components/chat-frame.js.map +1 -1
  187. package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.d.ts +3 -0
  188. package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  189. package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.js +11 -0
  190. package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.js.map +1 -1
  191. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-card-cleanup-and-success-runtime.test.js +7 -6
  192. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-card-cleanup-and-success-runtime.test.js.map +1 -1
  193. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts +17 -0
  194. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  195. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js +109 -17
  196. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js.map +1 -1
  197. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
  198. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js +69 -2
  199. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
  200. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.test.js +93 -1
  201. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.test.js.map +1 -1
  202. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.test.js +1 -0
  203. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.test.js.map +1 -1
  204. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.d.ts +1 -0
  205. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.d.ts.map +1 -1
  206. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.js.map +1 -1
  207. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts +3 -0
  208. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  209. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +26 -0
  210. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
  211. package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.d.ts.map +1 -1
  212. package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.js +20 -0
  213. package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.js.map +1 -1
  214. package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.test.d.ts +2 -0
  215. package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.test.d.ts.map +1 -0
  216. package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.test.js +79 -0
  217. package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.test.js.map +1 -0
  218. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme-schema.d.ts +12 -0
  219. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme-schema.d.ts.map +1 -1
  220. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme-schema.js +13 -0
  221. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme-schema.js.map +1 -1
  222. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme.d.ts +1 -1
  223. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  224. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme.js +18 -1
  225. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme.js.map +1 -1
  226. package/packages/pi-coding-agent/dist/modes/interactive/theme/themes.d.ts.map +1 -1
  227. package/packages/pi-coding-agent/dist/modes/interactive/theme/themes.js +36 -27
  228. package/packages/pi-coding-agent/dist/modes/interactive/theme/themes.js.map +1 -1
  229. package/packages/pi-coding-agent/dist/modes/interactive/tui-mode.d.ts +11 -0
  230. package/packages/pi-coding-agent/dist/modes/interactive/tui-mode.d.ts.map +1 -0
  231. package/packages/pi-coding-agent/dist/modes/interactive/tui-mode.js +18 -0
  232. package/packages/pi-coding-agent/dist/modes/interactive/tui-mode.js.map +1 -0
  233. package/packages/pi-coding-agent/dist/modes/interactive/tui-mode.test.d.ts +2 -0
  234. package/packages/pi-coding-agent/dist/modes/interactive/tui-mode.test.d.ts.map +1 -0
  235. package/packages/pi-coding-agent/dist/modes/interactive/tui-mode.test.js +48 -0
  236. package/packages/pi-coding-agent/dist/modes/interactive/tui-mode.test.js.map +1 -0
  237. package/packages/pi-coding-agent/dist/resources/extensions/memory/storage-safety-guard.test.d.ts +2 -0
  238. package/packages/pi-coding-agent/dist/resources/extensions/memory/storage-safety-guard.test.d.ts.map +1 -0
  239. package/packages/pi-coding-agent/dist/resources/extensions/memory/storage-safety-guard.test.js +10 -0
  240. package/packages/pi-coding-agent/dist/resources/extensions/memory/storage-safety-guard.test.js.map +1 -0
  241. package/packages/pi-coding-agent/dist/resources/extensions/memory/storage.d.ts.map +1 -1
  242. package/packages/pi-coding-agent/dist/resources/extensions/memory/storage.js +3 -2
  243. package/packages/pi-coding-agent/dist/resources/extensions/memory/storage.js.map +1 -1
  244. package/packages/pi-coding-agent/src/core/agent-session-abort-order.test.ts +36 -0
  245. package/packages/pi-coding-agent/src/core/agent-session.ts +8 -0
  246. package/packages/pi-coding-agent/src/core/chat-controller-ordering.test.ts +89 -0
  247. package/packages/pi-coding-agent/src/core/compaction/compaction.ts +18 -0
  248. package/packages/pi-coding-agent/src/core/compaction-threshold.test.ts +121 -0
  249. package/packages/pi-coding-agent/src/core/db-snapshot.test.ts +32 -0
  250. package/packages/pi-coding-agent/src/core/db-snapshot.ts +66 -0
  251. package/packages/pi-coding-agent/src/core/extensions/runner.test.ts +110 -0
  252. package/packages/pi-coding-agent/src/core/extensions/runner.ts +19 -1
  253. package/packages/pi-coding-agent/src/core/extensions/types.ts +7 -0
  254. package/packages/pi-coding-agent/src/core/model-registry.ts +4 -0
  255. package/packages/pi-coding-agent/src/core/settings-manager.ts +51 -1
  256. package/packages/pi-coding-agent/src/core/slash-commands.ts +1 -0
  257. package/packages/pi-coding-agent/src/modes/interactive/components/__tests__/chat-frame-compaction-tone.test.ts +7 -5
  258. package/packages/pi-coding-agent/src/modes/interactive/components/__tests__/tool-execution.test.ts +78 -15
  259. package/packages/pi-coding-agent/src/modes/interactive/components/adaptive-layout.test.ts +59 -0
  260. package/packages/pi-coding-agent/src/modes/interactive/components/adaptive-layout.ts +160 -0
  261. package/packages/pi-coding-agent/src/modes/interactive/components/assistant-message.ts +1 -0
  262. package/packages/pi-coding-agent/src/modes/interactive/components/chat-frame.ts +10 -9
  263. package/packages/pi-coding-agent/src/modes/interactive/components/settings-selector.ts +15 -0
  264. package/packages/pi-coding-agent/src/modes/interactive/components/tool-card-cleanup-and-success-runtime.test.ts +10 -9
  265. package/packages/pi-coding-agent/src/modes/interactive/components/tool-execution.ts +122 -17
  266. package/packages/pi-coding-agent/src/modes/interactive/controllers/chat-controller.test.ts +99 -1
  267. package/packages/pi-coding-agent/src/modes/interactive/controllers/chat-controller.ts +92 -3
  268. package/packages/pi-coding-agent/src/modes/interactive/controllers/input-controller.test.ts +1 -0
  269. package/packages/pi-coding-agent/src/modes/interactive/interactive-mode-state.ts +1 -1
  270. package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +28 -0
  271. package/packages/pi-coding-agent/src/modes/interactive/slash-command-handlers.test.ts +95 -0
  272. package/packages/pi-coding-agent/src/modes/interactive/slash-command-handlers.ts +24 -1
  273. package/packages/pi-coding-agent/src/modes/interactive/theme/theme-schema.ts +13 -0
  274. package/packages/pi-coding-agent/src/modes/interactive/theme/theme.ts +32 -2
  275. package/packages/pi-coding-agent/src/modes/interactive/theme/themes.ts +36 -27
  276. package/packages/pi-coding-agent/src/modes/interactive/tui-mode.test.ts +65 -0
  277. package/packages/pi-coding-agent/src/modes/interactive/tui-mode.ts +29 -0
  278. package/packages/pi-coding-agent/src/resources/extensions/memory/storage-safety-guard.test.ts +14 -0
  279. package/packages/pi-coding-agent/src/resources/extensions/memory/storage.ts +3 -2
  280. package/packages/pi-coding-agent/tsconfig.tsbuildinfo +1 -1
  281. package/packages/pi-tui/dist/__tests__/style.test.d.ts +2 -0
  282. package/packages/pi-tui/dist/__tests__/style.test.d.ts.map +1 -0
  283. package/packages/pi-tui/dist/__tests__/style.test.js +63 -0
  284. package/packages/pi-tui/dist/__tests__/style.test.js.map +1 -0
  285. package/packages/pi-tui/dist/__tests__/tui.test.js +24 -3
  286. package/packages/pi-tui/dist/__tests__/tui.test.js.map +1 -1
  287. package/packages/pi-tui/dist/index.d.ts +1 -0
  288. package/packages/pi-tui/dist/index.d.ts.map +1 -1
  289. package/packages/pi-tui/dist/index.js +2 -0
  290. package/packages/pi-tui/dist/index.js.map +1 -1
  291. package/packages/pi-tui/dist/style.d.ts +41 -0
  292. package/packages/pi-tui/dist/style.d.ts.map +1 -0
  293. package/packages/pi-tui/dist/style.js +158 -0
  294. package/packages/pi-tui/dist/style.js.map +1 -0
  295. package/packages/pi-tui/dist/tui.d.ts +0 -1
  296. package/packages/pi-tui/dist/tui.d.ts.map +1 -1
  297. package/packages/pi-tui/dist/tui.js +21 -16
  298. package/packages/pi-tui/dist/tui.js.map +1 -1
  299. package/packages/pi-tui/src/__tests__/style.test.ts +76 -0
  300. package/packages/pi-tui/src/__tests__/tui.test.ts +29 -3
  301. package/packages/pi-tui/src/index.ts +9 -0
  302. package/packages/pi-tui/src/style.ts +225 -0
  303. package/packages/pi-tui/src/tui.ts +23 -16
  304. package/packages/pi-tui/tsconfig.tsbuildinfo +1 -1
  305. package/pkg/dist/modes/interactive/theme/theme-schema.d.ts +12 -0
  306. package/pkg/dist/modes/interactive/theme/theme-schema.d.ts.map +1 -1
  307. package/pkg/dist/modes/interactive/theme/theme-schema.js +13 -0
  308. package/pkg/dist/modes/interactive/theme/theme-schema.js.map +1 -1
  309. package/pkg/dist/modes/interactive/theme/theme.d.ts +1 -1
  310. package/pkg/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  311. package/pkg/dist/modes/interactive/theme/theme.js +18 -1
  312. package/pkg/dist/modes/interactive/theme/theme.js.map +1 -1
  313. package/pkg/dist/modes/interactive/theme/themes.d.ts.map +1 -1
  314. package/pkg/dist/modes/interactive/theme/themes.js +36 -27
  315. package/pkg/dist/modes/interactive/theme/themes.js.map +1 -1
  316. package/src/resources/GSD-WORKFLOW.md +2 -2
  317. package/src/resources/extensions/github-sync/templates.ts +38 -8
  318. package/src/resources/extensions/github-sync/tests/inline-code.test.ts +66 -0
  319. package/src/resources/extensions/gsd/auto/loop-deps.ts +1 -0
  320. package/src/resources/extensions/gsd/auto/loop.ts +67 -18
  321. package/src/resources/extensions/gsd/auto/phases.ts +77 -48
  322. package/src/resources/extensions/gsd/auto/resolve.ts +23 -1
  323. package/src/resources/extensions/gsd/auto/run-unit.ts +42 -15
  324. package/src/resources/extensions/gsd/auto-dashboard.ts +57 -8
  325. package/src/resources/extensions/gsd/auto-dispatch.ts +17 -0
  326. package/src/resources/extensions/gsd/auto-post-unit.ts +10 -10
  327. package/src/resources/extensions/gsd/auto-prompts.ts +133 -2
  328. package/src/resources/extensions/gsd/auto-recovery.ts +207 -7
  329. package/src/resources/extensions/gsd/auto-start.ts +7 -6
  330. package/src/resources/extensions/gsd/auto-supervisor.ts +7 -0
  331. package/src/resources/extensions/gsd/auto-timeout-recovery.ts +2 -2
  332. package/src/resources/extensions/gsd/auto.ts +90 -4
  333. package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +37 -2
  334. package/src/resources/extensions/gsd/bootstrap/exec-tools.ts +27 -19
  335. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +39 -1
  336. package/src/resources/extensions/gsd/bootstrap/write-gate.ts +135 -1
  337. package/src/resources/extensions/gsd/clean-root-preflight.ts +41 -3
  338. package/src/resources/extensions/gsd/commands/dispatcher.ts +6 -0
  339. package/src/resources/extensions/gsd/commands-extract-learnings.ts +17 -12
  340. package/src/resources/extensions/gsd/context-budget.ts +44 -2
  341. package/src/resources/extensions/gsd/crash-recovery.ts +67 -10
  342. package/src/resources/extensions/gsd/custom-workflow-engine.ts +24 -1
  343. package/src/resources/extensions/gsd/db/unit-dispatches.ts +41 -0
  344. package/src/resources/extensions/gsd/db-base-schema.ts +19 -2
  345. package/src/resources/extensions/gsd/db-migration-steps.ts +25 -0
  346. package/src/resources/extensions/gsd/detection.ts +128 -0
  347. package/src/resources/extensions/gsd/graph.ts +12 -5
  348. package/src/resources/extensions/gsd/gsd-db.ts +168 -13
  349. package/src/resources/extensions/gsd/guided-flow.ts +98 -16
  350. package/src/resources/extensions/gsd/memory-store.ts +77 -12
  351. package/src/resources/extensions/gsd/migrate/command.ts +47 -1
  352. package/src/resources/extensions/gsd/migration-auto-check.ts +129 -0
  353. package/src/resources/extensions/gsd/planning-path-scope.ts +35 -0
  354. package/src/resources/extensions/gsd/pr-evidence.ts +63 -5
  355. package/src/resources/extensions/gsd/preferences-types.ts +1 -1
  356. package/src/resources/extensions/gsd/prompt-loader.ts +27 -2
  357. package/src/resources/extensions/gsd/prompts/complete-milestone.md +19 -19
  358. package/src/resources/extensions/gsd/prompts/parallel-research-slices.md +1 -1
  359. package/src/resources/extensions/gsd/prompts/plan-milestone.md +3 -1
  360. package/src/resources/extensions/gsd/prompts/plan-slice.md +1 -1
  361. package/src/resources/extensions/gsd/prompts/quick-task.md +1 -5
  362. package/src/resources/extensions/gsd/prompts/validate-milestone.md +2 -2
  363. package/src/resources/extensions/gsd/quick.ts +37 -2
  364. package/src/resources/extensions/gsd/safety/evidence-collector.ts +11 -2
  365. package/src/resources/extensions/gsd/tests/auto-abort-pause-regression.test.ts +7 -1
  366. package/src/resources/extensions/gsd/tests/auto-dashboard.test.ts +33 -0
  367. package/src/resources/extensions/gsd/tests/auto-loop.test.ts +155 -5
  368. package/src/resources/extensions/gsd/tests/auto-phases-lifecycle.test.ts +56 -13
  369. package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +184 -2
  370. package/src/resources/extensions/gsd/tests/clean-root-preflight.test.ts +88 -2
  371. package/src/resources/extensions/gsd/tests/commands-extract-learnings.test.ts +9 -0
  372. package/src/resources/extensions/gsd/tests/compaction-snapshot.test.ts +14 -1
  373. package/src/resources/extensions/gsd/tests/context-budget.test.ts +10 -1
  374. package/src/resources/extensions/gsd/tests/crash-handler-secondary.test.ts +55 -0
  375. package/src/resources/extensions/gsd/tests/crash-recovery-via-db.test.ts +22 -0
  376. package/src/resources/extensions/gsd/tests/custom-engine-loop-integration.test.ts +112 -6
  377. package/src/resources/extensions/gsd/tests/custom-workflow-engine.test.ts +40 -2
  378. package/src/resources/extensions/gsd/tests/db-migration-steps.integration.test.ts +428 -0
  379. package/src/resources/extensions/gsd/tests/db-schema-metadata.test.ts +2 -2
  380. package/src/resources/extensions/gsd/tests/detection.test.ts +140 -0
  381. package/src/resources/extensions/gsd/tests/dispatch-rule-coverage.test.ts +313 -0
  382. package/src/resources/extensions/gsd/tests/exec-history.test.ts +15 -0
  383. package/src/resources/extensions/gsd/tests/exec-sandbox.test.ts +65 -0
  384. package/src/resources/extensions/gsd/tests/fixtures/pr-body/commands-ship-basic.md +52 -0
  385. package/src/resources/extensions/gsd/tests/fixtures/pr-body/commands-ship-empty-optionals.md +42 -0
  386. package/src/resources/extensions/gsd/tests/fixtures/pr-body/swarm-lane-no-blockers.md +55 -0
  387. package/src/resources/extensions/gsd/tests/fixtures/pr-body/swarm-lane-with-blockers.md +60 -0
  388. package/src/resources/extensions/gsd/tests/graph-operations.test.ts +10 -0
  389. package/src/resources/extensions/gsd/tests/gsd-db.test.ts +44 -0
  390. package/src/resources/extensions/gsd/tests/has-pending-deep-stage.test.ts +33 -1
  391. package/src/resources/extensions/gsd/tests/journal-integration.test.ts +234 -0
  392. package/src/resources/extensions/gsd/tests/memory-decay-factor.test.ts +90 -0
  393. package/src/resources/extensions/gsd/tests/migrate-writer-integration.test.ts +48 -0
  394. package/src/resources/extensions/gsd/tests/migration-auto-check.test.ts +127 -0
  395. package/src/resources/extensions/gsd/tests/plan-slice.test.ts +50 -0
  396. package/src/resources/extensions/gsd/tests/plan-task.test.ts +21 -0
  397. package/src/resources/extensions/gsd/tests/pr-evidence-equivalence.test.ts +102 -0
  398. package/src/resources/extensions/gsd/tests/pr-evidence-hardening.test.ts +165 -0
  399. package/src/resources/extensions/gsd/tests/prompt-path-audit.test.ts +40 -0
  400. package/src/resources/extensions/gsd/tests/prompt-step-ordering.test.ts +19 -0
  401. package/src/resources/extensions/gsd/tests/quick-external-gsd.test.ts +40 -0
  402. package/src/resources/extensions/gsd/tests/right-sized-workflow-prompts.test.ts +192 -0
  403. package/src/resources/extensions/gsd/tests/safety-harness-false-positives.test.ts +29 -0
  404. package/src/resources/extensions/gsd/tests/schema-v27-v28-sequence.test.ts +156 -0
  405. package/src/resources/extensions/gsd/tests/signal-handlers.test.ts +27 -0
  406. package/src/resources/extensions/gsd/tests/smart-entry-complete.test.ts +38 -0
  407. package/src/resources/extensions/gsd/tests/stalled-tool-recovery.test.ts +49 -1
  408. package/src/resources/extensions/gsd/tests/start-auto-detached.test.ts +101 -2
  409. package/src/resources/extensions/gsd/tests/status-db-open.test.ts +9 -0
  410. package/src/resources/extensions/gsd/tests/unit-context-composer.test.ts +136 -4
  411. package/src/resources/extensions/gsd/tests/unit-dispatches.test.ts +30 -0
  412. package/src/resources/extensions/gsd/tests/unit-runtime.test.ts +37 -0
  413. package/src/resources/extensions/gsd/tests/uok-plan-v2-wiring.test.ts +1 -1
  414. package/src/resources/extensions/gsd/tests/workflow-tool-executors.test.ts +3 -0
  415. package/src/resources/extensions/gsd/tests/working-output-messages.test.ts +93 -0
  416. package/src/resources/extensions/gsd/tests/worktree-health-dispatch.test.ts +37 -6
  417. package/src/resources/extensions/gsd/tests/worktree-manager.test.ts +7 -0
  418. package/src/resources/extensions/gsd/tests/worktree-nested-git-safety.test.ts +9 -2
  419. package/src/resources/extensions/gsd/tests/worktree-write-gate.test.ts +179 -0
  420. package/src/resources/extensions/gsd/tools/context-mode-tool-result.ts +25 -0
  421. package/src/resources/extensions/gsd/tools/exec-search-tool.ts +7 -7
  422. package/src/resources/extensions/gsd/tools/exec-tool.ts +4 -23
  423. package/src/resources/extensions/gsd/tools/memory-tools.ts +1 -0
  424. package/src/resources/extensions/gsd/tools/plan-slice.ts +13 -0
  425. package/src/resources/extensions/gsd/tools/plan-task.ts +10 -0
  426. package/src/resources/extensions/gsd/tools/resume-tool.ts +7 -7
  427. package/src/resources/extensions/gsd/tools/workflow-tool-executors.ts +1 -1
  428. package/src/resources/extensions/gsd/unit-context-composer.ts +19 -4
  429. package/src/resources/extensions/gsd/unit-runtime.ts +25 -0
  430. package/src/resources/extensions/gsd/working-output-messages.ts +120 -0
  431. package/src/resources/extensions/gsd/worktree-manager.ts +15 -4
  432. package/packages/contracts/tsconfig.tsbuildinfo +0 -1
  433. /package/dist/web/standalone/.next/static/{bQDK5_LtkGVS64AirQgQG → 8F5YpnZNBaooIWGF4GBV3}/_buildManifest.js +0 -0
  434. /package/dist/web/standalone/.next/static/{bQDK5_LtkGVS64AirQgQG → 8F5YpnZNBaooIWGF4GBV3}/_ssgManifest.js +0 -0
@@ -22,6 +22,7 @@
22
22
  // intentionally independent store for cross-worktree claim races and is
23
23
  // excluded from this invariant.
24
24
  import { createRequire } from "node:module";
25
+ import { createHash } from "node:crypto";
25
26
  import { existsSync, copyFileSync, mkdirSync, realpathSync } from "node:fs";
26
27
  import { dirname } from "node:path";
27
28
  import { GSDError, GSD_STALE_STATE } from "./errors.js";
@@ -36,7 +37,7 @@ import { rowToActiveDecision, rowToActiveRequirement, rowToDecision, rowToRequir
36
37
  import { rowToGate } from "./db-gate-rows.js";
37
38
  import { rowToArtifact, rowToMilestone } from "./db-milestone-artifact-rows.js";
38
39
  import { backupDatabaseBeforeMigration } from "./db-migration-backup.js";
39
- import { applyMigrationV2Artifacts, applyMigrationV3Memories, applyMigrationV4DecisionMadeBy, applyMigrationV5HierarchyTables, applyMigrationV6SliceSummaries, applyMigrationV7Dependencies, applyMigrationV8PlanningFields, applyMigrationV9Ordering, applyMigrationV10ReplanTrigger, applyMigrationV11TaskPlanning, applyMigrationV12QualityGates, applyMigrationV13HotPathIndexes, applyMigrationV14SliceDependencies, applyMigrationV15AuditTables, applyMigrationV16EscalationSource, applyMigrationV17TaskEscalation, applyMigrationV18MemorySources, applyMigrationV19MemoryFts, applyMigrationV20MemoryRelations, applyMigrationV21StructuredMemories, applyMigrationV22QualityGateRepair, applyMigrationV23MilestoneQueue, } from "./db-migration-steps.js";
40
+ import { applyMigrationV2Artifacts, applyMigrationV3Memories, applyMigrationV4DecisionMadeBy, applyMigrationV5HierarchyTables, applyMigrationV6SliceSummaries, applyMigrationV7Dependencies, applyMigrationV8PlanningFields, applyMigrationV9Ordering, applyMigrationV10ReplanTrigger, applyMigrationV11TaskPlanning, applyMigrationV12QualityGates, applyMigrationV13HotPathIndexes, applyMigrationV14SliceDependencies, applyMigrationV15AuditTables, applyMigrationV16EscalationSource, applyMigrationV17TaskEscalation, applyMigrationV18MemorySources, applyMigrationV19MemoryFts, applyMigrationV20MemoryRelations, applyMigrationV21StructuredMemories, applyMigrationV22QualityGateRepair, applyMigrationV23MilestoneQueue, applyMigrationV26MilestoneCommitAttributions, applyMigrationV27ArtifactHash, applyMigrationV28MemoryLastHitAt, } from "./db-migration-steps.js";
40
41
  import { isMemoriesFtsAvailableSchema, tryCreateMemoriesFtsSchema } from "./db-memory-fts-schema.js";
41
42
  import { createDbOpenState } from "./db-open-state.js";
42
43
  import { createRuntimeKvTableV25 } from "./db-runtime-kv-schema.js";
@@ -52,7 +53,7 @@ const providerLoader = createSqliteProviderLoader({
52
53
  nodeVersion: process.versions.node,
53
54
  writeStderr: (message) => process.stderr.write(message),
54
55
  });
55
- export const SCHEMA_VERSION = 25;
56
+ export const SCHEMA_VERSION = 28;
56
57
  function initSchema(db, fileBacked) {
57
58
  if (fileBacked)
58
59
  db.exec("PRAGMA journal_mode=WAL");
@@ -246,6 +247,18 @@ function migrateSchema(db) {
246
247
  createRuntimeKvTableV25(db);
247
248
  recordSchemaVersion(db, 25);
248
249
  }
250
+ if (currentVersion < 26) {
251
+ applyMigrationV26MilestoneCommitAttributions(db);
252
+ recordSchemaVersion(db, 26);
253
+ }
254
+ if (currentVersion < 27) {
255
+ applyMigrationV27ArtifactHash(db);
256
+ recordSchemaVersion(db, 27);
257
+ }
258
+ if (currentVersion < 28) {
259
+ applyMigrationV28MemoryLastHitAt(db);
260
+ recordSchemaVersion(db, 28);
261
+ }
249
262
  db.exec("COMMIT");
250
263
  }
251
264
  catch (err) {
@@ -817,8 +830,9 @@ export function clearArtifacts() {
817
830
  export function insertArtifact(a) {
818
831
  if (!currentDb)
819
832
  throw new GSDError(GSD_STALE_STATE, "gsd-db: No database open");
820
- currentDb.prepare(`INSERT OR REPLACE INTO artifacts (path, artifact_type, milestone_id, slice_id, task_id, full_content, imported_at)
821
- VALUES (:path, :artifact_type, :milestone_id, :slice_id, :task_id, :full_content, :imported_at)`).run({
833
+ const contentHash = createHash("sha256").update(a.full_content).digest("hex");
834
+ currentDb.prepare(`INSERT OR REPLACE INTO artifacts (path, artifact_type, milestone_id, slice_id, task_id, full_content, imported_at, content_hash)
835
+ VALUES (:path, :artifact_type, :milestone_id, :slice_id, :task_id, :full_content, :imported_at, :content_hash)`).run({
822
836
  ":path": a.path,
823
837
  ":artifact_type": a.artifact_type,
824
838
  ":milestone_id": a.milestone_id,
@@ -826,6 +840,7 @@ export function insertArtifact(a) {
826
840
  ":task_id": a.task_id,
827
841
  ":full_content": a.full_content,
828
842
  ":imported_at": new Date().toISOString(),
843
+ ":content_hash": contentHash,
829
844
  });
830
845
  }
831
846
  export function insertMilestone(m) {
@@ -1158,6 +1173,47 @@ export function getSliceTasks(milestoneId, sliceId) {
1158
1173
  const rows = currentDb.prepare("SELECT * FROM tasks WHERE milestone_id = :mid AND slice_id = :sid ORDER BY sequence, id").all({ ":mid": milestoneId, ":sid": sliceId });
1159
1174
  return rows.map(rowToTask);
1160
1175
  }
1176
+ export function getCompletedMilestoneTaskFileHints(milestoneId) {
1177
+ if (!currentDb)
1178
+ return [];
1179
+ const rows = currentDb.prepare(`SELECT files, key_files
1180
+ FROM tasks
1181
+ WHERE milestone_id = :mid AND status IN ('complete', 'done')`).all({ ":mid": milestoneId });
1182
+ const hints = new Set();
1183
+ for (const row of rows) {
1184
+ for (const raw of [row["files"], row["key_files"]]) {
1185
+ for (const file of parseStringArrayColumn(raw)) {
1186
+ const normalized = normalizeRepoPath(file);
1187
+ if (normalized)
1188
+ hints.add(normalized);
1189
+ }
1190
+ }
1191
+ }
1192
+ return [...hints];
1193
+ }
1194
+ function parseStringArrayColumn(raw) {
1195
+ if (Array.isArray(raw))
1196
+ return raw.filter((entry) => typeof entry === "string");
1197
+ if (typeof raw !== "string")
1198
+ return [];
1199
+ const trimmed = raw.trim();
1200
+ if (!trimmed)
1201
+ return [];
1202
+ try {
1203
+ const parsed = JSON.parse(trimmed);
1204
+ if (Array.isArray(parsed))
1205
+ return parsed.filter((entry) => typeof entry === "string");
1206
+ if (typeof parsed === "string")
1207
+ return [parsed];
1208
+ }
1209
+ catch {
1210
+ return trimmed.split(",");
1211
+ }
1212
+ return [];
1213
+ }
1214
+ function normalizeRepoPath(file) {
1215
+ return file.trim().replace(/\\/g, "/").replace(/^\.\/+/, "");
1216
+ }
1161
1217
  // ─── ADR-011 Phase 2 escalation helpers ──────────────────────────────────
1162
1218
  /** Set pause-on-escalation state on a completed task. Mutually exclusive with awaiting_review. */
1163
1219
  export function setTaskEscalationPending(milestoneId, sliceId, taskId, artifactPath) {
@@ -1476,6 +1532,13 @@ export function reconcileWorktreeDb(mainDbPath, worktreeDbPath) {
1476
1532
  const hasEscalationAwaiting = wtTaskInfo.some((col) => col["name"] === "escalation_awaiting_review");
1477
1533
  const hasEscalationArtifact = wtTaskInfo.some((col) => col["name"] === "escalation_artifact_path");
1478
1534
  const hasEscalationOverride = wtTaskInfo.some((col) => col["name"] === "escalation_override_applied_at");
1535
+ const wtArtifactInfo = adapter.prepare("PRAGMA wt.table_info('artifacts')").all();
1536
+ const hasArtifactContentHash = wtArtifactInfo.some((col) => col["name"] === "content_hash");
1537
+ const wtMemoryInfo = adapter.prepare("PRAGMA wt.table_info('memories')").all();
1538
+ const hasMemoryScope = wtMemoryInfo.some((col) => col["name"] === "scope");
1539
+ const hasMemoryTags = wtMemoryInfo.some((col) => col["name"] === "tags");
1540
+ const hasMemoryStructuredFields = wtMemoryInfo.some((col) => col["name"] === "structured_fields");
1541
+ const hasMemoryLastHitAt = wtMemoryInfo.some((col) => col["name"] === "last_hit_at");
1479
1542
  const decConf = adapter.prepare(`SELECT m.id FROM decisions m INNER JOIN wt.decisions w ON m.id = w.id WHERE m.decision != w.decision OR m.choice != w.choice OR m.rationale != w.rationale OR ${hasMadeBy ? "m.made_by != w.made_by" : "'agent' != 'agent'"} OR m.superseded_by IS NOT w.superseded_by`).all();
1480
1543
  for (const row of decConf)
1481
1544
  conflicts.push(`decision ${row["id"]}: modified in both`);
@@ -1508,12 +1571,17 @@ export function reconcileWorktreeDb(mainDbPath, worktreeDbPath) {
1508
1571
  supporting_slices, validation, notes, full_content, superseded_by
1509
1572
  FROM wt.requirements
1510
1573
  `).run());
1574
+ // V27: preserve content_hash. If the worktree predates V27 (no column),
1575
+ // fall back to the main DB's existing hash so reconcile doesn't null
1576
+ // out integrity fingerprints on artifacts that were unchanged in wt.
1511
1577
  merged.artifacts = countChanges(adapter.prepare(`
1512
1578
  INSERT OR REPLACE INTO artifacts (
1513
- path, artifact_type, milestone_id, slice_id, task_id, full_content, imported_at
1579
+ path, artifact_type, milestone_id, slice_id, task_id, full_content, imported_at, content_hash
1514
1580
  )
1515
- SELECT path, artifact_type, milestone_id, slice_id, task_id, full_content, imported_at
1516
- FROM wt.artifacts
1581
+ SELECT w.path, w.artifact_type, w.milestone_id, w.slice_id, w.task_id, w.full_content, w.imported_at,
1582
+ ${hasArtifactContentHash ? "w.content_hash" : "m.content_hash"}
1583
+ FROM wt.artifacts w
1584
+ LEFT JOIN artifacts m ON m.path = w.path
1517
1585
  `).run());
1518
1586
  // Merge milestones — worktree may have updated status/planning fields.
1519
1587
  // Never downgrade status: complete > active > pre-planning (#4372).
@@ -1611,15 +1679,25 @@ export function reconcileWorktreeDb(mainDbPath, worktreeDbPath) {
1611
1679
  FROM wt.tasks w
1612
1680
  LEFT JOIN tasks m ON m.milestone_id = w.milestone_id AND m.slice_id = w.slice_id AND m.id = w.id
1613
1681
  `).run());
1614
- // Merge memories — keep worktree-learned insights
1682
+ // Merge memories — keep worktree-learned insights.
1683
+ // V18 (scope, tags), V21 (structured_fields), V28 (last_hit_at): for each
1684
+ // column the wt may not yet have (older worktree DB), fall back to the
1685
+ // main DB's existing value via LEFT JOIN so reconcile never silently
1686
+ // resets these fields to defaults on rows that already had them.
1615
1687
  merged.memories = countChanges(adapter.prepare(`
1616
1688
  INSERT OR REPLACE INTO memories (
1617
1689
  seq, id, category, content, confidence, source_unit_type, source_unit_id,
1618
- created_at, updated_at, superseded_by, hit_count
1690
+ created_at, updated_at, superseded_by, hit_count,
1691
+ scope, tags, structured_fields, last_hit_at
1619
1692
  )
1620
- SELECT seq, id, category, content, confidence, source_unit_type, source_unit_id,
1621
- created_at, updated_at, superseded_by, hit_count
1622
- FROM wt.memories
1693
+ SELECT w.seq, w.id, w.category, w.content, w.confidence, w.source_unit_type, w.source_unit_id,
1694
+ w.created_at, w.updated_at, w.superseded_by, w.hit_count,
1695
+ ${hasMemoryScope ? "w.scope" : "COALESCE(m.scope, 'project')"},
1696
+ ${hasMemoryTags ? "w.tags" : "COALESCE(m.tags, '[]')"},
1697
+ ${hasMemoryStructuredFields ? "w.structured_fields" : "m.structured_fields"},
1698
+ ${hasMemoryLastHitAt ? "w.last_hit_at" : "m.last_hit_at"}
1699
+ FROM wt.memories w
1700
+ LEFT JOIN memories m ON m.id = w.id
1623
1701
  `).run());
1624
1702
  // Merge verification evidence — append-only, use INSERT OR IGNORE to avoid duplicates
1625
1703
  merged.verification_evidence = countChanges(adapter.prepare(`
@@ -1735,6 +1813,7 @@ export function deleteMilestone(milestoneId) {
1735
1813
  currentDb.prepare(`DELETE FROM replan_history WHERE milestone_id = :mid`).run({ ":mid": milestoneId });
1736
1814
  currentDb.prepare(`DELETE FROM assessments WHERE milestone_id = :mid`).run({ ":mid": milestoneId });
1737
1815
  currentDb.prepare(`DELETE FROM artifacts WHERE milestone_id = :mid`).run({ ":mid": milestoneId });
1816
+ currentDb.prepare(`DELETE FROM milestone_commit_attributions WHERE milestone_id = :mid`).run({ ":mid": milestoneId });
1738
1817
  currentDb.prepare(`DELETE FROM milestone_leases WHERE milestone_id = :mid`).run({ ":mid": milestoneId });
1739
1818
  currentDb.prepare(`DELETE FROM milestones WHERE id = :mid`).run({ ":mid": milestoneId });
1740
1819
  });
@@ -1962,6 +2041,59 @@ export function upsertTurnGitTransaction(entry) {
1962
2041
  ":updated_at": entry.updatedAt,
1963
2042
  });
1964
2043
  }
2044
+ export function getMilestoneCommitAttributionShas(milestoneId) {
2045
+ if (!currentDb)
2046
+ return [];
2047
+ const rows = currentDb.prepare(`SELECT commit_sha
2048
+ FROM milestone_commit_attributions
2049
+ WHERE milestone_id = :mid
2050
+ ORDER BY created_at, commit_sha`).all({ ":mid": milestoneId });
2051
+ return rows
2052
+ .map((row) => typeof row["commit_sha"] === "string" ? row["commit_sha"] : "")
2053
+ .filter(Boolean);
2054
+ }
2055
+ export function recordMilestoneCommitAttribution(entry) {
2056
+ if (!currentDb)
2057
+ return;
2058
+ transaction(() => {
2059
+ currentDb.prepare(`INSERT OR REPLACE INTO milestone_commit_attributions (
2060
+ commit_sha, milestone_id, slice_id, task_id, source, confidence, files_json, created_at
2061
+ ) VALUES (
2062
+ :commit_sha, :milestone_id, :slice_id, :task_id, :source, :confidence, :files_json, :created_at
2063
+ )`).run({
2064
+ ":commit_sha": entry.commitSha,
2065
+ ":milestone_id": entry.milestoneId,
2066
+ ":slice_id": entry.sliceId ?? null,
2067
+ ":task_id": entry.taskId ?? null,
2068
+ ":source": entry.source,
2069
+ ":confidence": entry.confidence,
2070
+ ":files_json": JSON.stringify(entry.files),
2071
+ ":created_at": entry.createdAt,
2072
+ });
2073
+ currentDb.prepare(`INSERT OR IGNORE INTO audit_events (
2074
+ event_id, trace_id, turn_id, caused_by, category, type, ts, payload_json
2075
+ ) VALUES (
2076
+ :event_id, :trace_id, :turn_id, :caused_by, :category, :type, :ts, :payload_json
2077
+ )`).run({
2078
+ ":event_id": `milestone-commit-attribution:${entry.milestoneId}:${entry.commitSha}`,
2079
+ ":trace_id": "milestone-commit-attribution",
2080
+ ":turn_id": null,
2081
+ ":caused_by": null,
2082
+ ":category": "git",
2083
+ ":type": "milestone-commit-attribution-recorded",
2084
+ ":ts": entry.createdAt,
2085
+ ":payload_json": JSON.stringify({
2086
+ commitSha: entry.commitSha,
2087
+ milestoneId: entry.milestoneId,
2088
+ sliceId: entry.sliceId ?? null,
2089
+ taskId: entry.taskId ?? null,
2090
+ source: entry.source,
2091
+ confidence: entry.confidence,
2092
+ files: entry.files,
2093
+ }),
2094
+ });
2095
+ });
2096
+ }
1965
2097
  export function insertAuditEvent(entry) {
1966
2098
  if (!currentDb)
1967
2099
  return;
@@ -2048,6 +2180,7 @@ export function clearEngineHierarchy() {
2048
2180
  currentDb.exec("DELETE FROM slice_dependencies");
2049
2181
  currentDb.exec("DELETE FROM assessments");
2050
2182
  currentDb.exec("DELETE FROM replan_history");
2183
+ currentDb.exec("DELETE FROM milestone_commit_attributions");
2051
2184
  currentDb.exec("DELETE FROM tasks");
2052
2185
  currentDb.exec("DELETE FROM slices");
2053
2186
  currentDb.exec("DELETE FROM milestone_leases");
@@ -2323,7 +2456,7 @@ export function updateMemoryContentRow(id, content, confidence, updatedAt) {
2323
2456
  export function incrementMemoryHitCount(id, updatedAt) {
2324
2457
  if (!currentDb)
2325
2458
  throw new GSDError(GSD_STALE_STATE, "gsd-db: No database open");
2326
- currentDb.prepare("UPDATE memories SET hit_count = hit_count + 1, updated_at = :updated_at WHERE id = :id").run({ ":updated_at": updatedAt, ":id": id });
2459
+ currentDb.prepare("UPDATE memories SET hit_count = hit_count + 1, updated_at = :updated_at, last_hit_at = :last_hit_at WHERE id = :id").run({ ":updated_at": updatedAt, ":last_hit_at": updatedAt, ":id": id });
2327
2460
  }
2328
2461
  export function supersedeMemoryRow(oldId, newId, updatedAt) {
2329
2462
  if (!currentDb)
@@ -16,7 +16,7 @@ import { invalidateAllCaches } from "./cache.js";
16
16
  import { startAutoDetached } from "./auto.js";
17
17
  import { clearLock } from "./crash-recovery.js";
18
18
  import { assessInterruptedSession, formatInterruptedSessionRunningMessage, formatInterruptedSessionSummary, } from "./interrupted-session.js";
19
- import { listUnitRuntimeRecords, clearUnitRuntimeRecord } from "./unit-runtime.js";
19
+ import { listUnitRuntimeRecords, clearUnitRuntimeRecord, isInFlightRuntimePhase } from "./unit-runtime.js";
20
20
  import { resolveExpectedArtifactPath } from "./auto.js";
21
21
  import { gsdHome } from "./gsd-home.js";
22
22
  import { gsdRoot, milestonesDir, resolveMilestoneFile, resolveMilestonePath, resolveSliceFile, resolveSlicePath, resolveGsdRootFile, relGsdRootFile, relMilestoneFile, relSliceFile, clearPathCache, } from "./paths.js";
@@ -51,6 +51,21 @@ export { showQueue, handleQueueReorder, showQueueAdd, buildExistingMilestonesCon
51
51
  import { logWarning } from "./workflow-logger.js";
52
52
  import { deleteRuntimeKv } from "./db/runtime-kv.js";
53
53
  import { PAUSED_SESSION_KV_KEY } from "./interrupted-session.js";
54
+ function scheduleAutoStartAfterIdle(ctx, pi, basePath, verboseMode, options, launch = startAutoDetached) {
55
+ const waitForIdle = typeof ctx.waitForIdle === "function"
56
+ ? ctx.waitForIdle.bind(ctx)
57
+ : async () => { };
58
+ void waitForIdle()
59
+ .then(() => {
60
+ setTimeout(() => launch(ctx, pi, basePath, verboseMode, options), 0);
61
+ })
62
+ .catch((err) => {
63
+ const message = err instanceof Error ? err.message : String(err);
64
+ ctx.ui.notify(`Auto-start failed while waiting for the prior turn to settle: ${message}`, "error");
65
+ logWarning("guided", `auto-start idle wait failed: ${message}`);
66
+ });
67
+ }
68
+ export const _scheduleAutoStartAfterIdleForTest = scheduleAutoStartAfterIdle;
54
69
  // ─── Scope-based validator wrappers ──────────────────────────────────────────
55
70
  // These thin wrappers accept a MilestoneScope so callers that already hold a
56
71
  // pinned scope never have to re-derive (basePath, milestoneId) separately.
@@ -71,6 +86,19 @@ export function verifyExpectedArtifactForScope(scope, unitType, unitId) {
71
86
  export function resolveExpectedArtifactPathForScope(scope, unitType, unitId) {
72
87
  return resolveExpectedArtifactPath(unitType, unitId, scope.workspace.projectRoot);
73
88
  }
89
+ async function runQuickTaskChoice(ctx, pi) {
90
+ if (!ctx.hasUI) {
91
+ ctx.ui.notify("Run /gsd quick <task> for small bounded work, or /gsd do <task> for natural-language routing.", "info");
92
+ return;
93
+ }
94
+ const task = (await ctx.ui.input("Quick task", "Describe the small task to run with /gsd quick"))?.trim();
95
+ if (!task) {
96
+ ctx.ui.notify("Quick task cancelled.", "info");
97
+ return;
98
+ }
99
+ const { handleQuick } = await import("./quick.js");
100
+ await handleQuick(task, ctx, pi);
101
+ }
74
102
  /**
75
103
  * Scope-based overload of isGhostMilestone.
76
104
  * Binds basePath and milestoneId from the scope, ensuring path resolution
@@ -312,7 +340,7 @@ async function dispatchNextDeepProjectSetupStage(entry) {
312
340
  const { DISPATCH_RULES, hasPendingDeepStage } = await import("./auto-dispatch.js");
313
341
  if (!hasPendingDeepStage(prefs, entry.basePath)) {
314
342
  pendingDeepProjectSetupMap.delete(entry.basePath);
315
- startAutoDetached(entry.ctx, entry.pi, entry.basePath, false, { step: entry.step });
343
+ scheduleAutoStartAfterIdle(entry.ctx, entry.pi, entry.basePath, false, { step: entry.step });
316
344
  return true;
317
345
  }
318
346
  const state = await deriveState(entry.basePath);
@@ -346,14 +374,14 @@ async function dispatchNextDeepProjectSetupStage(entry) {
346
374
  }
347
375
  else if (hasPendingDeepStage(prefs, entry.basePath)) {
348
376
  pendingDeepProjectSetupMap.delete(entry.basePath);
349
- startAutoDetached(entry.ctx, entry.pi, entry.basePath, false, { step: entry.step });
377
+ scheduleAutoStartAfterIdle(entry.ctx, entry.pi, entry.basePath, false, { step: entry.step });
350
378
  return true;
351
379
  }
352
380
  return false;
353
381
  }
354
382
  if (!USER_DRIVEN_DEEP_SETUP_UNITS.has(result.unitType)) {
355
383
  pendingDeepProjectSetupMap.delete(entry.basePath);
356
- startAutoDetached(entry.ctx, entry.pi, entry.basePath, false, { step: entry.step });
384
+ scheduleAutoStartAfterIdle(entry.ctx, entry.pi, entry.basePath, false, { step: entry.step });
357
385
  return true;
358
386
  }
359
387
  entry.currentUnitType = result.unitType;
@@ -531,7 +559,7 @@ export function checkAutoStartAfterDiscuss() {
531
559
  }
532
560
  pendingAutoStartMap.delete(basePath);
533
561
  ctx.ui.notify(`Milestone ${milestoneId} ready.`, "success");
534
- startAutoDetached(ctx, pi, basePath, false, { step });
562
+ scheduleAutoStartAfterIdle(ctx, pi, basePath, false, { step });
535
563
  return true;
536
564
  }
537
565
  /**
@@ -1469,8 +1497,8 @@ function selfHealRuntimeRecords(basePath, ctx) {
1469
1497
  cleared++;
1470
1498
  continue;
1471
1499
  }
1472
- // Clear records stuck in dispatched or timeout phase (process died mid-unit)
1473
- if (phase === "dispatched" || phase === "timeout") {
1500
+ // Clear records stuck in an in-flight phase (process died mid-unit).
1501
+ if (isInFlightRuntimePhase(phase)) {
1474
1502
  clearUnitRuntimeRecord(basePath, unitType, unitId);
1475
1503
  cleared++;
1476
1504
  }
@@ -1695,6 +1723,20 @@ export async function showSmartEntry(ctx, pi, basePath, options) {
1695
1723
  return;
1696
1724
  }
1697
1725
  }
1726
+ if (interrupted.classification !== "recoverable") {
1727
+ try {
1728
+ const { autoImportMarkdownHierarchyIfDbMismatch } = await import("./migration-auto-check.js");
1729
+ const result = await autoImportMarkdownHierarchyIfDbMismatch(basePath);
1730
+ if (result.action === "imported") {
1731
+ ctx.ui.notify(`Recovered migrated planning state into gsd.db (${result.reason}): ${result.afterDb.milestones} milestone(s), ${result.afterDb.slices} slice(s), ${result.afterDb.tasks} task(s).`, "info");
1732
+ }
1733
+ }
1734
+ catch (err) {
1735
+ const message = err instanceof Error ? err.message : String(err);
1736
+ ctx.ui.notify(`GSD could not auto-import existing planning state into gsd.db: ${message}`, "warning");
1737
+ logWarning("guided", `planning state auto-import failed: ${message}`, { file: "guided-flow.ts" });
1738
+ }
1739
+ }
1698
1740
  // Always derive from the project root — the assessment may have derived
1699
1741
  // state from a worktree path that was cleaned up in the stale branch above.
1700
1742
  const state = await deriveState(basePath);
@@ -1716,8 +1758,8 @@ export async function showSmartEntry(ctx, pi, basePath, options) {
1716
1758
  // standard wizard below.
1717
1759
  {
1718
1760
  const prefs = loadEffectiveGSDPreferences(basePath)?.preferences;
1719
- const { hasPendingDeepStage } = await import("./auto-dispatch.js");
1720
- if (hasPendingDeepStage(prefs, basePath)) {
1761
+ const { shouldRunDeepProjectSetup } = await import("./auto-dispatch.js");
1762
+ if (shouldRunDeepProjectSetup(state, prefs, basePath)) {
1721
1763
  await startDeepProjectSetupForeground(ctx, pi, basePath, stepMode);
1722
1764
  return;
1723
1765
  }
@@ -1780,16 +1822,24 @@ export async function showSmartEntry(ctx, pi, basePath, options) {
1780
1822
  title: "GSD — Get Shit Done",
1781
1823
  summary: ["No active milestone."],
1782
1824
  actions: [
1825
+ {
1826
+ id: "quick_task",
1827
+ label: "Quick task",
1828
+ description: "For small bounded work, run /gsd quick <task> or /gsd do <task>.",
1829
+ recommended: true,
1830
+ },
1783
1831
  {
1784
1832
  id: "new_milestone",
1785
1833
  label: "Create next milestone",
1786
- description: "Define what to build next.",
1787
- recommended: true,
1834
+ description: "Define a larger body of work with planning artifacts.",
1788
1835
  },
1789
1836
  ],
1790
1837
  notYetMessage: "Run /gsd when ready.",
1791
1838
  });
1792
- if (choice === "new_milestone") {
1839
+ if (choice === "quick_task") {
1840
+ await runQuickTaskChoice(ctx, pi);
1841
+ }
1842
+ else if (choice === "new_milestone") {
1793
1843
  setPendingAutoStart(basePath, { ctx, pi, basePath, milestoneId: nextId, step: stepMode });
1794
1844
  await dispatchWorkflow(pi, await prepareAndBuildDiscussPrompt(ctx, pi, nextId, `New milestone ${nextId}.`, basePath), "gsd-run", ctx, "discuss-milestone");
1795
1845
  }
@@ -1809,11 +1859,16 @@ export async function showSmartEntry(ctx, pi, basePath, options) {
1809
1859
  title: `GSD — ${milestoneId}: ${milestoneTitle}`,
1810
1860
  summary: ["All milestones complete."],
1811
1861
  actions: [
1862
+ {
1863
+ id: "quick_task",
1864
+ label: "Quick task",
1865
+ description: "Do a small bounded task without opening a milestone.",
1866
+ recommended: true,
1867
+ },
1812
1868
  {
1813
1869
  id: "new_milestone",
1814
1870
  label: "Start new milestone",
1815
1871
  description: "Define and plan the next milestone.",
1816
- recommended: true,
1817
1872
  },
1818
1873
  {
1819
1874
  id: "status",
@@ -1823,7 +1878,10 @@ export async function showSmartEntry(ctx, pi, basePath, options) {
1823
1878
  ],
1824
1879
  notYetMessage: "Run /gsd when ready.",
1825
1880
  });
1826
- if (choice === "new_milestone") {
1881
+ if (choice === "quick_task") {
1882
+ await runQuickTaskChoice(ctx, pi);
1883
+ }
1884
+ else if (choice === "new_milestone") {
1827
1885
  const milestoneIds = findMilestoneIds(basePath);
1828
1886
  const uniqueMilestoneIds = !!loadEffectiveGSDPreferences()?.preferences?.unique_milestone_ids;
1829
1887
  const nextId = nextMilestoneIdReserved(milestoneIds, uniqueMilestoneIds, basePath);
@@ -1916,13 +1974,18 @@ export async function showSmartEntry(ctx, pi, basePath, options) {
1916
1974
  const contextFile = resolveMilestoneFile(basePath, milestoneId, "CONTEXT");
1917
1975
  const hasContext = !!(contextFile && await loadFile(contextFile));
1918
1976
  const actions = [
1977
+ {
1978
+ id: "quick_task",
1979
+ label: "Quick task instead",
1980
+ description: "Use this when the work is small and should not become a milestone.",
1981
+ recommended: true,
1982
+ },
1919
1983
  {
1920
1984
  id: "plan",
1921
1985
  label: "Create roadmap",
1922
1986
  description: hasContext
1923
1987
  ? "Context captured. Decompose into slices with a boundary map."
1924
1988
  : "Decompose the milestone into slices with a boundary map.",
1925
- recommended: true,
1926
1989
  },
1927
1990
  ...(!hasContext ? [{
1928
1991
  id: "discuss",
@@ -1946,7 +2009,10 @@ export async function showSmartEntry(ctx, pi, basePath, options) {
1946
2009
  actions,
1947
2010
  notYetMessage: "Run /gsd when ready.",
1948
2011
  });
1949
- if (choice === "plan") {
2012
+ if (choice === "quick_task") {
2013
+ await runQuickTaskChoice(ctx, pi);
2014
+ }
2015
+ else if (choice === "plan") {
1950
2016
  setPendingAutoStart(basePath, { ctx, pi, basePath, milestoneId, step: stepMode });
1951
2017
  await dispatchWorkflow(pi, await buildPlanMilestonePrompt(milestoneId, milestoneTitle, basePath), "gsd-run", ctx, "plan-milestone");
1952
2018
  }
@@ -14,6 +14,27 @@ const CATEGORY_PRIORITY = {
14
14
  environment: 4,
15
15
  preference: 5,
16
16
  };
17
+ // ─── Scoring Helpers ─────────────────────────────────────────────────────────
18
+ /**
19
+ * Time-decay factor for memory relevance scoring.
20
+ * Returns 1.0 for never-hit or recently-hit memories, decaying linearly to
21
+ * 0.7 for memories not accessed in 90+ days. Floor at 0.7 keeps old-but-valid
22
+ * knowledge from being fully suppressed.
23
+ *
24
+ * Defensive parsing: invalid timestamp strings (NaN from Date.parse) are
25
+ * treated as "no decay" rather than propagating NaN into score arithmetic.
26
+ * Future timestamps (clock skew, manual DB edits) clamp to daysAgo=0 so the
27
+ * factor stays in the documented [0.7, 1.0] contract.
28
+ */
29
+ export function memoryDecayFactor(lastHitAt) {
30
+ if (!lastHitAt)
31
+ return 1.0;
32
+ const ts = Date.parse(lastHitAt);
33
+ if (!Number.isFinite(ts))
34
+ return 1.0;
35
+ const daysAgo = Math.max(0, (Date.now() - ts) / 86_400_000);
36
+ return Math.max(0.7, 1.0 - 0.3 * Math.min(1.0, daysAgo / 90));
37
+ }
17
38
  // ─── Row Mapping ────────────────────────────────────────────────────────────
18
39
  function rowToMemory(row) {
19
40
  return {
@@ -31,6 +52,7 @@ function rowToMemory(row) {
31
52
  scope: row['scope'] ?? 'project',
32
53
  tags: parseTags(row['tags']),
33
54
  structured_fields: parseStructuredFields(row['structured_fields']),
55
+ last_hit_at: row['last_hit_at'] ?? null,
34
56
  };
35
57
  }
36
58
  function parseStructuredFields(raw) {
@@ -114,15 +136,37 @@ export function queryMemoriesRanked(opts) {
114
136
  ? semanticSearch(adapter, opts.queryVector, activeClause, 50)
115
137
  : [];
116
138
  if (keywordHits.length === 0 && semanticHits.length === 0 && !trimmedQuery) {
117
- // No query at all — fall back to the existing ranked-by-score listing.
118
- return getActiveMemoriesRanked(k).map((memory) => ({
119
- memory,
120
- score: memory.confidence * (1 + memory.hit_count * 0.1),
121
- keywordRank: null,
122
- semanticRank: null,
123
- confidenceBoost: memory.confidence * (1 + memory.hit_count * 0.1),
124
- reason: 'ranked',
125
- })).filter((hit) => passesFilters(hit.memory, opts));
139
+ // No query at all — return top-k by decay-aware ranked score.
140
+ //
141
+ // Build the candidate pool from a direct SQL query that honors the
142
+ // request's activeClause (i.e. include_superseded). Using
143
+ // getActiveMemoriesRanked here would silently drop superseded rows even
144
+ // when the caller explicitly opted in, and would slice by raw score
145
+ // before decay/filters had a chance to reorder.
146
+ const candidatePool = Math.min(Math.max(k * 5, 50), 500);
147
+ const rows = adapter
148
+ .prepare(`SELECT * FROM memories ${activeClause}
149
+ ORDER BY (confidence * (1.0 + hit_count * 0.1)) DESC
150
+ LIMIT :limit`)
151
+ .all({ ':limit': candidatePool });
152
+ const ranked = [];
153
+ for (const row of rows) {
154
+ const memory = rowToMemory(row);
155
+ if (!passesFilters(memory, opts))
156
+ continue;
157
+ const decay = memoryDecayFactor(memory.last_hit_at);
158
+ const score = memory.confidence * (1 + memory.hit_count * 0.1) * decay;
159
+ ranked.push({
160
+ memory,
161
+ score,
162
+ keywordRank: null,
163
+ semanticRank: null,
164
+ confidenceBoost: score,
165
+ reason: 'ranked',
166
+ });
167
+ }
168
+ ranked.sort((a, b) => b.score - a.score);
169
+ return ranked.slice(0, k);
126
170
  }
127
171
  // 3) Reciprocal rank fusion — each hit contributes 1/(rrfK + rank).
128
172
  const fused = new Map();
@@ -155,7 +199,7 @@ export function queryMemoriesRanked(opts) {
155
199
  for (const entry of fused.values()) {
156
200
  if (!passesFilters(entry.memory, opts))
157
201
  continue;
158
- const boost = entry.memory.confidence * (1 + entry.memory.hit_count * 0.1);
202
+ const boost = entry.memory.confidence * (1 + entry.memory.hit_count * 0.1) * memoryDecayFactor(entry.memory.last_hit_at);
159
203
  const reason = entry.kwRank != null && entry.semRank != null
160
204
  ? 'both'
161
205
  : entry.kwRank != null
@@ -194,6 +238,7 @@ function passesFilters(memory, filters) {
194
238
  }
195
239
  return true;
196
240
  }
241
+ let ftsWarningEmitted = false;
197
242
  function keywordSearch(adapter, rawQuery, activeClause, limit) {
198
243
  const ftsAvailable = isFtsAvailable(adapter);
199
244
  if (ftsAvailable) {
@@ -215,14 +260,26 @@ function keywordSearch(adapter, rawQuery, activeClause, limit) {
215
260
  // fall through to LIKE
216
261
  }
217
262
  }
218
- // LIKE fallback — scans the candidate pool.
263
+ // LIKE fallback — scans a capped candidate pool.
264
+ if (!ftsWarningEmitted) {
265
+ ftsWarningEmitted = true;
266
+ logWarning('memory-store', 'FTS5 unavailable — using LIKE fallback scan (consider enabling FTS5)');
267
+ }
219
268
  const terms = rawQuery
220
269
  .toLowerCase()
221
270
  .split(/[^a-z0-9_]+/)
222
271
  .filter((t) => t.length >= 2);
223
272
  if (terms.length === 0)
224
273
  return [];
225
- const rows = adapter.prepare(`SELECT * FROM memories ${activeClause}`).all();
274
+ const preScanCap = Math.min(limit * 20, 2000);
275
+ // ORDER BY confidence-weighted hit_count DESC so the cap keeps the most
276
+ // valuable candidates instead of the oldest-by-rowid (which would silently
277
+ // exclude recently-stored memories on tables larger than preScanCap).
278
+ const rows = adapter
279
+ .prepare(`SELECT * FROM memories ${activeClause}
280
+ ORDER BY (confidence * (1.0 + hit_count * 0.1)) DESC
281
+ LIMIT :preScanCap`)
282
+ .all({ ':preScanCap': preScanCap });
226
283
  const scored = [];
227
284
  for (const row of rows) {
228
285
  const memory = rowToMemory(row);