@rynx-ai/runtime 0.1.11-beta.4 → 0.1.11-beta.41

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.
Files changed (65) hide show
  1. package/dist/claude/executor.d.ts +19 -5
  2. package/dist/claude/executor.js +56 -12
  3. package/dist/claude/models.d.ts +0 -5
  4. package/dist/claude/models.js +1 -7
  5. package/dist/claude/native-bridge.d.ts +103 -1
  6. package/dist/claude/native-bridge.js +445 -30
  7. package/dist/claude/native-hook-main.js +81 -1
  8. package/dist/claude/native-hooks.js +7 -0
  9. package/dist/claude/native-integration.d.ts +178 -26
  10. package/dist/claude/native-integration.js +1528 -170
  11. package/dist/claude/session-status.d.ts +39 -0
  12. package/dist/claude/session-status.js +163 -0
  13. package/dist/claude/transcript-clone.d.ts +18 -0
  14. package/dist/claude/transcript-clone.js +497 -0
  15. package/dist/claude/transcript.d.ts +27 -4
  16. package/dist/claude/transcript.js +158 -47
  17. package/dist/codex-app-server/client.d.ts +10 -6
  18. package/dist/codex-app-server/client.js +67 -15
  19. package/dist/codex-app-server/forwarder.d.ts +92 -3
  20. package/dist/codex-app-server/forwarder.js +532 -57
  21. package/dist/codex-app-server/mapping.d.ts +3 -6
  22. package/dist/codex-app-server/mapping.js +206 -36
  23. package/dist/codex-app-server/mcp-startup.d.ts +13 -0
  24. package/dist/codex-app-server/mcp-startup.js +63 -0
  25. package/dist/codex-app-server/process-registry.d.ts +36 -0
  26. package/dist/codex-app-server/process-registry.js +320 -0
  27. package/dist/codex-app-server/protocol.d.ts +64 -7
  28. package/dist/codex-app-server/ws-channel.d.ts +7 -0
  29. package/dist/codex-app-server/ws-channel.js +104 -28
  30. package/dist/codex-home.d.ts +35 -3
  31. package/dist/codex-home.js +323 -18
  32. package/dist/codex-session-store.d.ts +23 -0
  33. package/dist/codex-session-store.js +21 -0
  34. package/dist/host.d.ts +103 -46
  35. package/dist/host.js +1988 -634
  36. package/dist/index.d.ts +3 -3
  37. package/dist/index.js +1 -1
  38. package/dist/input-resources.d.ts +4 -0
  39. package/dist/input-resources.js +21 -5
  40. package/dist/models-catalog.d.ts +2 -1
  41. package/dist/models-catalog.js +94 -6
  42. package/dist/runner/child.d.ts +97 -28
  43. package/dist/runner/child.js +1486 -100
  44. package/dist/runner/manager.d.ts +110 -29
  45. package/dist/runner/manager.js +1481 -246
  46. package/dist/runner/protocol.d.ts +212 -24
  47. package/dist/runner/protocol.js +5 -0
  48. package/dist/runner/startup-policy.d.ts +7 -0
  49. package/dist/runner/startup-policy.js +10 -0
  50. package/dist/runner/transport.d.ts +18 -2
  51. package/dist/runner/transport.js +82 -3
  52. package/dist/runner-main.js +8 -3
  53. package/dist/terminal/claude-tui.d.ts +3 -1
  54. package/dist/terminal/claude-tui.js +3 -1
  55. package/dist/terminal/codex-tui.d.ts +4 -0
  56. package/dist/terminal/codex-tui.js +5 -0
  57. package/dist/terminal/control-parser.d.ts +39 -0
  58. package/dist/terminal/control-parser.js +172 -0
  59. package/dist/terminal/registry.d.ts +18 -15
  60. package/dist/terminal/registry.js +44 -23
  61. package/dist/terminal/spool.d.ts +47 -0
  62. package/dist/terminal/spool.js +231 -0
  63. package/dist/terminal/tmux.d.ts +126 -74
  64. package/dist/terminal/tmux.js +807 -211
  65. package/package.json +4 -4
