@opengsd/gsd-pi 1.1.1-dev.9f86580 → 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 (261) 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/index.js +39 -22
  4. package/dist/resources/extensions/browser-tools/state.js +12 -0
  5. package/dist/resources/extensions/browser-tools/tools/session.js +3 -2
  6. package/dist/resources/extensions/browser-tools/utils.js +3 -3
  7. package/dist/resources/extensions/gsd/auto/loop.js +4 -2
  8. package/dist/resources/extensions/gsd/auto/phases.js +43 -10
  9. package/dist/resources/extensions/gsd/auto/session.js +20 -1
  10. package/dist/resources/extensions/gsd/auto/workflow-kernel.js +1 -0
  11. package/dist/resources/extensions/gsd/auto-dispatch.js +72 -12
  12. package/dist/resources/extensions/gsd/auto-model-selection.js +128 -9
  13. package/dist/resources/extensions/gsd/auto-post-unit.js +19 -2
  14. package/dist/resources/extensions/gsd/auto-prompts.js +24 -19
  15. package/dist/resources/extensions/gsd/auto-recovery.js +4 -2
  16. package/dist/resources/extensions/gsd/auto-runtime-state.js +3 -0
  17. package/dist/resources/extensions/gsd/auto-start.js +1 -1
  18. package/dist/resources/extensions/gsd/auto.js +14 -11
  19. package/dist/resources/extensions/gsd/bootstrap/db-tools.js +3 -3
  20. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +172 -65
  21. package/dist/resources/extensions/gsd/closeout-wizard.js +32 -9
  22. package/dist/resources/extensions/gsd/commands/handlers/ops.js +2 -9
  23. package/dist/resources/extensions/gsd/commands-maintenance.js +93 -15
  24. package/dist/resources/extensions/gsd/commands-prefs-wizard.js +2 -2
  25. package/dist/resources/extensions/gsd/db-writer.js +35 -0
  26. package/dist/resources/extensions/gsd/docs/preferences-reference.md +50 -1
  27. package/dist/resources/extensions/gsd/gsd-db.js +480 -172
  28. package/dist/resources/extensions/gsd/markdown-renderer.js +37 -53
  29. package/dist/resources/extensions/gsd/md-importer.js +38 -3
  30. package/dist/resources/extensions/gsd/migration-auto-check.js +126 -31
  31. package/dist/resources/extensions/gsd/parsers-legacy.js +23 -0
  32. package/dist/resources/extensions/gsd/planning-path-scope.js +22 -4
  33. package/dist/resources/extensions/gsd/pre-execution-checks.js +10 -2
  34. package/dist/resources/extensions/gsd/preferences-models.js +110 -43
  35. package/dist/resources/extensions/gsd/preferences-types.js +13 -0
  36. package/dist/resources/extensions/gsd/preferences-validation.js +68 -3
  37. package/dist/resources/extensions/gsd/preferences.js +4 -1
  38. package/dist/resources/extensions/gsd/prompts/gate-evaluate.md +1 -1
  39. package/dist/resources/extensions/gsd/prompts/plan-slice.md +1 -1
  40. package/dist/resources/extensions/gsd/prompts/refine-slice.md +1 -1
  41. package/dist/resources/extensions/gsd/roadmap-slices.js +5 -1
  42. package/dist/resources/extensions/gsd/safety/content-validator.js +6 -4
  43. package/dist/resources/extensions/gsd/source-observations.js +306 -0
  44. package/dist/resources/extensions/gsd/state-reconciliation/drift/completion.js +15 -8
  45. package/dist/resources/extensions/gsd/state-reconciliation/drift/stale-render.js +33 -5
  46. package/dist/resources/extensions/gsd/state-reconciliation/drift/stale-worker.js +34 -13
  47. package/dist/resources/extensions/gsd/state-reconciliation/index.js +39 -14
  48. package/dist/resources/extensions/gsd/state-reconciliation/spawn-gate.js +4 -4
  49. package/dist/resources/extensions/gsd/state.js +7 -3
  50. package/dist/resources/extensions/gsd/tool-contract.js +14 -0
  51. package/dist/resources/extensions/gsd/tool-presentation-plan.js +1 -9
  52. package/dist/resources/extensions/gsd/tools/complete-slice.js +7 -6
  53. package/dist/resources/extensions/gsd/tools/plan-slice.js +42 -11
  54. package/dist/resources/extensions/gsd/tools/plan-task.js +7 -1
  55. package/dist/resources/extensions/gsd/tools/workflow-tool-executors.js +57 -429
  56. package/dist/resources/extensions/gsd/uat-policy.js +130 -0
  57. package/dist/resources/extensions/gsd/uat-run.js +414 -0
  58. package/dist/resources/extensions/gsd/unit-context-manifest.js +3 -4
  59. package/dist/resources/extensions/gsd/verdict-parser.js +3 -8
  60. package/dist/resources/extensions/gsd/workflow-manifest.js +132 -5
  61. package/dist/resources/extensions/gsd/workflow-projections.js +8 -0
  62. package/dist/resources/extensions/gsd/worktree-state-projection.js +18 -17
  63. package/dist/resources/extensions/subagent/agents.js +1 -0
  64. package/dist/resources/extensions/subagent/index.js +27 -12
  65. package/dist/resources/extensions/subagent/launch.js +7 -2
  66. package/dist/web/standalone/.next/BUILD_ID +1 -1
  67. package/dist/web/standalone/.next/app-path-routes-manifest.json +8 -8
  68. package/dist/web/standalone/.next/build-manifest.json +2 -2
  69. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  70. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  71. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  72. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  73. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  74. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  75. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  76. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  77. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  78. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  79. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  80. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  81. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  82. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  83. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  84. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  85. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  86. package/dist/web/standalone/.next/server/app/index.html +1 -1
  87. package/dist/web/standalone/.next/server/app/index.rsc +1 -1
  88. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  89. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
  90. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  91. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  92. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  93. package/dist/web/standalone/.next/server/app-paths-manifest.json +8 -8
  94. package/dist/web/standalone/.next/server/chunks/8357.js +1 -1
  95. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  96. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  97. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  98. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  99. package/dist/web/standalone/node_modules/@gsd/native/dist/native.js +22 -0
  100. package/dist/web/standalone/node_modules/node-pty/build/Makefile +1 -1
  101. package/package.json +4 -4
  102. package/packages/cloud-mcp-gateway/package.json +2 -2
  103. package/packages/contracts/package.json +1 -1
  104. package/packages/daemon/package.json +4 -4
  105. package/packages/gsd-agent-core/package.json +5 -5
  106. package/packages/gsd-agent-modes/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
  107. package/packages/gsd-agent-modes/dist/modes/interactive/components/assistant-message.js +21 -23
  108. package/packages/gsd-agent-modes/dist/modes/interactive/components/assistant-message.js.map +1 -1
  109. package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.d.ts +3 -0
  110. package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  111. package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.js +25 -0
  112. package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.js.map +1 -1
  113. package/packages/gsd-agent-modes/dist/modes/interactive/components/transcript-design.d.ts +1 -0
  114. package/packages/gsd-agent-modes/dist/modes/interactive/components/transcript-design.d.ts.map +1 -1
  115. package/packages/gsd-agent-modes/dist/modes/interactive/components/transcript-design.js +66 -12
  116. package/packages/gsd-agent-modes/dist/modes/interactive/components/transcript-design.js.map +1 -1
  117. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
  118. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.js +18 -11
  119. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
  120. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-chat-render.d.ts.map +1 -1
  121. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-chat-render.js +16 -0
  122. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-chat-render.js.map +1 -1
  123. package/packages/gsd-agent-modes/package.json +7 -7
  124. package/packages/mcp-server/dist/workflow-tools.js +1 -1
  125. package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
  126. package/packages/mcp-server/package.json +3 -3
  127. package/packages/native/dist/native.js +22 -0
  128. package/packages/native/package.json +1 -1
  129. package/packages/pi-agent-core/package.json +1 -1
  130. package/packages/pi-ai/dist/image-models.generated.d.ts +30 -0
  131. package/packages/pi-ai/dist/image-models.generated.d.ts.map +1 -1
  132. package/packages/pi-ai/dist/image-models.generated.js +30 -0
  133. package/packages/pi-ai/dist/image-models.generated.js.map +1 -1
  134. package/packages/pi-ai/dist/models.generated.d.ts +23 -17
  135. package/packages/pi-ai/dist/models.generated.d.ts.map +1 -1
  136. package/packages/pi-ai/dist/models.generated.js +25 -24
  137. package/packages/pi-ai/dist/models.generated.js.map +1 -1
  138. package/packages/pi-ai/package.json +1 -1
  139. package/packages/pi-coding-agent/dist/core/settings-manager.js +1 -1
  140. package/packages/pi-coding-agent/dist/core/settings-manager.js.map +1 -1
  141. package/packages/pi-coding-agent/dist/theme/themes.js +1 -1
  142. package/packages/pi-coding-agent/dist/theme/themes.js.map +1 -1
  143. package/packages/pi-coding-agent/package.json +7 -7
  144. package/packages/pi-tui/dist/utils.d.ts +11 -0
  145. package/packages/pi-tui/dist/utils.d.ts.map +1 -1
  146. package/packages/pi-tui/dist/utils.js +119 -6
  147. package/packages/pi-tui/dist/utils.js.map +1 -1
  148. package/packages/pi-tui/package.json +2 -1
  149. package/packages/rpc-client/package.json +2 -2
  150. package/pkg/dist/theme/themes.js +1 -1
  151. package/pkg/dist/theme/themes.js.map +1 -1
  152. package/pkg/package.json +1 -1
  153. package/src/resources/extensions/browser-tools/index.ts +39 -22
  154. package/src/resources/extensions/browser-tools/state.ts +13 -0
  155. package/src/resources/extensions/browser-tools/tests/browser-tools-unit.test.cjs +57 -0
  156. package/src/resources/extensions/browser-tools/tools/session.ts +4 -2
  157. package/src/resources/extensions/browser-tools/utils.ts +3 -3
  158. package/src/resources/extensions/gsd/auto/loop-deps.ts +1 -0
  159. package/src/resources/extensions/gsd/auto/loop.ts +4 -2
  160. package/src/resources/extensions/gsd/auto/phases.ts +42 -10
  161. package/src/resources/extensions/gsd/auto/session.ts +22 -1
  162. package/src/resources/extensions/gsd/auto/workflow-kernel.ts +1 -0
  163. package/src/resources/extensions/gsd/auto-dispatch.ts +85 -12
  164. package/src/resources/extensions/gsd/auto-model-selection.ts +164 -12
  165. package/src/resources/extensions/gsd/auto-post-unit.ts +20 -2
  166. package/src/resources/extensions/gsd/auto-prompts.ts +23 -20
  167. package/src/resources/extensions/gsd/auto-recovery.ts +22 -3
  168. package/src/resources/extensions/gsd/auto-runtime-state.ts +5 -0
  169. package/src/resources/extensions/gsd/auto-start.ts +1 -1
  170. package/src/resources/extensions/gsd/auto.ts +13 -10
  171. package/src/resources/extensions/gsd/bootstrap/db-tools.ts +3 -3
  172. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +225 -72
  173. package/src/resources/extensions/gsd/closeout-wizard.ts +47 -13
  174. package/src/resources/extensions/gsd/commands/handlers/ops.ts +2 -17
  175. package/src/resources/extensions/gsd/commands-maintenance.ts +124 -13
  176. package/src/resources/extensions/gsd/commands-prefs-wizard.ts +2 -2
  177. package/src/resources/extensions/gsd/db-writer.ts +38 -0
  178. package/src/resources/extensions/gsd/docs/preferences-reference.md +50 -1
  179. package/src/resources/extensions/gsd/gsd-db.ts +564 -186
  180. package/src/resources/extensions/gsd/markdown-renderer.ts +44 -66
  181. package/src/resources/extensions/gsd/md-importer.ts +49 -2
  182. package/src/resources/extensions/gsd/migration-auto-check.ts +154 -34
  183. package/src/resources/extensions/gsd/parsers-legacy.ts +20 -0
  184. package/src/resources/extensions/gsd/planning-path-scope.ts +22 -4
  185. package/src/resources/extensions/gsd/pre-execution-checks.ts +9 -2
  186. package/src/resources/extensions/gsd/preferences-models.ts +112 -43
  187. package/src/resources/extensions/gsd/preferences-types.ts +39 -0
  188. package/src/resources/extensions/gsd/preferences-validation.ts +76 -2
  189. package/src/resources/extensions/gsd/preferences.ts +5 -0
  190. package/src/resources/extensions/gsd/prompts/gate-evaluate.md +1 -1
  191. package/src/resources/extensions/gsd/prompts/plan-slice.md +1 -1
  192. package/src/resources/extensions/gsd/prompts/refine-slice.md +1 -1
  193. package/src/resources/extensions/gsd/roadmap-slices.ts +6 -1
  194. package/src/resources/extensions/gsd/safety/content-validator.ts +8 -5
  195. package/src/resources/extensions/gsd/source-observations.ts +402 -0
  196. package/src/resources/extensions/gsd/state-reconciliation/drift/completion.ts +20 -8
  197. package/src/resources/extensions/gsd/state-reconciliation/drift/stale-render.ts +44 -5
  198. package/src/resources/extensions/gsd/state-reconciliation/drift/stale-worker.ts +39 -11
  199. package/src/resources/extensions/gsd/state-reconciliation/index.ts +45 -15
  200. package/src/resources/extensions/gsd/state-reconciliation/spawn-gate.ts +4 -4
  201. package/src/resources/extensions/gsd/state.ts +7 -4
  202. package/src/resources/extensions/gsd/tests/auto-loop.test.ts +15 -0
  203. package/src/resources/extensions/gsd/tests/auto-model-selection.test.ts +299 -1
  204. package/src/resources/extensions/gsd/tests/auto-paused-ui-cleanup.test.ts +32 -0
  205. package/src/resources/extensions/gsd/tests/auto-phases-lifecycle.test.ts +75 -3
  206. package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +22 -1
  207. package/src/resources/extensions/gsd/tests/before-provider-context-management.test.ts +145 -0
  208. package/src/resources/extensions/gsd/tests/closeout-wizard.test.ts +44 -0
  209. package/src/resources/extensions/gsd/tests/commands-dispatcher-unmerged-milestone.test.ts +26 -1
  210. package/src/resources/extensions/gsd/tests/content-validator.test.ts +74 -0
  211. package/src/resources/extensions/gsd/tests/custom-engine-loop-integration.test.ts +16 -2
  212. package/src/resources/extensions/gsd/tests/doctor-scope-db-unavailable.test.ts +1 -11
  213. package/src/resources/extensions/gsd/tests/gate-dispatch.test.ts +64 -0
  214. package/src/resources/extensions/gsd/tests/gate-storage.test.ts +15 -0
  215. package/src/resources/extensions/gsd/tests/gsd-recover.test.ts +62 -1
  216. package/src/resources/extensions/gsd/tests/journal-integration.test.ts +15 -0
  217. package/src/resources/extensions/gsd/tests/markdown-renderer.test.ts +42 -0
  218. package/src/resources/extensions/gsd/tests/migration-auto-check.test.ts +99 -0
  219. package/src/resources/extensions/gsd/tests/plan-slice.test.ts +99 -2
  220. package/src/resources/extensions/gsd/tests/plan-task.test.ts +19 -0
  221. package/src/resources/extensions/gsd/tests/preferences.test.ts +14 -0
  222. package/src/resources/extensions/gsd/tests/prefs-wizard-coverage.test.ts +1 -0
  223. package/src/resources/extensions/gsd/tests/prompt-contracts.test.ts +9 -0
  224. package/src/resources/extensions/gsd/tests/register-hooks-depth-verification.test.ts +101 -1
  225. package/src/resources/extensions/gsd/tests/repository-registry.test.ts +2 -2
  226. package/src/resources/extensions/gsd/tests/runtime-invariant-modules.test.ts +8 -0
  227. package/src/resources/extensions/gsd/tests/schema-v21-sequence.test.ts +5 -3
  228. package/src/resources/extensions/gsd/tests/schema-v27-v28-sequence.test.ts +162 -18
  229. package/src/resources/extensions/gsd/tests/skipped-validation-db-atomicity.test.ts +8 -0
  230. package/src/resources/extensions/gsd/tests/source-observations.test.ts +275 -0
  231. package/src/resources/extensions/gsd/tests/stale-queued-milestone.test.ts +43 -0
  232. package/src/resources/extensions/gsd/tests/state-reconciliation-drift.test.ts +76 -21
  233. package/src/resources/extensions/gsd/tests/thinking-level-resolution.test.ts +203 -0
  234. package/src/resources/extensions/gsd/tests/uat-policy.test.ts +170 -0
  235. package/src/resources/extensions/gsd/tests/unit-context-manifest.test.ts +7 -1
  236. package/src/resources/extensions/gsd/tests/workflow-kernel.test.ts +7 -0
  237. package/src/resources/extensions/gsd/tests/workflow-manifest.test.ts +306 -1
  238. package/src/resources/extensions/gsd/tests/workflow-tool-executors.test.ts +73 -6
  239. package/src/resources/extensions/gsd/tests/worktree-db.test.ts +511 -1
  240. package/src/resources/extensions/gsd/tests/worktree-state-projection.test.ts +44 -0
  241. package/src/resources/extensions/gsd/tool-contract.ts +28 -0
  242. package/src/resources/extensions/gsd/tool-presentation-plan.ts +1 -11
  243. package/src/resources/extensions/gsd/tools/complete-slice.ts +7 -6
  244. package/src/resources/extensions/gsd/tools/plan-slice.ts +54 -12
  245. package/src/resources/extensions/gsd/tools/plan-task.ts +8 -1
  246. package/src/resources/extensions/gsd/tools/workflow-tool-executors.ts +66 -526
  247. package/src/resources/extensions/gsd/types.ts +1 -0
  248. package/src/resources/extensions/gsd/uat-policy.ts +191 -0
  249. package/src/resources/extensions/gsd/uat-run.ts +550 -0
  250. package/src/resources/extensions/gsd/unit-context-manifest.ts +3 -4
  251. package/src/resources/extensions/gsd/verdict-parser.ts +3 -10
  252. package/src/resources/extensions/gsd/workflow-manifest.ts +193 -7
  253. package/src/resources/extensions/gsd/workflow-projections.ts +9 -0
  254. package/src/resources/extensions/gsd/worktree-state-projection.ts +22 -22
  255. package/src/resources/extensions/shared/tests/format-utils.test.ts +8 -3
  256. package/src/resources/extensions/subagent/agents.ts +4 -0
  257. package/src/resources/extensions/subagent/index.ts +28 -3
  258. package/src/resources/extensions/subagent/launch.ts +8 -0
  259. package/src/resources/extensions/subagent/tests/model-override.test.ts +31 -0
  260. /package/dist/web/standalone/.next/static/{zzYMrKpPGfRQRxSFO32Jr → tJOKQbQRO-9MiFDO8DIDS}/_buildManifest.js +0 -0
  261. /package/dist/web/standalone/.next/static/{zzYMrKpPGfRQRxSFO32Jr → tJOKQbQRO-9MiFDO8DIDS}/_ssgManifest.js +0 -0
