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

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.
@@ -15,8 +15,8 @@
15
15
  * transport drops in here later without touching `ConversationRuntime` or any
16
16
  * channel.
17
17
  */
18
- import { spawn as nodeSpawn } from "node:child_process";
19
- import { 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";
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";
20
20
  import { type CodexSessionStore } from "../host.js";
21
21
  import type { ResolveInteractionResult } from "../interactions.js";
22
22
  import { type InjectOutcome, type TerminalOpenErrorCode, type TerminalRole } from "./protocol.js";
@@ -43,6 +43,10 @@ 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. */
@@ -53,15 +57,45 @@ export interface RunnerManagerOptions {
53
57
  liveStartTimeoutMs?: number;
54
58
  /** Max wait for a Provider-native thread to become ready. */
55
59
  liveReadyTimeoutMs?: number;
60
+ /** Claude SessionStart deadline. Codex/Traex use `liveStartTimeoutMs` for the
61
+ * pane/observer acknowledgement; their thread discovery is asynchronous. */
62
+ nativeLiveStartTimeoutMs?: number;
63
+ /** Max wait for a native interrupt acknowledgement before reporting it
64
+ * unproven. Callers may then use the explicit force-stop path. */
65
+ liveInterruptTimeoutMs?: number;
66
+ /** Max wait for an accepted owner TUI submission to become observable as a
67
+ * mirrored response or a published native rotation. */
68
+ terminalInputHandoffTimeoutMs?: number;
69
+ /** Initial exponential backoff for retrying unproven terminal cleanup. */
70
+ terminalInputCleanupRetryMs?: number;
56
71
  /** Injected for tests. */
57
72
  spawn?: typeof nodeSpawn;
73
+ /** Injected for tests. Defaults to signaling the whole POSIX process group
74
+ * created for the runner child, with direct-child fallback. */
75
+ 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. */
78
+ terminateTerminalServer?: (terminalName: string) => boolean | Promise<boolean>;
79
+ /** Injected tmux `#{window_activity}` reader. Returns epoch seconds. */
80
+ terminalWindowActivityAt?: (terminalName: string) => number | null | Promise<number | null>;
81
+ /** Injected tmux attached-client probe. */
82
+ terminalHasAttachedClient?: (terminalName: string) => boolean | Promise<boolean>;
58
83
  now?: () => number;
84
+ /** Wall clock used to compare tmux's epoch timestamp. */
85
+ wallNow?: () => number;
59
86
  /** Extra env merged into every spawned runner child — e.g. the control-plane
60
87
  * URL + token so a claude-native PermissionRequest hook can POST back. */
61
88
  childEnv?: Record<string, string>;
62
89
  /** Optional lifecycle context for Session runners. The shared `__cap__`
63
90
  * runner never opens one. */
64
91
  sessionContextProvider?: RunnerSessionContextProvider;
92
+ /** Dynamic process-wide admission fence. Existing cancellation and read-only
93
+ * inspection remain available; every path that may spawn or attach execution
94
+ * checks this immediately before admission. */
95
+ admissionOpen?: () => boolean;
96
+ /** Atomically reserves one work-producing admission while it crosses the
97
+ * asynchronous boundary into an observable live runner/turn. */
98
+ admissionReserve?: () => AdmissionReservation | undefined;
65
99
  }
66
100
  /** A session rotation reported by a runner child (claude `/clear`·`/fork`): the
67
101
  * new session `to` is now aliased to `from`'s runner; carries meta to carry over. */
@@ -115,9 +149,13 @@ export declare class RunnerManager implements AgentCapabilities {
115
149
  private readonly sessionStore;
116
150
  private readonly runnerEntry;
117
151
  private readonly idleTtlMs;
152
+ private readonly nativePaneIdleTtlMs;
153
+ private readonly nativePaneOutputBusyWindowMs;
118
154
  private readonly spawn;
119
155
  private readonly childEnv;
120
156
  private readonly sessionContextProvider;
157
+ private readonly admissionOpen;
158
+ private readonly admissionReserve;
121
159
  private readonly now;
122
160
  private readonly defaultRuntime;
123
161
  private readonly handles;
@@ -127,8 +165,18 @@ export declare class RunnerManager implements AgentCapabilities {
127
165
  private readonly reapTimer;
128
166
  private readonly shutdownGraceMs;
129
167
  private readonly shutdownKillGraceMs;
168
+ private readonly signalChild;
169
+ private readonly terminateTerminalServer;
170
+ private readonly terminalWindowActivityAt;
171
+ private readonly terminalHasAttachedClient;
172
+ private readonly wallNow;
130
173
  private readonly liveStartTimeoutMs;
131
174
  private readonly liveReadyTimeoutMs;
175
+ private readonly nativeLiveStartTimeoutMs;
176
+ private readonly liveInterruptTimeoutMs;
177
+ private readonly terminalInputHandoffTimeoutMs;
178
+ private readonly terminalInputCleanupRetryMs;
179
+ private reapPromise;
132
180
  private stopping;
133
181
  private stopPromise;
134
182
  /** Sink for mirrored {@link SessionEvent}s from every session's forwarder. */
@@ -154,6 +202,9 @@ export declare class RunnerManager implements AgentCapabilities {
154
202
  private readonly forkOperations;
155
203
  private readonly forkBufferedMessages;
156
204
  private readonly forkBufferedTerminalInputs;
205
+ /** Owner TUI submissions accepted by the parent but not yet represented by a
206
+ * mirrored response or a durably published native rotation. */
207
+ private readonly terminalInputHandoffs;
157
208
  constructor(opts: RunnerManagerOptions);
158
209
  /**
159
210
  * Open a live terminal on the session's runner child (spawning it if needed).
@@ -181,11 +232,24 @@ export declare class RunnerManager implements AgentCapabilities {
181
232
  /** Register the sink for session rotations (claude `/clear`·`/fork`): the server
182
233
  * records the new session's meta (carry-over agent/model/title). */
183
234
  onRotate(listener: (rotation: RotateInfo) => void | Promise<void>): void;
235
+ /** Accepted TUI submissions that have not crossed into an observable runtime
236
+ * state. The daemon folds this into runningTurns after closing admission. */
237
+ pendingTerminalInputCount(): number;
238
+ private beginTerminalInputHandoffs;
239
+ private settleTerminalInputHandoff;
240
+ private settleTerminalRotationHandoff;
241
+ private currentTerminalSessionId;
242
+ private finishTerminalInputHandoff;
243
+ private finishAllTerminalInputHandoffs;
244
+ private preserveTerminalInputHandoffsAsActivity;
245
+ private scheduleTerminalInputCleanupRetry;
246
+ private expireTerminalInputHandoffs;
184
247
  /**
185
248
  * Eagerly bring up a session's codex-native live view (persistent forwarder +
186
249
  * detached `codex --remote` TUI) in its runner child, spawning the runner if
187
- * needed. Idempotent. Resolves true once the codex thread is bound; false for a
188
- * non-codex / non-live session (the caller then uses the normal run path).
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.
189
253
  */
190
254
  ensureLiveSession(localThreadId: string, opts: {
191
255
  workspace: SessionWorkspaceSnapshot;
@@ -211,6 +275,7 @@ export declare class RunnerManager implements AgentCapabilities {
211
275
  * the session has no live forwarder (caller falls back to the run path).
212
276
  */
213
277
  injectMessage(localThreadId: string, input: RuntimeUserInput | string): Promise<InjectOutcome>;
278
+ private injectMessageAdmitted;
214
279
  /**
215
280
  * Interrupt a session's active live turn — the web Stop button (codex
216
281
  * `turn/interrupt`, claude Escape). Best-effort: resolves false with NO spawn
@@ -243,13 +308,19 @@ export declare class RunnerManager implements AgentCapabilities {
243
308
  }): Promise<import("../host.js").CodexRuntimeStatus>;
244
309
  /** Stop the runner bound to one session (if any), rejecting its in-flight work. */
245
310
  stopRunner(localThreadId: string): void;
311
+ /** Force-stop and join the runner bound to one Session. Unlike stopRunner,
312
+ * completion proves that the child process has exited. */
313
+ terminateLiveSession(localThreadId: string): Promise<boolean>;
246
314
  /** Stop every runner and join all child exits. Idempotent across concurrent calls. */
247
315
  stop(): Promise<void>;
316
+ private handleForCleanup;
248
317
  /** Forward a capability to a runner child. Session-less caps (listModels/status)
249
318
  * use the shared `CAP_KEY` child; per-thread caps pass the session's key so they
250
319
  * run on that session's child (which owns its private CODEX_HOME). */
251
320
  private forwardCap;
252
321
  private getOrSpawn;
322
+ private assertAdmissionOpen;
323
+ private reserveAdmission;
253
324
  private performManagedFork;
254
325
  private performClaudeManagedFork;
255
326
  private bufferForkTargetMessage;
@@ -262,5 +333,17 @@ export declare class RunnerManager implements AgentCapabilities {
262
333
  private failHandle;
263
334
  private terminateHandle;
264
335
  private reapIdle;
336
+ private reapIdleOnce;
337
+ /** Provider-authoritative turn state is the lifecycle truth; attached clients
338
+ * and tmux's own activity clock are independent busy evidence. There is no
339
+ * "user was inactive for an hour" override for an active turn. */
340
+ private reapNativePane;
341
+ private isNativePaneBusy;
342
+ private isManagedNativeHandle;
343
+ private failActiveResponses;
344
+ /** Track the provider-authoritative response lifecycle. Native-pane busy
345
+ * classification consumes this level directly; output volume is separately
346
+ * grounded in tmux's own activity clock. */
347
+ private observeHandleRuntimeEvent;
265
348
  private openSessionContext;
266
349
  }