Pure helpers for the model-facing context snapshot.
The mutable session state lives outside this namespace; these functions only advance the turn/iteration cursor, project form envelopes for persisted result messages, and compute utilization metadata.
Pure helpers for the model-facing context snapshot. The mutable session state lives outside this namespace; these functions only advance the turn/iteration cursor, project form envelopes for persisted result messages, and compute utilization metadata.
(advance-iter ctx _form-results-vec)Advance the cursor so the next iter starts at "iter" (current+1),
"next_form" 1. Prior tool outputs are carried by append-only message
history, not by mutable context state. CTX is STRING-KEYED end to end —
it is bound into Python as the context dict and nippy-persisted, and
both surfaces are strings-only.
Advance the cursor so the next iter starts at "iter" (current+1), "next_form" 1. Prior tool outputs are carried by append-only message history, not by mutable context state. CTX is STRING-KEYED end to end — it is bound into Python as the `context` dict and nippy-persisted, and both surfaces are strings-only.
(block->envelope block position cursor)(block->envelope block _position cursor head-tag-resolver)Project one loop-side block {:code :result :error :stdout} plus its
1-based position and the engine cursor into the form envelope shape
(one block = one form, 1:1):
{:scope :tag :src :duration-ms :result :error :stdout}
:src carries the block's source text. :tag is derived from the source via
classify-form-tag. :result is included only when the block has
one (engine convention: drop on default/nil). :error is included
only when the block errored. :stdout (what the block PRINTED) is the
single display surface — channels paint it, and the model reads it back.
:duration-ms is derived from the loop's block envelope so persisted TUI
replays keep the same footer timing as live progress bubbles.
Every result is walked through realize-value so any IDeref (Var,
atom) is derefed and lazy seqs land as data, never as
#:vis{:ref :expr} placeholders left over from persistence flattening
unrealized seqs.
Project one loop-side block `{:code :result :error :stdout}` plus its
1-based position and the engine cursor into the form envelope shape
(one block = one form, 1:1):
{:scope :tag :src :duration-ms :result :error :stdout}
`:src` carries the block's source text. `:tag` is derived from the source via
`classify-form-tag`. `:result` is included only when the block has
one (engine convention: drop on default/nil). `:error` is included
only when the block errored. `:stdout` (what the block PRINTED) is the
single display surface — channels paint it, and the model reads it back.
`:duration-ms` is derived from the loop's block envelope so persisted TUI
replays keep the same footer timing as live progress bubbles.
Every result is walked through `realize-value` so any `IDeref` (Var,
atom) is derefed and lazy seqs land as data, never as
`#:vis{:ref :expr}` placeholders left over from persistence flattening
unrealized seqs.(blocks->forms blocks cursor)(blocks->forms blocks {:keys [turn iter]} head-tag-resolver)Map a loop-side blocks vec into a vec of engine envelopes. :cursor
is {:turn :iter} of THIS iter; each block gets a 1-based form
position by its index in the vec.
3-arity passes head-tag-resolver (see classify-form-tag) through
to every block->envelope call so extension-declared mutation tools
(patch, git/commit!, any symbol with inline :tag on its
vis/symbol entry) classify correctly without the engine
hard-coding their symbol set.
Map a loop-side blocks vec into a vec of engine envelopes. `:cursor`
is `{:turn :iter}` of THIS iter; each block gets a 1-based form
position by its index in the vec.
3-arity passes `head-tag-resolver` (see `classify-form-tag`) through
to every `block->envelope` call so extension-declared mutation tools
(`patch`, `git/commit!`, any symbol with inline `:tag` on its
`vis/symbol` entry) classify correctly without the engine
hard-coding their symbol set.(classify-form-tag src)(classify-form-tag src head-tag-resolver)Classify a form-source string as :observation or :mutation.
1-arity: pure, engine-only. Returns :mutation when the head is a
member of core-mutation-heads; everything else is :observation.
Use this from contexts that have no access to the extension
registry (tests, pure tools).
2-arity: takes head-tag-resolver, an optional fn
(fn [^Symbol head]) -> :mutation | :observation | nil. The
resolver wins when it returns a non-nil tag; on nil the engine
falls back to core-mutation-heads. The integration layer in
loop.clj builds the resolver from extension/op-tag so every
extension-declared tool (patch, git/commit!, anything new an
extension ships) classifies correctly without the engine hard-
coding its symbol.
Classify a form-source string as `:observation` or `:mutation`. 1-arity: pure, engine-only. Returns `:mutation` when the head is a member of `core-mutation-heads`; everything else is `:observation`. Use this from contexts that have no access to the extension registry (tests, pure tools). 2-arity: takes `head-tag-resolver`, an optional fn `(fn [^Symbol head]) -> :mutation | :observation | nil`. The resolver wins when it returns a non-nil tag; on nil the engine falls back to `core-mutation-heads`. The integration layer in `loop.clj` builds the resolver from `extension/op-tag` so every extension-declared tool (`patch`, `git/commit!`, anything new an extension ships) classifies correctly without the engine hard- coding its symbol.
(compact-src src)One-line, length-capped form source for the you ran: scope index
rendered in the cross-turn <conversation-so-far> resume block.
One-line, length-capped form source for the `you ran:` scope index rendered in the cross-turn `<conversation-so-far>` resume block.
(compress-scopes scopes universe)Compress a seq/set of concrete tN/iN scopes into a compact, model-legible
token vector, RELATIVE to universe (every tN/iN currently on the wire):
"tN/*""tA-tB/*"["t*"]"tN/iM" (singleton) or "tN/iA-iC" (run).
Tokens are ordered by (turn, iter). Scopes/universe entries that don't parse
are ignored. Pure — same inputs → same output.Compress a seq/set of concrete `tN/iN` scopes into a compact, model-legible
token vector, RELATIVE to `universe` (every `tN/iN` currently on the wire):
- a turn whose EVERY universe iteration is present → `"tN/*"`
- a run of consecutive such FULL turns → `"tA-tB/*"`
- full turns covering the WHOLE universe → `["t*"]`
- a partially-present turn stays explicit, its iteration numbers themselves
run-compressed: `"tN/iM"` (singleton) or `"tN/iA-iC"` (run).
Tokens are ordered by (turn, iter). Scopes/universe entries that don't parse
are ignored. Pure — same inputs → same output.Soft per-call request-size guardrail surfaced to the model as
session_utilization.auto_compress_above. ~72% of a typical 200k window,
leaving headroom for the model's own output. Compared against PROVIDER-reported
input tokens (no local tokenizer) — see utilization.
Soft per-call request-size guardrail surfaced to the model as `session_utilization.auto_compress_above`. ~72% of a typical 200k window, leaving headroom for the model's own output. Compared against PROVIDER-reported input tokens (no local tokenizer) — see `utilization`.
(empty-ctx)(empty-ctx session-id)A minimal CTX scaffold with all model-facing keys filled by empty / default values. Useful as the starting point for scenario replays.
Includes the engine-ephemeral key "engine_warnings" so the rest of
the system can swap! it without nil-puncturing. Stripped at
persistence boundaries via strip-ephemeral.
CTX is STRING-KEYED end to end: it is bound into Python as the context
dict (strings-only boundary) and nippy-persisted (strings-only DB), so
there is no keyword->string projection anywhere between.
A minimal CTX scaffold with all model-facing keys filled by empty / default values. Useful as the starting point for scenario replays. Includes the engine-ephemeral key `"engine_warnings"` so the rest of the system can swap! it without nil-puncturing. Stripped at persistence boundaries via `strip-ephemeral`. CTX is STRING-KEYED end to end: it is bound into Python as the `context` dict (strings-only boundary) and nippy-persisted (strings-only DB), so there is no keyword->string projection anywhere between.
(enter-turn ctx turn-pos)Idempotent turn-start sync. Sets :session/turn to turn-pos,
resets :session/scope to {:turn turn-pos :iter 1 :next-form 1},
clears :engine/blockers, then runs gc-pass.
Safe to call repeatedly with the same turn-pos (no-op
semantically); safe to call when ctx was loaded fresh from DB at
turn-pos > 1.
THIS is what the integration layer (vis loop) calls at the start
of every turn. Single chokepoint for engine turn-state advance —
no advance-turn alias, no auto-incrementing variant. The integration
layer always knows the target turn-pos (DB tracks
session_turn_soul.position), so the engine takes it as an explicit
arg.
Idempotent turn-start sync. Sets `:session/turn` to `turn-pos`,
resets `:session/scope` to `{:turn turn-pos :iter 1 :next-form 1}`,
clears `:engine/blockers`, then runs `gc-pass`.
Safe to call repeatedly with the same `turn-pos` (no-op
semantically); safe to call when ctx was loaded fresh from DB at
turn-pos > 1.
THIS is what the integration layer (vis loop) calls at the start
of every turn. Single chokepoint for engine turn-state advance —
no `advance-turn` alias, no auto-incrementing variant. The integration
layer always knows the target turn-pos (DB tracks
`session_turn_soul.position`), so the engine takes it as an explicit
arg.(expand-through summaries universe)Resolve every fold SELECTOR on each summary against universe (the caller's
own live iteration scopes) into a concrete "scopes" set, so ONE intent
expands consistently wherever summaries are read (apply-summaries: the
trailer; resume / prior-turn: that turn's forms; folds-view: the ledger).
Selector keys (all optional, their results UNIONED):
"scopes" explicit ids — a tN/iN is kept verbatim; a bare tN EXPANDS
to every iteration of that turn present in universe.
"through" tN/iN cursor → every universe scope AT OR BEFORE it (start→cursor).
"from"/"to" inclusive window — either bound optional (open start / end).
"since" tN/iN cursor → every universe scope AT OR AFTER it (cursor→newest).
A range cursor may also be a bare tN — a WHOLE-TURN boundary: through/to tN
cover all of turn N (its last iteration), from/since tN its first.
The range keys are dropped after resolution and the union lands in "scopes".
EXPLICIT whole-turn intent is recorded as "turns" (a set of turn numbers):
a bare tN token, or a RANGE selector whose resolved window covers every
universe iteration of that turn. An ENUMERATED tN/iN list never yields
whole-turn intent — even when it happens to name every iteration — so a
fine-grained fold can never silently erase a turn's Q/A recap downstream
(previous-turn-context keys Q/A removal off "turns", not scope cover).
Intents with none of these keys pass through untouched. Pure — same inputs → same output.
Resolve every fold SELECTOR on each summary against `universe` (the caller's
own live iteration scopes) into a concrete `"scopes"` set, so ONE intent
expands consistently wherever summaries are read (apply-summaries: the
trailer; resume / prior-turn: that turn's forms; folds-view: the ledger).
Selector keys (all optional, their results UNIONED):
`"scopes"` explicit ids — a `tN/iN` is kept verbatim; a bare `tN` EXPANDS
to every iteration of that turn present in `universe`.
`"through"` `tN/iN` cursor → every universe scope AT OR BEFORE it (start→cursor).
`"from"`/`"to"` inclusive window — either bound optional (open start / end).
`"since"` `tN/iN` cursor → every universe scope AT OR AFTER it (cursor→newest).
A range cursor may also be a bare `tN` — a WHOLE-TURN boundary: `through`/`to tN`
cover all of turn N (its last iteration), `from`/`since tN` its first.
The range keys are dropped after resolution and the union lands in `"scopes"`.
EXPLICIT whole-turn intent is recorded as `"turns"` (a set of turn numbers):
a bare `tN` token, or a RANGE selector whose resolved window covers every
universe iteration of that turn. An ENUMERATED `tN/iN` list never yields
whole-turn intent — even when it happens to name every iteration — so a
fine-grained fold can never silently erase a turn's Q/A recap downstream
(`previous-turn-context` keys Q/A removal off `"turns"`, not scope cover).
Intents with none of these keys pass through untouched. Pure — same inputs →
same output.(finalize-turn ctx _form-scope _args)Finalize a turn: the loop ships :answer to the channel and the engine
returns ctx unchanged so the turn can settle.
Finalize a turn: the loop ships `:answer` to the channel and the engine returns ctx unchanged so the turn can settle.
(folds-view summaries universe weights util)(folds-view summaries universe weights util protected-scopes)(folds-view summaries universe weights util protected-scopes turn-weights)Model-facing LIVE BUDGET derived from the recorded session_fold intents.
The GIST of each fold lives ONCE — in its transcript breadcrumb, rendered in
place where the collapsed content was, carrying its file:line anchors (see
pretty-scopes). So this view deliberately holds NO gists; echoing them here
would duplicate the breadcrumb that is already on the wire. It emits only the
volatile budget signal:
"now" VOLATILE but TINY — shaped "context <U>% · saved <C>/<T> (<P>%, ~<toks> tok) · live <scopes>":
saved how much of the wire (<T> = every tN/iN still on the
wire, so folded scopes that already scrolled off the trailer never
inflate it) is folded away, priced BOTH in scopes (<C>/<T>) AND —
when weights are stamped — in reclaimed context (~<toks> tok,
summed from engine_iter_weights with the SAME estimator the
session_fold card and the over-budget nudge use, PLUS the Q/A
recaps of explicitly whole-turn-folded turns priced from
turn-weights / engine_turn_weights); and live the
compressed scopes STILL on the wire (accounting only; current-turn
scopes are not foldable). No gists. context is the
LIVE per-call saturation (util's saturation), so the same delta
carries how full the window is now. No gists, no position (the # tN/iN
step tag the model already sees carries that) — so re-emitting it every
iteration costs a handful of tokens.
Returns {"now" …} when a universe (the live tN/iN scopes this send) is
stamped: selectors resolved (expand-through), covered folds dropped
(supersede-summaries), budget computed against the live wire. Without one
(resume / fresh seed, before the first live send) returns {} — the
breadcrumbs still carry every gist until the next send re-stamps the universe.
The token clause is best-effort: no weights (or a scope not yet weighed)
simply omits ~<toks> tok, leaving the scope counts — never breaks the line.
protected-scopes (optional fifth arg) are live skill-body iterations; they
are excluded from the collapsed set so the ledger describes the exact wire.
turn-weights (optional sixth arg, {turn-number → ~tokens}) prices the
removed Q/A recap of every whole-turn-folded turn into the same clause.
Pure.
Model-facing LIVE BUDGET derived from the recorded `session_fold` intents.
The GIST of each fold lives ONCE — in its transcript breadcrumb, rendered in
place where the collapsed content was, carrying its file:line anchors (see
`pretty-scopes`). So this view deliberately holds NO gists; echoing them here
would duplicate the breadcrumb that is already on the wire. It emits only the
volatile budget signal:
`"now"` VOLATILE but TINY — shaped `"context <U>% · saved <C>/<T> (<P>%, ~<toks> tok) · live <scopes>"`:
`saved` how much of the wire (`<T>` = every `tN/iN` still on the
wire, so folded scopes that already scrolled off the trailer never
inflate it) is folded away, priced BOTH in scopes (`<C>/<T>`) AND —
when `weights` are stamped — in reclaimed context (`~<toks> tok`,
summed from `engine_iter_weights` with the SAME estimator the
`session_fold` card and the over-budget nudge use, PLUS the Q/A
recaps of explicitly whole-turn-folded turns priced from
`turn-weights` / `engine_turn_weights`); and `live` the
compressed scopes STILL on the wire (accounting only; current-turn
scopes are not foldable). No gists. `context` is the
LIVE per-call saturation (`util`'s `saturation`), so the same delta
carries how full the window is now. No gists, no position (the `# tN/iN`
step tag the model already sees carries that) — so re-emitting it every
iteration costs a handful of tokens.
Returns `{"now" …}` when a `universe` (the live `tN/iN` scopes this send) is
stamped: selectors resolved (`expand-through`), covered folds dropped
(`supersede-summaries`), budget computed against the live wire. Without one
(resume / fresh seed, before the first live send) returns `{}` — the
breadcrumbs still carry every gist until the next send re-stamps the universe.
The token clause is best-effort: no `weights` (or a scope not yet weighed)
simply omits `~<toks> tok`, leaving the scope counts — never breaks the line.
`protected-scopes` (optional fifth arg) are live skill-body iterations; they
are excluded from the collapsed set so the ledger describes the exact wire.
`turn-weights` (optional sixth arg, `{turn-number → ~tokens}`) prices the
removed Q/A recap of every whole-turn-folded turn into the same clause.
Pure.(form-head-name src)Return the head call NAME (a string) of src — a Python source string —
or nil when src is not a name(...) call form. Leading comments and
blank lines are skipped. Reading the head name (rather than scanning the
raw source) avoids false positives — a "patch(x)" inside a string can't
match. Used by classify-form-tag.
Return the head call NAME (a string) of `src` — a Python source string — or nil when `src` is not a `name(...)` call form. Leading comments and blank lines are skipped. Reading the head name (rather than scanning the raw source) avoids false positives — a `"patch(x)"` inside a string can't match. Used by `classify-form-tag`.
(gc-pass ctx)Passthrough. Tasks/facts/archive are gone — there is nothing to GC.
Kept so the turn-lifecycle chokepoint (enter-turn) and any external
callers stay valid.
Passthrough. Tasks/facts/archive are gone — there is nothing to GC. Kept so the turn-lifecycle chokepoint (`enter-turn`) and any external callers stay valid.
(malformed-scope? s)True if s is a string but does not parse as ::cs/scope-form.
True if `s` is a string but does not parse as `::cs/scope-form`.
EXACT set of session_* keys the model is meant to see. Security access is
included as an environment-derived value; engine bookkeeping remains hidden.
EXACT set of `session_*` keys the model is meant to see. Security access is included as an environment-derived value; engine bookkeeping remains hidden.
(model-form-envelope r)Project one executed-form envelope onto the MODEL contract the
trailer stores: :scope :src :result :error (+ engine forensic
fields), WITHOUT the channel sink slice, the host failure/metadata
chains, or :tag. The mutation/observation tag stays load-bearing
on LIVE op envelopes and on the persisted rows — but NOTHING reads
it from the trailer (the observation-prune that once did was
removed), and the model can see what a form does from :src. The
full envelopes stay on the progress chunks and the persisted
session_turn_iteration.forms rows — channels and the persisted
forms rows keep total fidelity; the trailer is what rides every
prompt.
Empty payloads are DROPPED, not rendered: "result": None /
[] / {} and empty :error maps say nothing the form's absence
of an error/result doesn't already say — the :src stays, the dead
field goes.
Project one executed-form envelope onto the MODEL contract the
trailer stores: `:scope :src :result :error` (+ engine forensic
fields), WITHOUT the channel sink slice, the host failure/metadata
chains, or `:tag`. The mutation/observation tag stays load-bearing
on LIVE op envelopes and on the persisted rows — but NOTHING reads
it from the trailer (the observation-prune that once did was
removed), and the model can see what a form does from `:src`. The
full envelopes stay on the progress chunks and the persisted
`session_turn_iteration.forms` rows — channels and the persisted
forms rows keep total fidelity; the trailer is what rides every
prompt.
Empty payloads are DROPPED, not rendered: `"result": None` /
`[]` / `{}` and empty `:error` maps say nothing the form's absence
of an error/result doesn't already say — the `:src` stays, the dead
field goes.(over-budget-hint util current-turn since-turn)Stateful compaction guidance for session_utilization.
Pressure starts at 75% of auto_compress_above, escalates at 90%, and becomes
mandatory above the operating ceiling. Once armed it remains visible while
pressure remains; an ignored warning must never silently expire. The hint names
session_fold, the safe settled boundary, and the evidence to preserve. Pure.
Stateful compaction guidance for `session_utilization`. Pressure starts at 75% of `auto_compress_above`, escalates at 90%, and becomes mandatory above the operating ceiling. Once armed it remains visible while pressure remains; an ignored warning must never silently expire. The hint names `session_fold`, the safe settled boundary, and the evidence to preserve. Pure.
(parse-scope-form s)Parse a tN/iM (or legacy tN/iM/fK) scope into {:turn :iter :form} or nil
if malformed. :form is nil for an iteration-level scope. Pure value-or-nil.
Parse a `tN/iM` (or legacy `tN/iM/fK`) scope into `{:turn :iter :form}` or nil
if malformed. `:form` is nil for an iteration-level scope. Pure value-or-nil.(pretty-scopes scopes universe)Render a seq of concrete tN/iN scopes as ONE beautiful, model-legible anchor
string using the SAME grammar the fold ledger speaks (compress-scopes +
join-scopes): same-turn iter-runs merge (t3/i3-i4,i7-i8), whole turns
collapse to tN/* / tA-tB/* / t*. universe (the live wire scopes, or
nil) drives the whole-turn collapse; nil/empty keeps every turn explicit.
nil for an empty/unparseable seq. Pure. Shared by the ledger AND the
transcript fold breadcrumb so a collapsed region reads identically wherever
it surfaces — one anchor vocabulary, not two.
Render a seq of concrete `tN/iN` scopes as ONE beautiful, model-legible anchor string using the SAME grammar the fold ledger speaks (`compress-scopes` + `join-scopes`): same-turn iter-runs merge (`t3/i3-i4,i7-i8`), whole turns collapse to `tN/*` / `tA-tB/*` / `t*`. `universe` (the live wire scopes, or nil) drives the whole-turn collapse; nil/empty keeps every turn explicit. nil for an empty/unparseable seq. Pure. Shared by the ledger AND the transcript fold breadcrumb so a collapsed region reads identically wherever it surfaces — one anchor vocabulary, not two.
(scope-compare a b)Total order on form-scope strings by (turn, iter, form). Returns int. Compares parsed segments; malformed scopes sort before all valid ones to make their presence obvious in render.
Total order on form-scope strings by (turn, iter, form). Returns int. Compares parsed segments; malformed scopes sort before all valid ones to make their presence obvious in render.
(scope-key scope)Ordered key for a scope so ranges can compare scopes: "t1/i2" or
"t1/i2/f3" → [1 2] (the form index is dropped — ranges cover WHOLE
iterations). nil when the scope can't be parsed, so callers skip it rather
than mis-order it. Lives here (below apply-summaries in loop) so BOTH the
wire (apply-summaries) and the render-time ledger (folds-view) resolve fold
selectors through the SAME function.
Ordered key for a scope so ranges can compare scopes: `"t1/i2"` or `"t1/i2/f3"` → `[1 2]` (the form index is dropped — ranges cover WHOLE iterations). nil when the scope can't be parsed, so callers skip it rather than mis-order it. Lives here (below `apply-summaries` in loop) so BOTH the wire (`apply-summaries`) and the render-time ledger (`folds-view`) resolve fold selectors through the SAME function.
(session-view ctx)(session-view ctx _warnings)THE single projection from engine-internal ctx to the model-facing
session_* view.
Both consumers derive from this, so the rendered <context> block and the
Python session dict are the same map by construction:
ctx-renderer/render-ctx serializes this viewctx-loop/session-snapshot binds this view as read-only sessionKeeps ONLY model-facing-keys (so engine bookkeeping never leaks) and
projects "engine_utilization" → "session_utilization". The second
arity takes legacy warnings for call-site compatibility but ignores them.
Pure; STRING keys in and out.
THE single projection from engine-internal ctx to the model-facing `session_*` view. Both consumers derive from this, so the rendered `<context>` block and the Python `session` dict are the same map by construction: - `ctx-renderer/render-ctx` serializes this view - `ctx-loop/session-snapshot` binds this view as read-only `session` Keeps ONLY `model-facing-keys` (so engine bookkeeping never leaks) and projects `"engine_utilization"` → `"session_utilization"`. The second arity takes legacy `warnings` for call-site compatibility but ignores them. Pure; STRING keys in and out.
(strip-ephemeral ctx)Remove every "engine_*" key from a ctx. Call before Nippy-snapshotting
to persistence so transient mutator state (warnings, pending satisfy
requests) does not leak into the durable record.
Remove every `"engine_*"` key from a ctx. Call before Nippy-snapshotting to persistence so transient mutator state (warnings, pending satisfy requests) does not leak into the durable record.
(supersede-summaries summaries)Collapse 'summary of summary': drop any summary whose scope set is fully
COVERED by another's — a proper subset, or an equal set recorded earlier — so
re-folding a region with a broader/newer gist REPLACES the finer breadcrumb
instead of stacking a second line. Coverage is never lost: every scope of a
dropped summary is present in the one that supersedes it (the superset wins;
for equal sets the later/newer wins), and the dropped summary's explicit
whole-turn intent ("turns") is MERGED into a surviving coverer so a
fold-of-fold can never resurrect an already-folded turn's Q/A recap.
Order-stable. Expects scopes already resolved (run AFTER expand-through).
Pure.
Collapse 'summary of summary': drop any summary whose scope set is fully COVERED by another's — a proper subset, or an equal set recorded earlier — so re-folding a region with a broader/newer gist REPLACES the finer breadcrumb instead of stacking a second line. Coverage is never lost: every scope of a dropped summary is present in the one that supersedes it (the superset wins; for equal sets the later/newer wins), and the dropped summary's explicit whole-turn intent (`"turns"`) is MERGED into a surviving coverer so a fold-of-fold can never resurrect an already-folded turn's Q/A recap. Order-stable. Expects scopes already resolved (run AFTER expand-through). Pure.
(turn-key scope)Turn number of a bare WHOLE-TURN scope "tN" (no /iN); nil otherwise, so
expand-through only whole-turn-expands an id that is JUST a turn — a plain
"t1/i2" stays a single iteration.
Turn number of a bare WHOLE-TURN scope `"tN"` (no `/iN`); nil otherwise, so `expand-through` only whole-turn-expands an id that is JUST a turn — a plain `"t1/i2"` stays a single iteration.
(utilization request-tokens window-tokens turn-tokens fold-cap)Pure: the "session_utilization" map the model reads to see how much
of the context window the LAST request consumed. Keys are spelled out
so they can't be misread:
last_request_tokens input size of the most recent model call
model_input_limit HARD per-call ceiling (provider rejects above)
saturation last-request / model-input-limit, as a rounded
percentage — how FULL the per-call window is
headroom_tokens tokens still free before the ceiling
(model_input_limit - last request); the
actionable 'can I keep going or must I fold?'
auto_compress_above soft guardrail threshold for request size
turn_total_tokens cumulative input this turn (billing, NOT a
per-call limit — may exceed the limit safely)
hint throttled compaction nudge, present ONLY when the
handled context has grown past auto_compress_above
(a bigger task) — the actionable partner to the passive
ceiling numbers; added by session-view from
over-budget-hint, self-silences after 3 turns
Returns nil until a request has actually been measured (req <= 0), so
the first iter of a turn shows nothing rather than a bogus 0%.
Pure: the `"session_utilization"` map the model reads to see how much
of the context window the LAST request consumed. Keys are spelled out
so they can't be misread:
last_request_tokens input size of the most recent model call
model_input_limit HARD per-call ceiling (provider rejects above)
saturation last-request / model-input-limit, as a rounded
percentage — how FULL the per-call window is
headroom_tokens tokens still free before the ceiling
(model_input_limit - last request); the
actionable 'can I keep going or must I fold?'
auto_compress_above soft guardrail threshold for request size
turn_total_tokens cumulative input this turn (billing, NOT a
per-call limit — may exceed the limit safely)
hint throttled compaction nudge, present ONLY when the
handled context has grown past `auto_compress_above`
(a bigger task) — the actionable partner to the passive
ceiling numbers; added by `session-view` from
`over-budget-hint`, self-silences after 3 turns
Returns nil until a request has actually been measured (req <= 0), so
the first iter of a turn shows nothing rather than a bogus 0%.cljdoc builds & hosts documentation for Clojure/Script libraries
| Ctrl+k | Jump to recent docs |
| ← | Move to previous article |
| → | Move to next article |
| Ctrl+/ | Jump to the search field |