package/dist/index.d.ts CHANGED
@@ -9,10 +9,10 @@ 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";
16
- export { TmuxTerminal, isTmuxAvailable, terminateTmuxServer, tmuxSocketPath, } from "./terminal/tmux.js";
17
- export type { TerminalAttachment, TmuxTerminalOptions } from "./terminal/tmux.js";
16
+ export { TmuxTerminal, isTmuxAvailable, resolveTmuxBin, terminateTmuxServer, tmuxSocketPath, } from "./terminal/tmux.js";
17
+ export type { PreparedTerminalAttachment, PullTerminalAttachment, TmuxTerminalOptions, } from "./terminal/tmux.js";
18
18
  export { TerminalRegistry } from "./terminal/registry.js";
package/dist/index.js CHANGED
@@ -12,5 +12,5 @@ export { RunnerManager, TerminalOpenError } from "./runner/manager.js";
12
12
  export { probeRuntimeStatus } from "./runtime-status.js";
13
13
  export { listRuntimeModels } from "./models-catalog.js";
14
14
  // Live-terminal subsystem (Phase C): tmux-backed terminals + per-runner registry.
15
- export { TmuxTerminal, isTmuxAvailable, terminateTmuxServer, tmuxSocketPath, } from "./terminal/tmux.js";
15
+ export { TmuxTerminal, isTmuxAvailable, resolveTmuxBin, terminateTmuxServer, tmuxSocketPath, } from "./terminal/tmux.js";
16
16
  export { TerminalRegistry } from "./terminal/registry.js";
@@ -3,6 +3,10 @@ import type { UserInput } from "./codex-app-server/protocol.js";
3
3
  /** Convert the provider-native user echo back to resource references. Unknown
4
4
  * local paths and remote URLs are deliberately omitted rather than exposed. */
5
5
  export declare function codexUserContent(input: readonly UserInput[]): UserContentPart[];
6
+ /** Match the forwarder's public user echo shape exactly. Codex merges adjacent
7
+ * text inputs (including the internal file marker) into one string before the
8
+ * Host sees the completed userMessage item. */
9
+ export declare function codexUserEchoContent(input: readonly UserInput[]): string | UserContentPart[] | undefined;
6
10
  /** Claude's native TUI has no structured image RPC. The target daemon supplies
7
11
  * only managed local paths and the marker makes the transcript echo reversible. */
8
12
  export declare function claudeInputText(input: RuntimeUserInput, attachmentToken?: string): string;
@@ -1,7 +1,7 @@
1
1
  import { randomUUID } from "node:crypto";
2
2
  import { basename, extname } from "node:path";
3
3
  const CLAUDE_ATTACHMENT_TOKEN = "RYNX_ATTACHMENT_SET";
4
- const CLAUDE_ATTACHMENT_TOKEN_RE = /^\[RYNX_ATTACHMENT_SET (att_[0-9a-f-]{36}): inspect each RYNX_IMAGE_RESOURCE path with the Read tool before answering\.\]$/m;
4
+ const CLAUDE_ATTACHMENT_TOKEN_RE = /^\[RYNX_ATTACHMENT_SET (att_[0-9a-f-]{36}): inspect each RYNX_IMAGE_RESOURCE or RYNX_FILE_RESOURCE path with the Read tool before answering\.\]$/m;
5
5
  /** Convert the provider-native user echo back to resource references. Unknown
6
6
  * local paths and remote URLs are deliberately omitted rather than exposed. */
7
7
  export function codexUserContent(input) {
@@ -20,21 +20,37 @@ export function codexUserContent(input) {
20
20
  }
21
21
  return parts;
22
22
  }
23
+ /** Match the forwarder's public user echo shape exactly. Codex merges adjacent
24
+ * text inputs (including the internal file marker) into one string before the
25
+ * Host sees the completed userMessage item. */
26
+ export function codexUserEchoContent(input) {
27
+ const parts = codexUserContent(input);
28
+ if (parts.length === 0)
29
+ return undefined;
30
+ if (parts.every((part) => part.type === "input_text")) {
31
+ const text = parts.map((part) => part.text).join("").trim();
32
+ return text || undefined;
33
+ }
34
+ return parts;
35
+ }
23
36
  /** Claude's native TUI has no structured image RPC. The target daemon supplies
24
37
  * only managed local paths and the marker makes the transcript echo reversible. */
