Skip to content

Harness API: per-session context ceiling (set_context_cap) so concurrent sessions can hold different compaction budgets #1971

Closed

Description

@guyb1

Context. We embed jcode headless through the harness API (@1jehuang/jcode-sdk), one connection and one session per conversation, with several conversations running at the same time in one daemon. We set [compaction] max_context_tokens = 200000 (the compaction half of #723) so a 1M-window model folds at about 160k, and short messages stay fast and cheap.

The cap is one value for the whole daemon, but the right value differs per conversation. A short question-and-answer thread wants the small cap. A long multi-step task in another conversation (a large refactor, research across many files, a big data job) is slowed down by it: it compacts again and again, loses detail it then has to re-read, and takes longer overall. We would like each session to have its own ceiling, and to change it as the work changes.

Citations are against master @ b09a8a5.

Today

  • The cap is read from the global config. CompactionManager::refresh_context_cap copies crate::config::config().compaction.max_context_tokens (crates/jcode-base/src/compaction.rs:255-259), and it runs before every request through ensure_context_fits (:959) and on every set_budget (:251).
  • Each session already has its own CompactionManager (the Registry clone gives a fresh one, crates/jcode-app-core/src/tool/mod.rs:339-341). So the compaction state is per session, but the ceiling is not.
  • Rewriting config.toml changes the cap for every live session in the daemon at its next request. Two concurrent sessions cannot hold different values.
  • The harness API already has per-session set_reasoning_effort (crates/jcode-harness-api/src/requests.rs:269), set_model, and compact (:275). There is no per-session control over the context ceiling. (The native wire has set_compaction_mode, which the harness API does not expose either.)

Ask 1: a per-session context ceiling

A harness API request such as:

{ "req": "set_context_cap", "session_id": "...", "max_context_tokens": 600000 }

Semantics we would expect:

  • It applies to that session only. 0 or null clears it, and the session falls back to the global [compaction] max_context_tokens.
  • Effective budget = min(model window, session override if set, else the global cap). Never above the model's window.
  • It is persisted with the session, so resume_session after a daemon restart keeps it.
  • Lowering it below current usage behaves like the existing thresholds (background compaction at 80%, hard compact at 95%). The embedder can call compact to fold right away.
  • The reply carries the effective budget, for example { "ev": "context_cap_changed", "session_id": "...", "max_context_tokens": 600000, "effective_budget": 600000 }.

Ask 2 (nice to have): report the effective budget

Expose the session's effective budget where an embedder can read it, for example on model_info or token_usage. Then the embedder can show "x of y tokens" and decide when to raise or lower the ceiling. This is related to the context-meter point still open in #723.

Why

Our users run one agent for both quick questions and long tasks. With a single ceiling we have to pick a trade-off for everyone. A small one makes big tasks slow and lossy. A large one makes every short message re-send a huge transcript. Per session, the agent (or the embedder) can raise the ceiling for a big task and lower it afterwards, without affecting the other conversations running in the same daemon.

Happy to test a pre-release build against our integration.

Activity

  1. ianalitis commented on Oct 26, 2026

    @ianalitis
    Contributor

    Thanks, the citations match master: CompactionManager::refresh_context_cap copies the global [compaction] max_context_tokens before every request, each session already has its own CompactionManager, and the harness API has set_reasoning_effort / set_model / compact but no per-session ceiling.

    The contract you describe is implementable as specified (session override, 0/null clears, min(model window, override or global), persisted across resume_session, reply carries the effective budget). Two things are maintainer decisions rather than implementation detail, so it is bucketed needs-decision:

    1. Adding a request to the public harness API and SDKs is an API surface commitment; it should be approved before someone codes it.
    2. Persisting the override with the session changes what resume_session restores, so it needs agreement on whether it is stored in session metadata or re-applied by the embedder after attach (the same question feat: per-session environment for tool-spawned processes (finish #1967) #1969 is asking about per-session env).

    Ask 2 falls out of the same change if the effective budget is computed once and exposed on token_usage.


    — Jcode agent (automated triage), on behalf of @1jehuang

  2. 1jehuang commented on Oct 30, 2026

    @1jehuang
    Owner

    Implemented and pushed to master in 2054a04 and cf05b03 (persisted per-session context caps, daemon controls, and effective-budget reporting), 6b8e8ef (harness API and SDK support), 3403aee (public-transport regression tests), and a30ea25 (documentation). Closing as completed.

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions