Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
15 commits
Select commit Hold shift + click to select a range
570b677
feat(native-chat): a status for a message waiting on an exit Orca cou…
brennanb2025 Oct 1, 2026
20e5651
fix(native-chat): a message after a Stop whose exit was unproven retr…
brennanb2025 Oct 1, 2026
53172a3
fix(native-chat): every operation that reaches the agent finishes an …
brennanb2025 Oct 1, 2026
c0e87a3
test(native-chat): type the option-change envelope in the unproven-st…
brennanb2025 Oct 1, 2026
53118b3
fix(native-chat): the stop that proves the exit hands over the messag…
brennanb2025 Oct 1, 2026
7f855c4
docs(native-chat): say who retries a wind-down a Stop leaves owed
brennanb2025 Oct 1, 2026
bc4af0d
fix(native-chat): a waiting message retries the unproven stop once, a…
brennanb2025 Oct 1, 2026
baab367
fix(native-chat): only a retry continues an owed stop; a new close is…
brennanb2025 Oct 1, 2026
a8d87c3
fix(native-chat): a held message always says why it waits
brennanb2025 Oct 1, 2026
e10a102
fix(native-chat): an option change waits only on an unproven exit, no…
brennanb2025 Oct 1, 2026
ad83641
test(native-chat): type the unproven-stop test's envelope fields by t…
brennanb2025 Oct 1, 2026
d9771b9
Merge commit '56c7642aae2' into brennanb2025/send-after-unproven-stop
brennanb2025 Oct 1, 2026
b2390ae
Merge remote-tracking branch 'origin/main' into brennanb2025/send-aft…
brennanb2025 Oct 1, 2026
5ed0911
test(native-chat): a message after a Codex Stop whose exit was unprov…
brennanb2025 Oct 1, 2026
90dcb74
Merge remote-tracking branch 'origin/main' into brennanb2025/send-aft…
brennanb2025 Oct 1, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ import type { StructuredAgentSessionAttachContext } from './structured-agent-ses
import { attachStructuredAgentSessionUnderSerialize } from './structured-agent-session-attach-orchestration'
import { failedCreateRefusal } from './structured-agent-session-failed-create-refusal'
import { adapterSupportsRecord } from './structured-agent-session-provider-support'
import {
finishOwedStructuredAgentSessionWindDownUnderSerialize,
type StructuredAgentSessionLifetimeContext
} from './structured-agent-session-host-lifetime'
import {
structuredAgentSessionResumeOperationId,
structuredAgentSessionResumeParams
Expand All @@ -48,6 +52,48 @@ export type StructuredAgentSessionResumeOutcome =
/** The attach's caller key: the ledger row a start settles is Orca's own. */
const AGENT_START_CALLER_KEY = 'trusted-local:agent-start'

/**
* Every operation that reaches the provider finishes a stop an earlier attempt left owed first: the
* child that stop could not prove gone takes no input, and none may start beside it. Still
* unproven, the operation is refused with the exit `unverifiable`, never assumed `exited`.
*/
export async function finishOwedStructuredAgentSessionStop(
context: StructuredAgentSessionLifetimeContext,
sessionId: string
): Promise<StructuredAgentSessionResumeOutcome> {
if (await finishOwedStructuredAgentSessionWindDownUnderSerialize(context, sessionId)) {
return { ok: true }
}
return {
ok: false,
refusal: refuse(
'agent_session_ownership_unknown',
{ reason: 'previousExitUnverifiable', ownerVerdict: 'unverifiable' },
"Orca could not prove this chat's previous agent process exited."
)
}
}

/** The same, for an operation the running child performs, which starts none: only an exit still
* unproven, its child still on record, holds it back. A proven exit owes only bookkeeping, which
* gates no such operation: its retry was reported, and the operation goes on as with none owed. */
export async function finishOwedStructuredAgentSessionStopForProviderWrite(
context: StructuredAgentSessionLifetimeContext,
sessionId: string
): Promise<StructuredAgentSessionResumeOutcome> {
const settled = await finishOwedStructuredAgentSessionStop(context, sessionId)
return settled.ok || context.sessions.get(sessionId)?.child ? settled : { ok: true }
}

export function isStructuredAgentSessionPreviousExitUnverifiable(
refusal: AgentSessionWireRefusal
): boolean {
return (
refusal.code === 'agent_session_ownership_unknown' &&
refusal.details?.reason === 'previousExitUnverifiable'
)
}

/**
* Gives the session a provider child if it has none, for a caller inside its serialize — which is
* what makes "if it has none" exact: two askers run this in turn, and the second finds the first
Expand All @@ -58,6 +104,10 @@ export async function ensureStructuredAgentSessionAgent(
sessionId: string,
startedFor?: string
): Promise<StructuredAgentSessionResumeOutcome> {
const settled = await finishOwedStructuredAgentSessionStop(context, sessionId)
if (!settled.ok) {
return settled
}
if (context.sessions.get(sessionId)?.child) {
return { ok: true }
}
Expand Down
Loading
Loading