mirror of
https://github.com/stablyai/orca.git
synced 2026-10-25 16:03:53 +00:00
Six findings from the review of the structured Codex session, all of them about a session that is live in the map while the thing it names is not: - A prompt is addressed by its own approval id, not by the tool item it is about. A shell bridge re-asks per command under one parent item, and the second ask used to evict the first, leaving that turn blocked on a request nobody could answer. - A subagent runs its own thread over the same connection. Its `turn/started` no longer satisfies the root thread's waiter, and every event now carries the thread it actually came from. - Publication is one step at the end of acquire: the process identity is in hand, the child is still alive, and the attempt still owns the session. A failure reaps only its own child, so a replacement already published keeps running. - Codex talks before its thread is named. Those events wait in the acquisition window and are delivered in arrival order once the session is published, instead of being dropped. - A broken stdin pipe ends the transport rather than one write: the child is reaped and the owner learns its lease is worthless. - A death that reaches us through several listeners reports once, keeping the first cause rather than the generic exit that follows it.
38 lines
1.4 KiB
TypeScript
38 lines
1.4 KiB
TypeScript
// Starting or resuming the single Codex thread a structured session owns.
|
|
//
|
|
// The reply is verified before the caller registers the session, because a
|
|
// resume that lands on a different thread is a fork wearing a resume's name —
|
|
// recording it would make the durable handle chain lie about what this session
|
|
// actually proved.
|
|
|
|
import type { CodexAppServerConnection } from './codex-app-server-connection'
|
|
import { readCodexThreadId, readCodexThreadPath } from './codex-structured-thread-facts'
|
|
|
|
export type CodexOpenedThread = {
|
|
threadId: string
|
|
/** Rollout file Codex named, when it named one. */
|
|
historyPath: string | null
|
|
}
|
|
|
|
export async function openCodexThread(
|
|
connection: CodexAppServerConnection,
|
|
launch: { cwd: string; resumeThreadId: string | null },
|
|
timeoutMs: number | undefined
|
|
): Promise<CodexOpenedThread> {
|
|
const opened = await connection.request(
|
|
launch.resumeThreadId ? 'thread/resume' : 'thread/start',
|
|
launch.resumeThreadId
|
|
? { threadId: launch.resumeThreadId, cwd: launch.cwd }
|
|
: { cwd: launch.cwd },
|
|
{ timeoutMs }
|
|
)
|
|
const threadId = readCodexThreadId(opened)
|
|
if (!threadId) {
|
|
throw new Error('codex app-server did not name the thread it opened')
|
|
}
|
|
if (launch.resumeThreadId && threadId !== launch.resumeThreadId) {
|
|
throw new Error(`codex app-server resumed ${threadId} instead of ${launch.resumeThreadId}`)
|
|
}
|
|
return { threadId, historyPath: readCodexThreadPath(opened) }
|
|
}
|