Commit Graph
1801 Commits
Author SHA1 Message Date
TheArchitectit 66006cca34 feat(config): parse maxParallelToolCalls and maxConcurrentAgents settings
Adds optional positive-integer parsing for the two new fan-out caps
next to subagentModel, in both camelCase and snake_case forms. Zero
parses as unset; non-numeric values are a named config error. Covered
by three regression tests mirroring the subagentModel test style.
2026-10-13 00:12:35 +00:00
TheArchitectitandClaw 08cce855b5 fix: restore workspace unsafe_code=forbid, fix TaskClaim TOCTOU, remove TUI deps
Review fixes for PR #3250:

1. Restore workspace-level unsafe_code = "forbid" (was downgraded to
   "warn" for TUI support). The per-crate [lints.rust] unsafe_code =
   "allow" on rusty-claude-cli already overrides it where needed.

2. Fix TaskClaim TOCTOU race in claim_task(). Replace exists()-then-write
   with OpenOptions::create_new(true) which atomically fails if the lock
   file already exists, eliminating the race window between the check and
   the write.

3. Remove TUI scaffolding deps (ratatui, tui-textarea, gag, crossbeam-channel,
   libc, unicode-width) and the /tui slash command spec — these belong in
   a future TUI PR and are unused in the current source.

Also updates the provider chain precedence test to reflect the new behavior
where the caller's resolved model is the chain primary.

Co-Authored-By: Claw <noreply@openclaw.ai>
2026-10-13 00:12:35 +00:00
TheArchitectit fe71ead55f test: add subagentModel accessor chain tests
Adds 3 unit tests covering the original silent-drop bug:
- camelCase 'subagentModel' key reads end-to-end
- snake_case 'subagent_model' key reads end-to-end
- blank value returns None (falls back to default model)

Requested by @1716775457damn in PR review.
2026-10-13 00:12:35 +00:00
TheArchitectit 334768249f fix: model_token_limit catch-all returns None for unknown models
Restores the fix from @Offwhite-Del that was lost during rebase.
Unknown models now return None so preflight skips the context-window
check and lets the API enforce its own limits.
2026-10-13 00:12:35 +00:00
TheArchitectit aab02464b2 cargo fmt 2026-10-13 00:12:35 +00:00
TheArchitectitandClaude Fable 5 7aa00a2798 feat(team): team enhancements, subagentModel, parallel tool exec
Port the agent-team enhancement layer onto upstream/main so the model
can spin up coordinated sub-agent teams for parallel work.

subagentModel config wiring (fix):
- Add subagent_model field to RuntimeFeatureConfig + RuntimeConfig::subagent_model()
  accessor so the subagentModel setting (already validated by config_validate.rs)
  is now actually read and stored.
- Agent tool's resolve_agent_model falls back to subagentModel from config
  when no explicit model is passed.

Provider namespace separation:
- New 'custom-openai' provider kind with dedicated env vars
  (CLAWCUSTOMOPENAI_API_KEY / CLAWCUSTOMOPENAI_BASE_URL)
- /setup wizard saves kind: 'custom-openai' for option 5
- Bare model name normalized to 'custom/' prefix to avoid proxy 404s
- Sub-agents inherit /setup-saved provider config via inject_config_as_env_fallbacks

Parallel tool execution:
- Override execute_batch to classify read-only tools as parallel-safe
  and run them concurrently via std::thread::scope
- Results return in original model order

Team coordination layer:
- AgentMessage, TaskClaim, TeamStatus tools + shared mailbox directory
- Mode presets (tiny/1x ... mega/6x) + enriched TeamCreate/Agent descriptions
- Background team watcher
- /team slash command: on/off/status/toggle

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-10-13 00:12:35 +00:00
bellmanandOmX 76fe2e9f46 Document provider compatibility diagnostics and passthrough
Explain the provider compatibility behavior that now has executable coverage so future changes preserve routing, custom gateway slugs, diagnostics, and extra-body passthrough semantics.

