Skip to content

Add Codex-owned in-process Token Miser - #16

Open
Harold Hunt (huntharo) wants to merge 2 commits into
pwragentfrom
feat/in-process-token-miser
Open

Add Codex-owned in-process Token Miser#16
Harold Hunt (huntharo) wants to merge 2 commits into
pwragentfrom
feat/in-process-token-miser

Conversation

@huntharo

Copy link
Copy Markdown

Summary

  • add an explicitly enabled, Codex-owned Token Miser mode under [features.code_mode.token_miser]
  • retain exact terminal Code Mode output as model-invisible rollout records before reduction
  • run a bounded Luna one-shot child through the ordinary core session path used by codex exec
  • expose thread-scoped, bounded tools.read_token_miser_output and tools.search_token_miser_output
  • aggregate Luna usage into the root session and the standard turn.completed.usage JSON event
  • preserve existing external/managed reducer behavior when this mode is disabled

Architecture and threat model

Activation is explicit:

[features.code_mode]
enabled = true

[features.code_mode.token_miser]
enabled = true
model = "gpt-5.6-luna"

The raw terminal result is admitted once as an opaque token_miser_output rollout item and flushed before Luna or a receipt runs. It retains the structured FunctionCallOutputContentItem values exactly. The in-memory rollout representation uses Arc<TokenMiserOutput>, and the wire serializer borrows that payload, avoiding a second deep clone for admission or serialization.

Admission is capped at 1,024 content items and 8 MiB across variable metadata and content sources. The 8 MiB cap is codex_code_mode::host::MAX_FRAME_BYTES / 8, derived from the host's authoritative 64 MiB serialized IPC-frame limit; the factor of eight covers worst-case JSON escaping before disk persistence. Injected/future providers receive the same lower bound. Outputs beyond it are withheld without autonomous chunking or unbounded allocation by Token Miser.

Raw records and decision records are excluded from model context, app-server thread history, forked history, rollout search/metadata projections, and ordinary exec JSON events. Legacy rollouts remain readable. Resume restores the opaque catalog directly from the owning thread store; a raw record lacking a committed decision is retained but never reduced a second time.

Object IDs are UUIDv4 values. Lookup also requires the current session's internal ThreadId; authorization is not carried by the model-visible ID alone. Reads are capped at 8 KiB with next_offset; searches cap the query at 256 bytes, results at 20, snippets at 512 bytes, and the complete serialized result at 8 KiB. Failed lookup/search does not authorize terminal passthrough.

Direct terminal results are persisted and reduced immediately. Yielded cells expose only bounded actionable running state; the exact terminal result is reduced once when wait completes. Terminal resolution runs in a detached Tokio task so parent cancellation cannot discard a result or its usage. Per-cell persistence and decision OnceCell values make terminal delivery, replay, acceptance, and accounting idempotent.

Luna runs through run_codex_thread_one_shot, which is available in the core session used by one-shot codex exec; it does not require app-server or ACP. The child omits parent user/developer instructions and history, uses custom reducer instructions, disables Code Mode, shell, apps, plugins, collaboration, MCP, web search, image access, planning, and user-input tools, and sends an empty tools array.

The entire raw-bearing ContextualUserFragment, including markers, is capped at 896 UTF-8 bytes. Deterministic head/tail selection omits the middle and never starts an autonomous summarization loop. Because a byte contributes at most one tokenizer token, the complete framed item stays below the repository's 1K-token manual-review threshold and far below the 10K hard limit, including token-dense Unicode and control bytes.

Two explicit model-visible surfaces can exceed 1K tokens and therefore need P0/manual context review: a requested retrieval result is at most 8 KiB plus fixed framing, and a Luna replacement is at most 8 KiB plus fixed fences. Both remain below the 10K-token hard cap; ordinary receipts are fixed, short, and actionable.

Luna may select exact passthrough, a bounded replacement, or hide. Reducer startup, timeout, malformed output, and persistence failures do not fail the outer tool/turn; execution continues while raw content remains withheld behind a neutral receipt. This is operationally fail-open and content fail-closed.

Usage and Harbor

The Luna child's complete provider TokenUsage is stored on the decision and added exactly once to the root absolute token total. Input, cached input, cache-write input, output, reasoning output, total tokens, and rollout budget units are preserved where represented. The decision and resulting absolute TokenCount are persisted as one ordered batch before the standard root event is delivered.

codex exec --json now includes total_tokens alongside the existing detailed fields on its standard root turn.completed.usage event. The core integration tests prove Luna usage is included once in the root total; the subprocess test proves the Harbor-shaped CLI stdout and persisted rollout carry that authoritative total. Child-only events are not required.

