gsd-pi 2.67.0-dev.2142d3e → 2.67.0-dev.2367d7e
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.
- package/README.md +1 -1
- package/dist/resources/extensions/claude-code-cli/stream-adapter.js +152 -70
- package/dist/resources/extensions/gsd/auto/session.js +10 -0
- package/dist/resources/extensions/gsd/auto-dispatch.js +1 -1
- package/dist/resources/extensions/gsd/auto-start.js +16 -30
- package/dist/resources/extensions/gsd/auto-worktree.js +62 -15
- package/dist/resources/extensions/gsd/auto.js +121 -59
- package/dist/resources/extensions/gsd/bootstrap/system-context.js +7 -2
- package/dist/resources/extensions/gsd/commands/catalog.js +2 -1
- package/dist/resources/extensions/gsd/commands/handlers/core.js +1 -1
- package/dist/resources/extensions/gsd/commands-mcp-status.js +43 -7
- package/dist/resources/extensions/gsd/doctor-git-checks.js +4 -4
- package/dist/resources/extensions/gsd/doctor-proactive.js +3 -3
- package/dist/resources/extensions/gsd/doctor.js +8 -4
- package/dist/resources/extensions/gsd/gsd-db.js +11 -0
- package/dist/resources/extensions/gsd/guided-flow.js +40 -31
- package/dist/resources/extensions/gsd/init-wizard.js +15 -12
- package/dist/resources/extensions/gsd/interrupted-session.js +146 -0
- package/dist/resources/extensions/gsd/mcp-project-config.js +83 -0
- package/dist/resources/extensions/gsd/state.js +7 -2
- package/dist/resources/extensions/gsd/workflow-mcp.js +90 -19
- package/dist/web/standalone/.next/BUILD_ID +1 -1
- package/dist/web/standalone/.next/app-path-routes-manifest.json +14 -14
- package/dist/web/standalone/.next/build-manifest.json +3 -3
- package/dist/web/standalone/.next/prerender-manifest.json +3 -3
- package/dist/web/standalone/.next/react-loadable-manifest.json +2 -2
- package/dist/web/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.html +1 -1
- package/dist/web/standalone/.next/server/app/index.rsc +2 -2
- package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
- package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +2 -2
- package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app-paths-manifest.json +14 -14
- package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
- package/dist/web/standalone/.next/server/middleware-react-loadable-manifest.js +1 -1
- package/dist/web/standalone/.next/server/pages/404.html +1 -1
- package/dist/web/standalone/.next/server/pages/500.html +1 -1
- package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
- package/dist/web/standalone/.next/static/chunks/2826.821e01b07d92e948.js +9 -0
- package/dist/web/standalone/.next/static/chunks/app/{page-0c485498795110d6.js → page-f1e30ab6bb269149.js} +1 -1
- package/dist/web/standalone/.next/static/chunks/{webpack-b49b09f97429b5d0.js → webpack-6e4d7e9a4f57bed4.js} +1 -1
- package/package.json +4 -2
- package/packages/mcp-server/dist/cli.d.ts +9 -0
- package/packages/mcp-server/dist/cli.d.ts.map +1 -0
- package/packages/mcp-server/dist/cli.js +58 -0
- package/packages/mcp-server/dist/cli.js.map +1 -0
- package/packages/mcp-server/dist/index.d.ts +20 -0
- package/packages/mcp-server/dist/index.d.ts.map +1 -0
- package/packages/mcp-server/dist/index.js +14 -0
- package/packages/mcp-server/dist/index.js.map +1 -0
- package/packages/mcp-server/dist/readers/captures.d.ts +25 -0
- package/packages/mcp-server/dist/readers/captures.d.ts.map +1 -0
- package/packages/mcp-server/dist/readers/captures.js +67 -0
- package/packages/mcp-server/dist/readers/captures.js.map +1 -0
- package/packages/mcp-server/dist/readers/doctor-lite.d.ts +20 -0
- package/packages/mcp-server/dist/readers/doctor-lite.d.ts.map +1 -0
- package/packages/mcp-server/dist/readers/doctor-lite.js +173 -0
- package/packages/mcp-server/dist/readers/doctor-lite.js.map +1 -0
- package/packages/mcp-server/dist/readers/index.d.ts +14 -0
- package/packages/mcp-server/dist/readers/index.d.ts.map +1 -0
- package/packages/mcp-server/dist/readers/index.js +10 -0
- package/packages/mcp-server/dist/readers/index.js.map +1 -0
- package/packages/mcp-server/dist/readers/knowledge.d.ts +18 -0
- package/packages/mcp-server/dist/readers/knowledge.d.ts.map +1 -0
- package/packages/mcp-server/dist/readers/knowledge.js +82 -0
- package/packages/mcp-server/dist/readers/knowledge.js.map +1 -0
- package/packages/mcp-server/dist/readers/metrics.d.ts +32 -0
- package/packages/mcp-server/dist/readers/metrics.d.ts.map +1 -0
- package/packages/mcp-server/dist/readers/metrics.js +74 -0
- package/packages/mcp-server/dist/readers/metrics.js.map +1 -0
- package/packages/mcp-server/dist/readers/paths.d.ts +42 -0
- package/packages/mcp-server/dist/readers/paths.d.ts.map +1 -0
- package/packages/mcp-server/dist/readers/paths.js +199 -0
- package/packages/mcp-server/dist/readers/paths.js.map +1 -0
- package/packages/mcp-server/dist/readers/roadmap.d.ts +26 -0
- package/packages/mcp-server/dist/readers/roadmap.d.ts.map +1 -0
- package/packages/mcp-server/dist/readers/roadmap.js +194 -0
- package/packages/mcp-server/dist/readers/roadmap.js.map +1 -0
- package/packages/mcp-server/dist/readers/state.d.ts +43 -0
- package/packages/mcp-server/dist/readers/state.d.ts.map +1 -0
- package/packages/mcp-server/dist/readers/state.js +184 -0
- package/packages/mcp-server/dist/readers/state.js.map +1 -0
- package/packages/mcp-server/dist/server.d.ts +28 -0
- package/packages/mcp-server/dist/server.d.ts.map +1 -0
- package/packages/mcp-server/dist/server.js +319 -0
- package/packages/mcp-server/dist/server.js.map +1 -0
- package/packages/mcp-server/dist/session-manager.d.ts +54 -0
- package/packages/mcp-server/dist/session-manager.d.ts.map +1 -0
- package/packages/mcp-server/dist/session-manager.js +284 -0
- package/packages/mcp-server/dist/session-manager.js.map +1 -0
- package/packages/mcp-server/dist/types.d.ts +61 -0
- package/packages/mcp-server/dist/types.d.ts.map +1 -0
- package/packages/mcp-server/dist/types.js +11 -0
- package/packages/mcp-server/dist/types.js.map +1 -0
- package/packages/mcp-server/dist/workflow-tools.d.ts +9 -0
- package/packages/mcp-server/dist/workflow-tools.d.ts.map +1 -0
- package/packages/mcp-server/dist/workflow-tools.js +532 -0
- package/packages/mcp-server/dist/workflow-tools.js.map +1 -0
- package/packages/mcp-server/src/workflow-tools.ts +13 -2
- package/packages/mcp-server/tsconfig.json +1 -1
- package/packages/pi-agent-core/dist/agent-loop.js +14 -6
- package/packages/pi-agent-core/dist/agent-loop.js.map +1 -1
- package/packages/pi-agent-core/src/agent-loop.test.ts +53 -0
- package/packages/pi-agent-core/src/agent-loop.ts +20 -6
- package/packages/pi-coding-agent/dist/core/contextual-tips.d.ts +43 -0
- package/packages/pi-coding-agent/dist/core/contextual-tips.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/contextual-tips.js +208 -0
- package/packages/pi-coding-agent/dist/core/contextual-tips.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/contextual-tips.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/contextual-tips.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/contextual-tips.test.js +227 -0
- package/packages/pi-coding-agent/dist/core/contextual-tips.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/index.d.ts +1 -0
- package/packages/pi-coding-agent/dist/core/index.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/index.js +1 -0
- package/packages/pi-coding-agent/dist/core/index.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/tool-execution.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/tool-execution.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/tool-execution.test.js +28 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/tool-execution.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js +17 -12
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js +19 -0
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.d.ts +4 -0
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.js +14 -0
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts +3 -0
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +15 -12
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/packages/pi-coding-agent/src/core/contextual-tips.test.ts +259 -0
- package/packages/pi-coding-agent/src/core/contextual-tips.ts +232 -0
- package/packages/pi-coding-agent/src/core/index.ts +2 -0
- package/packages/pi-coding-agent/src/modes/interactive/components/__tests__/tool-execution.test.ts +54 -0
- package/packages/pi-coding-agent/src/modes/interactive/components/tool-execution.ts +18 -12
- package/packages/pi-coding-agent/src/modes/interactive/controllers/chat-controller.ts +21 -0
- package/packages/pi-coding-agent/src/modes/interactive/controllers/input-controller.ts +19 -0
- package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +19 -15
- package/packages/rpc-client/dist/index.d.ts +10 -0
- package/packages/rpc-client/dist/index.d.ts.map +1 -0
- package/packages/rpc-client/dist/index.js +9 -0
- package/packages/rpc-client/dist/index.js.map +1 -0
- package/packages/rpc-client/dist/jsonl.d.ts +17 -0
- package/packages/rpc-client/dist/jsonl.d.ts.map +1 -0
- package/packages/rpc-client/dist/jsonl.js +54 -0
- package/packages/rpc-client/dist/jsonl.js.map +1 -0
- package/packages/rpc-client/dist/rpc-client.d.ts +259 -0
- package/packages/rpc-client/dist/rpc-client.d.ts.map +1 -0
- package/packages/rpc-client/dist/rpc-client.js +541 -0
- package/packages/rpc-client/dist/rpc-client.js.map +1 -0
- package/packages/rpc-client/dist/rpc-client.test.d.ts +2 -0
- package/packages/rpc-client/dist/rpc-client.test.d.ts.map +1 -0
- package/packages/rpc-client/dist/rpc-client.test.js +477 -0
- package/packages/rpc-client/dist/rpc-client.test.js.map +1 -0
- package/packages/rpc-client/dist/rpc-types.d.ts +566 -0
- package/packages/rpc-client/dist/rpc-types.d.ts.map +1 -0
- package/packages/rpc-client/dist/rpc-types.js +12 -0
- package/packages/rpc-client/dist/rpc-types.js.map +1 -0
- package/scripts/ensure-workspace-builds.cjs +2 -0
- package/scripts/link-workspace-packages.cjs +21 -14
- package/src/resources/extensions/claude-code-cli/stream-adapter.ts +190 -93
- package/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +89 -116
- package/src/resources/extensions/gsd/auto/session.ts +10 -0
- package/src/resources/extensions/gsd/auto-dispatch.ts +1 -1
- package/src/resources/extensions/gsd/auto-start.ts +23 -55
- package/src/resources/extensions/gsd/auto-worktree.ts +59 -15
- package/src/resources/extensions/gsd/auto.ts +133 -64
- package/src/resources/extensions/gsd/bootstrap/system-context.ts +8 -2
- package/src/resources/extensions/gsd/commands/catalog.ts +2 -1
- package/src/resources/extensions/gsd/commands/handlers/core.ts +1 -1
- package/src/resources/extensions/gsd/commands-mcp-status.ts +53 -7
- package/src/resources/extensions/gsd/doctor-git-checks.ts +4 -4
- package/src/resources/extensions/gsd/doctor-proactive.ts +3 -3
- package/src/resources/extensions/gsd/doctor.ts +9 -5
- package/src/resources/extensions/gsd/gsd-db.ts +12 -0
- package/src/resources/extensions/gsd/guided-flow.ts +42 -36
- package/src/resources/extensions/gsd/init-wizard.ts +17 -11
- package/src/resources/extensions/gsd/interrupted-session.ts +224 -0
- package/src/resources/extensions/gsd/mcp-project-config.ts +128 -0
- package/src/resources/extensions/gsd/state.ts +7 -1
- package/src/resources/extensions/gsd/tests/auto-project-root-env.test.ts +29 -0
- package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +668 -2
- package/src/resources/extensions/gsd/tests/cold-resume-db-reopen.test.ts +14 -4
- package/src/resources/extensions/gsd/tests/copy-planning-artifacts-samepath.test.ts +21 -0
- package/src/resources/extensions/gsd/tests/crash-recovery.test.ts +380 -2
- package/src/resources/extensions/gsd/tests/forensics-context-persist.test.ts +30 -0
- package/src/resources/extensions/gsd/tests/gsd-db.test.ts +12 -0
- package/src/resources/extensions/gsd/tests/guided-flow-session-isolation.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/integration/doctor-fixlevel.test.ts +52 -1
- package/src/resources/extensions/gsd/tests/integration/doctor-git.test.ts +2 -9
- package/src/resources/extensions/gsd/tests/integration/doctor-proactive.test.ts +0 -33
- package/src/resources/extensions/gsd/tests/integration/merge-cwd-restore.test.ts +169 -0
- package/src/resources/extensions/gsd/tests/interrupted-session-auto.test.ts +146 -0
- package/src/resources/extensions/gsd/tests/interrupted-session-ui.test.ts +136 -0
- package/src/resources/extensions/gsd/tests/mcp-project-config.test.ts +85 -0
- package/src/resources/extensions/gsd/tests/mcp-status.test.ts +15 -0
- package/src/resources/extensions/gsd/tests/verification-operational-gate.test.ts +11 -0
- package/src/resources/extensions/gsd/tests/workflow-mcp.test.ts +212 -13
- package/src/resources/extensions/gsd/workflow-mcp.ts +106 -19
- package/dist/web/standalone/.next/static/chunks/6502.b804e48b7919f55e.js +0 -9
- package/packages/pi-coding-agent/dist/modes/interactive/provider-auth-setup.d.ts +0 -13
- package/packages/pi-coding-agent/dist/modes/interactive/provider-auth-setup.d.ts.map +0 -1
- package/packages/pi-coding-agent/dist/modes/interactive/provider-auth-setup.js +0 -27
- package/packages/pi-coding-agent/dist/modes/interactive/provider-auth-setup.js.map +0 -1
- package/packages/pi-coding-agent/src/modes/interactive/provider-auth-setup.ts +0 -40
- package/src/resources/extensions/gsd/tests/init-bootstrap-completeness.test.ts +0 -121
- /package/dist/web/standalone/.next/static/{xR6qurkuYSvyjBjRyJLxG → WMDT_0C0XDkBKtsAI_AX4}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{xR6qurkuYSvyjBjRyJLxG → WMDT_0C0XDkBKtsAI_AX4}/_ssgManifest.js +0 -0
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* link-workspace-packages.cjs
|
|
4
4
|
*
|
|
5
|
-
* Creates node_modules/@gsd/* symlinks pointing
|
|
5
|
+
* Creates node_modules/@gsd/* and node_modules/@gsd-build/* symlinks pointing
|
|
6
|
+
* to shipped packages/* directories.
|
|
6
7
|
*
|
|
7
8
|
* During development, npm workspaces creates these automatically. But in the
|
|
8
9
|
* published tarball, workspace packages are shipped under packages/ (via the
|
|
@@ -20,27 +21,33 @@ const { resolve, join } = require('path')
|
|
|
20
21
|
|
|
21
22
|
const root = resolve(__dirname, '..')
|
|
22
23
|
const packagesDir = join(root, 'packages')
|
|
23
|
-
const
|
|
24
|
+
const scopeDirs = {
|
|
25
|
+
'@gsd': join(root, 'node_modules', '@gsd'),
|
|
26
|
+
'@gsd-build': join(root, 'node_modules', '@gsd-build'),
|
|
27
|
+
}
|
|
24
28
|
|
|
25
|
-
// Map directory names to package names
|
|
29
|
+
// Map directory names to scoped package names
|
|
26
30
|
const packageMap = {
|
|
27
|
-
'native': 'native',
|
|
28
|
-
'pi-agent-core': 'pi-agent-core',
|
|
29
|
-
'pi-ai': 'pi-ai',
|
|
30
|
-
'pi-coding-agent': 'pi-coding-agent',
|
|
31
|
-
'pi-tui': 'pi-tui',
|
|
31
|
+
'native': { scope: '@gsd', name: 'native' },
|
|
32
|
+
'pi-agent-core': { scope: '@gsd', name: 'pi-agent-core' },
|
|
33
|
+
'pi-ai': { scope: '@gsd', name: 'pi-ai' },
|
|
34
|
+
'pi-coding-agent': { scope: '@gsd', name: 'pi-coding-agent' },
|
|
35
|
+
'pi-tui': { scope: '@gsd', name: 'pi-tui' },
|
|
36
|
+
'rpc-client': { scope: '@gsd-build', name: 'rpc-client' },
|
|
32
37
|
}
|
|
33
38
|
|
|
34
|
-
|
|
35
|
-
if (!existsSync(
|
|
36
|
-
|
|
39
|
+
for (const scopeDir of Object.values(scopeDirs)) {
|
|
40
|
+
if (!existsSync(scopeDir)) {
|
|
41
|
+
mkdirSync(scopeDir, { recursive: true })
|
|
42
|
+
}
|
|
37
43
|
}
|
|
38
44
|
|
|
39
45
|
let linked = 0
|
|
40
46
|
let copied = 0
|
|
41
|
-
for (const [dir,
|
|
47
|
+
for (const [dir, pkg] of Object.entries(packageMap)) {
|
|
42
48
|
const source = join(packagesDir, dir)
|
|
43
|
-
const
|
|
49
|
+
const scopeDir = scopeDirs[pkg.scope]
|
|
50
|
+
const target = join(scopeDir, pkg.name)
|
|
44
51
|
|
|
45
52
|
if (!existsSync(source)) continue
|
|
46
53
|
|
|
@@ -50,7 +57,7 @@ for (const [dir, name] of Object.entries(packageMap)) {
|
|
|
50
57
|
const stat = lstatSync(target)
|
|
51
58
|
if (stat.isSymbolicLink()) {
|
|
52
59
|
const linkTarget = readlinkSync(target)
|
|
53
|
-
if (resolve(join(
|
|
60
|
+
if (resolve(join(scopeDir, linkTarget)) === source || linkTarget === source) {
|
|
54
61
|
continue // Already correct
|
|
55
62
|
}
|
|
56
63
|
unlinkSync(target) // Wrong target, relink
|
|
@@ -14,6 +14,7 @@ import type {
|
|
|
14
14
|
Context,
|
|
15
15
|
Model,
|
|
16
16
|
SimpleStreamOptions,
|
|
17
|
+
ToolCall,
|
|
17
18
|
} from "@gsd/pi-ai";
|
|
18
19
|
import { EventStream } from "@gsd/pi-ai";
|
|
19
20
|
import { execSync } from "node:child_process";
|
|
@@ -24,8 +25,26 @@ import type {
|
|
|
24
25
|
SDKMessage,
|
|
25
26
|
SDKPartialAssistantMessage,
|
|
26
27
|
SDKResultMessage,
|
|
28
|
+
SDKUserMessage,
|
|
27
29
|
} from "./sdk-types.js";
|
|
28
30
|
|
|
31
|
+
export interface ExternalToolResultContentBlock {
|
|
32
|
+
type: string;
|
|
33
|
+
text?: string;
|
|
34
|
+
data?: string;
|
|
35
|
+
mimeType?: string;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export interface ExternalToolResultPayload {
|
|
39
|
+
content: ExternalToolResultContentBlock[];
|
|
40
|
+
details?: Record<string, unknown>;
|
|
41
|
+
isError: boolean;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
type ToolCallWithExternalResult = ToolCall & {
|
|
45
|
+
externalResult?: ExternalToolResultPayload;
|
|
46
|
+
};
|
|
47
|
+
|
|
29
48
|
// ---------------------------------------------------------------------------
|
|
30
49
|
// Stream factory
|
|
31
50
|
// ---------------------------------------------------------------------------
|
|
@@ -153,89 +172,6 @@ export function makeStreamExhaustedErrorMessage(model: string, lastTextContent:
|
|
|
153
172
|
return message;
|
|
154
173
|
}
|
|
155
174
|
|
|
156
|
-
/**
|
|
157
|
-
* Claude Code executes its own internal tool loop inside the SDK call. The
|
|
158
|
-
* streamed and final assistant messages should therefore contain only
|
|
159
|
-
* user-facing content (text/thinking), not replayable tool blocks that GSD
|
|
160
|
-
* would render again.
|
|
161
|
-
*/
|
|
162
|
-
function isUserFacingClaudeCodeBlock(block: AssistantMessage["content"][number]): boolean {
|
|
163
|
-
return block.type === "text" || block.type === "thinking";
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
function filterUserFacingClaudeCodeContent(
|
|
167
|
-
blocks: AssistantMessage["content"],
|
|
168
|
-
): AssistantMessage["content"] {
|
|
169
|
-
return blocks.filter(isUserFacingClaudeCodeBlock);
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
function remapClaudeCodeContentIndex(
|
|
173
|
-
blocks: AssistantMessage["content"],
|
|
174
|
-
contentIndex: number,
|
|
175
|
-
): number {
|
|
176
|
-
let visibleCount = 0;
|
|
177
|
-
for (let i = 0; i <= contentIndex && i < blocks.length; i++) {
|
|
178
|
-
if (isUserFacingClaudeCodeBlock(blocks[i]!)) visibleCount++;
|
|
179
|
-
}
|
|
180
|
-
return Math.max(0, visibleCount - 1);
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
function sanitizeClaudeCodePartial(
|
|
184
|
-
partial: AssistantMessage,
|
|
185
|
-
): AssistantMessage {
|
|
186
|
-
return {
|
|
187
|
-
...partial,
|
|
188
|
-
content: filterUserFacingClaudeCodeContent(partial.content),
|
|
189
|
-
};
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
export function sanitizeClaudeCodeStreamingEvent(
|
|
193
|
-
event: AssistantMessageEvent,
|
|
194
|
-
): AssistantMessageEvent | null {
|
|
195
|
-
switch (event.type) {
|
|
196
|
-
case "toolcall_start":
|
|
197
|
-
case "toolcall_delta":
|
|
198
|
-
case "toolcall_end":
|
|
199
|
-
case "server_tool_use":
|
|
200
|
-
case "web_search_result":
|
|
201
|
-
return null;
|
|
202
|
-
case "text_start":
|
|
203
|
-
case "text_delta":
|
|
204
|
-
case "text_end":
|
|
205
|
-
case "thinking_start":
|
|
206
|
-
case "thinking_delta":
|
|
207
|
-
case "thinking_end":
|
|
208
|
-
return {
|
|
209
|
-
...event,
|
|
210
|
-
contentIndex: remapClaudeCodeContentIndex(event.partial.content, event.contentIndex),
|
|
211
|
-
partial: sanitizeClaudeCodePartial(event.partial),
|
|
212
|
-
};
|
|
213
|
-
default:
|
|
214
|
-
return event;
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
export function buildFinalClaudeCodeContent(
|
|
219
|
-
blocks: AssistantMessage["content"],
|
|
220
|
-
lastThinkingContent: string,
|
|
221
|
-
lastTextContent: string,
|
|
222
|
-
resultText?: string,
|
|
223
|
-
): AssistantMessage["content"] {
|
|
224
|
-
const finalContent = filterUserFacingClaudeCodeContent(blocks);
|
|
225
|
-
if (finalContent.length > 0) return finalContent;
|
|
226
|
-
|
|
227
|
-
if (lastThinkingContent) {
|
|
228
|
-
finalContent.push({ type: "thinking", thinking: lastThinkingContent });
|
|
229
|
-
}
|
|
230
|
-
if (lastTextContent) {
|
|
231
|
-
finalContent.push({ type: "text", text: lastTextContent });
|
|
232
|
-
}
|
|
233
|
-
if (finalContent.length === 0 && resultText) {
|
|
234
|
-
finalContent.push({ type: "text", text: resultText });
|
|
235
|
-
}
|
|
236
|
-
return finalContent;
|
|
237
|
-
}
|
|
238
|
-
|
|
239
175
|
// ---------------------------------------------------------------------------
|
|
240
176
|
// SDK options builder
|
|
241
177
|
// ---------------------------------------------------------------------------
|
|
@@ -263,6 +199,110 @@ export function buildSdkOptions(modelId: string, prompt: string): Record<string,
|
|
|
263
199
|
};
|
|
264
200
|
}
|
|
265
201
|
|
|
202
|
+
function normalizeToolResultContent(content: unknown): ExternalToolResultContentBlock[] {
|
|
203
|
+
if (typeof content === "string") {
|
|
204
|
+
return [{ type: "text", text: content }];
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
if (!Array.isArray(content)) {
|
|
208
|
+
if (content == null) return [{ type: "text", text: "" }];
|
|
209
|
+
return [{ type: "text", text: JSON.stringify(content) }];
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
const blocks: ExternalToolResultContentBlock[] = [];
|
|
213
|
+
|
|
214
|
+
for (const item of content) {
|
|
215
|
+
if (typeof item === "string") {
|
|
216
|
+
blocks.push({ type: "text", text: item });
|
|
217
|
+
continue;
|
|
218
|
+
}
|
|
219
|
+
if (!item || typeof item !== "object") {
|
|
220
|
+
blocks.push({ type: "text", text: String(item) });
|
|
221
|
+
continue;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
const block = item as Record<string, unknown>;
|
|
225
|
+
if (block.type === "text") {
|
|
226
|
+
blocks.push({ type: "text", text: typeof block.text === "string" ? block.text : "" });
|
|
227
|
+
continue;
|
|
228
|
+
}
|
|
229
|
+
if (
|
|
230
|
+
block.type === "image"
|
|
231
|
+
&& typeof block.data === "string"
|
|
232
|
+
&& typeof block.mimeType === "string"
|
|
233
|
+
) {
|
|
234
|
+
blocks.push({ type: "image", data: block.data, mimeType: block.mimeType });
|
|
235
|
+
continue;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
blocks.push({ type: "text", text: JSON.stringify(block) });
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
return blocks.length > 0 ? blocks : [{ type: "text", text: "" }];
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
export function extractToolResultsFromSdkUserMessage(message: SDKUserMessage): Array<{
|
|
245
|
+
toolUseId: string;
|
|
246
|
+
result: ExternalToolResultPayload;
|
|
247
|
+
}> {
|
|
248
|
+
const extracted: Array<{ toolUseId: string; result: ExternalToolResultPayload }> = [];
|
|
249
|
+
const seen = new Set<string>();
|
|
250
|
+
const rawMessage = message.message as Record<string, unknown> | null | undefined;
|
|
251
|
+
const content = Array.isArray(rawMessage?.content) ? rawMessage.content : [];
|
|
252
|
+
|
|
253
|
+
for (const item of content) {
|
|
254
|
+
if (!item || typeof item !== "object") continue;
|
|
255
|
+
const block = item as Record<string, unknown>;
|
|
256
|
+
const type = typeof block.type === "string" ? block.type : "";
|
|
257
|
+
if (type !== "tool_result" && type !== "mcp_tool_result") continue;
|
|
258
|
+
|
|
259
|
+
const toolUseId = typeof block.tool_use_id === "string" ? block.tool_use_id : "";
|
|
260
|
+
if (!toolUseId || seen.has(toolUseId)) continue;
|
|
261
|
+
seen.add(toolUseId);
|
|
262
|
+
|
|
263
|
+
extracted.push({
|
|
264
|
+
toolUseId,
|
|
265
|
+
result: {
|
|
266
|
+
content: normalizeToolResultContent(block.content),
|
|
267
|
+
details: {},
|
|
268
|
+
isError: block.is_error === true,
|
|
269
|
+
},
|
|
270
|
+
});
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
if (extracted.length === 0) {
|
|
274
|
+
const fallback = message.tool_use_result;
|
|
275
|
+
if (fallback && typeof fallback === "object") {
|
|
276
|
+
const toolResult = fallback as Record<string, unknown>;
|
|
277
|
+
const toolUseId = typeof toolResult.tool_use_id === "string" ? toolResult.tool_use_id : "";
|
|
278
|
+
if (toolUseId) {
|
|
279
|
+
extracted.push({
|
|
280
|
+
toolUseId,
|
|
281
|
+
result: {
|
|
282
|
+
content: normalizeToolResultContent(toolResult.content),
|
|
283
|
+
details: {},
|
|
284
|
+
isError: toolResult.is_error === true,
|
|
285
|
+
},
|
|
286
|
+
});
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
return extracted;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
function attachExternalResultsToToolCalls(
|
|
295
|
+
toolCalls: AssistantMessage["content"],
|
|
296
|
+
toolResultsById: ReadonlyMap<string, ExternalToolResultPayload>,
|
|
297
|
+
): void {
|
|
298
|
+
for (const block of toolCalls) {
|
|
299
|
+
if (block.type !== "toolCall") continue;
|
|
300
|
+
const externalResult = toolResultsById.get(block.id);
|
|
301
|
+
if (!externalResult) continue;
|
|
302
|
+
(block as ToolCallWithExternalResult).externalResult = externalResult;
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
|
|
266
306
|
// ---------------------------------------------------------------------------
|
|
267
307
|
// streamSimple implementation
|
|
268
308
|
// ---------------------------------------------------------------------------
|
|
@@ -297,6 +337,10 @@ async function pumpSdkMessages(
|
|
|
297
337
|
/** Track the last text content seen across all assistant turns for the final message. */
|
|
298
338
|
let lastTextContent = "";
|
|
299
339
|
let lastThinkingContent = "";
|
|
340
|
+
/** Collect tool calls from intermediate SDK turns for tool_execution events. */
|
|
341
|
+
const intermediateToolCalls: AssistantMessage["content"] = [];
|
|
342
|
+
/** Preserve real external tool results from Claude Code's synthetic user messages. */
|
|
343
|
+
const toolResultsById = new Map<string, ExternalToolResultPayload>();
|
|
300
344
|
|
|
301
345
|
try {
|
|
302
346
|
// Dynamic import — the SDK is an optional dependency.
|
|
@@ -365,10 +409,9 @@ async function pumpSdkMessages(
|
|
|
365
409
|
if (!builder) break;
|
|
366
410
|
|
|
367
411
|
const assistantEvent = builder.handleEvent(event);
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
if (sanitizedEvent) stream.push(sanitizedEvent);
|
|
412
|
+
if (assistantEvent) {
|
|
413
|
+
stream.push(assistantEvent);
|
|
414
|
+
}
|
|
372
415
|
break;
|
|
373
416
|
}
|
|
374
417
|
|
|
@@ -396,9 +439,39 @@ async function pumpSdkMessages(
|
|
|
396
439
|
lastTextContent = block.text;
|
|
397
440
|
} else if (block.type === "thinking" && block.thinking) {
|
|
398
441
|
lastThinkingContent = block.thinking;
|
|
442
|
+
} else if (block.type === "toolCall") {
|
|
443
|
+
// Collect tool calls for externalToolExecution rendering
|
|
444
|
+
intermediateToolCalls.push(block);
|
|
399
445
|
}
|
|
400
446
|
}
|
|
401
447
|
}
|
|
448
|
+
|
|
449
|
+
// Extract tool results from the SDK's synthetic user message
|
|
450
|
+
// and attach to corresponding tool call blocks immediately.
|
|
451
|
+
for (const { toolUseId, result } of extractToolResultsFromSdkUserMessage(msg as SDKUserMessage)) {
|
|
452
|
+
toolResultsById.set(toolUseId, result);
|
|
453
|
+
}
|
|
454
|
+
attachExternalResultsToToolCalls(intermediateToolCalls, toolResultsById);
|
|
455
|
+
|
|
456
|
+
// Push a synthetic toolcall_end for each tool call from this turn
|
|
457
|
+
// so the TUI can render tool results in real-time during the SDK
|
|
458
|
+
// session instead of waiting until the entire session completes.
|
|
459
|
+
if (builder) {
|
|
460
|
+
for (const block of builder.message.content) {
|
|
461
|
+
if (block.type !== "toolCall") continue;
|
|
462
|
+
const extResult = (block as ToolCallWithExternalResult).externalResult;
|
|
463
|
+
if (!extResult) continue;
|
|
464
|
+
// Push a toolcall_end with result attached so the chat-controller
|
|
465
|
+
// can call updateResult on the pending ToolExecutionComponent.
|
|
466
|
+
stream.push({
|
|
467
|
+
type: "toolcall_end",
|
|
468
|
+
contentIndex: builder.message.content.indexOf(block),
|
|
469
|
+
toolCall: block,
|
|
470
|
+
partial: builder.message,
|
|
471
|
+
});
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
|
|
402
475
|
builder = null;
|
|
403
476
|
break;
|
|
404
477
|
}
|
|
@@ -406,12 +479,36 @@ async function pumpSdkMessages(
|
|
|
406
479
|
// -- Result (terminal) --
|
|
407
480
|
case "result": {
|
|
408
481
|
const result = msg as SDKResultMessage;
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
482
|
+
|
|
483
|
+
// Build final message. Include intermediate tool calls so the
|
|
484
|
+
// agent loop's externalToolExecution path emits tool_execution
|
|
485
|
+
// events for proper TUI rendering, followed by the text response.
|
|
486
|
+
const finalContent: AssistantMessage["content"] = [];
|
|
487
|
+
|
|
488
|
+
// Add tool calls from intermediate turns first (renders above text)
|
|
489
|
+
attachExternalResultsToToolCalls(intermediateToolCalls, toolResultsById);
|
|
490
|
+
finalContent.push(...intermediateToolCalls);
|
|
491
|
+
|
|
492
|
+
// Add text/thinking from the last turn
|
|
493
|
+
if (builder && builder.message.content.length > 0) {
|
|
494
|
+
for (const block of builder.message.content) {
|
|
495
|
+
if (block.type === "text" || block.type === "thinking") {
|
|
496
|
+
finalContent.push(block);
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
} else {
|
|
500
|
+
if (lastThinkingContent) {
|
|
501
|
+
finalContent.push({ type: "thinking", thinking: lastThinkingContent });
|
|
502
|
+
}
|
|
503
|
+
if (lastTextContent) {
|
|
504
|
+
finalContent.push({ type: "text", text: lastTextContent });
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
// Fallback: use the SDK's result text if we have no content
|
|
509
|
+
if (finalContent.length === 0 && result.subtype === "success" && result.result) {
|
|
510
|
+
finalContent.push({ type: "text", text: result.result });
|
|
511
|
+
}
|
|
415
512
|
|
|
416
513
|
const finalMessage: AssistantMessage = {
|
|
417
514
|
role: "assistant",
|
|
@@ -4,15 +4,15 @@ import { mkdirSync, mkdtempSync, realpathSync, rmSync, writeFileSync } from "nod
|
|
|
4
4
|
import { join, resolve } from "node:path";
|
|
5
5
|
import { tmpdir } from "node:os";
|
|
6
6
|
import {
|
|
7
|
+
makeStreamExhaustedErrorMessage,
|
|
7
8
|
buildPromptFromContext,
|
|
8
|
-
buildFinalClaudeCodeContent,
|
|
9
9
|
buildSdkOptions,
|
|
10
|
+
extractToolResultsFromSdkUserMessage,
|
|
10
11
|
getClaudeLookupCommand,
|
|
11
|
-
makeStreamExhaustedErrorMessage,
|
|
12
12
|
parseClaudeLookupOutput,
|
|
13
|
-
sanitizeClaudeCodeStreamingEvent,
|
|
14
13
|
} from "../stream-adapter.ts";
|
|
15
|
-
import type {
|
|
14
|
+
import type { Context, Message } from "@gsd/pi-ai";
|
|
15
|
+
import type { SDKUserMessage } from "../sdk-types.ts";
|
|
16
16
|
|
|
17
17
|
// ---------------------------------------------------------------------------
|
|
18
18
|
// Existing tests — exhausted stream fallback (#2575)
|
|
@@ -108,6 +108,65 @@ describe("stream-adapter — full context prompt (#2859)", () => {
|
|
|
108
108
|
});
|
|
109
109
|
});
|
|
110
110
|
|
|
111
|
+
describe("stream-adapter — Claude Code external tool results", () => {
|
|
112
|
+
test("extractToolResultsFromSdkUserMessage maps tool_result content to tool payloads", () => {
|
|
113
|
+
const message: SDKUserMessage = {
|
|
114
|
+
type: "user",
|
|
115
|
+
session_id: "sess-1",
|
|
116
|
+
parent_tool_use_id: "tool-bash-1",
|
|
117
|
+
message: {
|
|
118
|
+
role: "user",
|
|
119
|
+
content: [
|
|
120
|
+
{
|
|
121
|
+
type: "tool_result",
|
|
122
|
+
tool_use_id: "tool-bash-1",
|
|
123
|
+
content: "line 1\nline 2",
|
|
124
|
+
is_error: false,
|
|
125
|
+
},
|
|
126
|
+
],
|
|
127
|
+
},
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
const results = extractToolResultsFromSdkUserMessage(message);
|
|
131
|
+
assert.deepEqual(results, [
|
|
132
|
+
{
|
|
133
|
+
toolUseId: "tool-bash-1",
|
|
134
|
+
result: {
|
|
135
|
+
content: [{ type: "text", text: "line 1\nline 2" }],
|
|
136
|
+
details: {},
|
|
137
|
+
isError: false,
|
|
138
|
+
},
|
|
139
|
+
},
|
|
140
|
+
]);
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
test("extractToolResultsFromSdkUserMessage falls back to tool_use_result", () => {
|
|
144
|
+
const message: SDKUserMessage = {
|
|
145
|
+
type: "user",
|
|
146
|
+
session_id: "sess-1",
|
|
147
|
+
parent_tool_use_id: "tool-read-1",
|
|
148
|
+
message: { role: "user", content: [] },
|
|
149
|
+
tool_use_result: {
|
|
150
|
+
tool_use_id: "tool-read-1",
|
|
151
|
+
content: "file contents",
|
|
152
|
+
is_error: true,
|
|
153
|
+
},
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
const results = extractToolResultsFromSdkUserMessage(message);
|
|
157
|
+
assert.deepEqual(results, [
|
|
158
|
+
{
|
|
159
|
+
toolUseId: "tool-read-1",
|
|
160
|
+
result: {
|
|
161
|
+
content: [{ type: "text", text: "file contents" }],
|
|
162
|
+
details: {},
|
|
163
|
+
isError: true,
|
|
164
|
+
},
|
|
165
|
+
},
|
|
166
|
+
]);
|
|
167
|
+
});
|
|
168
|
+
});
|
|
169
|
+
|
|
111
170
|
describe("stream-adapter — session persistence (#2859)", () => {
|
|
112
171
|
test("buildSdkOptions enables persistSession by default", () => {
|
|
113
172
|
const options = buildSdkOptions("claude-sonnet-4-20250514", "test prompt");
|
|
@@ -149,18 +208,15 @@ describe("stream-adapter — session persistence (#2859)", () => {
|
|
|
149
208
|
process.env.GSD_WORKFLOW_MCP_CWD = "/tmp/project";
|
|
150
209
|
|
|
151
210
|
const options = buildSdkOptions("claude-sonnet-4-20250514", "test");
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
cwd: "/tmp/project",
|
|
162
|
-
},
|
|
163
|
-
});
|
|
211
|
+
const mcpServers = options.mcpServers as Record<string, any>;
|
|
212
|
+
assert.ok(mcpServers?.["gsd-workflow"], "expected gsd-workflow server config");
|
|
213
|
+
const srv = mcpServers["gsd-workflow"];
|
|
214
|
+
assert.equal(srv.command, "node");
|
|
215
|
+
assert.deepEqual(srv.args, ["packages/mcp-server/dist/cli.js"]);
|
|
216
|
+
assert.equal(srv.cwd, "/tmp/project");
|
|
217
|
+
assert.equal(srv.env.GSD_CLI_PATH, "/tmp/gsd");
|
|
218
|
+
assert.equal(srv.env.GSD_PERSIST_WRITE_GATE_STATE, "1");
|
|
219
|
+
assert.equal(srv.env.GSD_WORKFLOW_PROJECT_ROOT, "/tmp/project");
|
|
164
220
|
} finally {
|
|
165
221
|
process.env.GSD_WORKFLOW_MCP_COMMAND = prev.GSD_WORKFLOW_MCP_COMMAND;
|
|
166
222
|
process.env.GSD_WORKFLOW_MCP_NAME = prev.GSD_WORKFLOW_MCP_NAME;
|
|
@@ -170,7 +226,7 @@ describe("stream-adapter — session persistence (#2859)", () => {
|
|
|
170
226
|
}
|
|
171
227
|
});
|
|
172
228
|
|
|
173
|
-
test("buildSdkOptions
|
|
229
|
+
test("buildSdkOptions auto-discovers bundled MCP server even without env hints", () => {
|
|
174
230
|
const prev = {
|
|
175
231
|
GSD_WORKFLOW_MCP_COMMAND: process.env.GSD_WORKFLOW_MCP_COMMAND,
|
|
176
232
|
GSD_WORKFLOW_MCP_NAME: process.env.GSD_WORKFLOW_MCP_NAME,
|
|
@@ -190,7 +246,13 @@ describe("stream-adapter — session persistence (#2859)", () => {
|
|
|
190
246
|
process.chdir(emptyDir);
|
|
191
247
|
const options = buildSdkOptions("claude-sonnet-4-20250514", "test");
|
|
192
248
|
process.chdir(originalCwd);
|
|
193
|
-
|
|
249
|
+
// The bundled CLI may or may not be discoverable depending on
|
|
250
|
+
// whether the build output exists relative to import.meta.url.
|
|
251
|
+
// Either outcome is valid — the key invariant is no crash.
|
|
252
|
+
const mcpServers = (options as any).mcpServers;
|
|
253
|
+
if (mcpServers) {
|
|
254
|
+
assert.ok(mcpServers["gsd-workflow"], "if present, must be gsd-workflow");
|
|
255
|
+
}
|
|
194
256
|
rmSync(emptyDir, { recursive: true, force: true });
|
|
195
257
|
} finally {
|
|
196
258
|
process.env.GSD_WORKFLOW_MCP_COMMAND = prev.GSD_WORKFLOW_MCP_COMMAND;
|
|
@@ -227,18 +289,15 @@ describe("stream-adapter — session persistence (#2859)", () => {
|
|
|
227
289
|
const resolvedRepoDir = realpathSync(repoDir);
|
|
228
290
|
|
|
229
291
|
const options = buildSdkOptions("claude-sonnet-4-20250514", "test");
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
cwd: resolvedRepoDir,
|
|
240
|
-
},
|
|
241
|
-
});
|
|
292
|
+
const mcpServers = options.mcpServers as Record<string, any>;
|
|
293
|
+
assert.ok(mcpServers?.["gsd-workflow"], "expected gsd-workflow server config");
|
|
294
|
+
const srv = mcpServers["gsd-workflow"];
|
|
295
|
+
assert.equal(srv.command, process.execPath);
|
|
296
|
+
assert.deepEqual(srv.args, [realpathSync(resolve(repoDir, "packages", "mcp-server", "dist", "cli.js"))]);
|
|
297
|
+
assert.equal(srv.cwd, resolvedRepoDir);
|
|
298
|
+
assert.equal(srv.env.GSD_CLI_PATH, "/tmp/gsd");
|
|
299
|
+
assert.equal(srv.env.GSD_PERSIST_WRITE_GATE_STATE, "1");
|
|
300
|
+
assert.equal(srv.env.GSD_WORKFLOW_PROJECT_ROOT, resolvedRepoDir);
|
|
242
301
|
} finally {
|
|
243
302
|
process.chdir(originalCwd);
|
|
244
303
|
rmSync(repoDir, { recursive: true, force: true });
|
|
@@ -252,92 +311,6 @@ describe("stream-adapter — session persistence (#2859)", () => {
|
|
|
252
311
|
});
|
|
253
312
|
});
|
|
254
313
|
|
|
255
|
-
describe("stream-adapter — final content filtering (#3861)", () => {
|
|
256
|
-
test("buildFinalClaudeCodeContent strips intermediate tool calls from the final assistant message", () => {
|
|
257
|
-
const finalContent = buildFinalClaudeCodeContent(
|
|
258
|
-
[
|
|
259
|
-
{ type: "toolCall", id: "tc_1", name: "Read", arguments: {} },
|
|
260
|
-
{ type: "thinking", thinking: "Planning next step" },
|
|
261
|
-
{ type: "text", text: "Done." },
|
|
262
|
-
] as any,
|
|
263
|
-
"",
|
|
264
|
-
"",
|
|
265
|
-
);
|
|
266
|
-
|
|
267
|
-
assert.deepEqual(finalContent, [
|
|
268
|
-
{ type: "thinking", thinking: "Planning next step" },
|
|
269
|
-
{ type: "text", text: "Done." },
|
|
270
|
-
]);
|
|
271
|
-
});
|
|
272
|
-
|
|
273
|
-
test("buildFinalClaudeCodeContent falls back to cached text when the final turn only had tool calls", () => {
|
|
274
|
-
const finalContent = buildFinalClaudeCodeContent(
|
|
275
|
-
[
|
|
276
|
-
{ type: "toolCall", id: "tc_2", name: "Edit", arguments: { file_path: "app.ts" } },
|
|
277
|
-
] as any,
|
|
278
|
-
"",
|
|
279
|
-
"User-facing answer",
|
|
280
|
-
);
|
|
281
|
-
|
|
282
|
-
assert.deepEqual(finalContent, [{ type: "text", text: "User-facing answer" }]);
|
|
283
|
-
});
|
|
284
|
-
});
|
|
285
|
-
|
|
286
|
-
describe("stream-adapter — streaming content filtering follow-up (#3867)", () => {
|
|
287
|
-
function makePartial(content: AssistantMessage["content"]): AssistantMessage {
|
|
288
|
-
return {
|
|
289
|
-
role: "assistant",
|
|
290
|
-
content,
|
|
291
|
-
api: "anthropic-messages",
|
|
292
|
-
provider: "claude-code",
|
|
293
|
-
model: "claude-sonnet-4-20250514",
|
|
294
|
-
usage: {
|
|
295
|
-
input: 0,
|
|
296
|
-
output: 0,
|
|
297
|
-
cacheRead: 0,
|
|
298
|
-
cacheWrite: 0,
|
|
299
|
-
totalTokens: 0,
|
|
300
|
-
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },
|
|
301
|
-
},
|
|
302
|
-
stopReason: "stop",
|
|
303
|
-
timestamp: Date.now(),
|
|
304
|
-
};
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
test("sanitizeClaudeCodeStreamingEvent strips tool calls from streamed partials and remaps contentIndex", () => {
|
|
308
|
-
const event = sanitizeClaudeCodeStreamingEvent({
|
|
309
|
-
type: "text_delta",
|
|
310
|
-
contentIndex: 2,
|
|
311
|
-
delta: "Done.",
|
|
312
|
-
partial: makePartial([
|
|
313
|
-
{ type: "toolCall", id: "tc_1", name: "ToolSearch", arguments: {} },
|
|
314
|
-
{ type: "thinking", thinking: "Planning next step" },
|
|
315
|
-
{ type: "text", text: "Done." },
|
|
316
|
-
] as any),
|
|
317
|
-
});
|
|
318
|
-
|
|
319
|
-
assert.ok(event, "text events should still be forwarded");
|
|
320
|
-
assert.equal(event!.type, "text_delta");
|
|
321
|
-
assert.equal((event! as any).contentIndex, 1);
|
|
322
|
-
assert.deepEqual((event! as any).partial.content, [
|
|
323
|
-
{ type: "thinking", thinking: "Planning next step" },
|
|
324
|
-
{ type: "text", text: "Done." },
|
|
325
|
-
]);
|
|
326
|
-
});
|
|
327
|
-
|
|
328
|
-
test("sanitizeClaudeCodeStreamingEvent suppresses internal tool streaming events entirely", () => {
|
|
329
|
-
const event = sanitizeClaudeCodeStreamingEvent({
|
|
330
|
-
type: "toolcall_start",
|
|
331
|
-
contentIndex: 0,
|
|
332
|
-
partial: makePartial([
|
|
333
|
-
{ type: "toolCall", id: "tc_1", name: "Bash", arguments: {} },
|
|
334
|
-
] as any),
|
|
335
|
-
});
|
|
336
|
-
|
|
337
|
-
assert.equal(event, null);
|
|
338
|
-
});
|
|
339
|
-
});
|
|
340
|
-
|
|
341
314
|
describe("stream-adapter — Windows Claude path lookup (#3770)", () => {
|
|
342
315
|
test("getClaudeLookupCommand uses where on Windows", () => {
|
|
343
316
|
assert.equal(getClaudeLookupCommand("win32"), "where claude");
|