Skip to content

Sessions

A session is the durable record of your work, stored on your machine. Polytoken writes every event in order as it happens (your prompts, the model’s replies, tool calls, and markers for compactions and clears) and replays those events to reconstitute the conversation. The session holds them all, however few the model is working from on a given turn.

Use the offline exporter to make a standalone HTML transcript of a retained session:

Terminal window
polytoken export-session <session-id> --output-file ./transcript.html

The command reads the local session snapshot without loading configuration, starting a daemon, contacting a provider, or changing the session. By default, it reads sessions from $XDG_DATA_HOME/polytoken/sessions (default: ~/.local/share/polytoken/sessions); use --sessions-dir <dir> to select another retained-sessions root. The output parent directory must already exist. The command refuses an existing output file; pass --force to replace it atomically. Polytoken also refuses a destination that is a directory, a symlink, a path inside the selected session directory, or an alias of a source session file, even with --force.

The transcript keeps user and assistant prose in recorded order and includes full tool inputs and outputs. Recognized saved references display in their @ form; malformed or unknown reference tags remain literal text. Ordinary tool entries start folded for navigation; question and plan-handoff entries start open, with the recorded questions and the saved plan visible. Folding is presentation only, not redaction: included tool content may contain sensitive values. The transcript omits reasoning/thinking, notifications, injected reminders, audit records, and subagent lifecycle transcripts. Compaction, clear, facet, and model markers appear in place, and every earlier entry stays in the transcript.

Each export is a self-contained HTML file with inline styles and native expandable sections. It uses no JavaScript and loads no remote resources. Remote Markdown images appear as their alt text with a safe source link.

When the recorded active plan identifies a root plan.md or plan-*.md inside the selected session directory, the exporter includes the whole file at export time inside its handoff entry and labels it Saved plan file at export time. It never guesses a newer plan or reads outside the selected session directory. When a plan is missing, unsafe, or unreadable, the exporter shows an unavailable note and keeps the recorded handoff content.

Compacting, clearing, and rewinding all work against this record. They change what the model is working from, or move you to an earlier event. None of them touches the events already on disk.

The context is what the model works from on a given turn: the events Polytoken draws from the session and sends to the model. A model can take in only so much at once, so the context is bounded while the session keeps growing. Compacting and clearing both act on the context, not the session: they change what the model works from without touching the events on disk.

The status line shows the percentage of context left, such as 62% context left. Polytoken calculates the percentage from the effective request limit for the current model. The effective request limit can be lower than the model’s advertised context window.

A ~ prefix means Polytoken is showing a local estimate because no fresh provider-reported figure is available yet, such as immediately after switching models. The next provider-reported figure replaces the estimate. While a turn runs, the gauge keeps its last value. After /clear, the gauge briefly reads context: n/a until Polytoken refreshes the value.

When the gauge shows little room, see Compacting for how Polytoken frees context while preserving the session record.

When a provider temporarily rejects a request, Polytoken waits and tries again rather than failing the turn. During the wait, the turn status line shows the reason, a countdown to the next retry, and which retry is next. If you cancel the turn while Polytoken is waiting, the status line states how far into the wait the cancel arrived. Waits of a minute or longer read in minutes and hours (for example 1m 30s or 2h 1m) instead of a large second count. The wait line survives detaching and reattaching: if you reconnect while a retry wait is still running, the status line picks the countdown back up from the current remaining time rather than restarting the full delay.

Each request is also checked against the model’s context limit before Polytoken sends it. A request that would not fit triggers one automatic compaction to make room, and a request that still would not fit is never sent: the turn ends with an error saying the conversation no longer fits the model, so you can compact or clear deliberately instead of watching the same rejection repeat.

When automatic compaction fails repeatedly, Polytoken shows an always-visible remediation card. The card names /compact, rewind, and /clear so you can choose a recovery action before sending another request.

A model that streams only reasoning, with no visible reply, is nudged to answer. When the pattern persists, Polytoken discards the unproductive attempts and retries once from the last completed exchange of messages and tool calls. A turn that still cannot produce a reply ends with a visible note saying the model could not be moved to answer. Reasoning produced without a reply no longer takes up room in the context.

Repeated tool calls have a bound as well. When the model calls the same tool with the same arguments again and again, whether those calls succeed or fail, a reminder card tells the model to change course. If the model continues, the turn ends with an error that names the call.

A model group is an ordered list of model selectors that Polytoken can reuse as one routing target. Under modelgroups, a group’s value is a single selector, a group reference, or an ordered list of either. Nested groups flatten in authored order, and duplicate positions remain distinct. Configure groups under modelgroups; see the configuration reference.