@@ -6,24 +6,70 @@ import {
6
6
  readTransaction,
7
7
  restoreManifest,
8
8
  } from "./gsd-db.js";
9
- import type { MilestoneRow } from "./db-milestone-artifact-rows.js";
9
+ import type { ArtifactRow, MilestoneRow } from "./db-milestone-artifact-rows.js";
10
10
  import type { SliceRow, TaskRow } from "./db-task-slice-rows.js";
11
11
  import type { VerificationEvidenceRow } from "./db-verification-evidence-rows.js";
12
- import type { Decision } from "./types.js";
12
+ import type { Decision, GateRow, Requirement } from "./types.js";
13
13
  import { atomicWriteSync } from "./atomic-write.js";
14
+ import { getAllDecisionsFromMemories } from "./context-store.js";
15
+ import { backfillDecisionsToMemories } from "./memory-backfill.js";
16
+ import { invalidateAllCaches } from "./cache.js";
14
17
  import { readFileSync, existsSync, mkdirSync } from "node:fs";
15
- import { join } from "node:path";
18
+ import { isAbsolute, join, relative, resolve } from "node:path";
16
19
 
17
20
  // ─── Manifest Types ──────────────────────────────────────────────────────
18
21
 
22
+ export interface ManifestArtifactRow extends ArtifactRow {
23
+ content_hash: string | null;
24
+ }
25
+
26
+ export interface ReplanHistoryManifestRow {
27
+ id: number;
28
+ milestone_id: string;
29
+ slice_id: string | null;
30
+ task_id: string | null;
31
+ summary: string;
32
+ previous_artifact_path: string | null;
33
+ replacement_artifact_path: string | null;
34
+ created_at: string;
35
+ }
36
+
37
+ export interface AssessmentManifestRow {
38
+ path: string;
39
+ milestone_id: string;
40
+ slice_id: string | null;
41
+ task_id: string | null;
42
+ status: string;
43
+ scope: string;
44
+ full_content: string;
45
+ created_at: string;
46
+ }
47
+
48
+ export interface MilestoneCommitAttributionManifestRow {
49
+ commit_sha: string;
50
+ milestone_id: string;
51
+ slice_id: string | null;
52
+ task_id: string | null;
53
+ source: string;
54
+ confidence: number;
55
+ files_json: string;
56
+ created_at: string;
57
+ }
58
+
19
59
  export interface StateManifest {
20
60
  version: 1;
21
61
  exported_at: string; // ISO 8601
62
+ requirements?: Requirement[];
63
+ artifacts?: ManifestArtifactRow[];
22
64
  milestones: MilestoneRow[];
23
65
  slices: SliceRow[];
24
66
  tasks: TaskRow[];
25
67
  decisions: Decision[];
68
+ replan_history?: ReplanHistoryManifestRow[];
69
+ assessments?: AssessmentManifestRow[];
70
+ quality_gates?: GateRow[];
26
71
  verification_evidence: VerificationEvidenceRow[];
72
+ milestone_commit_attributions?: MilestoneCommitAttributionManifestRow[];
27
73
  }
