mirror of
https://github.com/stablyai/orca.git
synced 2026-09-25 16:02:38 +00:00
* fix(native-chat): stop the spinner on a not-yet-flushed transcript A brand-new agent session can take minutes to write its first JSONL line, and one that is never prompted never writes it at all. The host emitted no stream frame until the file resolved, so every native-chat client sat on a bare spinner with the composer enabled but the transcript blank -- forever, in the never-prompted case. The resolve poll now reports the transcript as pending after a short grace, and both host handlers emit a `pending: true` snapshot. It is deliberately not a plain empty snapshot: an empty window sold as a settled read would capture over retained history and unblock consumers that require a trustworthy transcript (the launch-draft adoption would re-offer a prompt the agent may already have taken). Clients render it as the "start a chat" empty state while keeping the read unsettled -- `awaiting-transcript` on mobile, an `awaiting` read phase on desktop, which also stops the seed loop expiring into an error card for a session that is simply new. New optional field only, so older clients ignore it and still stop spinning. * fix(native-chat): negotiate pending transcript frames
75 lines
2.3 KiB
TypeScript
75 lines
2.3 KiB
TypeScript
import type {
|
|
AgentType,
|
|
NativeChatMessage,
|
|
NativeChatTurnLifecycle
|
|
} from '../../shared/native-chat-types'
|
|
|
|
// notFound marks a not-yet-on-disk miss (retry-worthy) vs a real read/parse error (#8401).
|
|
export type NativeChatReadSessionResult =
|
|
| {
|
|
messages: NativeChatMessage[]
|
|
lifecycle?: NativeChatTurnLifecycle
|
|
}
|
|
| { error: string; notFound?: true }
|
|
|
|
/** Messages appended to a live-tailed transcript since the previous emit. */
|
|
export type NativeChatAppendedMessages = NativeChatMessage[]
|
|
|
|
export type NativeChatSubscriptionFrame =
|
|
| {
|
|
type: 'snapshot'
|
|
messages: NativeChatMessage[]
|
|
hasMore: boolean
|
|
error?: string
|
|
lifecycle?: NativeChatTurnLifecycle
|
|
/** No transcript exists behind this window yet — render it, but do not
|
|
* treat it as a settled read of the session's history. */
|
|
pending?: boolean
|
|
}
|
|
| {
|
|
type: 'replacement'
|
|
messages: NativeChatMessage[]
|
|
hasMore: boolean
|
|
lifecycle?: NativeChatTurnLifecycle
|
|
}
|
|
| {
|
|
type: 'appended'
|
|
messages: NativeChatMessage[]
|
|
lifecycle?: NativeChatTurnLifecycle
|
|
}
|
|
|
|
/** Wire payload for the `nativeChat:appended` push channel. */
|
|
export type NativeChatAppendedPayload = {
|
|
subscriptionId: string
|
|
frame: NativeChatSubscriptionFrame
|
|
}
|
|
|
|
export type NativeChatSubscribeArgs = {
|
|
/** Unique per-caller id, echoed on every append so multiple live panes in
|
|
* one renderer don't cross-talk. */
|
|
subscriptionId: string
|
|
agent: AgentType
|
|
sessionId: string
|
|
/** Authoritative transcript path from the agent hook (providerSession). */
|
|
transcriptPath?: string
|
|
/** First snapshot size; later readSession calls grow this for pagination. */
|
|
limit?: number
|
|
}
|
|
|
|
export type NativeChatApi = {
|
|
/** Read the on-disk transcript for an agent + session id, windowed to the most recent `limit`
|
|
* turns. `transcriptPath` is the hook-reported authoritative path, preferred over the id glob. */
|
|
readSession: (
|
|
agent: AgentType,
|
|
sessionId: string,
|
|
limit?: number,
|
|
transcriptPath?: string
|
|
) => Promise<NativeChatReadSessionResult>
|
|
/** Live-tail a transcript. The first frame is a bounded race-safe snapshot;
|
|
* later frames contain only newly appended messages. */
|
|
subscribe: (
|
|
args: NativeChatSubscribeArgs,
|
|
onFrame: (frame: NativeChatSubscriptionFrame) => void
|
|
) => () => void
|
|
}
|