mirror of
https://github.com/stablyai/orca.git
synced 2026-10-26 08:02:44 +00:00
* fix(native-chat): a provider's own retry progress is quoted in its retry row A retry row whose fact carries a detail the provider wrote for a person now quotes it, the same way a rejected message or failed compaction does, so the row says how the retry is going. A log detail still stays out of the sentence. * fix(native-chat): a Codex stream retry is one warning row that updates in place An error Codex says it will retry used to fall through to the generic frame row: red, and a new row for every attempt. It now writes one providerRetrying row per retry run, warning-toned, revised by each attempt with Codex's own progress sentence. A run is the retry frames of one turn with nothing else the thread journals between them; every attempt still publishes, so the idle sweep keeps seeing activity. Errors Codex will not retry are unchanged. * fix(native-chat): a Codex retry row says it is retrying and keeps the frame behind Details The quoted retry sentence leads with "is retrying", which holds for any provider's progress text. The Codex retry row also keeps the whole bounded frame behind the row's Details, as the generic row did, so Codex's additionalDetails stays available to diagnose a retry. * test(native-chat): a Codex retry re-handled after backpressure keeps its one row Pins the run being opened before the write: a first attempt whose publish is refused and is handed back must revise the row it already wrote, not open a second run. Also stops the fixture claiming Codex sends an idle thread status beside each retry, which the app server does not do. * perf(native-chat): a Codex frame with no retry run open is not classified Ending a retry run classified every non-retry frame, and classifying walks the whole payload: every streaming delta, and every large item/completed, paid a walk about as costly as parsing the frame. Only a thread with a run open needs the answer, so the classification now runs only there. * fix(native-chat): each Codex retry attempt is its own warning row, with what failed on its second line A stream error Codex says it will retry is written as its own warning row with a providerRetrying fact, under the same per-frame identity every Codex frame row gets. The host no longer tracks retry runs or rewrites one row in place, so there is no run state to open, end or clear, and no frame has to be classified to end a run. Every attempt publishes, which keeps renewing the idle clock while Codex retries. Codex's additionalDetails, which its own UI shows under the progress message, is kept on the fact as the retry's cause and printed on the row's second line. Errors Codex will not retry are unchanged. * fix(native-chat): a transcript draws only the latest row of a provider retry run The shared structured message projection, which both the desktop and the mobile transcript read, collapses a run of retry rows into its latest row. A run is retry rows from the same agent with no other drawn row between them; a row that draws nothing, or a queued send drawn after the conversation, does not split it. The earlier attempts stay in the journal. * test(native-chat): the retry-run render test uses the message list's current props * fix(native-chat): a Codex frame row is named for its connection, so a later one never revises it Frame rows were named provider-frame:codex:<n> from a counter that starts over with every connection, so the first frame row after a reconnect in the same session revised an earlier connection's row in place, at its old spot. Each connection's frame rows now carry the acquisition generation, minted once before the translator is built: provider-frame:codex:<generation>:<n>. Rows already written keep their identities. * fix(native-chat): agents retrying at once each keep one row, read from the row's own agent * test(native-chat): a reconnect's Codex rows are named for the acquisition that received them * fix(native-chat): a retry run is one agent's, so another agent's row never splits it Each agent's rows are drawn apart: the session's own rows are the conversation, and a subagent's rows open in that subagent's section. Splitting a run on any other row in the flat list left two adjacent retry rows on screen whenever another agent wrote between two attempts: a subagent finishing a command while the session reconnected, or the session working while a subagent reconnected. A run is now per agent: an agent's retry rows with none of its own other rows between them, drawn as its latest. * test(native-chat): a subagent's retry run is checked in its own section, and the run rule's words say same agent * test(mobile): the retry-rows test typechecks, so the test ratchet keeps checking it
70 lines
3.3 KiB
TypeScript
70 lines
3.3 KiB
TypeScript
import type { AgentJournalItemIdentity } from '../../shared/agent-session-journal-types'
|
|
import type { CodexDispatchRequestOrigin } from './codex-structured-dispatch-echo'
|
|
import type { AgentSessionDeltaCoalescerDeps } from '../native-chat/agent-session-wire/agent-session-delta-coalescer'
|
|
import type { StructuredAgentSessionEventSink } from '../native-chat/agent-session-wire/structured-agent-session-event-sink'
|
|
import type { CodexStructuredSessionEvent } from './codex-structured-session-adapter'
|
|
import type { CodexSubagentExecutions } from './codex-subagent-executions'
|
|
import type { StructuredAgentSessionCommandRun } from '../native-chat/agent-session-wire/structured-agent-session-adapter'
|
|
|
|
export type CodexJournalTranslatorDeps = {
|
|
sink: StructuredAgentSessionEventSink
|
|
/** Names this connection in frame-row identities, so a later connection never revises its rows. */
|
|
acquisitionId?: string
|
|
/** Keys restored lifecycle rows to the live identity; without it history restore skips them. */
|
|
sessionId?: string
|
|
now?: () => number
|
|
bindPromptItemId?: (
|
|
journalItemId: string,
|
|
threadId: string,
|
|
promptKey: string,
|
|
turnId?: string | null
|
|
) => void
|
|
clearPromptTurn?: (threadId: string, turnId: string) => void
|
|
/** Settles a send's identity off the echoed user message, using the very
|
|
* identity the journal row carries so a replay computes the same key. */
|
|
onUserMessageEcho?: (clientMessageId: string, identity: AgentJournalItemIdentity) => void
|
|
primaryThreadId?: () => string | null
|
|
/** Codex reported the primary thread is not running while no turn is open here.
|
|
* A send whose dispatch was never answered is owed nothing after this. */
|
|
onPrimaryThreadStoppedRunning?: () => void
|
|
/** Submission origin for one exact client message still awaiting its echo. */
|
|
dispatchRequestOrigin?: (clientMessageId: string) => CodexDispatchRequestOrigin | null
|
|
subagentExecutions?: CodexSubagentExecutions
|
|
coalesceMs?: number
|
|
maxRetainedBytes?: number
|
|
schedule?: AgentSessionDeltaCoalescerDeps['schedule']
|
|
}
|
|
|
|
export type CodexJournalTranslator = {
|
|
handle: (event: CodexStructuredSessionEvent) => CodexJournalTranslationAdmission
|
|
/** Before Orca sends a conversation command: the next primary turn carries it out, and that
|
|
* turn's end is the command's. */
|
|
beginCommand: (command: StructuredAgentSessionCommandRun) => void
|
|
/** The command was never taken. */
|
|
forgetCommand: (turnId: string) => void
|
|
/** The provider turn a Stop naming `turnId` interrupts; undefined while a command has none. */
|
|
commandProviderTurnId: (turnId: string) => string | undefined
|
|
cancelPrompt: (journalItemId: string) => CodexJournalTranslationAdmission
|
|
restoreThread: (
|
|
threadId: string,
|
|
thread: Record<string, unknown>
|
|
) => CodexJournalTranslationAdmission
|
|
resolvePrompt: (journalItemId: string) => void
|
|
flush: () => void
|
|
dispose: () => void
|
|
}
|
|
|
|
export type CodexJournalTranslationAdmission =
|
|
| { accepted: true }
|
|
| { accepted: false; reason: 'backpressure' | 'failed' | 'closed' | 'untranslated' }
|
|
|
|
export type CodexItemTranslation =
|
|
| { handled: false }
|
|
| {
|
|
handled: true
|
|
admission: CodexJournalTranslationAdmission
|
|
dispatchEcho?: { clientMessageId: string; providerIdentity: AgentJournalItemIdentity }
|
|
}
|
|
|
|
export const CODEX_JOURNAL_ADMITTED = { accepted: true } as const
|