Skip to content

v2026.10.16

Choose a tag to compare

@github-actions github-actions released this 16 Oct 02:02
· 213 commits to main since this release

@code-yeongyu/senpi

Breaking Changes

Added

  • Paths in messages mean one file everywhere: RPC resolve_path { path } (capability resolve_path) resolves a relative path against the session's working directory, then the project root, and answers the realpath or every candidate tried; the TUI turns such links into file:// links to the same file (#3415).

  • RPC unload_session frees a loaded session and keeps it reopenable: it refuses with a reason and an action while work runs, child sessions are open or other clients are attached (unless interrupt, children: "detach" or force), keeps queued input for the reopen, and parks it with session_parked { reason: "unloaded" }; the idle sweep's park now says reason: "idle". CLI: senpi host session unload (#3225).

  • A queued steer / follow_up sent without a clientMessageId is recorded durably under a host-minted id and restored exactly once after a host restart (#1971).

  • Two queue-or-start prompts arriving together on an idle session no longer start two turns; the second joins the first (#3224).

  • RPC steer and follow_up take whenIdle: "start" (capability steer_when_idle): on an idle session the message starts a turn (disposition: "started"), otherwise it is queued; two such inputs arriving together start one turn (#3224).

  • pi.getPermissionPreset() returns the permission preset the permission system enforces for the session right now (flag, settings, or the full-access default), or undefined when no permission system runs or its setup failed.

  • Subscription limit windows reach RPC clients: senpi reads the windows a ChatGPT subscription reports with each response (x-codex-primary-*/x-codex-secondary-*) and Claude's unified rate-limit headers, keeps the latest per provider and credential slot (never key material), and serves them as get_subscription_limits plus a subscription_limits record on change; an account nothing reported for is absent, not 0%. Thanks to jaeyoung for asking (#3370).

  • Every queued input has a queuedId (get_state.ordered, queue_update.ordered); RPC dequeue_message removes one and edit_queued_message replaces its text in place, optionally as ledger effects, with already_delivered / not_found answers that carry an action (#1971).

  • pi.appendEntry(customType, data, { startupBookkeeping: true }) marks an entry an extension writes at session start without user action. A session closed without a conversation is removed when it holds only setup entries and marked bookkeeping; any unmarked entry keeps the file (#3263).

  • /account <provider> reset <id> clears one account's cooldown or block (the credential-pool health record and any block a provider lane stored on the account), so the next request tries it again (#3285).

  • tool.glob({ pattern, path }) finds files by glob inside eval, the same way tool.grep searches text there. It is never a top-level tool, and the eval prompt now names it, so a model that reached for tool.glob no longer gets "Unknown tool glob" (#3330).

Changed

  • The terminal transcript folds a handoff block into one muted line, ▸ Handoff · Now: … · Next: …, and keeps its For you answer as ordinary text. Ctrl+O, or a click on the line, opens the block as written (#3323, proposed by @devxoul).
  • The built-in default model for anthropic-subscription and anthropic is now claude-opus-5-5 (was claude-opus-4-8). It applies only when no model is saved in settings and none is passed on the command line (#3179).
  • The built-in default model for xiaomi and the three xiaomi-token-plan-* providers is now mimo-v2.6-pro (was mimo-v2.5-pro, which models.dev marks deprecated and a regenerated catalog no longer carries). It applies only when no model is saved in settings and none is passed on the command line.

Fixed

  • Every session on a shared RPC host lists the same sign-in options as a standalone one: a session built on the host's model runtime now reads that runtime's credential store, so OAuth providers an extension registers (Anthropic Subscription, Cursor CLI) are no longer missing from get_auth_providers (#3434).

  • An RPC host shard no longer keeps itself alive through its own sessions' connections. A task tree whose children open their own children on the shard they run in left that shard running for days after its owner and every outside client were gone, holding its sessions and memory. RpcClient now names the host generation it runs inside (origin_host_instance), and a host does not count connections from its own generation toward idle exit, owner-death exit or empty-host exit. A dependent session mid-turn still keeps the host open until the turn ends (#3272).

  • Claude subscription sessions on 1M-window models (Opus 5.5 and others) no longer fail with "Prompt is too long" once they grow past 200k tokens. The request now asks Claude Code for the 1M window that compaction already planned against. If the account cannot use 1M ("Extra usage is required for long context"), the turn retries once at 200k on the same account and the lane plans compaction for 200k. 1M is offered again after 6 hours. That refusal no longer blocks the account as if it were a usage limit (#3408).

  • A turn stopped because its session was replaced (new_session, fork, switch_session) now says so: its agent_end carries abortCause: { kind: "session_replaced", reason, targetSessionFile, targetSessionId }, before session_replaced (#3357).

  • Full access never prompts: under the full-access preset a configured ask rule no longer raises a permission prompt, while a configured deny still refuses (#2465).

  • A refusal by a configured deny names the rule and the settings layer it came from, after the sentence that tells the model not to retry the call.

  • An "Allow always" answer reaches every open session at once and is stored under the agent directory, outside the project the agent can write.

  • A long image-heavy session no longer answers plain questions as if you had just sent a batch of images. When the session switches to the Claude subscription lane and replays its history, screenshots and other tool images the assistant already answered are no longer re-sent; each becomes a one-line note in its own turn ("already seen, run the tool again if you need it"), so at most your own earlier uploads and images still waiting for a reply are replayed, labeled as history. Older images past the 24 MB request budget are also dropped in fixed steps instead of on every new image, so long sessions stop re-sending their whole history on each image turn. On the Claude subscription lane, dropping an older image no longer counts as a change to the conversation at all, so a computer-use session full of large screenshots keeps its session and prompt cache instead of showing Session continuity lost (sent_stream_diverged) and re-billing the whole history on almost every turn (#2768). Thanks to @jcwleo for the analysis in the issue and to a guest for the report and the session data.

  • The TPS notice after a run no longer shows thousands of tok/s for a reply that arrived in one piece. The rate is now timed from the first streamed token to the last, so waiting for the first token no longer lowers it, and no rate is shown when nothing streamed or generation took under half a second (#3393).

  • A fallback chain no longer returns to the level that just failed when an entry's explicit level clamps to it (for example X:xhigh on a model without xhigh while X:high is running), and a network blip no longer falls back to another thinking level of the same model on the same failing connection (#3321, #3344).

  • Cache keep-alive pings, session prewarm usage, and the goal cache-warm savings estimate now price tokens with the model's cost card in force, so a scheduled list-price change applies to them too. A cost override in models.json keeps its own price and ignores the catalog's scheduled cards (#3378).

  • An extension that used its old context after an automatic session switch, compaction hand-off or reload no longer ends the whole session with exiting due to uncaughtException: ... extension ctx is stale .... The retired extension is already stopped at that point, so the error now goes to the debug log with its stack (which names the extension) and the session keeps running, in the TUI, print mode and RPC hosts (in print mode and RPC under Bun, only when the extension threw directly rather than from an async callback). Every other crash still exits as before, including with SENPI_RECOVER_INSPECTOR_VM_IMPORT=1 (#3389, oh-my-openagent#7114).

  • Opening the TUI and quitting, or running /new, without sending a message no longer leaves an empty session in /resume: entries that extensions write at startup no longer keep the empty file (#3263).

  • Several sessions sharing one credential pool no longer fail requests with "Credential store is busy ... held for 5500ms". Reading pool health takes no lock, callers in one process queue for it instead of starving each other, and a health write that still cannot take the lock is skipped and logged instead of failing a reply that already arrived (#3285).

  • Plain answers in the terminal no longer come wrapped in the Ask / For you / Now / Next block. When a question did not end in ?, the first-request todo list made it a one-task plan, and that list's result told the model to write the block. A one-task list now asks for nothing, and every preset reserves the block for turns that changed something, so an answer stays an answer even when finding it took tool calls (#3323, reported by @devxoul).

  • A retained worker session that the successor host reopens after an idle handover stays hidden from a default list_sessions and keeps its context, auto-titling and retry fallback. The successor now reopens a parked session through the same open as a client's open_session (#3368).

  • A client-less retained session opened with permissionPreset: "ask" keeps asking before gated tools after an idle host generation handover. The successor host reopened it under the host's default preset, so a client that attached without naming a preset ran gated tools without a prompt. The parked record now carries the session's permission preset, prompt surface and browser engine (#3364).

  • A client-less retained session (opened over the host socket with retain_on_disconnect, then disconnected) is no longer dropped on every idle host generation handover: the successor host now reopens exactly its predecessor's handoff-parked retained sessions on startup, by path and with the same durable session id, instead of coming up empty (#3282). A path whose open is already in flight attaches to that open once it finishes rather than failing session_path_in_use, and a waiting reopen is bounded on the waiter only so a slow-but-healthy open is never treated as stuck. The predecessor no longer reopens into its own registry a session it has just parked for its successor, which under load let its drain close the session and left the successor with nothing to reopen.

  • With no default model set, startup no longer settles on a model too small to start (for example a small custom model imported from OpenCode) and crashes with a stack trace. It moves to the next available model that fits and says why. If none fits, it prints one line naming the model, how it was chosen and what to do, then exits. A model you chose yourself is never swapped (#3333, reported by @taxilian).

Removed

@code-yeongyu/senpi-ai

Breaking Changes

Added

  • Model cost cards can carry scheduled list-price changes (cost.effectiveRates, each with an ISO UTC effectiveFrom). Usage is billed with the card in force when it is computed, and the Argon model rows on google and google-vertex now carry the provider's 2026-12-01 price change ($2/$10 to $4/$20 per 1M tokens), so cost totals stay right across the switch without a catalog refresh (#3378).

Changed

Fixed

  • OpenCode Go models now use the documented https://opencode.ai/inference/go base. The legacy /zen/go base has no route for the native generate-content wire, so the Argon Contributor model failed with a 404 page (#3377).

  • Newer-generation provider models that accept only discrete thinking levels now get a thinking level instead of a thinking budget, which they reject with HTTP 400. Rows without a thinking-level map, such as custom or discovered models, no longer offer the off and minimal levels those models reject (#3292).

  • A model that refuses a forced first-turn tool choice with "Thinking mode does not support this tool_choice" (opencode-go DeepSeek thinking models) no longer kills the turn at launch: the request is retried once without the forced choice (#3324).

Removed

@code-yeongyu/senpi-agent-core

Breaking Changes

Added

Changed

Fixed

Removed

@code-yeongyu/senpi-codemode

Breaking Changes

Added

Changed

Fixed

Removed

@code-yeongyu/senpi-tui

Breaking Changes

Added

Changed

Fixed

Removed