Model groups reverse the usual configuration precedence: user/global definitions replace project definitions, and project definitions replace shipped ones. Each winning group value replaces the complete lower-priority value. A group member cannot be null. Omit the group’s key under modelgroups to remove that layer’s override and restore the lower-priority definition. Polytoken validates every layer on its own, so an invalid definition still fails the load even when a higher-priority layer overrides that group’s name.

When a provider attempt fails, Polytoken first finishes the retries configured for that model, then moves to the next position in the group. A successful response makes the selected candidate sticky for later turns. When you or the model explicitly selects a group again, the route restarts at the first position.

When you reference a model group in a prompt or on the command line, the reference needs one of four prefixes: @modelgroup:name, @mg:name, modelgroup:name, or mg:name. A bare group name never resolves as a group.

Select a model group from the prompt with a reference:

Summarize the findings from both audits. @mg:polytoken:researcher

Select one for a non-interactive polytoken exec run with the --model flag:

Terminal window
polytoken exec --model mg:polytoken:plan "review this handoff plan"

During a reload, Polytoken keeps the route on the current position when the reloaded group still contains that selector. When the selector appears more than once, the route stays on the occurrence the session was already using rather than jumping to the first match. A selector dropped from the reloaded group keeps serving as the current model rather than silently switching to another group member. The session rejoins a group the next time you or the model explicitly selects one.

OpenAI Responses and Codex models reuse a cached copy of a conversation’s prompt prefix when a later request repeats it. While a conversation waits on long background work, the cached copy can expire, so the next turn pays full processing for the prefix again. Cache warming sends a small extra request during those waits to keep the prefix cached.

A warm request is an inference request, not a network ping. Polytoken sends the conversation’s prompt prefix unchanged, appends a single instruction asking the model to reply only OK, and discards the reply. Warm requests run no tools and append nothing to the session, so what the model works from never changes. Each warm request draws on your API spend or subscription quota like any other request, and a completed refresh does not guarantee a cache hit on the next turn: providers route cached prefixes under load and promise nothing.

The daemon.cache_warming key in your user/global config controls when Polytoken may send warm requests. A project config cannot set the key.

  • when_waiting is the default. Polytoken refreshes a conversation only while that conversation owns at least one running or queued background job, such as a subagent, a backgrounded shell command, or a tool flow. A long foreground shell or tool call runs inside the turn rather than as a background job, so on its own it never starts a refresh.
  • always refreshes whenever a conversation sits idle between requests. This value also covers waits inside long foreground tool calls and the time after you step away from a live session.
  • never stops every warm request for every model, including models whose built-in default enables warming. Use this value to spend nothing on warming.

Polytoken bounds successful warm requests per conversation, regardless of the policy. The user/global key daemon.cache_warming_max_refreshes defaults to 3 for the main conversation. The user/global key daemon.cache_warming_max_refreshes_subagent defaults to 1 for each subagent conversation. Both values must be at least 1. A new successful ordinary provider attempt resets the conversation’s count and starts a fresh idle stretch. Failed warm requests do not count, and failed ordinary attempts do not reset the count. When new conversation activity interrupts a refresh, Polytoken excludes that incomplete refresh from the count.

When a completed warm reports zero cached tokens, Polytoken stops warming that conversation immediately. A provider that omits cache usage does not count as a miss. Set daemon.cache_warming_refresh_limit_enabled to false to disable counting and cache-miss stops while retaining the regular warming policy. All three refresh-limit keys are user/global-only. A project config cannot set or erase them.

Warming runs only for models with an interval. Set models.<name>.cache_warming_interval_secs to a number of seconds between refreshes, anywhere from 1 to 86400:

models:
openai/gpt-5.6-terra:
cache_warming_interval_secs: 1200
openai/gpt-5.6-luna:
cache_warming_interval_secs: 0

The 0 value above turns warming off for that model. Omit the key to inherit the model’s built-in default. The shipped catalog enables warming at 1560 seconds (26 minutes) for the GPT-5.6 family of sol, terra, and luna, plus GPT-6 Astra, including the Codex -1m variants of those models; every other model defaults to warming off. That 26-minute interval matches what those specific models keep, and other models differ: earlier models hold a cached prefix for less time, so pick an interval below the retention your model actually keeps.

Only OpenAI Responses and Codex models act on an interval. A model served another way keeps the setting in your config for later, and Polytoken warns that the model’s backend does not support warming.

