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

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 (580) hide show
  1. package/README.md +4 -2
  2. package/dist/cli.js +0 -19
  3. package/dist/resources/.managed-resources-content-hash +1 -1
  4. package/dist/resources/GSD-WORKFLOW.md +2 -2
  5. package/dist/resources/extensions/claude-code-cli/stream-adapter.js +29 -0
  6. package/dist/resources/extensions/github-sync/templates.js +39 -8
  7. package/dist/resources/extensions/gsd/auto/loop.js +119 -18
  8. package/dist/resources/extensions/gsd/auto/phases.js +212 -135
  9. package/dist/resources/extensions/gsd/auto/resolve.js +29 -0
  10. package/dist/resources/extensions/gsd/auto/run-unit.js +41 -45
  11. package/dist/resources/extensions/gsd/auto/session.js +8 -0
  12. package/dist/resources/extensions/gsd/auto/workflow-dispatch-claim.js +33 -1
  13. package/dist/resources/extensions/gsd/auto/workflow-worker-heartbeat.js +9 -1
  14. package/dist/resources/extensions/gsd/auto-dashboard.js +51 -15
  15. package/dist/resources/extensions/gsd/auto-direct-dispatch.js +5 -32
  16. package/dist/resources/extensions/gsd/auto-dispatch.js +26 -0
  17. package/dist/resources/extensions/gsd/auto-post-unit.js +27 -14
  18. package/dist/resources/extensions/gsd/auto-prompts.js +214 -17
  19. package/dist/resources/extensions/gsd/auto-recovery.js +197 -9
  20. package/dist/resources/extensions/gsd/auto-start.js +199 -9
  21. package/dist/resources/extensions/gsd/auto-supervisor.js +8 -1
  22. package/dist/resources/extensions/gsd/auto-timeout-recovery.js +2 -2
  23. package/dist/resources/extensions/gsd/auto-worktree.js +111 -1
  24. package/dist/resources/extensions/gsd/auto.js +95 -27
  25. package/dist/resources/extensions/gsd/bootstrap/agent-end-recovery.js +103 -3
  26. package/dist/resources/extensions/gsd/bootstrap/db-tools.js +49 -36
  27. package/dist/resources/extensions/gsd/bootstrap/dynamic-tools.js +15 -5
  28. package/dist/resources/extensions/gsd/bootstrap/exec-tools.js +33 -20
  29. package/dist/resources/extensions/gsd/bootstrap/journal-tools.js +7 -1
  30. package/dist/resources/extensions/gsd/bootstrap/memory-tools.js +9 -3
  31. package/dist/resources/extensions/gsd/bootstrap/query-tools.js +8 -2
  32. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +330 -55
  33. package/dist/resources/extensions/gsd/bootstrap/system-context.js +82 -23
  34. package/dist/resources/extensions/gsd/bootstrap/write-gate.js +129 -1
  35. package/dist/resources/extensions/gsd/clean-root-preflight.js +65 -9
  36. package/dist/resources/extensions/gsd/commands/dispatcher.js +5 -0
  37. package/dist/resources/extensions/gsd/commands-extract-learnings.js +17 -12
  38. package/dist/resources/extensions/gsd/commands-handlers.js +23 -9
  39. package/dist/resources/extensions/gsd/context-budget.js +37 -2
  40. package/dist/resources/extensions/gsd/crash-recovery.js +56 -10
  41. package/dist/resources/extensions/gsd/custom-workflow-engine.js +22 -2
  42. package/dist/resources/extensions/gsd/db/unit-dispatches.js +92 -0
  43. package/dist/resources/extensions/gsd/db-base-schema.js +18 -2
  44. package/dist/resources/extensions/gsd/db-migration-steps.js +22 -0
  45. package/dist/resources/extensions/gsd/detection.js +106 -0
  46. package/dist/resources/extensions/gsd/ecosystem/gsd-extension-api.js +2 -0
  47. package/dist/resources/extensions/gsd/git-service.js +36 -4
  48. package/dist/resources/extensions/gsd/graph.js +9 -3
  49. package/dist/resources/extensions/gsd/gsd-db.js +146 -13
  50. package/dist/resources/extensions/gsd/guided-flow.js +129 -44
  51. package/dist/resources/extensions/gsd/memory-store.js +69 -12
  52. package/dist/resources/extensions/gsd/migrate/command.js +40 -1
  53. package/dist/resources/extensions/gsd/migration-auto-check.js +87 -0
  54. package/dist/resources/extensions/gsd/native-git-bridge.js +32 -8
  55. package/dist/resources/extensions/gsd/orphan-stash-audit.js +101 -0
  56. package/dist/resources/extensions/gsd/parallel-orchestrator.js +13 -3
  57. package/dist/resources/extensions/gsd/planning-path-scope.js +26 -0
  58. package/dist/resources/extensions/gsd/pr-evidence.js +57 -16
  59. package/dist/resources/extensions/gsd/pre-execution-checks.js +22 -0
  60. package/dist/resources/extensions/gsd/prompt-loader.js +28 -2
  61. package/dist/resources/extensions/gsd/prompts/complete-milestone.md +21 -19
  62. package/dist/resources/extensions/gsd/prompts/complete-slice.md +1 -1
  63. package/dist/resources/extensions/gsd/prompts/execute-task.md +4 -2
  64. package/dist/resources/extensions/gsd/prompts/parallel-research-slices.md +1 -1
  65. package/dist/resources/extensions/gsd/prompts/plan-milestone.md +3 -1
  66. package/dist/resources/extensions/gsd/prompts/plan-slice.md +1 -1
  67. package/dist/resources/extensions/gsd/prompts/quick-task.md +1 -5
  68. package/dist/resources/extensions/gsd/prompts/replan-slice.md +2 -2
  69. package/dist/resources/extensions/gsd/prompts/validate-milestone.md +2 -2
  70. package/dist/resources/extensions/gsd/quick.js +34 -2
  71. package/dist/resources/extensions/gsd/safety/evidence-collector.js +10 -2
  72. package/dist/resources/extensions/gsd/tools/context-mode-tool-result.js +15 -0
  73. package/dist/resources/extensions/gsd/tools/exec-search-tool.js +5 -0
  74. package/dist/resources/extensions/gsd/tools/exec-tool.js +3 -15
  75. package/dist/resources/extensions/gsd/tools/memory-tools.js +1 -0
  76. package/dist/resources/extensions/gsd/tools/plan-slice.js +9 -0
  77. package/dist/resources/extensions/gsd/tools/plan-task.js +9 -0
  78. package/dist/resources/extensions/gsd/tools/resume-tool.js +5 -0
  79. package/dist/resources/extensions/gsd/tools/workflow-tool-executors.js +1 -1
  80. package/dist/resources/extensions/gsd/unit-context-composer.js +12 -3
  81. package/dist/resources/extensions/gsd/unit-runtime.js +22 -0
  82. package/dist/resources/extensions/gsd/workflow-protocol.js +131 -0
  83. package/dist/resources/extensions/gsd/working-output-messages.js +64 -0
  84. package/dist/resources/extensions/gsd/worktree-manager.js +16 -14
  85. package/dist/resources/extensions/gsd/worktree-resolver.js +68 -21
  86. package/dist/tsconfig.extensions.tsbuildinfo +1 -1
  87. package/dist/web/standalone/.next/BUILD_ID +1 -1
  88. package/dist/web/standalone/.next/app-path-routes-manifest.json +11 -11
  89. package/dist/web/standalone/.next/build-manifest.json +3 -3
  90. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  91. package/dist/web/standalone/.next/react-loadable-manifest.json +1 -1
  92. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  93. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  94. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  95. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  96. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  97. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  98. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  99. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  100. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  101. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  102. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  103. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  104. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  105. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  106. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  107. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  108. package/dist/web/standalone/.next/server/app/api/onboarding/route.js +1 -1
  109. package/dist/web/standalone/.next/server/app/index.html +1 -1
  110. package/dist/web/standalone/.next/server/app/index.rsc +1 -1
  111. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  112. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
  113. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  114. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  115. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  116. package/dist/web/standalone/.next/server/app-paths-manifest.json +11 -11
  117. package/dist/web/standalone/.next/server/chunks/6897.js +3 -3
  118. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  119. package/dist/web/standalone/.next/server/middleware-react-loadable-manifest.js +1 -1
  120. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  121. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  122. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  123. package/dist/web/standalone/.next/static/chunks/{8336.6f6f30e410419aff.js → 8336.631939fb583761fa.js} +1 -1
  124. package/dist/web/standalone/.next/static/chunks/{webpack-d82dbee6356c1733.js → webpack-0481f1221120a7c6.js} +1 -1
  125. package/dist/welcome-screen.d.ts +2 -0
  126. package/dist/welcome-screen.js +9 -7
  127. package/package.json +12 -8
  128. package/packages/contracts/package.json +1 -1
  129. package/packages/mcp-server/dist/workflow-tools.d.ts.map +1 -1
  130. package/packages/mcp-server/dist/workflow-tools.js +22 -17
  131. package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
  132. package/packages/mcp-server/src/workflow-tools.test.ts +75 -2
  133. package/packages/mcp-server/src/workflow-tools.ts +30 -16
  134. package/packages/mcp-server/tsconfig.tsbuildinfo +1 -1
  135. package/packages/native/tsconfig.tsbuildinfo +1 -1
  136. package/packages/pi-agent-core/dist/agent-loop.d.ts.map +1 -1
  137. package/packages/pi-agent-core/dist/agent-loop.js +4 -1
  138. package/packages/pi-agent-core/dist/agent-loop.js.map +1 -1
  139. package/packages/pi-agent-core/dist/agent.d.ts +5 -0
  140. package/packages/pi-agent-core/dist/agent.d.ts.map +1 -1
  141. package/packages/pi-agent-core/dist/agent.js +2 -0
  142. package/packages/pi-agent-core/dist/agent.js.map +1 -1
  143. package/packages/pi-agent-core/dist/index.d.ts +1 -0
  144. package/packages/pi-agent-core/dist/index.d.ts.map +1 -1
  145. package/packages/pi-agent-core/dist/index.js +2 -0
  146. package/packages/pi-agent-core/dist/index.js.map +1 -1
  147. package/packages/pi-agent-core/dist/token-audit.d.ts +47 -0
  148. package/packages/pi-agent-core/dist/token-audit.d.ts.map +1 -0
  149. package/packages/pi-agent-core/dist/token-audit.js +221 -0
  150. package/packages/pi-agent-core/dist/token-audit.js.map +1 -0
  151. package/packages/pi-agent-core/dist/types.d.ts +9 -0
  152. package/packages/pi-agent-core/dist/types.d.ts.map +1 -1
  153. package/packages/pi-agent-core/dist/types.js.map +1 -1
  154. package/packages/pi-agent-core/src/agent-loop.test.ts +128 -0
  155. package/packages/pi-agent-core/src/agent-loop.ts +4 -1
  156. package/packages/pi-agent-core/src/agent.ts +8 -0
  157. package/packages/pi-agent-core/src/index.ts +2 -0
  158. package/packages/pi-agent-core/src/token-audit.test.ts +189 -0
  159. package/packages/pi-agent-core/src/token-audit.ts +287 -0
  160. package/packages/pi-agent-core/src/types.ts +14 -0
  161. package/packages/pi-agent-core/tsconfig.tsbuildinfo +1 -1
  162. package/packages/pi-ai/dist/models/fake-model.d.ts +12 -0
  163. package/packages/pi-ai/dist/models/fake-model.d.ts.map +1 -0
  164. package/packages/pi-ai/dist/models/fake-model.js +27 -0
  165. package/packages/pi-ai/dist/models/fake-model.js.map +1 -0
  166. package/packages/pi-ai/dist/models/index.d.ts.map +1 -1
  167. package/packages/pi-ai/dist/models/index.js +8 -0
  168. package/packages/pi-ai/dist/models/index.js.map +1 -1
  169. package/packages/pi-ai/dist/providers/fake.d.ts +42 -0
  170. package/packages/pi-ai/dist/providers/fake.d.ts.map +1 -0
  171. package/packages/pi-ai/dist/providers/fake.js +319 -0
  172. package/packages/pi-ai/dist/providers/fake.js.map +1 -0
  173. package/packages/pi-ai/dist/providers/register-builtins.d.ts.map +1 -1
  174. package/packages/pi-ai/dist/providers/register-builtins.js +24 -0
  175. package/packages/pi-ai/dist/providers/register-builtins.js.map +1 -1
  176. package/packages/pi-ai/src/models/fake-model.ts +30 -0
  177. package/packages/pi-ai/src/models/index.ts +9 -0
  178. package/packages/pi-ai/src/providers/fake.ts +376 -0
  179. package/packages/pi-ai/src/providers/register-builtins.ts +23 -0
  180. package/packages/pi-ai/tsconfig.tsbuildinfo +1 -1
  181. package/packages/pi-coding-agent/dist/core/agent-session-abort-order.test.js +32 -0
  182. package/packages/pi-coding-agent/dist/core/agent-session-abort-order.test.js.map +1 -1
  183. package/packages/pi-coding-agent/dist/core/agent-session-tool-refresh.test.js +18 -0
  184. package/packages/pi-coding-agent/dist/core/agent-session-tool-refresh.test.js.map +1 -1
  185. package/packages/pi-coding-agent/dist/core/agent-session.d.ts +12 -0
  186. package/packages/pi-coding-agent/dist/core/agent-session.d.ts.map +1 -1
  187. package/packages/pi-coding-agent/dist/core/agent-session.js +44 -7
  188. package/packages/pi-coding-agent/dist/core/agent-session.js.map +1 -1
  189. package/packages/pi-coding-agent/dist/core/chat-controller-ordering.test.js +76 -0
  190. package/packages/pi-coding-agent/dist/core/chat-controller-ordering.test.js.map +1 -1
  191. package/packages/pi-coding-agent/dist/core/compaction/compaction.d.ts +11 -0
  192. package/packages/pi-coding-agent/dist/core/compaction/compaction.d.ts.map +1 -1
  193. package/packages/pi-coding-agent/dist/core/compaction/compaction.js +9 -0
  194. package/packages/pi-coding-agent/dist/core/compaction/compaction.js.map +1 -1
  195. package/packages/pi-coding-agent/dist/core/compaction-threshold.test.d.ts +2 -0
  196. package/packages/pi-coding-agent/dist/core/compaction-threshold.test.d.ts.map +1 -0
  197. package/packages/pi-coding-agent/dist/core/compaction-threshold.test.js +103 -0
  198. package/packages/pi-coding-agent/dist/core/compaction-threshold.test.js.map +1 -0
  199. package/packages/pi-coding-agent/dist/core/db-snapshot.d.ts +15 -0
  200. package/packages/pi-coding-agent/dist/core/db-snapshot.d.ts.map +1 -0
  201. package/packages/pi-coding-agent/dist/core/db-snapshot.js +66 -0
  202. package/packages/pi-coding-agent/dist/core/db-snapshot.js.map +1 -0
  203. package/packages/pi-coding-agent/dist/core/db-snapshot.test.d.ts +2 -0
  204. package/packages/pi-coding-agent/dist/core/db-snapshot.test.d.ts.map +1 -0
  205. package/packages/pi-coding-agent/dist/core/db-snapshot.test.js +24 -0
  206. package/packages/pi-coding-agent/dist/core/db-snapshot.test.js.map +1 -0
  207. package/packages/pi-coding-agent/dist/core/extensions/loader.d.ts.map +1 -1
  208. package/packages/pi-coding-agent/dist/core/extensions/loader.js +8 -0
  209. package/packages/pi-coding-agent/dist/core/extensions/loader.js.map +1 -1
  210. package/packages/pi-coding-agent/dist/core/extensions/runner.d.ts +5 -0
  211. package/packages/pi-coding-agent/dist/core/extensions/runner.d.ts.map +1 -1
  212. package/packages/pi-coding-agent/dist/core/extensions/runner.js +20 -7
  213. package/packages/pi-coding-agent/dist/core/extensions/runner.js.map +1 -1
  214. package/packages/pi-coding-agent/dist/core/extensions/runner.test.js +102 -3
  215. package/packages/pi-coding-agent/dist/core/extensions/runner.test.js.map +1 -1
  216. package/packages/pi-coding-agent/dist/core/extensions/types.d.ts +39 -1
  217. package/packages/pi-coding-agent/dist/core/extensions/types.d.ts.map +1 -1
  218. package/packages/pi-coding-agent/dist/core/extensions/types.js.map +1 -1
  219. package/packages/pi-coding-agent/dist/core/hooks-runner.test.js +2 -0
  220. package/packages/pi-coding-agent/dist/core/hooks-runner.test.js.map +1 -1
  221. package/packages/pi-coding-agent/dist/core/model-registry.d.ts.map +1 -1
  222. package/packages/pi-coding-agent/dist/core/model-registry.js +5 -0
  223. package/packages/pi-coding-agent/dist/core/model-registry.js.map +1 -1
  224. package/packages/pi-coding-agent/dist/core/sdk-tool-filter.test.d.ts +2 -0
  225. package/packages/pi-coding-agent/dist/core/sdk-tool-filter.test.d.ts.map +1 -0
  226. package/packages/pi-coding-agent/dist/core/sdk-tool-filter.test.js +46 -0
  227. package/packages/pi-coding-agent/dist/core/sdk-tool-filter.test.js.map +1 -0
  228. package/packages/pi-coding-agent/dist/core/sdk.d.ts +10 -2
  229. package/packages/pi-coding-agent/dist/core/sdk.d.ts.map +1 -1
  230. package/packages/pi-coding-agent/dist/core/sdk.js +74 -2
  231. package/packages/pi-coding-agent/dist/core/sdk.js.map +1 -1
  232. package/packages/pi-coding-agent/dist/core/settings-manager.d.ts +24 -0
  233. package/packages/pi-coding-agent/dist/core/settings-manager.d.ts.map +1 -1
  234. package/packages/pi-coding-agent/dist/core/settings-manager.js +33 -0
  235. package/packages/pi-coding-agent/dist/core/settings-manager.js.map +1 -1
  236. package/packages/pi-coding-agent/dist/core/skill-tool.test.js +22 -0
  237. package/packages/pi-coding-agent/dist/core/skill-tool.test.js.map +1 -1
  238. package/packages/pi-coding-agent/dist/core/slash-commands.d.ts.map +1 -1
  239. package/packages/pi-coding-agent/dist/core/slash-commands.js +1 -0
  240. package/packages/pi-coding-agent/dist/core/slash-commands.js.map +1 -1
  241. package/packages/pi-coding-agent/dist/core/system-prompt.d.ts +6 -7
  242. package/packages/pi-coding-agent/dist/core/system-prompt.d.ts.map +1 -1
  243. package/packages/pi-coding-agent/dist/core/system-prompt.js +2 -3
  244. package/packages/pi-coding-agent/dist/core/system-prompt.js.map +1 -1
  245. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/chat-frame-compaction-tone.test.js +6 -4
  246. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/chat-frame-compaction-tone.test.js.map +1 -1
  247. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/tool-execution.test.js +54 -15
  248. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/tool-execution.test.js.map +1 -1
  249. package/packages/pi-coding-agent/dist/modes/interactive/components/adaptive-layout.d.ts +26 -0
  250. package/packages/pi-coding-agent/dist/modes/interactive/components/adaptive-layout.d.ts.map +1 -0
  251. package/packages/pi-coding-agent/dist/modes/interactive/components/adaptive-layout.js +112 -0
  252. package/packages/pi-coding-agent/dist/modes/interactive/components/adaptive-layout.js.map +1 -0
  253. package/packages/pi-coding-agent/dist/modes/interactive/components/adaptive-layout.test.d.ts +2 -0
  254. package/packages/pi-coding-agent/dist/modes/interactive/components/adaptive-layout.test.d.ts.map +1 -0
  255. package/packages/pi-coding-agent/dist/modes/interactive/components/adaptive-layout.test.js +51 -0
  256. package/packages/pi-coding-agent/dist/modes/interactive/components/adaptive-layout.test.js.map +1 -0
  257. package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
  258. package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.js.map +1 -1
  259. package/packages/pi-coding-agent/dist/modes/interactive/components/chat-frame.d.ts.map +1 -1
  260. package/packages/pi-coding-agent/dist/modes/interactive/components/chat-frame.js +10 -9
  261. package/packages/pi-coding-agent/dist/modes/interactive/components/chat-frame.js.map +1 -1
  262. package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.d.ts +3 -0
  263. package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  264. package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.js +11 -0
  265. package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.js.map +1 -1
  266. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-card-cleanup-and-success-runtime.test.js +7 -6
  267. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-card-cleanup-and-success-runtime.test.js.map +1 -1
  268. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts +17 -0
  269. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  270. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js +109 -17
  271. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js.map +1 -1
  272. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
  273. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js +69 -2
  274. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
  275. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.test.js +93 -1
  276. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.test.js.map +1 -1
  277. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.test.js +1 -0
  278. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.test.js.map +1 -1
  279. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.d.ts +1 -0
  280. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.d.ts.map +1 -1
  281. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.js.map +1 -1
  282. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts +3 -0
  283. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  284. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +26 -0
  285. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
  286. package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.d.ts.map +1 -1
  287. package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.js +20 -0
  288. package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.js.map +1 -1
  289. package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.test.d.ts +2 -0
  290. package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.test.d.ts.map +1 -0
  291. package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.test.js +79 -0
  292. package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.test.js.map +1 -0
  293. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme-schema.d.ts +12 -0
  294. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme-schema.d.ts.map +1 -1
  295. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme-schema.js +13 -0
  296. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme-schema.js.map +1 -1
  297. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme.d.ts +1 -1
  298. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  299. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme.js +18 -1
  300. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme.js.map +1 -1
  301. package/packages/pi-coding-agent/dist/modes/interactive/theme/themes.d.ts.map +1 -1
  302. package/packages/pi-coding-agent/dist/modes/interactive/theme/themes.js +36 -27
  303. package/packages/pi-coding-agent/dist/modes/interactive/theme/themes.js.map +1 -1
  304. package/packages/pi-coding-agent/dist/modes/interactive/tui-mode.d.ts +11 -0
  305. package/packages/pi-coding-agent/dist/modes/interactive/tui-mode.d.ts.map +1 -0
  306. package/packages/pi-coding-agent/dist/modes/interactive/tui-mode.js +18 -0
  307. package/packages/pi-coding-agent/dist/modes/interactive/tui-mode.js.map +1 -0
  308. package/packages/pi-coding-agent/dist/modes/interactive/tui-mode.test.d.ts +2 -0
  309. package/packages/pi-coding-agent/dist/modes/interactive/tui-mode.test.d.ts.map +1 -0
  310. package/packages/pi-coding-agent/dist/modes/interactive/tui-mode.test.js +48 -0
  311. package/packages/pi-coding-agent/dist/modes/interactive/tui-mode.test.js.map +1 -0
  312. package/packages/pi-coding-agent/dist/resources/extensions/memory/storage-safety-guard.test.d.ts +2 -0
  313. package/packages/pi-coding-agent/dist/resources/extensions/memory/storage-safety-guard.test.d.ts.map +1 -0
  314. package/packages/pi-coding-agent/dist/resources/extensions/memory/storage-safety-guard.test.js +10 -0
  315. package/packages/pi-coding-agent/dist/resources/extensions/memory/storage-safety-guard.test.js.map +1 -0
  316. package/packages/pi-coding-agent/dist/resources/extensions/memory/storage.d.ts.map +1 -1
  317. package/packages/pi-coding-agent/dist/resources/extensions/memory/storage.js +3 -2
  318. package/packages/pi-coding-agent/dist/resources/extensions/memory/storage.js.map +1 -1
  319. package/packages/pi-coding-agent/src/core/agent-session-abort-order.test.ts +36 -0
  320. package/packages/pi-coding-agent/src/core/agent-session-tool-refresh.test.ts +25 -0
  321. package/packages/pi-coding-agent/src/core/agent-session.ts +48 -7
  322. package/packages/pi-coding-agent/src/core/chat-controller-ordering.test.ts +89 -0
  323. package/packages/pi-coding-agent/src/core/compaction/compaction.ts +18 -0
  324. package/packages/pi-coding-agent/src/core/compaction-threshold.test.ts +121 -0
  325. package/packages/pi-coding-agent/src/core/db-snapshot.test.ts +32 -0
  326. package/packages/pi-coding-agent/src/core/db-snapshot.ts +66 -0
  327. package/packages/pi-coding-agent/src/core/extensions/loader.ts +10 -0
  328. package/packages/pi-coding-agent/src/core/extensions/runner.test.ts +113 -3
  329. package/packages/pi-coding-agent/src/core/extensions/runner.ts +24 -6
  330. package/packages/pi-coding-agent/src/core/extensions/types.ts +42 -1
  331. package/packages/pi-coding-agent/src/core/hooks-runner.test.ts +2 -0
  332. package/packages/pi-coding-agent/src/core/model-registry.ts +4 -0
  333. package/packages/pi-coding-agent/src/core/sdk-tool-filter.test.ts +60 -0
  334. package/packages/pi-coding-agent/src/core/sdk.ts +85 -3
  335. package/packages/pi-coding-agent/src/core/settings-manager.ts +51 -1
  336. package/packages/pi-coding-agent/src/core/skill-tool.test.ts +28 -0
  337. package/packages/pi-coding-agent/src/core/slash-commands.ts +1 -0
  338. package/packages/pi-coding-agent/src/core/system-prompt.ts +8 -10
  339. package/packages/pi-coding-agent/src/modes/interactive/components/__tests__/chat-frame-compaction-tone.test.ts +7 -5
  340. package/packages/pi-coding-agent/src/modes/interactive/components/__tests__/tool-execution.test.ts +78 -15
  341. package/packages/pi-coding-agent/src/modes/interactive/components/adaptive-layout.test.ts +59 -0
  342. package/packages/pi-coding-agent/src/modes/interactive/components/adaptive-layout.ts +160 -0
  343. package/packages/pi-coding-agent/src/modes/interactive/components/assistant-message.ts +1 -0
  344. package/packages/pi-coding-agent/src/modes/interactive/components/chat-frame.ts +10 -9
  345. package/packages/pi-coding-agent/src/modes/interactive/components/settings-selector.ts +15 -0
  346. package/packages/pi-coding-agent/src/modes/interactive/components/tool-card-cleanup-and-success-runtime.test.ts +10 -9
  347. package/packages/pi-coding-agent/src/modes/interactive/components/tool-execution.ts +122 -17
  348. package/packages/pi-coding-agent/src/modes/interactive/controllers/chat-controller.test.ts +99 -1
  349. package/packages/pi-coding-agent/src/modes/interactive/controllers/chat-controller.ts +92 -3
  350. package/packages/pi-coding-agent/src/modes/interactive/controllers/input-controller.test.ts +1 -0
  351. package/packages/pi-coding-agent/src/modes/interactive/interactive-mode-state.ts +1 -1
  352. package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +28 -0
  353. package/packages/pi-coding-agent/src/modes/interactive/slash-command-handlers.test.ts +95 -0
  354. package/packages/pi-coding-agent/src/modes/interactive/slash-command-handlers.ts +24 -1
  355. package/packages/pi-coding-agent/src/modes/interactive/theme/theme-schema.ts +13 -0
  356. package/packages/pi-coding-agent/src/modes/interactive/theme/theme.ts +32 -2
  357. package/packages/pi-coding-agent/src/modes/interactive/theme/themes.ts +36 -27
  358. package/packages/pi-coding-agent/src/modes/interactive/tui-mode.test.ts +65 -0
  359. package/packages/pi-coding-agent/src/modes/interactive/tui-mode.ts +29 -0
  360. package/packages/pi-coding-agent/src/resources/extensions/memory/storage-safety-guard.test.ts +14 -0
  361. package/packages/pi-coding-agent/src/resources/extensions/memory/storage.ts +3 -2
  362. package/packages/pi-coding-agent/tsconfig.tsbuildinfo +1 -1
  363. package/packages/pi-tui/dist/__tests__/style.test.d.ts +2 -0
  364. package/packages/pi-tui/dist/__tests__/style.test.d.ts.map +1 -0
  365. package/packages/pi-tui/dist/__tests__/style.test.js +63 -0
  366. package/packages/pi-tui/dist/__tests__/style.test.js.map +1 -0
  367. package/packages/pi-tui/dist/__tests__/tui.test.js +24 -3
  368. package/packages/pi-tui/dist/__tests__/tui.test.js.map +1 -1
  369. package/packages/pi-tui/dist/index.d.ts +1 -0
  370. package/packages/pi-tui/dist/index.d.ts.map +1 -1
  371. package/packages/pi-tui/dist/index.js +2 -0
  372. package/packages/pi-tui/dist/index.js.map +1 -1
  373. package/packages/pi-tui/dist/style.d.ts +41 -0
  374. package/packages/pi-tui/dist/style.d.ts.map +1 -0
  375. package/packages/pi-tui/dist/style.js +158 -0
  376. package/packages/pi-tui/dist/style.js.map +1 -0
  377. package/packages/pi-tui/dist/tui.d.ts +0 -1
  378. package/packages/pi-tui/dist/tui.d.ts.map +1 -1
  379. package/packages/pi-tui/dist/tui.js +21 -16
  380. package/packages/pi-tui/dist/tui.js.map +1 -1
  381. package/packages/pi-tui/src/__tests__/style.test.ts +76 -0
  382. package/packages/pi-tui/src/__tests__/tui.test.ts +29 -3
  383. package/packages/pi-tui/src/index.ts +9 -0
  384. package/packages/pi-tui/src/style.ts +225 -0
  385. package/packages/pi-tui/src/tui.ts +23 -16
  386. package/packages/pi-tui/tsconfig.tsbuildinfo +1 -1
  387. package/pkg/dist/modes/interactive/theme/theme-schema.d.ts +12 -0
  388. package/pkg/dist/modes/interactive/theme/theme-schema.d.ts.map +1 -1
  389. package/pkg/dist/modes/interactive/theme/theme-schema.js +13 -0
  390. package/pkg/dist/modes/interactive/theme/theme-schema.js.map +1 -1
  391. package/pkg/dist/modes/interactive/theme/theme.d.ts +1 -1
  392. package/pkg/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  393. package/pkg/dist/modes/interactive/theme/theme.js +18 -1
  394. package/pkg/dist/modes/interactive/theme/theme.js.map +1 -1
  395. package/pkg/dist/modes/interactive/theme/themes.d.ts.map +1 -1
  396. package/pkg/dist/modes/interactive/theme/themes.js +36 -27
  397. package/pkg/dist/modes/interactive/theme/themes.js.map +1 -1
  398. package/src/resources/GSD-WORKFLOW.md +2 -2
  399. package/src/resources/extensions/claude-code-cli/stream-adapter.ts +30 -0
  400. package/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +26 -0
  401. package/src/resources/extensions/github-sync/templates.ts +38 -8
  402. package/src/resources/extensions/github-sync/tests/inline-code.test.ts +66 -0
  403. package/src/resources/extensions/gsd/auto/loop-deps.ts +3 -2
  404. package/src/resources/extensions/gsd/auto/loop.ts +151 -26
  405. package/src/resources/extensions/gsd/auto/phases.ts +289 -196
  406. package/src/resources/extensions/gsd/auto/resolve.ts +42 -1
  407. package/src/resources/extensions/gsd/auto/run-unit.ts +52 -44
  408. package/src/resources/extensions/gsd/auto/session.ts +8 -0
  409. package/src/resources/extensions/gsd/auto/workflow-dispatch-claim.ts +63 -1
  410. package/src/resources/extensions/gsd/auto/workflow-worker-heartbeat.ts +14 -1
  411. package/src/resources/extensions/gsd/auto-dashboard.ts +57 -8
  412. package/src/resources/extensions/gsd/auto-direct-dispatch.ts +8 -34
  413. package/src/resources/extensions/gsd/auto-dispatch.ts +33 -0
  414. package/src/resources/extensions/gsd/auto-post-unit.ts +28 -14
  415. package/src/resources/extensions/gsd/auto-prompts.ts +228 -16
  416. package/src/resources/extensions/gsd/auto-recovery.ts +207 -7
  417. package/src/resources/extensions/gsd/auto-start.ts +237 -15
  418. package/src/resources/extensions/gsd/auto-supervisor.ts +7 -0
  419. package/src/resources/extensions/gsd/auto-timeout-recovery.ts +2 -2
  420. package/src/resources/extensions/gsd/auto-worktree.ts +123 -0
  421. package/src/resources/extensions/gsd/auto.ts +110 -22
  422. package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +119 -2
  423. package/src/resources/extensions/gsd/bootstrap/db-tools.ts +50 -36
  424. package/src/resources/extensions/gsd/bootstrap/dynamic-tools.ts +16 -5
  425. package/src/resources/extensions/gsd/bootstrap/exec-tools.ts +34 -19
  426. package/src/resources/extensions/gsd/bootstrap/journal-tools.ts +8 -1
  427. package/src/resources/extensions/gsd/bootstrap/memory-tools.ts +10 -3
  428. package/src/resources/extensions/gsd/bootstrap/query-tools.ts +9 -2
  429. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +386 -55
  430. package/src/resources/extensions/gsd/bootstrap/system-context.ts +90 -22
  431. package/src/resources/extensions/gsd/bootstrap/write-gate.ts +135 -1
  432. package/src/resources/extensions/gsd/clean-root-preflight.ts +72 -9
  433. package/src/resources/extensions/gsd/commands/dispatcher.ts +6 -0
  434. package/src/resources/extensions/gsd/commands-extract-learnings.ts +17 -12
  435. package/src/resources/extensions/gsd/commands-handlers.ts +34 -15
  436. package/src/resources/extensions/gsd/context-budget.ts +44 -2
  437. package/src/resources/extensions/gsd/crash-recovery.ts +67 -10
  438. package/src/resources/extensions/gsd/custom-workflow-engine.ts +24 -1
  439. package/src/resources/extensions/gsd/db/unit-dispatches.ts +107 -0
  440. package/src/resources/extensions/gsd/db-base-schema.ts +19 -2
  441. package/src/resources/extensions/gsd/db-migration-steps.ts +25 -0
  442. package/src/resources/extensions/gsd/detection.ts +128 -0
  443. package/src/resources/extensions/gsd/ecosystem/gsd-extension-api.ts +3 -0
  444. package/src/resources/extensions/gsd/git-service.ts +46 -8
  445. package/src/resources/extensions/gsd/graph.ts +12 -5
  446. package/src/resources/extensions/gsd/gsd-db.ts +168 -13
  447. package/src/resources/extensions/gsd/guided-flow.ts +150 -51
  448. package/src/resources/extensions/gsd/memory-store.ts +77 -12
  449. package/src/resources/extensions/gsd/migrate/command.ts +47 -1
  450. package/src/resources/extensions/gsd/migration-auto-check.ts +129 -0
  451. package/src/resources/extensions/gsd/native-git-bridge.ts +39 -6
  452. package/src/resources/extensions/gsd/orphan-stash-audit.ts +117 -0
  453. package/src/resources/extensions/gsd/parallel-orchestrator.ts +13 -3
  454. package/src/resources/extensions/gsd/planning-path-scope.ts +35 -0
  455. package/src/resources/extensions/gsd/pr-evidence.ts +63 -5
  456. package/src/resources/extensions/gsd/pre-execution-checks.ts +23 -0
  457. package/src/resources/extensions/gsd/preferences-types.ts +1 -1
  458. package/src/resources/extensions/gsd/prompt-loader.ts +27 -2
  459. package/src/resources/extensions/gsd/prompts/complete-milestone.md +21 -19
  460. package/src/resources/extensions/gsd/prompts/complete-slice.md +1 -1
  461. package/src/resources/extensions/gsd/prompts/execute-task.md +4 -2
  462. package/src/resources/extensions/gsd/prompts/parallel-research-slices.md +1 -1
  463. package/src/resources/extensions/gsd/prompts/plan-milestone.md +3 -1
  464. package/src/resources/extensions/gsd/prompts/plan-slice.md +1 -1
  465. package/src/resources/extensions/gsd/prompts/quick-task.md +1 -5
  466. package/src/resources/extensions/gsd/prompts/replan-slice.md +2 -2
  467. package/src/resources/extensions/gsd/prompts/validate-milestone.md +2 -2
  468. package/src/resources/extensions/gsd/quick.ts +37 -2
  469. package/src/resources/extensions/gsd/safety/evidence-collector.ts +11 -2
  470. package/src/resources/extensions/gsd/tests/artifact-retry-cap.test.ts +2 -2
  471. package/src/resources/extensions/gsd/tests/auto-abort-pause-regression.test.ts +7 -1
  472. package/src/resources/extensions/gsd/tests/auto-dashboard.test.ts +33 -0
  473. package/src/resources/extensions/gsd/tests/auto-loop.test.ts +516 -15
  474. package/src/resources/extensions/gsd/tests/auto-phases-lifecycle.test.ts +56 -13
  475. package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +184 -2
  476. package/src/resources/extensions/gsd/tests/auto-wrapup-inflight-guard.test.ts +168 -6
  477. package/src/resources/extensions/gsd/tests/clean-root-preflight.test.ts +97 -2
  478. package/src/resources/extensions/gsd/tests/commands-extract-learnings.test.ts +9 -0
  479. package/src/resources/extensions/gsd/tests/compaction-snapshot.test.ts +14 -1
  480. package/src/resources/extensions/gsd/tests/complete-milestone-excerpt.test.ts +31 -0
  481. package/src/resources/extensions/gsd/tests/complete-slice-composer.test.ts +3 -2
  482. package/src/resources/extensions/gsd/tests/context-budget.test.ts +10 -1
  483. package/src/resources/extensions/gsd/tests/context-store.test.ts +7 -1
  484. package/src/resources/extensions/gsd/tests/crash-handler-secondary.test.ts +55 -0
  485. package/src/resources/extensions/gsd/tests/crash-recovery-via-db.test.ts +22 -0
  486. package/src/resources/extensions/gsd/tests/custom-engine-loop-integration.test.ts +117 -7
  487. package/src/resources/extensions/gsd/tests/custom-workflow-engine.test.ts +40 -2
  488. package/src/resources/extensions/gsd/tests/db-migration-steps.integration.test.ts +428 -0
  489. package/src/resources/extensions/gsd/tests/db-schema-metadata.test.ts +2 -2
  490. package/src/resources/extensions/gsd/tests/detection.test.ts +140 -0
  491. package/src/resources/extensions/gsd/tests/dispatch-rule-coverage.test.ts +313 -0
  492. package/src/resources/extensions/gsd/tests/exec-history.test.ts +15 -0
  493. package/src/resources/extensions/gsd/tests/exec-sandbox.test.ts +65 -0
  494. package/src/resources/extensions/gsd/tests/execute-task-rendering.test.ts +5 -2
  495. package/src/resources/extensions/gsd/tests/fast-forward-reused-milestone-branch.test.ts +219 -0
  496. package/src/resources/extensions/gsd/tests/finalize-survivor-branch.test.ts +132 -0
  497. package/src/resources/extensions/gsd/tests/fixtures/pr-body/commands-ship-basic.md +52 -0
  498. package/src/resources/extensions/gsd/tests/fixtures/pr-body/commands-ship-empty-optionals.md +42 -0
  499. package/src/resources/extensions/gsd/tests/fixtures/pr-body/swarm-lane-no-blockers.md +55 -0
  500. package/src/resources/extensions/gsd/tests/fixtures/pr-body/swarm-lane-with-blockers.md +60 -0
  501. package/src/resources/extensions/gsd/tests/graph-operations.test.ts +10 -0
  502. package/src/resources/extensions/gsd/tests/gsd-db.test.ts +44 -0
  503. package/src/resources/extensions/gsd/tests/has-pending-deep-stage.test.ts +33 -1
  504. package/src/resources/extensions/gsd/tests/integration/git-service.test.ts +54 -0
  505. package/src/resources/extensions/gsd/tests/isolation-none-branch-guard.test.ts +6 -3
  506. package/src/resources/extensions/gsd/tests/journal-integration.test.ts +239 -1
  507. package/src/resources/extensions/gsd/tests/journal-query-tool.test.ts +32 -0
  508. package/src/resources/extensions/gsd/tests/knowledge.test.ts +47 -0
  509. package/src/resources/extensions/gsd/tests/memory-decay-factor.test.ts +90 -0
  510. package/src/resources/extensions/gsd/tests/merge-conflict-stops-loop.test.ts +1 -0
  511. package/src/resources/extensions/gsd/tests/migrate-writer-integration.test.ts +48 -0
  512. package/src/resources/extensions/gsd/tests/migration-auto-check.test.ts +127 -0
  513. package/src/resources/extensions/gsd/tests/milestone-merge-stash-restore.test.ts +242 -0
  514. package/src/resources/extensions/gsd/tests/native-git-bridge-exec-fallback.test.ts +34 -2
  515. package/src/resources/extensions/gsd/tests/originalbase-path-comparison.test.ts +3 -0
  516. package/src/resources/extensions/gsd/tests/orphan-merge-bootstrap.test.ts +133 -0
  517. package/src/resources/extensions/gsd/tests/orphan-stash-audit.test.ts +201 -0
  518. package/src/resources/extensions/gsd/tests/parallel-orchestrator-fast-forward.test.ts +113 -0
  519. package/src/resources/extensions/gsd/tests/plan-slice.test.ts +50 -0
  520. package/src/resources/extensions/gsd/tests/plan-task.test.ts +21 -0
  521. package/src/resources/extensions/gsd/tests/pr-evidence-equivalence.test.ts +102 -0
  522. package/src/resources/extensions/gsd/tests/pr-evidence-hardening.test.ts +165 -0
  523. package/src/resources/extensions/gsd/tests/pre-execution-checks.test.ts +45 -5
  524. package/src/resources/extensions/gsd/tests/prompt-duplication-cuts.test.ts +230 -0
  525. package/src/resources/extensions/gsd/tests/prompt-path-audit.test.ts +40 -0
  526. package/src/resources/extensions/gsd/tests/prompt-step-ordering.test.ts +19 -0
  527. package/src/resources/extensions/gsd/tests/query-tools-db-open.test.ts +3 -3
  528. package/src/resources/extensions/gsd/tests/quick-external-gsd.test.ts +40 -0
  529. package/src/resources/extensions/gsd/tests/restore-tools-after-discuss.test.ts +38 -17
  530. package/src/resources/extensions/gsd/tests/right-sized-workflow-prompts.test.ts +192 -0
  531. package/src/resources/extensions/gsd/tests/safety-harness-false-positives.test.ts +29 -0
  532. package/src/resources/extensions/gsd/tests/schema-v27-v28-sequence.test.ts +156 -0
  533. package/src/resources/extensions/gsd/tests/select-resumable-milestone.test.ts +96 -0
  534. package/src/resources/extensions/gsd/tests/session-start-footer.test.ts +77 -0
  535. package/src/resources/extensions/gsd/tests/session-switch-abort-misclassification.test.ts +166 -0
  536. package/src/resources/extensions/gsd/tests/signal-handlers.test.ts +27 -0
  537. package/src/resources/extensions/gsd/tests/smart-entry-complete.test.ts +38 -0
  538. package/src/resources/extensions/gsd/tests/stalled-tool-recovery.test.ts +49 -1
  539. package/src/resources/extensions/gsd/tests/start-auto-detached.test.ts +101 -2
  540. package/src/resources/extensions/gsd/tests/state-corruption-2945.test.ts +1 -0
  541. package/src/resources/extensions/gsd/tests/status-db-open.test.ts +9 -0
  542. package/src/resources/extensions/gsd/tests/system-context-memory.test.ts +112 -0
  543. package/src/resources/extensions/gsd/tests/system-context-message-routing.test.ts +7 -9
  544. package/src/resources/extensions/gsd/tests/token-tool-gating.test.ts +291 -0
  545. package/src/resources/extensions/gsd/tests/unit-context-composer.test.ts +136 -4
  546. package/src/resources/extensions/gsd/tests/unit-dispatches.test.ts +80 -1
  547. package/src/resources/extensions/gsd/tests/unit-runtime.test.ts +37 -0
  548. package/src/resources/extensions/gsd/tests/unstructured-continue-context-injection.test.ts +5 -4
  549. package/src/resources/extensions/gsd/tests/uok-plan-v2-wiring.test.ts +1 -1
  550. package/src/resources/extensions/gsd/tests/workflow-dispatch-claim.test.ts +142 -0
  551. package/src/resources/extensions/gsd/tests/workflow-protocol-excerpt.test.ts +99 -0
  552. package/src/resources/extensions/gsd/tests/workflow-tool-executors.test.ts +3 -0
  553. package/src/resources/extensions/gsd/tests/workflow-worker-heartbeat.test.ts +32 -1
  554. package/src/resources/extensions/gsd/tests/working-output-messages.test.ts +93 -0
  555. package/src/resources/extensions/gsd/tests/worktree-health-dispatch.test.ts +37 -6
  556. package/src/resources/extensions/gsd/tests/worktree-journal-events.test.ts +1 -0
  557. package/src/resources/extensions/gsd/tests/worktree-manager.test.ts +7 -0
  558. package/src/resources/extensions/gsd/tests/worktree-nested-git-safety.test.ts +9 -2
  559. package/src/resources/extensions/gsd/tests/worktree-path-injection.test.ts +22 -19
  560. package/src/resources/extensions/gsd/tests/worktree-project-root-degrade.test.ts +66 -0
  561. package/src/resources/extensions/gsd/tests/worktree-resolver.test.ts +167 -4
  562. package/src/resources/extensions/gsd/tests/worktree-write-gate.test.ts +179 -0
  563. package/src/resources/extensions/gsd/tools/context-mode-tool-result.ts +25 -0
  564. package/src/resources/extensions/gsd/tools/exec-search-tool.ts +7 -7
  565. package/src/resources/extensions/gsd/tools/exec-tool.ts +4 -23
  566. package/src/resources/extensions/gsd/tools/memory-tools.ts +1 -0
  567. package/src/resources/extensions/gsd/tools/plan-slice.ts +13 -0
  568. package/src/resources/extensions/gsd/tools/plan-task.ts +10 -0
  569. package/src/resources/extensions/gsd/tools/resume-tool.ts +7 -7
  570. package/src/resources/extensions/gsd/tools/workflow-tool-executors.ts +1 -1
  571. package/src/resources/extensions/gsd/unit-context-composer.ts +19 -4
  572. package/src/resources/extensions/gsd/unit-runtime.ts +25 -0
  573. package/src/resources/extensions/gsd/workflow-protocol.ts +160 -0
  574. package/src/resources/extensions/gsd/working-output-messages.ts +120 -0
  575. package/src/resources/extensions/gsd/worktree-manager.ts +15 -4
  576. package/src/resources/extensions/gsd/worktree-resolver.ts +85 -19
  577. package/packages/contracts/tsconfig.tsbuildinfo +0 -1
  578. package/src/resources/extensions/gsd/tests/phases-merge-error-stops-auto.test.ts +0 -97
  579. /package/dist/web/standalone/.next/static/{bQDK5_LtkGVS64AirQgQG → cWaxzf-sdbSSbbwYu8q7a}/_buildManifest.js +0 -0
  580. /package/dist/web/standalone/.next/static/{bQDK5_LtkGVS64AirQgQG → cWaxzf-sdbSSbbwYu8q7a}/_ssgManifest.js +0 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tui-mode.test.js","sourceRoot":"","sources":["../../../src/modes/interactive/tui-mode.test.ts"],"names":[],"mappings":"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","sourcesContent":["// GSD2 - Tests for adaptive TUI mode selection\n\nimport assert from \"node:assert/strict\";\nimport { describe, test } from \"node:test\";\nimport stripAnsi from \"strip-ansi\";\n\nimport { AdaptiveLayoutComponent } from \"./components/adaptive-layout.js\";\nimport { initTheme } from \"./theme/theme.js\";\nimport { resolveTuiMode } from \"./tui-mode.js\";\n\ninitTheme(\"dark\", false);\n\ndescribe(\"resolveTuiMode\", () => {\n\ttest(\"explicit overrides beat auto selection\", () => {\n\t\tassert.equal(\n\t\t\tresolveTuiMode({ terminalWidth: 60, override: \"debug\", gsdPhase: \"validating-milestone\" }),\n\t\t\t\"debug\",\n\t\t);\n\t});\n\n\ttest(\"prioritizes compact layouts on narrow terminals\", () => {\n\t\tassert.equal(\n\t\t\tresolveTuiMode({ terminalWidth: 60, override: \"auto\", hasBlockingError: true, gsdPhase: \"validating-milestone\" }),\n\t\t\t\"compact\",\n\t\t);\n\t});\n\n\ttest(\"uses debug mode for blocking errors on roomy terminals\", () => {\n\t\tassert.equal(resolveTuiMode({ terminalWidth: 100, hasBlockingError: true }), \"debug\");\n\t});\n\n\ttest(\"uses validation mode for validation and completion phases\", () => {\n\t\tassert.equal(resolveTuiMode({ terminalWidth: 100, gsdPhase: \"validating-milestone\" }), \"validation\");\n\t\tassert.equal(resolveTuiMode({ terminalWidth: 100, gsdPhase: \"complete-milestone\" }), \"validation\");\n\t});\n\n\ttest(\"uses workflow mode when tools or non-validation phases are active\", () => {\n\t\tassert.equal(resolveTuiMode({ terminalWidth: 100, activeToolCount: 1 }), \"workflow\");\n\t\tassert.equal(resolveTuiMode({ terminalWidth: 100, gsdPhase: \"execute-phase\" }), \"workflow\");\n\t});\n\n\ttest(\"falls back to chat mode for plain conversation\", () => {\n\t\tassert.equal(resolveTuiMode({ terminalWidth: 100 }), \"chat\");\n\t});\n});\n\ndescribe(\"AdaptiveLayoutComponent\", () => {\n\ttest(\"renders workflow layout with prototype rule frames\", () => {\n\t\tconst layout = new AdaptiveLayoutComponent(() => ({\n\t\t\toverride: \"workflow\",\n\t\t\tactiveToolCount: 2,\n\t\t\tgsdPhase: \"execute-task\",\n\t\t\tsessionName: \"main\",\n\t\t\tcwd: \"/Users/example/project\",\n\t\t}));\n\n\t\tconst plain = layout.render(120).map(stripAnsi);\n\n\t\tassert.match(plain[0], /^─+/, \"workflow layout should start with a rule frame\");\n\t\tassert.ok(plain.some((line) => line.includes(\"GSD Command Center\")), \"workflow title should render\");\n\t\tassert.ok(plain.some((line) => line.includes(\"signals\")), \"inspector title should render\");\n\t\tassert.ok(plain.some((line) => line.includes(\"│ Active\")), \"body rows should keep prototype gutter\");\n\t\tassert.ok(!plain.some((line) => /[╭╮╰╯]/.test(line)), \"workflow layout should not use rounded box corners\");\n\t});\n});\n"]}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=storage-safety-guard.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"storage-safety-guard.test.d.ts","sourceRoot":"","sources":["../../../../src/resources/extensions/memory/storage-safety-guard.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,10 @@
