gsd-pi 2.46.1 → 2.47.0
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 +46 -29
- package/dist/resources/extensions/claude-code-cli/index.js +25 -0
- package/dist/resources/extensions/claude-code-cli/models.js +40 -0
- package/dist/resources/extensions/claude-code-cli/package.json +11 -0
- package/dist/resources/extensions/claude-code-cli/partial-builder.js +223 -0
- package/dist/resources/extensions/claude-code-cli/readiness.js +26 -0
- package/dist/resources/extensions/claude-code-cli/sdk-types.js +8 -0
- package/dist/resources/extensions/claude-code-cli/stream-adapter.js +309 -0
- package/dist/resources/extensions/gsd/auto-start.js +9 -8
- package/dist/resources/extensions/gsd/prompts/complete-slice.md +1 -1
- package/dist/resources/extensions/gsd/prompts/guided-plan-milestone.md +2 -2
- package/dist/resources/extensions/gsd/prompts/plan-milestone.md +2 -2
- package/dist/resources/extensions/gsd/prompts/plan-slice.md +1 -1
- package/dist/resources/extensions/gsd/prompts/research-milestone.md +2 -2
- package/dist/resources/extensions/gsd/prompts/run-uat.md +2 -2
- package/dist/resources/extensions/gsd/repo-identity.js +5 -2
- package/dist/resources/extensions/gsd/state.js +29 -2
- package/dist/resources/extensions/gsd/workflow-events.js +1 -1
- package/dist/web/standalone/.next/BUILD_ID +1 -1
- package/dist/web/standalone/.next/app-path-routes-manifest.json +9 -9
- package/dist/web/standalone/.next/build-manifest.json +2 -2
- package/dist/web/standalone/.next/prerender-manifest.json +3 -3
- package/dist/web/standalone/.next/server/app/_global-error.html +2 -2
- 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.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 +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
- 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-paths-manifest.json +9 -9
- package/dist/web/standalone/.next/server/pages/404.html +1 -1
- package/dist/web/standalone/.next/server/pages/500.html +2 -2
- package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
- package/package.json +3 -1
- package/packages/pi-agent-core/dist/agent-loop.js +26 -1
- package/packages/pi-agent-core/dist/agent-loop.js.map +1 -1
- package/packages/pi-agent-core/dist/agent.d.ts +7 -0
- package/packages/pi-agent-core/dist/agent.d.ts.map +1 -1
- package/packages/pi-agent-core/dist/agent.js +2 -0
- package/packages/pi-agent-core/dist/agent.js.map +1 -1
- package/packages/pi-agent-core/dist/types.d.ts +9 -0
- package/packages/pi-agent-core/dist/types.d.ts.map +1 -1
- package/packages/pi-agent-core/dist/types.js.map +1 -1
- package/packages/pi-agent-core/src/agent-loop.ts +25 -1
- package/packages/pi-agent-core/src/agent.ts +10 -0
- package/packages/pi-agent-core/src/types.ts +10 -0
- package/packages/pi-coding-agent/dist/core/auth-storage.test.js +27 -2
- package/packages/pi-coding-agent/dist/core/auth-storage.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/sdk.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/sdk.js +1 -0
- package/packages/pi-coding-agent/dist/core/sdk.js.map +1 -1
- package/packages/pi-coding-agent/package.json +1 -1
- package/packages/pi-coding-agent/src/core/auth-storage.test.ts +27 -2
- package/packages/pi-coding-agent/src/core/sdk.ts +1 -0
- package/pkg/package.json +1 -1
- package/src/resources/extensions/claude-code-cli/index.ts +28 -0
- package/src/resources/extensions/claude-code-cli/models.ts +42 -0
- package/src/resources/extensions/claude-code-cli/package.json +11 -0
- package/src/resources/extensions/claude-code-cli/partial-builder.ts +258 -0
- package/src/resources/extensions/claude-code-cli/readiness.ts +30 -0
- package/src/resources/extensions/claude-code-cli/sdk-types.ts +149 -0
- package/src/resources/extensions/claude-code-cli/stream-adapter.ts +370 -0
- package/src/resources/extensions/gsd/auto-start.ts +8 -7
- package/src/resources/extensions/gsd/prompts/complete-slice.md +1 -1
- package/src/resources/extensions/gsd/prompts/guided-plan-milestone.md +2 -2
- package/src/resources/extensions/gsd/prompts/plan-milestone.md +2 -2
- package/src/resources/extensions/gsd/prompts/plan-slice.md +1 -1
- package/src/resources/extensions/gsd/prompts/research-milestone.md +2 -2
- package/src/resources/extensions/gsd/prompts/run-uat.md +2 -2
- package/src/resources/extensions/gsd/repo-identity.ts +5 -2
- package/src/resources/extensions/gsd/state.ts +33 -1
- package/src/resources/extensions/gsd/tests/inherited-repo-home-dir.test.ts +70 -0
- package/src/resources/extensions/gsd/tests/plan-slice-prompt.test.ts +40 -0
- package/src/resources/extensions/gsd/tests/run-uat.test.ts +25 -0
- package/src/resources/extensions/gsd/workflow-events.ts +1 -1
- /package/dist/web/standalone/.next/static/{P4nF4UcdATjrbNMBH_Ulh → VPcLnRF4BL8VoJEilBwlB}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{P4nF4UcdATjrbNMBH_Ulh → VPcLnRF4BL8VoJEilBwlB}/_ssgManifest.js +0 -0
package/README.md
CHANGED
|
@@ -25,40 +25,55 @@ One command. Walk away. Come back to a built project with clean git history.
|
|
|
25
25
|
|
|
26
26
|
---
|
|
27
27
|
|
|
28
|
-
## What's New in v2.
|
|
28
|
+
## What's New in v2.46.0
|
|
29
29
|
|
|
30
|
-
###
|
|
30
|
+
### Single-Writer State Engine
|
|
31
31
|
|
|
32
|
-
|
|
33
|
-
- **Docker sandbox template** — official Docker template for running GSD auto mode in an isolated container. (#2360)
|
|
34
|
-
- **Per-prompt token cost display** — opt-in `show_token_cost` preference shows per-prompt and cumulative session cost in the footer. (#2357)
|
|
35
|
-
- **"Change project root" in web UI** — switch project directories from the web interface without restarting. (#2355)
|
|
36
|
-
- **DB-backed planning tools** — write-side state transitions now use atomic SQLite tool calls instead of markdown mutation, improving reliability and enabling structured queries. (#2141)
|
|
32
|
+
The biggest architectural change since DB-backed planning tools. The single-writer engine enforces disciplined state transitions through three iterations:
|
|
37
33
|
|
|
38
|
-
|
|
34
|
+
- **v2 — discipline layer** — adds a write-side discipline layer on top of the DB architecture, ensuring all state mutations flow through controlled tool calls.
|
|
35
|
+
- **v3 — state machine guards, actor identity, reversibility** — introduces formal state machine guards, tracks which actor (human vs agent) initiated each transition, and makes transitions reversible.
|
|
36
|
+
- **Hardened** — closes TOCTOU race conditions, intercepts bypass attempts, and resolves status inconsistencies.
|
|
37
|
+
|
|
38
|
+
All prompts are now aligned with the single-writer tool API, and a new **workflow-logger** is wired into the engine, tool, manifest, and reconcile paths for full observability. (#2494)
|
|
39
39
|
|
|
40
|
-
|
|
41
|
-
- **Planning data loss prevention** — destructive upsert and post-unit re-import no longer overwrite planning data. (#2370)
|
|
42
|
-
- **Memory and resource leaks** — fixes across TUI, LSP, DB, and automation subsystems. (#2314)
|
|
43
|
-
- **DECISIONS.md preservation** — freeform content in DECISIONS.md is no longer overwritten on decision save. (#2319)
|
|
44
|
-
- **Auto-stash before squash merge** — dirty files are automatically stashed before merge, with filenames surfaced in errors. (#2298)
|
|
45
|
-
- **Extension TypeScript detection** — `.js` extension files containing TypeScript syntax are detected with a suggestion to rename. (#2386)
|
|
40
|
+
### v2.45.0 — New Commands and Capabilities
|
|
46
41
|
|
|
47
|
-
|
|
42
|
+
- **`/gsd rethink`** — conversational project reorganization. Rethink your milestone structure, slice decomposition, or overall approach through guided discussion. (#2459)
|
|
43
|
+
- **`/gsd mcp`** — MCP server status and connectivity. Check which MCP servers are configured, connected, and healthy. (#2362)
|
|
44
|
+
- **Complete offline mode** — GSD now works fully offline with local models. (#2429)
|
|
45
|
+
- **Global KNOWLEDGE.md injection** — `~/.gsd/agent/KNOWLEDGE.md` is injected into the system prompt, so cross-project knowledge persists globally. (#2331)
|
|
46
|
+
- **Mobile-responsive web UI** — the browser interface now works on phones and tablets. (#2354)
|
|
47
|
+
- **DB tool previews** — `renderCall`/`renderResult` previews on DB tools show what each tool call does before and after execution. (#2273)
|
|
48
|
+
- **Message timestamps** — user and assistant messages now include timestamps. (#2368)
|
|
48
49
|
|
|
49
|
-
|
|
50
|
-
- **Fast service tier outside auto-mode** — `/gsd fast` now applies in interactive sessions too. (#2126)
|
|
51
|
-
- **Startup optimizations** — pre-compiled extensions, compile cache, and batch discovery for faster boot. (#2125)
|
|
52
|
-
- **Stale process cleanup** — web server kills stale process before launch to prevent EADDRINUSE. (#2034)
|
|
50
|
+
### Key Changes
|
|
53
51
|
|
|
54
|
-
|
|
52
|
+
- **Default isolation mode changed to `none`** — `git.isolation` now defaults to `none` instead of `worktree`. Projects that rely on worktree isolation should set `git.isolation: worktree` explicitly in preferences. (#2481)
|
|
53
|
+
- **Startup checks** — GSD now validates Node.js version and git availability at startup, with clear error messages. (#2463)
|
|
54
|
+
- **Worktree lifecycle journaling** — worktree create, switch, merge, and remove events are recorded in the event journal. (#2486)
|
|
55
|
+
- **Milestone verification gate** — milestone completion is blocked when verification fails, preventing premature closure. (#2500)
|
|
56
|
+
|
|
57
|
+
### Key Fixes
|
|
55
58
|
|
|
56
|
-
- **
|
|
57
|
-
- **
|
|
58
|
-
-
|
|
59
|
-
-
|
|
60
|
-
- **
|
|
61
|
-
- **
|
|
59
|
+
- **Auto-mode stability** — recovery attempts reset on unit re-dispatch (#2424), survivor branch recovery handles `phase=complete` (#2427), and auto mode stops on real merge conflicts (#2428).
|
|
60
|
+
- **Supervision timeouts** — now respect task `est:` annotations, so complex tasks get proportionally longer timeouts. (#2434)
|
|
61
|
+
- **`auto_pr: true` fixed** — three interacting bugs prevented auto-PR creation; all three are resolved. (#2433)
|
|
62
|
+
- **Rich task plan preservation** — plans survive DB roundtrip without losing structured content. (#2453)
|
|
63
|
+
- **Artifact truncation prevention** — `saveArtifactToDb` no longer overwrites larger files with truncated content. (#2447)
|
|
64
|
+
- **Worktree teardown** — submodule state is detected and preserved during teardown (#2425), and worktree merge back to main works after `stopAuto` on milestone completion (#2430).
|
|
65
|
+
- **Windows portability** — `retentionDays=0` handling and CRLF fixes on Windows. (#2460)
|
|
66
|
+
- **Voice on Linux** — misleading portaudio error on PEP 668 systems replaced with actionable guidance. (#2407)
|
|
67
|
+
|
|
68
|
+
### Previous highlights (v2.42–v2.44)
|
|
69
|
+
|
|
70
|
+
- **Non-API-key provider extensions** — support for Claude Code CLI and similar providers. (#2382)
|
|
71
|
+
- **Docker sandbox template** — official Docker template for isolated auto mode. (#2360)
|
|
72
|
+
- **DB-backed planning tools** — write-side state transitions use atomic SQLite tool calls. (#2141)
|
|
73
|
+
- **Declarative workflow engine** — YAML workflows through auto-loop. (#2024)
|
|
74
|
+
- **`/gsd fast`** — toggle service tier for prioritized API routing. (#1862)
|
|
75
|
+
- **Forensics dedup** — duplicate detection before issue creation. (#2105)
|
|
76
|
+
- **Startup optimizations** — pre-compiled extensions, compile cache, batch discovery. (#2125)
|
|
62
77
|
|
|
63
78
|
---
|
|
64
79
|
|
|
@@ -138,7 +153,7 @@ See the full [Changelog](./CHANGELOG.md) for all 70+ fixes in this release.
|
|
|
138
153
|
|
|
139
154
|
## Documentation
|
|
140
155
|
|
|
141
|
-
Full documentation is available in the [`docs/`](./docs/) directory:
|
|
156
|
+
Full documentation is available at **[gsd.build](https://gsd.build)** (powered by Mintlify) and in the [`docs/`](./docs/) directory:
|
|
142
157
|
|
|
143
158
|
- **[Getting Started](./docs/getting-started.md)** — install, first run, basic usage
|
|
144
159
|
- **[Auto Mode](./docs/auto-mode.md)** — autonomous execution deep-dive
|
|
@@ -260,7 +275,7 @@ Auto mode is a state machine driven by files on disk. It reads `.gsd/STATE.md`,
|
|
|
260
275
|
|
|
261
276
|
2. **Context pre-loading** — The dispatch prompt includes inlined task plans, slice plans, prior task summaries, dependency summaries, roadmap excerpts, and decisions register. The LLM starts with everything it needs instead of spending tool calls reading files.
|
|
262
277
|
|
|
263
|
-
3. **Git
|
|
278
|
+
3. **Git isolation** — When `git.isolation` is set to `worktree` or `branch`, each milestone runs on its own `milestone/<MID>` branch (in a worktree or in-place). All slice work commits sequentially — no branch switching, no merge conflicts. When the milestone completes, it's squash-merged to main as one clean commit. The default is `none` (work on the current branch), configurable via preferences.
|
|
264
279
|
|
|
265
280
|
4. **Crash recovery** — A lock file tracks the current unit. If the session dies, the next `/gsd auto` reads the surviving session file, synthesizes a recovery briefing from every tool call that made it to disk, and resumes with full context. Parallel orchestrator state is persisted to disk with PID liveness detection, so multi-worker sessions survive crashes too. In headless mode, crashes trigger automatic restart with exponential backoff (default 3 attempts).
|
|
266
281
|
|
|
@@ -396,6 +411,8 @@ On first run, GSD launches a branded setup wizard that walks you through LLM pro
|
|
|
396
411
|
| `/gsd stop` | Stop auto mode gracefully |
|
|
397
412
|
| `/gsd steer` | Hard-steer plan documents during execution |
|
|
398
413
|
| `/gsd discuss` | Discuss architecture and decisions (works alongside auto mode) |
|
|
414
|
+
| `/gsd rethink` | Conversational project reorganization |
|
|
415
|
+
| `/gsd mcp` | MCP server status and connectivity |
|
|
399
416
|
| `/gsd status` | Progress dashboard |
|
|
400
417
|
| `/gsd queue` | Queue future milestones (safe during auto mode) |
|
|
401
418
|
| `/gsd prefs` | Model selection, timeouts, budget ceiling |
|
|
@@ -543,7 +560,7 @@ auto_report: true
|
|
|
543
560
|
| `skill_rules` | Situational rules for skill routing |
|
|
544
561
|
| `skill_staleness_days` | Skills unused for N days get deprioritized (default: 60, 0 = disabled) |
|
|
545
562
|
| `unique_milestone_ids` | Uses unique milestone names to avoid clashes when working in teams of people |
|
|
546
|
-
| `git.isolation` | `
|
|
563
|
+
| `git.isolation` | `none` (default), `worktree`, or `branch` — enable worktree or branch isolation for milestone work |
|
|
547
564
|
| `git.manage_gitignore` | Set `false` to prevent GSD from modifying `.gitignore` |
|
|
548
565
|
| `verification_commands`| Array of shell commands to run after task execution (e.g., `["npm run lint", "npm run test"]`) |
|
|
549
566
|
| `verification_auto_fix`| Auto-retry on verification failures (default: true) |
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Claude Code CLI Provider Extension
|
|
3
|
+
*
|
|
4
|
+
* Registers a model provider that delegates inference to the user's
|
|
5
|
+
* locally-installed Claude Code CLI via the official Agent SDK.
|
|
6
|
+
*
|
|
7
|
+
* Users with a Claude Code subscription (Pro/Max/Team) get access to
|
|
8
|
+
* subsidized inference through GSD's UI — no API key required.
|
|
9
|
+
*
|
|
10
|
+
* TOS-compliant: uses Anthropic's official `@anthropic-ai/claude-agent-sdk`,
|
|
11
|
+
* never touches credentials, never offers a login flow.
|
|
12
|
+
*/
|
|
13
|
+
import { CLAUDE_CODE_MODELS } from "./models.js";
|
|
14
|
+
import { isClaudeCodeReady } from "./readiness.js";
|
|
15
|
+
import { streamViaClaudeCode } from "./stream-adapter.js";
|
|
16
|
+
export default function claudeCodeCli(pi) {
|
|
17
|
+
pi.registerProvider("claude-code", {
|
|
18
|
+
authMode: "externalCli",
|
|
19
|
+
api: "anthropic-messages",
|
|
20
|
+
baseUrl: "local://claude-code",
|
|
21
|
+
isReady: isClaudeCodeReady,
|
|
22
|
+
streamSimple: streamViaClaudeCode,
|
|
23
|
+
models: CLAUDE_CODE_MODELS,
|
|
24
|
+
});
|
|
25
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Model definitions for the Claude Code CLI provider.
|
|
3
|
+
*
|
|
4
|
+
* Costs are zero because inference is covered by the user's Claude Code
|
|
5
|
+
* subscription. The SDK's `result` message still provides token counts
|
|
6
|
+
* for display in the TUI.
|
|
7
|
+
*
|
|
8
|
+
* Context windows and max tokens match the Anthropic API definitions
|
|
9
|
+
* in models.generated.ts.
|
|
10
|
+
*/
|
|
11
|
+
const ZERO_COST = { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 };
|
|
12
|
+
export const CLAUDE_CODE_MODELS = [
|
|
13
|
+
{
|
|
14
|
+
id: "claude-opus-4-6",
|
|
15
|
+
name: "Claude Opus 4.6 (via Claude Code)",
|
|
16
|
+
reasoning: true,
|
|
17
|
+
input: ["text", "image"],
|
|
18
|
+
cost: ZERO_COST,
|
|
19
|
+
contextWindow: 1_000_000,
|
|
20
|
+
maxTokens: 128_000,
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
id: "claude-sonnet-4-6",
|
|
24
|
+
name: "Claude Sonnet 4.6 (via Claude Code)",
|
|
25
|
+
reasoning: true,
|
|
26
|
+
input: ["text", "image"],
|
|
27
|
+
cost: ZERO_COST,
|
|
28
|
+
contextWindow: 1_000_000,
|
|
29
|
+
maxTokens: 64_000,
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
id: "claude-haiku-4-5",
|
|
33
|
+
name: "Claude Haiku 4.5 (via Claude Code)",
|
|
34
|
+
reasoning: true,
|
|
35
|
+
input: ["text", "image"],
|
|
36
|
+
cost: ZERO_COST,
|
|
37
|
+
contextWindow: 200_000,
|
|
38
|
+
maxTokens: 64_000,
|
|
39
|
+
},
|
|
40
|
+
];
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Content-block mapping helpers and streaming state tracker.
|
|
3
|
+
*
|
|
4
|
+
* Translates the Claude Agent SDK's `BetaRawMessageStreamEvent` sequence
|
|
5
|
+
* into GSD's `AssistantMessageEvent` deltas for incremental TUI rendering.
|
|
6
|
+
*/
|
|
7
|
+
// ---------------------------------------------------------------------------
|
|
8
|
+
// Content-block mapping helpers
|
|
9
|
+
// ---------------------------------------------------------------------------
|
|
10
|
+
/**
|
|
11
|
+
* Convert a single BetaContentBlock to the corresponding GSD content type.
|
|
12
|
+
*/
|
|
13
|
+
export function mapContentBlock(block) {
|
|
14
|
+
switch (block.type) {
|
|
15
|
+
case "text":
|
|
16
|
+
return { type: "text", text: block.text };
|
|
17
|
+
case "thinking":
|
|
18
|
+
return {
|
|
19
|
+
type: "thinking",
|
|
20
|
+
thinking: block.thinking,
|
|
21
|
+
...(block.signature ? { thinkingSignature: block.signature } : {}),
|
|
22
|
+
};
|
|
23
|
+
case "tool_use":
|
|
24
|
+
return {
|
|
25
|
+
type: "toolCall",
|
|
26
|
+
id: block.id,
|
|
27
|
+
name: block.name,
|
|
28
|
+
arguments: block.input,
|
|
29
|
+
};
|
|
30
|
+
case "server_tool_use":
|
|
31
|
+
return {
|
|
32
|
+
type: "serverToolUse",
|
|
33
|
+
id: block.id,
|
|
34
|
+
name: block.name,
|
|
35
|
+
input: block.input,
|
|
36
|
+
};
|
|
37
|
+
case "web_search_tool_result":
|
|
38
|
+
return {
|
|
39
|
+
type: "webSearchResult",
|
|
40
|
+
toolUseId: block.tool_use_id,
|
|
41
|
+
content: block.content,
|
|
42
|
+
};
|
|
43
|
+
default: {
|
|
44
|
+
const unknown = block;
|
|
45
|
+
return { type: "text", text: `[unknown content block: ${JSON.stringify(unknown)}]` };
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
export function mapStopReason(reason) {
|
|
50
|
+
switch (reason) {
|
|
51
|
+
case "end_turn":
|
|
52
|
+
case "stop_sequence":
|
|
53
|
+
return "stop";
|
|
54
|
+
case "max_tokens":
|
|
55
|
+
return "length";
|
|
56
|
+
case "tool_use":
|
|
57
|
+
return "toolUse";
|
|
58
|
+
default:
|
|
59
|
+
return "stop";
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Convert SDK usage + total_cost_usd into GSD's Usage shape.
|
|
64
|
+
*
|
|
65
|
+
* The SDK does not break cost down per-bucket, so all cost is
|
|
66
|
+
* attributed to `cost.total`.
|
|
67
|
+
*/
|
|
68
|
+
export function mapUsage(sdkUsage, totalCostUsd) {
|
|
69
|
+
return {
|
|
70
|
+
input: sdkUsage.input_tokens,
|
|
71
|
+
output: sdkUsage.output_tokens,
|
|
72
|
+
cacheRead: sdkUsage.cache_read_input_tokens,
|
|
73
|
+
cacheWrite: sdkUsage.cache_creation_input_tokens,
|
|
74
|
+
totalTokens: sdkUsage.input_tokens +
|
|
75
|
+
sdkUsage.output_tokens +
|
|
76
|
+
sdkUsage.cache_read_input_tokens +
|
|
77
|
+
sdkUsage.cache_creation_input_tokens,
|
|
78
|
+
cost: {
|
|
79
|
+
input: 0,
|
|
80
|
+
output: 0,
|
|
81
|
+
cacheRead: 0,
|
|
82
|
+
cacheWrite: 0,
|
|
83
|
+
total: totalCostUsd,
|
|
84
|
+
},
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
// ---------------------------------------------------------------------------
|
|
88
|
+
// Zero-cost usage constant
|
|
89
|
+
// ---------------------------------------------------------------------------
|
|
90
|
+
export const ZERO_USAGE = {
|
|
91
|
+
input: 0,
|
|
92
|
+
output: 0,
|
|
93
|
+
cacheRead: 0,
|
|
94
|
+
cacheWrite: 0,
|
|
95
|
+
totalTokens: 0,
|
|
96
|
+
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },
|
|
97
|
+
};
|
|
98
|
+
// ---------------------------------------------------------------------------
|
|
99
|
+
// Streaming partial-message state tracker
|
|
100
|
+
// ---------------------------------------------------------------------------
|
|
101
|
+
/**
|
|
102
|
+
* Mutable accumulator that tracks the partial AssistantMessage being built
|
|
103
|
+
* from a sequence of stream_event messages. Produces AssistantMessageEvent
|
|
104
|
+
* deltas that the TUI can render incrementally.
|
|
105
|
+
*/
|
|
106
|
+
export class PartialMessageBuilder {
|
|
107
|
+
partial;
|
|
108
|
+
/** Map from stream-event `index` to our content array index. */
|
|
109
|
+
indexMap = new Map();
|
|
110
|
+
/** Accumulated JSON input string per tool_use block (keyed by stream index). */
|
|
111
|
+
toolJsonAccum = new Map();
|
|
112
|
+
constructor(model) {
|
|
113
|
+
this.partial = {
|
|
114
|
+
role: "assistant",
|
|
115
|
+
content: [],
|
|
116
|
+
api: "anthropic-messages",
|
|
117
|
+
provider: "claude-code",
|
|
118
|
+
model,
|
|
119
|
+
usage: { ...ZERO_USAGE },
|
|
120
|
+
stopReason: "stop",
|
|
121
|
+
timestamp: Date.now(),
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
get message() {
|
|
125
|
+
return this.partial;
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Feed a BetaRawMessageStreamEvent and return the corresponding
|
|
129
|
+
* AssistantMessageEvent (or null if the event is not mapped).
|
|
130
|
+
*/
|
|
131
|
+
handleEvent(event) {
|
|
132
|
+
const streamIndex = event.index ?? 0;
|
|
133
|
+
switch (event.type) {
|
|
134
|
+
// ---- Block start ----
|
|
135
|
+
case "content_block_start": {
|
|
136
|
+
const block = event.content_block;
|
|
137
|
+
if (!block)
|
|
138
|
+
return null;
|
|
139
|
+
const contentIndex = this.partial.content.length;
|
|
140
|
+
this.indexMap.set(streamIndex, contentIndex);
|
|
141
|
+
if (block.type === "text") {
|
|
142
|
+
this.partial.content.push({ type: "text", text: "" });
|
|
143
|
+
return { type: "text_start", contentIndex, partial: this.partial };
|
|
144
|
+
}
|
|
145
|
+
if (block.type === "thinking") {
|
|
146
|
+
this.partial.content.push({ type: "thinking", thinking: "" });
|
|
147
|
+
return { type: "thinking_start", contentIndex, partial: this.partial };
|
|
148
|
+
}
|
|
149
|
+
if (block.type === "tool_use") {
|
|
150
|
+
this.toolJsonAccum.set(streamIndex, "");
|
|
151
|
+
this.partial.content.push({
|
|
152
|
+
type: "toolCall",
|
|
153
|
+
id: block.id,
|
|
154
|
+
name: block.name,
|
|
155
|
+
arguments: {},
|
|
156
|
+
});
|
|
157
|
+
return { type: "toolcall_start", contentIndex, partial: this.partial };
|
|
158
|
+
}
|
|
159
|
+
if (block.type === "server_tool_use") {
|
|
160
|
+
this.partial.content.push({
|
|
161
|
+
type: "serverToolUse",
|
|
162
|
+
id: block.id,
|
|
163
|
+
name: block.name,
|
|
164
|
+
input: block.input,
|
|
165
|
+
});
|
|
166
|
+
return { type: "server_tool_use", contentIndex, partial: this.partial };
|
|
167
|
+
}
|
|
168
|
+
return null;
|
|
169
|
+
}
|
|
170
|
+
// ---- Block delta ----
|
|
171
|
+
case "content_block_delta": {
|
|
172
|
+
const contentIndex = this.indexMap.get(streamIndex);
|
|
173
|
+
if (contentIndex === undefined)
|
|
174
|
+
return null;
|
|
175
|
+
const delta = event.delta;
|
|
176
|
+
if (!delta)
|
|
177
|
+
return null;
|
|
178
|
+
if (delta.type === "text_delta" && typeof delta.text === "string") {
|
|
179
|
+
const existing = this.partial.content[contentIndex];
|
|
180
|
+
existing.text += delta.text;
|
|
181
|
+
return { type: "text_delta", contentIndex, delta: delta.text, partial: this.partial };
|
|
182
|
+
}
|
|
183
|
+
if (delta.type === "thinking_delta" && typeof delta.thinking === "string") {
|
|
184
|
+
const existing = this.partial.content[contentIndex];
|
|
185
|
+
existing.thinking += delta.thinking;
|
|
186
|
+
return { type: "thinking_delta", contentIndex, delta: delta.thinking, partial: this.partial };
|
|
187
|
+
}
|
|
188
|
+
if (delta.type === "input_json_delta" && typeof delta.partial_json === "string") {
|
|
189
|
+
const accum = (this.toolJsonAccum.get(streamIndex) ?? "") + delta.partial_json;
|
|
190
|
+
this.toolJsonAccum.set(streamIndex, accum);
|
|
191
|
+
return { type: "toolcall_delta", contentIndex, delta: delta.partial_json, partial: this.partial };
|
|
192
|
+
}
|
|
193
|
+
return null;
|
|
194
|
+
}
|
|
195
|
+
// ---- Block stop ----
|
|
196
|
+
case "content_block_stop": {
|
|
197
|
+
const contentIndex = this.indexMap.get(streamIndex);
|
|
198
|
+
if (contentIndex === undefined)
|
|
199
|
+
return null;
|
|
200
|
+
const block = this.partial.content[contentIndex];
|
|
201
|
+
if (block.type === "text") {
|
|
202
|
+
return { type: "text_end", contentIndex, content: block.text, partial: this.partial };
|
|
203
|
+
}
|
|
204
|
+
if (block.type === "thinking") {
|
|
205
|
+
return { type: "thinking_end", contentIndex, content: block.thinking, partial: this.partial };
|
|
206
|
+
}
|
|
207
|
+
if (block.type === "toolCall") {
|
|
208
|
+
const jsonStr = this.toolJsonAccum.get(streamIndex) ?? "{}";
|
|
209
|
+
try {
|
|
210
|
+
block.arguments = JSON.parse(jsonStr);
|
|
211
|
+
}
|
|
212
|
+
catch {
|
|
213
|
+
block.arguments = { _raw: jsonStr };
|
|
214
|
+
}
|
|
215
|
+
return { type: "toolcall_end", contentIndex, toolCall: block, partial: this.partial };
|
|
216
|
+
}
|
|
217
|
+
return null;
|
|
218
|
+
}
|
|
219
|
+
default:
|
|
220
|
+
return null;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Readiness check for the Claude Code CLI provider.
|
|
3
|
+
*
|
|
4
|
+
* Verifies the `claude` binary is installed and responsive.
|
|
5
|
+
* Result is cached for 30 seconds to avoid shelling out on every
|
|
6
|
+
* model-availability check.
|
|
7
|
+
*/
|
|
8
|
+
import { execSync } from "node:child_process";
|
|
9
|
+
let cachedReady = null;
|
|
10
|
+
let lastCheckMs = 0;
|
|
11
|
+
const CHECK_INTERVAL_MS = 30_000;
|
|
12
|
+
export function isClaudeCodeReady() {
|
|
13
|
+
const now = Date.now();
|
|
14
|
+
if (cachedReady !== null && now - lastCheckMs < CHECK_INTERVAL_MS) {
|
|
15
|
+
return cachedReady;
|
|
16
|
+
}
|
|
17
|
+
try {
|
|
18
|
+
execSync("claude --version", { timeout: 5_000, stdio: "pipe" });
|
|
19
|
+
cachedReady = true;
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
cachedReady = false;
|
|
23
|
+
}
|
|
24
|
+
lastCheckMs = now;
|
|
25
|
+
return cachedReady;
|
|
26
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lightweight type mirrors for the Claude Agent SDK.
|
|
3
|
+
*
|
|
4
|
+
* These stubs allow the extension to compile without a hard dependency on
|
|
5
|
+
* `@anthropic-ai/claude-agent-sdk`. The real SDK is imported dynamically
|
|
6
|
+
* at runtime in stream-adapter.ts.
|
|
7
|
+
*/
|
|
8
|
+
export {};
|