diff --git a/CHANGELOG.md b/CHANGELOG.md index 566160685f..d01a6d47b6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - **Bash and script node failures no longer leak the inline script body into user-visible errors and logs.** When a `bash:` or `script:` DAG node failed, the error string interpolated `err.message` from Node's `ExecFileException`, which begins with `Command failed: bash -c
` (or `bun -e `) — embedding the entire substituted script body. Pino's default error serializer compounded this by writing `err.message`, `err.stack`, and `err.cmd` separately, producing three copies of the body per failure across the CLI, Web UI, and `node_failed` event payload. Diagnostic output (e.g. `Expected ")" but found "x" at [eval]:4:241`) was buried at the end. A new `formatSubprocessFailure()` helper now strips the `Command failed:` prefix line, prefers `stderr` over the message body, tail-caps at 2 KB, and exposes a controlled `{exitCode, killed, stderrTail}` log subset — never the raw error. Timeout / ENOENT / EACCES branches now also log through the sanitized helper, so the body cannot leak via the timeout path either. (#1389) - **Claude provider crashed in dev mode with `error: unknown option '--no-env-file'`.** The Claude Agent SDK switched from shipping `cli.js` to per-platform native binaries (via optional deps) in the 0.2.x series. Archon's `shouldPassNoEnvFile` predicate kept emitting the Bun-only `--no-env-file` flag in dev mode (when the SDK resolves its bundled binary), which the native binary rejects. Tightened the predicate to only emit the flag for explicitly-configured Bun-runnable JS entry points (`.js`/`.mjs`/`.cjs`). Target-repo `.env` isolation is unchanged — `stripCwdEnv()` at process boot remains the primary guard, and the native Claude binary does not auto-load `.env` from its cwd. (#1461) - **Pi structured-output now tolerates reasoning-model prose preamble.** `tryParseStructuredOutput` previously returned `undefined` whenever the assistant text wasn't pure JSON, even when the JSON object was clearly emitted at the end of a "Let me evaluate..." preamble. Reasoning models — observed on Minimax M2.7 — routinely "think out loud" before emitting structured output despite explicit JSON-only prompts. The parser now falls back to a forward-scan from the first `{` when the clean parse fails, recovering the structured output without changing the success path for fully compliant models. (#1440) +- **`CLAUDE_BIN_PATH` is now honored in dev mode.** Previously the env var was silently ignored when running from source (`BUNDLED_IS_BINARY=false`) — `resolveClaudeBinaryPath()` early-returned `undefined` before reading it, leaving glibc Linux contributors with no working escape hatch when the Claude SDK's bundled-binary auto-resolution picked the musl variant first. The env-var check now runs in both modes; config-file path (`assistants.claude.claudeBinaryPath`) remains binary-mode-only since it's a per-repo, not per-machine setting. Env-loading and target-repo `.env` isolation are unchanged — same `stripCwdEnv()` boot-time guard and same `shouldPassNoEnvFile()` predicate run downstream. (#1481) ## [0.3.9] - 2026-04-22 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 08993fc8a2..de4004a6ba 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