# Test 386 — opencode-cli stale agent-node launch gate

- date: 2026-07-16T03:20:57+00:00
bun test v1.3.1 (89fa0f34)

src/opencode-owner-mode.test.ts:
(pass) OpenCode owner/mode policy > accepts umask-0002 modes only for a non-root uid=gid layout [1.00ms]
(pass) OpenCode owner/mode policy > always rejects world write and keeps root/foreign ownership strict

src/opencode-agent-node-pair.test.ts:
(pass) OpenCode agent-node release pairing > pins the exact versions being released together [4.00ms]
(pass) OpenCode agent-node release pairing > rejects latest 2.4.x-style help and accepts the RFC-029 capability
(pass) OpenCode agent-node release pairing > admits only the exact preview package identity with safe file modes [5.00ms]
(pass) OpenCode agent-node release pairing > skips an exact project-local impersonator and selects the later global package [6.00ms]

src/opencode-launch-env.test.ts:
(pass) hardenOpencodeAgentNodeEnv > restores launcher PATH and strips every pre-entrypoint loader hook [1.00ms]
(pass) hardenOpencodeAgentNodeEnv > does not mutate the caller's env object
(pass) hardenOpencodeAgentNodeEnv > strips case-variant loader and PATH keys for Windows semantics

src/opencode-auth-login.test.ts:
(pass) OpenCode manual auth-login sandbox > builds deterministic provider-specific API-key login argv
(pass) OpenCode manual auth-login sandbox > uses a fresh all-XDG tree and strips ambient credentials/config hooks [39.00ms]
(pass) OpenCode manual auth-login sandbox > strictly consumes only the selected provider API record through a private leaf [29.00ms]
(pass) OpenCode manual auth-login sandbox > refuses OAuth, mixed-provider and symlink auth shapes without disclosing secrets [21.00ms]
(pass) OpenCode manual auth-login sandbox > persistent planted DB/log links are never exposed and cleanup never follows descendant links [37.00ms]
(pass) OpenCode manual auth-login sandbox > cleanup unlinks a swapped root symlink but never removes its outside target [18.00ms]
(pass) OpenCode manual auth-login sandbox > cleanup quarantines the tracked inode but leaves a regular root-name replacement untouched [18.00ms]
(pass) OpenCode manual auth-login sandbox > a live tracked root whose literal name ends in deleted is still removed [24.00ms]
(pass) OpenCode manual auth-login sandbox > Linux reports nlink zero for a removed directory retained by fd [11.00ms]
(pass) OpenCode manual auth-login sandbox > cleanup retains inode ownership after bounded failure and succeeds on retry [24.00ms]
(pass) OpenCode manual auth-login sandbox > refuses a concurrent live owner marker [28.00ms]
(pass) OpenCode manual auth-login sandbox > refuses a provider that does not match the node's unique configured preset [13.00ms]
(pass) OpenCode manual auth-login sandbox > prunes a dead owner's stale root without following its planted links [47.00ms]
(pass) OpenCode manual auth-login sandbox > PID reuse does not retain a stale credential root [51.00ms]
(pass) OpenCode manual auth-login sandbox > stale sweep resumes a crash-left quarantine while its owner marker remains [41.00ms]
(pass) OpenCode manual auth-login sandbox > stale sweep removes an empty quarantine left after marker-last deletion [27.00ms]
(pass) OpenCode manual auth-login sandbox > spawn-time revalidation rejects a hostile ancestor discovery candidate [24.00ms]
(pass) OpenCode manual auth-login sandbox > with helper always cleans the fresh root when the action throws [39.00ms]

