@rynx-ai/runtime 0.1.11-beta.21 → 0.1.11-beta.23

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -9,7 +9,7 @@ export type { CodexCapabilities, CapabilityResult, CodexRuntimeStatus, } from ".
9
9
  export type { ClaudeForkIntent, CodexSessionStore, CodexSessionRecord, } from "./codex-session-store.js";
10
10
  export { RunnerManager, TerminalOpenError } from "./runner/manager.js";
11
11
  export type { RunnerManagerOptions, RunnerSessionContext, RunnerSessionContextProvider, OpenTerminalOptions, ParentTerminal, } from "./runner/manager.js";
12
- export type { InjectOutcome, TerminalOpenErrorCode, TerminalRole } from "./runner/protocol.js";
12
+ export type { InjectOutcome, InjectResult, TerminalOpenErrorCode, TerminalRole } from "./runner/protocol.js";
13
13
  export type { ResolveInteractionResult, RuntimeInteractionEvent, RuntimeInteractionListener, } from "./interactions.js";
14
14
  export { probeRuntimeStatus } from "./runtime-status.js";
15
15
  export { listRuntimeModels } from "./models-catalog.js";
@@ -7,11 +7,11 @@
7
7
  * per-thread capabilities, answered against the same backend so the parent never
8
8
  * needs an app-server of its own.
9
9
  */
10
- import { type AgentCapabilities, type ResolvedExecutionSnapshot, type RuntimeUserInput, type SessionInteractionResolution, type SessionWorkspaceSnapshot } from "@rynx-ai/core";
10
+ import { type AgentCapabilities, type LiveSessionFailure, type ResolvedExecutionSnapshot, type RuntimeUserInput, type SessionInteractionResolution, type SessionWorkspaceSnapshot } from "@rynx-ai/core";
11
11
  import type { SessionEvent } from "@rynx-ai/core";
12
12
  import type { TerminalInjector } from "../claude/native-integration.js";
13
13
  import type { ResolveInteractionResult } from "../interactions.js";
14
- import { type InjectOutcome } from "./protocol.js";
14
+ import { type InjectResult } from "./protocol.js";
15
15
  import type { ChildTransport } from "./transport.js";
16
16
  /** Re-target the mirror to a freshly minted rynx session (claude `/clear`·`/fork`)
17
17
  * + record the terminal transfer with the daemon. The child owns the transport,
@@ -38,14 +38,23 @@ interface LiveCodexProvider {
38
38
  execution: ResolvedExecutionSnapshot;
39
39
  retargetMirror?: RetargetMirror;
40
40
  }): Promise<boolean>;
41
+ /** Known-thread Codex/Traex resume only: start the independent transcript
42
+ * observer after the replacement TUI has launched. Fresh discovery connects
43
+ * its listener before launch and this method is an idempotent no-op. */
44
+ startLiveCodexObserver?(localThreadId: string): void;
41
45
  /** `null` delegates the deadline to RunnerManager. */
42
46
  waitLiveReady?(localThreadId: string, timeoutMs?: number | null): Promise<boolean>;
43
47
  /** `null` delegates the deadline to RunnerManager. */
44
48
  waitTerminalReady?(localThreadId: string, timeoutMs?: number | null): Promise<boolean>;
45
49
  liveSessionError?(localThreadId: string): string | undefined;
50
+ liveSessionFailure?(localThreadId: string): LiveSessionFailure | undefined;
46
51
  failLiveStartup?(localThreadId: string, error: Error): boolean;
47
52
  failLiveSession?(localThreadId: string, error: Error): boolean;
48
- injectMessage?(localThreadId: string, input: RuntimeUserInput | string): Promise<InjectOutcome>;
53
+ /** Codex/Traex auxiliary Terminal lifecycle: settle the current Turn, stop
54
+ * observer/forwarder, and evict the owned app-server while preserving the
55
+ * durable native-session binding for a later cold resume. */
56
+ teardownLiveCodexSession?(localThreadId: string, error?: Error): boolean;
57
+ injectMessage?(localThreadId: string, input: RuntimeUserInput | string): Promise<InjectResult>;
49
58
  interruptLive?(localThreadId: string): Promise<boolean>;
