gsd-pi 2.80.0-dev.2be7caf18 → 2.80.0-dev.3065f7bfa

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 (268) hide show
  1. package/README.md +1 -0
  2. package/dist/resources/.managed-resources-content-hash +1 -1
  3. package/dist/resources/extensions/gsd/auto/phases.js +178 -108
  4. package/dist/resources/extensions/gsd/auto/resolve.js +17 -0
  5. package/dist/resources/extensions/gsd/auto/run-unit.js +20 -30
  6. package/dist/resources/extensions/gsd/auto/session.js +8 -0
  7. package/dist/resources/extensions/gsd/auto-direct-dispatch.js +5 -32
  8. package/dist/resources/extensions/gsd/auto-dispatch.js +16 -0
  9. package/dist/resources/extensions/gsd/auto-post-unit.js +17 -4
  10. package/dist/resources/extensions/gsd/auto-prompts.js +90 -15
  11. package/dist/resources/extensions/gsd/auto-recovery.js +43 -1
  12. package/dist/resources/extensions/gsd/auto-start.js +197 -6
  13. package/dist/resources/extensions/gsd/auto-supervisor.js +8 -1
  14. package/dist/resources/extensions/gsd/auto-timeout-recovery.js +2 -2
  15. package/dist/resources/extensions/gsd/auto-worktree.js +111 -1
  16. package/dist/resources/extensions/gsd/auto.js +79 -24
  17. package/dist/resources/extensions/gsd/bootstrap/agent-end-recovery.js +42 -2
  18. package/dist/resources/extensions/gsd/bootstrap/db-tools.js +49 -36
  19. package/dist/resources/extensions/gsd/bootstrap/dynamic-tools.js +15 -5
  20. package/dist/resources/extensions/gsd/bootstrap/exec-tools.js +9 -3
  21. package/dist/resources/extensions/gsd/bootstrap/journal-tools.js +7 -1
  22. package/dist/resources/extensions/gsd/bootstrap/memory-tools.js +9 -3
  23. package/dist/resources/extensions/gsd/bootstrap/query-tools.js +8 -2
  24. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +238 -28
  25. package/dist/resources/extensions/gsd/bootstrap/system-context.js +82 -23
  26. package/dist/resources/extensions/gsd/clean-root-preflight.js +24 -6
  27. package/dist/resources/extensions/gsd/commands-handlers.js +23 -9
  28. package/dist/resources/extensions/gsd/context-budget.js +37 -2
  29. package/dist/resources/extensions/gsd/db/unit-dispatches.js +39 -0
  30. package/dist/resources/extensions/gsd/db-base-schema.js +4 -2
  31. package/dist/resources/extensions/gsd/db-migration-steps.js +6 -0
  32. package/dist/resources/extensions/gsd/ecosystem/gsd-extension-api.js +2 -0
  33. package/dist/resources/extensions/gsd/git-service.js +36 -4
  34. package/dist/resources/extensions/gsd/gsd-db.js +46 -13
  35. package/dist/resources/extensions/gsd/guided-flow.js +66 -32
  36. package/dist/resources/extensions/gsd/memory-store.js +69 -12
  37. package/dist/resources/extensions/gsd/native-git-bridge.js +32 -8
  38. package/dist/resources/extensions/gsd/orphan-stash-audit.js +101 -0
  39. package/dist/resources/extensions/gsd/parallel-orchestrator.js +13 -3
  40. package/dist/resources/extensions/gsd/pre-execution-checks.js +22 -0
  41. package/dist/resources/extensions/gsd/prompt-loader.js +28 -2
  42. package/dist/resources/extensions/gsd/prompts/complete-milestone.md +16 -13
  43. package/dist/resources/extensions/gsd/prompts/complete-slice.md +1 -1
  44. package/dist/resources/extensions/gsd/prompts/execute-task.md +4 -2
  45. package/dist/resources/extensions/gsd/prompts/plan-slice.md +1 -1
  46. package/dist/resources/extensions/gsd/prompts/replan-slice.md +2 -2
  47. package/dist/resources/extensions/gsd/tools/memory-tools.js +1 -0
  48. package/dist/resources/extensions/gsd/tools/workflow-tool-executors.js +1 -1
  49. package/dist/resources/extensions/gsd/unit-runtime.js +11 -0
  50. package/dist/resources/extensions/gsd/workflow-protocol.js +131 -0
  51. package/dist/resources/extensions/gsd/worktree-resolver.js +68 -21
  52. package/dist/tsconfig.extensions.tsbuildinfo +1 -1
  53. package/dist/web/standalone/.next/BUILD_ID +1 -1
  54. package/dist/web/standalone/.next/app-path-routes-manifest.json +15 -15
  55. package/dist/web/standalone/.next/build-manifest.json +2 -2
  56. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  57. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  58. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  59. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  60. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  61. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  62. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  63. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  64. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  65. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  66. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  67. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  68. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  69. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  70. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  71. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  72. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  73. package/dist/web/standalone/.next/server/app/index.html +1 -1
  74. package/dist/web/standalone/.next/server/app/index.rsc +1 -1
  75. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  76. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
  77. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  78. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  79. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  80. package/dist/web/standalone/.next/server/app-paths-manifest.json +15 -15
  81. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  82. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  83. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  84. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  85. package/package.json +3 -3
  86. package/packages/mcp-server/src/workflow-tools.test.ts +9 -2
  87. package/packages/native/tsconfig.tsbuildinfo +1 -1
  88. package/packages/pi-agent-core/dist/agent-loop.d.ts.map +1 -1
  89. package/packages/pi-agent-core/dist/agent-loop.js +4 -1
  90. package/packages/pi-agent-core/dist/agent-loop.js.map +1 -1
  91. package/packages/pi-agent-core/dist/agent.d.ts +5 -0
  92. package/packages/pi-agent-core/dist/agent.d.ts.map +1 -1
  93. package/packages/pi-agent-core/dist/agent.js +2 -0
  94. package/packages/pi-agent-core/dist/agent.js.map +1 -1
  95. package/packages/pi-agent-core/dist/index.d.ts +1 -0
  96. package/packages/pi-agent-core/dist/index.d.ts.map +1 -1
  97. package/packages/pi-agent-core/dist/index.js +2 -0
  98. package/packages/pi-agent-core/dist/index.js.map +1 -1
  99. package/packages/pi-agent-core/dist/token-audit.d.ts +47 -0
  100. package/packages/pi-agent-core/dist/token-audit.d.ts.map +1 -0
  101. package/packages/pi-agent-core/dist/token-audit.js +221 -0
  102. package/packages/pi-agent-core/dist/token-audit.js.map +1 -0
  103. package/packages/pi-agent-core/dist/types.d.ts +9 -0
  104. package/packages/pi-agent-core/dist/types.d.ts.map +1 -1
  105. package/packages/pi-agent-core/dist/types.js.map +1 -1
  106. package/packages/pi-agent-core/src/agent-loop.test.ts +128 -0
  107. package/packages/pi-agent-core/src/agent-loop.ts +4 -1
  108. package/packages/pi-agent-core/src/agent.ts +8 -0
  109. package/packages/pi-agent-core/src/index.ts +2 -0
  110. package/packages/pi-agent-core/src/token-audit.test.ts +189 -0
  111. package/packages/pi-agent-core/src/token-audit.ts +287 -0
  112. package/packages/pi-agent-core/src/types.ts +14 -0
  113. package/packages/pi-agent-core/tsconfig.tsbuildinfo +1 -1
  114. package/packages/pi-coding-agent/dist/core/agent-session-abort-order.test.js +32 -0
  115. package/packages/pi-coding-agent/dist/core/agent-session-abort-order.test.js.map +1 -1
  116. package/packages/pi-coding-agent/dist/core/agent-session-tool-refresh.test.js +18 -0
  117. package/packages/pi-coding-agent/dist/core/agent-session-tool-refresh.test.js.map +1 -1
  118. package/packages/pi-coding-agent/dist/core/agent-session.d.ts +12 -0
  119. package/packages/pi-coding-agent/dist/core/agent-session.d.ts.map +1 -1
  120. package/packages/pi-coding-agent/dist/core/agent-session.js +41 -7
  121. package/packages/pi-coding-agent/dist/core/agent-session.js.map +1 -1
  122. package/packages/pi-coding-agent/dist/core/extensions/loader.d.ts.map +1 -1
  123. package/packages/pi-coding-agent/dist/core/extensions/loader.js +8 -0
  124. package/packages/pi-coding-agent/dist/core/extensions/loader.js.map +1 -1
  125. package/packages/pi-coding-agent/dist/core/extensions/runner.d.ts +2 -0
  126. package/packages/pi-coding-agent/dist/core/extensions/runner.d.ts.map +1 -1
  127. package/packages/pi-coding-agent/dist/core/extensions/runner.js +3 -6
  128. package/packages/pi-coding-agent/dist/core/extensions/runner.js.map +1 -1
  129. package/packages/pi-coding-agent/dist/core/extensions/runner.test.js +3 -3
  130. package/packages/pi-coding-agent/dist/core/extensions/runner.test.js.map +1 -1
  131. package/packages/pi-coding-agent/dist/core/extensions/types.d.ts +32 -1
  132. package/packages/pi-coding-agent/dist/core/extensions/types.d.ts.map +1 -1
  133. package/packages/pi-coding-agent/dist/core/extensions/types.js.map +1 -1
  134. package/packages/pi-coding-agent/dist/core/hooks-runner.test.js +2 -0
  135. package/packages/pi-coding-agent/dist/core/hooks-runner.test.js.map +1 -1
  136. package/packages/pi-coding-agent/dist/core/sdk-tool-filter.test.d.ts +2 -0
  137. package/packages/pi-coding-agent/dist/core/sdk-tool-filter.test.d.ts.map +1 -0
  138. package/packages/pi-coding-agent/dist/core/sdk-tool-filter.test.js +46 -0
  139. package/packages/pi-coding-agent/dist/core/sdk-tool-filter.test.js.map +1 -0
  140. package/packages/pi-coding-agent/dist/core/sdk.d.ts +10 -2
  141. package/packages/pi-coding-agent/dist/core/sdk.d.ts.map +1 -1
  142. package/packages/pi-coding-agent/dist/core/sdk.js +74 -2
  143. package/packages/pi-coding-agent/dist/core/sdk.js.map +1 -1
  144. package/packages/pi-coding-agent/dist/core/skill-tool.test.js +22 -0
  145. package/packages/pi-coding-agent/dist/core/skill-tool.test.js.map +1 -1
  146. package/packages/pi-coding-agent/dist/core/system-prompt.d.ts +6 -7
  147. package/packages/pi-coding-agent/dist/core/system-prompt.d.ts.map +1 -1
  148. package/packages/pi-coding-agent/dist/core/system-prompt.js +2 -3
  149. package/packages/pi-coding-agent/dist/core/system-prompt.js.map +1 -1
  150. package/packages/pi-coding-agent/src/core/agent-session-abort-order.test.ts +36 -0
  151. package/packages/pi-coding-agent/src/core/agent-session-tool-refresh.test.ts +25 -0
  152. package/packages/pi-coding-agent/src/core/agent-session.ts +45 -7
  153. package/packages/pi-coding-agent/src/core/extensions/loader.ts +10 -0
  154. package/packages/pi-coding-agent/src/core/extensions/runner.test.ts +3 -3
  155. package/packages/pi-coding-agent/src/core/extensions/runner.ts +5 -5
  156. package/packages/pi-coding-agent/src/core/extensions/types.ts +35 -1
  157. package/packages/pi-coding-agent/src/core/hooks-runner.test.ts +2 -0
  158. package/packages/pi-coding-agent/src/core/sdk-tool-filter.test.ts +60 -0
  159. package/packages/pi-coding-agent/src/core/sdk.ts +85 -3
  160. package/packages/pi-coding-agent/src/core/skill-tool.test.ts +28 -0
  161. package/packages/pi-coding-agent/src/core/system-prompt.ts +8 -10
  162. package/packages/pi-coding-agent/tsconfig.tsbuildinfo +1 -1
  163. package/src/resources/extensions/gsd/auto/loop-deps.ts +2 -2
  164. package/src/resources/extensions/gsd/auto/phases.ts +251 -172
  165. package/src/resources/extensions/gsd/auto/resolve.ts +23 -1
  166. package/src/resources/extensions/gsd/auto/run-unit.ts +25 -30
  167. package/src/resources/extensions/gsd/auto/session.ts +8 -0
  168. package/src/resources/extensions/gsd/auto-direct-dispatch.ts +8 -34
  169. package/src/resources/extensions/gsd/auto-dispatch.ts +16 -0
  170. package/src/resources/extensions/gsd/auto-post-unit.ts +18 -4
  171. package/src/resources/extensions/gsd/auto-prompts.ts +95 -14
  172. package/src/resources/extensions/gsd/auto-recovery.ts +54 -0
  173. package/src/resources/extensions/gsd/auto-start.ts +230 -9
  174. package/src/resources/extensions/gsd/auto-supervisor.ts +7 -0
  175. package/src/resources/extensions/gsd/auto-timeout-recovery.ts +2 -2
  176. package/src/resources/extensions/gsd/auto-worktree.ts +123 -0
  177. package/src/resources/extensions/gsd/auto.ts +87 -19
  178. package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +49 -1
  179. package/src/resources/extensions/gsd/bootstrap/db-tools.ts +50 -36
  180. package/src/resources/extensions/gsd/bootstrap/dynamic-tools.ts +16 -5
  181. package/src/resources/extensions/gsd/bootstrap/exec-tools.ts +10 -3
  182. package/src/resources/extensions/gsd/bootstrap/journal-tools.ts +8 -1
  183. package/src/resources/extensions/gsd/bootstrap/memory-tools.ts +10 -3
  184. package/src/resources/extensions/gsd/bootstrap/query-tools.ts +9 -2
  185. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +283 -28
  186. package/src/resources/extensions/gsd/bootstrap/system-context.ts +90 -22
  187. package/src/resources/extensions/gsd/clean-root-preflight.ts +32 -7
  188. package/src/resources/extensions/gsd/commands-handlers.ts +34 -15
  189. package/src/resources/extensions/gsd/context-budget.ts +44 -2
  190. package/src/resources/extensions/gsd/db/unit-dispatches.ts +41 -0
  191. package/src/resources/extensions/gsd/db-base-schema.ts +4 -2
  192. package/src/resources/extensions/gsd/db-migration-steps.ts +8 -0
  193. package/src/resources/extensions/gsd/ecosystem/gsd-extension-api.ts +3 -0
  194. package/src/resources/extensions/gsd/git-service.ts +46 -8
  195. package/src/resources/extensions/gsd/gsd-db.ts +50 -13
  196. package/src/resources/extensions/gsd/guided-flow.ts +84 -39
  197. package/src/resources/extensions/gsd/memory-store.ts +77 -12
  198. package/src/resources/extensions/gsd/native-git-bridge.ts +39 -6
  199. package/src/resources/extensions/gsd/orphan-stash-audit.ts +117 -0
  200. package/src/resources/extensions/gsd/parallel-orchestrator.ts +13 -3
  201. package/src/resources/extensions/gsd/pre-execution-checks.ts +23 -0
  202. package/src/resources/extensions/gsd/prompt-loader.ts +27 -2
  203. package/src/resources/extensions/gsd/prompts/complete-milestone.md +16 -13
  204. package/src/resources/extensions/gsd/prompts/complete-slice.md +1 -1
  205. package/src/resources/extensions/gsd/prompts/execute-task.md +4 -2
  206. package/src/resources/extensions/gsd/prompts/plan-slice.md +1 -1
  207. package/src/resources/extensions/gsd/prompts/replan-slice.md +2 -2
  208. package/src/resources/extensions/gsd/tests/artifact-retry-cap.test.ts +2 -2
  209. package/src/resources/extensions/gsd/tests/auto-loop.test.ts +417 -10
  210. package/src/resources/extensions/gsd/tests/auto-phases-lifecycle.test.ts +56 -13
  211. package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +14 -1
  212. package/src/resources/extensions/gsd/tests/auto-wrapup-inflight-guard.test.ts +168 -6
  213. package/src/resources/extensions/gsd/tests/clean-root-preflight.test.ts +15 -6
  214. package/src/resources/extensions/gsd/tests/compaction-snapshot.test.ts +3 -1
  215. package/src/resources/extensions/gsd/tests/complete-milestone-excerpt.test.ts +31 -0
  216. package/src/resources/extensions/gsd/tests/complete-slice-composer.test.ts +3 -2
  217. package/src/resources/extensions/gsd/tests/context-budget.test.ts +10 -1
  218. package/src/resources/extensions/gsd/tests/context-store.test.ts +7 -1
  219. package/src/resources/extensions/gsd/tests/custom-engine-loop-integration.test.ts +5 -1
  220. package/src/resources/extensions/gsd/tests/dispatch-rule-coverage.test.ts +313 -0
  221. package/src/resources/extensions/gsd/tests/execute-task-rendering.test.ts +5 -2
  222. package/src/resources/extensions/gsd/tests/fast-forward-reused-milestone-branch.test.ts +219 -0
  223. package/src/resources/extensions/gsd/tests/finalize-survivor-branch.test.ts +132 -0
  224. package/src/resources/extensions/gsd/tests/integration/git-service.test.ts +54 -0
  225. package/src/resources/extensions/gsd/tests/isolation-none-branch-guard.test.ts +6 -3
  226. package/src/resources/extensions/gsd/tests/journal-integration.test.ts +239 -1
  227. package/src/resources/extensions/gsd/tests/journal-query-tool.test.ts +32 -0
  228. package/src/resources/extensions/gsd/tests/knowledge.test.ts +47 -0
  229. package/src/resources/extensions/gsd/tests/memory-decay-factor.test.ts +90 -0
  230. package/src/resources/extensions/gsd/tests/merge-conflict-stops-loop.test.ts +1 -0
  231. package/src/resources/extensions/gsd/tests/milestone-merge-stash-restore.test.ts +242 -0
  232. package/src/resources/extensions/gsd/tests/native-git-bridge-exec-fallback.test.ts +34 -2
  233. package/src/resources/extensions/gsd/tests/originalbase-path-comparison.test.ts +3 -0
  234. package/src/resources/extensions/gsd/tests/orphan-merge-bootstrap.test.ts +133 -0
  235. package/src/resources/extensions/gsd/tests/orphan-stash-audit.test.ts +201 -0
  236. package/src/resources/extensions/gsd/tests/parallel-orchestrator-fast-forward.test.ts +113 -0
  237. package/src/resources/extensions/gsd/tests/pre-execution-checks.test.ts +45 -5
  238. package/src/resources/extensions/gsd/tests/prompt-duplication-cuts.test.ts +230 -0
  239. package/src/resources/extensions/gsd/tests/prompt-step-ordering.test.ts +19 -0
  240. package/src/resources/extensions/gsd/tests/query-tools-db-open.test.ts +3 -3
  241. package/src/resources/extensions/gsd/tests/restore-tools-after-discuss.test.ts +38 -17
  242. package/src/resources/extensions/gsd/tests/schema-v27-v28-sequence.test.ts +156 -0
  243. package/src/resources/extensions/gsd/tests/select-resumable-milestone.test.ts +96 -0
  244. package/src/resources/extensions/gsd/tests/session-switch-abort-misclassification.test.ts +106 -0
  245. package/src/resources/extensions/gsd/tests/signal-handlers.test.ts +27 -0
  246. package/src/resources/extensions/gsd/tests/stalled-tool-recovery.test.ts +49 -1
  247. package/src/resources/extensions/gsd/tests/start-auto-detached.test.ts +38 -0
  248. package/src/resources/extensions/gsd/tests/state-corruption-2945.test.ts +1 -0
  249. package/src/resources/extensions/gsd/tests/system-context-memory.test.ts +112 -0
  250. package/src/resources/extensions/gsd/tests/system-context-message-routing.test.ts +7 -9
  251. package/src/resources/extensions/gsd/tests/token-tool-gating.test.ts +291 -0
  252. package/src/resources/extensions/gsd/tests/unit-dispatches.test.ts +30 -0
  253. package/src/resources/extensions/gsd/tests/unit-runtime.test.ts +30 -0
  254. package/src/resources/extensions/gsd/tests/unstructured-continue-context-injection.test.ts +5 -4
  255. package/src/resources/extensions/gsd/tests/workflow-protocol-excerpt.test.ts +99 -0
  256. package/src/resources/extensions/gsd/tests/workflow-tool-executors.test.ts +3 -0
  257. package/src/resources/extensions/gsd/tests/worktree-journal-events.test.ts +1 -0
  258. package/src/resources/extensions/gsd/tests/worktree-path-injection.test.ts +22 -19
  259. package/src/resources/extensions/gsd/tests/worktree-project-root-degrade.test.ts +66 -0
  260. package/src/resources/extensions/gsd/tests/worktree-resolver.test.ts +167 -4
  261. package/src/resources/extensions/gsd/tools/memory-tools.ts +1 -0
  262. package/src/resources/extensions/gsd/tools/workflow-tool-executors.ts +1 -1
  263. package/src/resources/extensions/gsd/unit-runtime.ts +11 -0
  264. package/src/resources/extensions/gsd/workflow-protocol.ts +160 -0
  265. package/src/resources/extensions/gsd/worktree-resolver.ts +85 -19
  266. package/src/resources/extensions/gsd/tests/phases-merge-error-stops-auto.test.ts +0 -97
  267. /package/dist/web/standalone/.next/static/{3EDDd9ULaybSupPoA_vf- → O2WvnXROue6l7nAp_4dhC}/_buildManifest.js +0 -0
  268. /package/dist/web/standalone/.next/static/{3EDDd9ULaybSupPoA_vf- → O2WvnXROue6l7nAp_4dhC}/_ssgManifest.js +0 -0