28
74
 
29
75
  // ─── helpers ─────────────────────────────────────────────────────────────
@@ -51,11 +97,26 @@ export function toNumeric(value: unknown, fallback: number | null = null): numbe
51
97
  return fallback;
52
98
  }
53
99
 
100
+ function mergeDecisionSurfaces(legacyDecisions: Decision[], memoryDecisions: Decision[]): Decision[] {
101
+ const byId = new Map<string, Decision>();
102
+ for (const decision of legacyDecisions) {
103
+ byId.set(decision.id, decision);
104
+ }
105
+ for (const decision of memoryDecisions) {
106
+ byId.set(decision.id, decision);
107
+ }
108
+ return Array.from(byId.values()).sort((a, b) => {
109
+ const seqDelta = (a.seq ?? 0) - (b.seq ?? 0);
110
+ return seqDelta === 0 ? a.id.localeCompare(b.id) : seqDelta;
111
+ });
112
+ }
113
+
54
114
  // ─── snapshotState ───────────────────────────────────────────────────────
55
115
 
56
116
  /**
57
- * Capture complete DB state as a StateManifest.
58
- * Reads all rows from milestones, slices, tasks, decisions, verification_evidence.
117
+ * Capture DB-backed workflow state as a StateManifest.
118
+ * Runtime soft state and append-only audit streams stay outside this recovery
119
+ * substrate; correctness records and persisted evidence are included.
59
120
  *
60
121
  * Note: rows returned from raw queries are plain objects with TEXT columns for
61
122
  * JSON arrays. We parse them into typed Row objects using the same logic as
@@ -67,6 +128,34 @@ export function snapshotState(): StateManifest {
67
128
  // Wrap all reads in a deferred transaction so the snapshot is consistent
68
129
  // (all SELECTs see the same DB state even if a concurrent write lands between them).
69
130
  return readTransaction(() => {
131
+ const rawRequirements = db.prepare("SELECT * FROM requirements ORDER BY id").all() as Record<string, unknown>[];
132
+ const requirements: Requirement[] = rawRequirements.map((r) => ({
133
+ id: r["id"] as string,
134
+ class: (r["class"] as string) ?? "",
135
+ status: (r["status"] as string) ?? "",
136
+ description: (r["description"] as string) ?? "",
137
+ why: (r["why"] as string) ?? "",
138
+ source: (r["source"] as string) ?? "",
139
+ primary_owner: (r["primary_owner"] as string) ?? "",
140
+ supporting_slices: (r["supporting_slices"] as string) ?? "",
141
+ validation: (r["validation"] as string) ?? "",
142
+ notes: (r["notes"] as string) ?? "",
143
+ full_content: (r["full_content"] as string) ?? "",
144
+ superseded_by: (r["superseded_by"] as string) ?? null,
145
+ }));
146
+
147
+ const rawArtifacts = db.prepare("SELECT * FROM artifacts ORDER BY path").all() as Record<string, unknown>[];
148
+ const artifacts: ManifestArtifactRow[] = rawArtifacts.map((r) => ({
149
+ path: r["path"] as string,
150
+ artifact_type: (r["artifact_type"] as string) ?? "",
151
+ milestone_id: (r["milestone_id"] as string) ?? null,
152
+ slice_id: (r["slice_id"] as string) ?? null,
153
+ task_id: (r["task_id"] as string) ?? null,
154
+ full_content: (r["full_content"] as string) ?? "",
155
+ imported_at: (r["imported_at"] as string) ?? "",
156
+ content_hash: (r["content_hash"] as string) ?? null,
157
+ }));
158
+
70
159
  const rawMilestones = db.prepare(
71
160
  "SELECT * FROM milestones ORDER BY CASE WHEN sequence > 0 THEN 0 ELSE 1 END, sequence, id",
72
161
  ).all() as Record<string, unknown>[];
@@ -152,7 +241,7 @@ export function snapshotState(): StateManifest {
152
241
  }));
153
242
 
154
243
  const rawDecisions = db.prepare("SELECT * FROM decisions ORDER BY seq").all() as Record<string, unknown>[];
155
- const decisions: Decision[] = rawDecisions.map((r) => ({
244
+ const legacyDecisions: Decision[] = rawDecisions.map((r) => ({
156
245
  seq: toNumeric(r["seq"], 0) as number,
157
246
  id: r["id"] as string,
158
247
  when_context: (r["when_context"] as string) ?? "",
@@ -165,6 +254,45 @@ export function snapshotState(): StateManifest {
165
254
  source: (r["source"] as string) ?? "discussion",
166
255
  superseded_by: (r["superseded_by"] as string) ?? null,
167
256
  }));
257
+ const decisions = mergeDecisionSurfaces(legacyDecisions, getAllDecisionsFromMemories());
258
+
259
+ const rawReplanHistory = db.prepare("SELECT * FROM replan_history ORDER BY id").all() as Record<string, unknown>[];
260
+ const replan_history: ReplanHistoryManifestRow[] = rawReplanHistory.map((r) => ({
261
+ id: toNumeric(r["id"], 0) as number,
262
+ milestone_id: (r["milestone_id"] as string) ?? "",
263
+ slice_id: (r["slice_id"] as string) ?? null,
264
+ task_id: (r["task_id"] as string) ?? null,
265
+ summary: (r["summary"] as string) ?? "",
266
+ previous_artifact_path: (r["previous_artifact_path"] as string) ?? null,
267
+ replacement_artifact_path: (r["replacement_artifact_path"] as string) ?? null,
268
+ created_at: (r["created_at"] as string) ?? "",
269
+ }));
270
+
271
+ const rawAssessments = db.prepare("SELECT * FROM assessments ORDER BY path").all() as Record<string, unknown>[];
272
+ const assessments: AssessmentManifestRow[] = rawAssessments.map((r) => ({
273
+ path: r["path"] as string,
274
+ milestone_id: (r["milestone_id"] as string) ?? "",
275
+ slice_id: (r["slice_id"] as string) ?? null,
276
+ task_id: (r["task_id"] as string) ?? null,
277
+ status: (r["status"] as string) ?? "",
278
+ scope: (r["scope"] as string) ?? "",
279
+ full_content: (r["full_content"] as string) ?? "",
280
+ created_at: (r["created_at"] as string) ?? "",
281
+ }));
282
+
283
+ const rawQualityGates = db.prepare("SELECT * FROM quality_gates ORDER BY milestone_id, slice_id, gate_id, task_id").all() as Record<string, unknown>[];
284
+ const quality_gates: GateRow[] = rawQualityGates.map((r) => ({
285
+ milestone_id: r["milestone_id"] as string,
286
+ slice_id: r["slice_id"] as string,
287
+ gate_id: r["gate_id"] as GateRow["gate_id"],
288
+ scope: r["scope"] as GateRow["scope"],
289
+ task_id: (r["task_id"] as string) ?? "",
290
+ status: r["status"] as GateRow["status"],
291
+ verdict: r["status"] === "pending" ? null : (r["verdict"] as GateRow["verdict"]),
292
+ rationale: (r["rationale"] as string) ?? "",
293
+ findings: (r["findings"] as string) ?? "",
294
+ evaluated_at: (r["evaluated_at"] as string) ?? null,
295
+ }));
168
296
 
169
297
  const rawEvidence = db.prepare("SELECT * FROM verification_evidence ORDER BY id").all() as Record<string, unknown>[];
170
298
  const verification_evidence: VerificationEvidenceRow[] = rawEvidence.map((r) => ({
@@ -179,14 +307,34 @@ export function snapshotState(): StateManifest {
179
307
  created_at: r["created_at"] as string,
180
308
  }));
181
309
 
310
+ const rawCommitAttributions = db.prepare(
311
+ "SELECT * FROM milestone_commit_attributions ORDER BY milestone_id, commit_sha",
312
+ ).all() as Record<string, unknown>[];
313
+ const milestone_commit_attributions: MilestoneCommitAttributionManifestRow[] = rawCommitAttributions.map((r) => ({
314
+ commit_sha: r["commit_sha"] as string,
315
+ milestone_id: r["milestone_id"] as string,
316
+ slice_id: (r["slice_id"] as string) ?? null,
317
+ task_id: (r["task_id"] as string) ?? null,
318
+ source: (r["source"] as string) ?? "recorded",
319
+ confidence: toNumeric(r["confidence"], 1) as number,
320
+ files_json: (r["files_json"] as string) ?? "[]",
321
+ created_at: (r["created_at"] as string) ?? "",
322
+ }));
323
+
182
324
  const result: StateManifest = {
183
325
  version: 1,
184
326
  exported_at: new Date().toISOString(),
327
+ requirements,
328
+ artifacts,
185
329
  milestones,
186
330
  slices,
187
331
  tasks,
188
332
  decisions,
333
+ replan_history,
334
+ assessments,
335
+ quality_gates,
189
336
  verification_evidence,
337
+ milestone_commit_attributions,
190
338
  };
191
339
 
192
340
  return result;
@@ -232,20 +380,55 @@ export function readManifest(basePath: string): StateManifest | null {
232
380
  throw new Error(`Unsupported manifest version: ${parsed.version}`);
233
381
  }
234
382
 
235
- // Validate required fields to avoid cryptic errors during restore
383
+ // Validate required fields to avoid cryptic errors during restore.
236
384
  if (!Array.isArray(parsed.milestones) || !Array.isArray(parsed.slices) ||
237
385
  !Array.isArray(parsed.tasks) || !Array.isArray(parsed.decisions) ||
238
386
  !Array.isArray(parsed.verification_evidence)) {
239
387
  throw new Error("Malformed manifest: missing or invalid required arrays");
240
388
  }
241
389
 
390
+ for (const key of ["requirements", "artifacts", "replan_history", "assessments", "quality_gates", "milestone_commit_attributions"] as const) {
391
+ if (parsed[key] !== undefined && !Array.isArray(parsed[key])) {
392
+ throw new Error(`Malformed manifest: ${key} must be an array when present`);
393
+ }
394
+ }
395
+
242
396
  return parsed;
243
397
  }
244
398
 
399
+ function stripGsdPrefix(path: string): string {
400
+ return path.startsWith(".gsd/") ? path.slice(".gsd/".length) : path;
401
+ }
402
+
403
+ function artifactProjectionPath(basePath: string, artifactPath: string): string {
404
+ const gsdDir = resolve(basePath, ".gsd");
405
+ const fullPath = resolve(gsdDir, stripGsdPrefix(artifactPath));
406
+ const rel = relative(gsdDir, fullPath);
407
+ if (rel.startsWith("..") || isAbsolute(rel)) {
408
+ throw new Error(`Malformed manifest: artifact path escapes .gsd: ${artifactPath}`);
409
+ }
410
+ return fullPath;
411
+ }
412
+
413
+ function restoreArtifactProjections(basePath: string, manifest: StateManifest): void {
414
+ if (manifest.artifacts === undefined) return;
415
+
416
+ for (const artifact of manifest.artifacts) {
417
+ atomicWriteSync(
418
+ artifactProjectionPath(basePath, artifact.path),
419
+ artifact.full_content ?? "",
420
+ );
421
+ }
422
+ }
423
+
245
424
  // ─── bootstrapFromManifest ──────────────────────────────────────────────
246
425
 
247
426
  /**
248
427
  * Read state-manifest.json and restore DB state from it.
428
+ * Rehydrates artifact projection files for restored artifacts so file-based
429
+ * fallback paths see the same evidence as the DB.
430
+ * Re-mirrors restored legacy decisions into memories so the ADR-013
431
+ * memory-backed decision readers see bootstrapped decisions immediately.
249
432
  * Returns true if bootstrap succeeded, false if manifest file doesn't exist.
250
433
  */
