mirror of
https://github.com/stablyai/orca.git
synced 2026-10-31 00:57:45 +00:00
* fix: preserve streamed turn duration after host restart * test: run restart duration regression in Node * fix: preserve provider receipt time in journal updates * test: verify provider receipt in Claude ownership assertion * fix(native-chat): time interrupted turns from real provider output, not flushes Shared-timeline checkpoints stamped the coalescer flush or retry time as the provider's latest proof of life, so a turn whose provider died before a delayed flush settled with an inflated duration after a host restart. Carry each delta's receipt time through the coalescer and timeline lane. * fix(native-chat): derive interrupted-turn duration from saved output A reply cut off by a server restart could show "Worked for 0s" because recovery ended the turn at the last heartbeat or the reply's first timestamp. End it at the latest time this owner's output was saved to chat history instead, derived in the existing journal fold from the row save time. Drops the per-frame receipt plumbing through every provider and buffer. * fix(chat): exclude host recovery revisions from provider activity Mark journal-open and connection-cleanup revisions with the existing recovery annotation so an interrupted turn does not include host downtime. Preserve buffered provider output and advance restart fixtures to the restart clock. * fix(native-chat): interrupt turns a replaced server left running When the managed server died mid-reply and a new one started on the same host, the cut-off turn settled `unverifiable` with no end whenever the old provider's identity could not be proven gone, and nothing ever revised it: the chat showed no turn status and the sidebar said "unconfirmed". The new server opens the chat records holding the host's instance lock, so every runtime that wrote them before is gone, and every provider it held lost its pipes with it. Record that at load (the fences those runtimes granted, the loaded owner's last renewal, how its runtime ended) and let the stale settle end those runtimes' running or unverifiable turns as interrupted at the owner's last saved output or renewal, with the existing Orca-stop row. Lease adjudication and the live / unverifiable / exited owner verdicts are unchanged. * fix(native-chat): require the record store for any journal open that settles Only an acquisition's open may skip it; a reader open without the store would leave a replaced server's turns unverifiable. * fix(native-chat): say a remote host's Orca restarted, not that it was closed A reply cut off when a remote host's Orca server stopped read "Orca on <host> was closed while this response was in progress." Nobody closed anything there: the server stopped and Orca started it again before the row could show. For a chat on a remote environment, cause quit now reads "Orca on <host> restarted while this response was in progress." The desktop's own quit keeps "was closed". Renderer-only; no wire change. * fix(native-chat): say a remote host's Orca stopped, true for a shared desktop too * test(native-chat): register Grok without compaction in the recovery-output rig Main's #26502 made Grok declare compaction; the rest-test rig adapter has no compact, so the registry refused it before any recovery case ran. * chore(native-chat): say a replaced runtime also revises an unverifiable turn
44 lines
2.1 KiB
TypeScript
44 lines
2.1 KiB
TypeScript
// What this runtime took over from the Orca runtimes that ran chats on this state directory before
|
|
// it. It loaded their records holding the profile's instance lock, so none of them is still running:
|
|
// every provider they started lost its pipes with them, and nothing it did after can reach the chat.
|
|
// That is the execution host's own proof that a turn those runtimes left running is over, whether or
|
|
// not the provider process itself can still be proven gone.
|
|
|
|
import type { AgentSessionOrcaStopCause } from '../../shared/agent-session-orca-stop'
|
|
import type { AgentSessionStoreState } from './agent-session-store-state'
|
|
|
|
export type AgentSessionReplacedRuntime = {
|
|
/** The highest fence a replaced runtime granted; every lower fence was granted before it. */
|
|
fence: number
|
|
/** The last renewal of the owner loaded at `fence`, its last proof of life. */
|
|
lastProvenAliveAt?: number
|
|
/** How the runtime holding the owner loaded at `fence` ended, when its record says. */
|
|
runtimeEnd?: AgentSessionOrcaStopCause
|
|
}
|
|
|
|
/** One entry per chat whose fences were all granted by runtimes this one replaced. */
|
|
export function agentSessionReplacedRuntimes(
|
|
state: AgentSessionStoreState,
|
|
args: { hostId: string; incarnation: string }
|
|
): ReadonlyMap<string, AgentSessionReplacedRuntime> {
|
|
const replaced = new Map<string, AgentSessionReplacedRuntime>()
|
|
for (const [sessionId, { lease }] of state.records) {
|
|
const owner = lease.ownerProcess
|
|
// A terminal's own agent never wrote through Orca's pipes; another host's runtime is not one this
|
|
// replaced; and an owner this runtime recorded (a host reinstalled in one process) may still be held.
|
|
if (
|
|
lease.claimStatus === 'conflicted' ||
|
|
(owner && (owner.hostId !== args.hostId || owner.runtime === args.incarnation))
|
|
) {
|
|
continue
|
|
}
|
|
const runtimeEnd = owner?.runtime ? state.runtimeEnds?.get(owner.runtime) : undefined
|
|
replaced.set(sessionId, {
|
|
fence: lease.runtimeFence,
|
|
...(owner ? { lastProvenAliveAt: lease.lastRenewedAt } : {}),
|
|
...(runtimeEnd ? { runtimeEnd } : {})
|
|
})
|
|
}
|
|
return replaced
|
|
}
|