50
59
  stopLiveCodexSession?(localThreadId: string, opts?: {
51
60
  deferClaudeInteractionCleanup?: boolean;
@@ -84,6 +93,8 @@ export declare class RunnerSession {
84
93
  private readonly cancelledTerminalOpens;
85
94
  /** Session ids with a live codex forwarder started here (stopped on shutdown). */
86
95
  private readonly liveIds;
96
+ /** Provider name retained for asynchronous Terminal-exit diagnostics. */
97
+ private readonly liveRuntimes;
87
98
  private shuttingDown;
88
99
  constructor({ transport, executor, onShutdown }: RunnerSessionDeps);
89
100
  private handle;
@@ -96,10 +107,10 @@ export declare class RunnerSession {
96
107
  * the new session stays injectable. */
97
108
  private mirrorChannel;
98
109
  /**
99
- * Eagerly bring up a session's codex-native live view: start the persistent
100
- * observer connection before launching the detached Codex TUI. Fresh-session
101
- * thread discovery then continues in the background while the first
102
- * injection waits for the same bridge state.
110
+ * Eagerly bring up a session's codex-native live view. Fresh sessions connect
111
+ * the discovery listener before launching the detached TUI; known-thread
112
+ * resumes preload first, launch the replacement TUI, then start their
113
+ * persistent observer in the background.
103
114
  */
104
115
  private ensureLive;
105
116
  private monitorCodexThreadStartup;
@@ -1,8 +1,48 @@
1
+ /**
2
+ * Child-side runner session. One per runner process (i.e. per session); owns the
3
+ * single execution backend via the injected executor (a {@link LocalAgentHost}
4
+ * in production, which in a per-session process holds exactly one backend). It
5
+ * translates inbound {@link ToChild} control messages into the live co-drive
6
+ * surface (bring up the session's forwarder + TUI, inject / interrupt turns) and
7
+ * per-thread capabilities, answered against the same backend so the parent never
8
+ * needs an app-server of its own.
9
+ */
10
+ import { AgentRuntimeError, } from "@rynx-ai/core";
1
11
  import { TerminalRegistry } from "../terminal/registry.js";
2
- import { toWireError } from "./protocol.js";
3
- import { isManagedNativeProvider } from "./startup-policy.js";
12
+ import { toWireError, } from "./protocol.js";
13
+ import { isCodexLineageProvider } from "./startup-policy.js";
4
14
  /** Maximum time for a fresh Codex-lineage TUI to publish its native thread. */
5
15
  const CODEX_THREAD_START_TIMEOUT_MS = 30_000;
16
+ function providerDisplayName(runtime) {
17
+ return runtime === "traex" ? "Traex" : runtime === "claude" ? "Claude" : "Codex";
18
+ }
19
+ function providerFailure(provider, localThreadId, fallback) {
20
+ return provider.liveSessionFailure?.(localThreadId) ?? {
21
+ message: provider.liveSessionError?.(localThreadId) ?? fallback.message,
22
+ code: fallback.code,
23
+ statusCode: 503,
24
+ };
25
+ }
26
+ function nativePhaseError(runtime, code, phase, cause) {
27
+ const detail = cause === undefined
28
+ ? ""
29
+ : `: ${cause instanceof Error ? cause.message : String(cause)}`;
30
+ return new AgentRuntimeError(`${providerDisplayName(runtime)} ${phase}${detail}`, 503, code);
31
+ }
32
+ function terminalFailureDetail(pane) {
33
+ // A captured pane is a screen snapshot, not stderr. Do not present ordinary
34
+ // TUI chrome ("Working", shortcuts, model/status bars) as the process's exit
35
+ // cause. Retain only lines that plausibly carry an actual startup/runtime
36
+ // failure; the structured error still reports the proven pane exit itself.
37
+ return pane
38
+ .split("\n")
39
+ .map((line) => line.trim())
40
+ .filter(Boolean)
41
+ .filter((line) => /\b(?:error|fatal|failed|failure|panic|refused|denied|unauthorized|invalid)\b/i.test(line))
42
+ .slice(-6)
43
+ .join(" ")
44
+ .slice(-1_000);
45
+ }
6
46
  const TRAEX_STARTUP_WATCH_MS = 20_000;
7
47
  const TRAEX_STARTUP_POLL_MS = 100;
8
48
  const TRAEX_AUTHORIZATION_POLL_MS = 500;
@@ -38,6 +78,8 @@ export class RunnerSession {
38
78
  cancelledTerminalOpens = new Set();
39
79
  /** Session ids with a live codex forwarder started here (stopped on shutdown). */
40
80
  liveIds = new Set();
81
+ /** Provider name retained for asynchronous Terminal-exit diagnostics. */
82
+ liveRuntimes = new Map();
41
83
  shuttingDown = false;
42
84
  constructor({ transport, executor, onShutdown }) {
43
85
  this.transport = transport;
@@ -159,10 +201,10 @@ export class RunnerSession {
159
201
  return { emit, retarget };
160
202
  }
161
203
  /**
162
- * Eagerly bring up a session's codex-native live view: start the persistent
163
- * observer connection before launching the detached Codex TUI. Fresh-session
164
- * thread discovery then continues in the background while the first
165
- * injection waits for the same bridge state.
204
+ * Eagerly bring up a session's codex-native live view. Fresh sessions connect
205
+ * the discovery listener before launching the detached TUI; known-thread
206
+ * resumes preload first, launch the replacement TUI, then start their
207
+ * persistent observer in the background.
166
208
  */
167
209
  async ensureLive(msg) {
168
210
  const provider = this.liveProvider;
@@ -174,34 +216,38 @@ export class RunnerSession {
174
216
  retargetMirror: retarget,
175
217
  });
176
218
  if (!started) {
219
+ const runtime = msg.execution?.provider;
177
220
  this.transport.send({
178
221
  t: "live.ready",
179
222
  reqId: msg.reqId,
180
223
  localThreadId: msg.localThreadId,
181
224
  ok: false,
182
- error: provider.liveSessionError?.(msg.localThreadId)
183
- ?? "live provider did not start",
225
+ error: providerFailure(provider, msg.localThreadId, {
226
+ runtime,
227
+ code: "native_start_failed",
228
+ message: `${providerDisplayName(runtime)} native session did not start`,
229
+ }),
184
230
  });
185
231
  return;
186
232
  }
187
233
  this.liveIds.add(msg.localThreadId);
188
- // Codex-lineage startup is phase-bounded: app-server readiness is
189
- // handled by its channel, then the already-connected
190
- // observer gets a full window for the TUI's thread/started event.
191
- // Claude retains the parent-owned SessionStart deadline.
192
234
  const runtime = msg.execution?.provider;
193
- // Do not add another provider-local deadline around resume/preload. The
194
- // parent control request remains bounded for process safety; fresh-thread
195
- // discovery owns the separate 30s phase below.
196
- const terminalReadyTimeoutMs = isManagedNativeProvider(runtime) ? null : undefined;
235
+ if (runtime)
236
+ this.liveRuntimes.set(msg.localThreadId, runtime);
197
237
  // Launch the TUI attached to the already-bound thread. Re-launch when the
198
238
  // pane is absent OR its process has died (`isAlive` probes `#{pane_dead}`) — so a
199
239
  // reconnect after the TUI exited restarts it instead of skipping (a
200
240
  // launched-once guard would leave a dead "Pane is dead" husk forever).
201
241
  if (!this.terminals.get(`${msg.localThreadId}-main`)?.isAlive()) {
202
- const terminalReady = provider.waitTerminalReady
203
- ? await provider.waitTerminalReady(msg.localThreadId, terminalReadyTimeoutMs)
204
- : true;
242
+ // Codex/Traex `ensureLiveCodexSession` already performs the backend
243
+ // preload for an existing thread. Match the native lifecycle: launch
244
+ // the TUI immediately after that preload and let the observer subscribe
245
+ // in the background. Fresh sessions likewise launch before discovery.
246
+ const terminalReady = isCodexLineageProvider(runtime)
247
+ ? true
248
+ : provider.waitTerminalReady
249
+ ? await provider.waitTerminalReady(msg.localThreadId)
250
+ : true;
205
251
  if (!terminalReady) {
206
252
  const providerName = runtime === "traex" ? "Traex" : runtime === "codex" ? "Codex" : "Provider";
207
253
  this.transport.send({
@@ -209,14 +255,31 @@ export class RunnerSession {
209
255
  reqId: msg.reqId,
210
256
  localThreadId: msg.localThreadId,
211
257
  ok: false,
212
- error: runtime === "codex" || runtime === "traex"
213
- ? provider.liveSessionError?.(msg.localThreadId)
214
- ?? `${providerName} app-server was ready, but its existing thread could not be resumed for the Terminal`
215
- : "Provider thread was not ready for Terminal resume",
258
+ error: providerFailure(provider, msg.localThreadId, {
259
+ runtime,
260
+ code: "native_resume_failed",
261
+ message: runtime === "codex" || runtime === "traex"
262
+ ? `${providerName} app-server was ready, but its existing session could not be resumed for the Terminal`
263
+ : "Provider thread was not ready for Terminal resume",
264
+ }),
216
265
  });
217
266
  return;
218
267
  }
219
- await this.launchCodexPane(msg.localThreadId, msg.cols, msg.rows);
268
+ try {
269
+ await this.launchCodexPane(msg.localThreadId, msg.cols, msg.rows);
270
+ }
271
+ catch (error) {
272
+ throw error instanceof AgentRuntimeError
273
+ ? error
274
+ : nativePhaseError(runtime, "native_terminal_launch_failed", "Terminal failed to launch", error);
275
+ }
276
+ }
277
+ // Omnigent's known-thread path launches `codex --remote resume` first,
278
+ // then creates `_codex_forward_known_thread` as background work. Keep the
279
+ // ordering explicit here: observer connection failure cannot delay or
280
+ // reject cold-resume admission.
281
+ if (isCodexLineageProvider(runtime)) {
282
+ provider.startLiveCodexObserver?.(msg.localThreadId);
220
283
  }
221
284
  let ready = true;
222
285
  if (runtime === "codex" || runtime === "traex") {
@@ -226,12 +289,15 @@ export class RunnerSession {
226
289
  this.monitorCodexThreadStartup(msg.localThreadId, runtime, provider);
227
290
  }
228
291
  else if (msg.waitForReady !== false && provider.waitLiveReady) {
229
- ready = await provider.waitLiveReady(msg.localThreadId, terminalReadyTimeoutMs);
292
+ ready = await provider.waitLiveReady(msg.localThreadId);
230
293
  }
231
294
  const readinessError = ready
232
295
  ? undefined
233
- : provider.liveSessionError?.(msg.localThreadId)
234
- ?? "live session was not ready before timeout";
296
+ : providerFailure(provider, msg.localThreadId, {
297
+ runtime,
298
+ code: "native_readiness_failed",
299
+ message: `${providerDisplayName(runtime)} native session was not ready`,
300
+ });
235
301
  this.transport.send({
236
302
  t: "live.ready",
237
303
  reqId: msg.reqId,
@@ -246,7 +312,7 @@ export class RunnerSession {
246
312
  reqId: msg.reqId,
247
313
  localThreadId: msg.localThreadId,
248
314
  ok: false,
249
- error: error instanceof Error ? error.message : String(error),
315
+ error: toWireError(error),
250
316
  });
251
317
  }
252
318
  }
@@ -269,11 +335,15 @@ export class RunnerSession {
269
335
  : "";
270
336
  const detail = provider.liveSessionError?.(localThreadId)
271
337
  ?? (paneFailure
272
- ? `Provider terminal exited before session discovery: ${paneFailure}`
338
+ ? `${providerDisplayName(runtime)} Terminal exited before native session discovery: ${paneFailure}`
273
339
  : `${runtime === "traex" ? "Traex" : "Codex"} TUI did not publish thread/started within ${CODEX_THREAD_START_TIMEOUT_MS / 1_000}s after app-server and observer readiness`);
274
- this.failCodexThreadStartup(localThreadId, provider, new Error(detail));
340
+ this.failCodexThreadStartup(localThreadId, provider, new AgentRuntimeError(detail, 503, paneFailure
341
+ ? "native_terminal_exited_before_session"
342
+ : "native_thread_discovery_timeout"));
275
343
  }).catch((error) => {
276
- this.failCodexThreadStartup(localThreadId, provider, error instanceof Error ? error : new Error(String(error)));
344
+ this.failCodexThreadStartup(localThreadId, provider, error instanceof AgentRuntimeError
345
+ ? error
346
+ : nativePhaseError(runtime, "native_thread_discovery_failed", "native session discovery failed", error));
277
347
  });
278
348
  }
279
349
  /** Discovery failure is terminal for this partial native launch. Publish the
@@ -282,7 +352,7 @@ export class RunnerSession {
282
352
  * already-rejected readiness promise. */
283
353
  failCodexThreadStartup(localThreadId, provider, error) {
284
354
  if (!provider.failLiveStartup?.(localThreadId, error))
285
- return;
355
+ return false;
286
356
  const watcher = this.terminalWatchers.get(localThreadId);
287
357
  if (watcher)
288
358
  clearInterval(watcher);
@@ -294,24 +364,42 @@ export class RunnerSession {
294
364
  catch (closeError) {
295
365
  console.warn(`[runner] session=${localThreadId} failed to close native Terminal after startup failure: ${closeError instanceof Error ? closeError.message : String(closeError)}`);
296
366
  }
297
- provider.stopLiveCodexSession?.(localThreadId);
367
+ if (provider.teardownLiveCodexSession) {
368
+ provider.teardownLiveCodexSession(localThreadId);
369
+ }
370
+ else {
371
+ provider.stopLiveCodexSession?.(localThreadId);
372
+ }
298
373
  this.liveIds.delete(localThreadId);
374
+ return true;
299
375
  }
300
376
  async inject(msg) {
301
377
  const provider = this.liveProvider;
302
378
  try {
303
379
  const input = msg.input ?? msg.text;
304
- const outcome = (await provider.injectMessage?.(msg.localThreadId, input)) ?? "notLive";
380
+ const result = (await provider.injectMessage?.(msg.localThreadId, input))
381
+ ?? { outcome: "notLive" };
305
382
  // App-server injection is independent of the Terminal TUI startup, so it
306
383
  // must not cancel prompt handling for the pane that is still starting.
307
- const error = outcome === "injected" || outcome === "steered"
384
+ const error = result.outcome === "injected" || result.outcome === "steered"
308
385
  ? undefined
309
- : provider.liveSessionError?.(msg.localThreadId);
386
+ : providerFailure(provider, msg.localThreadId, {
387
+ code: result.outcome === "notReady"
388
+ ? "native_thread_not_ready"
389
+ : result.outcome === "notLive"
390
+ ? "native_session_not_live"
391
+ : "native_message_injection_failed",
392
+ message: result.outcome === "notReady"
393
+ ? "Native session was not ready for message injection"
394
+ : result.outcome === "notLive"
395
+ ? "Native session was not live for message injection"
396
+ : "Native message injection failed",
397
+ });
310
398
  this.transport.send({
311
399
  t: "injected",
312
400
  reqId: msg.reqId,
313
401
  localThreadId: msg.localThreadId,
314
- outcome,
402
+ result,
315
403
  ...(error ? { error } : {}),
316
404
  });
317
405
  }
@@ -320,8 +408,8 @@ export class RunnerSession {
320
408
  t: "injected",
321
409
  reqId: msg.reqId,
322
410
  localThreadId: msg.localThreadId,
323
- outcome: "failed",
324
- error: error instanceof Error ? error.message : String(error),
411
+ result: { outcome: "failed" },
412
+ error: toWireError(error),
325
413
  });
326
414
  }
327
415
  }
@@ -333,7 +421,7 @@ export class RunnerSession {
333
421
  ok = (await provider.interruptLive?.(msg.localThreadId)) ?? false;
334
422
  }
335
423
  catch (e) {
336
- error = e instanceof Error ? e.message : String(e);
424
+ error = toWireError(e);
337
425
  }
338
426
  this.transport.send({
339
427
  t: "interrupted",
@@ -376,15 +464,50 @@ export class RunnerSession {
376
464
  if (checking || this.shuttingDown || this.terminals.get(terminalId) !== terminal)
377
465
  return;
378
466
  checking = true;
379
- const probe = terminal.isAliveAsync?.() ?? Promise.resolve(terminal.isAlive());
380
- void probe.then((alive) => {
381
- if (alive || this.shuttingDown || this.terminals.get(terminalId) !== terminal)
467
+ const probe = terminal.lifecycleLivenessAsync
468
+ ? terminal.lifecycleLivenessAsync()
469
+ : terminal.livenessAsync
470
+ ? terminal.livenessAsync()
471
+ : terminal.isAliveAsync
472
+ ? terminal.isAliveAsync().then((alive) => alive ? "alive" : "dead")
473
+ : Promise.resolve(terminal.isAlive() ? "alive" : "dead");
474
+ void probe.then((liveness) => {
475
+ // A failed pane probe is inconclusive. Only Omnigent's lifecycle
476
+ // evidence — capture target gone or explicit pane_dead — may fail it.
477
+ if (liveness !== "dead" ||
478
+ this.shuttingDown ||
479
+ this.terminals.get(terminalId) !== terminal)
382
480
  return;
383
481
  clearInterval(timer);
384
482
  this.terminalWatchers.delete(localThreadId);
385
483
  this.cancelTraexStartupWatcher(localThreadId);
386
- this.liveProvider.failLiveSession?.(localThreadId, new Error("Provider terminal exited unexpectedly"));
387
- this.failCodexThreadStartup(localThreadId, this.liveProvider, new Error("Provider terminal exited before native thread discovery completed"));
484
+ const runtime = this.liveRuntimes.get(localThreadId);
485
+ const paneFailure = terminalFailureDetail(terminal.capturePane?.() ?? "");
486
+ const startupFailed = this.failCodexThreadStartup(localThreadId, this.liveProvider, nativePhaseError(runtime, "native_terminal_exited_before_session", "Terminal exited before native session discovery completed", paneFailure || undefined));
487
+ if (isCodexLineageProvider(runtime)) {
488
+ if (!startupFailed) {
489
+ try {
490
+ this.terminals.close(terminalId);
491
+ }
492
+ catch (closeError) {
493
+ console.warn(`[runner] session=${localThreadId} failed to close exited native Terminal: ${closeError instanceof Error ? closeError.message : String(closeError)}`);
494
+ }
495
+ const exitError = nativePhaseError(runtime, "native_terminal_exited", "Terminal exited unexpectedly", paneFailure || undefined);
496
+ if (this.liveProvider.teardownLiveCodexSession) {
497
+ this.liveProvider.teardownLiveCodexSession(localThreadId, exitError);
498
+ }
499
+ else {
500
+ this.liveProvider.failLiveSession?.(localThreadId, exitError);
501
+ this.liveProvider.stopLiveCodexSession?.(localThreadId);
502
+ }
503
+ this.liveIds.delete(localThreadId);
504
+ console.warn(`[runner] session=${localThreadId} ${providerDisplayName(runtime)} auxiliary Terminal exited; native runtime torn down for cold resume`);
505
+ }
506
+ return;
507
+ }
508
+ this.liveProvider.failLiveSession?.(localThreadId, nativePhaseError(runtime, "native_terminal_exited", "Terminal exited unexpectedly", paneFailure || undefined));
509
+ }).catch((error) => {
510
+ console.warn(`[runner] session=${localThreadId} native Terminal liveness probe failed: ${error instanceof Error ? error.message : String(error)}`);
388
511
  }).finally(() => {
389
512
  checking = false;
390
513
  });
@@ -469,6 +592,7 @@ export class RunnerSession {
469
592
  });
470
593
  }
471
594
  this.liveIds.clear();
595
+ this.liveRuntimes.clear();
472
596
  }
473
597
  /** Stop event forwarding, kill native terminals/hooks, then synchronously
474
598
  * scrub provider handoff files before the child process is allowed to exit. */
@@ -16,10 +16,10 @@
16
16
  * channel.
17
17
  */
18
18
  import { spawn as nodeSpawn, type ChildProcess } from "node:child_process";
19
- import { type AdmissionReservation, type AgentCapabilities, type AgentRuntimeId, type AppConfig, type CapabilityResult, type ModelListResponse, type ResolvedExecutionSnapshot, type RuntimeUserInput, type SessionInteractionResolution, type SessionEvent, type SessionWorkspaceSnapshot, type ThreadGoal } from "@rynx-ai/core";
19
+ import { type AdmissionReservation, type AgentCapabilities, type AgentRuntimeId, type AppConfig, type CapabilityResult, type ModelListResponse, type LiveSessionFailure, type InjectResult, type ResolvedExecutionSnapshot, type RuntimeUserInput, type SessionInteractionResolution, type SessionEvent, type SessionWorkspaceSnapshot, type ThreadGoal } from "@rynx-ai/core";
20
20
  import { type CodexSessionStore } from "../host.js";
21
21
  import type { ResolveInteractionResult } from "../interactions.js";
22
- import { type InjectOutcome, type TerminalOpenErrorCode, type TerminalRole } from "./protocol.js";
22
+ import { type TerminalOpenErrorCode, type TerminalRole } from "./protocol.js";
23
23
  /** Runtime-local context attached to one Session runner process. The provider is
24
24
  * intentionally generic: package-specific resources stay in the composition
25
25
  * root that implements this port. */
@@ -53,12 +53,13 @@ export interface RunnerManagerOptions {
53
53
  shutdownGraceMs?: number;
54
54
  /** Final bounded wait for child exit after SIGKILL. */
55
55
  shutdownKillGraceMs?: number;
56
- /** Max wait for a setup-pane launch acknowledgement. */
56
+ /** Max wait for a legacy setup-pane launch acknowledgement. Codex/Traex use
57
+ * their internal phase deadlines and are not wrapped by this timer. */
57
58
  liveStartTimeoutMs?: number;
58
59
  /** Max wait for a Provider-native thread to become ready. */
59
60
  liveReadyTimeoutMs?: number;
60
- /** Claude SessionStart deadline. Codex/Traex use `liveStartTimeoutMs` for the
61
- * pane/observer acknowledgement; their thread discovery is asynchronous. */
61
+ /** Claude SessionStart deadline. Codex/Traex have no parent-owned startup
62
+ * deadline; their app-server/discovery/injection phases report directly. */
62
63
  nativeLiveStartTimeoutMs?: number;
63
64
  /** Max wait for a native interrupt acknowledgement before reporting it
64
65
  * unproven. Callers may then use the explicit force-stop path. */
@@ -66,15 +67,13 @@ export interface RunnerManagerOptions {
66
67
  /** Max wait for an accepted owner TUI submission to become observable as a
67
68
  * mirrored response or a published native rotation. */
68
69
  terminalInputHandoffTimeoutMs?: number;
69
- /** Initial exponential backoff for retrying unproven terminal cleanup. */
70
- terminalInputCleanupRetryMs?: number;
71
70
  /** Injected for tests. */
72
71
  spawn?: typeof nodeSpawn;
73
72
  /** Injected for tests. Defaults to signaling the whole POSIX process group
74
73
  * created for the runner child, with direct-child fallback. */
75
74
  signalChild?: (child: ChildProcess, signal: NodeJS.Signals, processGroup: boolean) => void;
76
- /** Injected for tests. Kills and verifies the deterministic private tmux
77
- * server owned by a Session runner. */
75
+ /** Injected for tests. Best-effort close of the deterministic private tmux
76
+ * server owned by a Session runner; Omnigent does not verify/retry close. */
78
77
  terminateTerminalServer?: (terminalName: string) => boolean | Promise<boolean>;
79
78
  /** Injected tmux `#{window_activity}` reader. Returns epoch seconds. */
80
79
  terminalWindowActivityAt?: (terminalName: string) => number | null | Promise<number | null>;
@@ -175,7 +174,6 @@ export declare class RunnerManager implements AgentCapabilities {
175
174
  private readonly nativeLiveStartTimeoutMs;
176
175
  private readonly liveInterruptTimeoutMs;
177
176
  private readonly terminalInputHandoffTimeoutMs;
178
- private readonly terminalInputCleanupRetryMs;
179
177
  private reapPromise;
180
178
  private stopping;
181
179
  private stopPromise;
@@ -242,14 +240,14 @@ export declare class RunnerManager implements AgentCapabilities {
242
240
  private finishTerminalInputHandoff;
243
241
  private finishAllTerminalInputHandoffs;
244
242
  private preserveTerminalInputHandoffsAsActivity;
245
- private scheduleTerminalInputCleanupRetry;
246
243
  private expireTerminalInputHandoffs;
247
244
  /**
248
245
  * Eagerly bring up a session's codex-native live view (persistent forwarder +
249
246
  * detached `codex --remote` TUI) in its runner child, spawning the runner if
250
- * needed. Idempotent. For Codex-lineage fresh sessions, resolves after the
251
- * app-server/observer and pane are launched; thread discovery continues in
252
- * parallel with injection. Other providers retain their readiness gate.
247
+ * needed. Idempotent. For Codex-lineage sessions, resolves after backend
248
+ * preload and pane launch; known-thread observer attach and fresh-thread
249
+ * discovery continue in the background. Other providers retain their
250
+ * readiness gate.
253
251
  */
254
252
  ensureLiveSession(localThreadId: string, opts: {
255
253
  workspace: SessionWorkspaceSnapshot;
@@ -268,13 +266,14 @@ export declare class RunnerManager implements AgentCapabilities {
268
266
  }): Promise<boolean>;
269
267
  private requestLiveSession;
270
268
  lastLiveSessionError(localThreadId: string): string | undefined;
269
+ lastLiveSessionFailure(localThreadId: string): LiveSessionFailure | undefined;
271
270
  /**
272
271
  * Inject a user turn into a session's live codex thread — reference implementation's
273
272
  * single-writer web send (`turn/start` / `turn/steer`); the forwarder mirrors
274
273
  * all output. Resolves true when the app-server accepted the turn, false when
275
274
  * the session has no live forwarder (caller falls back to the run path).
276
275
  */
277
- injectMessage(localThreadId: string, input: RuntimeUserInput | string): Promise<InjectOutcome>;
276
+ injectMessage(localThreadId: string, input: RuntimeUserInput | string): Promise<InjectResult>;
278
277
  private injectMessageAdmitted;
279
278
  /**
280
279
  * Interrupt a session's active live turn — the web Stop button (codex