Constraint: Task 2 requires docs/status evidence while leader owns Ultragoal checkpoints and .omx/ultragoal remains untouched.
Rejected: Add a new CLI status command | broader than the assigned provider compatibility documentation/status surface.
Confidence: high
Scope-risk: narrow
Directive: Keep docs aligned with provider_diagnostics_for_model, MessageRequest::extra_body, and OpenAI-compatible slash-model tests.
Tested: cargo test --manifest-path rust/Cargo.toml -p api custom_openai_gateway_preserves_slash_model_ids_and_extra_body_params; cargo test --manifest-path rust/Cargo.toml -p api provider_diagnostics_explain_openai_compatible_capabilities; cargo check --manifest-path rust/Cargo.toml -p api
Not-tested: Live provider calls requiring external credentials.
Co-authored-by: OmX <omx@local>
2026-10-13 00:12:35 +00:00
bellmanandOmX 49e796051e Keep G007 mock parity references executable
The follow-up docs gate needed the parity manifest reference string that the mock parity diff checker requires, so this records the verified map-only repair after leader integration.

Constraint: Task 11 scope allowed only PARITY.md, rust/MOCK_PARITY_HARNESS.md, and docs/g007-plugin-mcp-verification-map.md; only PARITY.md needed a change.

Rejected: Editing harness code or .omx/ultragoal | checks proved the remaining gap was docs-map text only and Ultragoal state is leader-owned.

Confidence: high

Scope-risk: narrow

Directive: Keep auto_compact_triggered parity_refs synchronized with PARITY.md when mock parity scenarios change.

Tested: cd rust && python3 scripts/run_mock_parity_diff.py; python3 scripts/validate_cc2_board.py --board .omx/cc2/board.json; rg -n auto_compact_triggered token_cost_reporting auto_compaction threshold from env G007 Plugin/MCP G007-plugin-mcp 12 scripted scenarios 21 captured PARITY.md rust/MOCK_PARITY_HARNESS.md docs/g007-plugin-mcp-verification-map.md; git diff --check

Not-tested: Full workspace suite; Task 11 was a docs/parity-map follow-up with focused executable map validation.

Co-authored-by: OmX <omx@oh-my-codex.dev>
2026-10-13 00:12:35 +00:00
bellmanandOmX d2f2b23dc4 Document G007 mock parity verification boundaries
Record the W4 verification map and parity-doc alignment so plugin/MCP lifecycle mock coverage can be audited without scraping stale harness prose.

Constraint: Task 9 owns mock integration and regression verification/docs mapping, not parser fixes or leader-owned Ultragoal state.

Rejected: Editing .omx/ultragoal or duplicating Worker-4 plugin command fallthrough fixes | leader checkpointing and parser implementation are separate lanes.

Confidence: high

Scope-risk: narrow

Directive: Keep rust/mock_parity_scenarios.json, rust/MOCK_PARITY_HARNESS.md, PARITY.md, and mock_parity_harness.rs aligned when adding scenarios.

Tested: cd rust && python3 scripts/run_mock_parity_diff.py --no-run; cd rust && ./scripts/run_mock_parity_harness.sh; cd rust && python3 scripts/run_mock_parity_diff.py; cargo test --manifest-path rust/Cargo.toml -p rusty-claude-cli build_runtime_plugin_state -- --nocapture; cargo test --manifest-path rust/Cargo.toml -p rusty-claude-cli build_runtime_runs_plugin_lifecycle_init_and_shutdown -- --nocapture; cargo test --manifest-path rust/Cargo.toml -p plugins plugin_registry_runs_initialize_and_shutdown_for_enabled_plugins -- --nocapture; cargo fmt --manifest-path rust/Cargo.toml --all -- --check; cargo check --manifest-path rust/Cargo.toml -p rusty-claude-cli -p mock-anthropic-service; git diff --check

Not-tested: Full workspace test suite; W4 scope is docs/mock verification map and focused plugin/MCP lifecycle regression evidence.