Harbor's external rollout-to-ATIF adapter is outside this repository. It must map the standard root turn.completed.usage event fields:

  • total_tokens
  • input_tokens
  • cached_input_tokens
  • cache_write_input_tokens
  • output_tokens
  • reasoning_output_tokens

No synthetic "tokens saved" telemetry is added.

Compatibility

  • disabled/unconfigured mode preserves the prior reducer and Code Mode behavior
  • the in-process mode deterministically suppresses both configured external reduction and managed PwrAgent Token Miser, including runtime config refresh
  • stored internal records are not projected into legacy conversational history or forks
  • all persistence and retrieval logic is platform-neutral for Linux, macOS, and Windows

Test evidence

Focused Token Miser suite:

  • just test -p codex-core token_miser: 14 passed, 3961 skipped
    • immediate_replacement_takes_precedence_over_managed_reducer_without_reasoning_leakage — direct terminal replacement, no narration/reasoning/raw leak, resume retrieval, exact root usage
    • direct_small_large_passthrough_and_failure_outputs_remain_exact — small/large output, passthrough, replacement/receipt, malformed reducer result, exact persistence
    • yielded_exec_is_reduced_once_only_after_terminal_wait — yielded actionable state and terminal wait
    • parallel_terminals_have_unique_objects_and_exact_once_usage — parallel calls, unique objects, exact-once usage
    • cancellation_does_not_drop_or_repeat_reducer_accounting — parent cancellation and durable completion/accounting
    • reducer_input_is_hard_bounded_and_omits_the_middle_of_large_text — 896-byte complete frame, dense Unicode/control data, deterministic head/tail
    • retrieval_is_exact_bounded_and_thread_scoped — scoped read/search, continuations, adversarial escaped snippets
    • restored_decision_is_reused_without_an_uncommitted_cell — resume/replay idempotence
    • restored_raw_output_without_a_decision_is_not_reduced_again — crash-window fail-closed replay
    • structured_output_storage_bound_rejects_adversarial_oversize_without_serializing — item/source bounds and 64 MiB upstream relationship
    • fork_filters_token_miser_internal_objects_in_every_history_mode — fork denial
    • in_process_token_miser_precedence_tracks_runtime_config_refreshes — managed/external precedence
    • load_config_resolves_explicit_nested_token_miser_settings — activation syntax/default/clamping
    • token_miser_requires_explicit_enabled_true — disabled compatibility
  • just test -p codex-history token_miser: 1 passed, 14 skipped
    • token_miser_output_round_trips_exact_structured_content_without_clone_amplification — exact text/image/encrypted semantics and Arc identity
  • just test -p codex-app-server-protocol token_miser: 1 passed, 297 skipped
    • token_miser_records_are_not_admitted_to_thread_history — rollout-to-history exclusion
  • just test -p codex-thread-store token_miser: 1 passed, 235 skipped
    • exact_token_miser_output_is_retained_but_excluded_from_model_context — persistence plus model-context exclusion
  • just test -p codex-exec harbor_shaped: 2 passed, 150 skipped
    • harbor_shaped_root_turn_completed_usage_includes_all_token_miser_cost_once — exact root JSON usage shape
    • harbor_shaped_exec_json_and_rollout_preserve_authoritative_usage — real subprocess stdout event set and persisted rollout total; internal rollout records are not stdout conversational items

Full changed-package suites:

  • codex-features: 40/40
  • codex-history: 15/15
  • codex-rollout: 126/126
  • codex-state: 188/188
  • codex-app-server-protocol: 297/297, 1 skipped
  • codex-exec: 152/152, 1 slow
  • codex-external-agent-migration: 168/168
  • codex-memories-write: 43/43
  • codex-thread-store: 236/236

Generation and hygiene:

  • just write-config-schema: pass
  • just write-app-server-schema: pass
  • scoped just fix -p ... for all changed crates: pass
  • just fmt: pass

The full codex-core package suite was attempted once and was not usable as a green signal in this checkout: 3950 run; 3724 passed (2 slow, 17 flaky, 1 leaky), 222 failed, 4 timed out, 24 skipped. Failures were dominated by unavailable shared test infrastructure (target/debug/test_stdio_server), wiremock panics, realtime handshake timeouts, Code Mode host startup, and MCP lifecycle deadlines. The focused Token Miser suite subsequently passed 14/14. It was not rerun, and the workspace-wide suite was not run, per operator instruction.

The standalone Code Mode host build is also blocked on this Apple Silicon checkout because the published v8 150.4.0 build script receives HTTP 404 for librusty_v8_ptrcomp_sandbox_release_aarch64-apple-darwin.a.gz. Core integration coverage uses the injectable session provider and exercises the same parent/reducer lifecycle without app-server or ACP.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant