Files
Netcatty/components/ai/draftSendGate.ts
Eric Chan 4574f1e2b2
Some checks failed
build-packages / build-macos (push) Has been cancelled
build-packages / build-windows (push) Has been cancelled
build-packages / build-linux-x64 (push) Has been cancelled
build-packages / build-linux-arm64 (push) Has been cancelled
build-packages / release (push) Has been cancelled
fix: stabilize scoped AI draft/session transitions (#724)
* fix: correct terminal AI history resume behavior

The previous implementation plan mistakenly treated reopening an old terminal AI session in a fresh or reconnected SSH tab as a scope-retargeting feature.

The intended rule is draft-first:
- a fresh or reconnected terminal opens on a blank draft
- older chats remain available in history for manual access
- selecting history does not imply automatic scope transfer into the new tab

This change is a rule correction, not a conflict between product rules.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* fix: harden ai draft transitions

* fix ai session continuation from history

* fix: clear stale activeSessionIdMap entry when view resolves to draft

Addresses the Codex P2 review on aiPanelViewState.ts:38. When a terminal
scope mounts with a persisted activeSessionIdMap entry but no explicit
panelView and no draft, resolveDisplayedPanelView now returns the
default draft view (terminal fresh-start behavior). The sync effect
that writes into activeSessionIdMap is guarded by `if (!activeSession)
return`, so the old entry stays put. That stale entry then leaks into
activeTerminalTargetIds in every other scope, and
getSessionScopeMatchRank uses it to suppress host-matched history that
is actually resumable — so valid sessions vanish from the history
drawer until another action rewrites the map.

Add a dedicated effect that clears the scope's activeSessionIdMap
entry whenever the resolved panel view is draft but a persisted
session id is still present. This keeps the map an accurate record of
"which session each scope is currently showing" instead of a lagging
snapshot.

Also extend sessionScopeMatch.test.ts to cover the rank=2 exact-match
branch and the scope-type mismatch short-circuit, which were missing
from the original suite.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: track cross-terminal session ownership by session id, not targetId

Addresses the Codex follow-up review on commit 345244b2. When a user
resumes a session from history into a different terminal, the session's
`scope.targetId` still points at the original terminal. The previous
ownership tracking — which checked whether `session.scope.targetId`
appeared in `activeTerminalTargetIds` (derived from the keys of
`activeSessionIdMap`) — therefore:

- could not prevent the same session from being resumed in multiple
  terminals simultaneously, because the resumed session's targetId
  never matches the current scope's targetId; and
- let `pruneInactiveScopedSessions` treat a session as orphaned and
  clear its `externalSessionId` the moment the original terminal
  closed, even though another terminal was actively using it.

Switch ownership to be keyed on session id:

- `getSessionScopeMatchRank` now takes `activeTerminalSessionIds`
  (a Set of session ids currently displayed by other terminal scopes)
  and returns rank 0 when `session.id` is in that set.
- `AIChatSidePanel` derives `activeTerminalSessionIds` from the
  *values* of `activeSessionIdMap`, excluding the current scope's key.
- `pruneInactiveScopedSessions` gains an `activeSessionIds` parameter;
  sessions whose id is in this set are never reported as orphaned and
  never have their `externalSessionId` cleared, regardless of their
  stored `scope.targetId`.
- `cleanupOrphanedAISessions` computes the in-use set from the
  pre-cleanup `activeSessionIdMap`, filtered to live scopes, and
  passes it through. The map is read once and reused.

Tests cover the new id-based ownership, the rank-2 exact-match path,
the scope-type-mismatch short-circuit, and the
"resumed-elsewhere session must not be cleaned" invariant.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: bincxz <16399091+binaricat@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 17:16:10 +08:00

13 lines
250 B
TypeScript

export function tryBeginDraftSend(gate: { current: boolean }): boolean {
if (gate.current) {
return false;
}
gate.current = true;
return true;
}
export function endDraftSend(gate: { current: boolean }): void {
gate.current = false;
}