Repository navigation
Reconcile PTY agent sessions into the unified AIChat provider-mode model (#1220) - #1223
Conversation
Per ADR-034, PTY agent sessions are now provider mode 2d of the ai-chat node rather than a standalone subsystem. Backend: - Add node_id to LaunchSessionRequest (proto) threaded through the daemon launch path and tauri/cli command bindings. - capture_service: backfill the existing ai-chat node via update_node_unchecked instead of minting a new node at session end. Capture merges only capture:* metadata + status, preserving provider/messages. Frontend: - ai-chat-node-viewer dispatches on properties.provider: message UI for native/ollama/openai, embedded terminal (new ai-chat-pty-view) for pty, provider picker for an unconfigured placeholder node. - ai-chat-pty-view hosts the inline agent picker + Launch and the xterm terminal, passing nodeId so capture backfills this node. - Delete the standalone agent-sessions tab, launch/sessions panels, and all its nav/tab wiring. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
getCurrentBranch() and isWorkingDirectoryClean() assumed .git is a directory, but in a git worktree .git is a file with a 'gitdir:' pointer. Add resolveGitDir() to follow that pointer so the no-shell git reads work from worktrees (e.g. EnterWorktree), fixing 'bun run gh:pr' failing with 'Not in a git repository'. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Code Review — PR #1223 (Reconcile PTY sessions into unified AIChat provider-mode model)Review Type: Initial Review SummaryClean, well-scoped execution of ADR-034. The change correctly reframes PTY as provider mode 2d of the Requirements Check (issue #1220)
Correctness deep-dive (verified, no defects found)
Findings🟡 Important — Re-attach to a dead session renders a blank, silent terminal📁
Recommendation: gate the terminal on liveness — e.g., if 🟡 Important — No frontend reactivity to the daemon's backfill📁 The daemon backfills the node out-of-band via 🟢 Suggestion —
|
malibio
left a comment
There was a problem hiding this comment.
Automated pragmatic code review posted as a PR comment. Recommendation: COMMENT (net-positive, mergeable). Two non-blocking UX/consistency gaps noted: (1) re-attach to a dead/archived session renders a blank silent terminal; (2) no frontend reactivity to the daemon's out-of-band backfill. All 8 acceptance criteria met; standards compliant. See the full report comment for details.
Addresses pragmatic-code-review findings on PR #1223: - 🟡 Dead-session terminal on reopen (#1): the pty view no longer mounts a blank, silent terminal on a session whose PTY has exited. When the node is archived (capture backfill) or the session's exit is observed live, it renders a read-only summary/transcript + a 'Start new session' affordance. - 🟡 No live reactivity to the daemon's out-of-band backfill (#2): the view listens for pty-closed-<sessionId> on the session it launched and flips to the ended state immediately, instead of waiting for a reload. - 🟢 Relaunch path for archived nodes (#3): 'Start new session' clears the stale session id and returns to the config step. - 🟢 node_id contract doc (#4): reworded proto/tauri-commands docs — the desktop app always sets it; the CLI omits it (capture then skipped). Skipped 🟢 #5 (isWorkingDirectoryClean stub) — pre-existing, unchanged by this PR, out of scope. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Addressed review feedback (commit b5b55d0)✅ Addressed 4 of 5 findings:
⏭️ Skipped 1 finding:
Verification: |
Re-Review — PR #1223 (commit
|
malibio
left a comment
There was a problem hiding this comment.
Re-review posted as a PR comment (delta 4ecdfd1..b5b55d0). Summary: prior 🟡 findings substantively addressed; one new 🟡 — startNewSession's delete-by-omission does not actually clear the persisted capture:session_id against the backend's additive deep-merge (windowed re-attach-to-dead-session regression). Net-positive, non-blocking. Details in the comment.
…sion Re-review found that startNewSession() relied on delete-by-omission to clear capture:session_id, but the store's property merge is additive and never removes an omitted key — so the stale (dead) session id survived in storage. After relaunch+reload, the view could re-attach to the terminated PTY, reintroducing the blank-terminal symptom. Fix: don't try to delete the persisted id. Add a 'configuring' flag set by startNewSession() that suppresses re-attach to persistedSessionId until launch() overwrites it with the new session id. Removes reliance on the backend merge removing keys. Also: drop the redundant onDestroy (the $effect cleanup already runs on unmount) and scope the unlisten fn to the effect. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Delta re-review (commit
|
| Scenario | configuring | activeSessionId | sessionEnded | status | isEnded | sessionId | View |
|---|---|---|---|---|---|---|---|
| After Start new session | true | null | false | active | false | null | Config ✓ |
| After launch succeeds | false | newId | false | active | false | newId | Live terminal ✓ |
| Reopen archived node | false | null | false | archived | true | (n/a) | Ended ✓ |
| Reopen still-live session | false | null | false | active | false | persistedId | Re-attach ✓ (ADR-032) |
| Live session exits | false | oldId | true | active | true | (n/a) | Ended ✓ |
| Start new session → launch FAILS | true | null | false | active | false | null | Config + error ✓ |
The key win: in the window between Start-new-session and a successful (or failed) launch, configuring short-circuits the persistedSessionId fallback, so the stale dead id is never mounted. launch() then overwrites capture:session_id with the new id, so the stale value is gone after the next reload regardless.
Also verified:
- Redundant
onDestroyremoved; listener teardown now scoped to the$effectcleanup (locally-scopedunlisten), which runs on bothactiveSessionIdchange and unmount. The cancelled-before-registration race is handled. No leak. - Imports clean:
onMountstill used (line 154),UnlistenFnstill used (line 111),onDestroyremoved. - Atomic single-file change.
No remaining issues. Approving.
malibio
left a comment
There was a problem hiding this comment.
Delta re-review of 82a40151: the stale session-id re-attach bug is fully fixed. The configuring flag correctly suppresses the persistedSessionId fallback during the config window (including the launch-failure path), and launch() overwrites the persisted id on success. Listener teardown is now correctly scoped to the $effect cleanup with the redundant onDestroy removed. Imports clean. No remaining issues.
…del (#1220) (#1223) * Reconcile PTY sessions into unified AIChat provider-mode model (#1220) Per ADR-034, PTY agent sessions are now provider mode 2d of the ai-chat node rather than a standalone subsystem. Backend: - Add node_id to LaunchSessionRequest (proto) threaded through the daemon launch path and tauri/cli command bindings. - capture_service: backfill the existing ai-chat node via update_node_unchecked instead of minting a new node at session end. Capture merges only capture:* metadata + status, preserving provider/messages. Frontend: - ai-chat-node-viewer dispatches on properties.provider: message UI for native/ollama/openai, embedded terminal (new ai-chat-pty-view) for pty, provider picker for an unconfigured placeholder node. - ai-chat-pty-view hosts the inline agent picker + Launch and the xterm terminal, passing nodeId so capture backfills this node. - Delete the standalone agent-sessions tab, launch/sessions panels, and all its nav/tab wiring. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * Fix gh-utils git detection in linked worktrees getCurrentBranch() and isWorkingDirectoryClean() assumed .git is a directory, but in a git worktree .git is a file with a 'gitdir:' pointer. Add resolveGitDir() to follow that pointer so the no-shell git reads work from worktrees (e.g. EnterWorktree), fixing 'bun run gh:pr' failing with 'Not in a git repository'. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * Address review: archived pty session view + live exit reactivity Addresses pragmatic-code-review findings on PR #1223: - 🟡 Dead-session terminal on reopen (#1): the pty view no longer mounts a blank, silent terminal on a session whose PTY has exited. When the node is archived (capture backfill) or the session's exit is observed live, it renders a read-only summary/transcript + a 'Start new session' affordance. - 🟡 No live reactivity to the daemon's out-of-band backfill (#2): the view listens for pty-closed-<sessionId> on the session it launched and flips to the ended state immediately, instead of waiting for a reload. - 🟢 Relaunch path for archived nodes (#3): 'Start new session' clears the stale session id and returns to the config step. - 🟢 node_id contract doc (#4): reworded proto/tauri-commands docs — the desktop app always sets it; the CLI omits it (capture then skipped). Skipped 🟢 #5 (isWorkingDirectoryClean stub) — pre-existing, unchanged by this PR, out of scope. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * Address re-review: fix stale session-id re-attach after Start new session Re-review found that startNewSession() relied on delete-by-omission to clear capture:session_id, but the store's property merge is additive and never removes an omitted key — so the stale (dead) session id survived in storage. After relaunch+reload, the view could re-attach to the terminated PTY, reintroducing the blank-terminal symptom. Fix: don't try to delete the persisted id. Add a 'configuring' flag set by startNewSession() that suppresses re-attach to persistedSessionId until launch() overwrites it with the new session id. Removes reliance on the backend merge removing keys. Also: drop the redundant onDestroy (the $effect cleanup already runs on unmount) and scope the unlisten fn to the effect. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Michael Libio <malibio@Michaels-Mac-mini.local> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Closes #1220.
Reconciles the PTY agent-session code to match ADR-034: AIChat Provider Modes (nodespace-docs PR #47). PTY sessions are no longer a standalone subsystem — they are provider mode 2d of the
ai-chatnode. The conversation node exists up front; the terminal is a view onto it; capture backfills it instead of minting a new node. ADR-032's PTY mechanics are unchanged.What changed
Backend
optional string node_idtoLaunchSessionRequest, threaded through the daemon launch handler, the Tauri command binding, and the CLI launch command (CLI passesNone— not node-bound).capture_service.rs: mint → backfill. At session end, capture mergescapture:*metadata +status/last_activeonto the existingai-chatnode viaupdate_node_unchecked(additive deep-merge preservesprovider/messages). When nonode_idis present (e.g. CLI session), capture is skipped rather than minting a node.update_node_uncheckedover OCCupdate_node: capture is a single, additive, fire-and-forget writer, and a spurious version conflict from a concurrent viewer edit must not silently drop the capture.Frontend
ai-chat-node-viewer.sveltenow dispatches onproperties.provider:native/ollama/openai→ existing message UI (ADR-028), unchanged.pty→ embedded terminal via the newai-chat-pty-view.svelte(the terminal is the node's viewer)./ai-chatnode has no provider yet).ai-chat-pty-view.svelte(new): inline agent picker + availability + Launch (passingnodeId), then hosts thextermterminal. Re-attaches to a persisted session id on reopen.agent-sessionstab and itsagent-sessions-panel/agent-launch-panel/sessions-panelcluster, plus all nav/tab wiring (navigation.ts,layout.ts,pane-content.svelte,navigation-sidebar.svelte,tab-persistence-service.ts).pty-terminal.svelteis kept and re-hosted by the pty view.Acceptance criteria
/ai-chatcreates a placeholderai-chatnode (no provider chosen)provider: pty, the embedded terminal renders as the node's viewer (no separate tab/panel)agent-sessionstab and its panels are removedai-chatsessions = multiple nodesNotes
mainafter Migrate local DB from SurrealDB embedded to SQLite (libsql) #1222 (SurrealDB → libsql migration). Re-verified the full workspace build + tests against the new DB backend.Testing
bun run test:all: frontend 3936 passed, agent-tools 17, mcp 10, skill 13,nodespace-core1128 — all green.cargo test -p nodespace-daemon: 14 lib (incl. 7 capture) + 9 gRPC integration — all pass.bun run quality:fix: clean (eslint + tsc + svelte-check 0 problems; rustfmt + clippy clean across workspace).🤖 Generated with Claude Code