src/opencode-runtime-binding.test.ts:
(pass) external OpenCode runtime binding > survives regular config runtime downgrade and proves the original exact runtime [16.00ms]
(pass) external OpenCode runtime binding > read returns undefined only for absent state and deterministic keys separate nodes [9.00ms]
(pass) external OpenCode runtime binding > rejects binding-directory and leaf symlinks [10.00ms]
(pass) external OpenCode runtime binding > rejects permissive modes, hard links, and foreign ownership [7.00ms]
(pass) external OpenCode runtime binding > rejects private but tampered runtime, identity, and extra fields [8.00ms]
(pass) assertOpencodeNodeStateUntracked > allows ordinary non-Git projects [2.00ms]
(pass) assertOpencodeNodeStateUntracked > allows ignored/untracked state but rejects git add -f tracked state [39.00ms]
(pass) assertOpencodeNodeStateUntracked > rejects a force-added dotenv or any tracked file below the node directory [54.00ms]

src/opencode-preset.test.ts:
(pass) OPENCODE_PRESETS registry > exports the two blessed presets (anthropic + openai) [1.00ms]
(pass) OPENCODE_PRESETS registry > findOpencodePreset('anthropic') returns the record; unknown returns null
(pass) readPresetKeyFromEnv — env-only, no interactive prompt > returns the trimmed key when the env var is set
(pass) readPresetKeyFromEnv — env-only, no interactive prompt > returns null when the env var is missing / empty
(pass) buildAuthJsonBody + writeOpencodeAuthJson > body shape matches opencode auth.json convention
(pass) buildAuthJsonBody + writeOpencodeAuthJson > writes to <workdir>/.local/share/opencode/auth.json with mode 0o600 [7.00ms]
(pass) buildAuthJsonBody + writeOpencodeAuthJson > writeOpencodeConfigJson lands under .config/opencode with 0o600 [8.00ms]
(pass) buildAuthJsonBody + writeOpencodeAuthJson > keyless create atomically clears a private pre-planted auth file [6.00ms]
(pass) buildAuthJsonBody + writeOpencodeAuthJson > default tool policy disables filesystem, shell, task, and skill tools [1.00ms]
(pass) buildAuthJsonBody + writeOpencodeAuthJson > writes only blessed provider identity and strips all pre-planted routing/executable config [7.00ms]
(pass) buildAuthJsonBody + writeOpencodeAuthJson > atomically replaces a private but invalid pre-planted config without parsing it [8.00ms]
(pass) buildAuthJsonBody + writeOpencodeAuthJson > rejects symlink escapes in workDir, config/data ancestors, and final targets [13.00ms]
(pass) buildAuthJsonBody + writeOpencodeAuthJson > validates the full tree before mutation so a bad auth side cannot partially rewrite config [1.00ms]
(pass) buildAuthJsonBody + writeOpencodeAuthJson > rejects permissive modes and foreign owners without chmod-follow repair [7.00ms]
(pass) buildAuthJsonBody + writeOpencodeAuthJson > prepares .anet/nodes/node before profile secrets and provides atomic private leaf I/O [31.00ms]
(pass) buildAuthJsonBody + writeOpencodeAuthJson > accepts an ordinary 0775 project root for a non-root uid=gid private group [1.00ms]
(pass) buildAuthJsonBody + writeOpencodeAuthJson > profile preflight rejects .anet/nodes/node and config/.env symlink chains before secret writes [8.00ms]
(pass) buildAuthJsonBody + writeOpencodeAuthJson > profile preflight rejects writable ancestors and non-private node roots [2.00ms]

src/opencode-smoke-env.test.ts:
(pass) buildOpencodeSmokeEnv > locks the exact hardened ancestor candidate set [2.00ms]
(pass) buildOpencodeSmokeEnv > rejects sticky world-writable /tmp instead of silently degrading
(pass) buildOpencodeSmokeEnv > inherits only transport/locale trust settings and controls all OpenCode roots [1.00ms]
(pass) buildOpencodeSmokeEnv > every writable root can be precreated private, including XDG_RUNTIME_DIR [1.00ms]

