chainlesschain 0.166.133 → 0.166.136
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +15 -3
- package/package.json +1 -1
- package/src/assets/linux-subreaper/linux-arm64/manifest.json +1 -1
- package/src/assets/linux-subreaper/linux-x64/manifest.json +1 -1
- package/src/command-help-index.json +1 -1
- package/src/commands/agent.js +7 -0
- package/src/data/changelog.json +26 -0
- package/src/lib/background-launch-profile.js +167 -16
- package/src/lib/coding-task-guidance.js +10 -9
- package/src/lib/context-memory-kernel/message-adapter.js +5 -1
- package/src/lib/safe-mode.js +10 -0
- package/src/lib/session-history-origins.js +36 -0
- package/src/runtime/agent-core.js +117 -28
- package/src/runtime/headless-runner.js +3 -3
package/README.md
CHANGED
|
@@ -2,11 +2,23 @@
|
|
|
2
2
|
|
|
3
3
|
Command-line interface for installing, configuring, and managing [ChainlessChain](https://www.chainlesschain.com), including the coding agent, governed tools, sessions, memory, and IDE integrations.
|
|
4
4
|
|
|
5
|
-
## CLI 0.166.
|
|
5
|
+
## CLI 0.166.135 — verified public release
|
|
6
6
|
|
|
7
|
-
This
|
|
7
|
+
This release recovers genuine OpenAI Responses JSON disconnects before accepted output, bounds stalled requests, and settles completed streaming responses without waiting for socket EOF. It preserves empty tool allowlists across resume and rejects malformed saved permission, sandbox and budget settings. HTTP/provider failures, cancellation, usage persistence and incomplete tool-call replay protections remain authoritative.
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
Install with `npm i -g chainlesschain@0.166.135 --registry https://registry.npmjs.org`, restart the chat host, then run `cc --version` and `cc doctor`. Public Open VSX **0.37.173** recommends CLI **0.166.135**. JetBrains **0.4.189** recommends the same CLI and has been uploaded after passing all release gates, but awaits Marketplace approval/listing. Current public JetBrains **0.4.188** retains its CLI **0.166.133** recommendation; Kernel stays **0.1.11**. Check the [release guide](https://docs.chainlesschain.com/chainlesschain/agent-platform-release.html) for channel publication status. Microsoft Marketplace and PC/mobile releases are outside this release scope.
|
|
10
|
+
|
|
11
|
+
Exact CLI source `82aa6d703a8695721a7ccf0793a435853bc2bbf2` passed all 76 CLI CI jobs and all 6 Strict Sandbox jobs. [OIDC publication](https://github.com/chainlesschain/chainlesschain/actions/runs/41168880872) and [independent public readback](https://github.com/chainlesschain/chainlesschain/actions/runs/41169921448) verified registry bytes, provenance and direct child dependencies. [Component-specific publication evidence](../../docs/research/cli/evidence/release135-public/publication.json).
|
|
12
|
+
|
|
13
|
+
The preceding OpenAI native cohort accepted **39/45** tasks. Runtime regression tests and this release do not change that historical result or establish formal paid/native production admission.
|
|
14
|
+
|
|
15
|
+
A subsequent user-authorized retest used this public CLI, Open VSX 0.37.173 and the verified JetBrains 0.4.189 Actions build without waiting for Marketplace review. Only the six previously failed samples were rerun: **1/6** passed (`verify-26`), with **5** still failing independent acceptance. The cross-version cumulative result is **40/45**, not a new full 45-task success rate. All 100 upstream calls have complete usage; token-only estimate **$5.880530**, invoice unknown. The temporary task secret was deleted. [Targeted report](../../docs/research/cli/verify01-openai-135/retest-six-99b93fc209/REPORT.md).
|
|
16
|
+
|
|
17
|
+
## CLI 0.166.133 — verified preceding release
|
|
18
|
+
|
|
19
|
+
CLI **0.166.133** is published on npm. It preserves required empty OpenAI Responses reasoning summaries, applies the self-evolution switch to all model-owning `cc ui` entry points, and supports explicitly configured OpenAI-compatible local/LAN platforms through `custom`, with optional credentials and unchanged endpoint/model aliases. It also exposes canonical allowed mutation paths in provider tool schemas while retaining execution-time scope and file-identity checks. Controlled regressions and release gates do not establish live-platform task acceptance.
|
|
20
|
+
|
|
21
|
+
Install with `npm i -g chainlesschain@0.166.133 --registry https://registry.npmjs.org`, restart the chat host, then run `cc --version` and `cc doctor`. Companion Open VSX **0.37.171** / JetBrains **0.4.188** are public and recommend this CLI; their original payloads match the official artifacts. Kernel stays **0.1.11**. Exact source `2eb0c5b1e048509362f9d980f94acee55c2ffa9f` passed Linux, Windows and macOS CLI CI / CLI Strict Sandbox. [OIDC publication](https://github.com/chainlesschain/chainlesschain/actions/runs/41073612529) and [independent readback](https://github.com/chainlesschain/chainlesschain/actions/runs/41075409835) verified immutable package bytes and signed provenance. Consult the [current release guide](https://docs.chainlesschain.com/chainlesschain/agent-platform-release.html) for IDE channel availability. Microsoft Marketplace and PC/mobile releases are outside this release scope.
|
|
10
22
|
|
|
11
23
|
## CLI 0.166.132 — verified public baseline before this source revision
|
|
12
24
|
|
package/package.json
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
"a2a": "Usage: chainlesschain a2a [options] [command]\n\nA2A Protocol — agent-to-agent communication\n\nOptions:\n -h, --help display help for command\n\nCommands:\n register [options] <name> Register an agent card\n discover [options] Discover agents by capability or skill\n submit [options] <agent-id> <input> Submit a task to an agent\n status [options] <task-id> Get task status\n complete <task-id> <output> Mark a task as completed\n fail <task-id> <error> Mark a task as failed\n peers [options] List all registered agents\n cards [options] List all agent cards with details\n negotiate [options] <agent-id> <capabilities> Check if an agent supports required capabilities\n task-statuses [options] List V2 task statuses (Phase 81)\n card-statuses [options] List V2 card statuses (Phase 81)\n subscription-types [options] List V2 subscription types (Phase 81)\n negotiation-results [options] List V2 negotiation outcomes (Phase 81)\n validate-card [options] <name> Validate an agent card against the A2A schema\n set-card-status [options] <cardId> <status> Transition a card between active/inactive/expired\n card-status [options] <cardId> Show a card's V2 status\n send-task-v2 [options] <agentId> <input> Submit a V2 task (in-memory, with optional timeout)\n start-working [options] <taskId> Transition a V2 task to working\n request-input [options] <taskId> <prompt> Request user input while a V2 task is working\n provide-input [options] <taskId> <input> Provide input for an input-required V2 task\n complete-task-v2 [options] <taskId> [output] Complete a V2 task (from working only)\n fail-task-v2 [options] <taskId> [error] Fail a V2 task with an error message\n cancel-task [options] <taskId> [reason] Cancel a non-terminal V2 task\n check-timeout [options] <taskId> Check V2 task timeout (auto-fails if past deadline)\n task-v2 [options] <taskId> Show a V2 task snapshot\n tasks-v2 [options] List V2 tasks\n negotiate-v2 [options] <cardJson> Phase 81 capability negotiation against an agent card\n stats-v2 [options] Aggregate V2 stats (tasks + cards + subscriptions)\n enums-v2 Show V2 governance enums\n register-agent-v2 [options] Register an a2a agent profile (pending)\n activate-agent-v2 <id> Activate agent\n suspend-agent-v2 <id> Suspend agent\n retire-agent-v2 <id> Retire agent (terminal)\n touch-agent-v2 <id> Refresh lastTouchedAt\n get-agent-v2 <id> Get agent\n list-agents-v2 List agents\n create-message-v2 [options] Create an a2a message (queued)\n start-message-v2 <id> Transition message to sending\n deliver-message-v2 <id> Transition message to delivered\n fail-message-v2 [options] <id> Fail message\n cancel-message-v2 [options] <id> Cancel message\n get-message-v2 <id> Get message\n list-messages-v2 List messages\n set-max-active-agents-v2 <n> Set per-owner active cap\n set-max-pending-messages-v2 <n> Set per-agent pending cap\n set-agent-idle-ms-v2 <n> Set idle threshold\n set-message-stuck-ms-v2 <n> Set stuck threshold\n auto-suspend-idle-agents-v2 Auto-suspend idle agents\n auto-fail-stuck-messages-v2 Auto-fail stuck sending messages\n gov-stats-v2 V2 governance aggregate stats\n help [command] display help for command\n",
|
|
6
6
|
"activitypub": "Usage: chainlesschain activitypub|ap [options] [command]\n\nActivityPub C2S bridge — actors, outbox/inbox, Follow/Create/Like/Announce\n\nOptions:\n -h, --help display help for command\n\nCommands:\n actor Manage local and remote ActivityPub actors\n publish [options] <content> Publish a Create(Note) activity to the actor's outbox\n follow [options] <target> Publish a Follow activity (target: username or actor URL)\n accept [options] <follow-activity-id> Accept a pending Follow request\n unfollow [options] <target> Publish an Undo(Follow) activity\n like [options] <object-id> Publish a Like activity for an object URL\n announce [options] <object-id> Publish an Announce (boost) activity for an object URL\n outbox [options] <username> List an actor's outbox activities\n inbox [options] <username> List an actor's inbox activities\n deliver [options] <username> <activity-json> Simulate inbox delivery — push a JSON activity into a local actor's inbox\n followers [options] <username> List followers of an actor\n search [options] <query> Search actors and/or notes in the local Fediverse index\n following [options] <username> List who this actor is following\n enums-v2 Show V2 enums\n register-actor-v2 [options] Register an AP actor profile (pending)\n activate-actor-v2 <id> Activate actor\n suspend-actor-v2 <id> Suspend actor\n deactivate-actor-v2 <id> Deactivate actor (terminal)\n touch-actor-v2 <id> Refresh lastTouchedAt\n get-actor-v2 <id> Get actor\n list-actors-v2 List actors\n create-activity-v2 [options] Create an AP activity (queued)\n start-activity-v2 <id> Transition activity to delivering\n deliver-activity-v2 <id> Transition activity to delivered\n fail-activity-v2 [options] <id> Fail activity\n cancel-activity-v2 [options] <id> Cancel activity\n get-activity-v2 <id> Get activity\n list-activities-v2 List activities\n set-max-active-actors-v2 <n> Set per-owner active cap\n set-max-pending-activities-v2 <n> Set per-actor pending cap\n set-actor-idle-ms-v2 <n> Set idle threshold\n set-activity-stuck-ms-v2 <n> Set stuck threshold\n auto-suspend-idle-actors-v2 Auto-suspend idle actors\n auto-fail-stuck-activities-v2 Auto-fail stuck delivering activities\n gov-stats-v2 V2 governance aggregate stats\n help [command] display help for command\n",
|
|
7
7
|
"agenda": "Usage: chainlesschain agenda [options] [command]\n\nFire agent-scheduled wakeups / crons / monitors (persisted by the `schedule`\ntool)\n\nOptions:\n -h, --help display help for command\n\nCommands:\n list [options] List all scheduled entries\n run [options] Fire every entry that is due now\n cancel [options] <id> Remove a scheduled entry by id\n prune [options] Remove finished entries (fired / matched / exhausted /\n expired)\n help [command] display help for command\n",
|
|
8
|
-
"agent": "Usage: chainlesschain agent|a [options] [command] [task...]\n\nStart an agentic AI session (reads/writes files, runs commands)\n\nArguments:\n task Headless task 鈥?when given (or with -p /\n piped stdin), runs non-interactively\n\nOptions:\n --model <model> Model name\n --provider <provider> LLM provider (ollama, openai,\n volcengine, deepseek, ...)\n --base-url <url> API base URL\n --service-tier <tier> Reviewed provider service tier\n (standard, fast, flex, ultrafast)\n --api-key <key> API key\n --think [level] Enable Anthropic extended thinking\n (level: think | hard | ultra; Anthropic\n models only)\n --ultrathink Maximum Anthropic extended thinking (=\n --think ultra)\n --thinking-budget <n> Thinking token budget for legacy Claude\n models (Sonnet 4.5 / Opus 4.0-4.5 /\n older); clamped below max_tokens.\n Adaptive-thinking models ignore it (they\n use --think's effort). Requires\n --think/--ultrathink.\n --image <path> Attach an image (png/jpg/jpeg/gif/webp)\n to the prompt for a vision-capable model\n (headless; repeatable)\n --vision-model <id> Explicit image model override\n (otherwise: multimodal main model, then\n config llm.visionModel or\n doubao-seed-2-0-lite-260215)\n --session <id> Resume a previous agent session\n -c, --continue Resume the most recent session (no id\n needed 鈥?claude --continue parity)\n --resume [id] Resume a session by id (no id 鈫?most\n recent 鈥?claude --resume parity)\n --fork-session When resuming, branch into a NEW session\n id and leave the original untouched\n (claude --fork-session parity)\n --fork-request-id <key> Stable idempotency key for\n --fork-session retries (omit for a new\n fork each invocation)\n --add-dir <dir> Extra working directory the agent may\n read/search/edit (repeatable)\n --checkpoint Auto-snapshot the work tree before each\n mutating tool (DEFAULT inside a git\n repo; cc checkpoint restore to roll\n back)\n --no-checkpoint Disable auto-checkpointing (it is on by\n default inside git repos)\n --managed-checkpoint Checkpoint bounded mutating tools with\n Process Broker; unavailable writer\n lifetimes are reported as coverage none\n --managed-checkpoint-state <dir> Durable managed-checkpoint state\n directory (must be outside the\n workspace)\n --managed-checkpoint-exclude <path> Explicit workspace path excluded from\n managed checkpoints; repeatable and\n reported as partial coverage\n --safe-mode Run bare: disable project memory,\n settings hooks, memory recall, IDE\n context, status line and update notice\n (permission rules STAY active)\n --bare Everything --safe-mode disables PLUS\n skills, plugins and MCP/IDE auto-connect\n — minimal fast surface for scripted\n calls (explicit --mcp-config still\n loads; permission rules STAY active)\n --ephemeral No session persistence: nothing is\n written to the session store (headless;\n a --resume id still replays history\n read-only)\n --decision-mode <mode> Typed Skill-routing experiment for\n durable headless sessions: off | shadow\n | suggest (default: \"off\")\n --decision-provider <provider> Decision provider: typesafe | laya\n (local) | system-one (compatible API)\n (default: \"typesafe\")\n --decision-model <model> Decision model (defaults:\n typesafe=jev-latest, laya=laya; required\n for system-one)\n --decision-base-url <url> System One API base URL (laya: loopback\n only; other providers: HTTPS or\n loopback)\n --decision-timeout-ms <ms> Decision timeout in milliseconds\n (50-30000; default: 800)\n --capabilities Print a machine-readable capability\n manifest (JSON: protocol version, tools,\n permission modes, exit codes) and exit\n --disable-slash-commands Interactive REPL: don't dispatch '/'\n input as slash commands — it reaches the\n model verbatim (/exit and /quit stay\n live)\n --ax-screen-reader Screen-reader friendly output: no ANSI\n colors, no in-place status line repaints\n (also CC_SCREEN_READER=1)\n --worktree Run this session in a fresh git worktree\n (isolated branch; auto-removed when the\n session changes nothing)\n --no-worktree Background Git task: explicitly share\n the current work tree (intended for\n read-only tasks; writable runs are not\n isolated)\n --unattended Enable fail-closed shell action policy\n for scheduled/event-driven runs\n --unattended-allow <classes> Comma-separated action classes\n explicitly allowed for unattended shell\n runs\n --sandbox [image] Run shell commands in an ephemeral\n Docker sandbox (network disabled by\n default)\n --sandbox-mode <mode> Sandbox posture: off | workspace-write |\n strict (explicit isolation modes fail\n closed)\n --sandbox-network Allow network access inside --sandbox\n --otlp <file> Write the agent run's OpenTelemetry\n spans (model/tool/retry) as OTLP/JSON to\n <file>\n --otlp-content Include prompt/response CONTENT in\n --otlp spans (off by default; content is\n redacted unless this is set)\n --auto-rewake When an async `asyncRewake` hook (e.g. a\n background test) fails at turn end,\n re-engage the agent to fix it (bounded\n by --max-rewakes)\n --auto-pin Force-pin the original task (first user\n turn) through compaction (ON by default;\n disable with CC_AUTO_PIN=0 or config\n context.autoPin=false)\n --max-rewakes <n> Max auto-rewake re-drives per run\n (default 1); implies --auto-rewake\n --bg, --background Start as a detached background agent and\n return its id immediately\n --agent-id <id> Agent id for scoped memory recall\n --recall-limit <n> Top-K memories to inject into system\n prompt\n --recall-query <q> Query string for startup memory recall\n --no-recall-memory Disable startup memory recall\n --vim Start the interactive REPL in vim-mode\n line editing (toggle later with /vim; or\n CC_VIM=1)\n --no-stream Disable streamed response rendering\n --no-park-on-exit Close the session-core handle on exit\n instead of parking it\n --bundle <path> Agent bundle directory (may execute\n local MCP commands from mcp.json)\n -p, --print [prompt] Headless: run one non-interactive turn\n and exit\n --output-format <fmt> Headless output: text | json |\n stream-json (default: \"text\")\n --max-turns <n> Cap agent loop iterations (0 =\n unlimited; interactive streams default\n to unlimited, unattended runs to 50)\n --review-changes Headless: review write/edit tool changes\n once within remaining budgets; final\n text only (no partial deltas)\n --json-schema <file> Headless structured output: final reply\n must be JSON validating against this\n schema (invalid replies retried)\n --allowed-tools <list> Comma/space-separated tool allow-list\n (headless)\n --disallowed-tools <list> Comma/space-separated tool deny-list\n (headless)\n --permission-mode <mode> manual | auto | dontAsk | default | plan\n | acceptEdits | bypassPermissions (plan\n headless-only)\n --no-file-refs Disable @path file-reference expansion\n in the prompt (headless)\n --no-slash-macros Disable /name custom-command\n (.claude/commands) expansion in the\n prompt (headless)\n --system-prompt <text> Replace the built-in system prompt\n (literal text or @file)\n --append-system-prompt <text> Append extra guidance to the system\n prompt (literal text or @file)\n --no-project-memory Skip auto-loaded project memory (cc.md /\n CLAUDE.md / AGENTS.md hierarchy,\n .chainlesschain/rules.md,\n .claude/rules/*.md) — a lean system\n prompt for token-tight surfaces (e.g.\n IDE chat). The agent can still read\n those files with tools when it needs\n them.\n --output-style <name> Apply a named output-style persona\n (.claude/output-styles/<name>.md or a\n built-in: concise | explanatory |\n learning)\n --input-format <fmt> Headless input: text | stream-json\n (NDJSON user events on stdin,\n multi-turn) (default: \"text\")\n --trace-id <id> Correlation id stamped as `trace_id` on\n every stream-json output line (also read\n from CC_TRACE_ID). Lets an IDE/bridge\n trace one run across its own logs, the\n CLI, transcripts and diagnostics.\n Auto-generated per run when omitted.\n --fallback-model <model> Backup model(s) to try in order when the\n primary fails (transient error or\n model-not-found). Repeatable or\n comma-separated; up to 3. A\n `provider:model` entry (e.g.\n openai:gpt-4o) falls back\n cross-provider, using that provider's\n API key from its env var (skipped if\n unset — never reuses the primary key).\n Defaults to config llm.fallbackModels.\n --include-partial-messages Emit live assistant-text deltas as\n stream_event lines (requires\n --output-format stream-json)\n --include-hook-events Emit redacted hook and subagent\n lifecycle events (requires\n --output-format stream-json)\n --goal [id] Bind a cc goal into the run (id, or omit\n the value to auto-resolve the active\n goal)\n --goal-assess After the run, ask the model to assess\n goal progress and persist it (opt-in;\n costs one extra completion; requires\n --goal)\n --goal-condition <spec> Session completion condition; prefer\n verify:<manifest.json> for independent\n engineering acceptance, or\n exit-zero:/file-exists:/contains:/regex:;\n model:<desc> uses model judgment\n --goal-recovery-reviewed Resume an interrupted goal after\n reviewing prior effects and usage\n (requires --resume/--continue and\n --goal-condition)\n --max-outer-turns <n> Max outer re-drive turns for\n --goal-condition (default 10, hard cap\n 100)\n --goal-max-tokens <n> Token budget across --goal-condition\n outer turns\n --goal-max-cost <usd> Cost budget (USD) across\n --goal-condition outer turns\n --goal-max-time <ms> Wall-clock budget (ms) across\n --goal-condition outer turns\n --mcp-config <file> Load ad-hoc MCP servers from a JSON file\n for this run (headless); their tools\n become callable (mcp__<server>__<tool>)\n --no-mcp Don't auto-connect MCP servers\n registered with `cc mcp add\n --auto-connect` (--mcp-config still\n loads)\n --ide Force-enable IDE bridge auto-connect:\n discover a running editor's MCP server\n via ~/.chainlesschain/ide/*.json\n (default: auto inside an IDE integrated\n terminal)\n --no-ide Disable IDE bridge auto-connect\n --pdh Force-enable PDH bridge auto-connect:\n discover the Android app's\n device-capability MCP server via\n ~/.chainlesschain/pdh-bridge/*.json\n (default: auto when\n CHAINLESSCHAIN_PDH_PORT is set)\n --no-pdh Disable PDH bridge auto-connect\n --jetbrains Force-enable IntelliJ IDEA built-in MCP\n (IDEA >= 2025.2) as server `idea`\n (default: auto when the ChainlessChain\n JetBrains plugin injects\n CHAINLESSCHAIN_JETBRAINS_MCP_URL)\n --no-jetbrains Disable IDEA built-in MCP auto-connect\n --interactive-approvals Stream mode: confirm-tier approvals\n become approval_request/approval\n stdin-stdout events instead of failing\n closed (chat-panel UX)\n --permission-prompt-tool <tool> Defer tool approvals to an MCP tool\n (mcp__<server>__<tool>; requires\n --mcp-config) instead of headless\n fail-closed\n --remote-control Route confirm-tier approvals to paired\n mobile/web devices — requires a relay or\n explicit LAN opt-in; headless waits\n fail-closed, interactive races the\n terminal prompt (also /remote-control)\n --remote-control-allow-lan Permit --remote-control to expose its\n authenticated WS endpoint on a trusted\n LAN\n --channels <list> Inbound channels for the interactive\n session (webhook[:port],telegram) —\n external events enter as user turns;\n webhook needs its bearer token, telegram\n needs channels.telegram.allowFrom\n --settings <file> Merge an extra\n .claude/settings.json-shaped file for\n this run: permission rules\n (allow/ask/deny) + native config\n overrides (model, env)\n --max-budget-usd <amount> Hard USD spend cap: stop the run before\n the next paid LLM call once the\n estimated cost reaches this (headless;\n uses the cc cost price table)\n --session-budget Enable the durable session-wide budget\n authority (headless; requires\n --session/--resume/--continue)\n --session-max-concurrent <n> Durable session cap for concurrent child\n work\n --session-max-spawns <n> Durable session cap for total child-work\n admissions\n --session-max-depth <n> Durable session cap for child-agent\n nesting depth\n --session-max-turns <n> Durable session cap for model turns\n across resumes\n --session-max-tokens <n> Durable session cap for\n provider-reported tokens across resumes\n --session-max-cost-usd <amount> Durable session cost cap across resumes\n (requires priced provider usage)\n --session-max-wall-ms <n> Durable session cap for cumulative\n active wall time\n --session-max-tool-ms <n> Durable session cap for aggregate tool\n execution time\n --observability-workspace <id> Persist a workspace scope on new session\n authority for causal reports\n --observability-team <id> Persist a team scope on new session\n authority for causal reports\n --observability-policy <id> Persist a policy scope on new session\n authority for causal reports\n --strict-mcp-config Use ONLY --mcp-config servers; ignore\n registered (cc mcp add) and IDE-bridge\n MCP for a reproducible tool surface\n --project-mcp Opt in to loading a project-scoped\n .mcp.json (default off; a checked-in\n .mcp.json can spawn commands)\n --replay-user-messages Stream-input mode: echo each accepted\n stdin user message back as a `user`\n event (transcript/correlation)\n --allow-dangerous-bypass Bypass all permission prompts in\n headless mode (DANGEROUS: auto-approves\n all tool calls) (default: false)\n -y, --yolo Alias for --allow-dangerous-bypass\n (default: false)\n --dangerously-skip-permissions Alias for --allow-dangerous-bypass\n (default: false)\n -h, --help display help for command\n\nCommands:\n controlled-host [options] Run against a pre-provisioned Linux\n permission authority (one-shot, dontAsk)\n process-ownership Inspect Linux process quarantine or\n explicitly stop one recoverable\n execution\n",
|
|
8
|
+
"agent": "Usage: chainlesschain agent|a [options] [command] [task...]\n\nStart an agentic AI session (reads/writes files, runs commands)\n\nArguments:\n task Headless task 鈥?when given (or with -p /\n piped stdin), runs non-interactively\n\nOptions:\n --model <model> Model name\n --provider <provider> LLM provider (ollama, openai,\n volcengine, deepseek, ...)\n --base-url <url> API base URL\n --service-tier <tier> Reviewed provider service tier\n (standard, fast, flex, ultrafast)\n --api-key <key> API key\n --think [level] Enable Anthropic extended thinking\n (level: think | hard | ultra; Anthropic\n models only)\n --ultrathink Maximum Anthropic extended thinking (=\n --think ultra)\n --thinking-budget <n> Thinking token budget for legacy Claude\n models (Sonnet 4.5 / Opus 4.0-4.5 /\n older); clamped below max_tokens.\n Adaptive-thinking models ignore it (they\n use --think's effort). Requires\n --think/--ultrathink.\n --image <path> Attach an image (png/jpg/jpeg/gif/webp)\n to the prompt for a vision-capable model\n (headless; repeatable)\n --vision-model <id> Explicit image model override\n (otherwise: multimodal main model, then\n config llm.visionModel or\n doubao-seed-2-0-lite-260215)\n --session <id> Resume a previous agent session\n -c, --continue Resume the most recent session (no id\n needed 鈥?claude --continue parity)\n --resume [id] Resume a session by id (no id 鈫?most\n recent 鈥?claude --resume parity)\n --fork-session When resuming, branch into a NEW session\n id and leave the original untouched\n (claude --fork-session parity)\n --fork-request-id <key> Stable idempotency key for\n --fork-session retries (omit for a new\n fork each invocation)\n --add-dir <dir> Extra working directory the agent may\n read/search/edit (repeatable)\n --checkpoint Auto-snapshot the work tree before each\n mutating tool (DEFAULT inside a git\n repo; cc checkpoint restore to roll\n back)\n --no-checkpoint Disable auto-checkpointing (it is on by\n default inside git repos)\n --managed-checkpoint Checkpoint bounded mutating tools with\n Process Broker; unavailable writer\n lifetimes are reported as coverage none\n --managed-checkpoint-state <dir> Durable managed-checkpoint state\n directory (must be outside the\n workspace)\n --managed-checkpoint-exclude <path> Explicit workspace path excluded from\n managed checkpoints; repeatable and\n reported as partial coverage\n --safe-mode Run bare: disable project memory,\n settings hooks, memory recall, IDE\n context, status line and update notice\n (permission rules STAY active)\n --bare Everything --safe-mode disables PLUS\n skills, plugins and MCP/IDE auto-connect\n — minimal fast surface for scripted\n calls (explicit --mcp-config still\n loads; permission rules STAY active)\n --no-plugins Disable plugin customizations for this\n run\n --ephemeral No session persistence: nothing is\n written to the session store (headless;\n a --resume id still replays history\n read-only)\n --decision-mode <mode> Typed Skill-routing experiment for\n durable headless sessions: off | shadow\n | suggest (default: \"off\")\n --decision-provider <provider> Decision provider: typesafe | laya\n (local) | system-one (compatible API)\n (default: \"typesafe\")\n --decision-model <model> Decision model (defaults:\n typesafe=jev-latest, laya=laya; required\n for system-one)\n --decision-base-url <url> System One API base URL (laya: loopback\n only; other providers: HTTPS or\n loopback)\n --decision-timeout-ms <ms> Decision timeout in milliseconds\n (50-30000; default: 800)\n --capabilities Print a machine-readable capability\n manifest (JSON: protocol version, tools,\n permission modes, exit codes) and exit\n --disable-slash-commands Interactive REPL: don't dispatch '/'\n input as slash commands — it reaches the\n model verbatim (/exit and /quit stay\n live)\n --ax-screen-reader Screen-reader friendly output: no ANSI\n colors, no in-place status line repaints\n (also CC_SCREEN_READER=1)\n --worktree Run this session in a fresh git worktree\n (isolated branch; auto-removed when the\n session changes nothing)\n --no-worktree Background Git task: explicitly share\n the current work tree (intended for\n read-only tasks; writable runs are not\n isolated)\n --unattended Enable fail-closed shell action policy\n for scheduled/event-driven runs\n --unattended-allow <classes> Comma-separated action classes\n explicitly allowed for unattended shell\n runs\n --sandbox [image] Run shell commands in an ephemeral\n Docker sandbox (network disabled by\n default)\n --sandbox-mode <mode> Sandbox posture: off | workspace-write |\n strict (explicit isolation modes fail\n closed)\n --sandbox-network Allow network access inside --sandbox\n --otlp <file> Write the agent run's OpenTelemetry\n spans (model/tool/retry) as OTLP/JSON to\n <file>\n --otlp-content Include prompt/response CONTENT in\n --otlp spans (off by default; content is\n redacted unless this is set)\n --auto-rewake When an async `asyncRewake` hook (e.g. a\n background test) fails at turn end,\n re-engage the agent to fix it (bounded\n by --max-rewakes)\n --auto-pin Force-pin the original task (first user\n turn) through compaction (ON by default;\n disable with CC_AUTO_PIN=0 or config\n context.autoPin=false)\n --max-rewakes <n> Max auto-rewake re-drives per run\n (default 1); implies --auto-rewake\n --bg, --background Start as a detached background agent and\n return its id immediately\n --agent-id <id> Agent id for scoped memory recall\n --recall-limit <n> Top-K memories to inject into system\n prompt\n --recall-query <q> Query string for startup memory recall\n --no-recall-memory Disable startup memory recall\n --vim Start the interactive REPL in vim-mode\n line editing (toggle later with /vim; or\n CC_VIM=1)\n --no-stream Disable streamed response rendering\n --no-park-on-exit Close the session-core handle on exit\n instead of parking it\n --bundle <path> Agent bundle directory (may execute\n local MCP commands from mcp.json)\n -p, --print [prompt] Headless: run one non-interactive turn\n and exit\n --output-format <fmt> Headless output: text | json |\n stream-json (default: \"text\")\n --max-turns <n> Cap agent loop iterations (0 =\n unlimited; interactive streams default\n to unlimited, unattended runs to 50)\n --review-changes Headless: review write/edit tool changes\n once within remaining budgets; final\n text only (no partial deltas)\n --json-schema <file> Headless structured output: final reply\n must be JSON validating against this\n schema (invalid replies retried)\n --allowed-tools <list> Comma/space-separated tool allow-list\n (headless)\n --disallowed-tools <list> Comma/space-separated tool deny-list\n (headless)\n --permission-mode <mode> manual | auto | dontAsk | default | plan\n | acceptEdits | bypassPermissions (plan\n headless-only)\n --no-file-refs Disable @path file-reference expansion\n in the prompt (headless)\n --no-slash-macros Disable /name custom-command\n (.claude/commands) expansion in the\n prompt (headless)\n --system-prompt <text> Replace the built-in system prompt\n (literal text or @file)\n --append-system-prompt <text> Append extra guidance to the system\n prompt (literal text or @file)\n --no-project-memory Skip auto-loaded project memory (cc.md /\n CLAUDE.md / AGENTS.md hierarchy,\n .chainlesschain/rules.md,\n .claude/rules/*.md) — a lean system\n prompt for token-tight surfaces (e.g.\n IDE chat). The agent can still read\n those files with tools when it needs\n them.\n --output-style <name> Apply a named output-style persona\n (.claude/output-styles/<name>.md or a\n built-in: concise | explanatory |\n learning)\n --input-format <fmt> Headless input: text | stream-json\n (NDJSON user events on stdin,\n multi-turn) (default: \"text\")\n --trace-id <id> Correlation id stamped as `trace_id` on\n every stream-json output line (also read\n from CC_TRACE_ID). Lets an IDE/bridge\n trace one run across its own logs, the\n CLI, transcripts and diagnostics.\n Auto-generated per run when omitted.\n --fallback-model <model> Backup model(s) to try in order when the\n primary fails (transient error or\n model-not-found). Repeatable or\n comma-separated; up to 3. A\n `provider:model` entry (e.g.\n openai:gpt-4o) falls back\n cross-provider, using that provider's\n API key from its env var (skipped if\n unset — never reuses the primary key).\n Defaults to config llm.fallbackModels.\n --include-partial-messages Emit live assistant-text deltas as\n stream_event lines (requires\n --output-format stream-json)\n --include-hook-events Emit redacted hook and subagent\n lifecycle events (requires\n --output-format stream-json)\n --goal [id] Bind a cc goal into the run (id, or omit\n the value to auto-resolve the active\n goal)\n --goal-assess After the run, ask the model to assess\n goal progress and persist it (opt-in;\n costs one extra completion; requires\n --goal)\n --goal-condition <spec> Session completion condition; prefer\n verify:<manifest.json> for independent\n engineering acceptance, or\n exit-zero:/file-exists:/contains:/regex:;\n model:<desc> uses model judgment\n --goal-recovery-reviewed Resume an interrupted goal after\n reviewing prior effects and usage\n (requires --resume/--continue and\n --goal-condition)\n --max-outer-turns <n> Max outer re-drive turns for\n --goal-condition (default 10, hard cap\n 100)\n --goal-max-tokens <n> Token budget across --goal-condition\n outer turns\n --goal-max-cost <usd> Cost budget (USD) across\n --goal-condition outer turns\n --goal-max-time <ms> Wall-clock budget (ms) across\n --goal-condition outer turns\n --mcp-config <file> Load ad-hoc MCP servers from a JSON file\n for this run (headless); their tools\n become callable (mcp__<server>__<tool>)\n --no-mcp Don't auto-connect MCP servers\n registered with `cc mcp add\n --auto-connect` (--mcp-config still\n loads)\n --no-mcp-auto-connect Disable implicit MCP and editor\n discovery (explicit --mcp-config or\n bridge flags still apply)\n --ide Force-enable IDE bridge auto-connect:\n discover a running editor's MCP server\n via ~/.chainlesschain/ide/*.json\n (default: auto inside an IDE integrated\n terminal)\n --no-ide Disable IDE bridge auto-connect\n --pdh Force-enable PDH bridge auto-connect:\n discover the Android app's\n device-capability MCP server via\n ~/.chainlesschain/pdh-bridge/*.json\n (default: auto when\n CHAINLESSCHAIN_PDH_PORT is set)\n --no-pdh Disable PDH bridge auto-connect\n --jetbrains Force-enable IntelliJ IDEA built-in MCP\n (IDEA >= 2025.2) as server `idea`\n (default: auto when the ChainlessChain\n JetBrains plugin injects\n CHAINLESSCHAIN_JETBRAINS_MCP_URL)\n --no-jetbrains Disable IDEA built-in MCP auto-connect\n --interactive-approvals Stream mode: confirm-tier approvals\n become approval_request/approval\n stdin-stdout events instead of failing\n closed (chat-panel UX)\n --permission-prompt-tool <tool> Defer tool approvals to an MCP tool\n (mcp__<server>__<tool>; requires\n --mcp-config) instead of headless\n fail-closed\n --remote-control Route confirm-tier approvals to paired\n mobile/web devices — requires a relay or\n explicit LAN opt-in; headless waits\n fail-closed, interactive races the\n terminal prompt (also /remote-control)\n --remote-control-allow-lan Permit --remote-control to expose its\n authenticated WS endpoint on a trusted\n LAN\n --channels <list> Inbound channels for the interactive\n session (webhook[:port],telegram) —\n external events enter as user turns;\n webhook needs its bearer token, telegram\n needs channels.telegram.allowFrom\n --settings <file> Merge an extra\n .claude/settings.json-shaped file for\n this run: permission rules\n (allow/ask/deny) + native config\n overrides (model, env)\n --max-budget-usd <amount> Hard USD spend cap: stop the run before\n the next paid LLM call once the\n estimated cost reaches this (headless;\n uses the cc cost price table)\n --session-budget Enable the durable session-wide budget\n authority (headless; requires\n --session/--resume/--continue)\n --session-max-concurrent <n> Durable session cap for concurrent child\n work\n --session-max-spawns <n> Durable session cap for total child-work\n admissions\n --session-max-depth <n> Durable session cap for child-agent\n nesting depth\n --session-max-turns <n> Durable session cap for model turns\n across resumes\n --session-max-tokens <n> Durable session cap for\n provider-reported tokens across resumes\n --session-max-cost-usd <amount> Durable session cost cap across resumes\n (requires priced provider usage)\n --session-max-wall-ms <n> Durable session cap for cumulative\n active wall time\n --session-max-tool-ms <n> Durable session cap for aggregate tool\n execution time\n --observability-workspace <id> Persist a workspace scope on new session\n authority for causal reports\n --observability-team <id> Persist a team scope on new session\n authority for causal reports\n --observability-policy <id> Persist a policy scope on new session\n authority for causal reports\n --strict-mcp-config Use ONLY --mcp-config servers; ignore\n registered (cc mcp add) and IDE-bridge\n MCP for a reproducible tool surface\n --project-mcp Opt in to loading a project-scoped\n .mcp.json (default off; a checked-in\n .mcp.json can spawn commands)\n --replay-user-messages Stream-input mode: echo each accepted\n stdin user message back as a `user`\n event (transcript/correlation)\n --allow-dangerous-bypass Bypass all permission prompts in\n headless mode (DANGEROUS: auto-approves\n all tool calls) (default: false)\n -y, --yolo Alias for --allow-dangerous-bypass\n (default: false)\n --dangerously-skip-permissions Alias for --allow-dangerous-bypass\n (default: false)\n -h, --help display help for command\n\nCommands:\n controlled-host [options] Run against a pre-provisioned Linux\n permission authority (one-shot, dontAsk)\n process-ownership Inspect Linux process quarantine or\n explicitly stop one recoverable\n execution\n",
|
|
9
9
|
"agent-network": "Usage: chainlesschain agent-network|anet [options] [command]\n\nDecentralized Agent Network (Phase 24) — DID / registry / credentials / task\nrouting\n\nOptions:\n -h, --help display help for command\n\nCommands:\n config [options] Show network constants & reputation weights\n dimensions [options] List reputation dimensions\n task-statuses [options] List task lifecycle statuses\n did-create [options] Create an Agent DID (Ed25519 + DID document)\n did-resolve [options] <did> Resolve DID → DID document\n dids [options] List Agent DIDs\n did-deactivate [options] <did> Deactivate a DID (also forces registry offline)\n sign [options] <did> <data> Sign data with agent's Ed25519 key\n verify [options] <did> <data> <signature> Verify a signature from an agent\n register [options] <did> <orgId> Register an Agent in the federated registry\n unregister [options] <did> Remove an Agent from the registry\n heartbeat [options] <did> Bump last_heartbeat (forces online)\n discover [options] Discover agents (capability / org / status)\n sweep [options] Mark stale online agents offline\n peer-add [options] <peerId> Add/update a Kademlia peer (k-bucket auto-computed)\n peer-remove [options] <peerId> Remove a peer\n peers [options] List peers (optionally by k-bucket)\n auth-start [options] <did> Issue an auth challenge for a DID\n auth-complete [options] <sessionId> <signature> Complete auth with signature over challenge\n auth-validate [options] <token> Validate a session token (returns agentDid or exits 1)\n auth-sessions [options] List auth sessions\n credential-issue [options] <issuerDid> <subjectDid> Issue a VC with HMAC/Ed25519 proof\n credential-verify [options] <id> Verify a VC (signature + expiry + revocation)\n credential-revoke [options] <id> Revoke a credential\n credential-show [options] <id> Show a credential\n credentials [options] List credentials (filter by subject/issuer/status/type)\n task-route [options] <sourceOrg> <taskType> Route task → pick best-rep agent matching capability\n task-show [options] <taskId> Show a task\n task-status [options] <taskId> <status> Update task status (terminal states set completedAt)\n task-cancel [options] <taskId> Cancel a task (sets CANCELLED + reason)\n tasks [options] List tasks\n rep-update [options] <did> <dimension> <score> Record a reputation observation\n rep-show [options] <did> Show reputation summary (weighted total + per-dim avg)\n rep-history [options] <did> Show reputation history (newest first)\n rep-top [options] Top agents by dimension (or total)\n stats [options] Network-level statistics\n agent-maturities-v2 List V2 agent maturity states\n task-lifecycles-v2 List V2 task lifecycle states\n stats-v2 V2 agent-network stats\n get-max-active-agents-v2 Get max active agents per network (V2)\n set-max-active-agents-v2 <n> Set max active agents per network (V2)\n get-max-pending-tasks-v2 Get max pending tasks per agent (V2)\n set-max-pending-tasks-v2 <n> Set max pending tasks per agent (V2)\n get-agent-idle-ms-v2 Get agent idle threshold (V2)\n set-agent-idle-ms-v2 <ms> Set agent idle threshold (V2)\n get-task-stuck-ms-v2 Get task stuck threshold (V2)\n set-task-stuck-ms-v2 <ms> Set task stuck threshold (V2)\n active-agent-count-v2 <networkId> Active agent count for network (V2)\n pending-task-count-v2 <agentId> Pending task count for agent (V2)\n register-agent-v2 [options] <id> Register a V2 agent\n get-agent-v2 <id> Get a V2 agent\n list-agents-v2 [options] List V2 agents\n set-agent-status-v2 <id> <next> Set V2 agent status\n activate-agent-v2 <id> Activate a V2 agent\n suspend-agent-v2 <id> Suspend a V2 agent\n revoke-agent-v2 <id> Revoke a V2 agent\n touch-agent-v2 <id> Touch a V2 agent\n create-task-v2 [options] <id> Create a V2 task\n get-task-v2 <id> Get a V2 task\n list-tasks-v2 [options] List V2 tasks\n set-task-status-v2 <id> <next> Set V2 task status\n start-task-v2 <id> Start a V2 task\n complete-task-v2 <id> Complete a V2 task\n fail-task-v2 <id> Fail a V2 task\n cancel-task-v2 <id> Cancel a V2 task\n auto-suspend-idle-agents-v2 Auto-suspend idle V2 agents\n auto-fail-stuck-tasks-v2 Auto-fail stuck V2 tasks\n help [command] display help for command\n",
|
|
10
10
|
"agents": "Usage: chainlesschain agents [options] [command]\n\nUser-defined subagents (.claude/agents/*.md)\n\nOptions:\n -h, --help display help for command\n\nCommands:\n background|bg [options] List background agent sessions\n logs [options] <id> Print recent output from a background agent\n view [options] <id> Show a detailed background agent view\n stop [options] <id> Stop a running background agent\n rename [options] <id> <title...> Rename a background agent session\n list|ls [options] List discovered subagents (project +\n personal)\n show [options] <name> Show an agent's metadata + system prompt\n run [options] <name> [task...] Run a task headlessly as the named subagent\n new [options] <name> Scaffold a new agent file (project-native\n .chainlesschain/agents/)\n help [command] display help for command\n",
|
|
11
11
|
"android": "Usage: chainlesschain android [options] [command]\n\nAndroid-native bridge: ContentResolver / SAF / Accessibility / Shizuku / root\n(Plan A A7)\n\nOptions:\n -h, --help display help for command\n\nCommands:\n caps [options] Probe what bridge capabilities are available on this\n device\n contacts ContentResolver: contacts\n sms ContentResolver: SMS\n calls ContentResolver: call log\n app PackageManager: app inventory + Intent\n fs SAF / sandbox filesystem\n a11y Accessibility Service\n shizuku Shizuku ADB-like privileges\n root Magisk root su\n perms [options] <name> Check (and request) a runtime permission\n help [command] display help for command\n",
|
package/src/commands/agent.js
CHANGED
|
@@ -390,6 +390,7 @@ export function registerAgentCommand(program, dependencies = {}) {
|
|
|
390
390
|
"--bare",
|
|
391
391
|
"Everything --safe-mode disables PLUS skills, plugins and MCP/IDE auto-connect — minimal fast surface for scripted calls (explicit --mcp-config still loads; permission rules STAY active)",
|
|
392
392
|
)
|
|
393
|
+
.option("--no-plugins", "Disable plugin customizations for this run")
|
|
393
394
|
.option(
|
|
394
395
|
"--ephemeral",
|
|
395
396
|
"No session persistence: nothing is written to the session store (headless; a --resume id still replays history read-only)",
|
|
@@ -619,6 +620,10 @@ export function registerAgentCommand(program, dependencies = {}) {
|
|
|
619
620
|
"--no-mcp",
|
|
620
621
|
"Don't auto-connect MCP servers registered with `cc mcp add --auto-connect` (--mcp-config still loads)",
|
|
621
622
|
)
|
|
623
|
+
.option(
|
|
624
|
+
"--no-mcp-auto-connect",
|
|
625
|
+
"Disable implicit MCP and editor discovery (explicit --mcp-config or bridge flags still apply)",
|
|
626
|
+
)
|
|
622
627
|
.option(
|
|
623
628
|
"--ide",
|
|
624
629
|
"Force-enable IDE bridge auto-connect: discover a running editor's MCP server via ~/.chainlesschain/ide/*.json (default: auto inside an IDE integrated terminal)",
|
|
@@ -864,6 +869,8 @@ export function registerAgentCommand(program, dependencies = {}) {
|
|
|
864
869
|
// 2.1.169 parity): flip every customization kill-switch BEFORE anything
|
|
865
870
|
// loads. Permission rules stay active. --bare (CC_BARE) is safe mode
|
|
866
871
|
// PLUS skills, plugins and MCP/IDE/PDH/JetBrains auto-connect off.
|
|
872
|
+
const { applyCustomizationOptions } = await import("../lib/safe-mode.js");
|
|
873
|
+
applyCustomizationOptions(options);
|
|
867
874
|
let safeIsolationRequested = options.permissionMode === "auto";
|
|
868
875
|
{
|
|
869
876
|
const {
|
package/src/data/changelog.json
CHANGED
|
@@ -1,6 +1,32 @@
|
|
|
1
1
|
{
|
|
2
2
|
"source": "CHANGELOG.md",
|
|
3
3
|
"releases": [
|
|
4
|
+
{
|
|
5
|
+
"productVersion": null,
|
|
6
|
+
"cliVersion": "0.166.136",
|
|
7
|
+
"cliVersions": ["0.166.136"],
|
|
8
|
+
"date": "2026-10-31",
|
|
9
|
+
"title": "",
|
|
10
|
+
"sections": [
|
|
11
|
+
{
|
|
12
|
+
"heading": "Fixed - cc CLI 0.166.136: verified history forks and background policy restrictions (candidate, 2026-10-31)",
|
|
13
|
+
"body": "- Admit complete, verified session-history forks without weakening strict rewind-prefix checks; retain source provenance and capability rejection controls.\n- Preserve plugin/MCP auto-connect restrictions through persisted background launch profiles and real CLI flags. Reject malformed policy values before customization loading or fallback normalization.\n- Cover pinned-image metadata/pixel limits and owner-aborted pending approvals with positive and negative controls. Compare fixture inode identities as BigInt to avoid Windows Number precision loss.\n- This is an engineering repair of the five remaining failed tasks, not a new model acceptance run. Historical six-task acceptance stays 1/6 (cumulative 40/45); publication is blocked until the new exact commit passes complete CLI CI and Strict Sandbox."
|
|
14
|
+
}
|
|
15
|
+
]
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
"productVersion": null,
|
|
19
|
+
"cliVersion": "0.166.135",
|
|
20
|
+
"cliVersions": ["0.166.135"],
|
|
21
|
+
"date": "2026-10-31",
|
|
22
|
+
"title": "",
|
|
23
|
+
"sections": [
|
|
24
|
+
{
|
|
25
|
+
"heading": "Fixed - cc CLI 0.166.135: Responses disconnect recovery and launch policy preservation (candidate, 2026-10-31)",
|
|
26
|
+
"body": "- Recover genuine pre-output OpenAI Responses JSON disconnects with the existing bounded retry policy, distinct physical request IDs and synchronous usage settlement. Bound stalled headers and JSON reads, preserve HTTP/provider verdicts and caller cancellation, and keep workflow effects and incomplete tool streams non-replayable.\n- Settle completed Responses events without waiting for socket EOF. A hung reader cancellation no longer blocks timeout settlement; already queued transport aborts and user cancellation remain authoritative.\n- Select newly generated context handoffs before ordinary conversation so a full conversation partition cannot evict its summary. Retain the same input budget, data-only trust and oversized-summary rejection.\n- Preserve an explicit empty tool allowlist through saved launch argv and the CLI parser. Reject malformed persisted permission modes, sandbox modes and budgets instead of silently restoring weaker defaults.\n- Strengthen generic regression guidance for delegated lifecycle ownership, race controls, persisted history projections and a final response within the iteration budget. Add same-handle metadata-race and approval-cancellation controls without changing historical model candidates.\n- Give independent IDE cleanup a separate bounded deadline after the model task ends. The previous `verify-26` trace actually contains `error_max_turns` at 20/20; cleanup had obscured that result with an expired-deadline error. Historical OpenAI acceptance remains 39/45 and is not reclassified by these fixes.\n- Prepare companion Open VSX 0.37.172 and JetBrains 0.4.189 for CLI 0.166.135. Required child npm packages retain their published versions; publication must follow the exact-source multi-platform gates and upstream readback."
|
|
27
|
+
}
|
|
28
|
+
]
|
|
29
|
+
},
|
|
4
30
|
{
|
|
5
31
|
"productVersion": null,
|
|
6
32
|
"cliVersion": "0.166.133",
|
|
@@ -48,6 +48,29 @@ function positive(value, integer = false) {
|
|
|
48
48
|
return integer ? Math.floor(number) : number;
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
+
function invalidProfileField(field) {
|
|
52
|
+
const error = new Error(`Invalid background launch profile field: ${field}`);
|
|
53
|
+
error.code = "BACKGROUND_LAUNCH_PROFILE_INVALID";
|
|
54
|
+
return error;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function policyBudget(value, field) {
|
|
58
|
+
const text = String(value).trim();
|
|
59
|
+
const cost = field.toLowerCase().includes("cost");
|
|
60
|
+
const number = Number(value);
|
|
61
|
+
if (
|
|
62
|
+
value == null ||
|
|
63
|
+
!(cost ? /^\+?(?:\d+(?:\.\d*)?|\.\d+)(?:e[+-]?\d+)?$/i : /^\+?\d+$/).test(
|
|
64
|
+
text,
|
|
65
|
+
) ||
|
|
66
|
+
!Number.isFinite(number) ||
|
|
67
|
+
number <= 0 ||
|
|
68
|
+
(!cost && !Number.isSafeInteger(number))
|
|
69
|
+
)
|
|
70
|
+
throw invalidProfileField(`budget.${field}`);
|
|
71
|
+
return number;
|
|
72
|
+
}
|
|
73
|
+
|
|
51
74
|
function sortedStrings(values, { split = false, pathsFrom = null } = {}) {
|
|
52
75
|
const raw = Array.isArray(values) ? values : values ? [values] : [];
|
|
53
76
|
const expanded = split
|
|
@@ -356,6 +379,17 @@ export function captureBackgroundLaunchProfile({
|
|
|
356
379
|
index++;
|
|
357
380
|
return next;
|
|
358
381
|
};
|
|
382
|
+
const takePolicy = (allowEmpty = false) => {
|
|
383
|
+
const next = inline !== null ? inline : args[index + 1];
|
|
384
|
+
if (
|
|
385
|
+
next == null ||
|
|
386
|
+
(inline === null && next.startsWith("--")) ||
|
|
387
|
+
(!allowEmpty && next.trim() === "")
|
|
388
|
+
)
|
|
389
|
+
throw invalidProfileField(`argv.${name}`);
|
|
390
|
+
if (inline === null) index++;
|
|
391
|
+
return next;
|
|
392
|
+
};
|
|
359
393
|
switch (name) {
|
|
360
394
|
case "--provider":
|
|
361
395
|
profile.llm.provider = scalar(take());
|
|
@@ -376,14 +410,17 @@ export function captureBackgroundLaunchProfile({
|
|
|
376
410
|
fallbackModels.push(...orderedStrings(take(), { split: true }));
|
|
377
411
|
break;
|
|
378
412
|
case "--allowed-tools":
|
|
379
|
-
profile.tools.allowed = sortedStrings(
|
|
413
|
+
profile.tools.allowed = sortedStrings(takePolicy(true), {
|
|
414
|
+
split: true,
|
|
415
|
+
});
|
|
380
416
|
break;
|
|
381
417
|
case "--disallowed-tools":
|
|
382
|
-
profile.tools.disallowed = sortedStrings(
|
|
418
|
+
profile.tools.disallowed = sortedStrings(takePolicy(true), {
|
|
419
|
+
split: true,
|
|
420
|
+
});
|
|
383
421
|
break;
|
|
384
422
|
case "--permission-mode": {
|
|
385
|
-
|
|
386
|
-
profile.permission.mode = PERMISSION_MODES.has(mode) ? mode : "default";
|
|
423
|
+
profile.permission.mode = scalar(takePolicy());
|
|
387
424
|
break;
|
|
388
425
|
}
|
|
389
426
|
case "--allow-dangerous-bypass":
|
|
@@ -395,7 +432,7 @@ export function captureBackgroundLaunchProfile({
|
|
|
395
432
|
profile.permission.unattended = true;
|
|
396
433
|
break;
|
|
397
434
|
case "--unattended-allow":
|
|
398
|
-
profile.permission.unattendedAllow = sortedStrings(
|
|
435
|
+
profile.permission.unattendedAllow = sortedStrings(takePolicy(true), {
|
|
399
436
|
split: true,
|
|
400
437
|
});
|
|
401
438
|
break;
|
|
@@ -413,8 +450,7 @@ export function captureBackgroundLaunchProfile({
|
|
|
413
450
|
profile.sandbox.image = scalar(take(true));
|
|
414
451
|
break;
|
|
415
452
|
case "--sandbox-mode": {
|
|
416
|
-
|
|
417
|
-
profile.sandbox.mode = SANDBOX_MODES.has(mode) ? mode : null;
|
|
453
|
+
profile.sandbox.mode = scalar(takePolicy());
|
|
418
454
|
break;
|
|
419
455
|
}
|
|
420
456
|
case "--sandbox-network":
|
|
@@ -427,6 +463,12 @@ export function captureBackgroundLaunchProfile({
|
|
|
427
463
|
profile.mcp.disabled = true;
|
|
428
464
|
profile.runtime.noMcpAutoConnect = true;
|
|
429
465
|
break;
|
|
466
|
+
case "--no-mcp-auto-connect":
|
|
467
|
+
profile.runtime.noMcpAutoConnect = true;
|
|
468
|
+
break;
|
|
469
|
+
case "--no-plugins":
|
|
470
|
+
profile.plugins.disabled = true;
|
|
471
|
+
break;
|
|
430
472
|
case "--strict-mcp-config":
|
|
431
473
|
profile.mcp.strict = true;
|
|
432
474
|
break;
|
|
@@ -461,28 +503,43 @@ export function captureBackgroundLaunchProfile({
|
|
|
461
503
|
addDirs.push(take());
|
|
462
504
|
break;
|
|
463
505
|
case "--max-turns":
|
|
464
|
-
profile.budget.maxTurns =
|
|
506
|
+
profile.budget.maxTurns = policyBudget(takePolicy(), "maxTurns");
|
|
465
507
|
break;
|
|
466
508
|
case "--max-budget-usd":
|
|
467
|
-
profile.budget.maxCostUsd =
|
|
509
|
+
profile.budget.maxCostUsd = policyBudget(takePolicy(), "maxCostUsd");
|
|
468
510
|
break;
|
|
469
511
|
case "--thinking-budget":
|
|
470
|
-
profile.budget.thinkingTokens =
|
|
512
|
+
profile.budget.thinkingTokens = policyBudget(
|
|
513
|
+
takePolicy(),
|
|
514
|
+
"thinkingTokens",
|
|
515
|
+
);
|
|
471
516
|
break;
|
|
472
517
|
case "--max-rewakes":
|
|
473
|
-
profile.budget.maxRewakes =
|
|
518
|
+
profile.budget.maxRewakes = policyBudget(takePolicy(), "maxRewakes");
|
|
474
519
|
break;
|
|
475
520
|
case "--max-outer-turns":
|
|
476
|
-
profile.budget.maxOuterTurns =
|
|
521
|
+
profile.budget.maxOuterTurns = policyBudget(
|
|
522
|
+
takePolicy(),
|
|
523
|
+
"maxOuterTurns",
|
|
524
|
+
);
|
|
477
525
|
break;
|
|
478
526
|
case "--goal-max-tokens":
|
|
479
|
-
profile.budget.goalMaxTokens =
|
|
527
|
+
profile.budget.goalMaxTokens = policyBudget(
|
|
528
|
+
takePolicy(),
|
|
529
|
+
"goalMaxTokens",
|
|
530
|
+
);
|
|
480
531
|
break;
|
|
481
532
|
case "--goal-max-cost":
|
|
482
|
-
profile.budget.goalMaxCostUsd =
|
|
533
|
+
profile.budget.goalMaxCostUsd = policyBudget(
|
|
534
|
+
takePolicy(),
|
|
535
|
+
"goalMaxCostUsd",
|
|
536
|
+
);
|
|
483
537
|
break;
|
|
484
538
|
case "--goal-max-time":
|
|
485
|
-
profile.budget.goalMaxTimeMs =
|
|
539
|
+
profile.budget.goalMaxTimeMs = policyBudget(
|
|
540
|
+
takePolicy(),
|
|
541
|
+
"goalMaxTimeMs",
|
|
542
|
+
);
|
|
486
543
|
break;
|
|
487
544
|
case "--safe-mode":
|
|
488
545
|
profile.runtime.safeMode = true;
|
|
@@ -666,6 +723,93 @@ export function normalizeBackgroundLaunchProfile(input) {
|
|
|
666
723
|
`Unsupported background launch profile version: ${input?.version ?? "missing"}`,
|
|
667
724
|
);
|
|
668
725
|
}
|
|
726
|
+
const rejectField = (field) => {
|
|
727
|
+
throw invalidProfileField(field);
|
|
728
|
+
};
|
|
729
|
+
for (const group of [
|
|
730
|
+
"llm",
|
|
731
|
+
"tools",
|
|
732
|
+
"permission",
|
|
733
|
+
"sandbox",
|
|
734
|
+
"mcp",
|
|
735
|
+
"plugins",
|
|
736
|
+
"runtime",
|
|
737
|
+
"budget",
|
|
738
|
+
])
|
|
739
|
+
if (
|
|
740
|
+
input[group] != null &&
|
|
741
|
+
(typeof input[group] !== "object" || Array.isArray(input[group]))
|
|
742
|
+
)
|
|
743
|
+
rejectField(group);
|
|
744
|
+
for (const [group, fields] of Object.entries({
|
|
745
|
+
permission: [
|
|
746
|
+
"dangerousBypass",
|
|
747
|
+
"unattended",
|
|
748
|
+
"interactiveApprovals",
|
|
749
|
+
"remoteControl",
|
|
750
|
+
],
|
|
751
|
+
sandbox: ["enabled", "network"],
|
|
752
|
+
mcp: ["disabled", "strict", "project"],
|
|
753
|
+
plugins: ["disabled"],
|
|
754
|
+
runtime: [
|
|
755
|
+
"safeMode",
|
|
756
|
+
"bare",
|
|
757
|
+
"managedCheckpoint",
|
|
758
|
+
"noProjectMemory",
|
|
759
|
+
"noFileRefs",
|
|
760
|
+
"noSlashMacros",
|
|
761
|
+
"noRecallMemory",
|
|
762
|
+
"noMcpAutoConnect",
|
|
763
|
+
"autoRewake",
|
|
764
|
+
"autoPin",
|
|
765
|
+
"noStream",
|
|
766
|
+
"noParkOnExit",
|
|
767
|
+
],
|
|
768
|
+
}))
|
|
769
|
+
for (const field of fields)
|
|
770
|
+
if (
|
|
771
|
+
input[group]?.[field] !== undefined &&
|
|
772
|
+
typeof input[group][field] !== "boolean"
|
|
773
|
+
)
|
|
774
|
+
rejectField(`${group}.${field}`);
|
|
775
|
+
for (const field of ["ide", "pdh", "jetbrains"])
|
|
776
|
+
if (input.mcp?.[field] != null && !TRI_STATES.has(input.mcp[field]))
|
|
777
|
+
rejectField(`mcp.${field}`);
|
|
778
|
+
for (const [group, fields] of Object.entries({
|
|
779
|
+
tools: ["allowed", "disallowed"],
|
|
780
|
+
permission: ["unattendedAllow"],
|
|
781
|
+
}))
|
|
782
|
+
for (const field of fields) {
|
|
783
|
+
const value = input[group]?.[field];
|
|
784
|
+
if (
|
|
785
|
+
value != null &&
|
|
786
|
+
(!Array.isArray(value) ||
|
|
787
|
+
value.some((item) => typeof item !== "string" || !item.trim()))
|
|
788
|
+
)
|
|
789
|
+
rejectField(`${group}.${field}`);
|
|
790
|
+
}
|
|
791
|
+
// A persisted policy must not silently fall back to a weaker default.
|
|
792
|
+
if (
|
|
793
|
+
input.permission?.mode != null &&
|
|
794
|
+
!PERMISSION_MODES.has(input.permission.mode)
|
|
795
|
+
)
|
|
796
|
+
rejectField("permission.mode");
|
|
797
|
+
if (input.sandbox?.mode != null && !SANDBOX_MODES.has(input.sandbox.mode))
|
|
798
|
+
rejectField("sandbox.mode");
|
|
799
|
+
for (const field of [
|
|
800
|
+
"maxTurns",
|
|
801
|
+
"maxCostUsd",
|
|
802
|
+
"thinkingTokens",
|
|
803
|
+
"maxRewakes",
|
|
804
|
+
"maxOuterTurns",
|
|
805
|
+
"goalMaxTokens",
|
|
806
|
+
"goalMaxCostUsd",
|
|
807
|
+
"goalMaxTimeMs",
|
|
808
|
+
]) {
|
|
809
|
+
const value = input.budget?.[field];
|
|
810
|
+
if (value == null) continue;
|
|
811
|
+
policyBudget(value, field);
|
|
812
|
+
}
|
|
669
813
|
const cwd = canonicalPath(input.workspace?.cwd || process.cwd());
|
|
670
814
|
const safeBase = sanitizeBackgroundBaseUrl(input.llm?.baseUrl);
|
|
671
815
|
const permissionMode = PERMISSION_MODES.has(input.permission?.mode)
|
|
@@ -966,7 +1110,8 @@ export function buildArgvFromBackgroundLaunchProfile(profile) {
|
|
|
966
1110
|
pushValue(argv, "--fallback-model", model);
|
|
967
1111
|
}
|
|
968
1112
|
if (p.tools.allowed !== null) {
|
|
969
|
-
|
|
1113
|
+
// Empty means no tools; omitting this option would allow every tool.
|
|
1114
|
+
argv.push("--allowed-tools", p.tools.allowed.join(","));
|
|
970
1115
|
}
|
|
971
1116
|
pushValue(argv, "--disallowed-tools", p.tools.disallowed.join(","));
|
|
972
1117
|
pushValue(argv, "--permission-mode", p.permission.mode);
|
|
@@ -984,6 +1129,11 @@ export function buildArgvFromBackgroundLaunchProfile(profile) {
|
|
|
984
1129
|
pushFlag(argv, "--sandbox-network", p.sandbox.network);
|
|
985
1130
|
pushValue(argv, "--mcp-config", p.mcp.configFile);
|
|
986
1131
|
pushFlag(argv, "--no-mcp", p.mcp.disabled);
|
|
1132
|
+
pushFlag(
|
|
1133
|
+
argv,
|
|
1134
|
+
"--no-mcp-auto-connect",
|
|
1135
|
+
p.runtime.noMcpAutoConnect && !p.mcp.disabled,
|
|
1136
|
+
);
|
|
987
1137
|
pushFlag(argv, "--strict-mcp-config", p.mcp.strict);
|
|
988
1138
|
pushFlag(argv, "--project-mcp", p.mcp.project);
|
|
989
1139
|
for (const [key, value] of [
|
|
@@ -996,6 +1146,7 @@ export function buildArgvFromBackgroundLaunchProfile(profile) {
|
|
|
996
1146
|
}
|
|
997
1147
|
pushValue(argv, "--settings", p.settings.file);
|
|
998
1148
|
pushValue(argv, "--bundle", p.plugins.bundle);
|
|
1149
|
+
pushFlag(argv, "--no-plugins", p.plugins.disabled && !p.runtime.bare);
|
|
999
1150
|
for (const dir of p.workspace.addDirs) pushValue(argv, "--add-dir", dir);
|
|
1000
1151
|
pushValue(argv, "--max-turns", p.budget.maxTurns);
|
|
1001
1152
|
pushValue(argv, "--max-budget-usd", p.budget.maxCostUsd);
|
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
/** Shared correctness guidance for normal and file-only coding sessions. */
|
|
2
2
|
export const CODING_TASK_QUALITY_GUIDANCE = `Coding task quality:
|
|
3
|
-
- Read the actual implementation and its exported API before writing a caller or test.
|
|
4
|
-
- Keep progress explanations to one or two sentences.
|
|
5
|
-
- Resolve each relative import from the destination file's directory. Read back
|
|
6
|
-
-
|
|
7
|
-
-
|
|
8
|
-
- For
|
|
9
|
-
- Trace rejection before asserting its aftermath:
|
|
10
|
-
-
|
|
11
|
-
-
|
|
3
|
+
- Read the actual implementation and its exported API before writing a caller or test. Verify parameter order, return shapes, fixture requirements and the delegated owner of each lifecycle transition.
|
|
4
|
+
- Keep progress explanations to one or two sentences. Resolve uncertainty with targeted reads, reuse unchanged source already inspected, and put code directly in write/edit arguments.
|
|
5
|
+
- Resolve each relative import from the destination file's directory. Read back completed files and fix newDiagnostics. A successful write proves persistence, not test execution or acceptance.
|
|
6
|
+
- Map every acceptance requirement to a fixture, real production call path and observable assertion. Positive and negative controls must reach the same boundary with unrelated prerequisites valid. Avoid mocks that bypass it; identify which assertion would fail without that boundary.
|
|
7
|
+
- Derive case allocation from the user's requirements. A test-count cap does not waive coverage: merge redundant cases to cover gaps within the cap, authorized scope and remaining budget. Report unresolved gaps as incomplete.
|
|
8
|
+
- For races, control the intended transition and complete the current worker/request when testing cancellation or replacement. An earlier unrelated rejection does not prove the target guard. Derive expectations independently from the contract or authoritative state, not by calling the function under test.
|
|
9
|
+
- Trace rejection before asserting its aftermath: it may precede registration or persistence. Inspect whether state stays unchanged, a failed record exists or an event is emitted. Compare independently recorded identities and provenance; equal text does not prove ownership.
|
|
10
|
+
- Inspect persistence projections before comparing histories. Host notices, lineage and imports may accompany user/assistant messages; verify their provenance separately. Report defects that cannot be repaired within the permitted scope instead of inventing passing behavior.
|
|
11
|
+
- Run focused checks with permitted execution tools. With file tools only, inspect imports, contracts and fixtures and say tests were not run. Read-back or static inspection is not a passing test.
|
|
12
|
+
- Finish requested edits and read-back before the final reply; reserve the last available model request for a concise terminal account. For large files, write a small complete section, await its result, then extend it with a unique edit anchor rather than rewriting the growing file. Report delivery, actual checks and limitations once.`;
|
|
12
13
|
|
|
13
14
|
export const HERMETIC_CODING_SYSTEM_PROMPT = `You are a hermetic coding assistant. The exposed file tools are the complete tool surface. Inspect and mutate only the exact paths authorized by the user request. Do not invoke hidden tools, commands, language servers or other execution paths.
|
|
14
15
|
|
|
@@ -549,7 +549,11 @@ export function createSummaryContextItem({
|
|
|
549
549
|
Math.ceil((Buffer.byteLength(content, "utf8") - image.bytes) / 4) +
|
|
550
550
|
image.tokens,
|
|
551
551
|
),
|
|
552
|
-
|
|
552
|
+
// Keep the new handoff ahead of ordinary conversation in the final plan.
|
|
553
|
+
// Otherwise newer messages can fill the partition and evict the summary
|
|
554
|
+
// of the already-dropped history. This is selection priority only: the
|
|
555
|
+
// summary stays unpinned, data-only and subject to the same token ceilings.
|
|
556
|
+
priority: Math.max(600_000, ...parents.map((item) => item.priority)),
|
|
553
557
|
pinned: false,
|
|
554
558
|
createdAt: new Date(now).toISOString(),
|
|
555
559
|
...contentFields,
|
package/src/lib/safe-mode.js
CHANGED
|
@@ -83,3 +83,13 @@ export function bareModeRequested(opts = {}, env = process.env) {
|
|
|
83
83
|
const raw = env.CC_BARE;
|
|
84
84
|
return raw != null && /^(1|true|yes|on)$/i.test(String(raw).trim());
|
|
85
85
|
}
|
|
86
|
+
|
|
87
|
+
/** Apply independent saved restrictions without enabling the rest of bare mode. */
|
|
88
|
+
export function applyCustomizationOptions(options, env = process.env) {
|
|
89
|
+
if (options.plugins === false) env.CC_PLUGINS = "0";
|
|
90
|
+
if (options.mcpAutoConnect === false) {
|
|
91
|
+
options.mcp = false;
|
|
92
|
+
for (const field of ["ide", "pdh", "jetbrains"])
|
|
93
|
+
if (options[field] === undefined) options[field] = false;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
@@ -108,6 +108,42 @@ export function createSessionHistoryOrigins() {
|
|
|
108
108
|
},
|
|
109
109
|
branchPrefix(messages, head, count) {
|
|
110
110
|
if (!context) return null;
|
|
111
|
+
// A branch may retain the complete verified context. A rewind must remove
|
|
112
|
+
// something, so its strict-prefix admission cannot decide this case.
|
|
113
|
+
if (messages.length === context.length) {
|
|
114
|
+
if (
|
|
115
|
+
!/^[a-f0-9]{64}$/u.test(head) ||
|
|
116
|
+
!Number.isSafeInteger(count) ||
|
|
117
|
+
count < 0 ||
|
|
118
|
+
context.some(
|
|
119
|
+
(entry) =>
|
|
120
|
+
historyBearing(entry) &&
|
|
121
|
+
(!entry.origin ||
|
|
122
|
+
!Number.isSafeInteger(entry.origin.first) ||
|
|
123
|
+
!Number.isSafeInteger(entry.origin.last) ||
|
|
124
|
+
entry.origin.first < 0 ||
|
|
125
|
+
entry.origin.last < entry.origin.first ||
|
|
126
|
+
entry.origin.last >= count),
|
|
127
|
+
)
|
|
128
|
+
)
|
|
129
|
+
return null;
|
|
130
|
+
try {
|
|
131
|
+
if (
|
|
132
|
+
fingerprint(context.map((entry) => entry.message)) !==
|
|
133
|
+
fingerprint(messages)
|
|
134
|
+
)
|
|
135
|
+
return null;
|
|
136
|
+
} catch {
|
|
137
|
+
return null;
|
|
138
|
+
}
|
|
139
|
+
return {
|
|
140
|
+
cutoff: count,
|
|
141
|
+
entries: context.map((entry) => ({
|
|
142
|
+
message: entry.message,
|
|
143
|
+
origin: entry.origin ? { ...entry.origin } : null,
|
|
144
|
+
})),
|
|
145
|
+
};
|
|
146
|
+
}
|
|
111
147
|
const cutoff = api.rewind(
|
|
112
148
|
{
|
|
113
149
|
prevHash: head,
|
|
@@ -12671,34 +12671,69 @@ export async function chatWithTools(rawMessages, options) {
|
|
|
12671
12671
|
);
|
|
12672
12672
|
}
|
|
12673
12673
|
|
|
12674
|
-
|
|
12675
|
-
|
|
12676
|
-
|
|
12677
|
-
|
|
12678
|
-
|
|
12679
|
-
|
|
12680
|
-
|
|
12674
|
+
// A JSON-body disconnect has produced no accepted output or executable
|
|
12675
|
+
// tools. Apply the same bounded transport retry and usage settlement used
|
|
12676
|
+
// by streaming; HTTP/provider verdicts and governed workflow calls retain
|
|
12677
|
+
// their existing no-replay rules.
|
|
12678
|
+
return await _retryStreamingChat(
|
|
12679
|
+
(attempt = 0) =>
|
|
12680
|
+
_withResponsesRequestDeadline(
|
|
12681
|
+
async (requestSignal) => {
|
|
12682
|
+
const requestId = supportsOpenAIRequestIdentity
|
|
12683
|
+
? _physicalProviderRequestId(providerRequestId, attempt)
|
|
12684
|
+
: null;
|
|
12685
|
+
const response = await providerFetch(`${url}/responses`, {
|
|
12686
|
+
method: "POST",
|
|
12687
|
+
headers: {
|
|
12688
|
+
"Content-Type": "application/json",
|
|
12689
|
+
Authorization: `Bearer ${key}`,
|
|
12690
|
+
...(requestId ? { "X-Client-Request-Id": requestId } : {}),
|
|
12691
|
+
},
|
|
12692
|
+
signal: requestSignal,
|
|
12693
|
+
body: JSON.stringify(responseBody),
|
|
12694
|
+
});
|
|
12695
|
+
if (!response.ok) {
|
|
12696
|
+
throw await createProviderResponseError(provider, response);
|
|
12697
|
+
}
|
|
12698
|
+
const retryMetadata = captureStreamRetryMetadata(response);
|
|
12699
|
+
try {
|
|
12700
|
+
const data = await response.json();
|
|
12701
|
+
const out = normalizeOpenAIResponsesResponse(data);
|
|
12702
|
+
if (out.usage)
|
|
12703
|
+
out.usage = preserveRequestedServiceTierEvidence(
|
|
12704
|
+
out.usage,
|
|
12705
|
+
options.serviceTier,
|
|
12706
|
+
);
|
|
12707
|
+
delete out.responseId;
|
|
12708
|
+
const providerReceipt = supportsOpenAIRequestIdentity
|
|
12709
|
+
? _openAIProviderRequestReceipt(response, data, requestId)
|
|
12710
|
+
: null;
|
|
12711
|
+
if (providerReceipt) out.providerReceipt = providerReceipt;
|
|
12712
|
+
return out;
|
|
12713
|
+
} catch (error) {
|
|
12714
|
+
attachStreamRetryMetadata(error, retryMetadata);
|
|
12715
|
+
throw error;
|
|
12716
|
+
}
|
|
12717
|
+
},
|
|
12718
|
+
signal,
|
|
12719
|
+
options.streamStallTimeoutMs,
|
|
12720
|
+
),
|
|
12721
|
+
{
|
|
12722
|
+
signal,
|
|
12723
|
+
retries: options.workflowEffectId ? 0 : undefined,
|
|
12724
|
+
strictRetryObserver: options.strictUsageTelemetry === true,
|
|
12725
|
+
...(typeof options.onStreamRetry === "function"
|
|
12726
|
+
? {
|
|
12727
|
+
onRetry: (attempt, error, telemetry) =>
|
|
12728
|
+
options.onStreamRetry(attempt, error, {
|
|
12729
|
+
...telemetry,
|
|
12730
|
+
provider,
|
|
12731
|
+
model: effectiveModel,
|
|
12732
|
+
}),
|
|
12733
|
+
}
|
|
12681
12734
|
: {}),
|
|
12682
12735
|
},
|
|
12683
|
-
|
|
12684
|
-
body: JSON.stringify(responseBody),
|
|
12685
|
-
});
|
|
12686
|
-
if (!response.ok) {
|
|
12687
|
-
throw await createProviderResponseError(provider, response);
|
|
12688
|
-
}
|
|
12689
|
-
const data = await response.json();
|
|
12690
|
-
const out = normalizeOpenAIResponsesResponse(data);
|
|
12691
|
-
if (out.usage)
|
|
12692
|
-
out.usage = preserveRequestedServiceTierEvidence(
|
|
12693
|
-
out.usage,
|
|
12694
|
-
options.serviceTier,
|
|
12695
|
-
);
|
|
12696
|
-
delete out.responseId;
|
|
12697
|
-
const providerReceipt = supportsOpenAIRequestIdentity
|
|
12698
|
-
? _openAIProviderRequestReceipt(response, data, providerRequestId)
|
|
12699
|
-
: null;
|
|
12700
|
-
if (providerReceipt) out.providerReceipt = providerReceipt;
|
|
12701
|
-
return out;
|
|
12736
|
+
);
|
|
12702
12737
|
}
|
|
12703
12738
|
|
|
12704
12739
|
// Real token streaming (--include-partial-messages) for every OpenAI-compatible
|
|
@@ -13047,6 +13082,36 @@ const STREAM_STALL_MS = 20000;
|
|
|
13047
13082
|
const STREAM_STALL_TIMEOUT_MS = 180000;
|
|
13048
13083
|
const _STREAM_STALL = Symbol("stream-stall");
|
|
13049
13084
|
|
|
13085
|
+
/** Bound the whole JSON attempt, including headers and a stalled body read. */
|
|
13086
|
+
async function _withResponsesRequestDeadline(
|
|
13087
|
+
operation,
|
|
13088
|
+
signal,
|
|
13089
|
+
configuredTimeout,
|
|
13090
|
+
) {
|
|
13091
|
+
const timeoutMs = configuredTimeout ?? STREAM_STALL_TIMEOUT_MS;
|
|
13092
|
+
if (timeoutMs === 0) return operation(signal);
|
|
13093
|
+
if (!Number.isFinite(timeoutMs) || timeoutMs < 0 || timeoutMs > 2_147_483_647)
|
|
13094
|
+
throw new RangeError("Invalid Responses request timeout");
|
|
13095
|
+
const controller = new AbortController();
|
|
13096
|
+
const requestSignal = signal
|
|
13097
|
+
? AbortSignal.any([signal, controller.signal])
|
|
13098
|
+
: controller.signal;
|
|
13099
|
+
const timer = setTimeout(() => {
|
|
13100
|
+
const error = new Error(
|
|
13101
|
+
"OpenAI Responses request timed out before its complete response was received",
|
|
13102
|
+
);
|
|
13103
|
+
error.code = "ETIMEDOUT";
|
|
13104
|
+
controller.abort(error);
|
|
13105
|
+
}, timeoutMs);
|
|
13106
|
+
timer.unref?.();
|
|
13107
|
+
try {
|
|
13108
|
+
throwIfAborted(requestSignal);
|
|
13109
|
+
return await raceWithAbort(operation(requestSignal), requestSignal);
|
|
13110
|
+
} finally {
|
|
13111
|
+
clearTimeout(timer);
|
|
13112
|
+
}
|
|
13113
|
+
}
|
|
13114
|
+
|
|
13050
13115
|
/**
|
|
13051
13116
|
* Run a streaming chat attempt with bounded auto-retry on transient API
|
|
13052
13117
|
* connection drops (Claude-Code 2.1.181: "auto-retry for API connection drops
|
|
@@ -13176,7 +13241,9 @@ export async function* _iterateStreamWithStall(reader, opts = {}) {
|
|
|
13176
13241
|
// socket) and throw a retryable error the dispatch seam re-issues.
|
|
13177
13242
|
if (timeoutMs && elapsed >= timeoutMs) {
|
|
13178
13243
|
try {
|
|
13179
|
-
|
|
13244
|
+
// Cancellation itself can hang on a broken transport. Observe its
|
|
13245
|
+
// rejection, but never let it postpone the authoritative timeout.
|
|
13246
|
+
void Promise.resolve(reader.cancel()).catch(() => {});
|
|
13180
13247
|
} catch {
|
|
13181
13248
|
/* best-effort release — the throw below is what matters */
|
|
13182
13249
|
}
|
|
@@ -13474,7 +13541,7 @@ async function _chatOpenAIResponsesStreaming(
|
|
|
13474
13541
|
const decoder = new TextDecoder();
|
|
13475
13542
|
let buf = "";
|
|
13476
13543
|
try {
|
|
13477
|
-
for await (const value of _iterateStreamWithStall(reader, {
|
|
13544
|
+
responses: for await (const value of _iterateStreamWithStall(reader, {
|
|
13478
13545
|
onStall,
|
|
13479
13546
|
stallMs,
|
|
13480
13547
|
stallTimeoutMs,
|
|
@@ -13485,6 +13552,22 @@ async function _chatOpenAIResponsesStreaming(
|
|
|
13485
13552
|
for (const line of lines) {
|
|
13486
13553
|
reduceOpenAIResponsesStreamLine(state, line, ui.onToken, ui.onThinking);
|
|
13487
13554
|
await ui.settle();
|
|
13555
|
+
if (state.terminalError) throw state.terminalError;
|
|
13556
|
+
if (state.normalized) {
|
|
13557
|
+
// response.completed/incomplete is authoritative. Some gateways
|
|
13558
|
+
// leave the HTTP stream alive after it; socket EOF is not required.
|
|
13559
|
+
// Observe an already-queued transport error (especially cancellation)
|
|
13560
|
+
// without waiting for a pending trailing read to finish.
|
|
13561
|
+
await Promise.race([reader.read(), Promise.resolve()]);
|
|
13562
|
+
throwIfAborted(signal);
|
|
13563
|
+
try {
|
|
13564
|
+
void Promise.resolve(reader.cancel()).catch(() => {});
|
|
13565
|
+
} catch {
|
|
13566
|
+
// The terminal response is already complete; cleanup is best effort.
|
|
13567
|
+
}
|
|
13568
|
+
buf = "";
|
|
13569
|
+
break responses;
|
|
13570
|
+
}
|
|
13488
13571
|
}
|
|
13489
13572
|
}
|
|
13490
13573
|
if (buf.trim()) {
|
|
@@ -13526,6 +13609,12 @@ async function _chatOpenAIResponsesStreaming(
|
|
|
13526
13609
|
);
|
|
13527
13610
|
if (providerReceipt) out.providerReceipt = providerReceipt;
|
|
13528
13611
|
return out;
|
|
13612
|
+
} finally {
|
|
13613
|
+
try {
|
|
13614
|
+
reader.releaseLock?.();
|
|
13615
|
+
} catch {
|
|
13616
|
+
// A cancelled or still-settling reader can reject lock release.
|
|
13617
|
+
}
|
|
13529
13618
|
}
|
|
13530
13619
|
const out = finalizeOpenAIResponsesStream(state);
|
|
13531
13620
|
if (out.usage)
|
|
@@ -467,15 +467,15 @@ export function resolveEnabledTools({ allowedTools, readOnly } = {}) {
|
|
|
467
467
|
return names;
|
|
468
468
|
}
|
|
469
469
|
|
|
470
|
-
/**
|
|
470
|
+
/** An absent CLI list is unrestricted; an explicit empty list permits none. */
|
|
471
471
|
export function parseToolList(value) {
|
|
472
|
-
if (
|
|
472
|
+
if (value === null || value === undefined) return null;
|
|
473
473
|
if (Array.isArray(value)) return value.flatMap((v) => parseToolList(v) || []);
|
|
474
474
|
const out = String(value)
|
|
475
475
|
.split(/[,\s]+/)
|
|
476
476
|
.map((s) => s.trim())
|
|
477
477
|
.filter(Boolean);
|
|
478
|
-
return out
|
|
478
|
+
return out;
|
|
479
479
|
}
|
|
480
480
|
|
|
481
481
|
/**
|