@opengsd/gsd-pi 1.1.1-dev.a5a2de8 → 1.1.1-dev.b2556262

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 (325) hide show
  1. package/dist/headless-recover.js +56 -1
  2. package/dist/resources/.managed-resources-content-hash +1 -1
  3. package/dist/resources/extensions/browser-tools/engine/managed-gsd-browser.js +18 -2
  4. package/dist/resources/extensions/browser-tools/engine/selection.js +1 -1
  5. package/dist/resources/extensions/browser-tools/extension-manifest.json +1 -1
  6. package/dist/resources/extensions/browser-tools/index.js +68 -24
  7. package/dist/resources/extensions/browser-tools/state.js +12 -0
  8. package/dist/resources/extensions/browser-tools/tools/session.js +3 -2
  9. package/dist/resources/extensions/browser-tools/utils.js +3 -3
  10. package/dist/resources/extensions/browser-tools/web-app-detect.js +52 -0
  11. package/dist/resources/extensions/gsd/auto/loop.js +4 -2
  12. package/dist/resources/extensions/gsd/auto/phases.js +87 -12
  13. package/dist/resources/extensions/gsd/auto/session.js +22 -1
  14. package/dist/resources/extensions/gsd/auto/workflow-kernel.js +1 -0
  15. package/dist/resources/extensions/gsd/auto-dispatch.js +81 -13
  16. package/dist/resources/extensions/gsd/auto-model-selection.js +154 -9
  17. package/dist/resources/extensions/gsd/auto-post-unit.js +19 -2
  18. package/dist/resources/extensions/gsd/auto-prompts.js +26 -21
  19. package/dist/resources/extensions/gsd/auto-recovery.js +4 -2
  20. package/dist/resources/extensions/gsd/auto-runtime-state.js +3 -0
  21. package/dist/resources/extensions/gsd/auto-start.js +1 -1
  22. package/dist/resources/extensions/gsd/auto-timers.js +24 -10
  23. package/dist/resources/extensions/gsd/auto.js +40 -15
  24. package/dist/resources/extensions/gsd/bootstrap/db-tools.js +3 -3
  25. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +192 -77
  26. package/dist/resources/extensions/gsd/bootstrap/system-context.js +1 -1
  27. package/dist/resources/extensions/gsd/closeout-wizard.js +32 -9
  28. package/dist/resources/extensions/gsd/commands/handlers/auto.js +10 -0
  29. package/dist/resources/extensions/gsd/commands/handlers/ops.js +2 -9
  30. package/dist/resources/extensions/gsd/commands-maintenance.js +93 -15
  31. package/dist/resources/extensions/gsd/commands-mcp-status.js +1 -1
  32. package/dist/resources/extensions/gsd/commands-prefs-wizard.js +2 -2
  33. package/dist/resources/extensions/gsd/config-overlay.js +1 -0
  34. package/dist/resources/extensions/gsd/context-masker.js +129 -5
  35. package/dist/resources/extensions/gsd/db-writer.js +35 -0
  36. package/dist/resources/extensions/gsd/docs/preferences-reference.md +50 -1
  37. package/dist/resources/extensions/gsd/gsd-db.js +480 -172
  38. package/dist/resources/extensions/gsd/guided-flow.js +4 -1
  39. package/dist/resources/extensions/gsd/markdown-renderer.js +37 -53
  40. package/dist/resources/extensions/gsd/md-importer.js +38 -3
  41. package/dist/resources/extensions/gsd/migration-auto-check.js +126 -31
  42. package/dist/resources/extensions/gsd/parsers-legacy.js +23 -0
  43. package/dist/resources/extensions/gsd/planner-handoff.js +98 -0
  44. package/dist/resources/extensions/gsd/planning-path-scope.js +22 -4
  45. package/dist/resources/extensions/gsd/pre-execution-checks.js +10 -2
  46. package/dist/resources/extensions/gsd/preferences-models.js +111 -43
  47. package/dist/resources/extensions/gsd/preferences-types.js +13 -0
  48. package/dist/resources/extensions/gsd/preferences-validation.js +68 -3
  49. package/dist/resources/extensions/gsd/preferences.js +4 -1
  50. package/dist/resources/extensions/gsd/prompts/gate-evaluate.md +1 -1
  51. package/dist/resources/extensions/gsd/prompts/plan-milestone.md +1 -1
  52. package/dist/resources/extensions/gsd/prompts/plan-slice.md +1 -1
  53. package/dist/resources/extensions/gsd/prompts/refine-slice.md +1 -1
  54. package/dist/resources/extensions/gsd/prompts/run-uat.md +2 -2
  55. package/dist/resources/extensions/gsd/prompts/system.md +1 -1
  56. package/dist/resources/extensions/gsd/roadmap-slices.js +5 -1
  57. package/dist/resources/extensions/gsd/safety/content-validator.js +6 -4
  58. package/dist/resources/extensions/gsd/skill-manifest.js +12 -0
  59. package/dist/resources/extensions/gsd/source-observations.js +306 -0
  60. package/dist/resources/extensions/gsd/state-reconciliation/drift/completion.js +15 -8
  61. package/dist/resources/extensions/gsd/state-reconciliation/drift/stale-render.js +33 -5
  62. package/dist/resources/extensions/gsd/state-reconciliation/drift/stale-worker.js +34 -13
  63. package/dist/resources/extensions/gsd/state-reconciliation/index.js +39 -14
  64. package/dist/resources/extensions/gsd/state-reconciliation/spawn-gate.js +4 -4
  65. package/dist/resources/extensions/gsd/state.js +7 -3
  66. package/dist/resources/extensions/gsd/tool-contract.js +15 -1
  67. package/dist/resources/extensions/gsd/tool-presentation-plan.js +24 -2
  68. package/dist/resources/extensions/gsd/tools/complete-slice.js +28 -0
  69. package/dist/resources/extensions/gsd/tools/plan-slice.js +42 -11
  70. package/dist/resources/extensions/gsd/tools/plan-task.js +7 -1
  71. package/dist/resources/extensions/gsd/tools/workflow-tool-executors.js +62 -406
  72. package/dist/resources/extensions/gsd/uat-policy.js +130 -0
  73. package/dist/resources/extensions/gsd/uat-run.js +414 -0
  74. package/dist/resources/extensions/gsd/unit-context-manifest.js +3 -4
  75. package/dist/resources/extensions/gsd/unit-tool-contracts.js +38 -14
  76. package/dist/resources/extensions/gsd/verdict-parser.js +3 -8
  77. package/dist/resources/extensions/gsd/workflow-manifest.js +132 -5
  78. package/dist/resources/extensions/gsd/workflow-mcp.js +2 -3
  79. package/dist/resources/extensions/gsd/workflow-projections.js +8 -0
  80. package/dist/resources/extensions/gsd/worktree-manager.js +26 -0
  81. package/dist/resources/extensions/gsd/worktree-reentry.js +96 -0
  82. package/dist/resources/extensions/gsd/worktree-state-projection.js +18 -17
  83. package/dist/resources/extensions/shared/gsd-browser-cli.js +6 -0
  84. package/dist/resources/extensions/subagent/agents.js +1 -0
  85. package/dist/resources/extensions/subagent/index.js +27 -12
  86. package/dist/resources/extensions/subagent/launch.js +7 -2
  87. package/dist/web/standalone/.next/BUILD_ID +1 -1
  88. package/dist/web/standalone/.next/app-path-routes-manifest.json +6 -6
  89. package/dist/web/standalone/.next/build-manifest.json +2 -2
  90. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  91. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  92. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  93. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  94. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  95. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  96. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  97. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  98. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  99. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  100. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  101. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  102. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  103. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  104. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  105. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  106. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  107. package/dist/web/standalone/.next/server/app/index.html +1 -1
  108. package/dist/web/standalone/.next/server/app/index.rsc +1 -1
  109. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  110. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
  111. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  112. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  113. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  114. package/dist/web/standalone/.next/server/app-paths-manifest.json +6 -6
  115. package/dist/web/standalone/.next/server/chunks/8357.js +1 -1
  116. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  117. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  118. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  119. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  120. package/dist/web/standalone/node_modules/@gsd/native/dist/native.js +22 -0
  121. package/dist/web/standalone/node_modules/node-pty/build/Makefile +1 -1
  122. package/package.json +4 -4
  123. package/packages/cloud-mcp-gateway/package.json +2 -2
  124. package/packages/contracts/package.json +1 -1
  125. package/packages/daemon/package.json +4 -4
  126. package/packages/gsd-agent-core/package.json +5 -5
  127. package/packages/gsd-agent-modes/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
  128. package/packages/gsd-agent-modes/dist/modes/interactive/components/assistant-message.js +21 -23
  129. package/packages/gsd-agent-modes/dist/modes/interactive/components/assistant-message.js.map +1 -1
  130. package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.d.ts +3 -0
  131. package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  132. package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.js +25 -0
  133. package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.js.map +1 -1
  134. package/packages/gsd-agent-modes/dist/modes/interactive/components/transcript-design.d.ts +1 -0
  135. package/packages/gsd-agent-modes/dist/modes/interactive/components/transcript-design.d.ts.map +1 -1
  136. package/packages/gsd-agent-modes/dist/modes/interactive/components/transcript-design.js +66 -12
  137. package/packages/gsd-agent-modes/dist/modes/interactive/components/transcript-design.js.map +1 -1
  138. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
  139. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.js +18 -11
  140. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
  141. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-chat-render.d.ts.map +1 -1
  142. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-chat-render.js +16 -0
  143. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-chat-render.js.map +1 -1
  144. package/packages/gsd-agent-modes/package.json +7 -7
  145. package/packages/mcp-server/dist/workflow-tools.js +1 -1
  146. package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
  147. package/packages/mcp-server/package.json +3 -3
  148. package/packages/native/dist/native.js +22 -0
  149. package/packages/native/package.json +1 -1
  150. package/packages/pi-agent-core/package.json +1 -1
  151. package/packages/pi-ai/dist/image-models.generated.d.ts +30 -0
  152. package/packages/pi-ai/dist/image-models.generated.d.ts.map +1 -1
  153. package/packages/pi-ai/dist/image-models.generated.js +30 -0
  154. package/packages/pi-ai/dist/image-models.generated.js.map +1 -1
  155. package/packages/pi-ai/dist/models.generated.d.ts +174 -29
  156. package/packages/pi-ai/dist/models.generated.d.ts.map +1 -1
  157. package/packages/pi-ai/dist/models.generated.js +178 -54
  158. package/packages/pi-ai/dist/models.generated.js.map +1 -1
  159. package/packages/pi-ai/dist/providers/transform-messages.d.ts.map +1 -1
  160. package/packages/pi-ai/dist/providers/transform-messages.js +8 -1
  161. package/packages/pi-ai/dist/providers/transform-messages.js.map +1 -1
  162. package/packages/pi-ai/package.json +1 -1
  163. package/packages/pi-coding-agent/dist/core/settings-manager.js +1 -1
  164. package/packages/pi-coding-agent/dist/core/settings-manager.js.map +1 -1
  165. package/packages/pi-coding-agent/dist/theme/themes.js +1 -1
  166. package/packages/pi-coding-agent/dist/theme/themes.js.map +1 -1
  167. package/packages/pi-coding-agent/package.json +7 -7
  168. package/packages/pi-tui/dist/utils.d.ts +11 -0
  169. package/packages/pi-tui/dist/utils.d.ts.map +1 -1
  170. package/packages/pi-tui/dist/utils.js +119 -6
  171. package/packages/pi-tui/dist/utils.js.map +1 -1
  172. package/packages/pi-tui/package.json +2 -1
  173. package/packages/rpc-client/package.json +2 -2
  174. package/pkg/dist/theme/themes.js +1 -1
  175. package/pkg/dist/theme/themes.js.map +1 -1
  176. package/pkg/package.json +1 -1
  177. package/scripts/install/handoff.js +16 -3
  178. package/src/resources/extensions/browser-tools/engine/managed-gsd-browser.ts +21 -2
  179. package/src/resources/extensions/browser-tools/engine/selection.ts +1 -1
  180. package/src/resources/extensions/browser-tools/extension-manifest.json +1 -1
  181. package/src/resources/extensions/browser-tools/index.ts +75 -27
  182. package/src/resources/extensions/browser-tools/state.ts +13 -0
  183. package/src/resources/extensions/browser-tools/tests/browser-engine-selection.test.mjs +2 -2
  184. package/src/resources/extensions/browser-tools/tests/browser-tools-unit.test.cjs +57 -0
  185. package/src/resources/extensions/browser-tools/tests/gsd-browser-launch-config.test.mjs +37 -0
  186. package/src/resources/extensions/browser-tools/tests/web-app-detect.test.mjs +68 -0
  187. package/src/resources/extensions/browser-tools/tools/session.ts +4 -2
  188. package/src/resources/extensions/browser-tools/utils.ts +3 -3
  189. package/src/resources/extensions/browser-tools/web-app-detect.ts +63 -0
  190. package/src/resources/extensions/gsd/auto/loop-deps.ts +1 -0
  191. package/src/resources/extensions/gsd/auto/loop.ts +4 -2
  192. package/src/resources/extensions/gsd/auto/phases.ts +89 -15
  193. package/src/resources/extensions/gsd/auto/session.ts +24 -1
  194. package/src/resources/extensions/gsd/auto/workflow-kernel.ts +1 -0
  195. package/src/resources/extensions/gsd/auto-dispatch.ts +117 -12
  196. package/src/resources/extensions/gsd/auto-model-selection.ts +190 -12
  197. package/src/resources/extensions/gsd/auto-post-unit.ts +20 -2
  198. package/src/resources/extensions/gsd/auto-prompts.ts +25 -22
  199. package/src/resources/extensions/gsd/auto-recovery.ts +22 -3
  200. package/src/resources/extensions/gsd/auto-runtime-state.ts +5 -0
  201. package/src/resources/extensions/gsd/auto-start.ts +1 -1
  202. package/src/resources/extensions/gsd/auto-timers.ts +25 -9
  203. package/src/resources/extensions/gsd/auto.ts +41 -14
  204. package/src/resources/extensions/gsd/bootstrap/db-tools.ts +3 -3
  205. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +250 -78
  206. package/src/resources/extensions/gsd/bootstrap/system-context.ts +1 -1
  207. package/src/resources/extensions/gsd/closeout-wizard.ts +47 -13
  208. package/src/resources/extensions/gsd/commands/handlers/auto.ts +9 -0
  209. package/src/resources/extensions/gsd/commands/handlers/ops.ts +2 -17
  210. package/src/resources/extensions/gsd/commands-maintenance.ts +124 -13
  211. package/src/resources/extensions/gsd/commands-mcp-status.ts +1 -1
  212. package/src/resources/extensions/gsd/commands-prefs-wizard.ts +2 -2
  213. package/src/resources/extensions/gsd/config-overlay.ts +1 -0
  214. package/src/resources/extensions/gsd/context-masker.ts +152 -5
  215. package/src/resources/extensions/gsd/db-writer.ts +38 -0
  216. package/src/resources/extensions/gsd/docs/preferences-reference.md +50 -1
  217. package/src/resources/extensions/gsd/gsd-db.ts +564 -186
  218. package/src/resources/extensions/gsd/guided-flow.ts +4 -1
  219. package/src/resources/extensions/gsd/markdown-renderer.ts +44 -66
  220. package/src/resources/extensions/gsd/md-importer.ts +49 -2
  221. package/src/resources/extensions/gsd/migration-auto-check.ts +154 -34
  222. package/src/resources/extensions/gsd/parsers-legacy.ts +20 -0
  223. package/src/resources/extensions/gsd/planner-handoff.ts +149 -0
  224. package/src/resources/extensions/gsd/planning-path-scope.ts +22 -4
  225. package/src/resources/extensions/gsd/pre-execution-checks.ts +9 -2
  226. package/src/resources/extensions/gsd/preferences-models.ts +113 -43
  227. package/src/resources/extensions/gsd/preferences-types.ts +47 -0
  228. package/src/resources/extensions/gsd/preferences-validation.ts +76 -2
  229. package/src/resources/extensions/gsd/preferences.ts +5 -0
  230. package/src/resources/extensions/gsd/prompts/gate-evaluate.md +1 -1
  231. package/src/resources/extensions/gsd/prompts/plan-milestone.md +1 -1
  232. package/src/resources/extensions/gsd/prompts/plan-slice.md +1 -1
  233. package/src/resources/extensions/gsd/prompts/refine-slice.md +1 -1
  234. package/src/resources/extensions/gsd/prompts/run-uat.md +2 -2
  235. package/src/resources/extensions/gsd/prompts/system.md +1 -1
  236. package/src/resources/extensions/gsd/roadmap-slices.ts +6 -1
  237. package/src/resources/extensions/gsd/safety/content-validator.ts +8 -5
  238. package/src/resources/extensions/gsd/skill-manifest.ts +12 -0
  239. package/src/resources/extensions/gsd/source-observations.ts +402 -0
  240. package/src/resources/extensions/gsd/state-reconciliation/drift/completion.ts +20 -8
  241. package/src/resources/extensions/gsd/state-reconciliation/drift/stale-render.ts +44 -5
  242. package/src/resources/extensions/gsd/state-reconciliation/drift/stale-worker.ts +39 -11
  243. package/src/resources/extensions/gsd/state-reconciliation/index.ts +45 -15
  244. package/src/resources/extensions/gsd/state-reconciliation/spawn-gate.ts +4 -4
  245. package/src/resources/extensions/gsd/state.ts +7 -4
  246. package/src/resources/extensions/gsd/tests/auto-loop.test.ts +114 -0
  247. package/src/resources/extensions/gsd/tests/auto-model-selection-tool-poisoning.test.ts +66 -4
  248. package/src/resources/extensions/gsd/tests/auto-model-selection.test.ts +299 -1
  249. package/src/resources/extensions/gsd/tests/auto-paused-ui-cleanup.test.ts +32 -0
  250. package/src/resources/extensions/gsd/tests/auto-phases-lifecycle.test.ts +75 -3
  251. package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +22 -1
  252. package/src/resources/extensions/gsd/tests/auto-supervisor.test.mjs +4 -0
  253. package/src/resources/extensions/gsd/tests/before-provider-context-management.test.ts +145 -0
  254. package/src/resources/extensions/gsd/tests/bundled-skill-triggers.test.ts +9 -0
  255. package/src/resources/extensions/gsd/tests/closeout-wizard.test.ts +44 -0
  256. package/src/resources/extensions/gsd/tests/commands-dispatcher-unmerged-milestone.test.ts +26 -1
  257. package/src/resources/extensions/gsd/tests/complete-slice-verification-gate.test.ts +118 -0
  258. package/src/resources/extensions/gsd/tests/content-validator.test.ts +74 -0
  259. package/src/resources/extensions/gsd/tests/context-masker.test.ts +56 -1
  260. package/src/resources/extensions/gsd/tests/custom-engine-loop-integration.test.ts +17 -2
  261. package/src/resources/extensions/gsd/tests/dispatch-rule-coverage.test.ts +24 -0
  262. package/src/resources/extensions/gsd/tests/doctor-scope-db-unavailable.test.ts +1 -11
  263. package/src/resources/extensions/gsd/tests/gate-dispatch.test.ts +64 -0
  264. package/src/resources/extensions/gsd/tests/gate-storage.test.ts +15 -0
  265. package/src/resources/extensions/gsd/tests/gsd-recover.test.ts +62 -1
  266. package/src/resources/extensions/gsd/tests/integration/run-uat.test.ts +4 -1
  267. package/src/resources/extensions/gsd/tests/interrupted-session-auto.test.ts +27 -0
  268. package/src/resources/extensions/gsd/tests/journal-integration.test.ts +16 -0
  269. package/src/resources/extensions/gsd/tests/markdown-renderer.test.ts +42 -0
  270. package/src/resources/extensions/gsd/tests/mcp-project-config.test.ts +7 -1
  271. package/src/resources/extensions/gsd/tests/mcp-status.test.ts +1 -1
  272. package/src/resources/extensions/gsd/tests/migration-auto-check.test.ts +99 -0
  273. package/src/resources/extensions/gsd/tests/plan-slice.test.ts +99 -2
  274. package/src/resources/extensions/gsd/tests/plan-task.test.ts +19 -0
  275. package/src/resources/extensions/gsd/tests/planner-handoff.test.ts +100 -0
  276. package/src/resources/extensions/gsd/tests/preferences.test.ts +14 -0
  277. package/src/resources/extensions/gsd/tests/prefs-wizard-coverage.test.ts +1 -0
  278. package/src/resources/extensions/gsd/tests/prompt-contracts.test.ts +133 -0
  279. package/src/resources/extensions/gsd/tests/provider-switch-observer.test.ts +55 -0
  280. package/src/resources/extensions/gsd/tests/register-hooks-depth-verification.test.ts +101 -1
  281. package/src/resources/extensions/gsd/tests/repository-registry.test.ts +2 -2
  282. package/src/resources/extensions/gsd/tests/runtime-invariant-modules.test.ts +28 -0
  283. package/src/resources/extensions/gsd/tests/schema-v21-sequence.test.ts +5 -3
  284. package/src/resources/extensions/gsd/tests/schema-v27-v28-sequence.test.ts +162 -18
  285. package/src/resources/extensions/gsd/tests/skill-manifest.test.ts +4 -3
  286. package/src/resources/extensions/gsd/tests/skipped-validation-db-atomicity.test.ts +8 -0
  287. package/src/resources/extensions/gsd/tests/source-observations.test.ts +275 -0
  288. package/src/resources/extensions/gsd/tests/stale-queued-milestone.test.ts +43 -0
  289. package/src/resources/extensions/gsd/tests/state-reconciliation-drift.test.ts +76 -21
  290. package/src/resources/extensions/gsd/tests/thinking-level-resolution.test.ts +203 -0
  291. package/src/resources/extensions/gsd/tests/uat-policy.test.ts +170 -0
  292. package/src/resources/extensions/gsd/tests/unit-context-manifest.test.ts +7 -1
  293. package/src/resources/extensions/gsd/tests/workflow-kernel.test.ts +7 -0
  294. package/src/resources/extensions/gsd/tests/workflow-manifest.test.ts +306 -1
  295. package/src/resources/extensions/gsd/tests/workflow-mcp.test.ts +77 -10
  296. package/src/resources/extensions/gsd/tests/workflow-tool-executors.test.ts +260 -5
  297. package/src/resources/extensions/gsd/tests/worktree-db.test.ts +511 -1
  298. package/src/resources/extensions/gsd/tests/worktree-reentry.test.ts +102 -0
  299. package/src/resources/extensions/gsd/tests/worktree-state-projection.test.ts +44 -0
  300. package/src/resources/extensions/gsd/tool-contract.ts +29 -1
  301. package/src/resources/extensions/gsd/tool-presentation-plan.ts +41 -6
  302. package/src/resources/extensions/gsd/tools/complete-slice.ts +29 -0
  303. package/src/resources/extensions/gsd/tools/plan-slice.ts +54 -12
  304. package/src/resources/extensions/gsd/tools/plan-task.ts +8 -1
  305. package/src/resources/extensions/gsd/tools/workflow-tool-executors.ts +71 -489
  306. package/src/resources/extensions/gsd/types.ts +1 -0
  307. package/src/resources/extensions/gsd/uat-policy.ts +191 -0
  308. package/src/resources/extensions/gsd/uat-run.ts +550 -0
  309. package/src/resources/extensions/gsd/unit-context-manifest.ts +3 -4
  310. package/src/resources/extensions/gsd/unit-tool-contracts.ts +38 -14
  311. package/src/resources/extensions/gsd/verdict-parser.ts +3 -10
  312. package/src/resources/extensions/gsd/workflow-manifest.ts +193 -7
  313. package/src/resources/extensions/gsd/workflow-mcp.ts +2 -3
  314. package/src/resources/extensions/gsd/workflow-projections.ts +9 -0
  315. package/src/resources/extensions/gsd/worktree-manager.ts +32 -0
  316. package/src/resources/extensions/gsd/worktree-reentry.ts +103 -0
  317. package/src/resources/extensions/gsd/worktree-state-projection.ts +22 -22
  318. package/src/resources/extensions/shared/gsd-browser-cli.ts +6 -0
  319. package/src/resources/extensions/shared/tests/format-utils.test.ts +8 -3
  320. package/src/resources/extensions/subagent/agents.ts +4 -0
  321. package/src/resources/extensions/subagent/index.ts +28 -3
  322. package/src/resources/extensions/subagent/launch.ts +8 -0
  323. package/src/resources/extensions/subagent/tests/model-override.test.ts +31 -0
  324. /package/dist/web/standalone/.next/static/{9y3LeeR2uGr2yRj9RjY3D → tJOKQbQRO-9MiFDO8DIDS}/_buildManifest.js +0 -0
  325. /package/dist/web/standalone/.next/static/{9y3LeeR2uGr2yRj9RjY3D → tJOKQbQRO-9MiFDO8DIDS}/_ssgManifest.js +0 -0
