@opengsd/gsd-pi 1.1.1-dev.154fd443 → 1.1.1-dev.1854a79a

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 (169) hide show
  1. package/dist/project-sessions.js +4 -2
  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/claude-code-cli/stream-adapter.js +17 -9
  8. package/dist/resources/extensions/gsd/auto/contracts.js +8 -1
  9. package/dist/resources/extensions/gsd/auto/orchestrator.js +659 -57
  10. package/dist/resources/extensions/gsd/auto-prompts.js +14 -1
  11. package/dist/resources/extensions/gsd/auto-runtime-state.js +3 -0
  12. package/dist/resources/extensions/gsd/auto-tool-tracking.js +5 -0
  13. package/dist/resources/extensions/gsd/auto-unit-tool-scope.js +29 -0
  14. package/dist/resources/extensions/gsd/auto.js +62 -464
  15. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +2 -1
  16. package/dist/resources/extensions/gsd/debug-logger.js +10 -0
  17. package/dist/resources/extensions/gsd/doctor-proactive.js +7 -2
  18. package/dist/resources/extensions/gsd/markdown-renderer.js +31 -32
  19. package/dist/resources/extensions/gsd/mcp-filter.js +6 -0
  20. package/dist/resources/extensions/gsd/native-git-bridge.js +9 -0
  21. package/dist/resources/extensions/gsd/prompts/plan-slice.md +1 -1
  22. package/dist/resources/extensions/gsd/schemas/parsers.js +6 -1
  23. package/dist/resources/extensions/gsd/state-reconciliation/drift/artifact-db.js +21 -1
  24. package/dist/resources/extensions/gsd/tools/workflow-tool-executors.js +169 -20
  25. package/dist/web/standalone/.next/BUILD_ID +1 -1
  26. package/dist/web/standalone/.next/app-path-routes-manifest.json +6 -6
  27. package/dist/web/standalone/.next/build-manifest.json +2 -2
  28. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  29. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  30. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  31. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  32. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  33. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  34. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  35. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  36. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  37. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  38. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  39. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  40. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  41. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  42. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  43. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  44. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  45. package/dist/web/standalone/.next/server/app/api/boot/route.js.nft.json +1 -1
  46. package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route.js.nft.json +1 -1
  47. package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route.js.nft.json +1 -1
  48. package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route.js.nft.json +1 -1
  49. package/dist/web/standalone/.next/server/app/api/captures/route.js.nft.json +1 -1
  50. package/dist/web/standalone/.next/server/app/api/cleanup/route.js.nft.json +1 -1
  51. package/dist/web/standalone/.next/server/app/api/doctor/route.js.nft.json +1 -1
  52. package/dist/web/standalone/.next/server/app/api/export-data/route.js.nft.json +1 -1
  53. package/dist/web/standalone/.next/server/app/api/files/route.js.nft.json +1 -1
  54. package/dist/web/standalone/.next/server/app/api/forensics/route.js.nft.json +1 -1
  55. package/dist/web/standalone/.next/server/app/api/git/route.js +1 -1
  56. package/dist/web/standalone/.next/server/app/api/git/route.js.nft.json +1 -1
  57. package/dist/web/standalone/.next/server/app/api/history/route.js.nft.json +1 -1
  58. package/dist/web/standalone/.next/server/app/api/hooks/route.js.nft.json +1 -1
  59. package/dist/web/standalone/.next/server/app/api/inspect/route.js.nft.json +1 -1
  60. package/dist/web/standalone/.next/server/app/api/knowledge/route.js.nft.json +1 -1
  61. package/dist/web/standalone/.next/server/app/api/live-state/route.js.nft.json +1 -1
  62. package/dist/web/standalone/.next/server/app/api/mcp-connections/route.js.nft.json +1 -1
  63. package/dist/web/standalone/.next/server/app/api/notifications/route.js.nft.json +1 -1
  64. package/dist/web/standalone/.next/server/app/api/onboarding/route.js.nft.json +1 -1
  65. package/dist/web/standalone/.next/server/app/api/projects/route.js.nft.json +1 -1
  66. package/dist/web/standalone/.next/server/app/api/recovery/route.js.nft.json +1 -1
  67. package/dist/web/standalone/.next/server/app/api/session/browser/route.js.nft.json +1 -1
  68. package/dist/web/standalone/.next/server/app/api/session/command/route.js.nft.json +1 -1
  69. package/dist/web/standalone/.next/server/app/api/session/events/route.js.nft.json +1 -1
  70. package/dist/web/standalone/.next/server/app/api/session/manage/route.js.nft.json +1 -1
  71. package/dist/web/standalone/.next/server/app/api/settings-data/route.js.nft.json +1 -1
  72. package/dist/web/standalone/.next/server/app/api/shutdown/route.js.nft.json +1 -1
  73. package/dist/web/standalone/.next/server/app/api/skill-health/route.js.nft.json +1 -1
  74. package/dist/web/standalone/.next/server/app/api/steer/route.js.nft.json +1 -1
  75. package/dist/web/standalone/.next/server/app/api/switch-root/route.js.nft.json +1 -1
  76. package/dist/web/standalone/.next/server/app/api/terminal/sessions/route.js.nft.json +1 -1
  77. package/dist/web/standalone/.next/server/app/api/terminal/stream/route.js.nft.json +1 -1
  78. package/dist/web/standalone/.next/server/app/api/undo/route.js.nft.json +1 -1
  79. package/dist/web/standalone/.next/server/app/api/visualizer/route.js.nft.json +1 -1
  80. package/dist/web/standalone/.next/server/app/index.html +1 -1
  81. package/dist/web/standalone/.next/server/app/index.rsc +1 -1
  82. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  83. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
  84. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  85. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  86. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  87. package/dist/web/standalone/.next/server/app-paths-manifest.json +6 -6
  88. package/dist/web/standalone/.next/server/chunks/5047.js +2 -0
  89. package/dist/web/standalone/.next/server/chunks/5124.js +1 -0
  90. package/dist/web/standalone/.next/server/chunks/8357.js +2 -2
  91. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  92. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  93. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  94. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  95. package/dist/web/standalone/node_modules/node-pty/build/Makefile +1 -1
  96. package/package.json +6 -4
  97. package/packages/cloud-mcp-gateway/package.json +2 -2
  98. package/packages/contracts/package.json +1 -1
  99. package/packages/daemon/package.json +4 -4
  100. package/packages/gsd-agent-core/package.json +5 -5
  101. package/packages/gsd-agent-modes/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
  102. package/packages/gsd-agent-modes/dist/modes/interactive/components/assistant-message.js +21 -23
  103. package/packages/gsd-agent-modes/dist/modes/interactive/components/assistant-message.js.map +1 -1
  104. package/packages/gsd-agent-modes/dist/modes/interactive/components/transcript-design.js +1 -1
  105. package/packages/gsd-agent-modes/dist/modes/interactive/components/transcript-design.js.map +1 -1
  106. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
  107. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.js +18 -11
  108. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
  109. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-chat-render.d.ts.map +1 -1
  110. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-chat-render.js +16 -0
  111. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-chat-render.js.map +1 -1
  112. package/packages/gsd-agent-modes/package.json +7 -7
  113. package/packages/mcp-server/package.json +3 -3
  114. package/packages/native/package.json +1 -1
  115. package/packages/pi-agent-core/package.json +1 -1
  116. package/packages/pi-ai/dist/models.generated.d.ts +0 -34
  117. package/packages/pi-ai/dist/models.generated.d.ts.map +1 -1
  118. package/packages/pi-ai/dist/models.generated.js +0 -34
  119. package/packages/pi-ai/dist/models.generated.js.map +1 -1
  120. package/packages/pi-ai/package.json +1 -1
  121. package/packages/pi-coding-agent/dist/core/auth-storage.d.ts.map +1 -1
  122. package/packages/pi-coding-agent/dist/core/auth-storage.js +11 -3
  123. package/packages/pi-coding-agent/dist/core/auth-storage.js.map +1 -1
  124. package/packages/pi-coding-agent/dist/core/settings-manager.js +1 -1
  125. package/packages/pi-coding-agent/dist/core/settings-manager.js.map +1 -1
  126. package/packages/pi-coding-agent/package.json +7 -7
  127. package/packages/pi-tui/package.json +2 -2
  128. package/packages/rpc-client/package.json +2 -2
  129. package/pkg/package.json +1 -1
  130. package/src/resources/extensions/browser-tools/index.ts +39 -22
  131. package/src/resources/extensions/browser-tools/state.ts +13 -0
  132. package/src/resources/extensions/browser-tools/tests/browser-tools-unit.test.cjs +57 -0
  133. package/src/resources/extensions/browser-tools/tools/session.ts +4 -2
  134. package/src/resources/extensions/browser-tools/utils.ts +3 -3
  135. package/src/resources/extensions/claude-code-cli/stream-adapter.ts +18 -8
  136. package/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +2 -2
  137. package/src/resources/extensions/gsd/auto/contracts.ts +8 -119
  138. package/src/resources/extensions/gsd/auto/orchestrator.ts +794 -58
  139. package/src/resources/extensions/gsd/auto-prompts.ts +21 -1
  140. package/src/resources/extensions/gsd/auto-runtime-state.ts +4 -0
  141. package/src/resources/extensions/gsd/auto-tool-tracking.ts +5 -0
  142. package/src/resources/extensions/gsd/auto-unit-tool-scope.ts +33 -0
  143. package/src/resources/extensions/gsd/auto.ts +81 -500
  144. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +2 -0
  145. package/src/resources/extensions/gsd/debug-logger.ts +11 -0
  146. package/src/resources/extensions/gsd/doctor-proactive.ts +8 -2
  147. package/src/resources/extensions/gsd/markdown-renderer.ts +38 -19
  148. package/src/resources/extensions/gsd/mcp-filter.ts +7 -0
  149. package/src/resources/extensions/gsd/native-git-bridge.ts +9 -0
  150. package/src/resources/extensions/gsd/prompts/plan-slice.md +1 -1
  151. package/src/resources/extensions/gsd/schemas/parsers.ts +6 -1
  152. package/src/resources/extensions/gsd/state-reconciliation/drift/artifact-db.ts +31 -10
  153. package/src/resources/extensions/gsd/tests/artifact-db-drift-memo.test.ts +66 -0
  154. package/src/resources/extensions/gsd/tests/auto-dispatch-baseline-harness.test.ts +53 -0
  155. package/src/resources/extensions/gsd/tests/auto-orchestrator.test.ts +590 -855
  156. package/src/resources/extensions/gsd/tests/auto-paused-ui-cleanup.test.ts +38 -10
  157. package/src/resources/extensions/gsd/tests/debug-logger.test.ts +15 -0
  158. package/src/resources/extensions/gsd/tests/execute-summary-save-empty-project.test.ts +64 -1
  159. package/src/resources/extensions/gsd/tests/markdown-renderer-parse-cache.test.ts +75 -0
  160. package/src/resources/extensions/gsd/tests/orchestrator-legacy-parity.test.ts +127 -0
  161. package/src/resources/extensions/gsd/tests/parse-project-milestone-bridge.test.ts +77 -0
  162. package/src/resources/extensions/gsd/tests/plan-slice-prompt.test.ts +4 -2
  163. package/src/resources/extensions/gsd/tests/start-auto-detached.test.ts +19 -5
  164. package/src/resources/extensions/gsd/tests/worktree-safety.test.ts +24 -0
  165. package/src/resources/extensions/gsd/tests/write-gate-planning-unit.test.ts +15 -3
  166. package/src/resources/extensions/gsd/tools/workflow-tool-executors.ts +183 -21
  167. package/dist/web/standalone/.next/server/chunks/678.js +0 -2
  168. /package/dist/web/standalone/.next/static/{vAecbJ3K9eO213bAxU8Mi → h38jfi0dnRY0y3hbyBszg}/_buildManifest.js +0 -0
  169. /package/dist/web/standalone/.next/static/{vAecbJ3K9eO213bAxU8Mi → h38jfi0dnRY0y3hbyBszg}/_ssgManifest.js +0 -0
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gsd/pi-coding-agent",
3
- "version": "1.1.1-dev.154fd443",
3
+ "version": "1.1.1-dev.1854a79a",
4
4
  "description": "Coding agent CLI (vendored from earendil-works/pi)",