src/opencode-package-binary.test.ts:
(pass) validateOpencodePackageBinary > accepts only the canonical exact npm package entrypoint [3.00ms]
(pass) validateOpencodePackageBinary > rejects a same-version package impersonator inside the project [1.00ms]
(pass) validateOpencodePackageBinary > skips a same-version project shim and selects a later trusted package [3.00ms]
(pass) validateOpencodePackageBinary > rejects a monorepo-root package when invoked from a nested app [3.00ms]
(pass) validateOpencodePackageBinary > ordinary 0664 checkout package.json does not abort boundary discovery [3.00ms]
(pass) validateOpencodePackageBinary > accepts both exact registry spellings of bin.opencode [2.00ms]
(pass) validateOpencodePackageBinary > rejects forged name, version, and bin metadata [2.00ms]
(pass) validateOpencodePackageBinary > rejects writable files and writable package ancestors [3.00ms]
(pass) validateOpencodePackageBinary > rejects a symlinked package.json even when its contents are exact

 66 pass
 0 fail
 342 expect() calls
Ran 66 tests across 8 files. [900.00ms]
PASS: exact pairing plus launch, auth-login, smoke-env, and preset security unit tests
bun test v1.3.1 (89fa0f34)

src/runtime/opencode-acp/events.test.ts:
(pass) reduceOpencodeAcpNotification — session/update dispatch > agent_message_chunk with text content → replyText += content.text
(pass) reduceOpencodeAcpNotification — session/update dispatch > agent_thought_chunk with text → thoughtText, NOT replyText (grok discipline)
(pass) reduceOpencodeAcpNotification — session/update dispatch > tool_call and tool_call_update both bump toolCalls
(pass) reduceOpencodeAcpNotification — session/update dispatch > usage_update snaps totalTokens into state.usage
(pass) reduceOpencodeAcpNotification — session/update dispatch > available_commands_update consumed silently (session-init only) [1.00ms]
(pass) reduceOpencodeAcpNotification — session/update dispatch > agent_message_chunk without text content adds a warning
(pass) reduceOpencodeAcpNotification — session/update dispatch > unknown method returns ignored without mutating state
(pass) reduceOpencodeAcpNotification — session/update dispatch > unknown sessionUpdate subtype returns ignored (forward-compat)
(pass) reduceOpencodeAcpResponse — session/prompt terminal response > captures stopReason + usage from result
(pass) reduceOpencodeAcpResponse — session/prompt terminal response > missing stopReason still marks turn complete
(pass) reduceOpencodeAcpFrames — replay the Phase 0b captured turn > full one-word turn: 10 thought chunks + 1 message chunk + usage + response [1.00ms]
(pass) reduceOpencodeAcpFrames — replay the Phase 0b captured turn > thinking-only terminal turn (no agent_message_chunk) — replyText stays empty

src/runtime/opencode-acp/child-env.test.ts:
(pass) buildOpencodeChildEnv — deny-by-default boundary > locks the exact hardened ancestor candidate set
(pass) buildOpencodeChildEnv — deny-by-default boundary > rejects sticky world-writable /tmp instead of silently degrading [8.00ms]
(pass) buildOpencodeChildEnv — deny-by-default boundary > passes only runtime/network allowlist and controls all state roots [22.00ms]
(pass) buildOpencodeChildEnv — deny-by-default boundary > safe inline policy disables every local tool without replacing provider/model [13.00ms]
(pass) buildOpencodeChildEnv — deny-by-default boundary > unsafe opt-in explicitly overrides the wizard's persisted safe policy [10.00ms]
(pass) buildOpencodeChildEnv — deny-by-default boundary > safe runtime renders ordinary same-uid config through a strict allowlist [17.00ms]
(pass) buildOpencodeChildEnv — deny-by-default boundary > copies only blessed API auth fields into fresh data and keeps persistent state outside the child [22.00ms]
(pass) buildOpencodeChildEnv — deny-by-default boundary > never exposes planted persistent DB/log/cache/state/tmp descendants in safe or unsafe mode [30.00ms]
(pass) buildOpencodeChildEnv — deny-by-default boundary > removes a partially built launch tree when env construction fails [19.00ms]
(pass) buildOpencodeChildEnv — deny-by-default boundary > pre-spawn revalidation hard-fails when an ancestor discovery candidate appears [19.00ms]
(pass) buildOpencodeChildEnv — deny-by-default boundary > keeps active roots but reclaims a dead-owner crash root without following symlinks [47.00ms]
(pass) buildOpencodeChildEnv — deny-by-default boundary > a transient cleanup pathname swap is retried after child exit [29.00ms]
(pass) buildOpencodeChildEnv — deny-by-default boundary > a dead owner marker is retained while an orphan child still references the root [64.00ms]
(pass) buildOpencodeChildEnv — deny-by-default boundary > an exact exited-process identity exemption never hides a live descendant or PID mismatch [80.00ms]
(pass) buildOpencodeChildEnv — deny-by-default boundary > rejects symlinks at workDir and every security-sensitive state layer [19.00ms]
(pass) buildOpencodeChildEnv — deny-by-default boundary > rejects permissive modes and foreign ownership without repairing them [2.00ms]