@@ -1,3 +1,5 @@
1
+ // Project/App: GSD-2
2
+ // File Purpose: Registers GSD extension runtime hooks and token-saving tool policies.
1
3
  import { join } from "node:path";
2
4
  import { isToolCallEventType } from "@gsd/pi-coding-agent";
3
5
  import { updateSnapshot } from "../ecosystem/gsd-extension-api.js";
@@ -19,10 +21,149 @@ import { initNotificationWidget } from "../notification-widget.js";
19
21
  import { resolveWorktreeProjectRoot } from "../worktree-root.js";
20
22
  import { extractSubagentAgentClasses } from "./subagent-input.js";
21
23
  import { approvalGateIdForUnit, isExplicitApprovalResponse, shouldPauseForUserApprovalQuestion } from "../user-input-boundary.js";
24
+ import { resolveSkillManifest } from "../skill-manifest.js";
22
25
  // Skip the welcome screen on the very first session_start — cli.ts already
23
26
  // printed it before the TUI launched. Only re-print on /clear (subsequent sessions).
24
27
  let isFirstSession = true;
25
28
  let approvalQuestionAbortInFlight = false;
29
+ let deferredApprovalGate = null;
30
+ export const MINIMAL_GSD_TOOL_NAMES = [
31
+ "gsd_exec",
32
+ "gsd_exec_search",
33
+ "gsd_resume",
34
+ "gsd_milestone_status",
35
+ "gsd_checkpoint_db",
36
+ "memory_query",
37
+ "capture_thought",
38
+ ];
39
+ export const MINIMAL_AUTO_BASE_TOOL_NAMES = [
40
+ "ask_user_questions",
41
+ "bash",
42
+ "bg_shell",
43
+ "edit",
44
+ "glob",
45
+ "grep",
46
+ "ls",
47
+ "read",
48
+ "write",
49
+ ];
50
+ const AUTO_UNIT_SCOPED_TOOLS = {
51
+ "research-milestone": ["gsd_summary_save", "gsd_decision_save"],
52
+ "plan-milestone": ["gsd_plan_milestone", "gsd_decision_save", "gsd_requirement_update"],
53
+ "discuss-milestone": ["gsd_summary_save", "gsd_decision_save", "gsd_requirement_save"],
54
+ "validate-milestone": ["gsd_validate_milestone", "gsd_reassess_roadmap", "subagent"],
55
+ "complete-milestone": ["gsd_complete_milestone", "subagent"],
56
+ "research-slice": ["gsd_summary_save", "gsd_decision_save"],
57
+ "plan-slice": ["gsd_plan_slice", "gsd_plan_task", "gsd_decision_save"],
58
+ "refine-slice": ["gsd_plan_slice", "gsd_plan_task", "gsd_decision_save"],
59
+ "replan-slice": ["gsd_replan_slice", "gsd_plan_task", "gsd_decision_save"],
60
+ "complete-slice": ["gsd_slice_complete", "gsd_decision_save", "gsd_requirement_update", "subagent"],
61
+ "reassess-roadmap": ["gsd_reassess_roadmap"],
62
+ "execute-task": ["gsd_task_complete", "gsd_decision_save"],
63
+ "execute-task-simple": ["gsd_task_complete", "gsd_decision_save"],
64
+ "reactive-execute": ["gsd_task_complete", "gsd_decision_save"],
65
+ "run-uat": ["gsd_summary_save"],
66
+ "gate-evaluate": ["gsd_save_gate_result"],
67
+ "rewrite-docs": ["gsd_summary_save", "gsd_decision_save"],
68
+ "workflow-preferences": ["gsd_summary_save"],
69
+ "discuss-project": ["gsd_summary_save", "gsd_decision_save", "gsd_requirement_save"],
70
+ "discuss-requirements": ["gsd_requirement_save", "gsd_summary_save"],
71
+ "research-decision": ["gsd_summary_save"],
72
+ "research-project": ["gsd_summary_save", "gsd_decision_save"],
73
+ };
74
+ const WORKFLOW_GSD_TOOL_NAMES = [
75
+ ...MINIMAL_GSD_TOOL_NAMES,
76
+ ...Object.values(AUTO_UNIT_SCOPED_TOOLS).flat(),
77
+ ].filter(isGsdManagedTool);
78
+ function isGsdManagedTool(name) {
79
+ return name.startsWith("gsd_") || name === "memory_query" || name === "capture_thought" || name === "gsd_graph";
80
+ }
81
+ export function buildMinimalGsdToolSet(activeToolNames) {
82
+ const active = new Set(activeToolNames);
83
+ const preserved = activeToolNames.filter((name) => !isGsdManagedTool(name));
84
+ const minimal = MINIMAL_GSD_TOOL_NAMES.filter((name) => active.has(name));
85
+ return [...new Set([...preserved, ...minimal])];
86
+ }
87
+ export function buildMinimalAutoGsdToolSet(activeToolNames, unitType) {
88
+ const active = new Set(activeToolNames);
89
+ const unitTools = unitType ? AUTO_UNIT_SCOPED_TOOLS[unitType] ?? [] : [];
90
+ const autoBaseTools = new Set(MINIMAL_AUTO_BASE_TOOL_NAMES);
91
+ const preserved = activeToolNames.filter((name) => autoBaseTools.has(name));
92
+ const scoped = [...MINIMAL_GSD_TOOL_NAMES, ...unitTools].filter((name) => active.has(name));
93
+ return [...new Set([...preserved, ...scoped])];
94
+ }
95
+ export function buildMinimalGsdWorkflowToolSet(activeToolNames) {
96
+ const active = new Set(activeToolNames);
97
+ const autoBaseTools = new Set(MINIMAL_AUTO_BASE_TOOL_NAMES);
98
+ const preserved = activeToolNames.filter((name) => autoBaseTools.has(name));
99
+ const scoped = WORKFLOW_GSD_TOOL_NAMES.filter((name) => active.has(name));
100
+ return [...new Set([...preserved, ...scoped])];
101
+ }
102
+ export function buildRequestScopedGsdToolSet(activeToolNames, requestCustomMessages) {
103
+ for (let index = (requestCustomMessages?.length ?? 0) - 1; index >= 0; index--) {
104
+ const currentCustomType = requestCustomMessages?.[index]?.customType;
105
+ if (currentCustomType === "gsd-run" ||
106
+ currentCustomType === "gsd-discuss" ||
107
+ currentCustomType === "gsd-doctor-heal" ||
108
+ currentCustomType === "gsd-triage") {
109
+ return buildMinimalGsdWorkflowToolSet(activeToolNames);
110
+ }
111
+ }
112
+ return undefined;
113
+ }
114
+ export function isFullGsdToolSurfaceRequested() {
115
+ return process.env.PI_GSD_FULL_TOOLS === "1";
116
+ }
117
+ function isGeneralGsdToolScopingRequested() {
118
+ return process.env.PI_GSD_MINIMAL_TOOLS === "1";
119
+ }
120
+ function applyMinimalGsdToolSurface(pi) {
121
+ if (isFullGsdToolSurfaceRequested())
122
+ return;
123
+ const dash = getAutoRuntimeSnapshot();
124
+ if (dash.active && dash.currentUnit) {
125
+ pi.setActiveTools(buildMinimalAutoGsdToolSet(pi.getActiveTools(), dash.currentUnit.type));
126
+ return;
127
+ }
128
+ if (!isGeneralGsdToolScopingRequested())
129
+ return;
130
+ pi.setActiveTools(buildMinimalGsdToolSet(pi.getActiveTools()));
131
+ }
132
+ export function scopeGsdWorkflowToolsForDispatch(pi, unitType) {
133
+ if (isFullGsdToolSurfaceRequested())
134
+ return null;
135
+ const current = pi.getActiveTools();
136
+ const scoped = unitType
137
+ ? buildMinimalAutoGsdToolSet(current, unitType)
138
+ : buildMinimalGsdWorkflowToolSet(current);
139
+ const toolsChanged = !(scoped.length === current.length && scoped.every((name, index) => name === current[index]));
140
+ const skillManifest = resolveSkillManifest(unitType);
141
+ const canScopeSkills = skillManifest !== null && pi.getVisibleSkills && pi.setVisibleSkills;
142
+ if (!toolsChanged && !canScopeSkills) {
143
+ return null;
144
+ }
145
+ if (toolsChanged) {
146
+ pi.setActiveTools(scoped);
147
+ }
148
+ const visibleSkills = canScopeSkills ? pi.getVisibleSkills() : undefined;
149
+ if (canScopeSkills) {
150
+ pi.setVisibleSkills(skillManifest);
151
+ }
152
+ return {
153
+ tools: toolsChanged ? current : null,
154
+ visibleSkills,
155
+ restoreVisibleSkills: Boolean(canScopeSkills),
156
+ };
157
+ }
158
+ export function restoreGsdWorkflowTools(pi, savedState) {
159
+ if (!savedState)
160
+ return;
161
+ if (savedState.tools)
162
+ pi.setActiveTools(savedState.tools);
163
+ if (savedState.restoreVisibleSkills && pi.setVisibleSkills) {
164
+ pi.setVisibleSkills(savedState.visibleSkills);
165
+ }
166
+ }
26
167
  async function deriveGsdState(basePath) {
27
168
  const { deriveState } = await import("../state.js");
28
169
  return deriveState(basePath);
@@ -71,11 +212,51 @@ async function applyCompactionThresholdOverride(ctx) {
71
212
  // Non-fatal: leave any existing override in place.
72
213
  }
73
214
  }
