Skip to content
Open
Show file tree
Hide file tree
Changes from 65 commits
Commits
Show all changes
74 commits
Select commit Hold shift + click to select a range
a8e789a
feat(btw): retain native side sessions
MoerAI Aug 23, 2026
cb493d7
fix(btw): enforce retained side runtime policy
MoerAI Aug 23, 2026
66be6cc
Merge remote-tracking branch 'origin/main' into feat/btw-retained-ses…
MoerAI Aug 23, 2026
8735d2b
fix(btw): route tool isolation through session API
MoerAI Aug 23, 2026
549f8ce
fix(btw): enforce tool policy at provider boundary
MoerAI Aug 23, 2026
3a947d9
fix(btw): preserve parent branch context
MoerAI Aug 23, 2026
d537d31
Merge remote-tracking branch 'origin/main' into feat/btw-retained-ses…
MoerAI Aug 23, 2026
ac875f4
fix(btw): enforce policy on prepared requests
MoerAI Aug 23, 2026
5419584
fix(btw): persist initialized retained sessions
MoerAI Aug 23, 2026
cf9cc37
fix(btw): require persisted Main session
MoerAI Aug 23, 2026
bd7ef4d
fix(btw): preflight side isolation state
MoerAI Aug 23, 2026
661518b
fix(btw): guard extension editor dialogs
MoerAI Aug 23, 2026
396ad9e
fix(btw): validate parent session identity
MoerAI Aug 23, 2026
dc08ae8
fix(btw): restore captured Main leaf
MoerAI Aug 23, 2026
2e45079
fix(btw): harden picker and parent identity
MoerAI Aug 23, 2026
d3e2086
fix(btw): serialize picker dispatch
MoerAI Aug 23, 2026
2d6f797
fix(btw): bound catalog metadata reads
MoerAI Aug 23, 2026
a63dec6
fix(btw): preflight live provider auth
MoerAI Aug 23, 2026
a6f0e92
fix(btw): use metadata-only catalog listing
MoerAI Aug 23, 2026
619613c
docs(core): cover retained BTW host changes
MoerAI Aug 23, 2026
4373455
fix(btw): seed external session metadata
MoerAI Aug 23, 2026
eab6a02
fix(btw): preserve configured session storage
MoerAI Aug 23, 2026
174ff94
fix(btw): suppress unavailable tool prompts
MoerAI Aug 23, 2026
b06be7c
fix(btw): revalidate settled parent identity
MoerAI Aug 23, 2026
c852fc9
fix(btw): suppress MCP instructions
MoerAI Aug 23, 2026
175befc
fix(btw): serialize destructive close
MoerAI Aug 23, 2026
44f1770
fix(btw): serialize session actions
MoerAI Aug 23, 2026
ab41efb
fix(btw): serialize Main return
MoerAI Aug 23, 2026
37a4b00
fix(btw): suppress skill guidance
MoerAI Aug 23, 2026
912b537
fix(btw): resolve shortcut command collisions
MoerAI Aug 23, 2026
88d3ef6
fix(btw): suppress tool prompt presets
MoerAI Aug 23, 2026
b5faeed
fix(btw): revalidate active Main for New
MoerAI Aug 23, 2026
3cacd49
fix(btw): bound recovered catalog scans
MoerAI Aug 23, 2026
a99fac8
fix(btw): revalidate Main after auth
MoerAI Aug 24, 2026
dc03fb7
fix(btw): preserve action reservation
MoerAI Aug 24, 2026
6ecb31e
fix(btw): reject stale source after auth
MoerAI Aug 24, 2026
7080b3a
fix(btw): defer parent snapshot
MoerAI Aug 24, 2026
84480c8
fix(btw): wait before picker switch
MoerAI Aug 24, 2026
d6c1563
fix(btw): revalidate switch target
MoerAI Aug 24, 2026
789b58d
fix(btw): guard direct session returns
MoerAI Aug 24, 2026
c9ff2b8
fix(btw): bind session identity through teardown
MoerAI Aug 24, 2026
7fd8913
fix(btw): recover cancelled replacements
MoerAI Aug 24, 2026
be6eb5b
fix(btw): preserve recovery identity
MoerAI Aug 24, 2026
8cbba14
fix(btw): close discarded runtimes
MoerAI Aug 24, 2026
9d59c72
fix(btw): guard post-veto source state
MoerAI Aug 24, 2026
a6e18a8
fix(btw): preserve streaming close
MoerAI Aug 24, 2026
09fb31a
fix(btw): track streaming source generation
MoerAI Aug 24, 2026
81530c1
fix(btw): revalidate after host rebind
MoerAI Aug 24, 2026
696cde6
fix(btw): lock replacement callbacks
MoerAI Aug 24, 2026
ef9ba6c
fix(btw): harden replacement rollback
MoerAI Aug 24, 2026
76325cc
fix(btw): scope replacement privilege
MoerAI Aug 24, 2026
0d6651e
fix(btw): close replacement admission gaps
MoerAI Aug 24, 2026
64077ee
fix(btw): release initial turn close gate
MoerAI Aug 24, 2026
ff0c5cb
fix(btw): bind admission and deletion identity
MoerAI Aug 24, 2026
5cdc370
fix(btw): rollback rejected shortcut claims
MoerAI Aug 24, 2026
1b6e29d
fix(btw): cancel deferred shortcut claims
MoerAI Aug 24, 2026
046f4d3
fix(btw): clear deferred claims before rollback
MoerAI Aug 24, 2026
9238f91
Merge remote-tracking branch 'origin/main' into feat/btw-retained-ses…
MoerAI Aug 24, 2026
29f0690
Merge remote-tracking branch 'origin/main' into feat/btw-retained-ses…
MoerAI Aug 24, 2026
ad4ff63
fix(btw): recover failed retained persistence
MoerAI Aug 24, 2026
72310e7
fix(btw): claim retained persistence files
MoerAI Aug 24, 2026
67ea1af
fix(btw): quarantine failed persistence cleanup
MoerAI Aug 24, 2026
c199960
fix(btw): bind persistence to claimed inode
MoerAI Aug 24, 2026
0316d3c
fix(btw): detach failed candidate persistence
MoerAI Aug 24, 2026
a99ca35
Merge remote-tracking branch 'origin/main' into feat/btw-retained-ses…
MoerAI Aug 24, 2026
3fc81d1
Merge remote-tracking branch 'origin/main' into feat/btw-retained-ses…
MoerAI Aug 24, 2026
22ca1ce
Merge remote-tracking branch 'origin/main' into feat/btw-retained-ses…
MoerAI Aug 25, 2026
8c4c07b
fix(bash-timeout): tolerate missing prompt context
MoerAI Aug 25, 2026
6168d8e
Merge remote-tracking branch 'origin/main' into feat/btw-retained-ses…
MoerAI Aug 25, 2026
2028cb8
test(rpc): model replacement lifecycle in registry fixture
MoerAI Aug 25, 2026
8f4770a
Merge remote-tracking branch 'origin/main' into feat/btw-retained-ses…
MoerAI Aug 25, 2026
62778e3
Merge remote-tracking branch 'origin/main' into feat/btw-retained-ses…
MoerAI Aug 25, 2026
1e5962a
Merge remote-tracking branch 'origin/main' into feat/btw-retained-ses…
MoerAI Aug 25, 2026
ce9a799
Merge remote-tracking branch 'origin/main' into feat/btw-retained-ses…
MoerAI Aug 25, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 49 additions & 2 deletions packages/coding-agent/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,55 @@
- Assistant text painted during smooth streaming no longer vanishes and bursts back: `syncTrailingAssistantText` now yields the streaming head to the reveal controller while it paces (smooth streaming on, no toolCall in the head), so the paced prefix and the full head can no longer overwrite each other mid-stream ([#1102](https://github.com/code-yeongyu/senpi/pull/1102)).
- The goal continuation wait countdown no longer renders over the Working indicator during externally started turns; the `goal-wait` footer segment now hides while a turn runs and restores itself when the session parks again, leaving the cache-warm schedule and iteration accounting untouched ([#1100](https://github.com/code-yeongyu/senpi/pull/1100)).
- Webfetch now safely discards redirect response bodies under Bun 1.4.0's bare `undici`, which may omit `body.dump()`, by falling back to argument-free stream destruction instead of re-emitting cleanup failures as uncaught stream errors ([#1089](https://github.com/code-yeongyu/senpi/issues/1089)).

### Added
- Retained BTW picker switches now revalidate the selected session after asynchronous pre-switch
guards, preventing a replaced JSONL path from opening an unrelated session.
Comment on lines +103 to +104

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Move the BTW entries into Unreleased

These new BTW fixes and feature notes are appended beneath the already-released 2026.8.24 heading while the corresponding Unreleased sections remain empty. This rewrites an immutable published release record and prevents the next release tooling from collecting the changes; move the additions to the appropriate Unreleased subsections.

AGENTS.md reference: AGENTS.md:L101-L102

Useful? React with 👍 / 👎.

- Direct BTW returns and catalog discovery now reject path-reused Main or active-side session
files before switching, snapshotting, or deleting.
- Retained BTW creation and switching now revalidate parent and destination identities after
asynchronous veto and shutdown hooks, preventing stale session managers from being applied.
- Identity cancellations after shutdown now leave a rebound live runtime, and retained parent
checks use bounded session headers instead of loading complete transcripts.
- Cancellation recovery preserves the outgoing session identity, tree, and effective cwd, while
new/resume destinations are revalidated again after asynchronous runtime construction.
- Stale candidate runtimes now run shutdown hooks before disposal, and recovered persisted sessions
are revalidated after construction before the host rebinds them.
- Retained replacements now revalidate after removed-extension handlers and cancel when source
activity or tree generation changes during asynchronous switch vetoes.
- Streaming Ctrl+C close remains destructive, while guarded teardown prevents any later prompt
from starting on the outgoing session during shutdown handlers.
- Streaming-origin BTW closes now track prompt-admission generation, preventing later queued or
direct submissions during asynchronous switch vetoes from being aborted.
- Guarded replacements now revalidate after host rebind before callbacks, and typed extension
command hosts inherit every public identity/source option.
- Replacement sessions now block external prompt admission through callback completion, and
cancelled guarded creation removes its still-owned persisted side transcript.
- Replacement locks now block extension-triggered turns outside callbacks, while cancelled-side
cleanup atomically quarantines files and restores the outgoing runtime even when cleanup fails.
- Replacement callback privilege is now method-scoped, and cancelled-side cleanup verifies
ownership both before and after quarantine to preserve already-active replacements.
- Recovery now falls back on metadata errors, custom trigger turns count toward source generation,
and a pending replacement admits only the registered BTW close command.
- Retained BTW initial turns now release their switch reservation after prompt admission, allowing
Ctrl+C to close a slow or hung first answer.
- Retained prompt reservations now wait for actual provider admission, and destructive side
deletion preserves path-reused replacement sessions through an atomic ID-bound inode claim.
- Rejected BTW shortcut dispatch no longer strands its reservation, and concurrent replacement
writers are restored without leaving valid sessions at hidden quarantine paths.
- Retained BTW creation now restores the outgoing session when setup or initialized-session
persistence fails, removing inode-owned headerless partial files instead of leaving prompts bound
to a replacement-pending candidate; cleanup quarantines before identity validation so concurrent
path replacements survive, and initialized writes stay bound to the claimed descriptor so a
pre-write pathname swap cannot be overwritten. Failed candidates detach persistence before
extension shutdown handlers can append into a reused path.
- User abort during an asynchronous `agent_settled` handler now rejects discarded deferred BTW
shortcuts and releases their pending reservation after clearing the old delivery generation,
allowing synchronous retry without recapture or settlement hangs.

### Added

- Retained multi-session `/btw` TUI with native transcripts, a Main/numbered-side/New picker,
configurable `Ctrl+/`, `Ctrl+_`, and `Ctrl+7` switching, idle Escape-Escape return, and
current-side Ctrl+C deletion; non-TUI side queries remain one-shot and parallel.

### Changed

Expand Down
141 changes: 141 additions & 0 deletions packages/coding-agent/src/changes.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,146 @@
# changes

## 2026-08-23 - retained BTW sessions and configurable switch routing
Comment thread
MoerAI marked this conversation as resolved.

### What changed

- The builtin BTW TUI path creates separate retained host sessions and uses native session
selection/transcripts instead of the one-shot widget.
- The fresh replacement context applies a retained side's captured model and thinking level before
its first turn. A typed `newSession` policy capability persists the tool ban without crossing the
extension/core boundary, forces later extension startup to keep the active set empty, and blocks
Cursor exec registered-tool lookup. Final payload enforcement strips provider-native tools after
every extension transform.
- Main-side context snapshots follow Main's selected tree leaf rather than the file's physical
tail; retained siblings inherit that persisted leaf, and an oversized newest message is
truncated instead of emptying the snapshot. Creation waits for Main to settle before reloading
the catalog and snapshotting.
- Session tool policy is enforced at `ExtensionRunner`'s final payload boundary, covering normal,
compaction, and cache-keepalive provider requests after all extension transforms.
- Extension command contexts expose typed session listing/inspection. `newSession` can opt into
flushing initialized metadata before returning, which keeps empty retained sides durable without
crossing the extension/core boundary.
- BTW rejects a Main session path until its JSONL exists, not merely until the path has been
allocated.
- BTW switch input yields to an active extension dialog, and side creation validates captured model
authentication before replacing Main.
- Native provider prompt contributors honor the typed session tool-policy flag, keeping retained
side prompts coherent with their empty provider/tool surfaces.
- Interactive extension dialog state includes selectors, inputs, and editors, preventing raw
extension shortcuts from invisibly orphaning any of those pending flows.
- BTW inline questions require an active model before replacement, and retained catalogs match
parent session identity as well as filesystem path.
- Returning from a retained side restores Main's captured leaf across command, close, and picker
surfaces. Interactive dialog gating follows the actual focused component, including auth flows.
- Focused dialogs also retain destructive Ctrl+C. Duplicate retained labels include session
identity, and direct parent return validates the destination session ID before side deletion.
- BTW TUI dispatch uses idle/pending/running reservation to collapse concurrent shortcuts; picker
choices carry expected IDs and reject path-reuse replacements after selection.
- Side close reinspects the visible session ID immediately before unlink. BTW catalog discovery uses
bounded custom-entry inspection rather than full transcript parsing.
- ModelRegistry exposes live provider authentication checks; BTW requires one before replacing Main.
- Header-only session listing removes full-summary scans from BTW catalog construction, and close
restores the captured Main leaf even when side identity or unlink checks fail.
- Metadata discovery skips malformed rows and separately inspects the active Main/parent path, so
explicit sessions outside the configured directory remain switchable.
- Returning to an external Main preserves the configured session storage directory, and Escape used
to cancel a focused dialog cannot count toward the side's session-level return pair.
- Inline creation stops after Main identity loss; missing-cwd recovery keeps authoritative session
rows; terminal/bash-timeout/todo/imagegen prompts are suppressed under retained no-tools policy.
- Creation revalidates settled Main ID, recovered siblings bypass header-cwd prefiltering before
exact parent matching, and imagegen skill discovery is suppressed when tools are disabled.
- MCP session attachment and server-instruction injection are suppressed under retained no-tools policy.
- Repeated Ctrl+C collapses to one destructive close; direct return and picker identity checks use
header-only inspection rather than full transcript parsing.
- Switch and destructive close share one mutual-exclusion gate across raw and direct commands.
- Main return joins the same action mutex, and retained no-tools sessions skip MCP startup attachment.
- Retained no-tools sessions omit ordinary skill guidance that would require the unavailable read tool.
- BTW shortcuts dispatch collision-resolved builtin invocation names instead of hardcoded command text.
- Model-specific prompt presets cannot reintroduce file-tool guidance in retained no-tools sessions.
- New BTW rejects same-path Main replacement even when both catalog loads observe the replacement.
- Shared session-directory catalog scans exclude unrelated cwd rows before custom-data inspection.
- BTW creation rejects parent-path reuse that occurs during asynchronous authentication preflight.
- BTW action reservations persist across runner generations; disabled model selection resets presets.
- BTW creation cannot replace a source that became active or advanced to a newer leaf during auth.
- Retained parent snapshots are deferred until creation's final idle wait and source-leaf capture.
- Picker session switches wait for idle before and after target identity validation.
- Session switches can carry an expected destination ID through asynchronous pre-switch guards;
BTW uses it to reject and refresh a path-reused target before current-session teardown.
- Direct BTW Main returns and closes pass the parent ID into that guard, while catalog discovery
rejects current-path metadata whose persisted side ID differs from the live session.
- New-session replacement accepts an expected parent ID and validates it after async veto and
shutdown phases; guarded resume also reopens its destination after teardown before applying it.
- Post-teardown cancellation recovers and rebinds the outgoing runtime before returning, and
expected parent identities use header-only metadata inspection instead of full JSONL loading.
- `SessionManager.cloneInMemory()` preserves detached identity/tree state for recovery, retained
cwd overrides survive rebinding, and replacement identities are checked after runtime creation.
- Stale constructed candidates emit `session_shutdown` before disposal; recovery rechecks its
persisted outgoing path after construction and falls back to the detached snapshot before rebind.
- Failed retained setup or initialized-session persistence tears down the applied candidate,
removes any inode-claimed partial file even without a parseable header, and restores the outgoing
runtime before reporting the failure. Cleanup quarantines before inode validation so a path
replacement between inspection and deletion is restored instead of unlinked. Persistence writes
through the still-open claim descriptor and rejects a pathname identity swap before activation;
the failed candidate disables persistence before its shutdown lifecycle.
- Candidate identity is rechecked after removed-extension handlers, while source expectations let
guarded new/resume operations reject active or leaf-changed sessions after async vetoes.
- Source guards distinguish pre-existing streaming actions from activity begun during vetoes, and
AgentSession blocks new prompt admission after guarded teardown starts.
- AgentSession exposes a monotonic prompt-admission generation through extension contexts; BTW
source guards use it to reject later queued/direct submissions during streaming switch vetoes.
The getter remains optional for external extension hosts, while Senpi supplies it on every live
command context.
- Runtime identity checks now cover the awaited host-rebind phase before callbacks, and command
action option types alias public context methods to prevent typed hosts dropping BTW guards.
- Applied replacement sessions reject external prompts until callbacks finish; guarded creation
rollback deletes the initialized side file only when its persisted ID still matches.
- Replacement turn locks cover prompt/custom/user paths with callback-scoped privilege; cancelled
file cleanup uses atomic quarantine and cannot prevent outgoing runtime recovery.
- Replacement privilege is synchronous per context method rather than callback-wide, and side
cleanup performs pre-rename plus post-rename ownership validation.
- Recovery catches bounded metadata failures, custom triggerTurn admissions increment source
generation, and replacement-pending narrowly recognizes the registered BTW close invocation.
- Retained initial turns release their switch reservation after prompt admission so Ctrl+C can
acquire and execute the destructive close while the first answer is still streaming.
- Replacement-context prompts report actual provider admission after asynchronous preflight, and
BTW side deletion atomically claims and ID-validates the file inode before unlink.
- Fire-and-forget extension dispatch reports host rejection so shortcut reservations can roll back;
conditional side deletion preserves both a path-reused session and concurrent recreated data.
- Settlement-deferred dispatch also reports cancellation when user-abort generation changes,
resolving the deferred turn claim and releasing shortcut reservations for discarded actions.
Delivery state is cleared before callbacks so synchronous retries cannot be recaptured.
- `packages/coding-agent/src/core/agent-session-runtime.ts`: replacement lifecycle carries typed
setup, persistence, and session tool policy.
- `packages/coding-agent/src/core/agent-session.ts`: replacement actions expose the new typed
command-context capabilities.
- `packages/coding-agent/src/core/keybindings.ts`: configurable BTW switch defaults include all
terminal-safe aliases.
- `packages/coding-agent/src/core/model-registry.ts`: retained creation can run the live provider
auth check before replacement.
- `packages/coding-agent/src/core/sdk.ts`: the public session creation contract carries retained
setup/policy options.
- `packages/coding-agent/src/core/session-manager.ts`: initialized persistence and bounded
header/custom-metadata discovery support retained catalogs.
- `app.btw.switch` adds Ctrl+/, Ctrl+_, and Ctrl+7 defaults.
- Interactive extension UI exposes active key matching and effective-key lookup for configurable
terminal routing and visible fallback hints.

### Why

- Side conversations need durable identity, host scrolling, reliable return/close behavior, and a
fallback when an editor such as Zed reserves Ctrl+/ before the terminal receives it.

### Why an extension could not handle it

- Retained session replacement, model/auth preflight, session persistence, key parsing, SDK
contracts, and metadata-only discovery require host APIs that extensions consume but cannot
safely implement by reaching into private runtime state.

### Expected merge-conflict zones

- MEDIUM: builtin BTW wiring and keybinding definitions.
- LOW: extension UI context and interactive implementation.

## 2026-08-22 - emit agent_idle after settlement-deferred turns resolve

### What changed
Expand Down
Loading