src/runtime/opencode-acp/profile-state.test.ts:
(pass) OpenCode private profile state > loads, atomically updates, backs up, and writes a session [15.00ms]
(pass) OpenCode private profile state > a post-load config symlink cannot redirect session writeback [2.00ms]
(pass) OpenCode private profile state > boot refuses a config symlink before self-heal can write its target [1.00ms]
(pass) OpenCode private profile state > backup refuses a pre-planted .prev symlink
(pass) OpenCode private profile state > runtime hint rejects suspicious config leaves for every runtime [1.00ms]

src/runtime/opencode-acp/client.test.ts:
(pass) OpencodeAcpClient — request/response correlation > request() resolves with the matching response's result [102.00ms]
(pass) OpencodeAcpClient — request/response correlation > error response rejects the promise with a shaped message [73.00ms]
(pass) OpencodeAcpClient — streaming notifications > emits 'notification' for every session/update frame [78.00ms]
(pass) OpencodeAcpClient — process lifecycle > child exit rejects all pending requests [78.00ms]
(pass) OpencodeAcpClient — process lifecycle > isRunning flips false after stop() [10.00ms]
(pass) OpencodeAcpClient — process lifecycle > explicit child env is not merged with the client's process.env [72.00ms]

src/runtime/opencode-acp/runtime.test.ts:
[opencode-acp] session/new — ses_test...
(pass) openOpencodeRuntime — cwd and tool policy > safe default keeps spawn + ACP session in one external launch workspace [168.01ms]
[opencode-acp] session/load ok — resumed ses_existing...
(pass) openOpencodeRuntime — cwd and tool policy > safe session/load reuses the exact spawn PWD as its ACP cwd [143.01ms]
[opencode-acp] session/new — ses_test...
(pass) openOpencodeRuntime — cwd and tool policy > explicit unsafe flag restores project cwd and emits a trusted-task warning [117.00ms]
[opencode-acp] session/new — ses_test...
(pass) openOpencodeRuntime — opening lifecycle > normal stop removes the launch root and copied vendor auth [141.01ms]
[opencode-acp] session/new — ses_test...
[opencode-acp] session/new — ses_test...
[opencode-acp] session/new — ses_test...
[opencode-acp] session/new — ses_test...
[opencode-acp] session/new — ses_test...
[opencode-acp] session/new — ses_test...
[opencode-acp] session/new — ses_test...
[opencode-acp] session/new — ses_test...
[opencode-acp] session/new — ses_test...
[opencode-acp] session/new — ses_test...
[opencode-acp] session/new — ses_test...
[opencode-acp] session/new — ses_test...
[opencode-acp] session/new — ses_test...
[opencode-acp] session/new — ses_test...
[opencode-acp] session/new — ses_test...
[opencode-acp] session/new — ses_test...
[opencode-acp] session/new — ses_test...
[opencode-acp] session/new — ses_test...
[opencode-acp] session/new — ses_test...
[opencode-acp] session/new — ses_test...
[opencode-acp] session/new — ses_test...
[opencode-acp] session/new — ses_test...
[opencode-acp] session/new — ses_test...
[opencode-acp] session/new — ses_test...
[opencode-acp] session/new — ses_test...
(pass) openOpencodeRuntime — opening lifecycle > repeated open/stop cycles do not accumulate launch roots [3290.14ms]
(pass) openOpencodeRuntime — opening lifecycle > an ancestor candidate planted by the version probe hard-fails before ACP spawn [73.00ms]
(pass) openOpencodeRuntime — opening lifecycle > production rejects canonical same-version packages below project cwd or node workDir [27.00ms]
(pass) openOpencodeRuntime — opening lifecycle > initialize failure force-kills the child before rejecting [126.01ms]
(pass) openOpencodeRuntime — opening lifecycle > onClient exposes a stalled-handshake child synchronously for shutdown [54.00ms]
[opencode-acp] session/new — ses_idle...
(pass) opencodeThink — failed-turn lifecycle > prompt idle timeout force-kills the child before rejecting [181.01ms]
[opencode-acp] session/new — ses_rescue_i...
[opencode-acp] #383 thinking-only terminal turn (chunks=0 thoughtChunks=1) — re-prompting for plain-text final
(pass) opencodeThink — failed-turn lifecycle > a failed thinking-only rescue discards the child before returning [173.01ms]