25
38
  export function claudeInputText(input, attachmentToken = `att_${randomUUID()}`) {
26
- const images = input.content.filter((part) => part.type === "local_image");
39
+ const attachments = input.content.filter((part) => part.type !== "text");
27
40
  const text = input.content
28
41
  .filter((part) => part.type === "text")
29
42
  .map((part) => part.text)
30
43
  .join("");
31
- if (images.length === 0)
44
+ if (attachments.length === 0)
32
45
  return text;
33
46
  if (!/^att_[0-9a-f-]{36}$/.test(attachmentToken)) {
34
47
  throw new Error("Claude attachment token is invalid");
35
48
  }
36
- const preamble = `[${CLAUDE_ATTACHMENT_TOKEN} ${attachmentToken}: inspect each RYNX_IMAGE_RESOURCE path with the Read tool before answering.]`;
37
- const markers = images.map((part) => `[[RYNX_IMAGE_RESOURCE ${JSON.stringify({ path: part.path })}]]`);
49
+ const preamble = `[${CLAUDE_ATTACHMENT_TOKEN} ${attachmentToken}: inspect each RYNX_IMAGE_RESOURCE or RYNX_FILE_RESOURCE path with the Read tool before answering.]`;
50
+ const markers = attachments.map((part) => `[[RYNX_${part.type === "local_image" ? "IMAGE" : "FILE"}_RESOURCE ${JSON.stringify({
51
+ path: part.path,
52
+ ...(part.resource.filename ? { filename: part.resource.filename } : {}),
53
+ })}]]`);
38
54
  return [preamble, ...markers, text]
39
55
  .filter((part, index) => index <= markers.length || part.length > 0)
40
56
  .join("\n");
@@ -1,11 +1,12 @@
1
1
  import { type AgentRuntimeId, type AppConfig } from "@rynx-ai/core";
2
2
  import type { ModelListResponse } from "./codex-app-server/protocol.js";
3
3
  export interface RuntimeModelCatalogDeps {
4
+ readCodexModels?: () => Promise<unknown>;
4
5
  readTraexModels?: () => Promise<unknown>;
5
6
  readTraexDebugModels?: () => Promise<unknown>;
6
7
  }
7
8
  /**
8
9
  * The model list for a runtime, without an execution backend.
9
- * Falls back to the configured model if live Traex discovery is unavailable.
10
+ * Falls back to the configured model if local/native discovery is unavailable.
10
11
  */
11
12
  export declare function listRuntimeModels(config: AppConfig, runtime: AgentRuntimeId, deps?: RuntimeModelCatalogDeps): Promise<ModelListResponse | null>;
@@ -3,24 +3,37 @@
3
3
  *
4
4
  * The parent control plane no longer holds an app-server, so `/models` can't be
5
5
  * a live `model/list` RPC anymore. Following reference implementation's static-catalog model, we
6
- * serve a config-derived Codex list and Traex's native `models --json` catalog.
6
+ * serve Codex's local model cache and Traex's native `models --json` catalog.
7
7
  * claude already has its own static list ({@link listClaudeModels}).
8
8
  */
9
9
  import { execFile } from "node:child_process";
10
+ import { readFile } from "node:fs/promises";
11
+ import { join } from "node:path";
10
12
  import { promisify } from "node:util";
11
- import { resolveRuntimeBinary, resolveRuntimeModel, } from "@rynx-ai/core";
13
+ import { getRuntimeProfile, resolveRuntimeBinary, resolveRuntimeHome, resolveRuntimeModel, } from "@rynx-ai/core";
12
14
  import { listClaudeModels } from "./claude/models.js";
13
15
  const execFileAsync = promisify(execFile);
14
16
  const TRAEX_MODELS_TIMEOUT_MS = 8_000;
15
17
  const TRAEX_MODELS_MAX_BYTES = 2 * 1024 * 1024;
16
18
  /**
17
19
  * The model list for a runtime, without an execution backend.
18
- * Falls back to the configured model if live Traex discovery is unavailable.
20
+ * Falls back to the configured model if local/native discovery is unavailable.
19
21
  */
20
22
  export async function listRuntimeModels(config, runtime, deps = {}) {
21
23
  if (runtime === "claude") {
22
24
  return listClaudeModels();
23
25
  }
26
+ if (runtime === "codex") {
27
+ try {
28
+ const configuredDefault = resolveRuntimeModel(config, runtime).trim();
29
+ const models = normalizeCodexModels(await (deps.readCodexModels ?? readCodexModels)(), configuredDefault);
30
+ if (models.length > 0)
31
+ return { data: models };
32
+ }
33
+ catch {
34
+ // Keep the configured fallback usable before Codex has populated its cache.
35
+ }
36
+ }
24
37
  if (runtime === "traex") {
25
38
  try {
26
39
  const value = await (deps.readTraexModels ?? readTraexModels)();
@@ -48,6 +61,72 @@ export async function listRuntimeModels(config, runtime, deps = {}) {
48
61
  }
49
62
  return { data: [{ id: model, model, isDefault: true }] };
50
63
  }
64
+ async function readCodexModels() {
65
+ const cachePath = join(resolveRuntimeHome(getRuntimeProfile("codex")), "models_cache.json");
66
+ return JSON.parse(await readFile(cachePath, "utf8"));
67
+ }
68
+ function normalizeCodexModels(value, configuredDefault) {
69
+ if (!value || typeof value !== "object" || Array.isArray(value))
70
+ return [];
71
+ const entries = value.models;
72
+ if (!Array.isArray(entries))
73
+ return [];
74
+ const models = [];
75
+ const seen = new Set();
76
+ for (const item of entries) {
77
+ if (!item || typeof item !== "object" || Array.isArray(item))
78
+ continue;
79
+ const record = item;
80
+ const id = typeof record.slug === "string" ? record.slug.trim() : "";
81
+ if (!id || seen.has(id) || record.visibility === "hide")
82
+ continue;
83
+ seen.add(id);
84
+ const supportedReasoningEfforts = Array.isArray(record.supported_reasoning_levels)
85
+ ? record.supported_reasoning_levels.flatMap((raw) => {
86
+ if (!raw || typeof raw !== "object" || Array.isArray(raw))
87
+ return [];
88
+ const effort = typeof raw.effort === "string"
89
+ ? raw.effort.trim()
90
+ : "";
91
+ if (!effort)
92
+ return [];
93
+ const description = raw.description;
94
+ return [{
95
+ reasoningEffort: effort,
96
+ ...(typeof description === "string" && description.trim()
97
+ ? { description: description.trim() }
98
+ : {}),
99
+ }];
100
+ })
101
+ : [];
102
+ const displayName = typeof record.display_name === "string"
103
+ ? record.display_name.trim()
104
+ : "";
105
+ const description = typeof record.description === "string"
106
+ ? record.description.trim()
107
+ : "";
108
+ const defaultReasoningEffort = typeof record.default_reasoning_level === "string"
109
+ ? record.default_reasoning_level.trim()
110
+ : "";
111
+ models.push({
112
+ id,
113
+ model: id,
114
+ ...(displayName ? { displayName } : {}),
115
+ ...(description ? { description } : {}),
116
+ isDefault: id === configuredDefault,
117
+ ...(supportedReasoningEfforts.length > 0 ? { supportedReasoningEfforts } : {}),
118
+ ...(defaultReasoningEffort ? { defaultReasoningEffort } : {}),
119
+ });
120
+ }
121
+ if (configuredDefault && !seen.has(configuredDefault)) {
122
+ models.unshift({
123
+ id: configuredDefault,
124
+ model: configuredDefault,
125
+ isDefault: true,
126
+ });
127
+ }
128
+ return models;
129
+ }
51
130
  async function readTraexModels() {
52
131
  const { stdout } = await execFileAsync(resolveRuntimeBinary("traex"), ["models", "--json"], {
53
132
  encoding: "utf8",
@@ -93,19 +172,28 @@ function normalizeTraexModels(value, configuredDefault) {
93
172
  return [];
94
173
  const models = [];
95
174
  const seen = new Set();
175
+ let foundConfiguredDefault = false;
96
176
  for (const item of value) {
97
177
  if (!item || typeof item !== "object" || Array.isArray(item))
98
178
  continue;
99
179
  const record = item;
100
- const id = typeof record.name === "string" ? record.name.trim() : "";
180
+ const name = typeof record.name === "string" ? record.name.trim() : "";
181
+ // Traex 0.201.1 exposes the launchable model slug as real_name; name is its label/config alias.
182
+ const realName = typeof record.real_name === "string" ? record.real_name.trim() : "";
183
+ const id = realName || name;
101
184
  if (!id || seen.has(id))
102
185
  continue;
103
186
  seen.add(id);
187
+ const isDefault = configuredDefault
188
+ ? configuredDefault === id || configuredDefault === name
189
+ : models.length === 0;
190
+ foundConfiguredDefault ||= isDefault;
104
191
  models.push({
105
192
  id,
106
193
  model: id,
194
+ ...(name && name !== id ? { displayName: name } : {}),
107
195
  ...(typeof record.description === "string" ? { description: record.description } : {}),
108
- isDefault: configuredDefault ? id === configuredDefault : models.length === 0,
196
+ isDefault,
109
197
  ...(typeof record.context_window === "number" ? { contextWindow: record.context_window } : {}),
110
198
  ...(Array.isArray(record.supported_mime_types)
111
199
  ? { supportedMimeTypes: record.supported_mime_types }
@@ -113,7 +201,7 @@ function normalizeTraexModels(value, configuredDefault) {
113
201
  ...(record._meta && typeof record._meta === "object" ? { _meta: record._meta } : {}),
114
202
  });
115
203
  }
116
- if (configuredDefault && !seen.has(configuredDefault)) {
204
+ if (configuredDefault && !foundConfiguredDefault) {
117
205
  models.unshift({ id: configuredDefault, model: configuredDefault, isDefault: true });
118
206
  }
119
207
  return models;
@@ -1,18 +1,10 @@
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 { type AgentCapabilities, type ResolvedExecutionSnapshot, type RuntimeUserInput, type SessionInteractionResolution, type SessionWorkspaceSnapshot } from "@rynx-ai/core";
11
- import type { SessionEvent } from "@rynx-ai/core";
1
+ import { type AgentCapabilities, type LiveSessionFailure, type ResolvedExecutionSnapshot, type RuntimeTurnOptions, type RuntimeUserInput, type SessionCollaborationMode, type SessionInteractionResolution, type SessionWorkspaceSnapshot } from "@rynx-ai/core";
2
+ import { type TerminalLifecycle } from "../terminal/registry.js";
12
3
  import type { TerminalInjector } from "../claude/native-integration.js";
4
+ import type { MirrorEmitter } from "../host.js";
13
5
  import type { ResolveInteractionResult } from "../interactions.js";
14
- import { type InjectOutcome } from "./protocol.js";
15
- import type { ChildTransport } from "./transport.js";
6
+ import { type InjectResult } from "./protocol.js";
7
+ import { type ChildTransport } from "./transport.js";
16
8
  /** Re-target the mirror to a freshly minted rynx session (claude `/clear`·`/fork`)
17
9
  * + record the terminal transfer with the daemon. The child owns the transport,
18
10
  * so it supplies this to the host. */
@@ -21,7 +13,8 @@ type RetargetMirror = (newSessionId: string, meta: {
21
13
  workspace: SessionWorkspaceSnapshot;
22
14
  execution: ResolvedExecutionSnapshot;
23
15
  parentSessionId?: string;
24
- }) => void;
16
+ beforeApplied?: () => void | Promise<void>;
17
+ }) => void | Promise<void>;
25
18
  /** Codex-native live-session methods the executor (a `LocalAgentHost`) exposes
26
19
  * beyond the `AgentExecutor`/`AgentCapabilities` contract. Duck-typed so the
27
20
  * runner works with any executor (a claude-only host simply lacks them). */
@@ -32,24 +25,47 @@ interface LiveCodexProvider {
32
25
  cwd: string;
33
26
  env?: Record<string, string>;
34
27
  skipTraexStartupPrompts?: boolean;
28
+ scrollback?: number;
29
+ tmuxAllowPassthrough?: boolean;
30
+ tmuxStartOnAttach?: boolean;
31
+ keepAliveAfterExit?: boolean;
32
+ lifecycle: TerminalLifecycle;
35
33
  } | null>;
36
- ensureLiveCodexSession?(localThreadId: string, emit: (event: SessionEvent) => void, opts: {
34
+ ensureLiveCodexSession?(localThreadId: string, emit: MirrorEmitter, opts: {
37
35
  workspace: SessionWorkspaceSnapshot;
38
36
  execution: ResolvedExecutionSnapshot;
39
37
  retargetMirror?: RetargetMirror;
40
38
  }): Promise<boolean>;
41
- waitLiveReady?(localThreadId: string, timeoutMs?: number): Promise<boolean>;
42
- waitTerminalReady?(localThreadId: string, timeoutMs?: number): Promise<boolean>;
39
+ /** Known-thread Codex/Traex resume only: start the independent transcript
40
+ * observer after the replacement TUI has launched. Fresh discovery connects
41
+ * its listener before launch and this method is an idempotent no-op. */
42
+ startLiveCodexObserver?(localThreadId: string): void;
43
+ /** `null` delegates the deadline to RunnerManager. */
44
+ waitLiveReady?(localThreadId: string, timeoutMs?: number | null): Promise<boolean>;
45
+ /** `null` delegates the deadline to RunnerManager. */
46
+ waitTerminalReady?(localThreadId: string, timeoutMs?: number | null): Promise<boolean>;
43
47
  liveSessionError?(localThreadId: string): string | undefined;
44
- injectMessage?(localThreadId: string, input: RuntimeUserInput | string): Promise<InjectOutcome>;
48
+ liveSessionFailure?(localThreadId: string): LiveSessionFailure | undefined;
49
+ failLiveStartup?(localThreadId: string, error: Error): boolean;
50
+ failLiveSession?(localThreadId: string, error: Error): boolean;
51
+ /** Codex/Traex auxiliary Terminal lifecycle: settle the current Turn, stop
52
+ * observer/forwarder, and evict the owned app-server while preserving the
53
+ * durable native-session binding for a later cold resume. */
54
+ teardownLiveCodexSession?(localThreadId: string, error?: Error): boolean;
55
+ /** Cleanup coupled runtime resources after an auxiliary terminal is
56
+ * intentionally idle-reaped. Unexpected auxiliary exit does not call this. */
57
+ teardownAuxiliaryTerminalRuntime?(localThreadId: string): Promise<void>;
58
+ injectMessage?(localThreadId: string, input: RuntimeUserInput | string, options?: RuntimeTurnOptions): Promise<InjectResult>;
59
+ updateCollaborationMode?(localThreadId: string, mode: SessionCollaborationMode): Promise<void>;
45
60
  interruptLive?(localThreadId: string): Promise<boolean>;
46
61
  stopLiveCodexSession?(localThreadId: string, opts?: {
47
62
  deferClaudeInteractionCleanup?: boolean;
48
63
  }): void;
49
- finalizeStoppedLiveSessions?(): void;
64
+ finalizeStoppedLiveSessions?(): void | Promise<void>;
50
65
  resolveInteraction?(localThreadId: string, interactionId: string, resolution: SessionInteractionResolution): Promise<ResolveInteractionResult>;
51
- /** claude-native: hand the session's tmux pane injector to the host (which
52
- * doesn't own tmux). No-op for codex sessions (they inject via app-server). */
66
+ /** Hand the session's tmux pane injector to the host (which doesn't own
67
+ * tmux). Claude uses it for messages; Codex uses it only for TUI-local
68
+ * controls that the app-server cannot resolve, such as the final Plan picker. */
53
69
  attachTerminalInjector?(localThreadId: string, injector: TerminalInjector): void;
54
70
  /** Internal Provider operation used only by MachineSessionService through
55
71
  * RunnerManager. It is deliberately absent from public AgentCapabilities. */
@@ -65,21 +81,42 @@ export interface RunnerSessionDeps {
65
81
  /** Invoked on a `shutdown` message (default: close transport). */
66
82
  onShutdown?: () => void;
67
83
  }
84
+ /** Delivery retry schedule: 1, 2, 4, 8, 16, 30, 30… seconds.
85
+ * Clamp before exponentiation so an indefinitely transient outage cannot
86
+ * overflow after thousands of attempts. */
87
+ export declare function mirrorRetryDelayMs(failedAttempt: number): number;
68
88
  export declare class RunnerSession {
69
89
  private readonly transport;
70
90
  private readonly executor;
71
91
  private readonly onShutdown;
72
92
  /** Live terminals hosted by this session, and per-attach client handles. */
73
93
  private readonly terminals;
94
+ private readonly preparations;
74
95
  private readonly attachments;
75
96
  private readonly attachmentThreadIds;
97
+ private readonly attachmentRoles;
76
98
  private readonly traexStartupWatchers;
99
+ private readonly traexStartupGates;
100
+ private readonly codexStartupMonitors;
101
+ private readonly terminalWatchers;
77
102
  /** Opens are async; a close received before attach resolves tombstones the id. */
78
103
  private readonly pendingTerminalOpens;
79
104
  private readonly cancelledTerminalOpens;
80
105
  /** Session ids with a live codex forwarder started here (stopped on shutdown). */
81
106
  private readonly liveIds;
107
+ private readonly mirrorQueues;
108
+ private readonly mirrorAcks;
109
+ private readonly mirrorImageAcks;
110
+ private readonly mirrorRetryWaiters;
111
+ private readonly rotationPublishAcks;
112
+ private readonly rotationAppliedAcks;
113
+ /** Provider name retained for asynchronous Terminal-exit diagnostics. */
114
+ private readonly liveRuntimes;
115
+ private readonly terminalStatuses;
116
+ private consecutiveMirrorFailures;
117
+ private mirrorDegraded;
82
118
  private shuttingDown;
119
+ private shutdownPromise;
83
120
  constructor({ transport, executor, onShutdown }: RunnerSessionDeps);
84
121
  private handle;
85
122
  private get liveProvider();
@@ -90,29 +127,61 @@ export declare class RunnerSession {
90
127
  * new session AND tells the daemon to alias the runner (terminal transfer) so
91
128
  * the new session stays injectable. */
92
129
  private mirrorChannel;
130
+ private transferNativeSessionResources;
131
+ private enqueueMirror;
132
+ private sendMirroredEvent;
133
+ private deliverMirrorImageEvent;
134
+ private deliverMirrorImageTransportFrame;
135
+ private deliverMirrorEvent;
136
+ private noteMirrorDeliveryFailed;
137
+ private noteMirrorDeliveryRecovered;
138
+ private sendMirrorEventFrame;
139
+ private abandonMirrorDelivery;
140
+ private waitForMirrorRetry;
141
+ private cancelMirrorGeneration;
142
+ private rejectMirrorAcks;
143
+ private sendMirrorImageFrame;
144
+ private rejectMirrorImageAcks;
145
+ private sendRotationFrame;
146
+ private sendRotationAppliedFrame;
147
+ private rejectRotationAcks;
93
148
  /**
94
- * Eagerly bring up a session's codex-native live view: start the persistent
95
- * forwarder connection (which resume-subscribes to mirror every turn) and
96
- * launch the detached `codex --remote resume` TUI against the thread the
97
- * structured runtime created. The forwarder subscribes to that same thread,
98
- * so the TUI is usable immediately and its turns mirror to chat.
149
+ * Eagerly bring up a session's codex-native live view. Fresh sessions connect
150
+ * the discovery listener before launching the detached TUI; known-thread
151
+ * resumes preload first, launch the replacement TUI, then start their
152
+ * persistent observer in the background.
99
153
  */
100
154
  private ensureLive;
155
+ private monitorCodexThreadStartup;
156
+ /** Discovery failure is terminal for this partial native launch. Publish the
157
+ * exact cause first so an in-flight injection wakes, then drop the observer
158
+ * and TUI so a later task retry creates a clean launch instead of reusing an
159
+ * already-rejected readiness promise. */
160
+ private failCodexThreadStartup;
101
161
  private inject;
162
+ private updateCollaborationMode;
102
163
  private interruptLive;
164
+ private reapTerminal;
103
165
  /** Launch (idempotently) the session's codex TUI pane from the executor's
104
166
  * `codexTerminalSpec`. Shares the `${id}-main` terminal id with `term.open`,
105
167
  * so the web attach reuses the same detached pane. */
106
168
  private launchCodexPane;
169
+ private watchNativeTerminal;
107
170
  private cancelTraexStartupWatcher;
108
171
  private skipTraexStartupPrompts;
109
172
  private stopLive;
110
173
  /** Stop event forwarding, kill native terminals/hooks, then synchronously
111
174
  * scrub provider handoff files before the child process is allowed to exit. */
112
- shutdown(): void;
175
+ shutdown(): Promise<void>;
113
176
  private openTerminal;
114
- /** Attach an already-created terminal and forward its data/exit to the parent. */
177
+ /** Prepare captured history without starting the live control client. */
115
178
  private attachExisting;
179
+ private readTerminalSeed;
180
+ private startTerminal;
181
+ private readTerminalOutput;
182
+ private writeTerminalInput;
183
+ private resizeTerminal;
184
+ private sendTerminalOperationError;
116
185
  private runCap;
117
186
  private dispatchCap;
118
187
  }