* refactor(anthropic): split session-catalog into concept modules
Follow the #124178 sibling seam pattern while preserving behavior. Remove the max-lines suppression and baseline entry after the split.
* refactor(anthropic): own record caches in session-catalog-discovery
The record-shaped caches (catalogDiscoveryCache, claudeSessionScanCache,
their entry types, bounds, and TTLs) are consumed only by discovery; moving
them out of scan removes the scan->discovery type-import edge flagged by the
madge topology gate and leaves scan as record-agnostic fs primitives.
* refactor(session-catalog): unify adoption coordination
* refactor(codex): split session catalog modules
* fix(anthropic): narrow adopted history for import
The coordinator migration hoisted history to an optional outer binding;
afterCreate needs the definite array from the create scope.
Also drop the retired session-catalog max-lines ratchet entry.
* fix(sessions): isolated gateways no longer inherit HOME external session catalogs
A gateway on isolated state (custom OPENCLAW_STATE_DIR/CONFIG_PATH/OPENCLAW_HOME,
relocated home, or any named profile) listed, read, continued, archived, and
reopened the operator's real Claude Code/Codex/OpenCode/Pi sessions from the
process HOME. External catalogs now require the default install identity for
process-HOME scans: every catalog verb receives the isolation policy and rejects
HOME-fallback local targets, unknown providers fail closed unless they declare
supportsProcessHomeIsolation, and one structured warning records the skip.
Paired-node hosts and explicitly rooted stores (CLAUDE_CONFIG_DIR, CODEX_HOME,
OPENCODE_DB, Pi session dirs) keep working; default-identity gateways are
unchanged.
* fix(sessions): inject catalog HOME-isolation fact at registry construction
* chore(sdk): regenerate plugin API baselines after rebase
* chore(sdk): regenerate plugin API baselines after rebase
* fix(anthropic): detect Claude CLI routes pinned to non-default models
Route detection probed one hardcoded model id, so moving the packaged CLI
default stopped advertising session creation for configs routing an older
Claude model, and adoption stamped the packaged default onto sessions the
operator never routed. Probe the seeded CLI model ids instead and reuse the
routed model for both paths.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix(anthropic): keep the canonical CLI model id module-local
The route probe replaced its only external consumer, so the export is dead.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
* fix(anthropic): accept "cli" entrypoint in Claude session catalog discovery
Claude Code v2.x writes entrypoint: "cli" in its JSONL session files.
Previously only "sdk-cli" was recognized, causing all v2.x sessions to
be silently skipped.
Extract CLI_ENTRYPOINTS set and isCliEntrypoint() helper, then apply
the same fix to both TypeScript catalog and macOS paired-node native
catalog. Add matching regression coverage on both platforms.
Closes#105164
* fix(anthropic): refresh Claude CLI session discovery
Apply the reviewed cli/sdk-cli allowlist, mirrored negative coverage, and fallback-scope documentation to current main.
Co-authored-by: 黄攀0668000858 <huang.pan@xydigit.com>
---------
Co-authored-by: Peter Steinberger <steipete@gmail.com>
* fix(anthropic): complete transcript reverse-scan windows across short reads
readLocalClaudeTranscriptPage filled each reverse-scan window with a single
positional read and threw "Claude transcript changed while it was being read"
whenever bytesRead !== size. A positional read may return fewer bytes than
requested inside an unchanged file, so a benign short read failed the transcript
page load with a message implying the file changed. The forward metadata scan in
the same file already loops short reads; the reverse scan did not.
Fill each window across short reads, advancing both the buffer offset and the
file position. A zero-byte read before the window is filled is premature EOF and
still throws the same error.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* test(anthropic): tighten transcript short-read coverage
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
* feat(anthropic): import Claude Desktop custom groups
* fix(anthropic): resolve Claude Desktop groups from LevelDB entries
Group discovery regexed whole decompressed blocks, so matches were not
attributable to a Local Storage value and byte order decided the winner.
On a real 5.4MB store that mislabelled 59 of 159 sessions, surfacing a
mojibake label spliced out of Snappy copy-record bytes.
Parse SSTable entries properly instead: prefix-delta keys bounded by the
restart array, newest internal sequence per user key (honoring deletions),
and record scanning confined to a single value. Values are normalized so
Chromium's UTF-16 form scans like Latin-1, and unflushed WAL writes keep
precedence over SSTables.
Prod LOC grows ~77; it buys structural correctness in place of ordering
luck, and folds the old index-only walk into one shared entry decoder.
Verified against the live store: 159 assignments, 0 mislabelled versus an
independent entry-level ground truth, 0 control-character labels.
* fix(ui): sort custom session groups ahead of project groups
Custom groups were pushed into the section list as encountered, so their
position depended on roster order rather than the documented behavior;
the existing test only passed because its fixture happened to be ordered
that way. Collect custom and project groups separately and concatenate,
and assert the guarantee with a reversed-input case.
* fix(anthropic): widen LevelDB fixture key type for test typecheck
The prefix-delta helper assigned a Buffer into a Buffer-typed accumulator
whose generic argument differed, which tsgo rejects in the test lane.
* chore(anthropic): drop release-owned changelog edit from the PR
CHANGELOG.md is generated at release time, so a normal PR must not carry
an entry. The release-note context lives in the feature commit message and
the PR body instead.
Catalog session rows (sidebar context menu + click), the built-in viewer
header, and a new "Open Codex/Claude sessions in" preference can launch the
native CLI (codex resume / claude --resume) in the operator terminal on the
machine that owns the session.
- Gateway-local sessions spawn through the existing terminal launch policy
(sandbox/enabled gates preserved) with the resume command in the session cwd.
- Paired-node sessions run through a new seq-ordered node PTY relay: a
duplex node-host command streams PTY output via node.invoke.progress and
receives keystrokes/resize via a new node.invoke.input event, behind the
unchanged terminal.* client protocol (TerminalSessionManager gains a backend
abstraction; node relay reuses the streaming-invoke controller).
- Owner boundary: each plugin owns its resume command and builds argv from a
validated thread id; the gateway routes node opens through the node command
allowlist and plugin invoke policy (no advertisement-only trust), and nodes
re-verify session eligibility before spawning.
- UI setting catalogOpenTarget + canOpenTerminal capability gate every entry
point; capability requires the owning host to actually have the CLI.
Node PATH is normalized before command-availability probes, Windows .cmd/.bat
shims spawn via ComSpec, and catalog terminal opens reattach persisted tabs
before opening the new tab.
* fix(ui): make catalog sessions selectable in place and continuable from desktop-app sources
- sidebar: no phantom chat row for catalog keys, active highlight on catalog rows,
adopted sessions render as their live row inside the catalog group (deduped from
the regular list), host subtitle only for paired-node rows
- chat: view-only banner only after catalog metadata proves canContinue=false
- chat->sidebar continued event binds the adopted row before the next catalog poll
- anthropic: local claude-desktop sessions share the ~/.claude/projects store, so
list them as continuable and allow catalog continue to fork-resume them
* refactor(ui): extract catalog row rendering and lookup into shared modules
Keeps app-sidebar/chat-pane/anthropic session-catalog under the LOC ratchet by
moving cohesive blocks: sidebar catalog groups + adopted-row binding into
app-sidebar-session-catalogs.ts, the catalog metadata lookup into
catalog-key.ts, and the Claude node-host commands into
session-catalog-node-commands.ts; ratchet baselines down for all three.
* fix(ui): explain the disabled composer when catalog metadata cannot resolve a session
* style(ui): keep chat-pane within the LOC ratchet
* fix(anthropic): keep catalog node-command helpers module-local and repin SDK surface
The node-command module now owns its capability constant, projects-store
probe, and params parsing, so the catalog engine exports nothing new.
Repin the SDK surface budgets (+1 export/+1 callable landed on main
without a pin bump); unexport the UI helper types knip flagged.