@@ -41,3 +41,6 @@ export function clearToolInvocationError() {
41
41
  return;
42
42
  autoSession.lastToolInvocationError = null;
43
43
  }
44
+ export function getSourceObservationStore() {
45
+ return autoSession.sourceObservations;
46
+ }
@@ -1208,7 +1208,7 @@ export async function bootstrapAutoSession(s, ctx, pi, base, verboseMode, reques
1208
1208
  s.autoStartTime = Date.now();
1209
1209
  s.resourceVersionOnStart = readResourceVersion();
1210
1210
  s.pendingQuickTasks = [];
1211
- s.currentUnit = null;
1211
+ s.clearCurrentUnit();
1212
1212
  s.currentMilestoneId ??=
1213
1213
  strandedRecoveryAction?.milestoneId ??
1214
1214
  (deepProjectStagePending ? null : state.activeMilestone?.id ?? null);
@@ -104,6 +104,14 @@ export function startUnitSupervision(sctx) {
104
104
  const softTimeoutMs = supervisionTimeouts.softTimeoutMs;
105
105
  const idleTimeoutMs = supervisionTimeouts.idleTimeoutMs;
106
106
  const hardTimeoutMs = supervisionTimeouts.hardTimeoutMs;
107
+ // A single hung tool gets its own short budget, NOT the general idle window:
108
+ // a long-but-progressing session is not idle, but a tool stuck for minutes
109
+ // is. Falls back to the idle window only if misconfigured to zero. The
110
+ // hung-tool budget is intentionally not scaled by task estimate — a stuck
111
+ // tool call is stuck regardless of how long the overall task should take.
112
+ const stalledToolTimeoutMs = (supervisor.stalled_tool_timeout_minutes ?? 0) > 0
113
+ ? supervisor.stalled_tool_timeout_minutes * 60 * 1000
114
+ : idleTimeoutMs;
107
115
  // ── 1. Soft timeout warning ──
108
116
  s.wrapupWarningHandle = setTimeout(() => {
109
117
  s.wrapupWarningHandle = null;
@@ -144,10 +152,12 @@ export function startUnitSupervision(sctx) {
144
152
  const runtime = readUnitRuntimeRecord(s.basePath, unitType, unitId);
145
153
  if (!runtime)
146
154
  return;
147
- if (Date.now() - runtime.lastProgressAt < idleTimeoutMs)
148
- return;
149
- // Agent has tool calls currently executing not idle, just waiting.
150
- // But only suppress recovery if the tool started recently.
155
+ // In-flight tool handling runs on its own dedicated hung-tool budget,
156
+ // independent of the general idle gate below, so a genuinely stuck tool
157
+ // is caught in minutes instead of waiting out the (typically much longer)
158
+ // idle window (#2527, follow-up). A tool actively executing within budget
159
+ // is real progress, so refreshing lastProgressAt here also keeps the idle
160
+ // gate from firing during legitimate long-running tool calls.
151
161
  let stalledToolDetected = false;
152
162
  if (getInFlightToolCount() > 0) {
153
163
  // User-interactive tools (ask_user_questions, secure_env_collect) block
@@ -161,21 +171,25 @@ export function startUnitSupervision(sctx) {
161
171
  }
162
172
  const oldestStart = getOldestInFlightToolStart();
163
173
  const toolAgeMs = Date.now() - oldestStart;
164
- if (toolAgeMs < idleTimeoutMs) {
174
+ if (toolAgeMs < stalledToolTimeoutMs) {
165
175
  writeUnitRuntimeRecord(s.basePath, unitType, unitId, s.currentUnit.startedAt, {
166
176
  lastProgressAt: Date.now(),
167
177
  lastProgressKind: "tool-in-flight",
168
178
  });
169
179
  return;
170
180
  }
171
- // Tool has been in-flight longer than idle timeout — treat as hung.
172
- // Clear the stale entries so subsequent ticks don't re-detect them,
173
- // and set the flag so the filesystem-activity check below does not
174
- // override the stall verdict (#2527).
181
+ // Tool has been in-flight longer than the hung-tool budget — treat as
182
+ // hung. Clear the stale entries so subsequent ticks don't re-detect
183
+ // them, and set the flag so the idle gate and filesystem-activity check
184
+ // below do not override the stall verdict (#2527).
175
185
  stalledToolDetected = true;
176
186
  clearInFlightTools();
177
- ctx.ui.notify(`Stalled tool detected: a tool has been in-flight for ${Math.round(toolAgeMs / 60000)}min. Treating as hung — attempting idle recovery.`, "warning");
187
+ ctx.ui.notify(`Stalled tool detected: a tool has been in-flight for ${Math.round(toolAgeMs / 60000)}min (budget ${Math.round(stalledToolTimeoutMs / 60000)}min). Treating as hung — attempting idle recovery.`, "warning");
178
188
  }
189
+ // No hung tool — apply the general idle gate. A unit that has made
190
+ // meaningful progress within the idle window is not idle yet.
191
+ if (!stalledToolDetected && Date.now() - runtime.lastProgressAt < idleTimeoutMs)
192
+ return;
179
193
  // Check if the agent is producing work on disk.
180
194
  // Skip this when a stalled tool was just detected — filesystem changes
181
195
  // from earlier in the task should not override the stall verdict (#2527).
@@ -30,7 +30,7 @@ import { playNotificationBell, sendDesktopNotification } from "./notifications.j
30
30
  import { getBudgetAlertLevel, getNewBudgetAlertLevel, getBudgetEnforcementAction, resolveCompactionThresholdPercent, shouldRerootStepSessionForContext, } from "./auto-budget.js";
31
31
  import { markToolStart as _markToolStart, markToolEnd as _markToolEnd, getOldestInFlightToolAgeMs as _getOldestInFlightToolAgeMs, clearInFlightTools, isToolInvocationError, isQueuedUserMessageSkip, isDeterministicPolicyError, } from "./auto-tool-tracking.js";
32
32
  import { closeoutUnit } from "./auto-unit-closeout.js";
33
- import { selectAndApplyModel, resolveModelId, clearToolBaseline } from "./auto-model-selection.js";
33
+ import { selectAndApplyModel, resolveModelId, clearToolBaseline, getToolBaselineSnapshot } from "./auto-model-selection.js";
34
34
  import { resetRoutingHistory, recordOutcome } from "./routing-history.js";
35
35
  import { resetHookState, runPreDispatchHooks, restoreHookState, clearPersistedHookState, } from "./post-unit-hooks.js";
36
36
  import { runGSDDoctor, rebuildState } from "./doctor.js";
@@ -287,8 +287,24 @@ export function _synthesizePausedSessionRecoveryForTest(basePath, unitType, unit
287
287
  function handlePausedSessionResumeRecovery(basePath, state, notify) {
288
288
  if (!state.pausedSessionFile)
289
289
  return { skippedReplay: false };
290
- const pausedRecoveryUnitType = state.currentUnit?.type ?? state.pausedUnitType ?? "unknown";
291
- const pausedRecoveryUnitId = state.currentUnit?.id ?? state.pausedUnitId ?? "unknown";
290
+ const pausedRecoveryUnitType = state.currentUnit?.type ?? state.pausedUnitType ?? null;
291
+ const pausedRecoveryUnitId = state.currentUnit?.id ?? state.pausedUnitId ?? null;
292
+ // When the paused-session metadata never captured the unit identity (the
293
+ // pause happened between units, or the worker died before currentUnit was
294
+ // set), we have nothing to verify against and nothing correct to target. A
295
+ // replay synthesized with an "unknown" unit re-injects an unbounded,
296
+ // mis-identified tool-call blob into the fresh resume context — exactly the
297
+ // thrash that turns one stuck unit into several. Disk state has already been
298
+ // rebuilt (rebuildState + doctor) before this runs, so skip the replay and
299
+ // let the normal dispatcher recompute the next unit from disk.
300
+ if (!pausedRecoveryUnitType || !pausedRecoveryUnitId) {
301
+ state.pausedSessionFile = null;
302
+ state.pausedUnitType = null;
303
+ state.pausedUnitId = null;
304
+ state.pendingCrashRecovery = null;
305
+ notify("Paused session had no recorded unit identity. Skipping tool-call replay and resuming from disk state.");
306
+ return { skippedReplay: true };
307
+ }
292
308
  const completedPausedUnit = verifyExpectedArtifact(pausedRecoveryUnitType, pausedRecoveryUnitId, basePath);
293
309
  if (completedPausedUnit) {
294
310
  state.pausedSessionFile = null;
@@ -806,7 +822,7 @@ export async function cleanupAfterLoopExit(ctx) {
806
822
  const preserveStepSurface = s.preserveStepSurfaceAfterLoopExit;
807
823
  const preserveCompletionSurface = s.completionStopInProgress;
808
824
  const preservePausedSurface = s.paused;
809
- s.currentUnit = null;
825
+ s.clearCurrentUnit();
810
826
  s.active = false;
811
827
  deactivateGSD();
812
828
  clearUnitTimeout();
@@ -831,12 +847,15 @@ export async function cleanupAfterLoopExit(ctx) {
831
847
  // A transient provider-error pause intentionally leaves the paused badge
832
848
  // visible so the user still has a resumable auto-mode signal on screen.
833
849
  if (!s.paused) {
834
- if (preserveStepSurface) {
835
- s.preserveStepSurfaceAfterLoopExit = false;
836
- }
837
- else if (preserveCompletionSurface) {
850
+ if (preserveCompletionSurface) {
838
851
  ctx.ui.setStatus("gsd-auto", undefined);
839
852
  s.completionStopInProgress = false;
853
+ if (preserveStepSurface) {
854
+ s.preserveStepSurfaceAfterLoopExit = false;
855
+ }
856
+ }
857
+ else if (preserveStepSurface) {
858
+ s.preserveStepSurfaceAfterLoopExit = false;
840
859
  }
841
860
  else {
842
861
  ctx.ui.setStatus("gsd-auto", undefined);
@@ -1476,7 +1495,7 @@ export async function pauseAuto(ctx, _pi, _errorContext, options = {}) {
1476
1495
  // Non-fatal — best-effort closeout on pause
1477
1496
  logWarning("engine", `unit closeout on pause failed: ${err instanceof Error ? err.message : String(err)}`, { file: "auto.ts" });
1478
1497
  }
1479
- s.currentUnit = null;
1498
+ s.clearCurrentUnit();
1480
1499
  }
1481
1500
  // Keep STATE.md aligned with the DB-backed state before releasing pause state.
1482
1501
  // Without this, an interrupted deep run can leave STATE.md saying "no active
@@ -1637,7 +1656,10 @@ export function createWiredDispatchAdapter(ctx, pi, dispatchBasePath, session) {
1637
1656
  const authMode = sessionProvider && typeof ctx.modelRegistry?.getProviderAuthMode === "function"
1638
1657
  ? ctx.modelRegistry.getProviderAuthMode(sessionProvider)
1639
1658
  : undefined;
1640
- const activeTools = typeof pi.getActiveTools === "function" ? pi.getActiveTools() : [];
1659
+ // Use baseline snapshot same reason as phases.ts:runDispatch: the live
1660
+ // active set may be narrowed by the prior unit before selectAndApplyModel
1661
+ // restores it, causing false transport-preflight failures (#477 follow-up).
1662
+ const activeTools = getToolBaselineSnapshot(pi);
1641
1663
  // Mirrors runDispatch: deep-planning keeps approval gates in plain chat
1642
1664
  // because structured questions can be cancelled outside the chat turn on
1643
1665
  // some transports.
@@ -1678,6 +1700,9 @@ export function createWiredDispatchAdapter(ctx, pi, dispatchBasePath, session) {
1678
1700
  sessionContextWindow,
1679
1701
  sessionProvider,
1680
1702
  modelRegistry,
1703
+ activeTools,
1704
+ sessionAuthMode: authMode,
1705
+ sessionBaseUrl: ctx.model?.baseUrl,
1681
1706
  });
1682
1707
  if (action.action === "stop") {
1683
1708
  if (session)
@@ -2605,7 +2630,7 @@ export async function dispatchHookUnit(ctx, pi, hookName, triggerUnitType, trigg
2605
2630
  s.stepMode = true;
2606
2631
  s.cmdCtx = ctx;
2607
2632
  s.autoStartTime = Date.now();
2608
- s.currentUnit = null;
2633
+ s.clearCurrentUnit();
2609
2634
  s.pendingQuickTasks = [];
2610
2635
  }
2611
2636
  // ADR-016 phase 2 / B2 (#5620): hook-trigger basePath transition. Treats
@@ -2619,23 +2644,23 @@ export async function dispatchHookUnit(ctx, pi, hookName, triggerUnitType, trigg
2619
2644
  }
2620
2645
  const hookUnitType = `hook/${hookName}`;
2621
2646
  const hookStartedAt = Date.now();
2622
- s.currentUnit = {
2647
+ s.setCurrentUnit({
2623
2648
  type: triggerUnitType,
2624
2649
  id: triggerUnitId,
2625
2650
  startedAt: hookStartedAt,
2626
2651
  workspaceRoot: s.basePath,
2627
- };
2652
+ });
2628
2653
  const result = await s.cmdCtx.newSession({ workspaceRoot: s.basePath });
2629
2654
  if (result.cancelled) {
2630
2655
  await stopAuto(ctx, pi);
2631
2656
  return false;
2632
2657
  }
2633
- s.currentUnit = {
2658
+ s.setCurrentUnit({
2634
2659
  type: hookUnitType,
2635
2660
  id: triggerUnitId,
2636
2661
  startedAt: hookStartedAt,
2637
2662
  workspaceRoot: s.basePath,
2638
- };
2663
+ });
2639
2664
  if (hookModel) {
2640
2665
  const availableModels = ctx.modelRegistry.getAvailable();
2641
2666
  const match = resolveModelId(hookModel, availableModels, ctx.model?.provider);
@@ -664,7 +664,7 @@ export function registerDbTools(pi) {
664
664
  files: Type.Array(Type.String(), { description: "Array<string> of files likely touched; pass [\"path\"] or [], never a single string" }),
665
665
  verify: Type.String({ description: "Verification command or block" }),
666
666
  inputs: Type.Array(Type.String(), { description: "Array<string> of input files or references; pass [\"path\"] or [], never a single string" }),
667
- expectedOutput: Type.Array(Type.String(), { description: "Array<string> of expected output files or artifacts; pass [\"path\"] or [], never a single string" }),
667
+ expectedOutput: Type.Array(Type.String(), { description: "Array<string> of files this task creates or overwrites; pass [\"path\"] or [], never prose or a single string" }),
668
668
  observabilityImpact: Type.Optional(Type.String({ description: "Task observability impact" })),
669
669
  }), { description: "Planned tasks for the slice" }),
670
670
  // ── Enrichment metadata (optional — defaults to empty) ────────────
@@ -740,7 +740,7 @@ export function registerDbTools(pi) {
740
740
  files: Type.Array(Type.String(), { description: "Array<string> of files likely touched; pass [\"path\"] or [], never a single string" }),
741
741
  verify: Type.String({ description: "Verification command or block" }),
742
742
  inputs: Type.Array(Type.String(), { description: "Array<string> of input files or references; pass [\"path\"] or [], never a single string" }),
743
- expectedOutput: Type.Array(Type.String(), { description: "Array<string> of expected output files or artifacts; pass [\"path\"] or [], never a single string" }),
743
+ expectedOutput: Type.Array(Type.String(), { description: "Array<string> of files this task creates or overwrites; pass [\"path\"] or [], never prose or a single string" }),
744
744
  observabilityImpact: Type.Optional(Type.String({ description: "Task observability impact" })),
745
745
  // Single-writer v3 audit trail (Stream 2): caller-provided actor identity + causation.
746
746
  actorName: Type.Optional(Type.String({ description: "Caller-provided actor identity for the audit trail (e.g. 'executor-01', 'gsd-orchestrator')" })),
@@ -1069,7 +1069,7 @@ export function registerDbTools(pi) {
1069
1069
  files: Type.Array(Type.String(), { description: "Files likely touched" }),
1070
1070
  verify: Type.String({ description: "Verification command or block" }),
1071
1071
  inputs: Type.Array(Type.String(), { description: "Input files or references" }),
1072
- expectedOutput: Type.Array(Type.String(), { description: "Expected output files or artifacts" }),
1072
+ expectedOutput: Type.Array(Type.String(), { description: "Files this task creates or overwrites" }),
1073
1073
  }), { description: "Tasks to upsert (update existing or insert new)" }),
1074
1074
  removedTaskIds: Type.Array(Type.String(), { description: "Task IDs to remove from the slice" }),
1075
1075
  // Single-writer v3 audit trail (Stream 2): caller-provided actor identity + causation.
@@ -1,17 +1,17 @@
1
1
  // Project/App: gsd-pi
2
2
  // File Purpose: Registers GSD extension runtime hooks and token-saving tool policies.
3
- import { existsSync } from "node:fs";
3
+ import { existsSync, mkdirSync } from "node:fs";
4
4
  import { dirname, join } from "node:path";
5
5
  import { pathToFileURL } from "node:url";
6
6
  import { isToolCallEventType } from "@gsd/pi-coding-agent";
7
7
  import { ALWAYS_PRESERVED_SHIM_TOOL_NAMES } from "@gsd/pi-ai";
8
8
  import { updateSnapshot } from "../ecosystem/gsd-extension-api.js";
9
- import { buildMilestoneFileName, resolveMilestonePath, resolveSliceFile, resolveSlicePath } from "../paths.js";
9
+ import { buildMilestoneFileName, clearPathCache, milestonesDir, resolveMilestonePath, resolveSliceFile, resolveSlicePath } from "../paths.js";
10
10
  import { canonicalToolName, clearDiscussionFlowState, isDepthConfirmationAnswer, isMilestoneDepthVerified, isQueuePhaseActive, markApprovalGateVerified, markDepthVerified, resetWriteGateState, shouldBlockContextWrite, shouldBlockPlanningUnit, shouldBlockQueueExecution, shouldBlockWorktreeWrite, isGateQuestionId, setPendingGate, clearPendingGate, getPendingGate, shouldBlockPendingGate, shouldBlockPendingGateBash, extractDepthVerificationMilestoneId } from "./write-gate.js";
11
11
  import { resolveManifest } from "../unit-context-manifest.js";
12
12
  import { isBlockedStateFile, isBashWriteToStateFile, BLOCKED_WRITE_ERROR } from "../write-intercept.js";
13
13
  import { loadFile, saveFile, formatContinue } from "../files.js";
14
- import { clearToolInvocationError, getAutoRuntimeSnapshot, isAutoActive, isAutoCompletionStopInProgress, isAutoPaused, markToolEnd, markToolStart, recordToolInvocationError } from "../auto-runtime-state.js";
14
+ import { clearToolInvocationError, getAutoRuntimeSnapshot, getSourceObservationStore, isAutoActive, isAutoCompletionStopInProgress, isAutoPaused, markToolEnd, markToolStart, recordToolInvocationError, } from "../auto-runtime-state.js";
15
15
  import { checkToolCallLoop, resetToolCallLoopGuard } from "./tool-call-loop-guard.js";
16
16
  import { maybePauseAutoForApprovalGate, resetPendingGatePauseGuard } from "./pending-gate-pause.js";
17
17
  import { saveActivityLog } from "../activity-log.js";
@@ -31,6 +31,7 @@ import { registerPlanMilestoneSchemaRecovery } from "./plan-milestone-schema-rec
31
31
  import { AUTO_UNIT_SCOPED_TOOLS, RUN_UAT_BROWSER_TOOL_NAMES, isWorkflowAliasTool } from "../auto-unit-tool-scope.js";
32
32
  import { filterToolsForProvider } from "../model-router.js";
33
33
  import { RUN_UAT_READ_ONLY_TOOL_NAMES, RUN_UAT_WORKFLOW_TOOL_NAMES } from "../tool-presentation-plan.js";
34
+ import { injectSourceContextBlockIntoPayload, supportsSourceObservationsForUnit } from "../source-observations.js";
34
35
  let approvalQuestionAbortInFlight = false;
35
36
  async function loadWelcomeScreenModule() {
36
37
  const candidates = [];
@@ -210,7 +211,12 @@ export function buildRunUatGsdToolSet(activeToolNames, registeredToolNames = act
210
211
  "subagent",
211
212
  ...RUN_UAT_BROWSER_TOOL_NAMES,
212
213
  ]);
213
- return [...new Set(scoped)];
214
+ const resolved = [...new Set(scoped)];
215
+ const unresolved = RUN_UAT_WORKFLOW_TOOL_NAMES.filter((tool) => !resolved.some((name) => name === tool || (name.startsWith("mcp__") && name.endsWith(`__${tool}`))));
216
+ if (unresolved.length > 0) {
217
+ safetyLogWarning("bootstrap", `buildRunUatGsdToolSet: required run-uat workflow tool(s) not found in active/registered surface: ${unresolved.join(", ")}. Session may lack gsd-workflow MCP connection.`);
218
+ }
219
+ return resolved;
214
220
  }
215
221
  export function buildMinimalGsdWorkflowToolSet(activeToolNames, registeredToolNames = activeToolNames) {
216
222
  const autoBaseTools = new Set(MINIMAL_AUTO_BASE_TOOL_NAMES);
@@ -365,6 +371,50 @@ function deferApprovalGate(gateId, basePath) {
365
371
  function contextBasePath(ctx) {
366
372
  return typeof ctx?.cwd === "string" ? ctx.cwd : process.cwd();
367
373
  }
374
+ function beginSourceObservationStoreForCurrentUnit(ctx) {
375
+ if (!isAutoActive())
376
+ return null;
377
+ const dash = getAutoRuntimeSnapshot();
378
+ if (!dash.currentUnit)
379
+ return null;
380
+ if (!supportsSourceObservationsForUnit(dash.currentUnit.type))
381
+ return null;
382
+ const store = getSourceObservationStore();
383
+ store.beginUnit({
384
+ unitType: dash.currentUnit.type,
385
+ unitId: dash.currentUnit.id,
386
+ startedAt: dash.currentUnit.startedAt,
387
+ basePath: dash.currentUnit.workspaceRoot ?? (dash.basePath || contextBasePath(ctx)),
388
+ });
389
+ return store;
390
+ }
391
+ function refreshSourceObservationAfterMutation(canonicalName, input, ctx) {
392
+ if (canonicalName !== "edit" && canonicalName !== "write")
393
+ return;
394
+ if (!input || typeof input !== "object")
395
+ return;
396
+ const store = beginSourceObservationStoreForCurrentUnit(ctx);
397
+ if (!store)
398
+ return;
399
+ store.observeMutation(input);
400
+ }
401
+ function clearSourceObservationsAfterShell(canonicalName) {
402
+ if (!isAutoActive())
403
+ return;
404
+ if (!isShellExecutionTool(canonicalName))
405
+ return;
406
+ const dash = getAutoRuntimeSnapshot();
407
+ if (!dash.currentUnit || !supportsSourceObservationsForUnit(dash.currentUnit.type))
408
+ return;
409
+ getSourceObservationStore().clear();
410
+ }
411
+ function isShellExecutionTool(canonicalName) {
412
+ return canonicalName === "bash" ||
413
+ canonicalName === "bg_shell" ||
414
+ canonicalName === "async_bash" ||
415
+ canonicalName === "shell" ||
416
+ canonicalName === "powershell";
417
+ }
368
418
  function activateDeferredApprovalGate(basePath) {
369
419
  if (deferredApprovalGate?.basePath !== basePath)
370
420
  return;
@@ -387,6 +437,79 @@ function isContextDraftSummarySave(toolName, input) {
387
437
  return false;
388
438
  return input.artifact_type === "CONTEXT-DRAFT";
389
439
  }
440
+ function selectedAnswerLabel(selected) {
441
+ if (Array.isArray(selected))
442
+ return selected.map(String).join(", ");
443
+ if (selected == null)
444
+ return "";
445
+ return String(selected);
446
+ }
447
+ function formatQuestionExchange(questions, answers) {
448
+ const lines = [];
449
+ for (const question of questions) {
450
+ lines.push(`### ${question.header ?? "Question"}`, "", question.question ?? "");
451
+ if (Array.isArray(question.options)) {
452
+ lines.push("");
453
+ for (const opt of question.options) {
454
+ lines.push(`- **${opt.label ?? ""}** — ${opt.description ?? ""}`);
455
+ }
456
+ }
457
+ const answer = question.id ? answers?.[question.id] : undefined;
458
+ if (answer) {
459
+ lines.push("");
460
+ const selected = selectedAnswerLabel(answer.selected);
461
+ if (selected)
462
+ lines.push(`**Selected:** ${selected}`);
463
+ if (answer.notes)
464
+ lines.push(`**Notes:** ${String(answer.notes)}`);
465
+ }
466
+ lines.push("");
467
+ }
468
+ return lines.join("\n");
469
+ }
470
+ async function ensureMilestoneShell(basePath, milestoneId) {
471
+ const milestoneDir = resolveMilestonePath(basePath, milestoneId)
472
+ ?? join(milestonesDir(basePath), milestoneId);
473
+ mkdirSync(milestoneDir, { recursive: true });
474
+ clearPathCache();
475
+ try {
476
+ const { ensureDbOpen } = await import("./dynamic-tools.js");
477
+ if (await ensureDbOpen(basePath)) {
478
+ const { getMilestone, insertMilestone } = await import("../gsd-db.js");
479
+ if (!getMilestone(milestoneId)) {
480
+ insertMilestone({
481
+ id: milestoneId,
482
+ title: `New milestone ${milestoneId}`,
483
+ status: "queued",
484
+ });
485
+ }
486
+ }
487
+ }
488
+ catch (err) {
489
+ safetyLogWarning("guided", `failed to persist milestone shell for ${milestoneId}: ${err.message}`);
490
+ }
491
+ return milestoneDir;
492
+ }
493
+ async function saveDiscussionQuestionRound(basePath, milestoneId, questions, details) {
494
+ const milestoneDir = await ensureMilestoneShell(basePath, milestoneId);
495
+ const answers = details?.response?.answers;
496
+ const timestamp = new Date().toISOString();
497
+ const exchange = formatQuestionExchange(questions, answers);
498
+ const discussionPath = join(milestoneDir, buildMilestoneFileName(milestoneId, "DISCUSSION"));
499
+ const existingDiscussion = await loadFile(discussionPath) ?? `# ${milestoneId} Discussion Log\n\n`;
500
+ await saveFile(discussionPath, `${existingDiscussion}## Exchange — ${timestamp}\n\n${exchange}---\n\n`);
501
+ const draftPath = join(milestoneDir, buildMilestoneFileName(milestoneId, "CONTEXT-DRAFT"));
502
+ const existingDraft = await loadFile(draftPath);
503
+ const draftHeader = existingDraft
504
+ ?? [
505
+ `# ${milestoneId}: New milestone ${milestoneId}`,
506
+ "",
507
+ "This draft was captured automatically from structured question responses.",
508
+ "Use it so `/gsd` can resume the in-flight milestone discussion.",
509
+ "",
510
+ ].join("\n");
511
+ await saveFile(draftPath, `${draftHeader.trimEnd()}\n\n## Captured Question Round — ${timestamp}\n\n${exchange}`);
512
+ }
390
513
  function withDepthGateDisplayReason(result, displayReason = "Depth confirmation is waiting for your answer.") {
391
514
  if (!result.block)
392
515
  return result;
@@ -463,6 +586,18 @@ export function registerHooks(pi, ecosystemHandlers) {
463
586
  if (isAutoActive() || preserveCloseoutSurface) {
464
587
  ctx.ui.setWidget("gsd-health", undefined);
465
588
  }
589
+ // Cold start after /quit relaunches with cwd at the project root. When
590
+ // auto-mode is neither active nor paused (its own resume path re-enters the
591
+ // worktree with a lease check — auto.ts:3032), proactively chdir back into
592
+ // the active milestone's worktree so subsequent work isn't stranded at the
593
+ // root. Best-effort and a no-op when already inside a worktree.
594
+ if (!isAutoActive() && !isAutoPaused() && !preserveCloseoutSurface) {
595
+ try {
596
+ const { reenterActiveWorktreeIfNeeded } = await import("../worktree-reentry.js");
597
+ await reenterActiveWorktreeIfNeeded(basePath);
598
+ }
599
+ catch { /* non-fatal */ }
600
+ }
466
601
  });
467
602
  pi.on("session_switch", async (_event, ctx) => {
468
603
  const basePath = contextBasePath(ctx);
@@ -901,6 +1036,17 @@ export function registerHooks(pi, ecosystemHandlers) {
901
1036
  if (isAutoActive() && typeof event.toolCallId === "string") {
902
1037
  markToolEnd(event.toolCallId);
903
1038
  }
1039
+ const toolName = canonicalToolName(event.toolName);
1040
+ if (isAutoActive() && toolName === "read" && !event.isError) {
1041
+ const store = beginSourceObservationStoreForCurrentUnit(ctx);
1042
+ if (store) {
1043
+ store.observeRead(event.input);
1044
+ }
1045
+ }
1046
+ if (!event.isError) {
1047
+ refreshSourceObservationAfterMutation(toolName, event.input, ctx);
1048
+ clearSourceObservationsAfterShell(toolName);
1049
+ }
904
1050
  if (isAutoActive() && event.isError) {
905
1051
  const resultPayload = ("result" in event ? event.result : undefined);
906
1052
  const errorText = typeof resultPayload === "string"
@@ -917,12 +1063,10 @@ export function registerHooks(pi, ecosystemHandlers) {
917
1063
  else if (isAutoActive()) {
918
1064
  clearToolInvocationError();
919
1065
  }
920
- const toolName = canonicalToolName(event.toolName);
921
1066
  if (toolName !== "ask_user_questions")
922
1067
  return;
923
1068
  const basePath = contextBasePath(ctx);
924
1069
  const milestoneId = await getDiscussionMilestoneIdFor(basePath);
925
- const queueActive = isQueuePhaseActive(basePath);
926
1070
  const details = event.details;
927
1071
  // ── Discussion gate enforcement: handle gate question responses ──
928
1072
  // If the result is cancelled or has no response, the pending gate stays active
@@ -995,38 +1139,9 @@ export function registerHooks(pi, ecosystemHandlers) {
995
1139
  break;
996
1140
  }
997
1141
  }
998
- if (!milestoneId && !queueActive)
999
- return;
1000
1142
  if (!milestoneId)
1001
1143
  return;
1002
- const milestoneDir = resolveMilestonePath(basePath, milestoneId);
1003
- if (!milestoneDir)
1004
- return;
1005
- const discussionPath = join(milestoneDir, buildMilestoneFileName(milestoneId, "DISCUSSION"));
1006
- const timestamp = new Date().toISOString();
1007
- const lines = [`## Exchange — ${timestamp}`, ""];
1008
- for (const question of questions) {
1009
- lines.push(`### ${question.header ?? "Question"}`, "", question.question ?? "");
1010
- if (Array.isArray(question.options)) {
1011
- lines.push("");
1012
- for (const opt of question.options) {
1013
- lines.push(`- **${opt.label}** — ${opt.description ?? ""}`);
1014
- }
1015
- }
1016
- const answer = details.response?.answers?.[question.id];
1017
- if (answer) {
1018
- lines.push("");
1019
- const selected = Array.isArray(answer.selected) ? answer.selected.join(", ") : answer.selected;
1020
- lines.push(`**Selected:** ${selected}`);
1021
- if (answer.notes) {
1022
- lines.push(`**Notes:** ${answer.notes}`);
1023
- }
1024
- }
1025
- lines.push("");
1026
- }
1027
- lines.push("---", "");
1028
- const existing = await loadFile(discussionPath) ?? `# ${milestoneId} Discussion Log\n\n`;
1029
- await saveFile(discussionPath, existing + lines.join("\n"));
1144
+ await saveDiscussionQuestionRound(basePath, milestoneId, questions, details);
1030
1145
  });
1031
1146
  pi.on("tool_execution_start", async (event, ctx) => {
1032
1147
  const basePath = contextBasePath(ctx);
@@ -1078,51 +1193,51 @@ export function registerHooks(pi, ecosystemHandlers) {
1078
1193
  const payload = event.payload;
1079
1194
  if (!payload || typeof payload !== "object")
1080
1195
  return;
1081
- // ── Observation Masking ─────────────────────────────────────────────
1082
- // Replace old tool results with placeholders to reduce context bloat.
1083
- // Only active during auto-mode when context_management.observation_masking is enabled.
1084
- if (isAutoActive()) {
1085
- try {
1086
- const { loadEffectiveGSDPreferences } = await import("../preferences.js");
1087
- const prefs = loadEffectiveGSDPreferences();
1088
- const cmConfig = prefs?.preferences.context_management;
1089
- // Observation masking: replace old tool results with placeholders
1090
- if (cmConfig?.observation_masking !== false) {
1091
- const keepTurns = cmConfig?.observation_mask_turns ?? 8;
1092
- const { createObservationMask } = await import("../context-masker.js");
1093
- const mask = createObservationMask(keepTurns);
1094
- const messages = payload.messages;
1095
- if (Array.isArray(messages)) {
1096
- payload.messages = mask(messages);
1097
- }
1196
+ // ── Context Management ──────────────────────────────────────────────
1197
+ // Load preferences once for both masking and truncation.
1198
+ try {
1199
+ const { loadEffectiveGSDPreferences } = await import("../preferences.js");
1200
+ const { createObservationMask, createResponsesInputObservationMask, truncateContextResultMessages, truncateResponsesInputResultItems, } = await import("../context-masker.js");
1201
+ const prefs = loadEffectiveGSDPreferences();
1202
+ const cmConfig = prefs?.preferences.context_management;
1203
+ // Observation masking: replace old tool results with placeholders.
1204
+ // Only active during auto-mode when context_management.observation_masking is enabled.
1205
+ if (isAutoActive() && cmConfig?.observation_masking !== false) {
1206
+ const keepTurns = cmConfig?.observation_mask_turns ?? 8;
1207
+ const messages = payload.messages;
1208
+ if (Array.isArray(messages)) {
1209
+ payload.messages = createObservationMask(keepTurns)(messages);
1098
1210
  }
1099
- // Tool result truncation: cap individual tool result content length.
1100
- // In pi-ai format, toolResult messages have role: "toolResult" and content: TextContent[].
1101
- // Creates new objects to avoid mutating shared conversation state.
1102
- const maxChars = cmConfig?.tool_result_max_chars ?? 800;
1103
- const msgs = payload.messages;
1104
- if (Array.isArray(msgs)) {
1105
- payload.messages = msgs.map((msg) => {
1106
- // Match toolResult messages (role: "toolResult", content is array of content blocks)
1107
- if (msg?.role === "toolResult" && Array.isArray(msg.content)) {
1108
- const blocks = msg.content;
1109
- const totalLen = blocks.reduce((sum, b) => sum + (typeof b.text === "string" ? b.text.length : 0), 0);
1110
- if (totalLen > maxChars) {
1111
- const truncated = blocks.map(b => {
1112
- if (typeof b.text === "string" && b.text.length > maxChars) {
1113
- return { ...b, text: b.text.slice(0, maxChars) + "\n…[truncated]" };
1114
- }
1115
- return b;
1116
- });
1117
- return { ...msg, content: truncated };
1118
- }
1119
- }
1120
- return msg;
1121
- });
1211
+ const input = payload.input;
1212
+ if (Array.isArray(input)) {
1213
+ payload.input = createResponsesInputObservationMask(keepTurns)(input);
1214
+ }
1215
+ }
1216
+ // Tool result truncation: cap individual tool result content length.
1217
+ // Applies in ALL modes (auto + interactive) to prevent context bloat.
1218
+ // In pi-ai format, toolResult messages have role: "toolResult" and content: TextContent[].
1219
+ // Creates new objects to avoid mutating shared conversation state.
1220
+ const maxChars = cmConfig?.tool_result_max_chars ?? 800;
1221
+ const msgs = payload.messages;
1222
+ if (Array.isArray(msgs)) {
1223
+ payload.messages = truncateContextResultMessages(msgs, maxChars);
1224
+ }
1225
+ const input = payload.input;
1226
+ if (Array.isArray(input)) {
1227
+ payload.input = truncateResponsesInputResultItems(input, maxChars);
1228
+ }
1229
+ }
1230
+ catch { /* non-fatal */ }
1231
+ try {
1232
+ if (isAutoActive()) {
1233
+ const sourceContextBlock = getSourceObservationStore().renderActiveBlock();
1234
+ if (sourceContextBlock) {
1235
+ const nextPayload = injectSourceContextBlockIntoPayload(payload, sourceContextBlock);
1236
+ Object.assign(payload, nextPayload);
1122
1237
  }
1123
1238
  }
1124
- catch { /* non-fatal */ }
1125
1239
  }
1240
+ catch { /* non-fatal */ }
1126
1241
  // ── Service Tier ────────────────────────────────────────────────────
1127
1242
  const modelId = event.model?.id;
1128
1243
  if (!modelId)
@@ -56,7 +56,7 @@ export const BUNDLED_SKILL_TRIGGERS = [
56
56
  { trigger: "Core Web Vitals — fix LCP, CLS, INP; layout shifts; page experience optimization", skill: "core-web-vitals" },
57
57
  { trigger: "GitHub Actions CI/CD — write, run, and debug workflow files; live syntax and run monitoring", skill: "github-workflows" },
58
58
  { trigger: "Comprehensive web quality audit — performance, accessibility, SEO, and best-practices (Lighthouse-style)", skill: "web-quality-audit" },
59
- { trigger: "gsd-browser UAT default browser MCP/CLI for real UI verification, screenshots, assertions, console/network diagnostics", skill: "gsd-browser" },
59
+ { trigger: "gsd-browser opt-in and External MCP UAT screenshots, assertions, console/network diagnostics", skill: "gsd-browser" },
60
60
  { trigger: "Browser automation — open sites, fill forms, click, screenshot, scrape, or test web apps programmatically", skill: "agent-browser" },
61
61
  { trigger: "Review UI code for Web Interface Guidelines compliance — UX, design, and accessibility patterns", skill: "web-design-guidelines" },
62
62
  { trigger: "UI/UX patterns reference — animations, CSS, typography, prefetching, icons (file:line findings)", skill: "userinterface-wiki" },