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

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
@@ -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 RuntimeTurnOptions, type RuntimeUserInput, type SessionCollaborationMode, 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. */
@@ -43,33 +43,45 @@ export interface RunnerManagerOptions {
43
43
  runnerEntry?: string;
44
44
  /** Idle TTL (ms) after which an unused runner is reaped. */
45
45
  idleTtlMs?: number;
46
+ /** Native pane idle window. `<= 0` disables native-pane idle reaping. */
47
+ nativePaneIdleTtlMs?: number;
48
+ /** Recent tmux-output window used as independent native-pane busy evidence. */
49
+ nativePaneOutputBusyWindowMs?: number;
46
50
  /** Background reap sweep interval (ms); `0` disables the timer (tests). */
47
51
  reapIntervalMs?: number;
48
52
  /** Grace period between SIGTERM and SIGKILL during runner shutdown. */
49
53
  shutdownGraceMs?: number;
50
54
  /** Final bounded wait for child exit after SIGKILL. */
51
55
  shutdownKillGraceMs?: number;
52
- /** 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. */
53
58
  liveStartTimeoutMs?: number;
54
59
  /** Max wait for a Provider-native thread to become ready. */
55
60
  liveReadyTimeoutMs?: number;
61
+ /** Claude SessionStart deadline. Codex/Traex have no parent-owned startup
62
+ * deadline; their app-server/discovery/injection phases report directly. */
63
+ nativeLiveStartTimeoutMs?: number;
56
64
  /** Max wait for a native interrupt acknowledgement before reporting it
57
65
  * unproven. Callers may then use the explicit force-stop path. */
58
66
  liveInterruptTimeoutMs?: number;
59
67
  /** Max wait for an accepted owner TUI submission to become observable as a
60
68
  * mirrored response or a published native rotation. */
61
69
  terminalInputHandoffTimeoutMs?: number;
62
- /** Initial exponential backoff for retrying unproven terminal cleanup. */
63
- terminalInputCleanupRetryMs?: number;
64
70
  /** Injected for tests. */
65
71
  spawn?: typeof nodeSpawn;
66
72
  /** Injected for tests. Defaults to signaling the whole POSIX process group
67
73
  * created for the runner child, with direct-child fallback. */
68
74
  signalChild?: (child: ChildProcess, signal: NodeJS.Signals, processGroup: boolean) => void;
69
- /** Injected for tests. Kills and verifies the deterministic private tmux
70
- * server owned by a Session runner. */
71
- terminateTerminalServer?: (terminalName: string) => boolean | Promise<boolean>;
75
+ /** Injected for tests. Best-effort close of the deterministic private tmux
76
+ * server owned by a Session runner; reference implementation does not verify/retry close. */
77
+ terminateTerminalServer?: (terminalName: string, ownerPid?: number) => boolean | Promise<boolean>;
78
+ /** Injected tmux `#{window_activity}` reader. Returns epoch seconds. */
79
+ terminalWindowActivityAt?: (terminalName: string, ownerPid?: number) => number | null | Promise<number | null>;
80
+ /** Injected tmux attached-client probe. */
81
+ terminalHasAttachedClient?: (terminalName: string, ownerPid?: number) => boolean | Promise<boolean>;
72
82
  now?: () => number;
83
+ /** Wall clock used to compare tmux's epoch timestamp. */
84
+ wallNow?: () => number;
73
85
  /** Extra env merged into every spawned runner child — e.g. the control-plane
74
86
  * URL + token so a claude-native PermissionRequest hook can POST back. */
75
87
  childEnv?: Record<string, string>;
@@ -104,24 +116,39 @@ export interface OpenTerminalOptions {
104
116
  command?: string;
105
117
  args?: string[];
106
118
  }
107
- /**
108
- * Parent-side handle to a live terminal on a runner child. Bytes flow through
109
- * {@link onData} / {@link write} (decoded from the base64 wire frames); resize
110
- * and close are forwarded as control messages. {@link ready} resolves with the
111
- * granted role once the child attaches (an `owner` request is downgraded to
112
- * read-only when the owner slot is taken).
113
- */
119
+ export interface ParentTerminalRead {
120
+ data: Uint8Array;
121
+ nextOffset: number;
122
+ done: boolean;
123
+ finalOffset?: number;
124
+ }
125
+ export interface ParentTerminalReaderDone {
126
+ finalOffset: number;
127
+ reason: "terminal_exited" | "client_closed" | "backpressure" | "internal";
128
+ exitCode: number;
129
+ }
130
+ /** Parent-side demand-driven terminal handle. `ready` publishes only captured
131
+ * history metadata; the caller must pull every seed byte before `start`. */
114
132
  export interface ParentTerminal {
115
133
  readonly attachId: string;
116
134
  readonly ready: Promise<{
117
135
  role: TerminalRole;
136
+ seedBytes: number;
137
+ dimensions?: {
138
+ cols: number;
139
+ rows: number;
140
+ };
118
141
  }>;
119
- onData(listener: (chunk: string) => void): void;
120
- onExit(listener: (info: {
121
- exitCode: number;
142
+ readonly readerDone: Promise<ParentTerminalReaderDone>;
143
+ readSeed(offset: number, maxBytes: number): Promise<ParentTerminalRead>;
144
+ start(): Promise<void>;
145
+ read(offset: number, maxBytes: number): Promise<ParentTerminalRead>;
146
+ onResize?(listener: (dimensions: {
147
+ cols: number;
148
+ rows: number;
122
149
  }) => void): void;
123
- write(data: string): void;
124
- resize(cols: number, rows: number): void;
150
+ write(data: string | Uint8Array): Promise<void>;
151
+ resize(cols: number, rows: number): Promise<void>;
125
152
  close(): void;
126
153
  }
127
154
  /** A typed attach failure so transport adapters can distinguish an expected
@@ -136,6 +163,8 @@ export declare class RunnerManager implements AgentCapabilities {
136
163
  private readonly sessionStore;
137
164
  private readonly runnerEntry;
138
165
  private readonly idleTtlMs;
166
+ private readonly nativePaneIdleTtlMs;
167
+ private readonly nativePaneOutputBusyWindowMs;
139
168
  private readonly spawn;
140
169
  private readonly childEnv;
141
170
  private readonly sessionContextProvider;
@@ -152,15 +181,28 @@ export declare class RunnerManager implements AgentCapabilities {
152
181
  private readonly shutdownKillGraceMs;
153
182
  private readonly signalChild;
154
183
  private readonly terminateTerminalServer;
184
+ private readonly terminalWindowActivityAt;
185
+ private readonly terminalHasAttachedClient;
186
+ private readonly wallNow;
155
187
  private readonly liveStartTimeoutMs;
156
188
  private readonly liveReadyTimeoutMs;
189
+ private readonly nativeLiveStartTimeoutMs;
157
190
  private readonly liveInterruptTimeoutMs;
158
191
  private readonly terminalInputHandoffTimeoutMs;
159
- private readonly terminalInputCleanupRetryMs;
192
+ private reapPromise;
160
193
  private stopping;
161
194
  private stopPromise;
162
195
  /** Sink for mirrored {@link SessionEvent}s from every session's forwarder. */
163
196
  private mirrorListener;
197
+ /** Settles process-local projections only when an ordinary durable item has
198
+ * exhausted its permanent retry budget. */
199
+ private mirrorAbandonListener;
200
+ /** Clears process-live optimistic state when /clear permanently moves the
201
+ * native pane away from the old Session. */
202
+ private mirrorSupersedeListener;
203
+ /** Synchronous persistence hook for native collaboration-mode reflection.
204
+ * It runs before the event reaches the Session bus. */
205
+ private collaborationModeListener;
164
206
  /** Sink for session rotations (claude `/clear`·`/fork`) — server records meta. */
165
207
  private rotateListener;
166
208
  /** Session keys with a live codex forwarder — never reaped while present. */
@@ -189,9 +231,8 @@ export declare class RunnerManager implements AgentCapabilities {
189
231
  /**
190
232
  * Open a live terminal on the session's runner child (spawning it if needed).
191
233
  * Phase C hosts one terminal ("main") per session; the returned handle is a
192
- * single attach client — `owner` (read-write) or a downgraded `read-only`
193
- * viewer per the child's ownership rule. Not part of `AgentExecutor`; the WS
194
- * bridge calls it directly.
234
+ * single attach client with the requested `owner` (read-write) or `read-only`
235
+ * role. Not part of `AgentExecutor`; the WS bridge calls it directly.
195
236
  */
196
237
  openTerminal(localThreadId: string, opts: OpenTerminalOptions): ParentTerminal;
197
238
  /**
@@ -208,7 +249,10 @@ export declare class RunnerManager implements AgentCapabilities {
208
249
  * session's persistent codex forwarder (web- AND TUI-initiated turns). The
209
250
  * server wires this to `persistSessionEvent` (bus + canonical log).
210
251
  */
211
- onMirror(listener: (sessionId: string, event: SessionEvent) => void): void;
252
+ onMirror(listener: (sessionId: string, event: SessionEvent) => void | Promise<void>): void;
253
+ onMirrorAbandon(listener: (sessionId: string, token: string) => void): void;
254
+ onMirrorSupersede(listener: (sessionId: string) => void): void;
255
+ onCollaborationMode(listener: (sessionId: string, mode: SessionCollaborationMode) => void): void;
212
256
  /** Register the sink for session rotations (claude `/clear`·`/fork`): the server
213
257
  * records the new session's meta (carry-over agent/model/title). */
214
258
  onRotate(listener: (rotation: RotateInfo) => void | Promise<void>): void;
@@ -222,13 +266,14 @@ export declare class RunnerManager implements AgentCapabilities {
222
266
  private finishTerminalInputHandoff;
223
267
  private finishAllTerminalInputHandoffs;
224
268
  private preserveTerminalInputHandoffsAsActivity;
225
- private scheduleTerminalInputCleanupRetry;
226
269
  private expireTerminalInputHandoffs;
227
270
  /**
228
271
  * Eagerly bring up a session's codex-native live view (persistent forwarder +
229
272
  * detached `codex --remote` TUI) in its runner child, spawning the runner if
230
- * needed. Idempotent. Resolves true once the codex thread is bound; false for a
231
- * non-codex / non-live session (the caller then uses the normal run path).
273
+ * needed. Idempotent. For Codex-lineage sessions, resolves after backend
274
+ * preload and pane launch; known-thread observer attach and fresh-thread
275
+ * discovery continue in the background. Other providers retain their
276
+ * readiness gate.
232
277
  */
233
278
  ensureLiveSession(localThreadId: string, opts: {
234
279
  workspace: SessionWorkspaceSnapshot;
@@ -247,13 +292,14 @@ export declare class RunnerManager implements AgentCapabilities {
247
292
  }): Promise<boolean>;
248
293
  private requestLiveSession;
249
294
  lastLiveSessionError(localThreadId: string): string | undefined;
295
+ lastLiveSessionFailure(localThreadId: string): LiveSessionFailure | undefined;
250
296
  /**
251
297
  * Inject a user turn into a session's live codex thread — reference implementation's
252
298
  * single-writer web send (`turn/start` / `turn/steer`); the forwarder mirrors
253
299
  * all output. Resolves true when the app-server accepted the turn, false when
254
300
  * the session has no live forwarder (caller falls back to the run path).
255
301
  */
256
- injectMessage(localThreadId: string, input: RuntimeUserInput | string): Promise<InjectOutcome>;
302
+ injectMessage(localThreadId: string, input: RuntimeUserInput | string, options?: RuntimeTurnOptions): Promise<InjectResult>;
257
303
  private injectMessageAdmitted;
258
304
  /**
259
305
  * Interrupt a session's active live turn — the web Stop button (codex
@@ -263,6 +309,8 @@ export declare class RunnerManager implements AgentCapabilities {
263
309
  interruptLiveSession(localThreadId: string): Promise<boolean>;
264
310
  /** Resolve a native question/approval without ever spawning a new runner. */
265
311
  resolveInteraction(localThreadId: string, interactionId: string, resolution: SessionInteractionResolution): Promise<ResolveInteractionResult>;
312
+ /** Update a loaded Codex-lineage native thread without spawning a runner. */
313
+ updateCollaborationMode(localThreadId: string, mode: SessionCollaborationMode): Promise<void>;
266
314
  listModels(runtime?: AgentRuntimeId): Promise<ModelListResponse | null>;
267
315
  getGoal(localThreadId: string): Promise<CapabilityResult<ThreadGoal | null>>;
268
316
  setGoal(localThreadId: string, objective: string): Promise<CapabilityResult>;
@@ -305,12 +353,45 @@ export declare class RunnerManager implements AgentCapabilities {
305
353
  private bufferForkTargetMessage;
306
354
  private reservedForkTarget;
307
355
  private deliverForkBufferedMessage;
356
+ private deliverMirrorMessage;
357
+ private releaseMirrorDelivery;
358
+ private failMirrorImageTransfer;
308
359
  private deliverRotateMessage;
360
+ /** Bridge ownership is a child-local binding, but the parent is the only
361
+ * process that knows logical target publication committed. Mark that fact
362
+ * durably before allowing the child to apply and expose the alias. */
363
+ private publishNativeRotationBinding;
364
+ private publishNativeRotationRecords;
365
+ private deliverRotateAppliedMessage;
366
+ private finalizeNativeRotation;
367
+ private acknowledgeRotateApplied;
368
+ private releasePendingNativeRotation;
369
+ private publishNativeRotationNotice;
309
370
  private spawnHandle;
310
371
  private onChildMessage;
311
372
  /** Mark a handle dead and reject every pending run/cap with the exit reason. */
312
373
  private failHandle;
313
374
  private terminateHandle;
314
375
  private reapIdle;
376
+ private reapIdleOnce;
377
+ /** Provider-authoritative turn state is the lifecycle truth; attached clients
378
+ * and tmux's own activity clock are independent busy evidence. There is no
379
+ * "user was inactive for an hour" override for an active turn. */
380
+ private reapNativePane;
381
+ private reapNativeTerminal;
382
+ private isNativePaneBusy;
383
+ private isManagedNativeHandle;
384
+ private failActiveResponses;
385
+ private interruptActiveResponses;
386
+ /** Crash/termination compensation is intentionally best-effort because it is
387
+ * a terminal/status edge rather than an ordinary transcript delivery.
388
+ * Still consume an asynchronous persistence rejection so a dying child
389
+ * cannot take the parent daemon down with an unhandled rejection. */
390
+ private emitCompensatingMirror;
391
+ /** Track the provider-authoritative response lifecycle. Native-pane busy
392
+ * classification consumes this level directly; output volume is separately
393
+ * grounded in tmux's own activity clock. */
394
+ private observeHandleRuntimeEvent;
395
+ private isStaleMirrorGeneration;
315
396
  private openSessionContext;
316
397
  }