Co-authored-by: OmX <omx@oh-my-codex.dev>
2026-10-13 00:12:35 +00:00
Bellman 18c0bead90 Merge pull request #3336 from ultraworkers/lane/gjc-s32-r4
revert: duplicate /status /cost /version handlers (#3333)
2026-10-13 09:09:51 +09:00
Bellman 6f1819a8ee Merge pull request #3328 from ultraworkers/fix/issue-3259-comprehensive
fix(cli): comprehensive fix for issue #3259 - markdown streaming block spacing
2026-10-13 09:06:25 +09:00
Bellman 122cf3ab0c Merge pull request #3337 from ultraworkers/release/v0.1.4
docs: add CHANGELOG for v0.1.4 release
v0.1.4
2026-10-13 09:05:47 +09:00
clawdbot eb1537ca92 docs: add CHANGELOG for v0.1.4 release 2026-10-13 00:03:23 +00:00
clawdbot c13e71851b Revert "Merge pull request #3333 from ultraworkers/implement-slash-commands"
This reverts commit cf18dac3ee, reversing
changes made to a2eb74c00e.
2026-10-13 00:02:35 +00:00
Bellman 0508c2aa05 Merge pull request #3334 from ultraworkers/lane/gjc-s21-conflicts
Implement :conflicts selector for read_file tool (#3323)
2026-10-13 09:02:11 +09:00
clawdbot 3d8fd5f917 fix(cli): comprehensive fix for issue #3259 - markdown streaming block spacing
Fixes #3259: When rendering markdown in streaming mode, block-level elements
(code blocks, lists, tables, etc.) were losing trailing newlines due to
render_markdown() calling trim_end(). This caused improper spacing when chunks
were split at block boundaries.

Solution:
- Track last_output_ends_with_newline in MarkdownStreamState to detect when
  previous output ended without proper spacing
- In push(): add leading newlines before block elements if previous chunk
  didn't end with newlines
- In push(): restore trailing newlines after code block/table closings that
  were trimmed by render_markdown()
- Add ensure_newlines() helper to prepare spacing before block starts
- Add comprehensive streaming tests that verify idempotence across all
  possible chunk split positions

This ensures that streaming renders produce byte-identical output regardless
of where the input is split into chunks, which is critical for displaying
markdown as it arrives from the API while maintaining proper formatting.
2026-10-13 00:01:51 +00:00
Bellman cf18dac3ee Merge pull request #3333 from ultraworkers/implement-slash-commands
feat: implement /status, /cost, and /version slash commands
2026-10-13 08:59:43 +09:00
clawdbot 2b64546b30 Apply rustfmt formatting 2026-10-12 23:59:04 +00:00
clawdbot 30f607e03a Implement :conflicts selector for read_file tool
- Add real git merge conflict extraction matching GJC behavior
- Extract unresolved conflict blocks (<<<<<<, =======, >>>>>>>)
- Support 3-way conflicts with diff3 markers (|||||||)
- Format conflicts as indexed summary with line ranges and labels
- Return clear 'No unresolved git merge conflicts' when clean
- Add comprehensive behavior tests for 2-way, 3-way, and no-conflicts cases
- Update documentation to mark :conflicts as implemented
- Closes #3323

Tests:
- read_with_selector_conflicts_no_conflicts: verifies clean file message
- read_with_selector_conflicts_two_way: verifies multiple conflict detection
- read_with_selector_conflicts_three_way: verifies diff3 marker support
2026-10-12 23:57:50 +00:00
clawdbot 4bd5a6381e feat: implement /status, /cost, and /version slash commands
- Add real implementations for /status, /cost, and /version slash commands
- /status shows session metadata including message count and token usage
- /cost displays cumulative token usage including cache metrics
- /version shows the claw-code version
- All three commands are fully backed by existing Session infrastructure
- Add comprehensive tests for all three commands
- Remove expectations that these commands return None

These commands fulfill the requirement to implement 2-3 missing GJC slash
commands that the existing claw-code runtime can fully support.
2026-10-12 23:57:07 +00:00
Bellman a2eb74c00e Merge pull request #3332 from ultraworkers/lane/gjc-s21
feat(tools): GJC-compatible read selectors (#3323)
2026-10-13 08:52:18 +09:00
clawdbot 44ff1249fa style: apply rustfmt formatting 2026-10-12 23:49:29 +00:00
clawdbot f4c375641a feat: wire GJC-style line range selector into read_file tool
- Add selector field to ReadFileInput accepting GJC syntax (:50, :50-200, :50+150, :raw)
- Implement read_file_with_selector and read_file_in_workspace_with_selector
- Add apply_line_selector to parse and apply selectors to file contents
- Add truncation_footer field to ReadFileOutput for GJC-compatible output
- Add comprehensive tests for selector parsing and application through tool entry point

Selectors take precedence over offset/limit for backward compatibility.
Closes #3323
2026-10-12 23:48:29 +00:00
Bellman 20578124c4 Merge pull request #3330 from ultraworkers/lane/gjc-s11
feat(sdk): implement GJC SDK wire transport with WebSocket support
2026-10-13 08:46:25 +09:00
clawdbot 46eba21620 cleanup: remove phase 1a documentation (owned by PR #3329) 2026-10-12 23:44:44 +00:00
clawdbot 3b1f7220c5 feat(sdk): implement GJC SDK wire transport with WebSocket support
- Add error types and error code enumerations
- Implement frame types for SDK wire protocol (FrameType enum)
- Create Frame struct with JSON serialization/deserialization
- Implement SdkTransport with async WebSocket connection handling
- Support request/response correlation via request IDs
- Handle frame parsing and routing with background task
- Add comprehensive unit tests for frame creation and correlation
- Implement proper async handling with tokio and futures

Implements protocol version 3 with:
- Token-based authentication via query parameter
- Frame size limits (4 MiB max payload)
- Request timeout handling
- Proper error propagation and correlation
2026-10-12 23:43:10 +00:00
clawdbot e8c14616c6 docs: add final lane report for cc-gjc-s21 phase 1a completion 2026-10-12 23:43:04 +00:00
clawdbot 16541e987b docs: add Phase 1a completion report for line range selector 2026-10-12 23:42:30 +00:00
clawdbot baa31c7515 docs: add Slice 2.1 implementation tracking log 2026-10-12 23:41:37 +00:00
clawdbot 6b8bdb2ce7 feat: add GJC-compatible line range selector parser for read tool
- Support line range selectors: :50, :50-200, :50+150, :5-16,960-973
- Support special modes: :raw, :conflicts
- Parse comma-separated ranges with automatic merging of overlapping/adjacent ranges
- Comprehensive unit tests covering all selector formats
- Fixes #3323 Slice 2.1 - Phase 1a (read tool selector parsing)
2026-10-12 23:41:13 +00:00
clawdbot 1e55fb11bc docs: add GJC tool semantics analysis for Batch 1 (read/edit/search/find) 2026-10-12 23:39:56 +00:00
clawdbot 7e6981fbe7 docs: add GJC compatibility gap matrix 2026-10-12 23:38:42 +00:00
Bellman 0d052f831b Merge pull request #3313 from ultraworkers/lane/cc-issue-3247
build: add Nix flake dev shell and claw package
2026-10-13 08:37:46 +09:00
clawdbot 3859627013 build: add Nix flake dev shell and claw package (#3247) 2026-10-12 23:35:06 +00:00
Bellman 620ac1dbad Merge pull request #3327 from ultraworkers/lane/revert-3318
revert: #3318 (merged with red CI; main test build broken)
2026-10-13 08:18:42 +09:00
clawdbot 06c743f15f fix(test): update AnthropicRuntimeClient::new calls to match 9-arg signature
The test code was calling with 8 arguments; updated to include the
terminal_tail parameter in the correct position.
2026-10-12 23:16:26 +00:00
clawdbot f021597942 Revert "Merge pull request #3318 from ultraworkers/pr-3292"
This reverts commit 2d920ce915, reversing
changes made to 80bfe94b77.
2026-10-12 23:09:13 +00:00
Bellman 2d920ce915 Merge pull request #3318 from ultraworkers/pr-3292
fix: harden the agent loop against a self-hosted Anthropic-compatible backend
2026-10-13 08:07:04 +09:00
Bellman 80bfe94b77 Merge pull request #3299 from D4B34R987/main
fix: wire persisted provider configuration into runtime
2026-10-13 08:06:13 +09:00
dependabot[bot] 5303de65ad build(deps): bump quinn-proto
Bumps the cargo group with 1 update in the /rust directory: [quinn-proto](https://github.com/quinn-rs/quinn).


Updates `quinn-proto` from 0.11.14 to 0.11.19
- [Release notes](https://github.com/quinn-rs/quinn/releases)
- [Commits](https://github.com/quinn-rs/quinn/compare/quinn-proto-0.11.14...quinn-proto-0.11.19)

---
updated-dependencies:
- dependency-name: quinn-proto
  dependency-version: 0.11.19
  dependency-type: indirect
  dependency-group: cargo
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-10-12 23:03:59 +00:00
DaBear987 50ff600466 fix: wire persisted provider configuration into runtime 2026-10-12 23:03:56 +00:00
Bellman 4c3b10f087 Merge pull request #3317 from ultraworkers/lane/cc-issue-3283
docs: add OmniRoute documentation for OpenAI-compatible setup
2026-10-13 08:02:26 +09:00
reacechalmers 6a797de094 fix(runtime): keep a failed turn's tool results, and size guards from the real context window
Five defects found by auditing the agent stack, all of which let the harness act
confidently on a wrong picture of its own state.

1. A turn that errored discarded the record of tools that had already run. The CLI
   hands run_turn a CLONE of the session; the turn appends the user message, each
   assistant reply, and a tool_result per executed tool to that clone alone. The error
   path dropped it while the side effects of those tools remained on disk, and the
   recovery path then replayed the same user input against a session showing no sign
   they had ever run — inviting a second execution of tools that are not idempotent.
   The clone is now adopted before teardown, and recovery resumes the turn instead of
   replaying it (new ConversationRuntime::resume_turn).

2. Every sizing guard was computed against the model NAME's context window. With a
   local server behind a cloud model alias that is 200k against a much smaller
   backend, which does not make the preflights inaccurate — it makes them unreachable.
   CLAW_CONTEXT_WINDOW now declares the real window; model_token_limit honours it and
   returns a limit for unrecognised models instead of None, max_tokens is capped to a
   quarter of it, and the auto-compaction threshold derives from it (70%) rather than
   defaulting to a 200k-window constant.

3. Whether to recover by discarding conversation history was decided by substring
   matching on rendered error prose, including strings that ordinary transport and
   decode failures also produce — so a dropped connection cost the session its
   transcript. Classification now happens in the API layer, where the error still has
   its type and the request is still in hand: typed overflow errors, plus ambiguous
   transport failures only when the request actually filled the window. The two
   copy-pasted detection blocks are gone.

4. The top-level agent loop had no iteration ceiling (usize::MAX) while spawned
   subagents were bounded at 32. It now gets the same bound, overridable with
   CLAW_MAX_TURN_ITERATIONS.

5. Auto-continue nudges were injected as USER turns, so harness-authored text was
   persisted, re-sent on every later request, and folded into compaction summaries as
   something the user had said. They are recorded as system turns now; the wire
   payload is unchanged.

Also replaces the repository's own self-host section, which described a translator
and a serving stack no longer in the request path. That file is folded into the
system prompt verbatim, so it was briefing the model with an architecture that does
not exist.

(cherry picked from commit c8834cb4d1a3372eb70241429f641162a420297e)
2026-10-12 23:00:22 +00:00
Bellman e97ddc767f Merge pull request #3239 from AbbyJL/workspace-slash-command
fix(commands): wire workspace slash command
2026-10-13 07:59:47 +09:00
clawdbot 687c6b03a4 docs: add OmniRoute documentation for OpenAI-compatible setup (fixes #3283) 2026-10-12 22:59:18 +00:00
reacechalmers 7c38d78759 fix(runtime): auto-continue a turn the model stalled without a tool call
A reply carrying no tool_use ended the turn unconditionally. Small local
models routinely narrate the next tool call in prose — "Now running the
EEC-V probe:" — and then emit EOS, so the announced work never happened
and the user was dropped back at the prompt. The same failure wears a
second hat mid-task: stopping to ask for a go-ahead on work already
requested.

The loop now inspects the final line of a tool-less reply. If it
announces a pending action, or asks a question after tools have already
run this turn, a short nudge is pushed as a user message and the loop
continues instead of breaking. The budget is bounded (default 3 per
turn, CLAW_MAX_STALL_NUDGES, 0 disables) so a model that really is
finished still gets to stop.

Prevention pairs with recovery: the "Doing tasks" system-prompt section
now tells the model to act in the turn it plans in.

Tests cover the auto-continue, the bound, a plain completion that must
NOT be nudged, the disable switch, and last-line-only detection. The
behavioural pair was mutation-checked: reverting the continue to a break
fails both.

(cherry picked from commit 9035fe6f4bf4a8d4cb70d7a3baf1f3748c757498)
2026-10-12 22:58:32 +00:00
reacechalmers cb572811e1 fix(runtime): bound the git diff snapshot to a configurable budget
The git diff snapshot is rendered into the *system prompt*, so its cost is
paid again by every session whose working tree has changed -- and a diff
changes on essentially every edit. The 50_000-char budget was four times the
entire instruction-file budget (12_000) and unbounded in practical effect: a
single modified file added ~15_000 tokens to the prompt.

Measured against a self-hosted backend, one uncommitted change took the
prompt from 8_302 to 23_457 tokens and a one-word reply from ~3 s to 296 s.
With the new default the same session is 7_911 tokens and 36 s. That cost is
absorbable against a hosted frontier model and ruinous against a local one.

Default is now 4_000 chars, matching MAX_INSTRUCTION_FILE_CHARS, and is
overridable with CLAW_MAX_GIT_DIFF_CHARS following the same pattern as
CLAW_MAX_READ_CHARS. A budget of 0 omits the snapshot entirely rather than
emitting an empty section with a truncation marker; git status and the
recent-commit list are unaffected either way.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RNKqVB2JE9UnCkz8FC9HPy
(cherry picked from commit e01c6ea43d56a97e284ab80fa7a90a479997ed74)
2026-10-12 22:58:32 +00:00
reacechalmers fa0d53a969 fix(runtime): bound tool-result payloads to the actual context window
Two related gaps let a single agentic turn exceed a small context window,
with no recovery path once it happened.

1. read_file's character ceiling was a hardcoded 64 KB (~16k tokens), sized
   for a 200k window. Two reads in one turn reach ~48k tokens, which a 32k
   backend rejects outright. Make it configurable via CLAW_MAX_READ_CHARS,
   defaulting to the existing 64 KB so behavior is unchanged unless set.
   Reads remain paged, not refused: callers still get truncated/nextOffset.

2. Compaction could not help, because it works at message granularity and an
   agentic turn is five messages (user / assistant+tool_use / tool_result /
   assistant+tool_use / tool_result). With preserve_recent_messages: 4 the
   only droppable message is the short user prompt, so the two oversized
   tool results were preserved verbatim by design.

   Trim preserved tool-result payloads over 16 KB to a head/tail excerpt with
   an explicit "[... N characters elided ...]" marker, so the model knows its
   view is partial and can re-read. Small payloads pass through untouched.

   Also apply trimming on the early-return path: a session can exceed the
   window while holding fewer messages than preserve_recent_messages, where
   should_compact is false and message-granular compaction has no move to
   make. compact_now no longer gates on removed_message_count alone, which
   would have computed the smaller session and then discarded it.

Also fixes a pre-existing flaky test: repl_executes_python_code resolves the
python runtime through PATH, which the PowerShell tests blank while they run.
It now takes the same env_lock, so it no longer intermittently fails with
"python runtime not found" on a machine that has python.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013bVvRYn7CQKR6hT2j1L6Cs
(cherry picked from commit b0d4d41e8132f8dc75717b1f74554d649639de10)
2026-10-12 22:58:32 +00:00
reacechalmers 910ac4e326 fix(runtime): also check for compaction before a request is sent
The usage-based compaction gate is post-hoc: it can only see the context
after a call returns and reports its counters. Between that measurement and
the next request the session can grow enormously - a reasoning model emitting
a long thinking block, then a tool result carrying a large file.

Observed live: a call measured at 17,858 prompt tokens generated 10,842 more,
a read_file added ~19k on top, and the next request reached the provider at
47,755 tokens and was rejected outright. No tuning of the post-hoc threshold
prevents that, because nothing measures the session in between.

Add a pre-send check driven by estimate_session_tokens, a local heuristic over
the transcript, so it costs no API call and works before any usage has been
recorded at all.

Note the limit of this: compaction preserves the 4 most recent messages, and
in an agentic tool-use loop those messages ARE the tool results carrying the
bulk. This check bounds gradual growth; it cannot rescue a session whose last
four messages already exceed the window.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013bVvRYn7CQKR6hT2j1L6Cs
(cherry picked from commit 26ab52d70fb17e49b74d523aecfc1da7f69a6d60)
2026-10-12 22:58:19 +00:00
reacechalmers a25717aded fix(runtime): auto-compaction must measure the live context, not a running total
`maybe_auto_compact` gated on `usage_tracker.cumulative_usage().input_tokens`,
which is wrong in two independent ways:

1. It ignores cached input. With prompt caching a warm call reports
   `input_tokens: 2` and puts the real context in `cache_read_input_tokens`,
   so the counter barely moved and auto-compaction never fired at all,
   however large the conversation grew. Backends without prompt caching
   report the whole prompt as `input_tokens`, so the bug was invisible there.

2. A sum across calls is not a context measurement, and it never decreases.
   Once it did cross the threshold, every subsequent iteration compacted
   again, permanently, even after compaction had shrunk the context.

Gate on the latest call's total input instead — input + cache_creation +
cache_read — which is the live context window size. It rises with the real
context and falls back below the threshold once compaction has done its job,
so no counter reset is needed.

`CLAUDE_CODE_AUTO_COMPACT_INPUT_TOKENS` now means what it reads like:
compact when the live context reaches N tokens. The default of 100_000 is
unchanged and still lands at 50% of a 200k window.

Adds `TokenUsage::total_input_tokens()` (saturating) and four tests: the
cached-usage shape that never fired, non-latching across three turns, and
two unit tests for the new helper.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013bVvRYn7CQKR6hT2j1L6Cs
(cherry picked from commit aebba4e77eddf585fc5b21123967dbc4ebb6e6a1)
2026-10-12 22:58:19 +00:00