src/runtime/opencode-acp/binary.test.ts:
(pass) resolvePinnedOpencodeBinary > locks the non-root uid=gid umask-0002 compatibility policy [1.00ms]
(pass) resolvePinnedOpencodeBinary > accepts the canonical package entrypoint and probes it from the external cwd [50.00ms]
(pass) resolvePinnedOpencodeBinary > accepts an npm-style PATH shim but returns the canonical package binary [26.00ms]
(pass) resolvePinnedOpencodeBinary > rejects a same-version fake package inside the project before executing it [1.00ms]
(pass) resolvePinnedOpencodeBinary > rejects forged package metadata and noncanonical entrypoints [3.00ms]
(pass) resolvePinnedOpencodeBinary > rejects unsafe file, package-directory, ancestor, and owner modes [3.00ms]
(pass) resolvePinnedOpencodeBinary > still enforces exact --version output after package identity succeeds [26.00ms]
(pass) resolvePinnedOpencodeBinary > refuses a caller-selected version other than the vetted release pin [1.00ms]
(pass) resolvePinnedOpencodeBinary > rejects a same-version package in a monorepo ancestor before probing it [1.00ms]
(pass) resolvePinnedOpencodeBinary > discovers a workspace ancestor when the configured project leaf is absent [1.00ms]
(pass) resolvePinnedOpencodeBinary > launcher absolute path wins over a hostile search PATH [27.00ms]
(pass) resolvePinnedOpencodeBinary > rejects non-absolute overrides [1.00ms]

 62 pass
 0 fail
 636 expect() calls
Ran 62 tests across 6 files. [5.63s]
PASS: agent-node OpenCode package, child-env, profile-state, ACP client/events, and runtime unit tests
$ tsc --noEmit
PASS: agent-network typecheck
$ bun build src/client.ts --outdir dist/src --target node --minify && bun build bin/cli.ts --outdir dist/bin --target node --minify --external @sleep2agi/commhub-server --external bun:sqlite --external '../../server/*' && bun build src/node-server.ts --outdir dist/src --target node --minify --external @modelcontextprotocol/sdk && bun build src/im/feishu/worker.ts --outdir dist/src/im/feishu --target node --minify --external @larksuiteoapi/node-sdk && tsc --emitDeclarationOnly --declaration --outDir dist && bun x javascript-obfuscator dist/bin/cli.js --output dist/bin/cli.js --compact true --string-array true --string-array-encoding base64 && bun x javascript-obfuscator dist/src/client.js --output dist/src/client.js --compact true --string-array true && bun x javascript-obfuscator dist/src/node-server.js --output dist/src/node-server.js --compact true --string-array true
Bundled 1 module in 8ms

  client.js  3.67 KB  (entry point)

Bundled 93 modules in 70ms

  cli.js  0.65 MB  (entry point)

Bundled 2 modules in 6ms

  node-server.js  10.56 KB  (entry point)

Bundled 597 modules in 151ms

  worker.js  2.29 MB  (entry point)