74
- export function resolveNotificationStoreBasePath(cwd = process.cwd()) {
75
- return resolveWorktreeProjectRoot(cwd);
215
+ function clearDeferredApprovalGate(basePath) {
216
+ if (!basePath || deferredApprovalGate?.basePath === basePath) {
217
+ deferredApprovalGate = null;
218
+ }
219
+ }
220
+ function deferApprovalGate(gateId, basePath) {
221
+ deferredApprovalGate = { gateId, basePath };
222
+ }
223
+ function contextBasePath(ctx) {
224
+ return typeof ctx?.cwd === "string" ? ctx.cwd : process.cwd();
225
+ }
226
+ function activateDeferredApprovalGate(basePath) {
227
+ if (deferredApprovalGate?.basePath !== basePath)
228
+ return;
229
+ setPendingGate(deferredApprovalGate.gateId, basePath);
230
+ deferredApprovalGate = null;
231
+ }
232
+ function isContextDraftSummarySave(toolName, input) {
233
+ if (toolName !== "gsd_summary_save" && toolName !== "summary_save")
234
+ return false;
235
+ if (!input || typeof input !== "object")
236
+ return false;
237
+ return input.artifact_type === "CONTEXT-DRAFT";
238
+ }
239
+ function shouldBlockDeferredApprovalTool(toolName, input, basePath) {
240
+ if (deferredApprovalGate?.basePath !== basePath)
241
+ return { block: false };
242
+ if (toolName === "ask_user_questions")
243
+ return { block: false };
244
+ if (isContextDraftSummarySave(toolName, input))
245
+ return { block: false };
246
+ return {
247
+ block: true,
248
+ reason: [
249
+ `HARD BLOCK: Approval question "${deferredApprovalGate.gateId}" has been shown to the user.`,
250
+ `Only CONTEXT-DRAFT persistence may finish in this same assistant turn.`,
251
+ `Wait for the user's answer before calling additional tools.`,
252
+ ].join(" "),
253
+ };
254
+ }
255
+ export function resolveNotificationStoreBasePath(basePath) {
256
+ return resolveWorktreeProjectRoot(basePath);
76
257
  }
77
258
  function initSessionNotifications(ctx) {
78
- initNotificationStore(resolveNotificationStoreBasePath());
259
+ initNotificationStore(resolveNotificationStoreBasePath(contextBasePath(ctx)));
79
260
  installNotifyInterceptor(ctx);
80
261
  initNotificationWidget(ctx);
81
262
  }
@@ -109,28 +290,30 @@ async function writeContextModeCompactionSnapshot(basePath) {
109
290
  }
110
291
  export function registerHooks(pi, ecosystemHandlers) {
111
292
  pi.on("session_start", async (_event, ctx) => {
293
+ const basePath = contextBasePath(ctx);
112
294
  initSessionNotifications(ctx);
113
295
  if (!isAutoActive()) {
114
296
  const { initHealthWidget } = await import("../health-widget.js");
115
297
  initHealthWidget(ctx);
116
298
  }
117
- resetWriteGateState(process.cwd());
299
+ resetWriteGateState(basePath);
118
300
  resetToolCallLoopGuard();
119
301
  approvalQuestionAbortInFlight = false;
302
+ clearDeferredApprovalGate();
120
303
  await resetAskUserQuestionsTurnCache();
121
304
  await syncServiceTierStatus(ctx);
122
305
  await applyDisabledModelProviderPolicy(ctx);
123
306
  await applyCompactionThresholdOverride(ctx);
124
307
  // Skip MCP auto-prep when running inside an auto-worktree (see session_switch below).
125
308
  const { isInAutoWorktree } = await import("../auto-worktree.js");
126
- if (!isInAutoWorktree(process.cwd())) {
309
+ if (!isInAutoWorktree(basePath)) {
127
310
  const { prepareWorkflowMcpForProject } = await import("../workflow-mcp-auto-prep.js");
128
- prepareWorkflowMcpForProject(ctx, process.cwd());
311
+ prepareWorkflowMcpForProject(ctx, basePath);
129
312
  }
130
313
  // Apply show_token_cost preference (#1515)
131
314
  try {
132
315
  const { loadEffectiveGSDPreferences } = await import("../preferences.js");
133
- const prefs = loadEffectiveGSDPreferences();
316
+ const prefs = loadEffectiveGSDPreferences(basePath);
134
317
  process.env.GSD_SHOW_TOKEN_COST = prefs?.preferences.show_token_cost ? "1" : "";
135
318
  }
136
319
  catch { /* non-fatal */ }
@@ -162,11 +345,13 @@ export function registerHooks(pi, ecosystemHandlers) {
162
345
  }
163
346
  });
164
347
  pi.on("session_switch", async (_event, ctx) => {
348
+ const basePath = contextBasePath(ctx);
165
349
  initSessionNotifications(ctx);
166
- resetWriteGateState(process.cwd());
350
+ resetWriteGateState(basePath);
167
351
  resetToolCallLoopGuard();
352
+ clearDeferredApprovalGate();
168
353
  await resetAskUserQuestionsTurnCache();
169
- clearDiscussionFlowState(process.cwd());
354
+ clearDiscussionFlowState(basePath);
170
355
  await syncServiceTierStatus(ctx);
171
356
  await applyDisabledModelProviderPolicy(ctx);
172
357
  await applyCompactionThresholdOverride(ctx);
@@ -175,9 +360,9 @@ export function registerHooks(pi, ecosystemHandlers) {
175
360
  // post-chdir rewrites the file mid-run (non-idempotent due to cwd-relative
176
361
  // CLI path resolution), dirtying the tree and breaking the milestone merge.
177
362
  const { isInAutoWorktree } = await import("../auto-worktree.js");
178
- if (!isInAutoWorktree(process.cwd())) {
363
+ if (!isInAutoWorktree(basePath)) {
179
364
  const { prepareWorkflowMcpForProject } = await import("../workflow-mcp-auto-prep.js");
180
- prepareWorkflowMcpForProject(ctx, process.cwd());
365
+ prepareWorkflowMcpForProject(ctx, basePath);
181
366
  }
182
367
  await loadToolApiKeysForSession();
183
368
  if (!isAutoActive()) {
@@ -189,10 +374,11 @@ export function registerHooks(pi, ecosystemHandlers) {
189
374
  }
190
375
  });
191
376
  pi.on("before_agent_start", async (event, ctx) => {
377
+ applyMinimalGsdToolSurface(pi);
192
378
  // Wait for ecosystem loader to finish (no-op after first turn).
193
379
  const { getEcosystemReadyPromise } = await import("../ecosystem/loader.js");
194
380
  await getEcosystemReadyPromise();
195
- const beforeAgentBasePath = process.cwd();
381
+ const beforeAgentBasePath = contextBasePath(ctx);
196
382
  const pendingApprovalGate = getPendingGate(beforeAgentBasePath);
197
383
  if (pendingApprovalGate && isExplicitApprovalResponse(event.prompt, pendingApprovalGate)) {
198
384
  markApprovalGateVerified(pendingApprovalGate, beforeAgentBasePath);
@@ -201,13 +387,14 @@ export function registerHooks(pi, ecosystemHandlers) {
201
387
  markDepthVerified(milestoneId, beforeAgentBasePath);
202
388
  clearPendingGate(beforeAgentBasePath);
203
389
  }
390
+ clearDeferredApprovalGate(beforeAgentBasePath);
204
391
  // GSD's own context injection (existing behavior — unchanged).
205
392
  const { buildBeforeAgentStartResult } = await import("./system-context.js");
206
393
  const gsdResult = await buildBeforeAgentStartResult(event, ctx);
207
394
  // Refresh the snapshot used by ecosystem getPhase()/getActiveUnit().
208
395
  // deriveState has its own ~100ms cache so this is cheap on repeat calls.
209
396
  try {
210
- const state = await deriveGsdState(process.cwd());
397
+ const state = await deriveGsdState(beforeAgentBasePath);
211
398
  updateSnapshot(state);
212
399
  }
213
400
  catch {
@@ -244,7 +431,12 @@ export function registerHooks(pi, ecosystemHandlers) {
244
431
  resetToolCallLoopGuard();
245
432
  await resetAskUserQuestionsTurnCache();
246
433
  const { handleAgentEnd } = await import("./agent-end-recovery.js");
247
- await handleAgentEnd(pi, event, ctx);
434
+ try {
435
+ await handleAgentEnd(pi, event, ctx);
436
+ }
437
+ finally {
438
+ activateDeferredApprovalGate(contextBasePath(ctx));
439
+ }
248
440
  });
249
441
  // Squash-merge quick-task branch back to the original branch after the
250
442
  // agent turn completes (#2668). cleanupQuickBranch is a no-op when no
@@ -258,8 +450,8 @@ export function registerHooks(pi, ecosystemHandlers) {
258
450
  // Best-effort: don't break the turn lifecycle if cleanup fails.
259
451
  }
260
452
  });
261
- pi.on("session_before_compact", async () => {
262
- const basePath = process.cwd();
453
+ pi.on("session_before_compact", async (_event, ctx) => {
454
+ const basePath = contextBasePath(ctx);
263
455
  // Context Mode is default-on. Write the resumable snapshot before any
264
456
  // active-auto cancel return so auto sessions still leave re-entry context.
265
457
  await writeContextModeCompactionSnapshot(basePath);
@@ -323,7 +515,7 @@ export function registerHooks(pi, ecosystemHandlers) {
323
515
  if (!unitType) {
324
516
  try {
325
517
  const { getPendingDeepProjectSetupUnitForContext } = await import("../guided-flow.js");
326
- const pending = getPendingDeepProjectSetupUnitForContext(ctx, process.cwd());
518
+ const pending = getPendingDeepProjectSetupUnitForContext(ctx, contextBasePath(ctx));
327
519
  unitType = pending?.unitType;
328
520
  unitId = pending?.unitId;
329
521
  }
@@ -332,7 +524,7 @@ export function registerHooks(pi, ecosystemHandlers) {
332
524
  }
333
525
  }
334
526
  if (!unitType) {
335
- const milestoneId = await getDiscussionMilestoneIdFor(process.cwd());
527
+ const milestoneId = await getDiscussionMilestoneIdFor(contextBasePath(ctx));
336
528
  if (milestoneId) {
337
529
  unitType = "discuss-milestone";
338
530
  unitId = milestoneId;
@@ -342,11 +534,12 @@ export function registerHooks(pi, ecosystemHandlers) {
342
534
  return;
343
535
  const gateId = approvalGateIdForUnit(unitType, unitId);
344
536
  if (gateId)
345
- setPendingGate(gateId, process.cwd());
537
+ deferApprovalGate(gateId, contextBasePath(ctx));
346
538
  approvalQuestionAbortInFlight = true;
347
539
  ctx.ui.notify(`${unitType}${unitId ? ` ${unitId}` : ""} is waiting for your approval - pausing before more tool calls run.`, "info");
348
- // The pending gate set above blocks subsequent non-read-only tool calls
349
- // via the tool_call hook below, so we do not abort the in-flight stream.
540
+ // The durable pending gate is activated at agent_end so same-turn
541
+ // CONTEXT-DRAFT persistence can finish after the text boundary streams.
542
+ // The tool_call hook below still blocks non-draft tools in this turn.
350
543
  // Aborting mid-stream eats the model's question text on external CLI
351
544
  // providers (Claude Code SDK) because lastTextContent isn't populated
352
545
  // from in-flight builder state — the user only ever sees "Claude Code
@@ -356,7 +549,7 @@ export function registerHooks(pi, ecosystemHandlers) {
356
549
  const { isParallelActive, shutdownParallel } = await import("../parallel-orchestrator.js");
357
550
  if (isParallelActive()) {
358
551
  try {
359
- await shutdownParallel(process.cwd());
552
+ await shutdownParallel(contextBasePath(ctx));
360
553
  }
361
554
  catch {
362
555
  // best-effort
@@ -369,14 +562,17 @@ export function registerHooks(pi, ecosystemHandlers) {
369
562
  saveActivityLog(ctx, dash.basePath, dash.currentUnit.type, dash.currentUnit.id);
370
563
  }
371
564
  });
372
- pi.on("tool_call", async (event) => {
373
- const discussionBasePath = process.cwd();
565
+ pi.on("tool_call", async (event, ctx) => {
566
+ const discussionBasePath = contextBasePath(ctx);
374
567
  const toolName = canonicalToolName(event.toolName);
375
568
  // ── Loop guard: block repeated identical tool calls ──
376
569
  const loopCheck = checkToolCallLoop(toolName, event.input);
377
570
  if (loopCheck.block) {
378
571
  return { block: true, reason: loopCheck.reason };
379
572
  }
573
+ const deferredGateGuard = shouldBlockDeferredApprovalTool(toolName, event.input, discussionBasePath);
574
+ if (deferredGateGuard.block)
575
+ return deferredGateGuard;
380
576
  // ── Discussion gate enforcement: track pending gate questions ─────────
381
577
  // Only gate-shaped ask_user_questions calls should block execution.
382
578
  // The gate stays pending until the user selects the approval option.
@@ -515,7 +711,7 @@ export function registerHooks(pi, ecosystemHandlers) {
515
711
  }
516
712
  }
517
713
  });
518
- pi.on("tool_result", async (event) => {
714
+ pi.on("tool_result", async (event, ctx) => {
519
715
  if (isAutoActive() && typeof event.toolCallId === "string") {
520
716
  markToolEnd(event.toolCallId);
521
717
  }
@@ -535,7 +731,7 @@ export function registerHooks(pi, ecosystemHandlers) {
535
731
  const toolName = canonicalToolName(event.toolName);
536
732
  if (toolName !== "ask_user_questions")
537
733
  return;
538
- const basePath = process.cwd();
734
+ const basePath = contextBasePath(ctx);
539
735
  const milestoneId = await getDiscussionMilestoneIdFor(basePath);
540
736
  const queueActive = isQueuePhaseActive(basePath);
541
737
  const details = event.details;
@@ -737,8 +933,22 @@ export function registerHooks(pi, ecosystemHandlers) {
737
933
  // Tool set adaptation hook (ADR-005 Phase 4)
738
934
  // Extensions can override tool set after model selection by returning { toolNames: [...] }
739
935
  // Return undefined to let the built-in provider compatibility filtering proceed.
740
- pi.on("adjust_tool_set", async (_event) => {
741
- // Default: no override — let provider capability filtering handle tool set
936
+ pi.on("adjust_tool_set", async (event) => {
937
+ if (isFullGsdToolSurfaceRequested())
938
+ return undefined;
939
+ const removed = new Set(event.filteredTools);
940
+ const providerCompatible = event.activeToolNames.filter((name) => !removed.has(name));
941
+ const requestScoped = buildRequestScopedGsdToolSet(providerCompatible, event.requestCustomMessages);
942
+ if (requestScoped) {
943
+ return { toolNames: requestScoped };
944
+ }
945
+ const dash = getAutoRuntimeSnapshot();
946
+ if (dash.active && dash.currentUnit) {
947
+ return { toolNames: buildMinimalAutoGsdToolSet(providerCompatible, dash.currentUnit.type) };
948
+ }
949
+ if (isGeneralGsdToolScopingRequested()) {
950
+ return { toolNames: buildMinimalGsdToolSet(providerCompatible) };
951
+ }
742
952
  return undefined;
743
953
  });
744
954
  }
@@ -1,3 +1,5 @@
1
+ // Project/App: GSD-2
2
+ // File Purpose: System prompt and hidden context bootstrap for GSD sessions.
1
3
  import { existsSync, readFileSync, unlinkSync } from "node:fs";
2
4
  import { join } from "node:path";
3
5
  import { logWarning } from "../workflow-logger.js";
@@ -17,6 +19,11 @@ import { formatOverridesSection, formatShortcut, loadActiveOverrides, loadFile,
17
19
  import { toPosixPath } from "../../shared/mod.js";
18
20
  import { autoEnableCmuxPreferences } from "../commands-cmux.js";
19
21
  import { gsdHome } from "../gsd-home.js";
22
+ const DEFAULT_CONTEXT_MESSAGE_MAX_CHARS = 4_000;
23
+ const DEFAULT_KNOWLEDGE_MAX_CHARS = 12_000;
24
+ const DEFAULT_CODEBASE_MAX_CHARS = 8_000;
25
+ const MIN_CONTEXT_MESSAGE_MAX_CHARS = 1_000;
26
+ const MIN_KNOWLEDGE_MAX_CHARS = 1_000;
20
27
  /**
21
28
  * Bundled skill triggers — resolved dynamically at runtime instead of
22
29
  * hardcoding absolute paths in the system prompt template. Only skills
@@ -135,7 +142,6 @@ export async function buildBeforeAgentStartResult(event, ctx) {
135
142
  catch (e) {
136
143
  logWarning("bootstrap", `decisions backfill failed: ${e.message}`);
137
144
  }
138
- const memoryBlock = await loadMemoryBlock(event.prompt ?? "");
139
145
  let newSkillsBlock = "";
140
146
  if (hasSkillSnapshot()) {
141
147
  const newSkills = detectNewSkills();
@@ -165,11 +171,10 @@ export async function buildBeforeAgentStartResult(event, ctx) {
165
171
  if (rawContent) {
166
172
  // Cap injection size to ~2 000 tokens to avoid bloating every request.
167
173
  // Full map is always available at .gsd/CODEBASE.md.
168
- const MAX_CODEBASE_CHARS = 8_000;
169
174
  const generatedMatch = rawContent.match(/Generated: (\S+)/);
170
175
  const generatedAt = generatedMatch?.[1] ?? "unknown";
171
- const content = rawContent.length > MAX_CODEBASE_CHARS
172
- ? rawContent.slice(0, MAX_CODEBASE_CHARS) + "\n\n*(truncated — see .gsd/CODEBASE.md for full map)*"
176
+ const content = rawContent.length > DEFAULT_CODEBASE_MAX_CHARS
177
+ ? rawContent.slice(0, DEFAULT_CODEBASE_MAX_CHARS) + "\n\n*(truncated — see .gsd/CODEBASE.md for full map)*"
173
178
  : rawContent;
174
179
  codebaseBlock = `\n\n[PROJECT CODEBASE — File structure and descriptions (generated ${generatedAt}, auto-refreshed when GSD detects tracked file changes; use /gsd codebase stats for status)]\n\n${content}`;
175
180
  }
@@ -180,6 +185,9 @@ export async function buildBeforeAgentStartResult(event, ctx) {
180
185
  }
181
186
  warnDeprecatedAgentInstructions();
182
187
  const injection = await buildGuidedExecuteContextInjection(event.prompt, process.cwd());
188
+ const memoryBlock = await loadMemoryBlock(event.prompt ?? "", {
189
+ includePromptRelevant: !(injection && isLowEntropyResumePrompt(event.prompt ?? "")),
190
+ });
183
191
  // Re-inject forensics context on follow-up turns (#2941)
184
192
  const forensicsInjection = !injection ? buildForensicsContextInjection(process.cwd(), event.prompt) : null;
185
193
  const worktreeBlock = buildWorktreeContextBlock();
@@ -188,12 +196,9 @@ export async function buildBeforeAgentStartResult(event, ctx) {
188
196
  ? `\n\n## Subagent Model\n\nWhen spawning subagents via the \`subagent\` tool, always pass \`model: "${subagentModelConfig.primary}"\` in the tool call parameters. Never omit this — always specify it explicitly.`
189
197
  : "";
190
198
  // memoryBlock is FTS-queried against the user prompt and changes per call.
191
- // Removing it from `fullSystem` keeps the system-prompt cache breakpoint
192
- // stable across calls the only scoped goal of this fix. The pi-ai
193
- // Anthropic adapter additionally cache-marks the last user turn, so the
194
- // memoryBlock injected via the context message may itself be cached up to
195
- // that boundary; that's orthogonal and unchanged from prior behavior. The
196
- // load-bearing win here is preserving the system+tools cache hit. (#5019)
199
+ // Keeping it out of `fullSystem` preserves provider prompt-cache stability
200
+ // for the static system/tool prefix. The dynamic memory block rides the
201
+ // volatile context message instead. (#5019)
197
202
  const fullSystem = `${event.systemPrompt}\n\n[SYSTEM CONTEXT — GSD]\n\n${systemContent}${preferenceBlock}${knowledgeBlock}${codebaseBlock}${newSkillsBlock}${worktreeBlock}${subagentModelBlock}`;
198
203
  stopContextTimer({
199
204
  systemPromptSize: fullSystem.length,
@@ -217,20 +222,49 @@ export async function buildBeforeAgentStartResult(event, ctx) {
217
222
  * filesystem and DB dependencies to exercise this routing logic in-place.
218
223
  */
219
224
  export function buildContextMessage(opts) {
220
- const memoryContent = opts.memoryBlock.trim();
225
+ const contextCharLimit = getContextMessageCharLimit();
226
+ const memoryContent = markMemoryContextSupplied(opts.memoryBlock.trim());
221
227
  if (opts.injection) {
222
- const content = memoryContent ? `${memoryContent}\n\n${opts.injection}` : opts.injection;
228
+ const content = limitContextMessageContent(memoryContent ? `${memoryContent}\n\n${opts.injection}` : opts.injection, contextCharLimit);
223
229
  return { customType: "gsd-guided-context", content, display: false };
224
230
  }
225
231
  if (opts.forensicsInjection) {
226
- const content = memoryContent ? `${memoryContent}\n\n${opts.forensicsInjection}` : opts.forensicsInjection;
232
+ const content = limitContextMessageContent(memoryContent ? `${memoryContent}\n\n${opts.forensicsInjection}` : opts.forensicsInjection, contextCharLimit);
227
233
  return { customType: "gsd-forensics", content, display: false };
228
234
  }
229
235
  if (memoryContent) {
230
- return { customType: "gsd-memory", content: memoryContent, display: false };
236
+ return {
237
+ customType: "gsd-memory",
238
+ content: limitContextMessageContent(memoryContent, contextCharLimit),
239
+ display: false,
240
+ };
231
241
  }
232
242
  return null;
233
243
  }
244
+ function getContextMessageCharLimit() {
245
+ const raw = process.env.PI_GSD_CONTEXT_MAX_CHARS;
246
+ if (!raw)
247
+ return DEFAULT_CONTEXT_MESSAGE_MAX_CHARS;
248
+ if (raw === "0")
249
+ return null;
250
+ const parsed = Number(raw);
251
+ if (!Number.isFinite(parsed) || parsed < MIN_CONTEXT_MESSAGE_MAX_CHARS) {
252
+ return DEFAULT_CONTEXT_MESSAGE_MAX_CHARS;
253
+ }
254
+ return Math.floor(parsed);
255
+ }
256
+ function limitContextMessageContent(content, limit) {
257
+ if (!limit || content.length <= limit)
258
+ return content;
259
+ const suffix = "\n\n[GSD Context Truncated]\nFull context is available from the referenced .gsd files and tools; read on demand only if this excerpt lacks required evidence.";
260
+ const headBudget = Math.max(0, limit - suffix.length);
261
+ return `${content.slice(0, headBudget).trimEnd()}${suffix}`;
262
+ }
263
+ function markMemoryContextSupplied(memoryContent) {
264
+ if (!memoryContent)
265
+ return "";
266
+ return `[GSD Context Metadata]\n- Memory supplied: yes\n\n${memoryContent}`;
267
+ }
234
268
  /**
235
269
  * ADR-013 step 4 — auto-injection parity for the memories table.
236
270
  *
@@ -249,7 +283,7 @@ export function buildContextMessage(opts) {
249
283
  * with a token-budget cap. Failures degrade gracefully — the function never
250
284
  * throws and returns "" so the system prompt construction continues.
251
285
  */
252
- export async function loadMemoryBlock(userPrompt) {
286
+ export async function loadMemoryBlock(userPrompt, opts = {}) {
253
287
  try {
254
288
  const { formatMemoriesForPrompt, getActiveMemoriesRanked, queryMemoriesRanked } = await import("../memory-store.js");
255
289
  // Categories that belong in every turn. Pre-ADR-013 this was just
@@ -268,7 +302,7 @@ export async function loadMemoryBlock(userPrompt) {
268
302
  const criticalIds = new Set(critical.map((m) => m.id));
269
303
  let relevant = [];
270
304
  const trimmed = userPrompt.trim();
271
- if (trimmed) {
305
+ if (trimmed && opts.includePromptRelevant !== false) {
272
306
  const hits = queryMemoriesRanked({ query: trimmed, k: QUERY_K });
273
307
  relevant = hits.map((h) => h.memory).filter((m) => !criticalIds.has(m.id));
274
308
  }
@@ -319,15 +353,37 @@ export function loadKnowledgeBlock(gsdHomeDir, cwd) {
319
353
  return { block: "", globalSizeKb: 0 };
320
354
  }
321
355
  const parts = [];
322
- if (globalKnowledge)
323
- parts.push(`## Global Knowledge\n\n${globalKnowledge}`);
324
- if (projectKnowledge)
325
- parts.push(`## Project Knowledge\n\n${projectKnowledge}`);
356
+ if (globalKnowledge) {
357
+ parts.push(`## Global Knowledge\nSource: \`${globalKnowledgePath}\`\n\n${globalKnowledge}`);
358
+ }
359
+ if (projectKnowledge) {
360
+ parts.push(`## Project Knowledge\nSource: \`${knowledgePath}\`\n\n${projectKnowledge}`);
361
+ }
362
+ const body = limitKnowledgeBlock(parts.join("\n\n"), getKnowledgeCharLimit());
326
363
  return {
327
- block: `\n\n[KNOWLEDGE — Rules, patterns, and lessons learned]\n\n${parts.join("\n\n")}`,
364
+ block: `\n\n[KNOWLEDGE — Rules, patterns, and lessons learned]\n\n${body}`,
328
365
  globalSizeKb,
329
366
  };
330
367
  }
368
+ function getKnowledgeCharLimit() {
369
+ const raw = process.env.PI_GSD_KNOWLEDGE_MAX_CHARS;
370
+ if (!raw)
371
+ return DEFAULT_KNOWLEDGE_MAX_CHARS;
372
+ if (raw === "0")
373
+ return null;
374
+ const parsed = Number(raw);
375
+ if (!Number.isFinite(parsed) || parsed < MIN_KNOWLEDGE_MAX_CHARS) {
376
+ return DEFAULT_KNOWLEDGE_MAX_CHARS;
377
+ }
378
+ return Math.floor(parsed);
379
+ }
380
+ function limitKnowledgeBlock(content, limit) {
381
+ if (!limit || content.length <= limit)
382
+ return content;
383
+ const suffix = "\n\n[Knowledge Truncated]\nFull KNOWLEDGE.md content remains available at the source path(s) above; read on demand only if this excerpt lacks a required rule.";
384
+ const headBudget = Math.max(0, limit - suffix.length);
385
+ return `${content.slice(0, headBudget).trimEnd()}${suffix}`;
386
+ }
331
387
  function buildWorktreeContextBlock() {
332
388
  const worktreeName = getActiveWorktreeName();
333
389
  const worktreeMainCwd = getWorktreeOriginalCwd();
@@ -376,6 +432,10 @@ function buildWorktreeContextBlock() {
376
432
  * Tested against the trimmed, lowercased prompt with trailing punctuation stripped.
377
433
  */
378
434
  const RESUME_INTENT_PATTERNS = /^(continue|resume|ok|go|go ahead|proceed|keep going|carry on|next|yes|yeah|yep|sure|do it|let's go|pick up where you left off)$/;
435
+ export function isLowEntropyResumePrompt(prompt) {
436
+ const trimmed = prompt.trim().toLowerCase().replace(/[.!?,]+$/g, "");
437
+ return RESUME_INTENT_PATTERNS.test(trimmed);
438
+ }
379
439
  async function buildGuidedExecuteContextInjection(prompt, basePath) {
380
440
  const ensureStateDbOpen = async () => {
381
441
  const { ensureDbOpen } = await import("./dynamic-tools.js");
@@ -402,8 +462,7 @@ async function buildGuidedExecuteContextInjection(prompt, basePath) {
402
462
  // control/help/diagnostic prompts with unrelated execution context.
403
463
  // Phase-gated: only fire during "executing" to avoid misrouting during
404
464
  // replanning, gate evaluation, or other non-execution phases.
405
- const trimmed = prompt.trim().toLowerCase().replace(/[.!?,]+$/g, "");
406
- if (RESUME_INTENT_PATTERNS.test(trimmed)) {
465
+ if (isLowEntropyResumePrompt(prompt)) {
407
466
  await ensureStateDbOpen();
408
467
  const state = await deriveState(basePath);
409
468
  if (state.phase === "executing" && state.activeTask && state.activeMilestone && state.activeSlice) {