251
434
  export function bootstrapFromManifest(basePath: string): boolean {
@@ -256,5 +439,8 @@ export function bootstrapFromManifest(basePath: string): boolean {
256
439
  }
257
440
 
258
441
  restoreManifest(manifest);
442
+ restoreArtifactProjections(basePath, manifest);
443
+ backfillDecisionsToMemories();
444
+ invalidateAllCaches();
259
445
  return true;
260
446
  }
@@ -546,6 +546,15 @@ export async function regenerateIfMissing(
546
546
  return false;
547
547
  }
548
548
 
549
+ // A sketch slice (or any slice not yet planned into tasks) legitimately has
550
+ // no PLAN.md to project. renderPlanFromDb throws on a zero-task slice, so
551
+ // skip cleanly here rather than logging a spurious failure. The PLAN.md will
552
+ // be created once the slice is refined and its first task is written through
553
+ // the single writer.
554
+ if (fileType === "PLAN" && getSliceTasks(milestoneId, sliceId).length === 0) {
555
+ return false;
556
+ }
557
+
549
558
  // Regenerate the missing file. Each renderer may swallow its own errors
550
559
  // (e.g. renderStateProjection), so confirm the file actually exists on
551
560
  // disk before reporting success — true must mean "file is there now".
@@ -123,36 +123,29 @@ function forceOverwriteAssessmentsWithVerdict(
123
123
  }