[javascript-obfuscator-cli] Obfuscating file: dist/bin/cli.js... 

[javascript-obfuscator-cli] Obfuscating file: dist/src/client.js... 

[javascript-obfuscator-cli] Obfuscating file: dist/src/node-server.js... 
PASS: agent-network release bundle build
PASS network validator: /home/bun/prefix/node_modules/opencode-ai/bin/opencode.exe
PASS agent-node validator + version probe: /home/bun/prefix/node_modules/opencode-ai/bin/opencode.exe
PASS private uid=gid group-write paths: 6
PASS: non-root uid=gid=1000 umask-0002 real opencode-ai@1.18.1 passes network and agent-node gates
[anet] OpenCode anthropic API-key login for 'auth-gate' (exact opencode-ai@1.18.1, fresh private state).
[anet] Persistent auth changes only after upstream exits 0 and the credential shape validates.
[anet] ✗ OpenCode auth-login failed; persistent auth unchanged: upstream login exited without success (code=64 signal=none)
PASS: failed upstream auth-login exits nonzero, preserves old auth, and cleans its disposable root
[anet] Starting new session for "gate" [opencode-cli]...

[anet] using installed exact @sleep2agi/agent-node@2.5.0-preview.26.
[anet] Token: [REDACTED_TOKEN]
PASS: stale global bypassed; later exact global received protected PATH/binary/version/base; npx was not executed
[anet] Starting new session for "gate" [opencode-cli]...

[anet] using installed exact @sleep2agi/agent-node@2.5.0-preview.26.
[anet] Token: [REDACTED_TOKEN]
PASS: same-version project-local OpenCode/agent-node payloads skipped; canonical external packages launched
[anet] Starting new session for "gate" [opencode-cli]...

[anet] Incompatible agent-node for opencode-cli.
[anet] ANET_AGENT_NODE_BIN is not the exact trusted @sleep2agi/agent-node@2.5.0-preview.26: project/node-local agent-node package payload is not trusted
Install the exact vetted pair: npm install -g @sleep2agi/agent-network@2.3.0-preview.34 @sleep2agi/agent-node@2.5.0-preview.26
[anet] Refusing to start: an unsupported agent-node could silently select another runtime.
PASS: explicit exact project-local agent-node is rejected before execution and without npx
[anet] Starting new session for "gate" [opencode-cli]...

[anet] using installed exact @sleep2agi/agent-node@2.5.0-preview.26.
[anet] Token: [REDACTED_TOKEN]
PASS: capable-looking global preview.21 rejected; later exact global preview.26 launched without npx
[anet] Starting new session for "gate" [opencode-cli]...

[anet] Incompatible agent-node for opencode-cli.
[anet] ANET_AGENT_NODE_BIN is not the exact trusted @sleep2agi/agent-node@2.5.0-preview.26: resolved agent-node package is not exact version 2.5.0-preview.26
Install the exact vetted pair: npm install -g @sleep2agi/agent-network@2.3.0-preview.34 @sleep2agi/agent-node@2.5.0-preview.26
[anet] Refusing to start: an unsupported agent-node could silently select another runtime.
PASS: ANET_AGENT_NODE_BIN cannot bypass the exact hardened pair
[anet] Starting new session for "gate" [opencode-cli]...

[anet] Incompatible agent-node for opencode-cli.
[anet] No exact trusted global @sleep2agi/agent-node@2.5.0-preview.26 is available (no exact trusted @sleep2agi/agent-node@2.5.0-preview.26 package entrypoint found on PATH; first rejected candidate: @sleep2agi/agent-node@2.5.0-preview.26 entrypoint is outside node_modules/@sleep2agi/agent-node); automatic npx execution is disabled for opencode-cli
Install the exact vetted pair: npm install -g @sleep2agi/agent-network@2.3.0-preview.34 @sleep2agi/agent-node@2.5.0-preview.26
[anet] Refusing to start: an unsupported agent-node could silently select another runtime.
PASS: no exact global rejects startup, executes no npx, and prints exact dual-package remediation

OVERALL: PASS