Each refresh attempt shows a compact Cache refresh card in the conversation, naming the model and, when a subagent’s own refresh ran, the subagent. The card reports exactly what the provider observed: a hit shows the count of cached input tokens, a completed refresh with no hit says no cache hit was reported, a response without usage figures says cache usage is unavailable, and a failed refresh names the failure category. The card never claims a guarantee about the next request. The card belongs to the interface alone, not to the session, so it disappears when the conversation reloads, clears, or rewinds.

On the Codex backend, the endpoint rejects an output-token cap, so a warm request cannot bound its reply the way the OpenAI backend caps the reply at a few tokens. Replies stay short in practice and Polytoken cancels a stalled request, but the cost of a Codex warm request is not capped.

/compact summarizes the conversation so far. Polytoken writes a summary and appends it to the session as a compaction event. From then on the model works from the summary instead of the full transcript, which frees up room in the context.

Compacting is also what lets a session run for a long time. When the context fills past a threshold, Polytoken compacts on its own, so the conversation continues instead of stopping when the context is full. Your todos live on the session rather than the context, so they survive a compaction. The model keeps its plan even as the older conversation becomes a summary.

Polytoken checks every summary request against the model’s limit before sending it. When even the most aggressive trimming cannot make the summary request fit, Polytoken stops the compaction and names the part of the conversation that would not fit. Polytoken also logs a redacted summary of every rejected summary request: message counts and sizes, never conversation content. That summary makes a provider rejection diagnosable after the fact.

You can pass guidance with /compact to steer what the summary keeps.

Project instruction files use the conversation history rather than the provider system prompt. Before a provider request, Polytoken can persist an ordered reminder naming the applicable files from global configuration through filesystem ancestors, the project root, and the current working-directory stack. The reminder asks the model to read the files with its file tools. A chain fingerprint suppresses duplicate reminders on an unchanged resume, while /clear, compaction, and a changed chain allow the reminder to return. Tool activity can add path-only discovery reminders after a tool batch. Each encountered file receives one discovery reminder per context, and a context reset can remind the model about a file encountered earlier.

Local text compaction also carries project-instruction context forward. The compaction record retains the project-root file body, a small set of recent ledger hits when their current contents are available, and path-only entries for the remaining chain and ledger hits. A complete current file read can let Polytoken omit a non-root body because the model can read the file again. Partial, paginated, stale, and decorated reads do not qualify. The path list contains files the session encountered, not untouched sibling directories. Repeated identical bodies appear once, and the ledger update for reattached files is stored with the compaction record. Native compaction keeps its opaque provider context and uses the history reminder after a context boundary instead of this local text payload.

During a long turn, new tool results can grow the context again after an automatic compaction. Polytoken compacts again at the next safe point when the context crosses the compaction threshold, allowing the turn to continue with room for more tool calls. Polytoken limits these automatic repeats for one turn. A compaction card marks each pass in the TUI.

Use /daemon-reload (or /reload) after changing configuration, facets, skills, hooks, permissions, or project context. Polytoken applies an idle reload immediately. If a turn is running, Polytoken queues one reload for the next safe point and returns a queued response. The TUI shows a queued flash, then a completion flash and refreshes session state when the reload finishes. The event stream reports these milestones as ReloadQueued and ReloadCompleted.

A second reload request while one waits returns the reload_already_queued conflict. A reload request cannot join the queue when pending user input already waits behind the running turn, so Polytoken returns reload_blocked_queued_input. Send the reload again after the queued prompt starts or finishes.

Polytoken uses text compaction for most models. Verified built-in OpenAI and Codex configurations may use the provider’s native compaction operation instead. Native compaction keeps the provider’s opaque continuation state and does not create a readable summary card. The compaction card identifies the native method without exposing that opaque state.

Native compaction reports its own provider usage separately from the context gauge. The gauge remains unmeasured after installation until the next ordinary continuation reports usable provider input usage. Polytoken records that measurement once for the native context. Native admission treats 95% of the configured context window as usable, then best-effort trims trailing text tool output when the native request exceeds that target. Polytoken never truncates encrypted native items or structured tool content, and the provider remains the authority for the full context-window limit. If the provider rejects the native operation, Polytoken falls back to text compaction once and marks the card as a fallback.

