From 7d43194d4a9185979990a2bcf8fd869b3338ed43 Mon Sep 17 00:00:00 2001 From: Rasmus Widing <152263317+Wirasm@users.noreply.github.com> Date: Fri, 17 Apr 2026 09:33:52 +0200 Subject: [PATCH 01/11] fix(core/test): split connection.test.ts from DB-test batch to avoid mock pollution (#1269) messages.test.ts uses mock.module('./connection', ...) at module-load time. Per CLAUDE.md:131 (Bun issue oven-sh/bun#7823), mock.module() is process- global and irreversible. When Bun pre-loads all test files in a batch, the mock shadows the real connection module before connection.test.ts runs, causing getDatabaseType() to always return the mocked value regardless of DATABASE_URL. Move connection.test.ts into its own `bun test` invocation immediately after postgres.test.ts (which runs alone) and before the big DB/utils/ config/state batch that contains messages.test.ts. This follows the same isolation pattern already used for command-handler, clone, postgres, and path-validation tests. --- packages/core/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/package.json b/packages/core/package.json index 541685c3be..6a13b46fa2 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -22,7 +22,7 @@ "./state/*": "./src/state/*.ts" }, "scripts": { - "test": "bun test src/handlers/command-handler.test.ts && bun test src/handlers/clone.test.ts && bun test src/db/adapters/postgres.test.ts && bun test src/db/adapters/sqlite.test.ts src/db/codebases.test.ts src/db/connection.test.ts src/db/conversations.test.ts src/db/env-vars.test.ts src/db/isolation-environments.test.ts src/db/messages.test.ts src/db/sessions.test.ts src/db/workflow-events.test.ts src/db/workflows.test.ts src/utils/defaults-copy.test.ts src/utils/worktree-sync.test.ts src/utils/conversation-lock.test.ts src/utils/credential-sanitizer.test.ts src/utils/port-allocation.test.ts src/utils/error.test.ts src/utils/error-formatter.test.ts src/utils/github-graphql.test.ts src/config/ src/state/ && bun test src/utils/path-validation.test.ts && bun test src/db/workflow-analytics.test.ts && bun test src/services/cleanup-service.test.ts && bun test src/services/title-generator.test.ts && bun test src/services/cron-parser.test.ts && bun test src/services/knowledge-writer.test.ts && bun test src/workflows/ && bun test src/operations/workflow-operations.test.ts && bun test src/operations/isolation-operations.test.ts && bun test src/orchestrator/orchestrator.test.ts && bun test src/orchestrator/orchestrator-agent.test.ts && bun test src/orchestrator/orchestrator-isolation.test.ts", + "test": "bun test src/handlers/command-handler.test.ts && bun test src/handlers/clone.test.ts && bun test src/db/adapters/postgres.test.ts && bun test src/db/connection.test.ts && bun test src/db/adapters/sqlite.test.ts src/db/codebases.test.ts src/db/conversations.test.ts src/db/env-vars.test.ts src/db/isolation-environments.test.ts src/db/messages.test.ts src/db/sessions.test.ts src/db/workflow-events.test.ts src/db/workflows.test.ts src/utils/defaults-copy.test.ts src/utils/worktree-sync.test.ts src/utils/conversation-lock.test.ts src/utils/credential-sanitizer.test.ts src/utils/port-allocation.test.ts src/utils/error.test.ts src/utils/error-formatter.test.ts src/utils/github-graphql.test.ts src/config/ src/state/ && bun test src/utils/path-validation.test.ts && bun test src/db/workflow-analytics.test.ts && bun test src/services/cleanup-service.test.ts && bun test src/services/title-generator.test.ts && bun test src/services/cron-parser.test.ts && bun test src/services/knowledge-writer.test.ts && bun test src/workflows/ && bun test src/operations/workflow-operations.test.ts && bun test src/operations/isolation-operations.test.ts && bun test src/orchestrator/orchestrator.test.ts && bun test src/orchestrator/orchestrator-agent.test.ts && bun test src/orchestrator/orchestrator-isolation.test.ts", "type-check": "bun x tsc --noEmit", "build": "echo 'No build needed - Bun runs TypeScript directly'" }, From 8bef2deccf5d0015bf8b9c0ec5f6eb9eb51c14ef Mon Sep 17 00:00:00 2001 From: DIY Smart Code Date: Fri, 17 Apr 2026 14:15:37 +0200 Subject: [PATCH 02/11] fix(setup): align PORT default on 3090 across .env.example, wizard, and JSDoc (#1152) (#1271) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The server's getPort() fallback changed from 3000 to 3090 in the Hono migration (#318), but .env.example, the setup wizard's generated .env, and the JSDoc describing the fallback were not updated — leaving three different sources of truth for "the default PORT." When the wizard writes PORT=3000 to ~/.archon/.env (which the Hono server loads with override: true, while Vite only reads repo-local .env), the two processes can land on different ports silently. That mismatch is the real mechanism behind the failure described in #1152. - .env.example: comment out PORT, document 3090 as the default - packages/cli/src/commands/setup.ts: wizard no longer writes PORT=3000 into the generated .env; fix the "Additional Options" note - packages/cli/src/commands/setup.test.ts: assert no bare PORT= line and the commented default is present - packages/core/src/utils/port-allocation.ts: fix stale JSDoc "default 3000" -> "default 3090" - deploy/.env.example: keep Docker default at 3000 (compose/Caddy target that) but annotate it so users don't copy it for local dev Single source of truth for the local-dev default is now basePort in port-allocation.ts. --- .env.example | 2 +- deploy/.env.example | 2 +- packages/cli/src/commands/setup.test.ts | 4 +++- packages/cli/src/commands/setup.ts | 8 ++++++-- packages/core/src/utils/port-allocation.ts | 2 +- 5 files changed, 12 insertions(+), 6 deletions(-) diff --git a/.env.example b/.env.example index 329091edfa..245533afd1 100644 --- a/.env.example +++ b/.env.example @@ -133,7 +133,7 @@ GITEA_ALLOWED_USERS= # GITEA_BOT_MENTION=archon # Server -PORT=3000 +# PORT=3090 # Default: 3090. Uncomment to override — must match between server and Vite proxy. # HOST=0.0.0.0 # Bind address (default: 0.0.0.0). Set to 127.0.0.1 to restrict to localhost only. # Cloud Deployment (for --profile cloud with Caddy reverse proxy) diff --git a/deploy/.env.example b/deploy/.env.example index 9e2d5f521f..9a0b208e74 100644 --- a/deploy/.env.example +++ b/deploy/.env.example @@ -46,7 +46,7 @@ TELEGRAM_BOT_TOKEN=123456789:ABC... # ============================================ # Optional # ============================================ -PORT=3000 +PORT=3000 # Docker deployment default (the included compose/Caddy configs target :3000). For local dev (no Docker), omit PORT — server and Vite proxy both default to 3090. # TELEGRAM_STREAMING_MODE=stream # DISCORD_STREAMING_MODE=batch diff --git a/packages/cli/src/commands/setup.test.ts b/packages/cli/src/commands/setup.test.ts index 6d463d5fda..301b58c6d7 100644 --- a/packages/cli/src/commands/setup.test.ts +++ b/packages/cli/src/commands/setup.test.ts @@ -150,7 +150,9 @@ CODEX_ACCOUNT_ID=account1 expect(content).toContain('# Using SQLite (default)'); expect(content).toContain('CLAUDE_USE_GLOBAL_AUTH=true'); expect(content).toContain('DEFAULT_AI_ASSISTANT=claude'); - expect(content).toContain('PORT=3000'); + // PORT is intentionally commented out — server and Vite both default to 3090 when unset (#1152). + expect(content).toContain('# PORT=3090'); + expect(content).not.toMatch(/^PORT=/m); expect(content).not.toContain('DATABASE_URL='); }); diff --git a/packages/cli/src/commands/setup.ts b/packages/cli/src/commands/setup.ts index 0235ceec3e..16068ea7ff 100644 --- a/packages/cli/src/commands/setup.ts +++ b/packages/cli/src/commands/setup.ts @@ -1290,8 +1290,12 @@ export function generateEnvContent(config: SetupConfig): string { } // Server + // PORT is intentionally omitted: both the Hono server (packages/core/src/utils/port-allocation.ts) + // and the Vite dev proxy (packages/web/vite.config.ts) default to 3090 when unset, which keeps + // them in sync. Writing a fixed PORT here risked a mismatch if ~/.archon/.env leaks a PORT that + // the Vite proxy (which only reads repo-local .env) never sees — see #1152. lines.push('# Server'); - lines.push('PORT=3000'); + lines.push('# PORT=3090 # Default: 3090. Uncomment to override.'); lines.push(''); // Concurrency @@ -1769,7 +1773,7 @@ export async function setupCommand(options: SetupOptions): Promise { // Additional options note note( 'Other settings you can customize in ~/.archon/.env:\n' + - ' - PORT (default: 3000)\n' + + ' - PORT (default: 3090)\n' + ' - MAX_CONCURRENT_CONVERSATIONS (default: 10)\n' + ' - *_STREAMING_MODE (stream | batch per platform)\n\n' + 'These defaults work well for most users.', diff --git a/packages/core/src/utils/port-allocation.ts b/packages/core/src/utils/port-allocation.ts index efb34d3198..0ecb5b74e1 100644 --- a/packages/core/src/utils/port-allocation.ts +++ b/packages/core/src/utils/port-allocation.ts @@ -30,7 +30,7 @@ export function calculatePortOffset(path: string): number { * Get the port for the Hono server * - If PORT env var is set: use it (explicit override, validated) * - If running in worktree: auto-allocate deterministic port based on path hash - * - Otherwise: use default 3000 + * - Otherwise: use default 3090 (matches the Vite proxy fallback in packages/web/vite.config.ts) * * Note: Exits process with code 1 if PORT env var is set but invalid (not 1-65535) */ From e0c77513798cfc04c4aa868fefe8890cdb0b5cb4 Mon Sep 17 00:00:00 2001 From: Cocoon-Break <54054995+kuishou68@users.noreply.github.com> Date: Mon, 20 Apr 2026 19:15:27 +0800 Subject: [PATCH 03/11] fix(providers/claude): use || instead of ?? in hasExplicitTokens to handle empty-string env vars (#1028) Closes #1027 --- packages/providers/src/claude/provider.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/providers/src/claude/provider.ts b/packages/providers/src/claude/provider.ts index 7202f4e19e..2801ce446e 100644 --- a/packages/providers/src/claude/provider.ts +++ b/packages/providers/src/claude/provider.ts @@ -82,8 +82,9 @@ function normalizeClaudeUsage(usage?: { * - ~/.archon/.env loaded with override:true as the trusted source */ function buildSubprocessEnv(): NodeJS.ProcessEnv { + // Using || intentionally: empty string should be treated as missing credential const hasExplicitTokens = Boolean( - process.env.CLAUDE_CODE_OAUTH_TOKEN ?? process.env.CLAUDE_API_KEY + process.env.CLAUDE_CODE_OAUTH_TOKEN || process.env.CLAUDE_API_KEY ); const authMode = hasExplicitTokens ? 'explicit' : 'global'; getLog().info( From 8f87592a61d2b20a0c9c2d4aae4c89e2e00c7903 Mon Sep 17 00:00:00 2001 From: Rasmus Widing <152263317+Wirasm@users.noreply.github.com> Date: Tue, 28 Apr 2026 11:25:22 +0300 Subject: [PATCH 04/11] chore(deps): bump claude-agent-sdk to 0.2.121, codex-sdk to 0.125.0 (#1460) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Both SDKs were ~30 patch releases behind. Validation suite passes (type-check, lint, format, tests across all 10 packages) without code changes. The only sustained Claude SDK behavior change in the range — v0.2.111's options.env overlay/replace flap, since reverted to overlay — is a no-op for Archon, which already passes { ...process.env } as the SDK env. --- bun.lock | 116 +++++++++++++++++--------------- package.json | 2 +- packages/providers/package.json | 4 +- 3 files changed, 66 insertions(+), 56 deletions(-) diff --git a/bun.lock b/bun.lock index 8f1fcec74e..eacdffb2fa 100644 --- a/bun.lock +++ b/bun.lock @@ -4,7 +4,7 @@ "": { "name": "archon", "dependencies": { - "@anthropic-ai/claude-agent-sdk": "^0.2.74", + "@anthropic-ai/claude-agent-sdk": "^0.2.121", }, "devDependencies": { "@eslint/js": "^9.39.1", @@ -127,9 +127,9 @@ "name": "@archon/providers", "version": "0.3.6", "dependencies": { - "@anthropic-ai/claude-agent-sdk": "^0.2.89", + "@anthropic-ai/claude-agent-sdk": "^0.2.121", "@archon/paths": "workspace:*", - "@openai/codex-sdk": "^0.116.0", + "@openai/codex-sdk": "^0.125.0", }, "devDependencies": { "pino": "^9", @@ -231,9 +231,25 @@ "packages": { "@antfu/ni": ["@antfu/ni@25.0.0", "", { "dependencies": { "ansis": "^4.0.0", "fzf": "^0.5.2", "package-manager-detector": "^1.3.0", "tinyexec": "^1.0.1" }, "bin": { "na": "bin/na.mjs", "ni": "bin/ni.mjs", "nr": "bin/nr.mjs", "nci": "bin/nci.mjs", "nlx": "bin/nlx.mjs", "nun": "bin/nun.mjs", "nup": "bin/nup.mjs" } }, "sha512-9q/yCljni37pkMr4sPrI3G4jqdIk074+iukc5aFJl7kmDCCsiJrbZ6zKxnES1Gwg+i9RcDZwvktl23puGslmvA=="], - "@anthropic-ai/claude-agent-sdk": ["@anthropic-ai/claude-agent-sdk@0.2.74", "", { "optionalDependencies": { "@img/sharp-darwin-arm64": "^0.34.2", "@img/sharp-darwin-x64": "^0.34.2", "@img/sharp-linux-arm": "^0.34.2", "@img/sharp-linux-arm64": "^0.34.2", "@img/sharp-linux-x64": "^0.34.2", "@img/sharp-linuxmusl-arm64": "^0.34.2", "@img/sharp-linuxmusl-x64": "^0.34.2", "@img/sharp-win32-arm64": "^0.34.2", "@img/sharp-win32-x64": "^0.34.2" }, "peerDependencies": { "zod": "^4.0.0" } }, "sha512-S/SFSSbZHPL1HiQxAqCCxU3iHuE5nM+ir0OK1n0bZ+9hlVUH7OOn88AsV9s54E0c1kvH9YF4/foWH8J9kICsBw=="], + "@anthropic-ai/claude-agent-sdk": ["@anthropic-ai/claude-agent-sdk@0.2.121", "", { "dependencies": { "@anthropic-ai/sdk": "^0.81.0", "@modelcontextprotocol/sdk": "^1.29.0" }, "optionalDependencies": { "@anthropic-ai/claude-agent-sdk-darwin-arm64": "0.2.121", "@anthropic-ai/claude-agent-sdk-darwin-x64": "0.2.121", "@anthropic-ai/claude-agent-sdk-linux-arm64": "0.2.121", "@anthropic-ai/claude-agent-sdk-linux-arm64-musl": "0.2.121", "@anthropic-ai/claude-agent-sdk-linux-x64": "0.2.121", "@anthropic-ai/claude-agent-sdk-linux-x64-musl": "0.2.121", "@anthropic-ai/claude-agent-sdk-win32-arm64": "0.2.121", "@anthropic-ai/claude-agent-sdk-win32-x64": "0.2.121" }, "peerDependencies": { "zod": "^4.0.0" } }, "sha512-hwZNYTkGLKVixd/V/OCJwfH/SdfxZXGV0m6wvy5EBq6qfB+lvJTRz/MSOSa7dHqo4/F7zJY68crEEca68Wrxpw=="], - "@anthropic-ai/sdk": ["@anthropic-ai/sdk@0.74.0", "", { "dependencies": { "json-schema-to-ts": "^3.1.1" }, "peerDependencies": { "zod": "^3.25.0 || ^4.0.0" }, "optionalPeers": ["zod"], "bin": { "anthropic-ai-sdk": "bin/cli" } }, "sha512-srbJV7JKsc5cQ6eVuFzjZO7UR3xEPJqPamHFIe29bs38Ij2IripoAhC0S5NslNbaFUYqBKypmmpzMTpqfHEUDw=="], + "@anthropic-ai/claude-agent-sdk-darwin-arm64": ["@anthropic-ai/claude-agent-sdk-darwin-arm64@0.2.121", "", { "os": "darwin", "cpu": "arm64" }, "sha512-zVHcXvx6Hl/glDcOCH+EyNx4KPE9cMGLk42eEBSZe014tAN5W8bwM/By08iM6dxijnpH0NQRNNEAW+BryWzuDg=="], + + "@anthropic-ai/claude-agent-sdk-darwin-x64": ["@anthropic-ai/claude-agent-sdk-darwin-x64@0.2.121", "", { "os": "darwin", "cpu": "x64" }, "sha512-lIXdqKj+bpfDxCk/eU1F1TXNqsIsLTRrkUG/wx19WIGZ8gLUmmVSveUKGlNegTs7S6evMvuezprJzDJT4TcvPA=="], + + "@anthropic-ai/claude-agent-sdk-linux-arm64": ["@anthropic-ai/claude-agent-sdk-linux-arm64@0.2.121", "", { "os": "linux", "cpu": "arm64" }, "sha512-AQSnJzaiFvQpUPfO1tWLvsHgb6KNar4QYEQ/5/sk1itfgr3Fx9gxTreq43wX7AXSvkBX1QlDaP1aR1sfM/g/lQ=="], + + "@anthropic-ai/claude-agent-sdk-linux-arm64-musl": ["@anthropic-ai/claude-agent-sdk-linux-arm64-musl@0.2.121", "", { "os": "linux", "cpu": "arm64" }, "sha512-4XaGK+dRBYy7krln7BrDG0WsdE6ejUSgHjWHlUGXoubFfZUvls4GSahLcYjJBArLi4dLnxKw8zEuiQguPAIbrw=="], + + "@anthropic-ai/claude-agent-sdk-linux-x64": ["@anthropic-ai/claude-agent-sdk-linux-x64@0.2.121", "", { "os": "linux", "cpu": "x64" }, "sha512-DJUgpm7au086WaQV/S7BGOt2M8D90spGZRizT3twYsacf1BxzK1qsXqB/Pw1lUjPy6pI107pml/TaPzWuS/Vzg=="], + + "@anthropic-ai/claude-agent-sdk-linux-x64-musl": ["@anthropic-ai/claude-agent-sdk-linux-x64-musl@0.2.121", "", { "os": "linux", "cpu": "x64" }, "sha512-sQoGIgzLlBRrwizxsCV/lbaEuxXom/cfOwlDtQ2HnS1IzDDSjSf5d5pugpWItkOyXBWcHzMUu731WTTutvd/BQ=="], + + "@anthropic-ai/claude-agent-sdk-win32-arm64": ["@anthropic-ai/claude-agent-sdk-win32-arm64@0.2.121", "", { "os": "win32", "cpu": "arm64" }, "sha512-6n/NHkHxs0/lCJX3XPADjo1EFzXBf0IwYz/nyzJGBCDJjGKmgTe0i8eYBr/hviwt1/OPeK7dmVzVSVl6EL9Azg=="], + + "@anthropic-ai/claude-agent-sdk-win32-x64": ["@anthropic-ai/claude-agent-sdk-win32-x64@0.2.121", "", { "os": "win32", "cpu": "x64" }, "sha512-v2/R918/t94cCwc6rmbxk+UYeQPtF2oBLtQAk+cT0M60hvqmCZO2noyZx5uTp8TQncOlG4MkINIeNY2yfmWSoQ=="], + + "@anthropic-ai/sdk": ["@anthropic-ai/sdk@0.81.0", "", { "dependencies": { "json-schema-to-ts": "^3.1.1" }, "peerDependencies": { "zod": "^3.25.0 || ^4.0.0" }, "optionalPeers": ["zod"], "bin": { "anthropic-ai-sdk": "bin/cli" } }, "sha512-D4K5PvEV6wPiRtVlVsJHIUhHAmOZ6IT/I9rKlTf84gR7GyyAurPJK7z9BOf/AZqC5d1DhYQGJNKRmV+q8dGhgw=="], "@archon/adapters": ["@archon/adapters@workspace:packages/adapters"], @@ -471,9 +487,9 @@ "@img/colour": ["@img/colour@1.1.0", "", {}, "sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ=="], - "@img/sharp-darwin-arm64": ["@img/sharp-darwin-arm64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-arm64": "1.2.4" }, "os": "darwin", "cpu": "arm64" }, "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w=="], + "@img/sharp-darwin-arm64": ["@img/sharp-darwin-arm64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-arm64": "1.0.4" }, "os": "darwin", "cpu": "arm64" }, "sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ=="], - "@img/sharp-darwin-x64": ["@img/sharp-darwin-x64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-x64": "1.2.4" }, "os": "darwin", "cpu": "x64" }, "sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw=="], + "@img/sharp-darwin-x64": ["@img/sharp-darwin-x64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-x64": "1.0.4" }, "os": "darwin", "cpu": "x64" }, "sha512-fyHac4jIc1ANYGRDxtiqelIbdWkIuQaI84Mv45KvGRRxSAa7o7d1ZKAOBaYbnepLC1WqxfpimdeWfvqqSGwR2Q=="], "@img/sharp-libvips-darwin-arm64": ["@img/sharp-libvips-darwin-arm64@1.0.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-XblONe153h0O2zuFfTAbQYAX2JhYmDHeWikp1LM9Hul9gVPjFY427k6dFEcOL72O01QxQsWi761svJ/ev9xEDg=="], @@ -495,9 +511,9 @@ "@img/sharp-libvips-linuxmusl-x64": ["@img/sharp-libvips-linuxmusl-x64@1.0.4", "", { "os": "linux", "cpu": "x64" }, "sha512-viYN1KX9m+/hGkJtvYYp+CCLgnJXwiQB39damAO7WMdKWlIhmYTfHjwSbQeUK/20vY154mwezd9HflVFM1wVSw=="], - "@img/sharp-linux-arm": ["@img/sharp-linux-arm@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm": "1.2.4" }, "os": "linux", "cpu": "arm" }, "sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw=="], + "@img/sharp-linux-arm": ["@img/sharp-linux-arm@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm": "1.0.5" }, "os": "linux", "cpu": "arm" }, "sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ=="], - "@img/sharp-linux-arm64": ["@img/sharp-linux-arm64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm64": "1.2.4" }, "os": "linux", "cpu": "arm64" }, "sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg=="], + "@img/sharp-linux-arm64": ["@img/sharp-linux-arm64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm64": "1.0.4" }, "os": "linux", "cpu": "arm64" }, "sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA=="], "@img/sharp-linux-ppc64": ["@img/sharp-linux-ppc64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-ppc64": "1.2.4" }, "os": "linux", "cpu": "ppc64" }, "sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA=="], @@ -505,11 +521,11 @@ "@img/sharp-linux-s390x": ["@img/sharp-linux-s390x@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-s390x": "1.0.4" }, "os": "linux", "cpu": "s390x" }, "sha512-y/5PCd+mP4CA/sPDKl2961b+C9d+vPAveS33s6Z3zfASk2j5upL6fXVPZi7ztePZ5CuH+1kW8JtvxgbuXHRa4Q=="], - "@img/sharp-linux-x64": ["@img/sharp-linux-x64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-x64": "1.2.4" }, "os": "linux", "cpu": "x64" }, "sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ=="], + "@img/sharp-linux-x64": ["@img/sharp-linux-x64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-x64": "1.0.4" }, "os": "linux", "cpu": "x64" }, "sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA=="], - "@img/sharp-linuxmusl-arm64": ["@img/sharp-linuxmusl-arm64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-arm64": "1.2.4" }, "os": "linux", "cpu": "arm64" }, "sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg=="], + "@img/sharp-linuxmusl-arm64": ["@img/sharp-linuxmusl-arm64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-arm64": "1.0.4" }, "os": "linux", "cpu": "arm64" }, "sha512-XrHMZwGQGvJg2V/oRSUfSAfjfPxO+4DkiRh6p2AFjLQztWUuY/o8Mq0eMQVIY7HJ1CDQUJlxGGZRw1a5bqmd1g=="], - "@img/sharp-linuxmusl-x64": ["@img/sharp-linuxmusl-x64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-x64": "1.2.4" }, "os": "linux", "cpu": "x64" }, "sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q=="], + "@img/sharp-linuxmusl-x64": ["@img/sharp-linuxmusl-x64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-x64": "1.0.4" }, "os": "linux", "cpu": "x64" }, "sha512-WT+d/cgqKkkKySYmqoZ8y3pxx7lx9vVejxW/W4DOFMYVSkErR+w7mf2u8m/y4+xHe7yY9DAXQMWQhpnMuFfScw=="], "@img/sharp-wasm32": ["@img/sharp-wasm32@0.33.5", "", { "dependencies": { "@emnapi/runtime": "^1.2.0" }, "cpu": "none" }, "sha512-ykUW4LVGaMcU9lu9thv85CbRMAwfeadCJHRsg2GmeRa/cJxsVY9Rbd57JcMxBkKHag5U/x7TSBpScF4U8ElVzg=="], @@ -517,7 +533,7 @@ "@img/sharp-win32-ia32": ["@img/sharp-win32-ia32@0.33.5", "", { "os": "win32", "cpu": "ia32" }, "sha512-T36PblLaTwuVJ/zw/LaH0PdZkRz5rd3SmMHX8GSmR7vtNSP5Z6bQkExdSK7xGWyxLw4sUknBuugTelgw2faBbQ=="], - "@img/sharp-win32-x64": ["@img/sharp-win32-x64@0.34.5", "", { "os": "win32", "cpu": "x64" }, "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw=="], + "@img/sharp-win32-x64": ["@img/sharp-win32-x64@0.33.5", "", { "os": "win32", "cpu": "x64" }, "sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg=="], "@inquirer/ansi": ["@inquirer/ansi@1.0.2", "", {}, "sha512-S8qNSZiYzFd0wAcyG5AXCvUHC5Sr7xpZ9wZ2py9XR88jUz8wooStVx5M6dRzczbBWjic9NP7+rY0Xi7qqK/aMQ=="], @@ -541,7 +557,7 @@ "@mdx-js/mdx": ["@mdx-js/mdx@3.1.1", "", { "dependencies": { "@types/estree": "^1.0.0", "@types/estree-jsx": "^1.0.0", "@types/hast": "^3.0.0", "@types/mdx": "^2.0.0", "acorn": "^8.0.0", "collapse-white-space": "^2.0.0", "devlop": "^1.0.0", "estree-util-is-identifier-name": "^3.0.0", "estree-util-scope": "^1.0.0", "estree-walker": "^3.0.0", "hast-util-to-jsx-runtime": "^2.0.0", "markdown-extensions": "^2.0.0", "recma-build-jsx": "^1.0.0", "recma-jsx": "^1.0.0", "recma-stringify": "^1.0.0", "rehype-recma": "^1.0.0", "remark-mdx": "^3.0.0", "remark-parse": "^11.0.0", "remark-rehype": "^11.0.0", "source-map": "^0.7.0", "unified": "^11.0.0", "unist-util-position-from-estree": "^2.0.0", "unist-util-stringify-position": "^4.0.0", "unist-util-visit": "^5.0.0", "vfile": "^6.0.0" } }, "sha512-f6ZO2ifpwAQIpzGWaBQT2TXxPv6z3RBzQKpVftEWN78Vl/YweF1uwussDx8ECAXVtr3Rs89fKyG9YlzUs9DyGQ=="], - "@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.27.1", "", { "dependencies": { "@hono/node-server": "^1.19.9", "ajv": "^8.17.1", "ajv-formats": "^3.0.1", "content-type": "^1.0.5", "cors": "^2.8.5", "cross-spawn": "^7.0.5", "eventsource": "^3.0.2", "eventsource-parser": "^3.0.0", "express": "^5.2.1", "express-rate-limit": "^8.2.1", "hono": "^4.11.4", "jose": "^6.1.3", "json-schema-typed": "^8.0.2", "pkce-challenge": "^5.0.0", "raw-body": "^3.0.0", "zod": "^3.25 || ^4.0", "zod-to-json-schema": "^3.25.1" }, "peerDependencies": { "@cfworker/json-schema": "^4.1.1" }, "optionalPeers": ["@cfworker/json-schema"] }, "sha512-sr6GbP+4edBwFndLbM60gf07z0FQ79gaExpnsjMGePXqFcSSb7t6iscpjk9DhFhwd+mTEQrzNafGP8/iGGFYaA=="], + "@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.29.0", "", { "dependencies": { "@hono/node-server": "^1.19.9", "ajv": "^8.17.1", "ajv-formats": "^3.0.1", "content-type": "^1.0.5", "cors": "^2.8.5", "cross-spawn": "^7.0.5", "eventsource": "^3.0.2", "eventsource-parser": "^3.0.0", "express": "^5.2.1", "express-rate-limit": "^8.2.1", "hono": "^4.11.4", "jose": "^6.1.3", "json-schema-typed": "^8.0.2", "pkce-challenge": "^5.0.0", "raw-body": "^3.0.0", "zod": "^3.25 || ^4.0", "zod-to-json-schema": "^3.25.1" }, "peerDependencies": { "@cfworker/json-schema": "^4.1.1" }, "optionalPeers": ["@cfworker/json-schema"] }, "sha512-zo37mZA9hJWpULgkRpowewez1y6ML5GsXJPY8FI0tBBCd77HEvza4jDqRKOXgHNn867PVGCyTdzqpz0izu5ZjQ=="], "@mswjs/interceptors": ["@mswjs/interceptors@0.41.3", "", { "dependencies": { "@open-draft/deferred-promise": "^2.2.0", "@open-draft/logger": "^0.3.0", "@open-draft/until": "^2.0.0", "is-node-process": "^1.2.0", "outvariant": "^1.4.3", "strict-event-emitter": "^0.5.1" } }, "sha512-cXu86tF4VQVfwz8W1SPbhoRyHJkti6mjH/XJIxp40jhO4j2k1m4KYrEykxqWPkFF3vrK4rgQppBh//AwyGSXPA=="], @@ -587,21 +603,21 @@ "@open-draft/until": ["@open-draft/until@2.1.0", "", {}, "sha512-U69T3ItWHvLwGg5eJ0n3I62nWuE6ilHlmz7zM0npLBRvPRd7e6NYmg54vvRtP5mZG7kZqZCFVdsTWo7BPtBujg=="], - "@openai/codex": ["@openai/codex@0.116.0", "", { "optionalDependencies": { "@openai/codex-darwin-arm64": "npm:@openai/codex@0.116.0-darwin-arm64", "@openai/codex-darwin-x64": "npm:@openai/codex@0.116.0-darwin-x64", "@openai/codex-linux-arm64": "npm:@openai/codex@0.116.0-linux-arm64", "@openai/codex-linux-x64": "npm:@openai/codex@0.116.0-linux-x64", "@openai/codex-win32-arm64": "npm:@openai/codex@0.116.0-win32-arm64", "@openai/codex-win32-x64": "npm:@openai/codex@0.116.0-win32-x64" }, "bin": { "codex": "bin/codex.js" } }, "sha512-K6q9P2ZmpnzGmpS6Ybjvsdtvu8AbJx3f/Z4KmjH1u85StSS9TWMSQB8z0PPObKMejbtiIkHwhGyEIHi4iBYjig=="], + "@openai/codex": ["@openai/codex@0.125.0", "", { "optionalDependencies": { "@openai/codex-darwin-arm64": "npm:@openai/codex@0.125.0-darwin-arm64", "@openai/codex-darwin-x64": "npm:@openai/codex@0.125.0-darwin-x64", "@openai/codex-linux-arm64": "npm:@openai/codex@0.125.0-linux-arm64", "@openai/codex-linux-x64": "npm:@openai/codex@0.125.0-linux-x64", "@openai/codex-win32-arm64": "npm:@openai/codex@0.125.0-win32-arm64", "@openai/codex-win32-x64": "npm:@openai/codex@0.125.0-win32-x64" }, "bin": { "codex": "bin/codex.js" } }, "sha512-GiE9wlgL95u/5BRirY5d3EaRLU1tu7Y1R09R8lCHHVmcQdSmhS809FdPDWH3gIYHS7ZriAPqXwJ3aLA0WKl40Q=="], - "@openai/codex-darwin-arm64": ["@openai/codex@0.116.0-darwin-arm64", "", { "os": "darwin", "cpu": "arm64" }, "sha512-WkdL083p8uMeASpg8bwV0DPGgzkm48LjN3MyU2m/YukujbiLnknAmG29O2q2rFCLm0oLSDIGUK8EnXA4ZcAF9Q=="], + "@openai/codex-darwin-arm64": ["@openai/codex@0.125.0-darwin-arm64", "", { "os": "darwin", "cpu": "arm64" }, "sha512-Gn2fHiSO0XgyHp1OSd5DWUTm66Bv9UEuipW5pVEj1E+hWZCOrdqnYttllKFWtRGj5yiKefNX3JIxONgh/ZwlOQ=="], - "@openai/codex-darwin-x64": ["@openai/codex@0.116.0-darwin-x64", "", { "os": "darwin", "cpu": "x64" }, "sha512-Ax8uTwYSNIwGrzcNRcn0jJQhZzNcKGDbbn00Emde7gGOemjSLhRALjUaKjckAaW5xWnNqHTGdtzzPB4phNlDYg=="], + "@openai/codex-darwin-x64": ["@openai/codex@0.125.0-darwin-x64", "", { "os": "darwin", "cpu": "x64" }, "sha512-TZ5Lek2X/UXTI9LXFxzarvQaJeuTrqVh4POc7soO/8RclVnCxADnCf15sivxLd5eiFW4t0myGoeVoM4lciRiRg=="], - "@openai/codex-linux-arm64": ["@openai/codex@0.116.0-linux-arm64", "", { "os": "linux", "cpu": "arm64" }, "sha512-X7cL8rBSGDB+RSZc2FoKiqcMVeLPMmo06bkss/en4lLQsV1XG2DZI56WuXg92IOX3SjYl6Av/eOWgsb1t3UeLQ=="], + "@openai/codex-linux-arm64": ["@openai/codex@0.125.0-linux-arm64", "", { "os": "linux", "cpu": "arm64" }, "sha512-pPnJoJD6rZ2Iin0zNt/up36bO2/EOp2B+1/rPHu/lSq3PJbT3Fmnfut2kJy5LylXb7bGA2XQbtqOogZzIbnlkA=="], - "@openai/codex-linux-x64": ["@openai/codex@0.116.0-linux-x64", "", { "os": "linux", "cpu": "x64" }, "sha512-S9InOgJT3tj6uQp55NqrCA1k5tklwFaH00JdC2ElbRmxchm7ard4WxHSJZX9TiY8enj4cQoLIC04NFTUCO+/PQ=="], + "@openai/codex-linux-x64": ["@openai/codex@0.125.0-linux-x64", "", { "os": "linux", "cpu": "x64" }, "sha512-K2NTTEeBpz/G+N2x17UGWfauRt3So+ir4f+U/60l5PPnYEJB/w3YZrlXo2G9og8Dm9BqtoBAjoPV74sRv9tWWQ=="], - "@openai/codex-sdk": ["@openai/codex-sdk@0.116.0", "", { "dependencies": { "@openai/codex": "0.116.0" } }, "sha512-qrn1Pu5G1GJ9w4m/Lk3L3466ulMGG9SfyR0LPAaXdisuQI1rqgoUOuoZ4byX7cCzn0x1g2+WPc0apZgjMEK04Q=="], + "@openai/codex-sdk": ["@openai/codex-sdk@0.125.0", "", { "dependencies": { "@openai/codex": "0.125.0" } }, "sha512-1xCIHdSbQVF880nJ2aVWdPIsWZbSpKODwuP9y/gvtChDYhYfYEW0DKp2H8ZlctkzIjlzS/WzYmP6ZZPHIvs2Dg=="], - "@openai/codex-win32-arm64": ["@openai/codex@0.116.0-win32-arm64", "", { "os": "win32", "cpu": "arm64" }, "sha512-kX2oAUzkgZX9OsYpd4omv9IGf+9VWj4Vy3UtIAnQKBu1DTSzmTJmXDuDn87mkyUciSZadm2QbeqQQzm2NC0NYw=="], + "@openai/codex-win32-arm64": ["@openai/codex@0.125.0-win32-arm64", "", { "os": "win32", "cpu": "arm64" }, "sha512-zxoUakw9oIHIFrAyk400XkkLBJFA6nOym0NDq6sQ/jhdcYraKqNSRCII2nsBwZHk+/4zgUvuk52iuutgysY/rQ=="], - "@openai/codex-win32-x64": ["@openai/codex@0.116.0-win32-x64", "", { "os": "win32", "cpu": "x64" }, "sha512-6sBIMOoA9FNuxQvCCnK0P548Wqrlk3I9SMdtOCUg2zYzYU7jOF2mWS1VpRQ6R+Jvo2x50dxeJZ+W37dBmXfprw=="], + "@openai/codex-win32-x64": ["@openai/codex@0.125.0-win32-x64", "", { "os": "win32", "cpu": "x64" }, "sha512-ofpOK+OWH5QFuUZ9pTM0d/PcXUXiIP5z5DpRcE9MlucJoyOl4Zy4Nu3NcuHF4YzCkZMQb6x3j0tjDEPHKqNQzw=="], "@oslojs/encoding": ["@oslojs/encoding@1.1.0", "", {}, "sha512-70wQhgYmndg4GCPxPPxPGevRKqTIJ2Nh4OkiMWmDAVYsTQ+Ta7Sq+rPevXyXGdzr30/qZBnyOalCszoMxlyldQ=="], @@ -2449,7 +2465,7 @@ "@antfu/ni/tinyexec": ["tinyexec@1.0.2", "", {}, "sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg=="], - "@archon/providers/@anthropic-ai/claude-agent-sdk": ["@anthropic-ai/claude-agent-sdk@0.2.89", "", { "dependencies": { "@anthropic-ai/sdk": "^0.74.0", "@modelcontextprotocol/sdk": "^1.27.1" }, "optionalDependencies": { "@img/sharp-darwin-arm64": "^0.34.2", "@img/sharp-darwin-x64": "^0.34.2", "@img/sharp-linux-arm": "^0.34.2", "@img/sharp-linux-arm64": "^0.34.2", "@img/sharp-linux-x64": "^0.34.2", "@img/sharp-linuxmusl-arm64": "^0.34.2", "@img/sharp-linuxmusl-x64": "^0.34.2", "@img/sharp-win32-arm64": "^0.34.2", "@img/sharp-win32-x64": "^0.34.2" }, "peerDependencies": { "zod": "^4.0.0" } }, "sha512-/9W0lyBGuGHw1uu7pQafsp6BLpxfqCv1QYE0Z/eZTX6lGHht4j4Q+O3UImzjsiyEE9cGkOAwZBGAEHDEqt+QUA=="], + "@anthropic-ai/claude-agent-sdk/zod": ["zod@4.3.6", "", {}, "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg=="], "@astrojs/markdown-remark/remark-parse": ["remark-parse@11.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "mdast-util-from-markdown": "^2.0.0", "micromark-util-types": "^2.0.0", "unified": "^11.0.0" } }, "sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA=="], @@ -2491,20 +2507,6 @@ "@expressive-code/plugin-shiki/shiki": ["shiki@3.23.0", "", { "dependencies": { "@shikijs/core": "3.23.0", "@shikijs/engine-javascript": "3.23.0", "@shikijs/engine-oniguruma": "3.23.0", "@shikijs/langs": "3.23.0", "@shikijs/themes": "3.23.0", "@shikijs/types": "3.23.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-55Dj73uq9ZXL5zyeRPzHQsK7Nbyt6Y10k5s7OjuFZGMhpp4r/rsLBH0o/0fstIzX1Lep9VxefWljK/SKCzygIA=="], - "@img/sharp-darwin-arm64/@img/sharp-libvips-darwin-arm64": ["@img/sharp-libvips-darwin-arm64@1.2.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g=="], - - "@img/sharp-darwin-x64/@img/sharp-libvips-darwin-x64": ["@img/sharp-libvips-darwin-x64@1.2.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg=="], - - "@img/sharp-linux-arm/@img/sharp-libvips-linux-arm": ["@img/sharp-libvips-linux-arm@1.2.4", "", { "os": "linux", "cpu": "arm" }, "sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A=="], - - "@img/sharp-linux-arm64/@img/sharp-libvips-linux-arm64": ["@img/sharp-libvips-linux-arm64@1.2.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw=="], - - "@img/sharp-linux-x64/@img/sharp-libvips-linux-x64": ["@img/sharp-libvips-linux-x64@1.2.4", "", { "os": "linux", "cpu": "x64" }, "sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw=="], - - "@img/sharp-linuxmusl-arm64/@img/sharp-libvips-linuxmusl-arm64": ["@img/sharp-libvips-linuxmusl-arm64@1.2.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw=="], - - "@img/sharp-linuxmusl-x64/@img/sharp-libvips-linuxmusl-x64": ["@img/sharp-libvips-linuxmusl-x64@1.2.4", "", { "os": "linux", "cpu": "x64" }, "sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg=="], - "@inquirer/core/wrap-ansi": ["wrap-ansi@6.2.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA=="], "@mdx-js/mdx/estree-walker": ["estree-walker@3.0.3", "", { "dependencies": { "@types/estree": "^1.0.0" } }, "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g=="], @@ -2517,6 +2519,8 @@ "@modelcontextprotocol/sdk/ajv": ["ajv@8.18.0", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2" } }, "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A=="], + "@modelcontextprotocol/sdk/zod": ["zod@4.3.6", "", {}, "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg=="], + "@redocly/ajv/json-schema-traverse": ["json-schema-traverse@1.0.0", "", {}, "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="], "@redocly/openapi-core/colorette": ["colorette@1.4.0", "", {}, "sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g=="], @@ -2691,28 +2695,14 @@ "retext-stringify/unified": ["unified@11.0.5", "", { "dependencies": { "@types/unist": "^3.0.0", "bail": "^2.0.0", "devlop": "^1.0.0", "extend": "^3.0.0", "is-plain-obj": "^4.0.0", "trough": "^2.0.0", "vfile": "^6.0.0" } }, "sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA=="], + "shadcn/@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.27.1", "", { "dependencies": { "@hono/node-server": "^1.19.9", "ajv": "^8.17.1", "ajv-formats": "^3.0.1", "content-type": "^1.0.5", "cors": "^2.8.5", "cross-spawn": "^7.0.5", "eventsource": "^3.0.2", "eventsource-parser": "^3.0.0", "express": "^5.2.1", "express-rate-limit": "^8.2.1", "hono": "^4.11.4", "jose": "^6.1.3", "json-schema-typed": "^8.0.2", "pkce-challenge": "^5.0.0", "raw-body": "^3.0.0", "zod": "^3.25 || ^4.0", "zod-to-json-schema": "^3.25.1" }, "peerDependencies": { "@cfworker/json-schema": "^4.1.1" }, "optionalPeers": ["@cfworker/json-schema"] }, "sha512-sr6GbP+4edBwFndLbM60gf07z0FQ79gaExpnsjMGePXqFcSSb7t6iscpjk9DhFhwd+mTEQrzNafGP8/iGGFYaA=="], + "shadcn/commander": ["commander@14.0.3", "", {}, "sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw=="], "shadcn/execa": ["execa@9.6.1", "", { "dependencies": { "@sindresorhus/merge-streams": "^4.0.0", "cross-spawn": "^7.0.6", "figures": "^6.1.0", "get-stream": "^9.0.0", "human-signals": "^8.0.1", "is-plain-obj": "^4.1.0", "is-stream": "^4.0.1", "npm-run-path": "^6.0.0", "pretty-ms": "^9.2.0", "signal-exit": "^4.1.0", "strip-final-newline": "^4.0.0", "yoctocolors": "^2.1.1" } }, "sha512-9Be3ZoN4LmYR90tUoVu2te2BsbzHfhJyfEiAVfz7N5/zv+jduIfLrV2xdQXOHbaD6KgpGdO9PRPM1Y4Q9QkPkA=="], "shadcn/node-fetch": ["node-fetch@3.3.2", "", { "dependencies": { "data-uri-to-buffer": "^4.0.0", "fetch-blob": "^3.1.4", "formdata-polyfill": "^4.0.10" } }, "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA=="], - "sharp/@img/sharp-darwin-arm64": ["@img/sharp-darwin-arm64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-arm64": "1.0.4" }, "os": "darwin", "cpu": "arm64" }, "sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ=="], - - "sharp/@img/sharp-darwin-x64": ["@img/sharp-darwin-x64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-x64": "1.0.4" }, "os": "darwin", "cpu": "x64" }, "sha512-fyHac4jIc1ANYGRDxtiqelIbdWkIuQaI84Mv45KvGRRxSAa7o7d1ZKAOBaYbnepLC1WqxfpimdeWfvqqSGwR2Q=="], - - "sharp/@img/sharp-linux-arm": ["@img/sharp-linux-arm@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm": "1.0.5" }, "os": "linux", "cpu": "arm" }, "sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ=="], - - "sharp/@img/sharp-linux-arm64": ["@img/sharp-linux-arm64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm64": "1.0.4" }, "os": "linux", "cpu": "arm64" }, "sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA=="], - - "sharp/@img/sharp-linux-x64": ["@img/sharp-linux-x64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-x64": "1.0.4" }, "os": "linux", "cpu": "x64" }, "sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA=="], - - "sharp/@img/sharp-linuxmusl-arm64": ["@img/sharp-linuxmusl-arm64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-arm64": "1.0.4" }, "os": "linux", "cpu": "arm64" }, "sha512-XrHMZwGQGvJg2V/oRSUfSAfjfPxO+4DkiRh6p2AFjLQztWUuY/o8Mq0eMQVIY7HJ1CDQUJlxGGZRw1a5bqmd1g=="], - - "sharp/@img/sharp-linuxmusl-x64": ["@img/sharp-linuxmusl-x64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-x64": "1.0.4" }, "os": "linux", "cpu": "x64" }, "sha512-WT+d/cgqKkkKySYmqoZ8y3pxx7lx9vVejxW/W4DOFMYVSkErR+w7mf2u8m/y4+xHe7yY9DAXQMWQhpnMuFfScw=="], - - "sharp/@img/sharp-win32-x64": ["@img/sharp-win32-x64@0.33.5", "", { "os": "win32", "cpu": "x64" }, "sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg=="], - "sitemap/@types/node": ["@types/node@24.12.2", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-A1sre26ke7HDIuY/M23nd9gfB+nrmhtYyMINbjI1zHJxYteKR6qSMX56FsmjMcDb3SMcjJg5BiRRgOCC/yBD0g=="], "slice-ansi/ansi-styles": ["ansi-styles@6.2.3", "", {}, "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg=="], @@ -2813,6 +2803,10 @@ "ajv-formats/ajv/json-schema-traverse": ["json-schema-traverse@1.0.0", "", {}, "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="], + "astro/sharp/@img/sharp-darwin-arm64": ["@img/sharp-darwin-arm64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-arm64": "1.2.4" }, "os": "darwin", "cpu": "arm64" }, "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w=="], + + "astro/sharp/@img/sharp-darwin-x64": ["@img/sharp-darwin-x64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-x64": "1.2.4" }, "os": "darwin", "cpu": "x64" }, "sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw=="], + "astro/sharp/@img/sharp-libvips-darwin-arm64": ["@img/sharp-libvips-darwin-arm64@1.2.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g=="], "astro/sharp/@img/sharp-libvips-darwin-x64": ["@img/sharp-libvips-darwin-x64@1.2.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg=="], @@ -2829,12 +2823,24 @@ "astro/sharp/@img/sharp-libvips-linuxmusl-x64": ["@img/sharp-libvips-linuxmusl-x64@1.2.4", "", { "os": "linux", "cpu": "x64" }, "sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg=="], + "astro/sharp/@img/sharp-linux-arm": ["@img/sharp-linux-arm@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm": "1.2.4" }, "os": "linux", "cpu": "arm" }, "sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw=="], + + "astro/sharp/@img/sharp-linux-arm64": ["@img/sharp-linux-arm64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm64": "1.2.4" }, "os": "linux", "cpu": "arm64" }, "sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg=="], + "astro/sharp/@img/sharp-linux-s390x": ["@img/sharp-linux-s390x@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-s390x": "1.2.4" }, "os": "linux", "cpu": "s390x" }, "sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg=="], + "astro/sharp/@img/sharp-linux-x64": ["@img/sharp-linux-x64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-x64": "1.2.4" }, "os": "linux", "cpu": "x64" }, "sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ=="], + + "astro/sharp/@img/sharp-linuxmusl-arm64": ["@img/sharp-linuxmusl-arm64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-arm64": "1.2.4" }, "os": "linux", "cpu": "arm64" }, "sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg=="], + + "astro/sharp/@img/sharp-linuxmusl-x64": ["@img/sharp-linuxmusl-x64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-x64": "1.2.4" }, "os": "linux", "cpu": "x64" }, "sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q=="], + "astro/sharp/@img/sharp-wasm32": ["@img/sharp-wasm32@0.34.5", "", { "dependencies": { "@emnapi/runtime": "^1.7.0" }, "cpu": "none" }, "sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw=="], "astro/sharp/@img/sharp-win32-ia32": ["@img/sharp-win32-ia32@0.34.5", "", { "os": "win32", "cpu": "ia32" }, "sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg=="], + "astro/sharp/@img/sharp-win32-x64": ["@img/sharp-win32-x64@0.34.5", "", { "os": "win32", "cpu": "x64" }, "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw=="], + "cliui/string-width/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], "cliui/string-width/is-fullwidth-code-point": ["is-fullwidth-code-point@3.0.0", "", {}, "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="], @@ -2983,6 +2989,8 @@ "retext/unified/trough": ["trough@2.2.0", "", {}, "sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw=="], + "shadcn/@modelcontextprotocol/sdk/ajv": ["ajv@8.18.0", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2" } }, "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A=="], + "shadcn/execa/get-stream": ["get-stream@9.0.1", "", { "dependencies": { "@sec-ant/readable-stream": "^0.4.1", "is-stream": "^4.0.1" } }, "sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA=="], "shadcn/execa/human-signals": ["human-signals@8.0.1", "", {}, "sha512-eKCa6bwnJhvxj14kZk5NCPc6Hb6BdsU9DZcOnmQKSnO1VKrfV0zCvtttPZUsBvjmNDn8rpcJfpwSYnHBjc95MQ=="], @@ -3091,6 +3099,8 @@ "remark-parse/mdast-util-from-markdown/unist-util-stringify-position/@types/unist": ["@types/unist@2.0.11", "", {}, "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA=="], + "shadcn/@modelcontextprotocol/sdk/ajv/json-schema-traverse": ["json-schema-traverse@1.0.0", "", {}, "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="], + "shadcn/execa/npm-run-path/path-key": ["path-key@4.0.0", "", {}, "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ=="], "telegramify-markdown/remark-gfm/mdast-util-gfm/mdast-util-gfm-autolink-literal": ["mdast-util-gfm-autolink-literal@0.1.3", "", { "dependencies": { "ccount": "^1.0.0", "mdast-util-find-and-replace": "^1.1.0", "micromark": "^2.11.3" } }, "sha512-GjmLjWrXg1wqMIO9+ZsRik/s7PLwTaeCHVB7vRxUwLntZc8mzmTsLVr6HW1yLokcnhfURsn5zmSVdi3/xWWu1A=="], diff --git a/package.json b/package.json index 2fceb51a72..6c2521b633 100644 --- a/package.json +++ b/package.json @@ -52,6 +52,6 @@ "axios": "^1.15.0" }, "dependencies": { - "@anthropic-ai/claude-agent-sdk": "^0.2.74" + "@anthropic-ai/claude-agent-sdk": "^0.2.121" } } diff --git a/packages/providers/package.json b/packages/providers/package.json index 9e4e278b8e..cc0467305d 100644 --- a/packages/providers/package.json +++ b/packages/providers/package.json @@ -21,9 +21,9 @@ "type-check": "bun x tsc --noEmit" }, "dependencies": { - "@anthropic-ai/claude-agent-sdk": "^0.2.89", + "@anthropic-ai/claude-agent-sdk": "^0.2.121", "@archon/paths": "workspace:*", - "@openai/codex-sdk": "^0.116.0" + "@openai/codex-sdk": "^0.125.0" }, "devDependencies": { "pino": "^9" From 6f5aa458b666e804dfa880ef9ee61dab9b57ae46 Mon Sep 17 00:00:00 2001 From: Rasmus Widing <152263317+Wirasm@users.noreply.github.com> Date: Tue, 28 Apr 2026 15:20:24 +0300 Subject: [PATCH 05/11] fix(cli): lazy-import bundled skill files so non-setup commands don't crash on missing source (#1394) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The 18 top-level `import … with { type: 'text' }` statements in `bundled-skill.ts` resolve at module load. For `bun build --compile` that's build time, so the binary embeds the strings and works regardless of any on-disk skill files. For `bun link` (linked-source) installs that's every `archon` invocation — including `archon --help`, which doesn't even use the skill content. If any of the 18 source files are missing or moved, the import fails and the CLI cannot start at all. The skill content is data the binary deploys via `archon setup`, not data the CLI needs at runtime. There's only one consumer in production code: `copyArchonSkill()` in `setup.ts`. Moving the import into that function as a dynamic import preserves the compiled-binary behavior (Bun's bundler statically analyses literal-string `import()` and embeds the chunk — verified by grepping the SKILL.md frontmatter out of a freshly compiled binary) while making the linked-source install resilient: only `archon setup` triggers the bundled-skill module load now. Verified: a known skill string appears in the compiled binary 1×, and `archon --help` no longer needs the source files to start. `copyArchonSkill()` becomes async because the dynamic import is a Promise. The single production call site is already in an async function and gets an `await`. The four `setup.test.ts` cases become async too. --- packages/cli/src/commands/setup.test.ts | 16 ++++++++-------- packages/cli/src/commands/setup.ts | 15 ++++++++++++--- 2 files changed, 20 insertions(+), 11 deletions(-) diff --git a/packages/cli/src/commands/setup.test.ts b/packages/cli/src/commands/setup.test.ts index 301b58c6d7..0ef6d3f742 100644 --- a/packages/cli/src/commands/setup.test.ts +++ b/packages/cli/src/commands/setup.test.ts @@ -403,11 +403,11 @@ CODEX_ACCOUNT_ID=account1 }); describe('copyArchonSkill', () => { - it('should create skill files in target directory', () => { + it('should create skill files in target directory', async () => { const target = join(TEST_DIR, 'skill-target'); mkdirSync(target, { recursive: true }); - copyArchonSkill(target); + await copyArchonSkill(target); expect(existsSync(join(target, '.claude', 'skills', 'archon', 'SKILL.md'))).toBe(true); expect(existsSync(join(target, '.claude', 'skills', 'archon', 'guides', 'setup.md'))).toBe( @@ -421,11 +421,11 @@ CODEX_ACCOUNT_ID=account1 ).toBe(true); }); - it('should write non-empty content to skill files', () => { + it('should write non-empty content to skill files', async () => { const target = join(TEST_DIR, 'skill-target-content'); mkdirSync(target, { recursive: true }); - copyArchonSkill(target); + await copyArchonSkill(target); const content = readFileSync( join(target, '.claude', 'skills', 'archon', 'SKILL.md'), @@ -435,23 +435,23 @@ CODEX_ACCOUNT_ID=account1 expect(content).toContain('archon'); }); - it('should overwrite existing skill files', () => { + it('should overwrite existing skill files', async () => { const target = join(TEST_DIR, 'skill-target-overwrite'); const skillDir = join(target, '.claude', 'skills', 'archon'); mkdirSync(skillDir, { recursive: true }); writeFileSync(join(skillDir, 'SKILL.md'), 'old content'); - copyArchonSkill(target); + await copyArchonSkill(target); const content = readFileSync(join(skillDir, 'SKILL.md'), 'utf-8'); expect(content).not.toBe('old content'); }); - it('should create skill files even when target directory does not exist', () => { + it('should create skill files even when target directory does not exist', async () => { const target = join(TEST_DIR, 'non-existent-parent', 'skill-target-new'); // Do NOT pre-create target — copyArchonSkill must handle it - copyArchonSkill(target); + await copyArchonSkill(target); expect(existsSync(join(target, '.claude', 'skills', 'archon', 'SKILL.md'))).toBe(true); }); diff --git a/packages/cli/src/commands/setup.ts b/packages/cli/src/commands/setup.ts index 16068ea7ff..d913891486 100644 --- a/packages/cli/src/commands/setup.ts +++ b/packages/cli/src/commands/setup.ts @@ -24,7 +24,6 @@ import { } from '@clack/prompts'; import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'fs'; import { join, dirname } from 'path'; -import { BUNDLED_SKILL_FILES } from '../bundled-skill'; import { homedir } from 'os'; import { randomBytes } from 'crypto'; import { spawn, execSync, type ChildProcess } from 'child_process'; @@ -1334,8 +1333,18 @@ function writeEnvFiles( * Copy the bundled Archon skill files to /.claude/skills/archon/ * * Always overwrites existing files to ensure the latest skill version is installed. + * + * The `bundled-skill` module is dynamically imported here so that its 18 top-level + * `import … with { type: 'text' }` statements only execute when this function is + * actually called. Compiled binaries (`bun build --compile`) still statically + * analyze the literal-string `import()` and embed the chunk; linked-source + * installs (`bun link`) don't touch the source skill files unless the user runs + * `archon setup`. Without this indirection, every `archon` invocation — + * including `archon --help` — fails at module load when the source skill files + * are missing from disk. */ -export function copyArchonSkill(targetPath: string): void { +export async function copyArchonSkill(targetPath: string): Promise { + const { BUNDLED_SKILL_FILES } = await import('../bundled-skill'); const skillRoot = join(targetPath, '.claude', 'skills', 'archon'); for (const [relativePath, content] of Object.entries(BUNDLED_SKILL_FILES)) { const dest = join(skillRoot, relativePath); @@ -1679,7 +1688,7 @@ export async function setupCommand(options: SetupOptions): Promise { const skillTarget = skillTargetRaw; s.start('Installing Archon skill...'); try { - copyArchonSkill(skillTarget); + await copyArchonSkill(skillTarget); } catch (err) { s.stop('Archon skill installation failed'); cancel(`Could not install skill: ${(err as NodeJS.ErrnoException).message}`); From b31ad0d4e515cdae19a86fe8e1a0c0ad2f805aa3 Mon Sep 17 00:00:00 2001 From: Rasmus Widing <152263317+Wirasm@users.noreply.github.com> Date: Tue, 28 Apr 2026 12:50:47 +0300 Subject: [PATCH 06/11] fix(claude): stop passing --no-env-file to native binary in dev mode (#1461) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix(claude): stop passing --no-env-file to native binary in dev mode The Claude Agent SDK switched from shipping `cli.js` inside the package to per-platform native binaries via optional deps somewhere in the 0.2.x series. As of 0.2.121 there is no `cli.js` in the SDK package; dev mode resolves to `@anthropic-ai/claude-agent-sdk-darwin-arm64/claude` (Mach-O). That native binary rejects `--no-env-file` with `error: unknown option '--no-env-file'` and the subprocess exits 1. `shouldPassNoEnvFile` was returning true on `cliPath === undefined` on the assumption that "dev mode = JS executable run via Bun". That assumption is dead. Tighten the predicate to only return true on an explicit `.js` suffix, so we only emit the flag when the SDK is going to spawn a Bun-runnable script. CWD `.env` leak protection is unaffected. `stripCwdEnv()` in `@archon/paths` (#1067) deletes Bun-auto-loaded `.env`/`.env.local`/ `.env.development`/`.env.production` keys from `process.env` at every Archon entry point before any subprocess is spawned. The native Claude binary does not auto-load `.env` from its cwd either. `--no-env-file` was belt-and-suspenders for the JS-via-Bun case only. Verified end-to-end with a sentinel: added a unique `ARCHON_LEAK_SENTINEL_$$` to Archon's `.env`, ran e2e-claude-smoke with a bash probe checking the subprocess env. stderr shows `[archon] stripped 23 keys from /Users/rasmus/Projects/cole/Archon (.env, .env.local)` — sentinel was deleted. Bash node prints `PASS: simple='4', no sentinel leak`. Workflow completes cleanly, no `--no-env-file` rejection from the SDK binary. bun run validate: green across all 10 packages. * fix(claude): address review on #1461 (stale docs + test gaps) Critical: file-level JSDoc at provider.ts:18 still claimed dev mode resolves cli.js. Updated to reflect SDK 0.2.x's switch to per-platform native binaries. Important: security.md still listed --no-env-file as item 2 of target-repo .env isolation. Scoped that bullet to legacy Bun-runnable JS entry points and called out that native binaries don't auto-load .env from cwd. Added an Unreleased Fixed entry to CHANGELOG.md. Updated binary-resolver.ts JSDoc title that referenced cli.js. Polish: widened the predicate to accept .mjs and .cjs (also Bun-runnable JS — matches the SDK's own internal extension list). Dropped the redundant `passesNoEnvFile` log field that mirrored `isJsExecutable`. Added unit cases for .mjs/.cjs (now true) and .ts/.tsx/.jsx (deliberately false — never SDK entry points). Added an integration test that mocks resolveClaudeBinaryPath to return a .js path and asserts executableArgs: ['--no-env-file'] flows through buildBaseClaudeOptions all the way to the SDK call — catches future regressions in the conditional spread. bun run validate: green across all 10 packages. --- .../src/content/docs/reference/security.md | 4 +- .../providers/src/claude/binary-resolver.ts | 7 ++- .../providers/src/claude/provider.test.ts | 62 +++++++++++++++++-- packages/providers/src/claude/provider.ts | 57 ++++++++++------- 4 files changed, 99 insertions(+), 31 deletions(-) diff --git a/packages/docs-web/src/content/docs/reference/security.md b/packages/docs-web/src/content/docs/reference/security.md index b3d1696e04..8c0538e18b 100644 --- a/packages/docs-web/src/content/docs/reference/security.md +++ b/packages/docs-web/src/content/docs/reference/security.md @@ -128,8 +128,8 @@ The GitHub and Gitea adapters verify webhook signatures to ensure payloads origi Archon prevents target repo `.env` from leaking into subprocesses through structural protection: -1. **Boot cleanup:** `stripCwdEnv()` removes Bun-auto-loaded CWD `.env` keys from `process.env` before any application code runs. -2. **Claude Code subprocess:** `executableArgs: ['--no-env-file']` prevents Bun from auto-loading `.env` in the Claude Code subprocess CWD. +1. **Boot cleanup:** `stripCwdEnv()` removes Bun-auto-loaded CWD `.env` keys from `process.env` before any application code runs. **This is the primary guard** — every subprocess Archon spawns inherits from the already-cleaned `process.env`. +2. **Claude Code subprocess:** when the SDK is configured to spawn a Bun-runnable JS entry point (legacy npm-installed `cli.js`/`cli.mjs`/`cli.cjs`), Archon also passes `executableArgs: ['--no-env-file']` so Bun skips its env autoload inside the spawned process. SDK 0.2.x ships per-platform native binaries instead — those don't auto-load `.env` from cwd, so the flag is unnecessary and is omitted. 3. **Bun script nodes:** `bun --no-env-file` prevents script node subprocesses from loading target repo `.env`. 4. **Bash nodes:** Not affected — bash does not auto-load `.env` files. diff --git a/packages/providers/src/claude/binary-resolver.ts b/packages/providers/src/claude/binary-resolver.ts index c2273d85d2..6b918d44a5 100644 --- a/packages/providers/src/claude/binary-resolver.ts +++ b/packages/providers/src/claude/binary-resolver.ts @@ -53,9 +53,12 @@ const INSTALL_INSTRUCTIONS = 'See: https://archon.diy/docs/reference/configuration#claude'; /** - * Resolve the path to the Claude Code SDK's cli.js. + * Resolve the path to the Claude Code executable (native binary in SDK 0.2.x; + * legacy `cli.js` is still accepted for operators pinned to npm-installed + * SDKs that ship a JS entry point). * - * In dev mode: returns undefined (let SDK resolve via node_modules). + * In dev mode: returns undefined (let SDK resolve from its bundled per-platform + * native binary in `@anthropic-ai/claude-agent-sdk-`). * In binary mode: resolves from env/config, or throws with install instructions. */ export async function resolveClaudeBinaryPath( diff --git a/packages/providers/src/claude/provider.test.ts b/packages/providers/src/claude/provider.test.ts index 16641b1555..a5fd64380f 100644 --- a/packages/providers/src/claude/provider.test.ts +++ b/packages/providers/src/claude/provider.test.ts @@ -18,18 +18,37 @@ mock.module('@anthropic-ai/claude-agent-sdk', () => ({ import { ClaudeProvider, shouldPassNoEnvFile } from './provider'; import * as claudeModule from './provider'; +import * as binaryResolver from './binary-resolver'; describe('shouldPassNoEnvFile', () => { - test('returns true when cliPath is undefined (dev mode — SDK spawns cli.js via Bun)', () => { - expect(shouldPassNoEnvFile(undefined)).toBe(true); + test('returns false when cliPath is undefined (dev mode — SDK 0.2.x resolves a native binary)', () => { + // Pre-0.2.x the SDK shipped cli.js and dev mode = JS. Since 0.2.x the + // SDK ships per-platform native binaries via optional deps. The flag + // (a Bun runtime option) is meaningless to native binaries and gets + // rejected as `error: unknown option '--no-env-file'`. CWD .env leak + // protection comes from stripCwdEnv() at entry, not from this flag. + expect(shouldPassNoEnvFile(undefined)).toBe(false); }); - test('returns true for an explicit cli.js path (npm-installed, SDK spawns via Bun/Node)', () => { + test('returns true for an explicit cli.js path (legacy npm-installed cli.js, SDK spawns via Bun)', () => { expect( shouldPassNoEnvFile('/usr/local/lib/node_modules/@anthropic-ai/claude-code/cli.js') ).toBe(true); }); + test('returns true for .mjs and .cjs paths (also Bun-runnable JS entry points)', () => { + expect(shouldPassNoEnvFile('/path/to/cli.mjs')).toBe(true); + expect(shouldPassNoEnvFile('/path/to/cli.cjs')).toBe(true); + }); + + test('returns false for non-Bun-runnable JS-adjacent extensions', () => { + // `.ts`/`.tsx`/`.jsx` are deliberately excluded — the SDK never shipped + // those as entry points, so accepting them would only widen misconfiguration. + expect(shouldPassNoEnvFile('/path/to/cli.ts')).toBe(false); + expect(shouldPassNoEnvFile('/path/to/cli.tsx')).toBe(false); + expect(shouldPassNoEnvFile('/path/to/cli.jsx')).toBe(false); + }); + test('returns false for a native binary path (curl installer, SDK execs directly)', () => { expect(shouldPassNoEnvFile('/Users/test/.local/bin/claude')).toBe(false); }); @@ -504,8 +523,10 @@ describe('ClaudeProvider', () => { const callArgs = mockQuery.mock.calls[0][0] as { options: { env: NodeJS.ProcessEnv; executableArgs?: string[] }; }; - // --no-env-file prevents Bun from auto-loading .env in subprocess CWD - expect(callArgs.options.executableArgs).toEqual(['--no-env-file']); + // executableArgs is omitted when cliPath is undefined (dev mode, SDK + // 0.2.x resolves a native binary). CWD .env leak protection comes + // from stripCwdEnv() at entry, not from the --no-env-file flag. + expect(callArgs.options.executableArgs).toBeUndefined(); expect(callArgs.options.env.CUSTOM_USER_KEY).toBe('user-trusted-value'); // Windows uses "Path" casing in spread objects and USERPROFILE instead of HOME const envPath = callArgs.options.env.PATH ?? callArgs.options.env.Path; @@ -520,6 +541,37 @@ describe('ClaudeProvider', () => { else delete process.env.CUSTOM_USER_KEY; }); + test('passes executableArgs: [--no-env-file] when cliPath ends in a Bun-runnable JS extension', async () => { + // Belt-and-suspenders integration check: the dev-mode path is exercised + // in the test above (executableArgs: undefined). This test exercises the + // legacy explicit-cli.js path through the real buildBaseClaudeOptions + // codepath, so a regression in the conditional spread would be caught. + const spy = spyOn(binaryResolver, 'resolveClaudeBinaryPath').mockResolvedValue( + '/usr/local/lib/node_modules/@anthropic-ai/claude-code/cli.js' + ); + + mockQuery.mockImplementation(async function* () { + // empty + }); + + for await (const _ of client.sendQuery('test', '/workspace')) { + // consume + } + + const callArgs = mockQuery.mock.calls[0][0] as { + options: { + executableArgs?: string[]; + pathToClaudeCodeExecutable?: string; + }; + }; + expect(callArgs.options.executableArgs).toEqual(['--no-env-file']); + expect(callArgs.options.pathToClaudeCodeExecutable).toBe( + '/usr/local/lib/node_modules/@anthropic-ai/claude-code/cli.js' + ); + + spy.mockRestore(); + }); + test('classifies exit code errors as crash and retries up to 3 times', async () => { const error = new Error('process exited with code 1'); mockQuery.mockImplementation(async function* () { diff --git a/packages/providers/src/claude/provider.ts b/packages/providers/src/claude/provider.ts index 2801ce446e..8dea3ae5c4 100644 --- a/packages/providers/src/claude/provider.ts +++ b/packages/providers/src/claude/provider.ts @@ -15,8 +15,12 @@ * Binary resolution: * - In compiled binaries, `pathToClaudeCodeExecutable` is resolved from * `CLAUDE_BIN_PATH` env or `assistants.claude.claudeBinaryPath` config; - * see ./binary-resolver.ts. In dev mode the SDK resolves cli.js itself - * from node_modules. + * see ./binary-resolver.ts. In dev mode the resolver returns undefined + * and the SDK picks its bundled per-platform native binary (Mach-O/ELF/PE + * from `@anthropic-ai/claude-agent-sdk-` optional dep). Pre-0.2.x + * SDKs shipped `cli.js` in the package and dev mode resolved that JS file; + * the SDK switched to native binaries in the 0.2.x series. See + * `shouldPassNoEnvFile` for the implications on the `--no-env-file` flag. */ import { query, @@ -509,31 +513,43 @@ interface ToolResultEntry { toolCallId?: string; } +/** Bun-runnable JS extensions. `.ts`/`.tsx`/`.jsx` are excluded — the SDK has + * never shipped those as entry points, so accepting them would only widen the + * surface for misconfiguration. */ +const BUN_JS_EXTENSIONS = ['.js', '.mjs', '.cjs'] as const; + /** * Decide whether the Claude subprocess should be spawned with `--no-env-file`. * - * `--no-env-file` is a Bun flag that prevents auto-loading `.env` from the - * target repo cwd into the spawned process. It only applies when the SDK - * spawns the executable via Bun/Node — i.e. when the executable is a `.js` - * file (dev mode resolves cli.js, npm-installed resolves cli.js). For a - * native Claude Code binary (curl/PowerShell installer at - * `~/.local/bin/claude`), the SDK execs the binary directly and the flag - * gets passed to the native binary, which rejects unknown options and - * exits code 1. + * `--no-env-file` is a Bun flag (consumed by the Bun runtime, not by Claude + * Code itself) that prevents auto-loading `.env` from the target repo cwd + * into the spawned process. It only does anything when the SDK spawns a + * Bun-runnable JS file via `bun cli.js …` — Bun parses the flag and skips + * its env autoload. For native Claude Code binaries the flag is meaningless + * and, worse, gets handed to the binary which rejects unknown options. + * + * The dev-mode `cliPath === undefined` path used to imply "JS executable" + * because the SDK shipped `cli.js` inside its package. SDK 0.2.x switched + * to per-platform native binaries (e.g. `@anthropic-ai/claude-agent-sdk-darwin-arm64/claude`), + * so dev mode now resolves to a native executable and the historical + * `undefined → true` heuristic is unsafe. Only return `true` when we have + * an explicit Bun-runnable JS path (`.js`/`.mjs`/`.cjs`) — i.e. when the + * operator pointed Archon at a legacy Bun/Node-runnable cli script. + * Otherwise return `false`. * - * Returning `false` for native binaries is verified safe — the native - * binary does not auto-load `.env` from CWD (probed end-to-end with - * sentinel `.env` and `.env.local` in the workflow CWD; both arrived - * UNSET in the spawned bash tool). The first-layer protection — - * `stripCwdEnv()` in `@archon/paths` (#1067) — removes CWD env keys from - * the parent process before spawn, so the subprocess inherits a clean - * env regardless of executable type. + * Safety: target-repo `.env` leaks are prevented by `stripCwdEnv()` in + * `@archon/paths` (#1067), which deletes CWD `.env` keys from + * `process.env` at every Archon entry point before any subprocess is + * spawned. The native Claude binary does not auto-load `.env` from its + * cwd either (verified end-to-end with sentinel keys). `--no-env-file` + * was belt-and-suspenders for the JS-via-Bun case only. * * Exported so the decision can be unit-tested without needing to mock * `BUNDLED_IS_BINARY` or run the full provider sendQuery pathway. */ export function shouldPassNoEnvFile(cliPath: string | undefined): boolean { - return cliPath === undefined || cliPath.endsWith('.js'); + if (cliPath === undefined) return false; + return BUN_JS_EXTENSIONS.some(ext => cliPath.endsWith(ext)); } /** @@ -551,10 +567,7 @@ function buildBaseClaudeOptions( cliPath: string | undefined ): Options { const isJsExecutable = shouldPassNoEnvFile(cliPath); - getLog().debug( - { cliPath: cliPath ?? null, isJsExecutable, passesNoEnvFile: isJsExecutable }, - 'claude.subprocess_env_file_flag' - ); + getLog().debug({ cliPath: cliPath ?? null, isJsExecutable }, 'claude.subprocess_env_file_flag'); return { cwd, From b8a717377a301646d7f8f1b09ce1ff9369b5830f Mon Sep 17 00:00:00 2001 From: Kagura Date: Wed, 29 Apr 2026 17:38:18 +0800 Subject: [PATCH 07/11] fix(orchestrator): clear stale session ID on error_during_execution to prevent infinite failure loop (#1294) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix(orchestrator): clear stale session ID on error_during_execution to prevent infinite failure loop When a Claude API session expires (e.g. after container restart), the orchestrator persists the new (failed) session ID from the error result, causing every subsequent message in that conversation to hit the same error — an infinite failure loop. Fix: on error_during_execution result, set assistant_session_id to NULL instead of persisting the failed session ID. The next message starts a fresh session with full context rebuilt from the DB. Conversation history is unaffected since it lives in remote_agent_messages, independent of the Claude session. Changes: - updateSession() and tryPersistSessionId() now accept string | null - Both handleStreamMode and handleBatchMode clear session ID on error_during_execution Fixes #1280 * test(orchestrator): add stale session clearing tests + address review feedback Co-Authored-By: Claude Opus 4 (1M context) Signed-off-by: kagura-agent --------- Signed-off-by: kagura-agent Co-authored-by: Claude Opus 4 (1M context) --- packages/core/src/db/sessions.test.ts | 13 +++- packages/core/src/db/sessions.ts | 2 +- .../orchestrator/orchestrator-agent.test.ts | 78 ++++++++++++++++++- .../src/orchestrator/orchestrator-agent.ts | 57 ++++++++++++-- 4 files changed, 140 insertions(+), 10 deletions(-) diff --git a/packages/core/src/db/sessions.test.ts b/packages/core/src/db/sessions.test.ts index 810c815dd8..476e1c3acf 100644 --- a/packages/core/src/db/sessions.test.ts +++ b/packages/core/src/db/sessions.test.ts @@ -168,7 +168,18 @@ describe('sessions', () => { ); }); - test('throws SessionNotFoundError when session does not exist', async () => { + test('sets assistant_session_id to NULL when called with null', async () => { + mockQuery.mockResolvedValueOnce(createQueryResult([], 1)); + + await updateSession('session-123', null); + + expect(mockQuery).toHaveBeenCalledWith( + 'UPDATE remote_agent_sessions SET assistant_session_id = $1 WHERE id = $2', + [null, 'session-123'] + ); + }); + + test('throws SessionNotFoundError when session does not exist (updateSession)', async () => { mockQuery.mockResolvedValueOnce(createQueryResult([], 0)); // rowCount = 0 const error = await updateSession('non-existent', 'new-session-id').catch(e => e); diff --git a/packages/core/src/db/sessions.ts b/packages/core/src/db/sessions.ts index e04f602e75..38df36b55b 100644 --- a/packages/core/src/db/sessions.ts +++ b/packages/core/src/db/sessions.ts @@ -58,7 +58,7 @@ export async function createSession(data: { return result.rows[0]; } -export async function updateSession(id: string, sessionId: string): Promise { +export async function updateSession(id: string, sessionId: string | null): Promise { const result = await pool.query( 'UPDATE remote_agent_sessions SET assistant_session_id = $1 WHERE id = $2', [sessionId, id] diff --git a/packages/core/src/orchestrator/orchestrator-agent.test.ts b/packages/core/src/orchestrator/orchestrator-agent.test.ts index 3a4a1299c9..b90ae3cd62 100644 --- a/packages/core/src/orchestrator/orchestrator-agent.test.ts +++ b/packages/core/src/orchestrator/orchestrator-agent.test.ts @@ -72,10 +72,14 @@ mock.module('../db/codebases', () => ({ createCodebase: mock(() => Promise.resolve({ id: 'new-codebase-id' })), })); +const mockUpdateSession = mock(() => Promise.resolve()); +const mockTransitionSession = mock(() => + Promise.resolve({ id: 'session-1', assistant_session_id: null }) +); mock.module('../db/sessions', () => ({ getActiveSession: mock(() => Promise.resolve(null)), - updateSession: mock(() => Promise.resolve()), - transitionSession: mock(() => Promise.resolve({ id: 'session-1', assistant_session_id: null })), + updateSession: mockUpdateSession, + transitionSession: mockTransitionSession, })); const mockParseCommand = mock( @@ -1602,3 +1606,73 @@ describe('handleMessage — workflow context injection', () => { await expect(handleMessage(platform, 'conv-1', 'Hello')).resolves.toBeUndefined(); }); }); + +// ─── Stale session ID clearing on error_during_execution ──────────────────── + +describe('stale session ID clearing on error_during_execution', () => { + beforeEach(() => { + mockUpdateSession.mockClear(); + mockTransitionSession.mockClear(); + mockGetOrCreateConversation.mockReset(); + mockGetCodebase.mockReset(); + mockSendQuery.mockReset(); + mockLogger.warn.mockClear(); + mockGetRecentWorkflowResultMessages.mockReset(); + mockGetRecentWorkflowResultMessages.mockImplementation(() => Promise.resolve([])); + mockDiscoverWorkflowsWithConfig.mockReset(); + mockDiscoverWorkflowsWithConfig.mockImplementation(() => + Promise.resolve({ workflows: [], errors: [] }) + ); + mockGetOrCreateConversation.mockImplementation(() => Promise.resolve(makeConversation())); + mockGetCodebase.mockImplementation(() => Promise.resolve(null)); + mockListCodebases.mockReset(); + mockListCodebases.mockImplementation(() => Promise.resolve([])); + }); + + test('handleStreamMode: clears session ID on error_during_execution result', async () => { + // Simulate AI returning error_during_execution with a stale session ID + mockSendQuery.mockImplementationOnce(async function* () { + yield { + type: 'result', + isError: true, + errorSubtype: 'error_during_execution', + sessionId: 'stale-session-id', + }; + }); + // transitionSession returns a session with an existing assistant_session_id + mockTransitionSession.mockResolvedValueOnce({ + id: 'session-1', + assistant_session_id: 'stale-session-id', + }); + + const platform = makePlatform(); + // Use streaming mode + (platform.getStreamingMode as ReturnType).mockReturnValue('stream'); + await handleMessage(platform, 'conv-1', 'hello'); + + // updateSession should be called with null to clear the stale session ID + expect(mockUpdateSession).toHaveBeenCalledWith('session-1', null); + }); + + test('handleBatchMode: clears session ID on error_during_execution result', async () => { + mockSendQuery.mockImplementationOnce(async function* () { + yield { + type: 'result', + isError: true, + errorSubtype: 'error_during_execution', + sessionId: 'stale-session-id', + }; + }); + mockTransitionSession.mockResolvedValueOnce({ + id: 'session-1', + assistant_session_id: 'stale-session-id', + }); + + const platform = makePlatform(); + // batch is the default from makePlatform, but be explicit + (platform.getStreamingMode as ReturnType).mockReturnValue('batch'); + await handleMessage(platform, 'conv-1', 'hello'); + + expect(mockUpdateSession).toHaveBeenCalledWith('session-1', null); + }); +}); diff --git a/packages/core/src/orchestrator/orchestrator-agent.ts b/packages/core/src/orchestrator/orchestrator-agent.ts index 292f0e0ad8..cf590eb59c 100644 --- a/packages/core/src/orchestrator/orchestrator-agent.ts +++ b/packages/core/src/orchestrator/orchestrator-agent.ts @@ -335,12 +335,15 @@ async function dispatchOrchestratorWorkflow( // ─── Session Helpers ──────────────────────────────────────────────────────── -async function tryPersistSessionId(sessionId: string, assistantSessionId: string): Promise { +async function tryPersistSessionId( + sessionId: string, + assistantSessionId: string | null +): Promise { try { await sessionDb.updateSession(sessionId, assistantSessionId); } catch (error) { getLog().error( - { err: error as Error, sessionId, newSessionId: assistantSessionId }, + { err: error as Error, sessionId, persistedValue: assistantSessionId }, 'session_id_persist_failed' ); } @@ -964,11 +967,32 @@ async function handleStreamMode( await platform.sendStructuredEvent(conversationId, msg); } } else if (msg.type === 'result') { - if (msg.sessionId) { + if (msg.isError && msg.errorSubtype === 'error_during_execution') { + getLog().warn( + { + conversationId, + errorSubtype: msg.errorSubtype, + staleSessionId: msg.sessionId, + errors: msg.errors, + stopReason: msg.stopReason, + }, + 'clearing_stale_session_id' + ); + await tryPersistSessionId(session.id, null); + newSessionId = undefined; + } else if (msg.sessionId) { newSessionId = msg.sessionId; } if (msg.isError) { - getLog().warn({ conversationId, errorSubtype: msg.errorSubtype }, 'ai_result_error'); + getLog().warn( + { + conversationId, + errorSubtype: msg.errorSubtype, + errors: msg.errors, + stopReason: msg.stopReason, + }, + 'ai_result_error' + ); const syntheticError = new Error(msg.errorSubtype ?? 'AI result error'); await platform.sendMessage(conversationId, classifyAndFormatError(syntheticError)); if (newSessionId) { @@ -1087,11 +1111,32 @@ async function handleBatchMode( getLog().debug({ toolName: msg.toolName }, 'tool_call'); } } else if (msg.type === 'result') { - if (msg.sessionId) { + if (msg.isError && msg.errorSubtype === 'error_during_execution') { + getLog().warn( + { + conversationId, + errorSubtype: msg.errorSubtype, + staleSessionId: msg.sessionId, + errors: msg.errors, + stopReason: msg.stopReason, + }, + 'clearing_stale_session_id' + ); + await tryPersistSessionId(session.id, null); + newSessionId = undefined; + } else if (msg.sessionId) { newSessionId = msg.sessionId; } if (msg.isError) { - getLog().warn({ conversationId, errorSubtype: msg.errorSubtype }, 'ai_result_error'); + getLog().warn( + { + conversationId, + errorSubtype: msg.errorSubtype, + errors: msg.errors, + stopReason: msg.stopReason, + }, + 'ai_result_error' + ); const syntheticError = new Error(msg.errorSubtype ?? 'AI result error'); await platform.sendMessage(conversationId, classifyAndFormatError(syntheticError)); if (newSessionId) { From 827f688e07a7401ee1fb31737c47407472998ef1 Mon Sep 17 00:00:00 2001 From: Rasmus Widing <152263317+Wirasm@users.noreply.github.com> Date: Wed, 29 Apr 2026 12:43:15 +0300 Subject: [PATCH 08/11] fix(claude): honor CLAUDE_BIN_PATH in dev mode for libc-mismatch hosts (#1481) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix(claude): honor CLAUDE_BIN_PATH in dev mode for libc-mismatch hosts The Claude Agent SDK auto-resolves its bundled native binary in [linux-x64-musl, linux-x64] order. On glibc Linux hosts (Ubuntu/Debian/ Fedora), Bun installs both via optionalDependencies and the musl variant is picked first; its ELF interpreter (/lib/ld-musl-x86_64.so.1) does not exist on glibc, so spawn fails and the SDK reports a misleading "binary not found" — the file is on disk, the loader is not. The documented escape hatch CLAUDE_BIN_PATH was dead code in dev mode: the resolver early-returned undefined when BUNDLED_IS_BINARY=false before ever reading the env var. The only workaround was patching node_modules. Move the env-var block above the BUNDLED_IS_BINARY return. Config-file path stays binary-mode-only — it's per-repo, not per-machine; env is the right knob for libc mismatches. Behavior preserved: - env unset → unchanged (undefined in dev, autodetect/throw in binary) - env set + file exists → resolved (was binary-only; now also dev) - env set + file missing → clear error (was binary-only; now also dev) Closes #1474 * chore(claude): address CodeRabbit review on #1481 - CHANGELOG entry under [Unreleased] / Fixed describing the dev-mode CLAUDE_BIN_PATH escape hatch (previously ignored). Notes that config-file path remains binary-mode-only and that env-loading + target-repo .env isolation are unchanged downstream. - Empty-string test pinning that CLAUDE_BIN_PATH='' falls through to undefined rather than throwing — protects against a future predicate typo that would treat empty as "set". - One-line note in ai-assistants.md "Binary path configuration" section pointing dev-mode users at the env-var override for the glibc/musl mismatch case. Skipped from the review: - The other two docs-page rewrites (configuration.md / troubleshooting.md): the error message itself names CLAUDE_BIN_PATH, and #1474 documents the use case publicly. One mention in ai-assistants.md is enough for discovery. - Type-style consistency tweaks in the test file: pure bikeshed. --- .../docs/getting-started/ai-assistants.md | 2 + .../src/claude/binary-resolver-dev.test.ts | 82 ++++++++++++++----- .../providers/src/claude/binary-resolver.ts | 34 ++++---- 3 files changed, 85 insertions(+), 33 deletions(-) diff --git a/packages/docs-web/src/content/docs/getting-started/ai-assistants.md b/packages/docs-web/src/content/docs/getting-started/ai-assistants.md index b7eb80888f..e662087b8b 100644 --- a/packages/docs-web/src/content/docs/getting-started/ai-assistants.md +++ b/packages/docs-web/src/content/docs/getting-started/ai-assistants.md @@ -60,6 +60,8 @@ If neither is set in a compiled binary, Archon throws with install instructions The Claude Agent SDK accepts either the native compiled binary or a JS `cli.js`. +**Dev mode override:** when running from source (`bun run dev:server`), the SDK auto-resolves its bundled per-platform binary by default. Set `CLAUDE_BIN_PATH` if you need to override that — most commonly on glibc Linux where the SDK picks the musl variant first and fails to spawn. Config-file `claudeBinaryPath` is intentionally binary-mode-only (per-repo, not per-machine). + **Typical paths by install method:** | Install method | Typical executable path | diff --git a/packages/providers/src/claude/binary-resolver-dev.test.ts b/packages/providers/src/claude/binary-resolver-dev.test.ts index 2474c76d73..923490fbbd 100644 --- a/packages/providers/src/claude/binary-resolver-dev.test.ts +++ b/packages/providers/src/claude/binary-resolver-dev.test.ts @@ -1,8 +1,15 @@ /** * Tests for the Claude binary resolver in dev mode (BUNDLED_IS_BINARY=false). * Separate file because binary-mode tests mock BUNDLED_IS_BINARY=true. + * + * Dev mode normally lets the SDK resolve the binary from its bundled + * platform package. CLAUDE_BIN_PATH is honored as an escape hatch for + * environments where SDK auto-resolution picks the wrong variant — most + * notably glibc Linux hosts, where the SDK prefers the musl binary first + * and silently falls over with a misleading "not found" error. + * Config-file path is intentionally NOT honored in dev mode (still binary-only). */ -import { describe, test, expect, mock } from 'bun:test'; +import { describe, test, expect, mock, beforeEach, afterAll, spyOn } from 'bun:test'; import { createMockLogger } from '../test/mocks/logger'; mock.module('@archon/paths', () => ({ @@ -10,31 +17,68 @@ mock.module('@archon/paths', () => ({ BUNDLED_IS_BINARY: false, })); -import { resolveClaudeBinaryPath } from './binary-resolver'; +import * as resolver from './binary-resolver'; describe('resolveClaudeBinaryPath (dev mode)', () => { - test('returns undefined when BUNDLED_IS_BINARY is false', async () => { - const result = await resolveClaudeBinaryPath(); + const originalEnv = process.env.CLAUDE_BIN_PATH; + let fileExistsSpy: ReturnType | undefined; + + beforeEach(() => { + delete process.env.CLAUDE_BIN_PATH; + fileExistsSpy?.mockRestore(); + fileExistsSpy = undefined; + }); + + afterAll(() => { + if (originalEnv !== undefined) { + process.env.CLAUDE_BIN_PATH = originalEnv; + } else { + delete process.env.CLAUDE_BIN_PATH; + } + fileExistsSpy?.mockRestore(); + }); + + test('returns undefined when nothing is configured', async () => { + const result = await resolver.resolveClaudeBinaryPath(); expect(result).toBeUndefined(); }); - test('returns undefined even with config path set', async () => { - const result = await resolveClaudeBinaryPath('/some/custom/path'); + test('returns undefined when only config path is set (config is binary-mode only)', async () => { + const result = await resolver.resolveClaudeBinaryPath('/some/custom/path'); expect(result).toBeUndefined(); }); - test('returns undefined even with env var set', async () => { - const original = process.env.CLAUDE_BIN_PATH; - process.env.CLAUDE_BIN_PATH = '/some/env/path'; - try { - const result = await resolveClaudeBinaryPath(); - expect(result).toBeUndefined(); - } finally { - if (original !== undefined) { - process.env.CLAUDE_BIN_PATH = original; - } else { - delete process.env.CLAUDE_BIN_PATH; - } - } + test('honors CLAUDE_BIN_PATH env var when file exists', async () => { + process.env.CLAUDE_BIN_PATH = '/usr/local/bin/claude'; + fileExistsSpy = spyOn(resolver, 'fileExists').mockReturnValue(true); + + const result = await resolver.resolveClaudeBinaryPath(); + expect(result).toBe('/usr/local/bin/claude'); + }); + + test('throws when CLAUDE_BIN_PATH is set but file does not exist', async () => { + process.env.CLAUDE_BIN_PATH = '/nonexistent/claude'; + fileExistsSpy = spyOn(resolver, 'fileExists').mockReturnValue(false); + + await expect(resolver.resolveClaudeBinaryPath()).rejects.toThrow( + 'CLAUDE_BIN_PATH is set to "/nonexistent/claude" but the file does not exist' + ); + }); + + test('env var wins over config path in dev mode', async () => { + process.env.CLAUDE_BIN_PATH = '/env/claude'; + fileExistsSpy = spyOn(resolver, 'fileExists').mockReturnValue(true); + + const result = await resolver.resolveClaudeBinaryPath('/config/claude'); + expect(result).toBe('/env/claude'); + }); + + test('falls through to undefined when CLAUDE_BIN_PATH is the empty string', async () => { + // Pin the contract: an unset shell variable that gets exported as empty + // (e.g. `export CLAUDE_BIN_PATH=`) must behave the same as fully unset, + // not throw "file does not exist". + process.env.CLAUDE_BIN_PATH = ''; + const result = await resolver.resolveClaudeBinaryPath(); + expect(result).toBeUndefined(); }); }); diff --git a/packages/providers/src/claude/binary-resolver.ts b/packages/providers/src/claude/binary-resolver.ts index 6b918d44a5..5122e8790c 100644 --- a/packages/providers/src/claude/binary-resolver.ts +++ b/packages/providers/src/claude/binary-resolver.ts @@ -6,15 +6,17 @@ * own node_modules location; in compiled binaries that path is frozen to * the build host's filesystem and does not exist on end-user machines. * - * Resolution order (binary mode only): - * 1. `CLAUDE_BIN_PATH` environment variable - * 2. `assistants.claude.claudeBinaryPath` in config - * 3. Autodetect canonical install path (native installer default) - * 4. Throw with install instructions + * Resolution order: + * 1. `CLAUDE_BIN_PATH` environment variable (honored in both modes — escape + * hatch for hosts where the SDK's per-platform binary auto-resolution + * picks the wrong variant, e.g. glibc Linux + musl SDK package) + * 2. `assistants.claude.claudeBinaryPath` in config (binary mode only) + * 3. Autodetect canonical install path (binary mode only — native installer default) + * 4. Throw with install instructions (binary mode only) * - * In dev mode (BUNDLED_IS_BINARY=false), returns undefined so the caller - * omits `pathToClaudeCodeExecutable` entirely and the SDK resolves via its - * normal node_modules lookup. + * In dev mode (BUNDLED_IS_BINARY=false), if no env var is set, returns + * undefined so the caller omits `pathToClaudeCodeExecutable` entirely and + * the SDK resolves via its normal node_modules lookup. */ import { existsSync as _existsSync } from 'node:fs'; import { homedir } from 'node:os'; @@ -57,16 +59,18 @@ const INSTALL_INSTRUCTIONS = * legacy `cli.js` is still accepted for operators pinned to npm-installed * SDKs that ship a JS entry point). * - * In dev mode: returns undefined (let SDK resolve from its bundled per-platform - * native binary in `@anthropic-ai/claude-agent-sdk-`). - * In binary mode: resolves from env/config, or throws with install instructions. + * In dev mode: honors `CLAUDE_BIN_PATH` if set; otherwise returns undefined + * (let SDK resolve from its bundled per-platform native binary in + * `@anthropic-ai/claude-agent-sdk-`). + * In binary mode: resolves from env/config/autodetect, or throws with + * install instructions. */ export async function resolveClaudeBinaryPath( configClaudeBinaryPath?: string ): Promise { - if (!BUNDLED_IS_BINARY) return undefined; - - // 1. Environment variable override + // 1. Environment variable override — honored in dev mode too, so operators + // on libc mismatches (e.g. glibc host with the SDK's musl variant first in + // its resolution order) can pin a known-good binary without a compiled build. const envPath = process.env.CLAUDE_BIN_PATH; if (envPath) { if (!fileExists(envPath)) { @@ -80,6 +84,8 @@ export async function resolveClaudeBinaryPath( return envPath; } + if (!BUNDLED_IS_BINARY) return undefined; + // 2. Config file override if (configClaudeBinaryPath) { if (!fileExists(configClaudeBinaryPath)) { From afa3f05ccefc23f00c9d7ba8305610ae225cb4b8 Mon Sep 17 00:00:00 2001 From: Yasser <116118149+YrFnS@users.noreply.github.com> Date: Mon, 4 May 2026 20:41:10 +0300 Subject: [PATCH 09/11] fix(deps): bump hono to ^4.12.16 and @hono/node-server to ^1.19.13 (closes #1484) (#1499) --- bun.lock | 19 ++++++++++++------- package.json | 3 ++- packages/server/package.json | 2 +- 3 files changed, 15 insertions(+), 9 deletions(-) diff --git a/bun.lock b/bun.lock index eacdffb2fa..e9c0d03ff1 100644 --- a/bun.lock +++ b/bun.lock @@ -150,7 +150,7 @@ "@archon/workflows": "workspace:*", "@hono/zod-openapi": "^0.19.6", "dotenv": "^17.2.3", - "hono": "^4.11.4", + "hono": "^4.12.16", "zod": "^3.25.28", }, "devDependencies": { @@ -225,6 +225,7 @@ }, }, "overrides": { + "@hono/node-server": "^1.19.13", "axios": "^1.15.0", "test-exclude": "^7.0.1", }, @@ -471,7 +472,7 @@ "@grammyjs/types": ["@grammyjs/types@3.26.0", "", {}, "sha512-jlnyfxfev/2o68HlvAGRocAXgdPPX5QabG7jZlbqC2r9DZyWBfzTlg+nu3O3Fy4EhgLWu28hZ/8wr7DsNamP9A=="], - "@hono/node-server": ["@hono/node-server@1.19.11", "", { "peerDependencies": { "hono": "^4" } }, "sha512-dr8/3zEaB+p0D2n/IUrlPF1HZm586qgJNXK1a9fhg/PzdtkK7Ksd5l312tJX2yBuALqDYBlG20QEbayqPyxn+g=="], + "@hono/node-server": ["@hono/node-server@1.19.14", "", { "peerDependencies": { "hono": "^4" } }, "sha512-GwtvgtXxnWsucXvbQXkRgqksiH2Qed37H9xHZocE5sA3N8O8O8/8FA3uclQXxXVzc9XBZuEOMK7+r02FmSpHtw=="], "@hono/zod-openapi": ["@hono/zod-openapi@0.19.10", "", { "dependencies": { "@asteasolutions/zod-to-openapi": "^7.3.0", "@hono/zod-validator": "^0.7.1", "openapi3-ts": "^4.5.0" }, "peerDependencies": { "hono": ">=4.3.6", "zod": ">=3.0.0" } }, "sha512-dpoS6DenvoJyvxtQ7Kd633FRZ/Qf74+4+o9s+zZI8pEqnbjdF/DtxIib08WDpCaWabMEJOL5TXpMgNEZvb7hpA=="], @@ -1407,11 +1408,11 @@ "flat-cache": ["flat-cache@4.0.1", "", { "dependencies": { "flatted": "^3.2.9", "keyv": "^4.5.4" } }, "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw=="], - "flatted": ["flatted@3.4.1", "", {}, "sha512-IxfVbRFVlV8V/yRaGzk0UVIcsKKHMSfYw66T/u4nTwlWteQePsxe//LjudR1AMX4tZW3WFCh3Zqa/sjlqpbURQ=="], + "flatted": ["flatted@3.4.2", "", {}, "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA=="], "flattie": ["flattie@1.1.1", "", {}, "sha512-9UbaD6XdAL97+k/n+N7JwX46K/M6Zc6KcFYskrYL8wbBV/Uyk0CTAMY0VT+qiK5PM7AIc9aTWYtq65U7T+aCNQ=="], - "follow-redirects": ["follow-redirects@1.15.11", "", {}, "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ=="], + "follow-redirects": ["follow-redirects@1.16.0", "", {}, "sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw=="], "fontace": ["fontace@0.4.1", "", { "dependencies": { "fontkitten": "^1.0.2" } }, "sha512-lDMvbAzSnHmbYMTEld5qdtvNH2/pWpICOqpean9IgC7vUbUJc3k+k5Dokp85CegamqQpFbXf0rAVkbzpyTA8aw=="], @@ -1521,7 +1522,7 @@ "highlight.js": ["highlight.js@11.11.1", "", {}, "sha512-Xwwo44whKBVCYoliBQwaPvtd/2tYFkRQtXDWj1nackaV2JPXx3L0+Jvd8/qCJ2p+ML0/XVkJ2q+Mr+UVdpJK5w=="], - "hono": ["hono@4.12.7", "", {}, "sha512-jq9l1DM0zVIvsm3lv9Nw9nlJnMNPOcAtsbsgiUhWcFzPE99Gvo6yRTlszSLLYacMeQ6quHD6hMfId8crVHvexw=="], + "hono": ["hono@4.12.16", "", {}, "sha512-jN0ZewiNAWSe5khM3EyCmBb250+b40wWbwNILNfEvq84VREWwOIkuUsFONk/3i3nqkz7Oe1PcpM2mwQEK2L9Kg=="], "html-comment-regex": ["html-comment-regex@1.1.2", "", {}, "sha512-P+M65QY2JQ5Y0G9KKdlDpo0zK+/OHptU5AaBwUfAIDJZk1MYf32Frm84EcOytfJE0t5JvkAnKlmjsXDnWzCJmQ=="], @@ -1977,7 +1978,7 @@ "path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="], - "path-to-regexp": ["path-to-regexp@8.3.0", "", {}, "sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA=="], + "path-to-regexp": ["path-to-regexp@8.4.2", "", {}, "sha512-qRcuIdP69NPm4qbACK+aDogI5CBDMi1jKe0ry5rSQJz8JVLsC7jV8XpiJjGRLLol3N+R5ihGYcrPLTno6pAdBA=="], "pg": ["pg@8.20.0", "", { "dependencies": { "pg-connection-string": "^2.12.0", "pg-pool": "^3.13.0", "pg-protocol": "^1.13.0", "pg-types": "2.2.0", "pgpass": "1.0.5" }, "optionalDependencies": { "pg-cloudflare": "^1.3.0" }, "peerDependencies": { "pg-native": ">=3.0.1" }, "optionalPeers": ["pg-native"] }, "sha512-ldhMxz2r8fl/6QkXnBD3CR9/xg694oT6DZQ2s6c/RI28OjtSOpxnPrUCGOBJ46RCUxcWdx3p6kw/xnDHjKvaRA=="], @@ -2055,7 +2056,7 @@ "punycode": ["punycode@2.3.1", "", {}, "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="], - "qs": ["qs@6.15.0", "", { "dependencies": { "side-channel": "^1.1.0" } }, "sha512-mAZTtNCeetKMH+pSjrb76NAM8V9a05I9aBZOHztWy/UqcJdQYNsf59vrRKWnojAT9Y+GbIvoTBC++CPHqpDBhQ=="], + "qs": ["qs@6.15.1", "", { "dependencies": { "side-channel": "^1.1.0" } }, "sha512-6YHEFRL9mfgcAvql/XhwTvf5jKcOiiupt2FiJxHkiX1z4j7WL8J/jRHYLluORvc1XxB5rV20KoeK00gVJamspg=="], "queue-microtask": ["queue-microtask@1.2.3", "", {}, "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A=="], @@ -2519,6 +2520,8 @@ "@modelcontextprotocol/sdk/ajv": ["ajv@8.18.0", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2" } }, "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A=="], + "@modelcontextprotocol/sdk/hono": ["hono@4.12.7", "", {}, "sha512-jq9l1DM0zVIvsm3lv9Nw9nlJnMNPOcAtsbsgiUhWcFzPE99Gvo6yRTlszSLLYacMeQ6quHD6hMfId8crVHvexw=="], + "@modelcontextprotocol/sdk/zod": ["zod@4.3.6", "", {}, "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg=="], "@redocly/ajv/json-schema-traverse": ["json-schema-traverse@1.0.0", "", {}, "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="], @@ -2991,6 +2994,8 @@ "shadcn/@modelcontextprotocol/sdk/ajv": ["ajv@8.18.0", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2" } }, "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A=="], + "shadcn/@modelcontextprotocol/sdk/hono": ["hono@4.12.7", "", {}, "sha512-jq9l1DM0zVIvsm3lv9Nw9nlJnMNPOcAtsbsgiUhWcFzPE99Gvo6yRTlszSLLYacMeQ6quHD6hMfId8crVHvexw=="], + "shadcn/execa/get-stream": ["get-stream@9.0.1", "", { "dependencies": { "@sec-ant/readable-stream": "^0.4.1", "is-stream": "^4.0.1" } }, "sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA=="], "shadcn/execa/human-signals": ["human-signals@8.0.1", "", {}, "sha512-eKCa6bwnJhvxj14kZk5NCPc6Hb6BdsU9DZcOnmQKSnO1VKrfV0zCvtttPZUsBvjmNDn8rpcJfpwSYnHBjc95MQ=="], diff --git a/package.json b/package.json index 6c2521b633..736a97ac4c 100644 --- a/package.json +++ b/package.json @@ -49,7 +49,8 @@ }, "overrides": { "test-exclude": "^7.0.1", - "axios": "^1.15.0" + "axios": "^1.15.0", + "@hono/node-server": "^1.19.13" }, "dependencies": { "@anthropic-ai/claude-agent-sdk": "^0.2.121" diff --git a/packages/server/package.json b/packages/server/package.json index 3e9fe3d9ef..10e31874cc 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -19,7 +19,7 @@ "@archon/workflows": "workspace:*", "@hono/zod-openapi": "^0.19.6", "dotenv": "^17.2.3", - "hono": "^4.11.4", + "hono": "^4.12.16", "zod": "^3.25.28" }, "devDependencies": { From b297abaacace4103435dc6351e6211a44636110d Mon Sep 17 00:00:00 2001 From: Truffle Date: Mon, 4 May 2026 08:46:32 -0700 Subject: [PATCH 10/11] fix(orchestrator): create ~/.archon/workspaces before AI provider spawn (#1529) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix(orchestrator): create ~/.archon/workspaces before AI provider spawn On a fresh install, ~/.archon/workspaces doesn't exist yet. The orchestrator passes that path as cwd to the AI provider, which calls spawn() — which raises ENOENT. The error is then misclassified as "binary not found" in the friendly-error path, surfacing as an incorrect "Claude binary not found" message. Add ensureArchonWorkspacesPath() in @archon/paths that mkdir -p's the directory and returns the path. Use it at the orchestrator's spawn-cwd site so the directory is guaranteed to exist before spawn(). Other call sites of getArchonWorkspacesPath() (workflow discovery, path-prefix comparisons) only consume the path string and don't need the directory to exist; they keep using the pure getter. Closes #1528 * test(orchestrator): assert ensureArchonWorkspacesPath is called Capture the @archon/paths mock as a named variable and assert it was called in the syncWorkspace handleMessage path. Without this, the test suite passes even if orchestrator-agent.ts:824 reverts to the non-ensuring getArchonWorkspacesPath() variant — exactly the regression that surfaced as 'Claude Code native binary not found' in #1528. --- .../orchestrator/orchestrator-agent.test.ts | 6 +++ .../src/orchestrator/orchestrator-agent.ts | 4 +- .../orchestrator-isolation.test.ts | 1 + .../src/orchestrator/orchestrator.test.ts | 1 + packages/paths/src/archon-paths.test.ts | 38 +++++++++++++++++++ packages/paths/src/archon-paths.ts | 10 +++++ packages/paths/src/index.ts | 1 + 7 files changed, 59 insertions(+), 2 deletions(-) diff --git a/packages/core/src/orchestrator/orchestrator-agent.test.ts b/packages/core/src/orchestrator/orchestrator-agent.test.ts index b90ae3cd62..11ff15f0ea 100644 --- a/packages/core/src/orchestrator/orchestrator-agent.test.ts +++ b/packages/core/src/orchestrator/orchestrator-agent.test.ts @@ -51,9 +51,11 @@ const mockLoadConfig = mock(() => const mockLogger = createMockLogger(); +const mockEnsureArchonWorkspacesPath = mock(() => Promise.resolve('/home/test/.archon/workspaces')); mock.module('@archon/paths', () => ({ createLogger: mock(() => mockLogger), getArchonWorkspacesPath: mock(() => '/home/test/.archon/workspaces'), + ensureArchonWorkspacesPath: mockEnsureArchonWorkspacesPath, getArchonHome: mock(() => '/home/test/.archon'), })); @@ -906,6 +908,7 @@ describe('discoverAllWorkflows — remote sync', () => { mockSendQuery.mockClear(); mockGetCodebaseEnvVars.mockReset(); mockLoadConfig.mockReset(); + mockEnsureArchonWorkspacesPath.mockClear(); // Reset mocks between tests in this suite and restore safe defaults mockGetOrCreateConversation.mockImplementation(() => Promise.resolve(null)); mockGetCodebase.mockImplementation(() => Promise.resolve(null)); @@ -931,6 +934,9 @@ describe('discoverAllWorkflows — remote sync', () => { expect(mockSyncWorkspace).toHaveBeenCalledWith('/repos/test-repo', undefined, { resetAfterFetch: false, }); + // Regression guard: orchestrator must resolve cwd through the ensure variant + // so the workspaces dir is created before the AI provider spawn (issue #1528). + expect(mockEnsureArchonWorkspacesPath).toHaveBeenCalled(); }); test('passes resetAfterFetch=true for managed clones', async () => { diff --git a/packages/core/src/orchestrator/orchestrator-agent.ts b/packages/core/src/orchestrator/orchestrator-agent.ts index cf590eb59c..061bd5b5dd 100644 --- a/packages/core/src/orchestrator/orchestrator-agent.ts +++ b/packages/core/src/orchestrator/orchestrator-agent.ts @@ -25,7 +25,7 @@ import { formatToolCall } from '@archon/workflows/utils/tool-formatter'; import { classifyAndFormatError } from '../utils/error-formatter'; import { toError } from '../utils/error'; import { getAgentProvider, getProviderCapabilities } from '@archon/providers'; -import { getArchonHome, getArchonWorkspacesPath } from '@archon/paths'; +import { getArchonHome, getArchonWorkspacesPath, ensureArchonWorkspacesPath } from '@archon/paths'; import { syncArchonToWorktree } from '../utils/worktree-sync'; import { syncWorkspace, toRepoPath } from '@archon/git'; import type { WorkspaceSyncResult } from '@archon/git'; @@ -809,7 +809,7 @@ export async function handleMessage( attachedFiles, workflowContext ); - const cwd = getArchonWorkspacesPath(); + const cwd = await ensureArchonWorkspacesPath(); // 4. Update activity and get/create session await db.touchConversation(conversation.id); diff --git a/packages/core/src/orchestrator/orchestrator-isolation.test.ts b/packages/core/src/orchestrator/orchestrator-isolation.test.ts index 63ea6292ac..f8ca8d69b0 100644 --- a/packages/core/src/orchestrator/orchestrator-isolation.test.ts +++ b/packages/core/src/orchestrator/orchestrator-isolation.test.ts @@ -10,6 +10,7 @@ const mockLogger = createMockLogger(); mock.module('@archon/paths', () => ({ createLogger: mock(() => mockLogger), getArchonWorkspacesPath: mock(() => '/home/test/.archon/workspaces'), + ensureArchonWorkspacesPath: mock(() => Promise.resolve('/home/test/.archon/workspaces')), getArchonHome: mock(() => '/home/test/.archon'), })); diff --git a/packages/core/src/orchestrator/orchestrator.test.ts b/packages/core/src/orchestrator/orchestrator.test.ts index bd0caf3bf8..48cac3ceb0 100644 --- a/packages/core/src/orchestrator/orchestrator.test.ts +++ b/packages/core/src/orchestrator/orchestrator.test.ts @@ -12,6 +12,7 @@ const mockLogger = createMockLogger(); mock.module('@archon/paths', () => ({ createLogger: mock(() => mockLogger), getArchonWorkspacesPath: mock(() => '/home/test/.archon/workspaces'), + ensureArchonWorkspacesPath: mock(() => Promise.resolve('/home/test/.archon/workspaces')), getArchonHome: mock(() => '/home/test/.archon'), })); diff --git a/packages/paths/src/archon-paths.test.ts b/packages/paths/src/archon-paths.test.ts index 734516375f..6cd56406a1 100644 --- a/packages/paths/src/archon-paths.test.ts +++ b/packages/paths/src/archon-paths.test.ts @@ -10,6 +10,7 @@ import { isDocker, getArchonHome, getArchonWorkspacesPath, + ensureArchonWorkspacesPath, getArchonWorktreesPath, getArchonConfigPath, getCommandFolderSearchPaths, @@ -546,6 +547,43 @@ describe('ensureProjectStructure', () => { }); }); +describe('ensureArchonWorkspacesPath', () => { + let tempArchonHome: string; + useEnvSnapshot(); + + beforeEach(async () => { + delete process.env.WORKSPACE_PATH; + delete process.env.ARCHON_DOCKER; + tempArchonHome = join( + tmpdir(), + `archon-paths-test-${Date.now()}-${Math.random().toString(36).slice(2)}` + ); + process.env.ARCHON_HOME = tempArchonHome; + }); + + afterEach(async () => { + await rm(tempArchonHome, { recursive: true, force: true }); + }); + + test('creates the workspaces directory when missing', async () => { + const expected = getArchonWorkspacesPath(); + expect(existsSync(expected)).toBe(false); + + const returned = await ensureArchonWorkspacesPath(); + + expect(returned).toBe(expected); + expect((await lstat(expected)).isDirectory()).toBe(true); + }); + + test('is idempotent - safe to call twice', async () => { + await ensureArchonWorkspacesPath(); + await ensureArchonWorkspacesPath(); + + const expected = getArchonWorkspacesPath(); + expect((await lstat(expected)).isDirectory()).toBe(true); + }); +}); + describe('createProjectSourceSymlink', () => { let tempArchonHome: string; let tempTarget: string; diff --git a/packages/paths/src/archon-paths.ts b/packages/paths/src/archon-paths.ts index ca8ea73774..19235f81aa 100644 --- a/packages/paths/src/archon-paths.ts +++ b/packages/paths/src/archon-paths.ts @@ -80,6 +80,16 @@ export function getArchonWorkspacesPath(): string { return join(getArchonHome(), 'workspaces'); } +/** + * Ensure the workspaces directory exists and return its path. + * Safe to call on a fresh install before any workspace is registered. + */ +export async function ensureArchonWorkspacesPath(): Promise { + const path = getArchonWorkspacesPath(); + await mkdir(path, { recursive: true }); + return path; +} + /** * Get the global worktrees directory (~/.archon/worktrees/). * Used as the legacy fallback for repos not registered under workspaces/. diff --git a/packages/paths/src/index.ts b/packages/paths/src/index.ts index 8f067cfeca..f5b0e065c4 100644 --- a/packages/paths/src/index.ts +++ b/packages/paths/src/index.ts @@ -4,6 +4,7 @@ export { isDocker, getArchonHome, getArchonWorkspacesPath, + ensureArchonWorkspacesPath, getArchonWorktreesPath, getArchonConfigPath, getCommandFolderSearchPaths, From 7da9940cc55ec9f03d7897443603f900a44f2656 Mon Sep 17 00:00:00 2001 From: cjnprospa Date: Tue, 5 May 2026 21:39:16 +1000 Subject: [PATCH 11/11] docs(changelog): add Tier 1 batch 2 cherry-pick entry Co-Authored-By: Claude Opus 4.7 (1M context) --- CHANGELOG.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6d541d13da..490db677e7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed +- **Cherry-pick batch 2 from upstream (10 commits).** Selective Tier 1 picks from the upstream delta: + - `0ec74410` — Bumped `hono` to `^4.12.16` and added `@hono/node-server` `^1.19.13` override (closes upstream #1484). + - `0afbeb30` — Bumped `@anthropic-ai/claude-agent-sdk` to `0.2.121` and `@openai/codex-sdk` to `0.125.0`. Pi packages skipped (fork doesn't use Pi). + - `cbcca8c1` — Orchestrator clears stale session ID on `error_during_execution` instead of persisting the failed session ID, preventing infinite failure loops after Claude session expiry (closes upstream #1280). + - `0c5d7b12` — Orchestrator now creates `~/.archon/workspaces` before AI provider spawn so fresh-install ENOENT no longer surfaces as an incorrect "Claude binary not found" error. + - `45682bd2` — Claude provider's `hasExplicitTokens` uses `||` instead of `??` so empty-string env vars are treated as missing (upstream #1028). + - `4885ee64` — `CLAUDE_BIN_PATH` is now honored in dev mode (relevant for libc-mismatch hosts; upstream #1481). + - `ff901115` — Claude provider stops passing `--no-env-file` to the native binary in dev mode (the flag is Bun-only; upstream #1461). + - `7d067738` — CLI lazy-imports bundled skill files so non-setup commands don't crash on missing source (upstream #1394). + - `d89bc767` — Aligned PORT default to `3090` across `.env.example`, setup wizard, and JSDoc (upstream #1271). + - `301a139e` — Split `connection.test.ts` into its own batch in `@archon/core` test script to avoid mock pollution that caused `getDatabaseType()` tests to see leaked `DATABASE_URL` (upstream #1269). Also reapplied to preserve fork-only batches (`workflow-analytics`, `cron-parser`, `knowledge-writer`). + - **Bumped transitive `axios` to `^1.15.0` via root `overrides` to clear CVE-2025-62718** (NO_PROXY bypass via hostname normalization → potential SSRF). Archon pulls `axios` transitively through `@slack/bolt` and `@slack/web-api`; both semver ranges (`^1.12.0` and `^1.13.5`) accept the override cleanly, so no API surface changes. Credits @stefans71 for identifying and reporting the vulnerability in #1153. Closes #1053. - **Stale workspace symlink no longer reported as "not in a git repository" by the CLI.** When `archon workflow run` (or `--resume`) is invoked from a valid git repo whose `~/.archon/workspaces///source` symlink points somewhere else (common after moving/renaming the checkout), auto-registration fails but the repo is fine. Previously both the worktree-creation and resume paths fell through to the generic `Cannot create worktree: not in a git repository` / `Cannot resume: Not in a git repository` errors — a lie that sent users down the wrong diagnostic path. Both sites now preserve the registration error and throw `Cannot {create worktree,resume}: repository registration failed.` with the original cause and a concrete cleanup hint (`Remove the stale workspace entry at and retry`) when the failure matches the `createProjectSourceSymlink()` shape. Credits @Bortlesboat for identifying the root cause and the parser approach in #1157. Closes #1146. - **Server startup no longer marks actively-running workflows as failed.** The `failOrphanedRuns()` call has been removed from `packages/server/src/index.ts` to match the CLI precedent (`packages/cli/src/cli.ts:256-258`). Per the new CLAUDE.md principle "No Autonomous Lifecycle Mutation Across Process Boundaries", a stuck `running` row is now transitioned explicitly by the user: via the per-row Cancel/Abandon buttons on the dashboard workflow card, or `archon workflow abandon ` from the CLI. (`archon workflow cleanup` is a separate command that deletes OLD terminal runs for disk hygiene — it does not handle stuck `running` rows.) Closes #1216.