mirror of
https://github.com/stablyai/orca.git
synced 2026-10-03 00:02:19 +00:00
fix(native-chat): retry not-yet-flushed transcripts instead of settling into a permanent error (#8418)
* fix(native-chat): retry not-yet-flushed transcripts instead of settling into a permanent error A freshly-created session's transcript .jsonl lands on disk seconds to minutes after the process starts. Native chat's one-shot read raced that first flush: a miss became a permanent "No transcript found" error and the live-tail subscription silently degraded to a no-op, so the pane never recovered even after the file appeared. - transcript-reader/read-cache: mark the miss with notFound so callers can tell "not flushed yet" from a real parse/IO error (never cached). - transcript-watch: poll resolve+install (500ms backoff, 5s cap) for the subscription's lifetime instead of returning a dead no-op watcher. - use-native-chat-live-session: retry a notFound read with backoff for up to 60s while staying in the loading state, and let live appends render over a stale initial-read error. Fixes #8401 Claude-Session: https://claude.ai/code/session_01HA5g3X7wCakBttBpDru9Fp * fix(native-chat): address CodeRabbit review — ENOENT retryable, content over spinner, blank-id guard, unref poll timer - transcript-reader: an ENOENT after a successful resolve is the same first-flush/rotation race as an unresolved path — mark it notFound. - use-native-chat-live-session: live appends landing mid-retry render instead of the loading state (mirrors the stale-error gate). - transcript-watch: bail out for a blank session id with no explicit file (nothing to resolve-poll), and unref the poll timer so headless serve shutdown is never held open by an unresolvable session. Claude-Session: https://claude.ai/code/session_01HA5g3X7wCakBttBpDru9Fp --------- Co-authored-by: kaynan <kaynan.camargo@terceiro-sky.com.br>
This commit is contained in:
co-authored by
kaynan
parent
527c692b71
commit
dc4fb2aa03
@@ -9,7 +9,11 @@ import {
|
||||
} from './transcript-line-decoders'
|
||||
import { decodeTranscriptStream } from './transcript-stream-lines'
|
||||
|
||||
export type ReadTranscriptResult = { messages: NativeChatMessage[] } | { error: string }
|
||||
export type ReadTranscriptResult =
|
||||
| { messages: NativeChatMessage[] }
|
||||
// notFound marks a retry-worthy miss (transcript not flushed to disk yet,
|
||||
// #8401) as opposed to a real parse/IO error callers surface immediately.
|
||||
| { error: string; notFound?: true }
|
||||
|
||||
export type ReadTranscriptOptions = ResolveSessionFileOptions & {
|
||||
/** Resolve directly to this file, skipping path discovery (used by tests). */
|
||||
@@ -30,7 +34,7 @@ export async function readNativeChatTranscript(
|
||||
): Promise<ReadTranscriptResult> {
|
||||
const filePath = options.filePath ?? (await resolveSessionFilePath(agent, sessionId, options))
|
||||
if (!filePath) {
|
||||
return { error: `No transcript found for ${agent} session ${sessionId}` }
|
||||
return { error: `No transcript found for ${agent} session ${sessionId}`, notFound: true }
|
||||
}
|
||||
try {
|
||||
if (agent === 'claude') {
|
||||
@@ -44,6 +48,11 @@ export async function readNativeChatTranscript(
|
||||
}
|
||||
return { error: `Unsupported agent for native chat transcript: ${agent}` }
|
||||
} catch (err) {
|
||||
// Why: ENOENT after a successful resolve is the same first-flush/rotation
|
||||
// race as an unresolved path — keep it retry-worthy (#8401).
|
||||
if ((err as NodeJS.ErrnoException | null)?.code === 'ENOENT') {
|
||||
return { error: errorMessage(err), notFound: true }
|
||||
}
|
||||
return { error: errorMessage(err) }
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user