A model switch, facet change, reload, account refresh, or resume that changes the native compatibility identity converts the native context to portable text before Polytoken sends the next request. The conversion persists with the new selection, so a restart cannot restore incompatible opaque state. Subagents never inherit the main session’s native context: each subagent owns its context from the start, and a subagent whose model is native-eligible compacts natively on its own, with the same measurement and conversion rules applied to the subagent’s own log. Forking a subagent carries a measured native context forward when the fork uses the same model, and converts it to text when the fork lands on a different model.

The live provider matrix is evidence-driven. Custom endpoints and unverified model combinations do not opt into native compaction. The current Codex probes reached the real service but produced no passing native combination: gpt-5.6-luna used the text fallback, and gpt-5.5 returned ModelNotFound. OpenAI live access still requires a configured base URL; no OpenAI result is claimed without that route.

The model can also compact on its own between the steps of a long piece of work, using the compact_context tool. It acts only when the context is at least 40% full; below that it returns guidance and keeps working, because a mostly-empty context has nothing to reclaim. The model supplies a directive with the call, saying what to do once compaction finishes, and the directive may reference a skill. Polytoken appends the directive as the continuation the model follows after the compaction event, so a long plan keeps starting each step with as much working context as possible instead of waiting for the automatic threshold.

The model can pass an optional facet argument with compact_context to switch to a different facet before Polytoken compacts the context. Using facet requires the active facet to expose switch_facet; the plan facet does not expose it, so from plan the model can use handoff_plan to move from planning to execution.

If the switch is declined, Polytoken keeps the current facet and skips compaction. If the switch succeeds but compaction later fails, Polytoken keeps the new facet and the turn continues. The switch-first order means the requested facet shapes the summary Polytoken writes and the system prompt for the requests that follow.

/clear resets the context. The model starts fresh, as if the conversation had just begun, and Polytoken records the reset as a clear event in the session. Before the reset, Polytoken cancels any running subagents so their output does not leak into the new context. Your todos are part of the session, so clearing the context leaves them untouched. Shell environment changes made during the session survive the clear. Variables that existed at startup retain their current values, and variables created during the session remain available. If a variable that existed at startup was removed, Polytoken restores its original value.

Use /reset-shell when you want the full shell environment restored to the state captured at startup. Polytoken undoes all changes to variables, PATH, aliases, functions, and shell options without clearing context, appending a clear event, or changing your conversation history.

Flagged files carry across a clear according to how they were flagged: an included file is injected into the new context, and a referenced file is named in it without its contents.

Set daemon.disable_context_clear to true in your config to prevent clearing the context. When this flag is on, /clear returns an error and the plan handoff option to implement with a new context is not shown. Compaction is not affected.

Polytoken keeps the rewind picker closed while a turn runs or while cancellation is still in flight. The picker also closes automatically if a new turn starts while the picker is open. Wait for the turn or cancellation to finish, then use Esc twice, /rewind, or a card rewind action.

/rewind moves the conversation backward through the session. Trigger it with the /rewind command, or with the rewind key on a card in the conversation, then choose a point to return to. The rewind picker lists every rewind point in conversation order: prompts, facet switches, compaction summaries, context-clear boundaries, and ask_user_question tool calls. Rewinding an ask_user_question call removes the whole assistant message that carries the question, including any preamble, the question answer, and every later event.

For a prompt, Polytoken drops the prompt and every event after it, and the prompt text returns to the input area so you can edit and resubmit. For a facet switch, Polytoken returns the conversation to the previous facet and model. A plan handoff rewinds as one boundary, so the plan returns to its editable draft state with the handoff changes removed. For a compaction or context-clear boundary, Polytoken removes that boundary card and every card after it. The rewind is destructive, and the dropped events do not come back.

Because the session is one continuous record, a rewind can move past a clear or a compaction. Rewind to an earlier point and the clear or compaction event disappears along with everything after it. The model returns to the complete conversation it had before the clear or compaction. Polytoken restores your todos to the state they were in at the point you rewound to.

You cannot rewind into the middle of a tool call. Polytoken returns you to a clean boundary: before the call or after its result.

Polytoken runs as two parts: the daemon, which holds your session and does the work, and the TUI, the interface you read and type in. Because they are separate, closing the interface does not end the work.

Detach with /detach (or Ctrl+D) and the daemon keeps running with your session. Polytoken restores the terminal before it finishes bounded feedback submissions and prompt-history saves, and may print a brief one-line status while it finishes that background work. Run polytoken again in the same project and the TUI reconnects to the session. A single Ctrl+C cancels the active assistant turn, the same as Esc. Press Ctrl+C twice to quit: the second press stops the daemon, and the session ends with it.

