@rynx-ai/runtime 0.1.11-beta.24 → 0.1.11-beta.26

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.
@@ -23,7 +23,7 @@ import { AgentRuntimeError, } from "@rynx-ai/core";
23
23
  import { listRuntimeModels } from "../models-catalog.js";
24
24
  import { probeRuntimeStatus } from "../runtime-status.js";
25
25
  import { terminateTmuxServer, tmuxHasAttachedClient, tmuxWindowActivityAt, } from "../terminal/tmux.js";
26
- import { fromWireError, } from "./protocol.js";
26
+ import { RUNNER_IMAGE_CHUNK_CHARS, RUNNER_IMAGE_MAX_RESULT_CHARS, fromWireError, } from "./protocol.js";
27
27
  import { isCodexLineageProvider, isManagedNativeProvider } from "./startup-policy.js";
28
28
  import { StdioRunnerTransport } from "./transport.js";
29
29
  /** Routing key for the shared capability runner (slash-command RPCs). */
@@ -182,6 +182,9 @@ export class RunnerManager {
182
182
  stopPromise;
183
183
  /** Sink for mirrored {@link SessionEvent}s from every session's forwarder. */
184
184
  mirrorListener = null;
185
+ /** Synchronous persistence hook for native collaboration-mode reflection.
186
+ * It runs before the event reaches the Session bus. */
187
+ collaborationModeListener = null;
185
188
  /** Sink for session rotations (claude `/clear`·`/fork`) — server records meta. */
186
189
  rotateListener = null;
187
190
  /** Session keys with a live codex forwarder — never reaped while present. */
@@ -349,6 +352,9 @@ export class RunnerManager {
349
352
  onMirror(listener) {
350
353
  this.mirrorListener = listener;
351
354
  }
355
+ onCollaborationMode(listener) {
356
+ this.collaborationModeListener = listener;
357
+ }
352
358
  /** Register the sink for session rotations (claude `/clear`·`/fork`): the server
353
359
  * records the new session's meta (carry-over agent/model/title). */
354
360
  onRotate(listener) {
@@ -623,19 +629,19 @@ export class RunnerManager {
623
629
  * all output. Resolves true when the app-server accepted the turn, false when
624
630
  * the session has no live forwarder (caller falls back to the run path).
625
631
  */
626
- injectMessage(localThreadId, input) {
632
+ injectMessage(localThreadId, input, options) {
627
633
  const admission = this.reserveAdmission();
628
- return this.injectMessageAdmitted(localThreadId, input)
634
+ return this.injectMessageAdmitted(localThreadId, input, options)
629
635
  .finally(() => admission.release());
630
636
  }
631
- injectMessageAdmitted(localThreadId, input) {
637
+ injectMessageAdmitted(localThreadId, input, options) {
632
638
  const sourceReservation = this.sourceForkReservations.get(localThreadId);
633
639
  if (sourceReservation) {
634
- return sourceReservation.then(() => this.injectMessageAdmitted(localThreadId, input));
640
+ return sourceReservation.then(() => this.injectMessageAdmitted(localThreadId, input, options));
635
641
  }
636
642
  const reservation = this.forkReservations.get(localThreadId);
637
643
  if (reservation) {
638
- return reservation.then(() => this.injectMessageAdmitted(localThreadId, input));
644
+ return reservation.then(() => this.injectMessageAdmitted(localThreadId, input, options));
639
645
  }
640
646
  const handle = this.getOrSpawn(localThreadId);
641
647
  handle.lastUsedAt = this.now();
@@ -670,8 +676,8 @@ export class RunnerManager {
670
676
  resolve(finish());
671
677
  });
672
678
  handle.transport.send(typeof input === "string"
673
- ? { t: "inject", reqId, localThreadId, text: input }
674
- : { t: "inject", reqId, localThreadId, input });
679
+ ? { t: "inject", reqId, localThreadId, text: input, ...(options ? { options } : {}) }
680
+ : { t: "inject", reqId, localThreadId, input, ...(options ? { options } : {}) });
675
681
  });
676
682
  }
677
683
  /**
@@ -723,6 +729,34 @@ export class RunnerManager {
723
729
  });
724
730
  });
725
731
  }
732
+ /** Update a loaded Codex-lineage native thread without spawning a runner. */
733
+ updateCollaborationMode(localThreadId, mode) {
734
+ const handle = this.handles.get(localThreadId);
735
+ if (!handle || handle.dead || !this.liveSessionKeys.has(localThreadId)) {
736
+ return Promise.reject(new AgentRuntimeError("Native Session is not live", 409, "native_session_not_live"));
737
+ }
738
+ handle.lastUsedAt = this.now();
739
+ const reqId = randomUUID();
740
+ return new Promise((resolve, reject) => {
741
+ handle.live.set(reqId, (reply) => {
742
+ if (reply.collaborationUpdated) {
743
+ resolve();
744
+ return;
745
+ }
746
+ reject(fromWireError(reply.error ?? {
747
+ message: "Native collaboration mode update failed",
748
+ code: "collaboration_mode_update_failed",
749
+ statusCode: 503,
750
+ }));
751
+ });
752
+ handle.transport.send({
753
+ t: "collaboration.update",
754
+ reqId,
755
+ localThreadId,
756
+ mode,
757
+ });
758
+ });
759
+ }
726
760
  // ── AgentCapabilities ──────────────────────────────────────────────────────
727
761
  async listModels(runtime) {
728
762
  return listRuntimeModels(this.config, runtime ?? this.defaultRuntime);
@@ -1047,15 +1081,56 @@ export class RunnerManager {
1047
1081
  if (handle.dead)
1048
1082
  return;
1049
1083
  if (message.t === "mirror") {
1050
- this.observeHandleRuntimeEvent(handle, message.event);
1051
- this.mirrorListener?.(message.sessionId, message.event);
1052
- if (message.event.type === "response.created") {
1053
- this.settleTerminalInputHandoff(handle, message.sessionId, "turn");
1054
- }
1084
+ this.deliverMirrorMessage(handle, message);
1055
1085
  return;
1056
1086
  }
1057
1087
  this.deliverRotateMessage(handle, message);
1058
1088
  }
1089
+ deliverMirrorMessage(handle, message) {
1090
+ if (handle.dead)
1091
+ return;
1092
+ if (message.event.type === "session.collaboration_mode") {
1093
+ this.collaborationModeListener?.(message.sessionId, message.event.mode);
1094
+ }
1095
+ this.observeHandleRuntimeEvent(handle, message.event);
1096
+ let delivered;
1097
+ try {
1098
+ delivered = Promise.resolve(this.mirrorListener?.(message.sessionId, message.event));
1099
+ }
1100
+ catch (error) {
1101
+ delivered = Promise.reject(error);
1102
+ }
1103
+ if (message.event.type === "response.created") {
1104
+ // The listener projects response.created into SessionRuntimeIndex
1105
+ // synchronously. Only then may the accepted terminal reservation drain
1106
+ // into a maintenance activity snapshot.
1107
+ this.settleTerminalInputHandoff(handle, message.sessionId, "turn");
1108
+ }
1109
+ if (message.transferId && message.seq !== undefined) {
1110
+ void delivered.then(() => {
1111
+ if (!handle.dead) {
1112
+ handle.transport.send({
1113
+ t: "mirror.image.ack",
1114
+ transferId: message.transferId,
1115
+ seq: message.seq,
1116
+ });
1117
+ }
1118
+ }, (error) => {
1119
+ this.failMirrorImageTransfer(handle, `generated image publication failed: ${errorMessage(error)}`);
1120
+ });
1121
+ }
1122
+ else {
1123
+ void delivered.catch((error) => {
1124
+ console.error(`Session mirror publication failed: ${errorMessage(error)}`);
1125
+ });
1126
+ }
1127
+ }
1128
+ failMirrorImageTransfer(handle, reason) {
1129
+ handle.imageTransfers.clear();
1130
+ void this.terminateHandle(handle, reason).catch((error) => {
1131
+ logTerminationFailure(handle, error);
1132
+ });
1133
+ }
1059
1134
  deliverRotateMessage(handle, message) {
1060
1135
  if (this.forkReservations.has(message.to) ||
1061
1136
  this.sourceForkReservations.has(message.from)) {
@@ -1190,6 +1265,7 @@ export class RunnerManager {
1190
1265
  caps: new Map(),
1191
1266
  terminals: new Map(),
1192
1267
  live: new Map(),
1268
+ imageTransfers: new Map(),
1193
1269
  };
1194
1270
  this.childHandles.add(handle);
1195
1271
  void completion.then(() => this.childHandles.delete(handle));
@@ -1248,15 +1324,83 @@ export class RunnerManager {
1248
1324
  return;
1249
1325
  if (this.bufferForkTargetMessage(handle, msg))
1250
1326
  return;
1251
- this.observeHandleRuntimeEvent(handle, msg.event);
1252
- this.mirrorListener?.(msg.sessionId, msg.event);
1253
- if (msg.event.type === "response.created") {
1254
- // The listener projects response.created into SessionRuntimeIndex
1255
- // synchronously. Only then may the accepted terminal reservation
1256
- // drain into a maintenance activity snapshot.
1257
- this.settleTerminalInputHandoff(handle, msg.sessionId, "turn");
1327
+ this.deliverMirrorMessage(handle, msg);
1328
+ return;
1329
+ case "mirror.image.begin": {
1330
+ if (handle.dead ||
1331
+ handle.imageTransfers.has(msg.transferId) ||
1332
+ !Number.isSafeInteger(msg.totalChars) ||
1333
+ msg.totalChars <= 0 ||
1334
+ msg.totalChars > RUNNER_IMAGE_MAX_RESULT_CHARS ||
1335
+ !isDetachedGeneratedImageEvent(msg.event)) {
1336
+ this.failMirrorImageTransfer(handle, "invalid generated image transfer start");
1337
+ return;
1338
+ }
1339
+ handle.imageTransfers.set(msg.transferId, {
1340
+ sessionId: msg.sessionId,
1341
+ event: msg.event,
1342
+ totalChars: msg.totalChars,
1343
+ receivedChars: 0,
1344
+ nextSeq: 1,
1345
+ chunks: [],
1346
+ });
1347
+ handle.transport.send({ t: "mirror.image.ack", transferId: msg.transferId, seq: 0 });
1348
+ return;
1349
+ }
1350
+ case "mirror.image.chunk": {
1351
+ const transfer = handle.imageTransfers.get(msg.transferId);
1352
+ if (handle.dead ||
1353
+ !transfer ||
1354
+ msg.seq !== transfer.nextSeq ||
1355
+ !msg.data ||
1356
+ msg.data.length > RUNNER_IMAGE_CHUNK_CHARS ||
1357
+ transfer.receivedChars + msg.data.length > transfer.totalChars) {
1358
+ this.failMirrorImageTransfer(handle, "invalid generated image transfer chunk");
1359
+ return;
1360
+ }
1361
+ transfer.chunks.push(msg.data);
1362
+ transfer.receivedChars += msg.data.length;
1363
+ transfer.nextSeq += 1;
1364
+ handle.transport.send({
1365
+ t: "mirror.image.ack",
1366
+ transferId: msg.transferId,
1367
+ seq: msg.seq,
1368
+ });
1369
+ return;
1370
+ }
1371
+ case "mirror.image.commit": {
1372
+ const transfer = handle.imageTransfers.get(msg.transferId);
1373
+ if (handle.dead ||
1374
+ !transfer ||
1375
+ msg.seq !== transfer.nextSeq ||
1376
+ transfer.receivedChars !== transfer.totalChars) {
1377
+ this.failMirrorImageTransfer(handle, "invalid generated image transfer commit");
1378
+ return;
1379
+ }
1380
+ handle.imageTransfers.delete(msg.transferId);
1381
+ const event = attachGeneratedImageResult(transfer.event, transfer.chunks.join(""));
1382
+ if (!event) {
1383
+ this.failMirrorImageTransfer(handle, "generated image transfer target changed");
1384
+ return;
1385
+ }
1386
+ const mirrored = {
1387
+ t: "mirror",
1388
+ sessionId: transfer.sessionId,
1389
+ event,
1390
+ transferId: msg.transferId,
1391
+ seq: msg.seq,
1392
+ };
1393
+ if (this.bufferForkTargetMessage(handle, mirrored)) {
1394
+ handle.transport.send({
1395
+ t: "mirror.image.hold",
1396
+ transferId: msg.transferId,
1397
+ seq: msg.seq,
1398
+ });
1399
+ return;
1258
1400
  }
1401
+ this.deliverMirrorMessage(handle, mirrored);
1259
1402
  return;
1403
+ }
1260
1404
  case "rotate": {
1261
1405
  if (handle.dead)
1262
1406
  return;
@@ -1275,6 +1419,15 @@ export class RunnerManager {
1275
1419
  : { ok: msg.ok, error: msg.error });
1276
1420
  return;
1277
1421
  }
1422
+ case "collaboration.updated": {
1423
+ const resolve = handle.live.get(msg.reqId);
1424
+ handle.live.delete(msg.reqId);
1425
+ resolve?.({
1426
+ collaborationUpdated: msg.ok,
1427
+ ...(msg.error ? { error: msg.error } : {}),
1428
+ });
1429
+ return;
1430
+ }
1278
1431
  case "interaction.resolved": {
1279
1432
  const resolve = handle.live.get(msg.reqId);
1280
1433
  handle.live.delete(msg.reqId);
@@ -1322,6 +1475,7 @@ export class RunnerManager {
1322
1475
  handle.caps.clear();
1323
1476
  handle.terminals.clear();
1324
1477
  handle.live.clear();
1478
+ handle.imageTransfers.clear();
1325
1479
  handle.transport.close();
1326
1480
  }
1327
1481
  terminateHandle(handle, reason) {
@@ -1684,6 +1838,36 @@ function closeSessionContext(context) {
1684
1838
  }));
1685
1839
  }
1686
1840
  }
1841
+ function isDetachedGeneratedImageEvent(event) {
1842
+ if (event.type !== "response.output_item.done" ||
1843
+ event.item.type !== "function_call_output") {
1844
+ return false;
1845
+ }
1846
+ const data = event.item.data;
1847
+ return Boolean(data.__rynxGeneratedImage && data.__rynxGeneratedImage.result === undefined);
1848
+ }
1849
+ function attachGeneratedImageResult(event, result) {
1850
+ if (!isDetachedGeneratedImageEvent(event))
1851
+ return undefined;
1852
+ const outputEvent = event;
1853
+ const item = outputEvent.item;
1854
+ if (item.type !== "function_call_output")
1855
+ return undefined;
1856
+ const data = item.data;
1857
+ return {
1858
+ ...outputEvent,
1859
+ item: {
1860
+ ...item,
1861
+ data: {
1862
+ ...data,
1863
+ __rynxGeneratedImage: {
1864
+ ...data.__rynxGeneratedImage,
1865
+ result,
1866
+ },
1867
+ },
1868
+ },
1869
+ };
1870
+ }
1687
1871
  function errorMessage(error) {
1688
1872
  return error instanceof Error ? error.message : String(error);
1689
1873
  }
@@ -6,7 +6,7 @@
6
6
  * (`live.ensure` / `inject` / `live.interrupt`) plus per-thread capabilities and
7
7
  * terminal attachment; the reply channels mirror each request's `reqId`.
8
8
  */
9
- import { AgentRuntimeError, type InjectResult, type ResolvedExecutionSnapshot, type RuntimeUserInput, type SessionInteractionResolution, type SessionEvent, type SessionWorkspaceSnapshot } from "@rynx-ai/core";
9
+ import { AgentRuntimeError, type InjectResult, type ResolvedExecutionSnapshot, type RuntimeTurnOptions, type RuntimeUserInput, type SessionCollaborationMode, type SessionInteractionResolution, type SessionEvent, type SessionWorkspaceSnapshot } from "@rynx-ai/core";
10
10
  export type { InjectOutcome, InjectResult } from "@rynx-ai/core";
11
11
  import type { ResolveInteractionResult } from "../interactions.js";
12
12
  /** A runtime error flattened for the wire; rebuilt parent-side as `AgentRuntimeError`. */
@@ -26,6 +26,14 @@ export type TerminalOpenErrorCode = "terminal_not_live" | "terminal_open_failed"
26
26
  /** Parent → child. Terminal PTY bytes ride `term.input` base64-encoded so the
27
27
  * NDJSON line framing stays intact (raw bytes contain newlines). */
28
28
  export type ToChild = {
29
+ t: "mirror.image.ack";
30
+ transferId: string;
31
+ seq: number;
32
+ } | {
33
+ t: "mirror.image.hold";
34
+ transferId: string;
35
+ seq: number;
36
+ } | {
29
37
  t: "cap";
30
38
  capId: string;
31
39
  name: CapName;
@@ -88,6 +96,7 @@ export type ToChild = {
88
96
  t: "inject";
89
97
  reqId: string;
90
98
  localThreadId: string;
99
+ options?: RuntimeTurnOptions;
91
100
  } & ({
92
101
  input: RuntimeUserInput;
93
102
  text?: never;
@@ -95,6 +104,14 @@ export type ToChild = {
95
104
  text: string;
96
105
  input?: never;
97
106
  }))
107
+ /** Apply a Codex-lineage collaboration mode to an already-loaded native
108
+ * thread. This control path never starts a runner or waits for a new Turn. */
109
+ | {
110
+ t: "collaboration.update";
111
+ reqId: string;
112
+ localThreadId: string;
113
+ mode: SessionCollaborationMode;
114
+ }
98
115
  /** Interrupt the session's active turn — the web Stop button. codex: app-server
99
116
  * `turn/interrupt`; claude: an Escape keystroke to the pane. The child replies
100
117
  * `interrupted` (echoing `reqId`). */
@@ -142,6 +159,24 @@ export type FromChild = {
142
159
  t: "mirror";
143
160
  sessionId: string;
144
161
  event: SessionEvent;
162
+ /** Parent-local completion marker used after a chunked image is rebuilt. */
163
+ transferId?: string;
164
+ seq?: number;
165
+ } | {
166
+ t: "mirror.image.begin";
167
+ transferId: string;
168
+ sessionId: string;
169
+ event: SessionEvent;
170
+ totalChars: number;
171
+ } | {
172
+ t: "mirror.image.chunk";
173
+ transferId: string;
174
+ seq: number;
175
+ data: string;
176
+ } | {
177
+ t: "mirror.image.commit";
178
+ transferId: string;
179
+ seq: number;
145
180
  }
146
181
  /** The session rotated to a fresh machine-session (claude `/clear`·`/fork`): the
147
182
  * child re-pointed its mirror to `to` and asks the daemon to alias the runner
@@ -184,12 +219,23 @@ export type FromChild = {
184
219
  localThreadId: string;
185
220
  ok: boolean;
186
221
  error?: WireError;
222
+ } | {
223
+ t: "collaboration.updated";
224
+ reqId: string;
225
+ localThreadId: string;
226
+ ok: boolean;
227
+ error?: WireError;
187
228
  } | {
188
229
  t: "interaction.resolved";
189
230
  reqId: string;
190
231
  localThreadId: string;
191
232
  result: ResolveInteractionResult;
192
233
  };
234
+ /** Internal runner wire bounds. Generated images are already subject to the
235
+ * Session image byte limit; chunks keep the NDJSON pipe and parser bounded and
236
+ * each chunk is acknowledged before the next is sent. */
237
+ export declare const RUNNER_IMAGE_CHUNK_CHARS: number;
238
+ export declare const RUNNER_IMAGE_MAX_RESULT_CHARS: number;
193
239
  /** Encode a message as a single NDJSON line (newline included). */
194
240
  export declare function encodeMessage(msg: ToChild | FromChild): string;
195
241
  /** Parse one NDJSON line; returns `null` for blank lines or malformed JSON. */
@@ -7,6 +7,11 @@
7
7
  * terminal attachment; the reply channels mirror each request's `reqId`.
8
8
  */
9
9
  import { AgentRuntimeError, } from "@rynx-ai/core";
10
+ /** Internal runner wire bounds. Generated images are already subject to the
11
+ * Session image byte limit; chunks keep the NDJSON pipe and parser bounded and
12
+ * each chunk is acknowledged before the next is sent. */
13
+ export const RUNNER_IMAGE_CHUNK_CHARS = 32 * 1024;
14
+ export const RUNNER_IMAGE_MAX_RESULT_CHARS = 8 * 1024 * 1024;
10
15
  /** Encode a message as a single NDJSON line (newline included). */
11
16
  export function encodeMessage(msg) {
12
17
  return `${JSON.stringify(msg)}\n`;
@@ -59,7 +59,7 @@ export class TerminalRegistry {
59
59
  const terminal = this.terminals.get(id);
60
60
  if (!terminal)
61
61
  return;
62
- // Omnigent removes the resource from its registry before best-effort close.
62
+ // reference implementation removes the resource from its registry before best-effort close.
63
63
  this.terminals.delete(id);
64
64
  terminal.kill();
65
65
  }
@@ -14,7 +14,7 @@ export interface TerminalAttachment {
14
14
  /** Result of probing the inner tmux pane. `unknown` is deliberately distinct
15
15
  * from `dead`: a timed-out/unexecutable control command is inconclusive;
16
16
  * `dead` requires an explicit dead pane. The lifecycle watcher separately
17
- * mirrors Omnigent's capture failure path for a vanished server/session. */
17
+ * mirrors reference implementation's capture failure path for a vanished server/session. */
18
18
  export type TerminalLiveness = "alive" | "dead" | "unknown";
19
19
  /** Minimal node-pty surface (kept local so this module has no type dep on it). */
20
20
  interface PtyProcess {
@@ -67,7 +67,7 @@ export declare function tmuxWindowActivityAt(name: string, tmuxBin?: string): Pr
67
67
  * This remains authoritative if a parent-side attachment bookkeeping edge was
68
68
  * missed during a transport failure. */
69
69
  export declare function tmuxHasAttachedClient(name: string, tmuxBin?: string): Promise<boolean>;
70
- /** Best-effort close of one private tmux server. Mirrors Omnigent's bounded
70
+ /** Best-effort close of one private tmux server. Mirrors reference implementation's bounded
71
71
  * `TerminalInstance.close`: attempt `kill-server`, then retire the private
72
72
  * socket regardless of command outcome. The registry has already forgotten
73
73
  * the resource, so cleanup failure is diagnostic rather than a second
@@ -93,7 +93,7 @@ export declare class TmuxTerminal {
93
93
  * out once per attached client. */
94
94
  private paneLivenessFlight?;
95
95
  /** Shared by lifecycle callers so capture + pane-dead remains one ordered
96
- * Omnigent-style observation per Terminal. */
96
+ * reference implementation-style observation per Terminal. */
97
97
  private lifecycleLivenessFlight?;
98
98
  constructor(opts: TmuxTerminalOptions);
99
99
  /** tmux argv prefix targeting this terminal's private server. */
@@ -138,10 +138,10 @@ export declare class TmuxTerminal {
138
138
  /** Async pane-liveness probe — MUST NOT block the event loop. The attach
139
139
  * pane-death watcher polls this on an interval; a synchronous `execFileSync`
140
140
  * there stalls the runner child's event loop (freezing the PTY stream → the
141
- * terminal appears "stuck"). Exactly like Omnigent's definitive pane probe,
141
+ * terminal appears "stuck"). Exactly like reference implementation's definitive pane probe,
142
142
  * every command error is `unknown`; only `#{pane_dead}=1` is `dead`. */
143
143
  livenessAsync(): Promise<TerminalLiveness>;
144
- /** Omnigent's always-on terminal lifecycle watcher first captures the pane:
144
+ /** reference implementation's always-on terminal lifecycle watcher first captures the pane:
145
145
  * a control command that ran and reports the target missing is terminal exit;
146
146
  * a probe that cannot spawn is inconclusive. If capture succeeds, the normal
147
147
  * definitive `pane_dead` probe distinguishes live from exited. */
@@ -79,7 +79,7 @@ export function tmuxHasAttachedClient(name, tmuxBin = resolveTmuxBin()) {
79
79
  });
80
80
  });
81
81
  }
82
- /** Best-effort close of one private tmux server. Mirrors Omnigent's bounded
82
+ /** Best-effort close of one private tmux server. Mirrors reference implementation's bounded
83
83
  * `TerminalInstance.close`: attempt `kill-server`, then retire the private
84
84
  * socket regardless of command outcome. The registry has already forgotten
85
85
  * the resource, so cleanup failure is diagnostic rather than a second
@@ -96,7 +96,7 @@ export function terminateTmuxServer(name, tmuxBin = resolveTmuxBin()) {
96
96
  });
97
97
  }
98
98
  catch {
99
- // Omnigent treats close as best-effort and still retires the resource.
99
+ // reference implementation treats close as best-effort and still retires the resource.
100
100
  }
101
101
  try {
102
102
  unlinkSync(socketPath);
@@ -146,7 +146,7 @@ export class TmuxTerminal {
146
146
  * out once per attached client. */
147
147
  paneLivenessFlight;
148
148
  /** Shared by lifecycle callers so capture + pane-dead remains one ordered
149
- * Omnigent-style observation per Terminal. */
149
+ * reference implementation-style observation per Terminal. */
150
150
  lifecycleLivenessFlight;
151
151
  constructor(opts) {
152
152
  this.name = opts.name;
@@ -285,7 +285,7 @@ export class TmuxTerminal {
285
285
  /** Async pane-liveness probe — MUST NOT block the event loop. The attach
286
286
  * pane-death watcher polls this on an interval; a synchronous `execFileSync`
287
287
  * there stalls the runner child's event loop (freezing the PTY stream → the
288
- * terminal appears "stuck"). Exactly like Omnigent's definitive pane probe,
288
+ * terminal appears "stuck"). Exactly like reference implementation's definitive pane probe,
289
289
  * every command error is `unknown`; only `#{pane_dead}=1` is `dead`. */
290
290
  livenessAsync() {
291
291
  if (!this.started)
@@ -320,7 +320,7 @@ export class TmuxTerminal {
320
320
  });
321
321
  return flight;
322
322
  }
323
- /** Omnigent's always-on terminal lifecycle watcher first captures the pane:
323
+ /** reference implementation's always-on terminal lifecycle watcher first captures the pane:
324
324
  * a control command that ran and reports the target missing is terminal exit;
325
325
  * a probe that cannot spawn is inconclusive. If capture succeeds, the normal
326
326
  * definitive `pane_dead` probe distinguishes live from exited. */
@@ -340,7 +340,7 @@ export class TmuxTerminal {
340
340
  }
341
341
  this.lastPaneSnapshot = stdout.toString();
342
342
  void this.livenessAsync().then((liveness) => {
343
- // A successful capture proves the target exists. Omnigent treats a
343
+ // A successful capture proves the target exists. reference implementation treats a
344
344
  // subsequent pane-dead probe failure as non-dead for this tick.
345
345
  resolve(liveness === "dead" ? "dead" : "alive");
346
346
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rynx-ai/runtime",
3
- "version": "0.1.11-beta.24",
3
+ "version": "0.1.11-beta.26",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/rynx-ai/rynx.git",
@@ -27,7 +27,7 @@
27
27
  "node-pty": "1.2.0-beta.15",
28
28
  "smol-toml": "1.7.1",
29
29
  "ws": "^8.21.0",
30
- "@rynx-ai/core": "0.1.11-beta.24"
30
+ "@rynx-ai/core": "0.1.11-beta.26"
31
31
  },
32
32
  "devDependencies": {
33
33
  "@types/ws": "^8.18.1"