5
5
  "type": "module",
6
6
  "gsd": {
@@ -33,7 +33,7 @@
33
33
  "copy-assets": "node scripts/copy-assets.cjs"
34
34
  },
35
35
  "dependencies": {
36
- "@opengsd/contracts": "^1.1.1-dev.154fd443",
36
+ "@opengsd/contracts": "^1.1.1-dev.1854a79a",
37
37
  "@mariozechner/jiti": "^2.6.2",
38
38
  "@silvia-odwyer/photon-node": "0.3.4",
39
39
  "chalk": "5.6.2",
@@ -53,11 +53,11 @@
53
53
  "typebox": "1.1.38",
54
54
  "undici": "7.26.0",
55
55
  "yaml": "2.9.0",
56
- "@gsd/agent-core": "^1.1.1-dev.154fd443",
57
- "@gsd/native": "^1.1.1-dev.154fd443",
58
- "@gsd/pi-agent-core": "^1.1.1-dev.154fd443",
59
- "@gsd/pi-ai": "^1.1.1-dev.154fd443",
60
- "@gsd/pi-tui": "^1.1.1-dev.154fd443",
56
+ "@gsd/agent-core": "^1.1.1-dev.1854a79a",
57
+ "@gsd/native": "^1.1.1-dev.1854a79a",
58
+ "@gsd/pi-agent-core": "^1.1.1-dev.1854a79a",
59
+ "@gsd/pi-ai": "^1.1.1-dev.1854a79a",
60
+ "@gsd/pi-tui": "^1.1.1-dev.1854a79a",
61
61
  "@sinclair/typebox": "^0.34.41"
62
62
  },