When Polytoken manages the daemon before the interface starts or while it cleans up a failed startup, each bootstrap or cleanup request uses a ten-second budget. If Polytoken cannot read the credential during one of those flows, Polytoken prints one line naming the session, the daemon’s process ID, and the daemon’s port, then sends the request without credentials. If the request fails, kill the daemon process by that ID, or start polytoken again in the same project and quit from the interface. The TUI’s normal quit path uses its authenticated connection and does not use this bounded cleanup request or print this credential diagnostic.

If the TUI stops because terminal input reaches EOF, terminal input returns repeated errors, or the session event channel closes, Polytoken names the cause in the exit message and shows the command to reattach the session. A lost daemon attachment lease also shows the reattach command. When Polytoken writes a crash log for the lease loss, the message includes the log path.

If the daemon stops unexpectedly while a turn is in progress (a crash, a kill, or a lost connection mid-response), you can resume the session with polytoken continue. Polytoken restores the conversation from the events on disk and brings the prompt back ready for your next message. When the selected session’s daemon log shows that the daemon exited only moments ago, Polytoken refuses the resume briefly and tells you to retry. Polytoken ignores missing, older, or uncertain log timestamps so a log problem does not block recovery. When you resume a session from outside its project directory, Polytoken anchors the resumed session at the session’s project directory. Polytoken tells the model about the difference with a system reminder that names both directories. To work elsewhere, use pushd and approve the directory request; the approval also grants read access. Resuming restores the latest facet recorded in the session history, so changing your configured default facet does not move an older session to a different facet. When Polytoken resolves the facet after a resume or rewind, it checks candidates in order: the facet recorded in history, the session seed, the configured default, and finally execute, using the first one that is still available. If the facet Polytoken drops pinned a specific model, the model recorded in history is still restored; the facet’s pin only supplies a fallback model when history has none. A facet you choose at resume time or through later switches lives in the history, and rewind restores the latest registered facet in the retained history prefix.

The current TUI log remains available as an optional, bounded attachment when you send feedback. See Crash Reporting and Feedback.

Use polytoken reap to find live local daemons that have no attached TUI. The command lists each candidate as <session-id> <title> in session-ID order. Polytoken skips daemons marked as managed by an Outpost and skips any daemon whose attachment status cannot be verified.

The sweep asks for confirmation through /dev/tty and defaults to No. Type y or yes to continue. An empty line, another answer, end of input, or a missing controlling terminal leaves every daemon running. Use polytoken reap --force when you intentionally run the sweep without a terminal.

Use polytoken reap <session-id> to target one session. The explicit form does not check TUI attachment, so it can stop an attached session. Polytoken refuses an Outpost-managed session and directs you to stop that session through the Outpost.

Polytoken requests graceful shutdown first. A sweep escalates only after the daemon confirms the attachment-safe shutdown request and then fails to exit within the bounded wait. An unconfirmed request is reported and never force-killed. The explicit form escalates after a confirmed request fails to finish within the bounded wait, or after an unconfirmed request passes the stored process-start-token and process-group safety checks. Polytoken sends SIGTERM and then SIGKILL to the daemon’s process group. Token-less or unverifiable targets remain running and produce a failure message.

A pre-upgrade Outpost-launched daemon has no outpost_managed marker, so the local sweep treats that legacy record as an ordinary local session. Outpost sessions stored in a custom Outpost data root do not appear in the default local sessions directory. Reaping removes the live registry record but preserves log.jsonl and session.json, so the session remains resumable. The daemon’s credential file is single-use: on a graceful shutdown the daemon removes that file itself, and a signal-killed daemon leaves it behind as inert data.

When you run polytoken continue without a session ID, Polytoken checks for an available update before opening the session picker. If you accept an update, the updated process skips the second check and opens the picker once. The update check also runs when Polytoken finds no resumable sessions. The picker lists sessions for your current project by default. Press Tab to toggle between your project and all sessions.

From the picker you can select a session with Enter, terminate a live session that does not have an attached TUI by pressing K, or create a new session by pressing N. Polytoken asks for confirmation before terminating the daemon process. The session history remains on disk and can be resumed later with polytoken continue.

polytoken sessions lists live sessions and removes stale registry entries. Pass polytoken sessions --all to list every session stored on disk, including historical sessions and legacy sessions that predate session metadata. When Polytoken cannot read the sessions directory, the command reports no sessions; the live-only listing reports an error instead. With --all, --format json returns summary rows without pid or port fields, unlike live-only rows. You cannot resume a legacy session.