1
+ import assert from "node:assert/strict";
2
+ import { readFileSync } from "node:fs";
3
+ import { join } from "node:path";
4
+ import test from "node:test";
5
+ test("MemoryStorage persists sql.js snapshots through the atomic DB snapshot writer", () => {
6
+ const src = readFileSync(join(process.cwd(), "packages", "pi-coding-agent", "src", "resources", "extensions", "memory", "storage.ts"), "utf-8");
7
+ assert.match(src, /atomicWriteDbSnapshotSync\(/, "storage must use atomic DB snapshot writes");
8
+ assert.doesNotMatch(src, /writeFileSync\(this\.dbPath/, "direct live DB overwrite is forbidden");
9
+ });
10
+ //# sourceMappingURL=storage-safety-guard.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"storage-safety-guard.test.js","sourceRoot":"","sources":["../../../../src/resources/extensions/memory/storage-safety-guard.test.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,oBAAoB,CAAC;AACxC,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,IAAI,CAAC,+EAA+E,EAAE,GAAG,EAAE;IAC1F,MAAM,GAAG,GAAG,YAAY,CACvB,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,UAAU,EAAE,iBAAiB,EAAE,KAAK,EAAE,WAAW,EAAE,YAAY,EAAE,QAAQ,EAAE,YAAY,CAAC,EAC5G,OAAO,CACP,CAAC;IAEF,MAAM,CAAC,KAAK,CAAC,GAAG,EAAE,6BAA6B,EAAE,4CAA4C,CAAC,CAAC;IAC/F,MAAM,CAAC,YAAY,CAAC,GAAG,EAAE,6BAA6B,EAAE,uCAAuC,CAAC,CAAC;AAClG,CAAC,CAAC,CAAC","sourcesContent":["import assert from \"node:assert/strict\";\nimport { readFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport test from \"node:test\";\n\ntest(\"MemoryStorage persists sql.js snapshots through the atomic DB snapshot writer\", () => {\n\tconst src = readFileSync(\n\t\tjoin(process.cwd(), \"packages\", \"pi-coding-agent\", \"src\", \"resources\", \"extensions\", \"memory\", \"storage.ts\"),\n\t\t\"utf-8\",\n\t);\n\n\tassert.match(src, /atomicWriteDbSnapshotSync\\(/, \"storage must use atomic DB snapshot writes\");\n\tassert.doesNotMatch(src, /writeFileSync\\(this\\.dbPath/, \"direct live DB overwrite is forbidden\");\n});\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"storage.d.ts","sourceRoot":"","sources":["../../../../src/resources/extensions/memory/storage.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"storage.d.ts","sourceRoot":"","sources":["../../../../src/resources/extensions/memory/storage.ts"],"names":[],"mappings":"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"}
@@ -8,8 +8,9 @@
8
8
  */
9
9
  import initSqlJs from "sql.js";
10
10
  import { randomUUID } from "crypto";
11
- import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
11
+ import { existsSync, mkdirSync, readFileSync } from "fs";
12
12
  import { dirname } from "path";
13
+ import { atomicWriteDbSnapshotSync } from "../../../core/db-snapshot.js";
13
14
  export class MemoryStorage {
14
15
  constructor(db, dbPath) {
15
16
  this.persistTimer = null;
@@ -33,7 +34,7 @@ export class MemoryStorage {
33
34
  }
34
35
  persist() {
35
36
  const data = this.db.export();
36
- writeFileSync(this.dbPath, Buffer.from(data));
37
+ atomicWriteDbSnapshotSync(this.dbPath, data);
37
38
  }
38
39
  schedulePersist() {
39
40
  if (this.persistTimer) {
@@ -1 +1 @@
1
- {"version":3,"file":"storage.js","sourceRoot":"","sources":["../../../../src/resources/extensions/memory/storage.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * SQLite storage for the memory extraction pipeline.\n *\n * Tables:\n * - threads: tracks session files and their processing state\n * - stage1_outputs: stores per-thread extraction results\n * - jobs: lease-based job queue for pipeline phases\n */\n\nimport initSqlJs, { type Database as SqlJsDatabase } from \"sql.js\";\nimport { randomUUID } from \"crypto\";\nimport { existsSync, mkdirSync, readFileSync, writeFileSync } from \"fs\";\nimport { dirname } from \"path\";\n\nexport interface ThreadRow {\n\tthread_id: string;\n\tfile_path: string;\n\tfile_size: number;\n\tfile_mtime: number;\n\tcwd: string;\n\tstatus: \"pending\" | \"processing\" | \"done\" | \"error\";\n\terror_message: string | null;\n\tcreated_at: string;\n\tupdated_at: string;\n}\n\nexport interface Stage1OutputRow {\n\tthread_id: string;\n\textraction_json: string;\n\tcreated_at: string;\n}\n\nexport interface JobRow {\n\tid: string;\n\tphase: \"stage1\" | \"stage2\";\n\tthread_id: string | null;\n\tstatus: \"pending\" | \"claimed\" | \"done\" | \"error\";\n\tworker_id: string | null;\n\townership_token: string | null;\n\tlease_expires_at: string | null;\n\terror_message: string | null;\n\tcreated_at: string;\n\tupdated_at: string;\n}\n\nexport class MemoryStorage {\n\tprivate db: SqlJsDatabase;\n\tprivate dbPath: string;\n\tprivate persistTimer: ReturnType<typeof setTimeout> | null = null;\n\n\tprivate constructor(db: SqlJsDatabase, dbPath: string) {\n\t\tthis.db = db;\n\t\tthis.dbPath = dbPath;\n\t}\n\n\tstatic async create(dbPath: string): Promise<MemoryStorage> {\n\t\tconst dir = dirname(dbPath);\n\t\tif (!existsSync(dir)) {\n\t\t\tmkdirSync(dir, { recursive: true });\n\t\t}\n\n\t\tconst SQL = await initSqlJs();\n\t\tconst buffer = existsSync(dbPath) ? readFileSync(dbPath) : undefined;\n\t\tconst db = buffer ? new SQL.Database(buffer) : new SQL.Database();\n\n\t\tdb.run(\"PRAGMA journal_mode = WAL\");\n\t\tdb.run(\"PRAGMA synchronous = NORMAL\");\n\t\tdb.run(\"PRAGMA busy_timeout = 5000\");\n\n\t\tconst storage = new MemoryStorage(db, dbPath);\n\t\tstorage.initSchema();\n\t\treturn storage;\n\t}\n\n\tprivate persist(): void {\n\t\tconst data = this.db.export();\n\t\twriteFileSync(this.dbPath, Buffer.from(data));\n\t}\n\n\tprivate schedulePersist(): void {\n\t\tif (this.persistTimer) {\n\t\t\tclearTimeout(this.persistTimer);\n\t\t}\n\t\tthis.persistTimer = setTimeout(() => {\n\t\t\tthis.persistTimer = null;\n\t\t\tthis.persist();\n\t\t}, 500);\n\t}\n\n\tprivate initSchema(): void {\n\t\tthis.db.run(`\n\t\t\tCREATE TABLE IF NOT EXISTS threads (\n\t\t\t\tthread_id TEXT PRIMARY KEY,\n\t\t\t\tfile_path TEXT NOT NULL,\n\t\t\t\tfile_size INTEGER NOT NULL DEFAULT 0,\n\t\t\t\tfile_mtime INTEGER NOT NULL DEFAULT 0,\n\t\t\t\tcwd TEXT NOT NULL DEFAULT '',\n\t\t\t\tstatus TEXT NOT NULL DEFAULT 'pending',\n\t\t\t\terror_message TEXT,\n\t\t\t\tcreated_at TEXT NOT NULL DEFAULT (datetime('now')),\n\t\t\t\tupdated_at TEXT NOT NULL DEFAULT (datetime('now'))\n\t\t\t)\n\t\t`);\n\t\tthis.db.run(`\n\t\t\tCREATE TABLE IF NOT EXISTS stage1_outputs (\n\t\t\t\tthread_id TEXT PRIMARY KEY,\n\t\t\t\textraction_json TEXT NOT NULL,\n\t\t\t\tcreated_at TEXT NOT NULL DEFAULT (datetime('now')),\n\t\t\t\tFOREIGN KEY (thread_id) REFERENCES threads(thread_id) ON DELETE CASCADE\n\t\t\t)\n\t\t`);\n\t\tthis.db.run(`\n\t\t\tCREATE TABLE IF NOT EXISTS jobs (\n\t\t\t\tid TEXT PRIMARY KEY,\n\t\t\t\tphase TEXT NOT NULL,\n\t\t\t\tthread_id TEXT,\n\t\t\t\tstatus TEXT NOT NULL DEFAULT 'pending',\n\t\t\t\tworker_id TEXT,\n\t\t\t\townership_token TEXT,\n\t\t\t\tlease_expires_at TEXT,\n\t\t\t\terror_message TEXT,\n\t\t\t\tcreated_at TEXT NOT NULL DEFAULT (datetime('now')),\n\t\t\t\tupdated_at TEXT NOT NULL DEFAULT (datetime('now'))\n\t\t\t)\n\t\t`);\n\t\tthis.db.run(\"CREATE INDEX IF NOT EXISTS idx_jobs_phase_status ON jobs(phase, status)\");\n\t\tthis.db.run(\"CREATE INDEX IF NOT EXISTS idx_threads_status ON threads(status)\");\n\t\tthis.db.run(\"CREATE INDEX IF NOT EXISTS idx_threads_cwd ON threads(cwd)\");\n\t\tthis.persist();\n\t}\n\n\tprivate queryAll<T>(sql: string, params: unknown[] = []): T[] {\n\t\tconst stmt = this.db.prepare(sql);\n\t\tstmt.bind(params as (string | number | null | Uint8Array)[]);\n\t\tconst rows: T[] = [];\n\t\twhile (stmt.step()) {\n\t\t\trows.push(stmt.getAsObject() as T);\n\t\t}\n\t\tstmt.free();\n\t\treturn rows;\n\t}\n\n\tprivate queryOne<T>(sql: string, params: unknown[] = []): T | undefined {\n\t\tconst rows = this.queryAll<T>(sql, params);\n\t\treturn rows[0];\n\t}\n\n\t/**\n\t * Insert or update thread records. Skips threads whose file hasn't changed\n\t * (same size + mtime = watermark match).\n\t */\n\tupsertThreads(\n\t\tthreads: Array<{\n\t\t\tthreadId: string;\n\t\t\tfilePath: string;\n\t\t\tfileSize: number;\n\t\t\tfileMtime: number;\n\t\t\tcwd: string;\n\t\t}>,\n\t): { inserted: number; updated: number; skipped: number } {\n\t\tlet inserted = 0;\n\t\tlet updated = 0;\n\t\tlet skipped = 0;\n\n\t\tfor (const t of threads) {\n\t\t\tconst existing = this.queryOne<{ file_size: number; file_mtime: number; status: string }>(\n\t\t\t\t\"SELECT file_size, file_mtime, status FROM threads WHERE thread_id = ?\",\n\t\t\t\t[t.threadId],\n\t\t\t);\n\n\t\t\tif (!existing) {\n\t\t\t\tthis.db.run(\n\t\t\t\t\t\"INSERT INTO threads (thread_id, file_path, file_size, file_mtime, cwd, status) VALUES (?, ?, ?, ?, ?, 'pending')\",\n\t\t\t\t\t[t.threadId, t.filePath, t.fileSize, t.fileMtime, t.cwd],\n\t\t\t\t);\n\t\t\t\tthis.db.run(\n\t\t\t\t\t\"INSERT OR IGNORE INTO jobs (id, phase, thread_id, status) VALUES (?, 'stage1', ?, 'pending')\",\n\t\t\t\t\t[randomUUID(), t.threadId],\n\t\t\t\t);\n\t\t\t\tinserted++;\n\t\t\t} else if (existing.file_size !== t.fileSize || existing.file_mtime !== t.fileMtime) {\n\t\t\t\tthis.db.run(\n\t\t\t\t\t\"UPDATE threads SET file_path = ?, file_size = ?, file_mtime = ?, cwd = ?, status = 'pending', updated_at = datetime('now') WHERE thread_id = ?\",\n\t\t\t\t\t[t.filePath, t.fileSize, t.fileMtime, t.cwd, t.threadId],\n\t\t\t\t);\n\t\t\t\tif (existing.status === \"done\" || existing.status === \"error\") {\n\t\t\t\t\tthis.db.run(\n\t\t\t\t\t\t\"INSERT OR IGNORE INTO jobs (id, phase, thread_id, status) VALUES (?, 'stage1', ?, 'pending')\",\n\t\t\t\t\t\t[randomUUID(), t.threadId],\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tupdated++;\n\t\t\t} else {\n\t\t\t\tskipped++;\n\t\t\t}\n\t\t}\n\n\t\tthis.schedulePersist();\n\t\treturn { inserted, updated, skipped };\n\t}\n\n\t/**\n\t * Claim up to `limit` stage1 jobs for the given worker.\n\t * Uses lease-based ownership with an ownership_token UUID.\n\t */\n\tclaimStage1Jobs(\n\t\tworkerId: string,\n\t\tlimit: number,\n\t\tleaseSeconds: number,\n\t): Array<{ jobId: string; threadId: string; ownershipToken: string }> {\n\t\tconst token = randomUUID();\n\t\tconst expiresAt = new Date(Date.now() + leaseSeconds * 1000).toISOString();\n\n\t\tthis.db.run(\n\t\t\t`UPDATE jobs SET\n\t\t\t\tstatus = 'claimed',\n\t\t\t\tworker_id = ?,\n\t\t\t\townership_token = ?,\n\t\t\t\tlease_expires_at = ?,\n\t\t\t\tupdated_at = datetime('now')\n\t\t\tWHERE id IN (\n\t\t\t\tSELECT id FROM jobs\n\t\t\t\tWHERE phase = 'stage1'\n\t\t\t\t\tAND (status = 'pending' OR (status = 'claimed' AND lease_expires_at < datetime('now')))\n\t\t\t\tLIMIT ?\n\t\t\t)`,\n\t\t\t[workerId, token, expiresAt, limit],\n\t\t);\n\n\t\tconst rows = this.queryAll<{ id: string; thread_id: string }>(\n\t\t\t\"SELECT id, thread_id FROM jobs WHERE ownership_token = ? AND status = 'claimed'\",\n\t\t\t[token],\n\t\t);\n\n\t\tthis.schedulePersist();\n\n\t\treturn rows.map((r) => ({\n\t\t\tjobId: r.id,\n\t\t\tthreadId: r.thread_id,\n\t\t\townershipToken: token,\n\t\t}));\n\t}\n\n\t/**\n\t * Mark a stage1 job as complete and store the extraction output.\n\t */\n\tcompleteStage1Job(threadId: string, output: string): void {\n\t\tthis.db.run(\n\t\t\t\"UPDATE jobs SET status = 'done', updated_at = datetime('now') WHERE thread_id = ? AND phase = 'stage1' AND status = 'claimed'\",\n\t\t\t[threadId],\n\t\t);\n\t\tthis.db.run(\n\t\t\t\"INSERT OR REPLACE INTO stage1_outputs (thread_id, extraction_json, created_at) VALUES (?, ?, datetime('now'))\",\n\t\t\t[threadId, output],\n\t\t);\n\t\tthis.db.run(\n\t\t\t\"UPDATE threads SET status = 'done', updated_at = datetime('now') WHERE thread_id = ?\",\n\t\t\t[threadId],\n\t\t);\n\t\tthis.schedulePersist();\n\t}\n\n\t/**\n\t * Mark a stage1 job as errored.\n\t */\n\tfailStage1Job(threadId: string, errorMessage: string): void {\n\t\tthis.db.run(\n\t\t\t\"UPDATE jobs SET status = 'error', error_message = ?, updated_at = datetime('now') WHERE thread_id = ? AND phase = 'stage1' AND status = 'claimed'\",\n\t\t\t[errorMessage, threadId],\n\t\t);\n\t\tthis.db.run(\n\t\t\t\"UPDATE threads SET status = 'error', error_message = ?, updated_at = datetime('now') WHERE thread_id = ?\",\n\t\t\t[errorMessage, threadId],\n\t\t);\n\t\tthis.schedulePersist();\n\t}\n\n\t/**\n\t * Try to claim the global phase 2 consolidation job.\n\t * Only one worker can hold this at a time.\n\t */\n\ttryClaimGlobalPhase2Job(\n\t\tworkerId: string,\n\t\tleaseSeconds: number,\n\t): { jobId: string; ownershipToken: string } | null {\n\t\tconst token = randomUUID();\n\t\tconst expiresAt = new Date(Date.now() + leaseSeconds * 1000).toISOString();\n\n\t\tconst pendingStage1 = this.queryOne<{ cnt: number }>(\n\t\t\t\"SELECT COUNT(*) as cnt FROM jobs WHERE phase = 'stage1' AND status IN ('pending', 'claimed')\",\n\t\t);\n\n\t\tif (pendingStage1 && pendingStage1.cnt > 0) {\n\t\t\treturn null;\n\t\t}\n\n\t\tconst existingPhase2 = this.queryOne<{ id: string }>(\n\t\t\t\"SELECT id FROM jobs WHERE phase = 'stage2' AND status = 'claimed' AND lease_expires_at > datetime('now')\",\n\t\t);\n\n\t\tif (existingPhase2) {\n\t\t\treturn null;\n\t\t}\n\n\t\tconst outputCount = this.queryOne<{ cnt: number }>(\n\t\t\t\"SELECT COUNT(*) as cnt FROM stage1_outputs\",\n\t\t);\n\n\t\tif (!outputCount || outputCount.cnt === 0) {\n\t\t\treturn null;\n\t\t}\n\n\t\tconst jobId = randomUUID();\n\t\tthis.db.run(\n\t\t\t\"INSERT INTO jobs (id, phase, status, worker_id, ownership_token, lease_expires_at) VALUES (?, 'stage2', 'claimed', ?, ?, ?)\",\n\t\t\t[jobId, workerId, token, expiresAt],\n\t\t);\n\n\t\tthis.schedulePersist();\n\t\treturn { jobId, ownershipToken: token };\n\t}\n\n\t/**\n\t * Complete the phase 2 consolidation job.\n\t */\n\tcompletePhase2Job(jobId: string): void {\n\t\tthis.db.run(\n\t\t\t\"UPDATE jobs SET status = 'done', updated_at = datetime('now') WHERE id = ? AND phase = 'stage2'\",\n\t\t\t[jobId],\n\t\t);\n\t\tthis.schedulePersist();\n\t}\n\n\t/**\n\t * Get all stage1 extraction outputs.\n\t */\n\tgetStage1Outputs(): Array<{ threadId: string; extractionJson: string }> {\n\t\tconst rows = this.queryAll<{ thread_id: string; extraction_json: string }>(\n\t\t\t\"SELECT thread_id, extraction_json FROM stage1_outputs\",\n\t\t);\n\n\t\treturn rows.map((r) => ({\n\t\t\tthreadId: r.thread_id,\n\t\t\textractionJson: r.extraction_json,\n\t\t}));\n\t}\n\n\t/**\n\t * Get all stage1 outputs for a specific cwd.\n\t */\n\tgetStage1OutputsForCwd(cwd: string): Array<{ threadId: string; extractionJson: string }> {\n\t\tconst rows = this.queryAll<{ thread_id: string; extraction_json: string }>(\n\t\t\t`SELECT s.thread_id, s.extraction_json FROM stage1_outputs s\n\t\t\tINNER JOIN threads t ON t.thread_id = s.thread_id\n\t\t\tWHERE t.cwd = ?`,\n\t\t\t[cwd],\n\t\t);\n\n\t\treturn rows.map((r) => ({\n\t\t\tthreadId: r.thread_id,\n\t\t\textractionJson: r.extraction_json,\n\t\t}));\n\t}\n\n\t/**\n\t * Get thread info by ID.\n\t */\n\tgetThread(threadId: string): ThreadRow | undefined {\n\t\treturn this.queryOne<ThreadRow>(\n\t\t\t\"SELECT * FROM threads WHERE thread_id = ?\",\n\t\t\t[threadId],\n\t\t);\n\t}\n\n\t/**\n\t * Get pipeline statistics.\n\t */\n\tgetStats(): {\n\t\ttotalThreads: number;\n\t\tpendingThreads: number;\n\t\tdoneThreads: number;\n\t\terrorThreads: number;\n\t\ttotalStage1Outputs: number;\n\t\tpendingStage1Jobs: number;\n\t} {\n\t\tconst threads = this.queryOne<{ total: number; pending: number; done: number; errors: number }>(`\n\t\t\tSELECT\n\t\t\t\tCOUNT(*) as total,\n\t\t\t\tSUM(CASE WHEN status = 'pending' THEN 1 ELSE 0 END) as pending,\n\t\t\t\tSUM(CASE WHEN status = 'done' THEN 1 ELSE 0 END) as done,\n\t\t\t\tSUM(CASE WHEN status = 'error' THEN 1 ELSE 0 END) as errors\n\t\t\tFROM threads\n\t\t`)!;\n\n\t\tconst outputs = this.queryOne<{ cnt: number }>(\n\t\t\t\"SELECT COUNT(*) as cnt FROM stage1_outputs\",\n\t\t)!;\n\n\t\tconst pendingJobs = this.queryOne<{ cnt: number }>(\n\t\t\t\"SELECT COUNT(*) as cnt FROM jobs WHERE phase = 'stage1' AND status IN ('pending', 'claimed')\",\n\t\t)!;\n\n\t\treturn {\n\t\t\ttotalThreads: threads.total,\n\t\t\tpendingThreads: threads.pending,\n\t\t\tdoneThreads: threads.done,\n\t\t\terrorThreads: threads.errors,\n\t\t\ttotalStage1Outputs: outputs.cnt,\n\t\t\tpendingStage1Jobs: pendingJobs.cnt,\n\t\t};\n\t}\n\n\t/**\n\t * Clear all data (for /memory clear).\n\t */\n\tclearAll(): void {\n\t\tthis.db.run(\"DELETE FROM stage1_outputs\");\n\t\tthis.db.run(\"DELETE FROM jobs\");\n\t\tthis.db.run(\"DELETE FROM threads\");\n\t\tthis.schedulePersist();\n\t}\n\n\t/**\n\t * Clear data for a specific cwd (for /memory clear in project scope).\n\t */\n\tclearForCwd(cwd: string): void {\n\t\tthis.db.run(\n\t\t\t\"DELETE FROM stage1_outputs WHERE thread_id IN (SELECT thread_id FROM threads WHERE cwd = ?)\",\n\t\t\t[cwd],\n\t\t);\n\t\tthis.db.run(\n\t\t\t\"DELETE FROM jobs WHERE thread_id IN (SELECT thread_id FROM threads WHERE cwd = ?)\",\n\t\t\t[cwd],\n\t\t);\n\t\tthis.db.run(\"DELETE FROM threads WHERE cwd = ?\", [cwd]);\n\t\tthis.schedulePersist();\n\t}\n\n\t/**\n\t * Reset all threads to pending (for /memory rebuild).\n\t */\n\tresetAllForCwd(cwd: string): void {\n\t\tthis.db.run(\n\t\t\t\"DELETE FROM stage1_outputs WHERE thread_id IN (SELECT thread_id FROM threads WHERE cwd = ?)\",\n\t\t\t[cwd],\n\t\t);\n\t\tthis.db.run(\n\t\t\t\"DELETE FROM jobs WHERE thread_id IN (SELECT thread_id FROM threads WHERE cwd = ?)\",\n\t\t\t[cwd],\n\t\t);\n\t\tthis.db.run(\n\t\t\t\"UPDATE threads SET status = 'pending', updated_at = datetime('now') WHERE cwd = ?\",\n\t\t\t[cwd],\n\t\t);\n\n\t\tconst threads = this.queryAll<{ thread_id: string }>(\n\t\t\t\"SELECT thread_id FROM threads WHERE cwd = ?\",\n\t\t\t[cwd],\n\t\t);\n\n\t\tfor (const t of threads) {\n\t\t\tthis.db.run(\n\t\t\t\t\"INSERT INTO jobs (id, phase, thread_id, status) VALUES (?, 'stage1', ?, 'pending')\",\n\t\t\t\t[randomUUID(), t.thread_id],\n\t\t\t);\n\t\t}\n\t\tthis.schedulePersist();\n\t}\n\n\tclose(): void {\n\t\tif (this.persistTimer) {\n\t\t\tclearTimeout(this.persistTimer);\n\t\t\tthis.persistTimer = null;\n\t\t}\n\t\tthis.persist();\n\t\tthis.db.close();\n\t}\n}\n"]}
1
+ {"version":3,"file":"storage.js","sourceRoot":"","sources":["../../../../src/resources/extensions/memory/storage.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * SQLite storage for the memory extraction pipeline.\n *\n * Tables:\n * - threads: tracks session files and their processing state\n * - stage1_outputs: stores per-thread extraction results\n * - jobs: lease-based job queue for pipeline phases\n */\n\nimport initSqlJs, { type Database as SqlJsDatabase } from \"sql.js\";\nimport { randomUUID } from \"crypto\";\nimport { existsSync, mkdirSync, readFileSync } from \"fs\";\nimport { dirname } from \"path\";\nimport { atomicWriteDbSnapshotSync } from \"../../../core/db-snapshot.js\";\n\nexport interface ThreadRow {\n\tthread_id: string;\n\tfile_path: string;\n\tfile_size: number;\n\tfile_mtime: number;\n\tcwd: string;\n\tstatus: \"pending\" | \"processing\" | \"done\" | \"error\";\n\terror_message: string | null;\n\tcreated_at: string;\n\tupdated_at: string;\n}\n\nexport interface Stage1OutputRow {\n\tthread_id: string;\n\textraction_json: string;\n\tcreated_at: string;\n}\n\nexport interface JobRow {\n\tid: string;\n\tphase: \"stage1\" | \"stage2\";\n\tthread_id: string | null;\n\tstatus: \"pending\" | \"claimed\" | \"done\" | \"error\";\n\tworker_id: string | null;\n\townership_token: string | null;\n\tlease_expires_at: string | null;\n\terror_message: string | null;\n\tcreated_at: string;\n\tupdated_at: string;\n}\n\nexport class MemoryStorage {\n\tprivate db: SqlJsDatabase;\n\tprivate dbPath: string;\n\tprivate persistTimer: ReturnType<typeof setTimeout> | null = null;\n\n\tprivate constructor(db: SqlJsDatabase, dbPath: string) {\n\t\tthis.db = db;\n\t\tthis.dbPath = dbPath;\n\t}\n\n\tstatic async create(dbPath: string): Promise<MemoryStorage> {\n\t\tconst dir = dirname(dbPath);\n\t\tif (!existsSync(dir)) {\n\t\t\tmkdirSync(dir, { recursive: true });\n\t\t}\n\n\t\tconst SQL = await initSqlJs();\n\t\tconst buffer = existsSync(dbPath) ? readFileSync(dbPath) : undefined;\n\t\tconst db = buffer ? new SQL.Database(buffer) : new SQL.Database();\n\n\t\tdb.run(\"PRAGMA journal_mode = WAL\");\n\t\tdb.run(\"PRAGMA synchronous = NORMAL\");\n\t\tdb.run(\"PRAGMA busy_timeout = 5000\");\n\n\t\tconst storage = new MemoryStorage(db, dbPath);\n\t\tstorage.initSchema();\n\t\treturn storage;\n\t}\n\n\tprivate persist(): void {\n\t\tconst data = this.db.export();\n\t\tatomicWriteDbSnapshotSync(this.dbPath, data);\n\t}\n\n\tprivate schedulePersist(): void {\n\t\tif (this.persistTimer) {\n\t\t\tclearTimeout(this.persistTimer);\n\t\t}\n\t\tthis.persistTimer = setTimeout(() => {\n\t\t\tthis.persistTimer = null;\n\t\t\tthis.persist();\n\t\t}, 500);\n\t}\n\n\tprivate initSchema(): void {\n\t\tthis.db.run(`\n\t\t\tCREATE TABLE IF NOT EXISTS threads (\n\t\t\t\tthread_id TEXT PRIMARY KEY,\n\t\t\t\tfile_path TEXT NOT NULL,\n\t\t\t\tfile_size INTEGER NOT NULL DEFAULT 0,\n\t\t\t\tfile_mtime INTEGER NOT NULL DEFAULT 0,\n\t\t\t\tcwd TEXT NOT NULL DEFAULT '',\n\t\t\t\tstatus TEXT NOT NULL DEFAULT 'pending',\n\t\t\t\terror_message TEXT,\n\t\t\t\tcreated_at TEXT NOT NULL DEFAULT (datetime('now')),\n\t\t\t\tupdated_at TEXT NOT NULL DEFAULT (datetime('now'))\n\t\t\t)\n\t\t`);\n\t\tthis.db.run(`\n\t\t\tCREATE TABLE IF NOT EXISTS stage1_outputs (\n\t\t\t\tthread_id TEXT PRIMARY KEY,\n\t\t\t\textraction_json TEXT NOT NULL,\n\t\t\t\tcreated_at TEXT NOT NULL DEFAULT (datetime('now')),\n\t\t\t\tFOREIGN KEY (thread_id) REFERENCES threads(thread_id) ON DELETE CASCADE\n\t\t\t)\n\t\t`);\n\t\tthis.db.run(`\n\t\t\tCREATE TABLE IF NOT EXISTS jobs (\n\t\t\t\tid TEXT PRIMARY KEY,\n\t\t\t\tphase TEXT NOT NULL,\n\t\t\t\tthread_id TEXT,\n\t\t\t\tstatus TEXT NOT NULL DEFAULT 'pending',\n\t\t\t\tworker_id TEXT,\n\t\t\t\townership_token TEXT,\n\t\t\t\tlease_expires_at TEXT,\n\t\t\t\terror_message TEXT,\n\t\t\t\tcreated_at TEXT NOT NULL DEFAULT (datetime('now')),\n\t\t\t\tupdated_at TEXT NOT NULL DEFAULT (datetime('now'))\n\t\t\t)\n\t\t`);\n\t\tthis.db.run(\"CREATE INDEX IF NOT EXISTS idx_jobs_phase_status ON jobs(phase, status)\");\n\t\tthis.db.run(\"CREATE INDEX IF NOT EXISTS idx_threads_status ON threads(status)\");\n\t\tthis.db.run(\"CREATE INDEX IF NOT EXISTS idx_threads_cwd ON threads(cwd)\");\n\t\tthis.persist();\n\t}\n\n\tprivate queryAll<T>(sql: string, params: unknown[] = []): T[] {\n\t\tconst stmt = this.db.prepare(sql);\n\t\tstmt.bind(params as (string | number | null | Uint8Array)[]);\n\t\tconst rows: T[] = [];\n\t\twhile (stmt.step()) {\n\t\t\trows.push(stmt.getAsObject() as T);\n\t\t}\n\t\tstmt.free();\n\t\treturn rows;\n\t}\n\n\tprivate queryOne<T>(sql: string, params: unknown[] = []): T | undefined {\n\t\tconst rows = this.queryAll<T>(sql, params);\n\t\treturn rows[0];\n\t}\n\n\t/**\n\t * Insert or update thread records. Skips threads whose file hasn't changed\n\t * (same size + mtime = watermark match).\n\t */\n\tupsertThreads(\n\t\tthreads: Array<{\n\t\t\tthreadId: string;\n\t\t\tfilePath: string;\n\t\t\tfileSize: number;\n\t\t\tfileMtime: number;\n\t\t\tcwd: string;\n\t\t}>,\n\t): { inserted: number; updated: number; skipped: number } {\n\t\tlet inserted = 0;\n\t\tlet updated = 0;\n\t\tlet skipped = 0;\n\n\t\tfor (const t of threads) {\n\t\t\tconst existing = this.queryOne<{ file_size: number; file_mtime: number; status: string }>(\n\t\t\t\t\"SELECT file_size, file_mtime, status FROM threads WHERE thread_id = ?\",\n\t\t\t\t[t.threadId],\n\t\t\t);\n\n\t\t\tif (!existing) {\n\t\t\t\tthis.db.run(\n\t\t\t\t\t\"INSERT INTO threads (thread_id, file_path, file_size, file_mtime, cwd, status) VALUES (?, ?, ?, ?, ?, 'pending')\",\n\t\t\t\t\t[t.threadId, t.filePath, t.fileSize, t.fileMtime, t.cwd],\n\t\t\t\t);\n\t\t\t\tthis.db.run(\n\t\t\t\t\t\"INSERT OR IGNORE INTO jobs (id, phase, thread_id, status) VALUES (?, 'stage1', ?, 'pending')\",\n\t\t\t\t\t[randomUUID(), t.threadId],\n\t\t\t\t);\n\t\t\t\tinserted++;\n\t\t\t} else if (existing.file_size !== t.fileSize || existing.file_mtime !== t.fileMtime) {\n\t\t\t\tthis.db.run(\n\t\t\t\t\t\"UPDATE threads SET file_path = ?, file_size = ?, file_mtime = ?, cwd = ?, status = 'pending', updated_at = datetime('now') WHERE thread_id = ?\",\n\t\t\t\t\t[t.filePath, t.fileSize, t.fileMtime, t.cwd, t.threadId],\n\t\t\t\t);\n\t\t\t\tif (existing.status === \"done\" || existing.status === \"error\") {\n\t\t\t\t\tthis.db.run(\n\t\t\t\t\t\t\"INSERT OR IGNORE INTO jobs (id, phase, thread_id, status) VALUES (?, 'stage1', ?, 'pending')\",\n\t\t\t\t\t\t[randomUUID(), t.threadId],\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tupdated++;\n\t\t\t} else {\n\t\t\t\tskipped++;\n\t\t\t}\n\t\t}\n\n\t\tthis.schedulePersist();\n\t\treturn { inserted, updated, skipped };\n\t}\n\n\t/**\n\t * Claim up to `limit` stage1 jobs for the given worker.\n\t * Uses lease-based ownership with an ownership_token UUID.\n\t */\n\tclaimStage1Jobs(\n\t\tworkerId: string,\n\t\tlimit: number,\n\t\tleaseSeconds: number,\n\t): Array<{ jobId: string; threadId: string; ownershipToken: string }> {\n\t\tconst token = randomUUID();\n\t\tconst expiresAt = new Date(Date.now() + leaseSeconds * 1000).toISOString();\n\n\t\tthis.db.run(\n\t\t\t`UPDATE jobs SET\n\t\t\t\tstatus = 'claimed',\n\t\t\t\tworker_id = ?,\n\t\t\t\townership_token = ?,\n\t\t\t\tlease_expires_at = ?,\n\t\t\t\tupdated_at = datetime('now')\n\t\t\tWHERE id IN (\n\t\t\t\tSELECT id FROM jobs\n\t\t\t\tWHERE phase = 'stage1'\n\t\t\t\t\tAND (status = 'pending' OR (status = 'claimed' AND lease_expires_at < datetime('now')))\n\t\t\t\tLIMIT ?\n\t\t\t)`,\n\t\t\t[workerId, token, expiresAt, limit],\n\t\t);\n\n\t\tconst rows = this.queryAll<{ id: string; thread_id: string }>(\n\t\t\t\"SELECT id, thread_id FROM jobs WHERE ownership_token = ? AND status = 'claimed'\",\n\t\t\t[token],\n\t\t);\n\n\t\tthis.schedulePersist();\n\n\t\treturn rows.map((r) => ({\n\t\t\tjobId: r.id,\n\t\t\tthreadId: r.thread_id,\n\t\t\townershipToken: token,\n\t\t}));\n\t}\n\n\t/**\n\t * Mark a stage1 job as complete and store the extraction output.\n\t */\n\tcompleteStage1Job(threadId: string, output: string): void {\n\t\tthis.db.run(\n\t\t\t\"UPDATE jobs SET status = 'done', updated_at = datetime('now') WHERE thread_id = ? AND phase = 'stage1' AND status = 'claimed'\",\n\t\t\t[threadId],\n\t\t);\n\t\tthis.db.run(\n\t\t\t\"INSERT OR REPLACE INTO stage1_outputs (thread_id, extraction_json, created_at) VALUES (?, ?, datetime('now'))\",\n\t\t\t[threadId, output],\n\t\t);\n\t\tthis.db.run(\n\t\t\t\"UPDATE threads SET status = 'done', updated_at = datetime('now') WHERE thread_id = ?\",\n\t\t\t[threadId],\n\t\t);\n\t\tthis.schedulePersist();\n\t}\n\n\t/**\n\t * Mark a stage1 job as errored.\n\t */\n\tfailStage1Job(threadId: string, errorMessage: string): void {\n\t\tthis.db.run(\n\t\t\t\"UPDATE jobs SET status = 'error', error_message = ?, updated_at = datetime('now') WHERE thread_id = ? AND phase = 'stage1' AND status = 'claimed'\",\n\t\t\t[errorMessage, threadId],\n\t\t);\n\t\tthis.db.run(\n\t\t\t\"UPDATE threads SET status = 'error', error_message = ?, updated_at = datetime('now') WHERE thread_id = ?\",\n\t\t\t[errorMessage, threadId],\n\t\t);\n\t\tthis.schedulePersist();\n\t}\n\n\t/**\n\t * Try to claim the global phase 2 consolidation job.\n\t * Only one worker can hold this at a time.\n\t */\n\ttryClaimGlobalPhase2Job(\n\t\tworkerId: string,\n\t\tleaseSeconds: number,\n\t): { jobId: string; ownershipToken: string } | null {\n\t\tconst token = randomUUID();\n\t\tconst expiresAt = new Date(Date.now() + leaseSeconds * 1000).toISOString();\n\n\t\tconst pendingStage1 = this.queryOne<{ cnt: number }>(\n\t\t\t\"SELECT COUNT(*) as cnt FROM jobs WHERE phase = 'stage1' AND status IN ('pending', 'claimed')\",\n\t\t);\n\n\t\tif (pendingStage1 && pendingStage1.cnt > 0) {\n\t\t\treturn null;\n\t\t}\n\n\t\tconst existingPhase2 = this.queryOne<{ id: string }>(\n\t\t\t\"SELECT id FROM jobs WHERE phase = 'stage2' AND status = 'claimed' AND lease_expires_at > datetime('now')\",\n\t\t);\n\n\t\tif (existingPhase2) {\n\t\t\treturn null;\n\t\t}\n\n\t\tconst outputCount = this.queryOne<{ cnt: number }>(\n\t\t\t\"SELECT COUNT(*) as cnt FROM stage1_outputs\",\n\t\t);\n\n\t\tif (!outputCount || outputCount.cnt === 0) {\n\t\t\treturn null;\n\t\t}\n\n\t\tconst jobId = randomUUID();\n\t\tthis.db.run(\n\t\t\t\"INSERT INTO jobs (id, phase, status, worker_id, ownership_token, lease_expires_at) VALUES (?, 'stage2', 'claimed', ?, ?, ?)\",\n\t\t\t[jobId, workerId, token, expiresAt],\n\t\t);\n\n\t\tthis.schedulePersist();\n\t\treturn { jobId, ownershipToken: token };\n\t}\n\n\t/**\n\t * Complete the phase 2 consolidation job.\n\t */\n\tcompletePhase2Job(jobId: string): void {\n\t\tthis.db.run(\n\t\t\t\"UPDATE jobs SET status = 'done', updated_at = datetime('now') WHERE id = ? AND phase = 'stage2'\",\n\t\t\t[jobId],\n\t\t);\n\t\tthis.schedulePersist();\n\t}\n\n\t/**\n\t * Get all stage1 extraction outputs.\n\t */\n\tgetStage1Outputs(): Array<{ threadId: string; extractionJson: string }> {\n\t\tconst rows = this.queryAll<{ thread_id: string; extraction_json: string }>(\n\t\t\t\"SELECT thread_id, extraction_json FROM stage1_outputs\",\n\t\t);\n\n\t\treturn rows.map((r) => ({\n\t\t\tthreadId: r.thread_id,\n\t\t\textractionJson: r.extraction_json,\n\t\t}));\n\t}\n\n\t/**\n\t * Get all stage1 outputs for a specific cwd.\n\t */\n\tgetStage1OutputsForCwd(cwd: string): Array<{ threadId: string; extractionJson: string }> {\n\t\tconst rows = this.queryAll<{ thread_id: string; extraction_json: string }>(\n\t\t\t`SELECT s.thread_id, s.extraction_json FROM stage1_outputs s\n\t\t\tINNER JOIN threads t ON t.thread_id = s.thread_id\n\t\t\tWHERE t.cwd = ?`,\n\t\t\t[cwd],\n\t\t);\n\n\t\treturn rows.map((r) => ({\n\t\t\tthreadId: r.thread_id,\n\t\t\textractionJson: r.extraction_json,\n\t\t}));\n\t}\n\n\t/**\n\t * Get thread info by ID.\n\t */\n\tgetThread(threadId: string): ThreadRow | undefined {\n\t\treturn this.queryOne<ThreadRow>(\n\t\t\t\"SELECT * FROM threads WHERE thread_id = ?\",\n\t\t\t[threadId],\n\t\t);\n\t}\n\n\t/**\n\t * Get pipeline statistics.\n\t */\n\tgetStats(): {\n\t\ttotalThreads: number;\n\t\tpendingThreads: number;\n\t\tdoneThreads: number;\n\t\terrorThreads: number;\n\t\ttotalStage1Outputs: number;\n\t\tpendingStage1Jobs: number;\n\t} {\n\t\tconst threads = this.queryOne<{ total: number; pending: number; done: number; errors: number }>(`\n\t\t\tSELECT\n\t\t\t\tCOUNT(*) as total,\n\t\t\t\tSUM(CASE WHEN status = 'pending' THEN 1 ELSE 0 END) as pending,\n\t\t\t\tSUM(CASE WHEN status = 'done' THEN 1 ELSE 0 END) as done,\n\t\t\t\tSUM(CASE WHEN status = 'error' THEN 1 ELSE 0 END) as errors\n\t\t\tFROM threads\n\t\t`)!;\n\n\t\tconst outputs = this.queryOne<{ cnt: number }>(\n\t\t\t\"SELECT COUNT(*) as cnt FROM stage1_outputs\",\n\t\t)!;\n\n\t\tconst pendingJobs = this.queryOne<{ cnt: number }>(\n\t\t\t\"SELECT COUNT(*) as cnt FROM jobs WHERE phase = 'stage1' AND status IN ('pending', 'claimed')\",\n\t\t)!;\n\n\t\treturn {\n\t\t\ttotalThreads: threads.total,\n\t\t\tpendingThreads: threads.pending,\n\t\t\tdoneThreads: threads.done,\n\t\t\terrorThreads: threads.errors,\n\t\t\ttotalStage1Outputs: outputs.cnt,\n\t\t\tpendingStage1Jobs: pendingJobs.cnt,\n\t\t};\n\t}\n\n\t/**\n\t * Clear all data (for /memory clear).\n\t */\n\tclearAll(): void {\n\t\tthis.db.run(\"DELETE FROM stage1_outputs\");\n\t\tthis.db.run(\"DELETE FROM jobs\");\n\t\tthis.db.run(\"DELETE FROM threads\");\n\t\tthis.schedulePersist();\n\t}\n\n\t/**\n\t * Clear data for a specific cwd (for /memory clear in project scope).\n\t */\n\tclearForCwd(cwd: string): void {\n\t\tthis.db.run(\n\t\t\t\"DELETE FROM stage1_outputs WHERE thread_id IN (SELECT thread_id FROM threads WHERE cwd = ?)\",\n\t\t\t[cwd],\n\t\t);\n\t\tthis.db.run(\n\t\t\t\"DELETE FROM jobs WHERE thread_id IN (SELECT thread_id FROM threads WHERE cwd = ?)\",\n\t\t\t[cwd],\n\t\t);\n\t\tthis.db.run(\"DELETE FROM threads WHERE cwd = ?\", [cwd]);\n\t\tthis.schedulePersist();\n\t}\n\n\t/**\n\t * Reset all threads to pending (for /memory rebuild).\n\t */\n\tresetAllForCwd(cwd: string): void {\n\t\tthis.db.run(\n\t\t\t\"DELETE FROM stage1_outputs WHERE thread_id IN (SELECT thread_id FROM threads WHERE cwd = ?)\",\n\t\t\t[cwd],\n\t\t);\n\t\tthis.db.run(\n\t\t\t\"DELETE FROM jobs WHERE thread_id IN (SELECT thread_id FROM threads WHERE cwd = ?)\",\n\t\t\t[cwd],\n\t\t);\n\t\tthis.db.run(\n\t\t\t\"UPDATE threads SET status = 'pending', updated_at = datetime('now') WHERE cwd = ?\",\n\t\t\t[cwd],\n\t\t);\n\n\t\tconst threads = this.queryAll<{ thread_id: string }>(\n\t\t\t\"SELECT thread_id FROM threads WHERE cwd = ?\",\n\t\t\t[cwd],\n\t\t);\n\n\t\tfor (const t of threads) {\n\t\t\tthis.db.run(\n\t\t\t\t\"INSERT INTO jobs (id, phase, thread_id, status) VALUES (?, 'stage1', ?, 'pending')\",\n\t\t\t\t[randomUUID(), t.thread_id],\n\t\t\t);\n\t\t}\n\t\tthis.schedulePersist();\n\t}\n\n\tclose(): void {\n\t\tif (this.persistTimer) {\n\t\t\tclearTimeout(this.persistTimer);\n\t\t\tthis.persistTimer = null;\n\t\t}\n\t\tthis.persist();\n\t\tthis.db.close();\n\t}\n}\n"]}
@@ -120,6 +120,7 @@ describe("#4243 — abort() must run before _disconnectFromAgent()", () => {
120
120
 
121
121
  it("newSession() invokes abort() before _disconnectFromAgent()", async () => {
122
122
  const session = await createSession();
123
+ (session as any).agent.state.isStreaming = true;
123
124
  const order = recordCallOrder(session as any, ["abort", "_disconnectFromAgent"]);
124
125
 
125
126
  const ok = await session.newSession();
@@ -138,6 +139,40 @@ describe("#4243 — abort() must run before _disconnectFromAgent()", () => {
138
139
  );
139
140
  });
140
141
 
142
+ it("newSession() waits instead of aborting when the prior turn is idle but not settled", async () => {
143
+ const session = await createSession();
144
+ const order: string[] = [];
145
+ let releaseIdle!: () => void;
146
+ const idle = new Promise<void>((resolve) => {
147
+ releaseIdle = resolve;
148
+ });
149
+
150
+ (session as any).agent.state.isStreaming = false;
151
+ (session as any).agent.waitForIdle = () => {
152
+ order.push("waitForIdle");
153
+ return idle;
154
+ };
155
+ (session as any).abort = async () => {
156
+ order.push("abort");
157
+ };
158
+ const originalDisconnect = (session as any)._disconnectFromAgent.bind(session);
159
+ (session as any)._disconnectFromAgent = () => {
160
+ order.push("_disconnectFromAgent");
161
+ originalDisconnect();
162
+ };
163
+
164
+ const pendingNewSession = session.newSession();
165
+ await Promise.resolve();
166
+ assert.deepEqual(order, ["waitForIdle"]);
167
+ assert.equal(order.includes("abort"), false);
168
+
169
+ releaseIdle();
170
+ const ok = await pendingNewSession;
171
+ assert.equal(ok, true);
172
+ assert.deepEqual(order, ["waitForIdle", "_disconnectFromAgent"]);
173
+ assert.equal(order.includes("abort"), false);
174
+ });
175
+
141
176
  it("newSession() waits instead of aborting while agent_end processing is still streaming", async () => {
142
177
  const session = await createSession();
143
178
  const order: string[] = [];
@@ -432,6 +467,7 @@ describe("#4243 — abort() must run before _disconnectFromAgent()", () => {
432
467
  const sessionFile = session.sessionFile;
433
468
  assert.ok(typeof sessionFile === "string" && sessionFile.length > 0, "need a session file to switch to");
434
469
 
470
+ (session as any).agent.state.isStreaming = true;
435
471
  const order = recordCallOrder(session as any, ["abort", "_disconnectFromAgent"]);
436
472
 
437
473
  const ok = await session.switchSession(sessionFile);
@@ -130,4 +130,29 @@ describe("#3616 — newSession() restores narrowed tool set when cwd unchanged",
130
130
  "cwd-changed branch must rebuild with includeAllExtensionTools: true",
131
131
  );
132
132
  });
133
+
134
+ it("uses explicit workspaceRoot option instead of process.cwd() when rebuilding runtime", async () => {
135
+ const session = await createSession();
136
+ const explicitWorkspaceRoot = mkdtempSync(join(testDir, "explicit-workspace-"));
137
+ (session as any)._cwd = process.cwd();
138
+
139
+ let buildRuntimeCalled = false;
140
+ let capturedBuildOptions: { includeAllExtensionTools?: boolean } | undefined;
141
+ const originalBuild = (session as any)._buildRuntime.bind(session);
142
+ (session as any)._buildRuntime = (options?: { includeAllExtensionTools?: boolean }) => {
143
+ buildRuntimeCalled = true;
144
+ capturedBuildOptions = options;
145
+ return originalBuild(options);
146
+ };
147
+
148
+ const ok = await session.newSession({ workspaceRoot: explicitWorkspaceRoot });
149
+ assert.equal(ok, true);
150
+ assert.equal((session as any)._cwd, explicitWorkspaceRoot);
151
+ assert.ok(buildRuntimeCalled, "explicit workspace root differing from prior root must rebuild runtime");
152
+ assert.strictEqual(
153
+ capturedBuildOptions?.includeAllExtensionTools,
154
+ true,
155
+ "explicit workspaceRoot rebuild must pass includeAllExtensionTools: true",
156
+ );
157
+ });
133
158
  });
@@ -305,6 +305,8 @@ export class AgentSession {
305
305
 
306
306
  // Base system prompt (without extension appends) - used to apply fresh appends each turn
307
307
  private _baseSystemPrompt = "";
308
+ // Optional prompt-only skill catalog filter. Skills remain loaded and invocable by name.
309
+ private _visibleSkillNames: Set<string> | undefined = undefined;
308
310
 
309
311
  constructor(config: AgentSessionConfig) {
310
312
  this.agent = config.agent;
@@ -867,6 +869,25 @@ export class AgentSession {
867
869
  this.agent.setSystemPrompt(this._baseSystemPrompt);
868
870
  }
869
871
 
872
+ /**
873
+ * Set or clear a prompt-only filter for the <available_skills> catalog.
874
+ *
875
+ * This does not unload skills or disable the Skill tool. It only controls
876
+ * which loaded skills are advertised in the system prompt on rebuild.
877
+ */
878
+ setVisibleSkillsByName(skillNames: string[] | undefined): void {
879
+ this._visibleSkillNames = skillNames === undefined
880
+ ? undefined
881
+ : new Set(skillNames.map((name) => name.trim().toLowerCase()).filter(Boolean));
882
+ this._baseSystemPrompt = this._rebuildSystemPrompt(this.getActiveToolNames());
883
+ this.agent.setSystemPrompt(this._baseSystemPrompt);
884
+ }
885
+
886
+ /** Get the current prompt-only skill catalog filter, if one is active. */
887
+ getVisibleSkillNames(): string[] | undefined {
888
+ return this._visibleSkillNames ? [...this._visibleSkillNames] : undefined;
889
+ }
890
+
870
891
  /** Whether compaction or branch summarization is currently running */
871
892
  get isCompacting(): boolean {
872
893
  return this._compactionOrchestrator.isCompacting;
@@ -1045,6 +1066,9 @@ export class AgentSession {
1045
1066
  return buildSystemPrompt({
1046
1067
  cwd: this._cwd,
1047
1068
  skills: loadedSkills,
1069
+ skillFilter: this._visibleSkillNames
1070
+ ? (skill) => this._visibleSkillNames!.has(skill.name.trim().toLowerCase())
1071
+ : undefined,
1048
1072
  contextFiles: loadedContextFiles,
1049
1073
  customPrompt: loaderSystemPrompt,
1050
1074
  appendSystemPrompt,
@@ -1626,6 +1650,11 @@ export class AgentSession {
1626
1650
  // message_end/agent_end events fire while listeners are still connected.
1627
1651
  // During agent_end handling the turn is already ending; aborting there can
1628
1652
  // convert a successful auto-mode handoff into an aborted provider message.
1653
+ if (!this.agent.state.isStreaming) {
1654
+ this._retryHandler.abortRetry();
1655
+ await this.agent.waitForIdle();
1656
+ return;
1657
+ }
1629
1658
  await this.abort();
1630
1659
  }
1631
1660
 
@@ -1640,6 +1669,8 @@ export class AgentSession {
1640
1669
  async newSession(options?: {
1641
1670
  parentSession?: string;
1642
1671
  setup?: (sessionManager: SessionManager) => Promise<void>;
1672
+ /** Explicit workspace root for the new session/tool runtime. */
1673
+ workspaceRoot?: string;
1643
1674
  /** See ExtensionCommandContext.newSession for docs (#3731). */
1644
1675
  abortSignal?: AbortSignal;
1645
1676
  }): Promise<boolean> {
@@ -1661,10 +1692,10 @@ export class AgentSession {
1661
1692
  try {
1662
1693
  await this._settleCurrentTurnForSessionTransition();
1663
1694
 
1664
- // #3731: If the caller aborted (e.g. runUnit() timed out and restored cwd to
1665
- // project root), discard this session before capturing process.cwd() and
1666
- // rebuilding the tool runtime. Without this check, the late newSession()
1667
- // would rebuild tools with root cwd, breaking worktree isolation.
1695
+ // #3731: If the caller aborted (e.g. runUnit() timed out while the
1696
+ // worktree was being torn down), discard this session before rebuilding
1697
+ // the tool runtime. Without this check, the late newSession() could
1698
+ // rebuild tools with a stale workspace root.
1668
1699
  if (options?.abortSignal?.aborted) {
1669
1700
  return false;
1670
1701
  }
@@ -1674,15 +1705,18 @@ export class AgentSession {
1674
1705
  } finally {
1675
1706
  this._sessionSwitchPending = false;
1676
1707
  }
1677
- // Update cwd to current process directory auto-mode may have chdir'd
1678
- // into a worktree since the original session was created.
1708
+ // Update the workspace root for the new tool runtime. Auto-mode passes
1709
+ // this explicitly so session routing does not depend on global
1710
+ // process.cwd() after worktree merge/teardown. Other callers keep the
1711
+ // historical default.
1679
1712
  const previousCwd = this._cwd;
1680
- this._cwd = process.cwd();
1713
+ this._cwd = options?.workspaceRoot ?? process.cwd();
1681
1714
  this.sessionManager.newSession({ parentSession: options?.parentSession });
1682
1715
  this.agent.sessionId = this.sessionManager.getSessionId();
1683
1716
  this._steeringMessages = [];
1684
1717
  this._followUpMessages = [];
1685
1718
  this._pendingNextTurnMessages = [];
1719
+ this._visibleSkillNames = undefined;
1686
1720
 
1687
1721
  this.sessionManager.appendThinkingLevelChange(this.thinkingLevel);
1688
1722
 
@@ -2180,6 +2214,8 @@ export class AgentSession {
2180
2214
  getActiveTools: () => this.getActiveToolNames(),
2181
2215
  getAllTools: () => this.getAllTools(),
2182
2216
  setActiveTools: (toolNames) => this.setActiveToolsByName(toolNames),
2217
+ getVisibleSkills: () => this.getVisibleSkillNames(),
2218
+ setVisibleSkills: (skillNames) => this.setVisibleSkillsByName(skillNames),
2183
2219
  refreshTools: () => this._refreshToolRegistry(),
2184
2220
  getCommands,
2185
2221
  setModel: async (model, options) => {
@@ -2211,6 +2247,9 @@ export class AgentSession {
2211
2247
  })();
2212
2248
  },
2213
2249
  getSystemPrompt: () => this.systemPrompt,
2250
+ setCompactionThresholdOverride: (percent) => {
2251
+ this.settingsManager.setCompactionThresholdOverride(percent);
2252
+ },
2214
2253
  },
2215
2254
  );
2216
2255
  }
@@ -2345,6 +2384,7 @@ export class AgentSession {
2345
2384
  this.settingsManager.reload();
2346
2385
  resetApiProviders();
2347
2386
  await this._resourceLoader.reload();
2387
+ this._visibleSkillNames = undefined;
2348
2388
  this._buildRuntime({
2349
2389
  activeToolNames: this.getActiveToolNames(),
2350
2390
  flagValues: previousFlagValues,
@@ -2534,6 +2574,7 @@ export class AgentSession {
2534
2574
  this._steeringMessages = [];
2535
2575
  this._followUpMessages = [];
2536
2576
  this._pendingNextTurnMessages = [];
2577
+ this._visibleSkillNames = undefined;
2537
2578
 
2538
2579
  // Set new session
2539
2580
  this.sessionManager.setSessionFile(sessionPath);
@@ -1002,6 +1002,95 @@ test("chat-controller does not pin when there are no tool calls", async () => {
1002
1002
  assert.equal(host.pinnedMessageContainer.children.length, 0, "pinned zone should stay empty without tool calls");
1003
1003
  });
1004
1004
 
1005
+ test("chat-controller rolls up only contiguous low-signal tool runs on message_end", async () => {
1006
+ (globalThis as any)[Symbol.for("@gsd/pi-coding-agent:theme")] = {
1007
+ fg: (_key: string, text: string) => text,
1008
+ bg: (_key: string, text: string) => text,
1009
+ bold: (text: string) => text,
1010
+ italic: (text: string) => text,
1011
+ truncate: (text: string) => text,
1012
+ };
1013
+
1014
+ const host = createHost();
1015
+ host.getMarkdownThemeWithSettings = () => ({});
1016
+
1017
+ const t1 = { type: "toolCall", id: "t1", name: "bash", arguments: { command: "true" } };
1018
+ const t2 = { type: "toolCall", id: "t2", name: "bash", arguments: { command: "true" } };
1019
+ const text = { type: "text", text: "middle output" };
1020
+ const t3 = { type: "toolCall", id: "t3", name: "read", arguments: { path: "/tmp/a" } };
1021
+ const t4 = { type: "toolCall", id: "t4", name: "read", arguments: { path: "/tmp/b" } };
1022
+ const content = [t1, t2, text, t3, t4];
1023
+
1024
+ await handleAgentEvent(host, { type: "message_start", message: makeAssistant([]) } as any);
1025
+ await handleAgentEvent(host, {
1026
+ type: "message_update",
1027
+ message: makeAssistant(content),
1028
+ assistantMessageEvent: {
1029
+ type: "text_delta",
1030
+ contentIndex: 2,
1031
+ delta: text.text,
1032
+ partial: makeAssistant(content),
1033
+ },
1034
+ } as any);
1035
+
1036
+ for (const tool of [t1, t2, t3, t4]) {
1037
+ await handleAgentEvent(host, {
1038
+ type: "tool_execution_end",
1039
+ toolCallId: tool.id,
1040
+ isError: false,
1041
+ result: { content: [], details: {} },
1042
+ } as any);
1043
+ }
1044
+
1045
+ await handleAgentEvent(host, { type: "message_end", message: makeAssistant(content) } as any);
1046
+
1047
+ assert.equal(host.chatContainer.children.length, 3, "two separated tool runs should become two summaries around text");
1048
+ assert.equal(host.chatContainer.children[0]?.constructor?.name, "ToolPhaseSummaryComponent");
1049
+ assert.equal(host.chatContainer.children[1]?.constructor?.name, "AssistantMessageComponent");
1050
+ assert.equal(host.chatContainer.children[2]?.constructor?.name, "ToolPhaseSummaryComponent");
1051
+ assert.match(host.chatContainer.children[0].render(120).join("\n"), /Setup \/ shell 2 actions/);
1052
+ assert.match(host.chatContainer.children[2].render(120).join("\n"), /Context reads 2 actions/);
1053
+ assert.equal(host.chatContainer._prevRender, null, "summary reposition must invalidate the chat container render cache");
1054
+ });
1055
+
1056
+ test("chat-controller rolls up low-signal direct tool execution events on agent_end", async () => {
1057
+ (globalThis as any)[Symbol.for("@gsd/pi-coding-agent:theme")] = {
1058
+ fg: (_key: string, text: string) => text,
1059
+ bg: (_key: string, text: string) => text,
1060
+ bold: (text: string) => text,
1061
+ italic: (text: string) => text,
1062
+ truncate: (text: string) => text,
1063
+ };
1064
+
1065
+ const host = createHost();
1066
+ host.getMarkdownThemeWithSettings = () => ({});
1067
+
1068
+ for (const toolCallId of ["bash-1", "bash-2", "bash-3"]) {
1069
+ await handleAgentEvent(host, {
1070
+ type: "tool_execution_start",
1071
+ toolCallId,
1072
+ toolName: "bash",
1073
+ args: { command: "true" },
1074
+ } as any);
1075
+ await handleAgentEvent(host, {
1076
+ type: "tool_execution_end",
1077
+ toolCallId,
1078
+ isError: false,
1079
+ result: { content: [], details: {} },
1080
+ } as any);
1081
+ }
1082
+
1083
+ assert.equal(host.chatContainer.children.length, 1, "direct tool events roll up as they finish");
1084
+ assert.equal(host.chatContainer.children[0]?.constructor?.name, "ToolPhaseSummaryComponent");
1085
+ assert.match(host.chatContainer.children[0].render(120).join("\n"), /Setup \/ shell 3 actions/);
1086
+
1087
+ await handleAgentEvent(host, { type: "agent_end" } as any);
1088
+
1089
+ assert.equal(host.chatContainer.children.length, 1, "direct low-signal tool rows should roll up on agent_end");
1090
+ assert.equal(host.chatContainer.children[0]?.constructor?.name, "ToolPhaseSummaryComponent");
1091
+ assert.match(host.chatContainer.children[0].render(120).join("\n"), /Setup \/ shell 3 actions/);
1092
+ });
1093
+
1005
1094
  // Regression test for issue #4144: interleaved text/tool content must render in content[] index order.
1006
1095
  // Stream: [text "A", toolCall T1, text "B", toolCall T2, text "C"]
1007
1096
  // Expected chatContainer order: textRun(A), toolExec(T1), textRun(B), toolExec(T2), textRun(C)
@@ -86,6 +86,13 @@ export interface CompactionSettings {
86
86
  enabled: boolean;
87
87
  reserveTokens: number;
88
88
  keepRecentTokens: number;
89
+ /**
90
+ * Optional percent-of-context-window threshold (0 < value < 1). When set,
91
+ * `shouldCompact()` fires once `contextTokens > contextWindow * thresholdPercent`,
92
+ * overriding the absolute `reserveTokens` calculation. Lets host integrations
93
+ * (e.g. GSD) express compaction policy as a fraction independent of model size.
94
+ */
95
+ thresholdPercent?: number;
89
96
  }
90
97
 
91
98
  export const DEFAULT_COMPACTION_SETTINGS: CompactionSettings = {
@@ -185,9 +192,20 @@ export function estimateContextTokens(messages: AgentMessage[]): ContextUsageEst
185
192
 
186
193
  /**
187
194
  * Check if compaction should trigger based on context usage.
195
+ *
196
+ * When `thresholdPercent` is set (and within (0, 1)), it overrides the absolute
197
+ * `reserveTokens` calculation: compaction fires at `contextWindow * thresholdPercent`.
198
+ * Otherwise the legacy `contextWindow - reserveTokens` headroom is used.
188
199
  */
189
200
  export function shouldCompact(contextTokens: number, contextWindow: number, settings: CompactionSettings): boolean {
190
201
  if (!settings.enabled) return false;
202
+ if (
203
+ settings.thresholdPercent !== undefined &&
204
+ settings.thresholdPercent > 0 &&
205
+ settings.thresholdPercent < 1
206
+ ) {
207
+ return contextTokens > contextWindow * settings.thresholdPercent;
208
+ }
191
209
  return contextTokens > contextWindow - settings.reserveTokens;
192
210
  }
193
211