63
63
  "devDependencies": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gsd/pi-tui",
3
- "version": "1.1.1-dev.154fd443",
3
+ "version": "1.1.1-dev.1854a79a",
4
4
  "description": "Terminal UI library (vendored from earendil-works/pi)",
5
5
  "type": "module",
6
6
  "gsd": {
@@ -21,7 +21,7 @@
21
21
  "build": "node ../../scripts/clean-package-dist.cjs && tsc -p tsconfig.json --incremental false"
22
22
  },
23
23
  "dependencies": {
24
- "@gsd/native": "^1.1.1-dev.154fd443",
24
+ "@gsd/native": "^1.1.1-dev.1854a79a",
25
25
  "get-east-asian-width": "1.6.0",
26
26
  "marked": "15.0.12",
27
27
  "@sinclair/typebox": "^0.34.41"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opengsd/rpc-client",
3
- "version": "1.1.1-dev.154fd443",
3
+ "version": "1.1.1-dev.1854a79a",
4
4
  "description": "Standalone RPC client SDK for GSD — zero internal dependencies",
5
5
  "license": "MIT",
6
6
  "gsd": {
@@ -34,7 +34,7 @@
34
34
  "test": "node --test dist/rpc-client.test.js"
35
35
  },
36
36
  "dependencies": {
37
- "@opengsd/contracts": "^1.1.1-dev.154fd443"
37
+ "@opengsd/contracts": "^1.1.1-dev.1854a79a"
38
38
  },
39
39
  "engines": {
40
40
  "node": ">=22.0.0"
package/pkg/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@glittercowboy/gsd",
3
- "version": "1.1.1-dev.154fd443",
3
+ "version": "1.1.1-dev.1854a79a",
4
4
  "piConfig": {
5
5
  "name": "gsd",
6
6
  "configDir": ".gsd"
@@ -3,6 +3,7 @@ import { importExtensionModule, type ExtensionAPI, type ExtensionContext } from
3
3
 
4
4
  import { closeManagedGsdBrowser, registerManagedGsdBrowserTools, warmUpManagedGsdBrowser } from "./engine/managed-gsd-browser.js";
5
5
  import { resolveBrowserEngineMode, type BrowserEngineMode } from "./engine/selection.js";
6
+ import { setArtifactRootForCwd } from "./state.js";
6
7
  import { detectWebApp } from "./web-app-detect.js";
7
8
 
8
9
  let legacyRegistrationPromise: Promise<void> | null = null;
@@ -119,28 +120,29 @@ async function registerLegacyBrowserTools(pi: ExtensionAPI): Promise<void> {
119
120
  formatArtifactTimestamp: utils.formatArtifactTimestamp,
120
121
  };
121
122
 
122
- navigation.registerNavigationTools(pi, deps);
123
- screenshot.registerScreenshotTools(pi, deps);
124
- interaction.registerInteractionTools(pi, deps);
125
- inspection.registerInspectionTools(pi, deps);
126
- session.registerSessionTools(pi, deps);
127
- assertions.registerAssertionTools(pi, deps);
128
- refTools.registerRefTools(pi, deps);
129
- wait.registerWaitTools(pi, deps);
130
- pages.registerPageTools(pi, deps);
131
- forms.registerFormTools(pi, deps);
132
- intent.registerIntentTools(pi, deps);
133
- pdf.registerPdfTools(pi, deps);
134
- statePersistence.registerStatePersistenceTools(pi, deps);
135
- networkMock.registerNetworkMockTools(pi, deps);
136
- device.registerDeviceTools(pi, deps);
137
- extract.registerExtractTools(pi, deps);
138
- visualDiff.registerVisualDiffTools(pi, deps);
139
- zoom.registerZoomTools(pi, deps);
140
- codegen.registerCodegenTools(pi, deps);
141
- actionCache.registerActionCacheTools(pi, deps);
142
- injectionDetection.registerInjectionDetectionTools(pi, deps);
143
- verify.registerVerifyTools(pi, deps);
123
+ const cwdScopedPi = withBrowserArtifactCwdScope(pi);
124
+ navigation.registerNavigationTools(cwdScopedPi, deps);
125
+ screenshot.registerScreenshotTools(cwdScopedPi, deps);
126
+ interaction.registerInteractionTools(cwdScopedPi, deps);
127
+ inspection.registerInspectionTools(cwdScopedPi, deps);
128
+ session.registerSessionTools(cwdScopedPi, deps);
129
+ assertions.registerAssertionTools(cwdScopedPi, deps);
130
+ refTools.registerRefTools(cwdScopedPi, deps);
131
+ wait.registerWaitTools(cwdScopedPi, deps);
132
+ pages.registerPageTools(cwdScopedPi, deps);
133
+ forms.registerFormTools(cwdScopedPi, deps);
134
+ intent.registerIntentTools(cwdScopedPi, deps);
135
+ pdf.registerPdfTools(cwdScopedPi, deps);
136
+ statePersistence.registerStatePersistenceTools(cwdScopedPi, deps);
137
+ networkMock.registerNetworkMockTools(cwdScopedPi, deps);
138
+ device.registerDeviceTools(cwdScopedPi, deps);
139
+ extract.registerExtractTools(cwdScopedPi, deps);
140
+ visualDiff.registerVisualDiffTools(cwdScopedPi, deps);
141
+ zoom.registerZoomTools(cwdScopedPi, deps);
142
+ codegen.registerCodegenTools(cwdScopedPi, deps);
143
+ actionCache.registerActionCacheTools(cwdScopedPi, deps);
144
+ injectionDetection.registerInjectionDetectionTools(cwdScopedPi, deps);
145
+ verify.registerVerifyTools(cwdScopedPi, deps);
144
146
  })().catch((error) => {
145
147
  legacyRegistrationPromise = null;
146
148
  throw error;
@@ -150,6 +152,21 @@ async function registerLegacyBrowserTools(pi: ExtensionAPI): Promise<void> {
150
152
  return legacyRegistrationPromise;
151
153
  }
152
154
 
155
+ function withBrowserArtifactCwdScope(pi: ExtensionAPI): ExtensionAPI {
156
+ return {
157
+ ...pi,
158
+ registerTool(definition) {
159
+ pi.registerTool({
160
+ ...definition,
161
+ async execute(toolCallId, params, signal, onUpdate, ctx) {
162
+ if (ctx?.cwd) setArtifactRootForCwd(ctx.cwd);
163
+ return definition.execute(toolCallId, params, signal, onUpdate, ctx);
164
+ },
165
+ });
166
+ },
167
+ };
168
+ }
169
+
153
170
  async function registerBrowserTools(pi: ExtensionAPI): Promise<void> {
154
171
  const engine = resolveBrowserEngineMode();
155
172
  if (engine === "off") return;
@@ -186,6 +186,18 @@ export interface BrowserAssertionCheckInput {
186
186
  // Mutable state variables — accessed only via get/set functions
187
187
  // ---------------------------------------------------------------------------
188
188
 
189
+ // 0. artifactRoot
190
+ let _artifactRoot = ARTIFACT_ROOT;
191
+ export function getArtifactRoot(): string { return _artifactRoot; }
192
+ export function setArtifactRootForCwd(cwd: string): string {
193
+ const newRoot = path.resolve(cwd, ".artifacts", "browser");
194
+ if (newRoot !== _artifactRoot) {
195
+ _artifactRoot = newRoot;
196
+ _sessionArtifactDir = null;
197
+ }
198
+ return _artifactRoot;
199
+ }
200
+
189
201
  // 1. browser
190
202
  let _browser: Browser | null = null;
191
203
  export function getBrowser(): Browser | null { return _browser; }
@@ -290,6 +302,7 @@ export function setHarState(h: HarState): void { _harState = h; }
290
302
  // ---------------------------------------------------------------------------
291
303
 
292
304
  export function resetAllState(): void {
305
+ _artifactRoot = ARTIFACT_ROOT;
293
306
  _browser = null;
294
307
  _context = null;
295
308
  pageRegistry.pages = [];
@@ -10,6 +10,9 @@
10
10
 
11
11
  const { describe, it, beforeEach } = require("node:test");
12
12
  const assert = require("node:assert/strict");
13
+ const { mkdtempSync, rmSync } = require("node:fs");
14
+ const { tmpdir } = require("node:os");
15
+ const { join } = require("node:path");
13
16
  const jiti = require("jiti")(__filename, { interopDefault: true, debug: false });
14
17
 
15
18
  // ---------------------------------------------------------------------------
@@ -28,9 +31,12 @@ const {
28
31
  getUrlHash,
29
32
  firstErrorLine,
30
33
  formatArtifactTimestamp,
34
+ ensureSessionArtifactDir,
31
35
  } = jiti("../utils.ts");
32
36
 
33
37
  const {
38
+ getArtifactRoot,
39
+ setArtifactRootForCwd,
34
40
  getBrowser,
35
41
  setBrowser,
36
42
  getContext,
@@ -469,6 +475,57 @@ describe("state accessors", () => {
469
475
  assert.equal(getSessionArtifactDir(), "/tmp/artifacts");
470
476
  });
471
477
 
478
+ it("uses the active tool context cwd for session artifact paths", async (t) => {
479
+ const processRoot = mkdtempSync(join(tmpdir(), "browser-tools-process-"));
480
+ const contextRoot = mkdtempSync(join(tmpdir(), "browser-tools-context-"));
481
+ const previousCwd = process.cwd();
482
+ t.after(() => {
483
+ process.chdir(previousCwd);
484
+ rmSync(processRoot, { recursive: true, force: true });
485
+ rmSync(contextRoot, { recursive: true, force: true });
486
+ });
487
+
488
+ process.chdir(processRoot);
489
+ resetAllState();
490
+ const expectedRoot = join(contextRoot, ".artifacts", "browser");
491
+ setArtifactRootForCwd(contextRoot);
492
+
493
+ const sessionDir = await ensureSessionArtifactDir();
494
+
495
+ assert.equal(getArtifactRoot(), expectedRoot);
496
+ assert.ok(
497
+ sessionDir.startsWith(`${expectedRoot}/`),
498
+ `session artifact dir should stay under context cwd: ${sessionDir}`,
499
+ );
500
+ assert.ok(
501
+ !sessionDir.startsWith(join(processRoot, ".artifacts", "browser")),
502
+ `session artifact dir must not use process cwd: ${sessionDir}`,
503
+ );
504
+ });
505
+
506
+ it("session artifact dir is recomputed when artifact root changes", async (t) => {
507
+ const root1 = mkdtempSync(join(tmpdir(), "browser-tools-root1-"));
508
+ const root2 = mkdtempSync(join(tmpdir(), "browser-tools-root2-"));
509
+ t.after(() => {
510
+ rmSync(root1, { recursive: true, force: true });
511
+ rmSync(root2, { recursive: true, force: true });
512
+ });
513
+
514
+ resetAllState();
515
+ setArtifactRootForCwd(root1);
516
+ const sessionDir1 = await ensureSessionArtifactDir();
517
+ assert.ok(sessionDir1.startsWith(join(root1, ".artifacts", "browser")));
518
+
519
+ // Change the root — cached session dir must be invalidated
520
+ setArtifactRootForCwd(root2);
521
+ const sessionDir2 = await ensureSessionArtifactDir();
522
+ assert.ok(
523
+ sessionDir2.startsWith(join(root2, ".artifacts", "browser")),
524
+ `session dir should be under new root after root change: ${sessionDir2}`,
525
+ );
526
+ assert.notEqual(sessionDir1, sessionDir2, "session dir must differ after root change");
527
+ });
528
+
472
529
  it("setCurrentRefMap/getCurrentRefMap round-trip", () => {
473
530
  assert.deepStrictEqual(getCurrentRefMap(), {});
474
531
  const refMap = { e1: { ref: "e1", tag: "button" } };
@@ -9,8 +9,8 @@ import {
9
9
  } from "../core.js";
10
10
  import type { ToolDeps } from "../state.js";
11
11
  import {
12
- ARTIFACT_ROOT,
13
12
  HAR_FILENAME,
13
+ getArtifactRoot,
14
14
  getPageRegistry,
15
15
  getActiveFrame,
16
16
  getConsoleLogs,
@@ -315,7 +315,9 @@ export function registerSessionTools(pi: ExtensionAPI, deps: ToolDeps): void {
315
315
  const { page: p } = await deps.ensureBrowser();
316
316
  const startedAt = Date.now();
317
317
  const sessionDir = await deps.ensureSessionArtifactDir();
318
- const bundleDir = path.join(ARTIFACT_ROOT, `${deps.formatArtifactTimestamp(startedAt)}-${deps.sanitizeArtifactName(params.name ?? "debug-bundle", "debug-bundle")}`);
318
+ const bundleName =
319
+ `${deps.formatArtifactTimestamp(startedAt)}-${deps.sanitizeArtifactName(params.name ?? "debug-bundle", "debug-bundle")}`;
320
+ const bundleDir = path.join(getArtifactRoot(), bundleName);
319
321
  await ensureDir(bundleDir);
320
322
  const pages = await deps.getLivePagesSnapshot();
321
323
  const actionTimeline = getActionTimeline();
@@ -21,8 +21,8 @@ import {
21
21
  registryListPages,
22
22
  } from "./core.js";
23
23
  import {
24
- ARTIFACT_ROOT,
25
24
  getActiveFrame,
25
+ getArtifactRoot,
26
26
  getActiveTraceSession,
27
27
  getConsoleLogs,
28
28
  getDialogLogs,
@@ -119,7 +119,7 @@ export async function ensureSessionArtifactDir(): Promise<string> {
119
119
  return existing;
120
120
  }
121
121
  const startedAt = ensureSessionStartedAt();
122
- const dir = path.join(ARTIFACT_ROOT, `${formatArtifactTimestamp(startedAt)}-session`);
122
+ const dir = path.join(getArtifactRoot(), `${formatArtifactTimestamp(startedAt)}-session`);
123
123
  setSessionArtifactDir(dir);
124
124
  await ensureDir(dir);
125
125
  return dir;
@@ -159,7 +159,7 @@ export function getActiveFrameMetadata() {
159
159
 
160
160
  export function getSessionArtifactMetadata() {
161
161
  return {
162
- artifactRoot: ARTIFACT_ROOT,
162
+ artifactRoot: getArtifactRoot(),
163
163
  sessionStartedAt: getSessionStartedAt(),
164
164
  sessionArtifactDir: getSessionArtifactDir(),
165
165
  activeTraceSession: getActiveTraceSession(),
@@ -38,6 +38,7 @@ import {
38
38
  discoverBrowserMcpServerName,
39
39
  discoverMcpServers,
40
40
  discoverMcpServerNames,
41
+ discoverUserMcpServerNames,
41
42
  discoverWorkflowMcpServerName,
42
43
  computeMcpDisallowedTools,
43
44
  } from "../gsd/mcp-filter.js";
@@ -1658,14 +1659,23 @@ export function buildSdkOptions(
1658
1659
  const workflowServerName = projectWorkflowServerName ?? injectedWorkflowServerName;
1659
1660
  const browserServerName = projectBrowserServerName ?? injectedBrowserServerName;
1660
1661
 
1661
- // If a default GSD MCP server is already declared in the project's .mcp.json
1662
- // or .claude/settings.json, do not inject it again via mcpServers. Passing the
1663
- // same server name from two sources causes a duplicate registration conflict
1664
- // that prevents the MCP server from loading (tools become unavailable).
1662
+ // Non-strict (non-phase) sessions load ~/.claude/settings.json via settingSources
1663
+ // including "user". If the user declared the same server names there, injecting them
1664
+ // again via mcpServers causes duplicate registration and leaves MCP tools unavailable.
1665
+ // Strict (gsdPhase) sessions set settingSources=[] so user settings are not loaded.
1666
+ const userDiscovered = !gsdPhase ? discoverUserMcpServerNames() : [];
1667
+ const allDiscovered = discovered.length > 0 || userDiscovered.length > 0
1668
+ ? [...discovered, ...userDiscovered]
1669
+ : discovered;
1670
+
1671
+ // If a default GSD MCP server is already declared in project config or in the user's
1672
+ // ~/.claude/settings.json (when that file will be loaded), do not inject it again via
1673
+ // mcpServers. Passing the same server name from two sources causes a duplicate
1674
+ // registration conflict that prevents the MCP server from loading (tools become unavailable).
1665
1675
  const workflowAlreadyInProject = projectWorkflowServerName !== undefined
1666
- || (injectedWorkflowServerName !== undefined && discovered.includes(injectedWorkflowServerName));
1676
+ || (injectedWorkflowServerName !== undefined && allDiscovered.includes(injectedWorkflowServerName));
1667
1677
  const browserAlreadyInProject = projectBrowserServerName !== undefined
1668
- || (injectedBrowserServerName !== undefined && discovered.includes(injectedBrowserServerName));
1678
+ || (injectedBrowserServerName !== undefined && allDiscovered.includes(injectedBrowserServerName));
1669
1679
  const mcpServersToInject = { ...defaultMcpServers.servers };
1670
1680
  if (workflowAlreadyInProject && injectedWorkflowServerName) delete mcpServersToInject[injectedWorkflowServerName];
1671
1681
  if (browserAlreadyInProject && injectedBrowserServerName) delete mcpServersToInject[injectedBrowserServerName];
@@ -1725,9 +1735,9 @@ export function buildSdkOptions(
1725
1735
  const sdkMcpServers = inlinePhaseMcpServers ?? filteredMcpServers;
1726
1736
  const strictMcpConfig = !!inlinePhaseMcpServers;
1727
1737
  // Strict phase configs inline the exact MCP servers GSD needs. Loading
1728
- // project/local settings at the same time can duplicate those servers and
1738
+ // Claude Code settings at the same time can duplicate those servers and
1729
1739
  // leave allowed mcp__... tools with no registered backing tool.
1730
- const settingSources = strictMcpConfig ? [] : ["project", "local"];
1740
+ const settingSources = strictMcpConfig ? [] : ["user", "project", "local"];
1731
1741
  const exactWorkflowMcpTools = resolveExactWorkflowMcpToolsForPhase(
1732
1742
  gsdPhase,
1733
1743
  workflowServerName,
@@ -796,9 +796,9 @@ describe("stream-adapter — session persistence (#2859)", () => {
796
796
  assert.equal(options.persistSession, true, "persistSession must default to true");
797
797
  });
798
798
 
799
- test("buildSdkOptions loads project and local settings so approved .mcp.json servers are active", () => {
799
+ test("buildSdkOptions loads user, project, and local settings so approved Claude Code config is active", () => {
800
800
  const options = buildSdkOptions("claude-sonnet-4-20250514", "test prompt");
801
- assert.deepEqual(options.settingSources, ["project", "local"]);
801
+ assert.deepEqual(options.settingSources, ["user", "project", "local"]);
802
802
  });
803
803
 
804
804
  test("buildSdkOptions sets model and prompt correctly", () => {
@@ -1,9 +1,14 @@
1
1
  // Project/App: gsd-pi
2
- // File Purpose: Auto Orchestration module interfaces and ADR-015 invariant adapter contracts.
2
+ // File Purpose: Auto Orchestration module public contract types.
3
+ //
4
+ // Phase 2 of #442 collapsed the nine single-implementation adapter interfaces
5
+ // (DispatchAdapter, RecoveryAdapter, StateReconciliationAdapter,
6
+ // ToolContractAdapter, WorktreeAdapter, HealthAdapter, UokGateAdapter,
7
+ // RuntimePersistenceAdapter, NotificationAdapter) and AutoOrchestratorDeps
8
+ // into AutoOrchestrator itself (auto/orchestrator.ts). Only the public result
9
+ // and lifecycle-interface types remain here.
3
10
 
4
11
  import type { GSDState } from "../types.js";
5
- import type { MinimalModelRegistry } from "../context-budget.js";
6
- import type { AutoSession } from "./session.js";
7
12
 
8
13
  export interface AutoSessionContext {
9
14
  basePath: string;
@@ -41,119 +46,3 @@ export interface AutoOrchestrationModule {
41
46
  stop(reason: string): Promise<AutoAdvanceResult>;
42
47
  getStatus(): AutoStatus;
43
48
  }
44
-
45
- export interface DispatchAdapter {
46
- decideNextUnit(input: {
47
- stateSnapshot: GSDState;
48
- /** Optional live session context, forwarded to dispatch rules that need session-derived state. */
49
- session?: AutoSession;
50
- /** Mirrors `DispatchContext.structuredQuestionsAvailable` — "true"/"false" string per the dispatch contract. */
51
- structuredQuestionsAvailable?: "true" | "false";
52
- /** Session model context window in tokens, forwarded to the budget engine. */
53
- sessionContextWindow?: number;
54
- /** Session model provider, used for provider-specific effective context windows. */
55
- sessionProvider?: string;
56
- /** Model registry for executor-model lookups inside the budget engine. */
57
- modelRegistry?: MinimalModelRegistry;
58
- }): Promise<
59
- | {
60
- kind: "blocked";
61
- reason: string;
62
- action: "pause" | "stop";
63
- }
64
- | {
65
- kind: "skipped";
66
- reason: string;
67
- }
68
- | {
69
- unitType: string;
70
- unitId: string;
71
- reason: string;
72
- preconditions: string[];
73
- }
74
- | null
75
- >;
76
- }
77
-
78
- export interface RecoveryAdapter {
79
- classifyAndRecover(input: {
80
- error: unknown;
81
- unitType?: string;
82
- unitId?: string;
83
- }): Promise<{
84
- action: "retry" | "escalate" | "stop";
85
- reason: string;
86
- }>;
87
- }
88
-
89
- export type InvariantAdapterResult =
90
- | { ok: true; reason?: string; stateSnapshot?: GSDState }
91
- | { ok: false; reason: string; stateSnapshot?: GSDState };
92
-
93
- export interface StateReconciliationAdapter {
94
- reconcileBeforeDispatch(): Promise<InvariantAdapterResult & { stateSnapshot?: GSDState }>;
95
- }
96
-
97
- export interface ToolContractAdapter {
98
- compileUnitToolContract(unitType: string, unitId: string): Promise<InvariantAdapterResult>;
99
- }
100
-
101
- export interface WorktreeAdapter {
102
- prepareForUnit(unitType: string, unitId: string): Promise<InvariantAdapterResult>;
103
- syncAfterUnit(unitType: string, unitId: string): Promise<void>;
104
- cleanupOnStop(reason: string): Promise<void>;
105
- }
106
-
107
- export type HealthGateResult =
108
- | { kind: "pass"; fixesApplied?: readonly string[] }
109
- | { kind: "fail"; reason: string; action?: "pause" | "stop" }
110
- | { kind: "threw"; error: unknown };
111
-
112
- export interface HealthAdapter {
113
- checkResourcesStale(): string | null;
114
- preAdvanceGate(): Promise<HealthGateResult>;
115
- postAdvanceRecord(result: AutoAdvanceResult): Promise<void>;
116
- }
117
-
118
- export interface UokGateInput {
119
- gateId: string;
120
- gateType: "policy" | "execution";
121
- outcome: "pass" | "fail" | "manual-attention";
122
- failureClass: "none" | "policy" | "manual-attention";
123
- rationale: string;
124
- findings?: string;
125
- milestoneId?: string;
126
- }
127
-
128
- export interface UokGateAdapter {
129
- emit(input: UokGateInput): Promise<void>;
130
- }
131
-
132
- export interface RuntimePersistenceAdapter {
133
- ensureLockOwnership(): Promise<void>;
134
- journalTransition(event: {
135
- name: string;
136
- reason?: string;
137
- unitType?: string;
138
- unitId?: string;
139
- }): Promise<void>;
140
- }
141
-
142
- export interface NotificationAdapter {
143
- notifyLifecycle(event: {
144
- name: string;
145
- detail?: string;
146
- }): Promise<void>;
147
- }
148
-
149
- export interface AutoOrchestratorDeps {
150
- stateReconciliation: StateReconciliationAdapter;
151
- dispatch: DispatchAdapter;
152
- toolContract: ToolContractAdapter;
153
- recovery: RecoveryAdapter;
154
- worktree: WorktreeAdapter;
155
- health: HealthAdapter;
156
- runtime: RuntimePersistenceAdapter;
157
- notifications: NotificationAdapter;
158
- uokGate: UokGateAdapter;
159
- }