124
124
  }
125
125
 
126
- function syncTopLevelMilestoneArtifacts(
126
+ function syncOtherMilestoneArtifacts(
127
127
  srcMilestonesDir: string,
128
128
  dstMilestonesDir: string,
129
+ currentMilestoneId: string,
129
130
  ): void {
130
131
  if (!existsSync(srcMilestonesDir)) return;
131
132
 
132
133
  try {
133
134
  for (const milestoneEntry of readdirSync(srcMilestonesDir, { withFileTypes: true })) {
134
135
  if (!milestoneEntry.isDirectory()) continue;
136
+ // The current milestone is already fully projected by the caller's
137
+ // additive safeCopyRecursive; skip it here to avoid redundant work.
138
+ if (milestoneEntry.name === currentMilestoneId) continue;
135
139
  const srcMilestoneDir = join(srcMilestonesDir, milestoneEntry.name);
136
140
  const dstMilestoneDir = join(dstMilestonesDir, milestoneEntry.name);
137
141
 
138
- try {
139
- mkdirSync(dstMilestoneDir, { recursive: true });
140
- for (const fileEntry of readdirSync(srcMilestoneDir, { withFileTypes: true })) {
141
- if (!fileEntry.isFile()) continue;
142
- if (!fileEntry.name.endsWith(".md") && !fileEntry.name.endsWith(".json")) continue;
143
-
144
- safeCopy(
145
- join(srcMilestoneDir, fileEntry.name),
146
- join(dstMilestoneDir, fileEntry.name),
147
- { force: false },
148
- );
149
- }
150
- } catch (err) {
151
- logWarning(
152
- "worktree",
153
- `milestone top-level artifact sync failed (${milestoneEntry.name}): ${err instanceof Error ? err.message : String(err)}`,
154
- );
155
- }
142
+ // Additively project the entire milestone subtree (force:false), not just
143
+ // top-level files. Prior completed milestones keep their per-slice and
144
+ // per-task SUMMARY.md / UAT.md on disk so the worktree's stale-render
145
+ // detector doesn't flag them as missing (DB has summary, disk doesn't).
146
+ // force:false preserves any worktree-local files (#1886 invariant) and
147
+ // only fills in files absent from the worktree projection.
148
+ safeCopyRecursive(srcMilestoneDir, dstMilestoneDir, { force: false });
156
149
  }
157
150
  } catch (err) {
158
151
  logWarning(
@@ -248,9 +241,16 @@ export function _projectRootToWorktreeImpl(
248
241
  { force: false },
249
242
  );
250
243
 
251
- // Additively project missing top-level milestone files for all milestones
252
- // so worktree-bound units can verify future-milestone context artifacts.
253
- syncTopLevelMilestoneArtifacts(join(prGsd, "milestones"), join(wtGsd, "milestones"));
244
+ // Additively project the full subtree of every OTHER milestone so
245
+ // worktree-bound units can read prior-milestone context artifacts and so
246
+ // completed milestones retain their per-slice/per-task SUMMARY.md and
247
+ // UAT.md on the worktree filesystem (otherwise the stale-render detector
248
+ // flags them as "complete in DB but missing on disk").
249
+ syncOtherMilestoneArtifacts(
250
+ join(prGsd, "milestones"),
251
+ join(wtGsd, "milestones"),
252
+ milestoneId,
253
+ );
254
254
 
255
255
  // Force-sync ASSESSMENT files that have a verdict from project root (#2821).
256
256
  // The additive-only copy above preserves worktree-local files, but
@@ -11,6 +11,7 @@ import {
11
11
  centerLine,
12
12
  fitColumns,
13
13
  } from "../layout-utils.js";
14
+ import { visibleWidth } from "@gsd/pi-tui";
14
15
 
15
16
  describe("formatDuration", () => {
16
17
  it("formats seconds", () => {
@@ -59,8 +60,10 @@ describe("joinColumns", () => {
59
60
 
60
61
  it("truncates when content overflows", () => {
61
62
  const result = joinColumns("a".repeat(20), "b".repeat(20), 30);
62
- // Should be truncated to 30 chars
63
- assert.ok(result.length <= 30);
63
+ // truncateToWidth is ANSI-aware and brackets the ellipsis with zero-width
64
+ // SGR resets, so the visible width — not the raw string length is the
65
+ // invariant that must hold within the column budget.
66
+ assert.ok(visibleWidth(result) <= 30);
64
67
  });
65
68
  });
66
69
 
@@ -72,7 +75,9 @@ describe("centerLine", () => {
72
75
 
73
76
  it("truncates when content exceeds width", () => {
74
77
  const result = centerLine("abcdefgh", 4);
75
- assert.ok(result.length <= 4);
78
+ // Visible width is the meaningful budget; the result may carry zero-width
79
+ // SGR reset codes around the ellipsis (see truncateToWidth).
80
+ assert.ok(visibleWidth(result) <= 4);
76
81
  });
77
82
  });
78
83
 
@@ -15,6 +15,8 @@ export interface AgentConfig {
15
15
  description: string;
16
16
  tools?: string[];
17
17
  model?: string;
18
+ /** Default reasoning effort for this agent, forwarded as `--thinking` (#508). */
19
+ thinking?: string;
18
20
  conflictsWith?: string[];
19
21
  systemPrompt: string;
20
22
  source: "user" | "project";
@@ -31,6 +33,7 @@ interface AgentFrontmatter extends Record<string, unknown> {
31
33
  description?: string;
32
34
  tools?: string | string[];
33
35
  model?: string;
36
+ thinking?: string;
34
37
  conflicts_with?: string;
35
38
  }
36
39
 
@@ -100,6 +103,7 @@ function loadAgentsFromDir(dir: string, source: "user" | "project"): AgentConfig
100
103
  description: frontmatter.description,
101
104
  tools: tools && tools.length > 0 ? tools : undefined,
102
105
  model: frontmatter.model,
106
+ thinking: typeof frontmatter.thinking === "string" ? frontmatter.thinking : undefined,
103
107
  conflictsWith,
104
108
  systemPrompt: body,
105
109
  source,
@@ -341,6 +341,7 @@ interface TaskParam {
341
341
  task: string;
342
342
  cwd?: string;
343
343
  model?: string;
344
+ thinking?: string;
344
345
  context?: SubagentContextMode;
345
346
  }
346
347
 
@@ -416,6 +417,9 @@ async function runSingleAgent(
416
417
  parentSessionManager?: Parameters<typeof createSubagentLaunchPlan>[0]["parentSessionManager"],
417
418
  sessionOverride?: SubagentSessionArgs,
418
419
  trackingName?: string,
420
+ // Trailing param (kept after trackingName so existing positional call sites
421
+ // don't shift). Reasoning effort forwarded to the child (#508).
422
+ thinkingOverride?: string,
419
423
  ): Promise<SingleResult> {
420
424
  const agent = agents.find((a) => a.name === agentName);
421
425
 
@@ -489,6 +493,7 @@ async function runSingleAgent(
489
493
  task,
490
494
  tmpPromptPath,
491
495
  modelOverride,
496
+ thinkingOverride,
492
497
  contextMode,
493
498
  parentSessionManager,
494
499
  session: sessionOverride,
@@ -581,10 +586,12 @@ async function runSingleAgentInCmuxSplit(
581
586
  parentSessionManager?: Parameters<typeof createSubagentLaunchPlan>[0]["parentSessionManager"],
582
587
  sessionOverride?: SubagentSessionArgs,
583
588
  trackingName?: string,
589
+ // Trailing param (see runSingleAgent). Reasoning effort forwarded to the child (#508).
590
+ thinkingOverride?: string,
584
591
  ): Promise<SingleResult> {
585
592
  const agent = agents.find((a) => a.name === agentName);
586
593
  if (!agent) {
587
- return runSingleAgent(defaultCwd, agents, agentName, task, cwd, step, signal, onUpdate, makeDetails, modelOverride, contextMode, parentSessionManager, sessionOverride, trackingName);
594
+ return runSingleAgent(defaultCwd, agents, agentName, task, cwd, step, signal, onUpdate, makeDetails, modelOverride, contextMode, parentSessionManager, sessionOverride, trackingName, thinkingOverride);
588
595
  }
589
596
 
590
597
  let tmpPromptDir: string | null = null;
@@ -631,7 +638,7 @@ async function runSingleAgentInCmuxSplit(
631
638
  ? await cmuxClient.createSplit(directionOrSurfaceId as "right" | "down" | "left" | "up")
632
639
  : directionOrSurfaceId;
633
640
  if (!cmuxSurfaceId) {
634
- return runSingleAgent(defaultCwd, agents, agentName, task, cwd, step, signal, onUpdate, makeDetails, modelOverride, contextMode, parentSessionManager, sessionOverride, trackingName);
641
+ return runSingleAgent(defaultCwd, agents, agentName, task, cwd, step, signal, onUpdate, makeDetails, modelOverride, contextMode, parentSessionManager, sessionOverride, trackingName, thinkingOverride);
635
642
  }
636
643
 
637
644
  const bundledPaths = (process.env.GSD_BUNDLED_EXTENSION_PATHS ?? "").split(path.delimiter).map((s) => s.trim()).filter(Boolean);
@@ -641,6 +648,7 @@ async function runSingleAgentInCmuxSplit(
641
648
  task,
642
649
  tmpPromptPath,
643
650
  modelOverride,
651
+ thinkingOverride,
644
652
  contextMode,
645
653
  parentSessionManager,
646
654
  session: sessionOverride,
@@ -665,7 +673,7 @@ async function runSingleAgentInCmuxSplit(
665
673
 
666
674
  const sent = await cmuxClient.sendSurface(cmuxSurfaceId, `bash -lc ${shellEscape(innerScript)}`);
667
675
  if (!sent) {
668
- return runSingleAgent(defaultCwd, agents, agentName, task, cwd, step, signal, onUpdate, makeDetails, modelOverride, contextMode, parentSessionManager, sessionOverride, trackingName);
676
+ return runSingleAgent(defaultCwd, agents, agentName, task, cwd, step, signal, onUpdate, makeDetails, modelOverride, contextMode, parentSessionManager, sessionOverride, trackingName, thinkingOverride);
669
677
  }
670
678
 
671
679
  const finished = await waitForFile(exitPath, signal);
@@ -726,11 +734,18 @@ async function runSingleAgentInCmuxSplit(
726
734
  }
727
735
  }
728
736
 
737
+ const ThinkingLevelSchema = StringEnum(["off", "minimal", "low", "medium", "high", "xhigh"] as const, {
738
+ description:
739
+ "Reasoning effort override for the subagent (forwarded as --thinking). " +
740
+ "Independent of model choice; unsupported levels are clamped to the model at dispatch.",
741
+ });
742
+
729
743
  const TaskItem = Type.Object({
730
744
  agent: Type.String({ description: "Name of the agent to invoke" }),
731
745
  task: Type.String({ description: "Task to delegate to the agent" }),
732
746
  cwd: Type.Optional(Type.String({ description: "Working directory for the agent process" })),
733
747
  model: Type.Optional(Type.String({ description: "Model override for this task (e.g. 'claude-sonnet-4-6')" })),
748
+ thinking: Type.Optional(ThinkingLevelSchema),
734
749
  context: Type.Optional(StringEnum(["fresh", "fork"] as const, {
735
750
  description: 'Context mode for this task (see context field on the top-level params).',
736
751
  default: "fresh",
@@ -742,6 +757,7 @@ const ChainItem = Type.Object({
742
757
  task: Type.String({ description: "Task with optional {previous} placeholder for prior step output" }),
743
758
  cwd: Type.Optional(Type.String({ description: "Working directory for the agent process" })),
744
759
  model: Type.Optional(Type.String({ description: "Model override for this step (e.g. 'claude-sonnet-4-6')" })),
760
+ thinking: Type.Optional(ThinkingLevelSchema),
745
761
  context: Type.Optional(StringEnum(["fresh", "fork"] as const, {
746
762
  description: 'Context mode for this step (see context field on the top-level params).',
747
763
  default: "fresh",
@@ -779,6 +795,7 @@ const SubagentParams = Type.Object({
779
795
  ),
780
796
  cwd: Type.Optional(Type.String({ description: "Working directory for the agent process (single mode)" })),
781
797
  model: Type.Optional(Type.String({ description: "Model override for the subagent (e.g. 'claude-sonnet-4-6'). Takes precedence over the agent's frontmatter model." })),
798
+ thinking: Type.Optional(ThinkingLevelSchema),
782
799
  isolated: Type.Optional(
783
800
  Type.Boolean({
784
801
  description:
@@ -930,6 +947,7 @@ export default function (pi: ExtensionAPI) {
930
947
  ctx.sessionManager,
931
948
  { mode: "fork", sessionFile: selected.sessionFile, sessionDir: path.dirname(selected.sessionFile) },
932
949
  selected.trackingName,
950
+ params.thinking,
933
951
  );
934
952
  return {
935
953
  content: [{ type: "text", text: getFinalOutput(result.messages) || result.errorMessage || result.stderr || "(no output)" }],
@@ -1244,6 +1262,7 @@ export default function (pi: ExtensionAPI) {
1244
1262
  ctx.sessionManager,
1245
1263
  undefined,
1246
1264
  dispatchTrackingNames[0],
1265
+ params.thinking,
1247
1266
  );
1248
1267
  if (isolation && result.exitCode === 0) {
1249
1268
  const patches = await isolation.captureDelta();
@@ -1317,6 +1336,7 @@ export default function (pi: ExtensionAPI) {
1317
1336
  ctx.sessionManager,
1318
1337
  undefined,
1319
1338
  dispatchTrackingNames[i],
1339
+ step.thinking || params.thinking,
1320
1340
  );
1321
1341
  results.push(result);
1322
1342
  persistRunResults(results);
@@ -1397,6 +1417,7 @@ export default function (pi: ExtensionAPI) {
1397
1417
  const results = await mapWithConcurrencyLimit(taskParams, MAX_CONCURRENCY, async (t, index) => {
1398
1418
  const workerId = registerWorker(t.agent, t.task, index, batchSize, batchId);
1399
1419
  const taskModel = t.model || params.model;
1420
+ const taskThinking = t.thinking || params.thinking;
1400
1421
  const updateParallelResult = (partial: AgentToolResult<SubagentDetails>) => {
1401
1422
  if (partial.details?.results[0]) {
1402
1423
  allResults[index] = partial.details.results[0];
@@ -1422,6 +1443,7 @@ export default function (pi: ExtensionAPI) {
1422
1443
  ctx.sessionManager,
1423
1444
  undefined,
1424
1445
  dispatchTrackingNames[index],
1446
+ taskThinking,
1425
1447
  )
1426
1448
  : runSingleAgent(
1427
1449
  ctx.cwd,
@@ -1438,6 +1460,7 @@ export default function (pi: ExtensionAPI) {
1438
1460
  ctx.sessionManager,
1439
1461
  undefined,
1440
1462
  dispatchTrackingNames[index],
1463
+ taskThinking,
1441
1464
  );
1442
1465
  const runTask = async () => {
1443
1466
  let isolation: IsolationEnvironment | null = null;
@@ -1536,6 +1559,7 @@ export default function (pi: ExtensionAPI) {
1536
1559
  ctx.sessionManager,
1537
1560
  undefined,
1538
1561
  dispatchTrackingNames[0],
1562
+ params.thinking,
1539
1563
  )
1540
1564
  : await runSingleAgent(
1541
1565
  ctx.cwd,
@@ -1552,6 +1576,7 @@ export default function (pi: ExtensionAPI) {
1552
1576
  ctx.sessionManager,
1553
1577
  undefined,
1554
1578
  dispatchTrackingNames[0],
1579
+ params.thinking,
1555
1580
  );
1556
1581
  finalResults = [result];
1557
1582
 
@@ -25,6 +25,8 @@ export interface SubagentLaunchInput {
25
25
  task: string;
26
26
  tmpPromptPath: string | null;
27
27
  modelOverride?: string;
28
+ /** Reasoning effort override forwarded as `--thinking` (ADR-026 / #508). */
29
+ thinkingOverride?: string;
28
30
  contextMode?: SubagentContextMode;
29
31
  parentSessionManager?: SubagentParentSessionManager;
30
32
  session?: SubagentSessionArgs;
@@ -60,6 +62,7 @@ export function buildSubagentProcessArgs(
60
62
  task: string,
61
63
  tmpPromptPath: string | null,
62
64
  modelOverride?: string,
65
+ thinkingOverride?: string,
63
66
  session: SubagentSessionArgs = { mode: "fresh" },
64
67
  ): string[] {
65
68
  const args: string[] = ["--mode", "json", "-p"];
@@ -71,6 +74,10 @@ export function buildSubagentProcessArgs(
71
74
  }
72
75
  const effectiveModel = modelOverride ?? agent.model;
73
76
  if (effectiveModel) args.push("--model", effectiveModel);
77
+ // Reasoning effort travels with the model (ADR-026 / #508). The child CLI
78
+ // validates `--thinking` and clamps to the resolved model at dispatch.
79
+ const effectiveThinking = thinkingOverride ?? agent.thinking;
80
+ if (effectiveThinking) args.push("--thinking", effectiveThinking);
74
81
  if (agent.tools && agent.tools.length > 0) args.push("--tools", agent.tools.join(","));
75
82
  if (tmpPromptPath) args.push("--append-system-prompt", tmpPromptPath);
76
83
  args.push(`Task: ${task}`);
@@ -122,6 +129,7 @@ export function createSubagentLaunchPlan(input: SubagentLaunchInput): SubagentLa
122
129
  input.task,
123
130
  input.tmpPromptPath,
124
131
  input.modelOverride,
132
+ input.thinkingOverride,
125
133
  session,
126
134
  ),
127
135
  env: buildSubagentProcessEnv(),
@@ -53,3 +53,34 @@ describe("buildSubagentProcessArgs model override", () => {
53
53
  assert.equal(args[modelIndex + 1], "model-override");
54
54
  });
55
55
  });
56
+
57
+ describe("buildSubagentProcessArgs thinking override (#508)", () => {
58
+ it("forwards thinkingOverride as --thinking", () => {
59
+ const args = buildSubagentProcessArgs(makeAgent(), "task", null, undefined, "low");
60
+ const idx = args.indexOf("--thinking");
61
+ assert.notEqual(idx, -1, "should include --thinking flag");
62
+ assert.equal(args[idx + 1], "low");
63
+ });
64
+
65
+ it("falls back to agent.thinking when no override provided", () => {
66
+ const args = buildSubagentProcessArgs(makeAgent({ thinking: "high" }), "task", null);
67
+ const idx = args.indexOf("--thinking");
68
+ assert.equal(args[idx + 1], "high");
69
+ });
70
+
71
+ it("override takes precedence over agent.thinking", () => {
72
+ const args = buildSubagentProcessArgs(makeAgent({ thinking: "high" }), "task", null, undefined, "minimal");
73
+ assert.equal(args[args.indexOf("--thinking") + 1], "minimal");
74
+ });
75
+
76
+ it("omits --thinking when neither override nor agent.thinking is set", () => {
77
+ const args = buildSubagentProcessArgs(makeAgent(), "task", null);
78
+ assert.equal(args.indexOf("--thinking"), -1, "should not include --thinking flag");
79
+ });
80
+
81
+ it("forwards both model and thinking together", () => {
82
+ const args = buildSubagentProcessArgs(makeAgent(), "task", null, "model-x", "xhigh");
83
+ assert.equal(args[args.indexOf("--model") + 1], "model-x");
84
+ assert.equal(args[args.indexOf("--thinking") + 1], "xhigh");
85
+ });
86
+ });