@rynx-ai/runtime 0.1.11-beta.37 → 0.1.11-beta.39

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.
@@ -16,6 +16,10 @@ export interface WireError {
16
16
  code: string;
17
17
  statusCode: number;
18
18
  }
19
+ /** Parent classification for a mirror write. `ambiguous` means persistence
20
+ * crossed the bounded response deadline: the
21
+ * operation may still commit, so retrying it would risk a duplicate. */
22
+ export type MirrorNackClassification = "transient" | "permanent" | "ambiguous";
19
23
  /** Capabilities forwarded to a runner (parent serves status/listModels itself). */
20
24
  export type CapName = "getGoal" | "setGoal" | "clearGoal" | "forkSession";
21
25
  /** A live terminal role: `owner` is read-write, `read-only` a viewer. */
@@ -26,13 +30,46 @@ export type TerminalOpenErrorCode = "terminal_not_live" | "terminal_open_failed"
26
30
  /** Parent → child. Raw terminal bytes ride `term.input` base64-encoded so the
27
31
  * NDJSON line framing stays intact (raw bytes contain newlines). */
28
32
  export type ToChild = {
33
+ t: "mirror.ack";
34
+ deliveryId: string;
35
+ generation: number;
36
+ } | {
37
+ t: "mirror.nack";
38
+ deliveryId: string;
39
+ generation: number;
40
+ classification: MirrorNackClassification;
41
+ message: string;
42
+ } | {
29
43
  t: "mirror.image.ack";
30
44
  transferId: string;
31
45
  seq: number;
46
+ generation: number;
32
47
  } | {
33
48
  t: "mirror.image.hold";
34
49
  transferId: string;
35
50
  seq: number;
51
+ generation: number;
52
+ } | {
53
+ t: "mirror.image.nack";
54
+ transferId: string;
55
+ seq: number;
56
+ generation: number;
57
+ classification: MirrorNackClassification;
58
+ message: string;
59
+ } | {
60
+ t: "rotate.ack";
61
+ rotationId: string;
62
+ generation: number;
63
+ } | {
64
+ t: "rotate.nack";
65
+ rotationId: string;
66
+ generation: number;
67
+ classification: MirrorNackClassification;
68
+ message: string;
69
+ } | {
70
+ t: "rotate.applied.ack";
71
+ rotationId: string;
72
+ generation: number;
36
73
  } | {
37
74
  t: "cap";
38
75
  capId: string;
@@ -217,24 +254,49 @@ export type FromChild = {
217
254
  t: "mirror";
218
255
  sessionId: string;
219
256
  event: SessionEvent;
220
- /** Parent-local completion marker used after a chunked image is rebuilt. */
221
- transferId?: string;
222
- seq?: number;
257
+ /** Correlates the event with the parent's post-persistence ACK. */
258
+ deliveryId: string;
259
+ /** Logical mirror generation. A clear/fork supersedes every pending
260
+ * delivery from an older generation. */
261
+ generation: number;
262
+ /** One-based attempt for the same logical ordinary delivery. Lets the
263
+ * parent settle process-local projections exactly when the permanent
264
+ * retry budget is exhausted. */
265
+ attempt: number;
266
+ }
267
+ /** The child stopped waiting for an ordinary delivery after an ambiguous
268
+ * outcome or an exhausted permanent budget. The parent keeps the exact
269
+ * delivery-to-projection association until the listener settles, so a late
270
+ * persistence failure can retire only the matching optimistic input. */
271
+ | {
272
+ t: "mirror.abandon";
273
+ deliveryId: string;
274
+ generation: number;
223
275
  } | {
224
276
  t: "mirror.image.begin";
225
277
  transferId: string;
226
278
  sessionId: string;
227
279
  event: SessionEvent;
228
280
  totalChars: number;
281
+ generation: number;
229
282
  } | {
230
283
  t: "mirror.image.chunk";
231
284
  transferId: string;
232
285
  seq: number;
233
286
  data: string;
287
+ generation: number;
234
288
  } | {
235
289
  t: "mirror.image.commit";
236
290
  transferId: string;
237
291
  seq: number;
292
+ generation: number;
293
+ }
294
+ /** Child no longer waits for this transfer (ambiguous handled, permanent
295
+ * budget exhausted, or generation superseded); parent may release chunks. */
296
+ | {
297
+ t: "mirror.image.abandon";
298
+ transferId: string;
299
+ generation: number;
238
300
  } | {
239
301
  t: "terminal.lifecycle.ended";
240
302
  localThreadId: string;
@@ -256,11 +318,22 @@ export type FromChild = {
256
318
  t: "rotate";
257
319
  from: string;
258
320
  to: string;
321
+ rotationId: string;
322
+ generation: number;
259
323
  kind: "clear" | "fork";
260
324
  workspace: SessionWorkspaceSnapshot;
261
325
  execution: ResolvedExecutionSnapshot;
262
326
  parentSessionId?: string;
263
327
  }
328
+ /** Child-side terminal/runtime ownership has moved to the durable target.
329
+ * The parent must not expose the target routing alias before this frame. */
330
+ | {
331
+ t: "rotate.applied";
332
+ rotationId: string;
333
+ generation: number;
334
+ from: string;
335
+ to: string;
336
+ }
264
337
  /** Result of a `live.ensure`: for Codex-lineage sessions `ok` means backend
265
338
  * preload and pane launch completed. A resume observer and fresh-thread
266
339
  * discovery may continue in the background. Other Providers retain their
@@ -18,6 +18,15 @@ export interface RunnerTransport<TSend, TRecv> {
18
18
  export type ParentTransport = RunnerTransport<ToChild, FromChild>;
19
19
  /** Child-side transport: sends {@link FromChild}, receives {@link ToChild}. */
20
20
  export type ChildTransport = RunnerTransport<FromChild, ToChild>;
21
+ /** Structured write failure used by delivery code to distinguish a frame that
22
+ * definitely never entered the pipe from one whose post-write result is
23
+ * uncertain. */
24
+ export declare class RunnerTransportWriteError extends Error {
25
+ readonly delivery: "not-sent" | "ambiguous";
26
+ constructor(message: string, delivery: "not-sent" | "ambiguous", options?: {
27
+ cause?: unknown;
28
+ });
29
+ }
21
30
  /**
22
31
  * NDJSON-over-streams transport. `input` is line-read for inbound messages;
23
32
  * `output` receives one JSON line per {@link send}. Used on both ends:
@@ -11,6 +11,17 @@
11
11
  */
12
12
  import readline from "node:readline";
13
13
  import { decodeMessage, encodeMessage } from "./protocol.js";
14
+ /** Structured write failure used by delivery code to distinguish a frame that
15
+ * definitely never entered the pipe from one whose post-write result is
16
+ * uncertain. */
17
+ export class RunnerTransportWriteError extends Error {
18
+ delivery;
19
+ constructor(message, delivery, options) {
20
+ super(message, options);
21
+ this.delivery = delivery;
22
+ this.name = "RunnerTransportWriteError";
23
+ }
24
+ }
14
25
  /**
15
26
  * NDJSON-over-streams transport. `input` is line-read for inbound messages;
16
27
  * `output` receives one JSON line per {@link send}. Used on both ends:
@@ -77,9 +88,16 @@ export class StdioRunnerTransport {
77
88
  handler(error);
78
89
  }
79
90
  writeMessage(msg) {
80
- if (this.closed)
81
- return Promise.reject(new Error("runner transport is closed"));
82
- const encoded = encodeMessage(msg);
91
+ if (this.closed) {
92
+ return Promise.reject(new RunnerTransportWriteError("runner transport is closed before write", "not-sent"));
93
+ }
94
+ let encoded;
95
+ try {
96
+ encoded = encodeMessage(msg);
97
+ }
98
+ catch (error) {
99
+ return Promise.reject(new RunnerTransportWriteError("runner transport frame could not be encoded", "not-sent", { cause: error }));
100
+ }
83
101
  return new Promise((resolve, reject) => {
84
102
  let callbackDone = false;
85
103
  let drainDone = false;
@@ -104,17 +122,26 @@ export class StdioRunnerTransport {
104
122
  return;
105
123
  settled = true;
106
124
  cleanup();
107
- reject(error);
125
+ reject(new RunnerTransportWriteError("runner transport write result is uncertain", "ambiguous", { cause: error }));
108
126
  };
109
127
  this.output.once("error", onError);
110
- const accepted = this.output.write(encoded, (error) => {
111
- if (error) {
112
- onError(error);
113
- return;
114
- }
115
- callbackDone = true;
116
- finish();
117
- });
128
+ let accepted;
129
+ try {
130
+ accepted = this.output.write(encoded, (error) => {
131
+ if (error) {
132
+ onError(error);
133
+ return;
134
+ }
135
+ callbackDone = true;
136
+ finish();
137
+ });
138
+ }
139
+ catch (error) {
140
+ settled = true;
141
+ cleanup();
142
+ reject(new RunnerTransportWriteError("runner transport rejected the frame before write", "not-sent", { cause: error }));
143
+ return;
144
+ }
118
145
  drainDone = accepted;
119
146
  if (!accepted)
120
147
  this.output.once("drain", onDrain);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rynx-ai/runtime",
3
- "version": "0.1.11-beta.37",
3
+ "version": "0.1.11-beta.39",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/rynx-ai/rynx.git",
@@ -26,7 +26,7 @@
26
26
  "dependencies": {
27
27
  "smol-toml": "1.7.1",
28
28
  "ws": "^8.21.0",
29
- "@rynx-ai/core": "0.1.11-beta.37"
29
+ "@rynx-ai/core": "0.1.11-beta.39"
30
30
  },
31
31
  "devDependencies": {
32
32
  "@types/ws": "^8.18.1"