@rynx-ai/server 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.
Files changed (100) hide show
  1. package/dist/control-web/assets/{abnfDiagram-VRR7QNED-CmU_MmlR.js → abnfDiagram-VRR7QNED-D_NL89dn.js} +1 -1
  2. package/dist/control-web/assets/architecture-TIHT7OUA-C9wU1KtW.js +1 -0
  3. package/dist/control-web/assets/{architectureDiagram-ZJ3FMSHR-OecV-i7M.js → architectureDiagram-ZJ3FMSHR-CV0rxVH6.js} +1 -1
  4. package/dist/control-web/assets/{blockDiagram-677ZJIJ3-DOxDUxjd.js → blockDiagram-677ZJIJ3-Cc7nrkZh.js} +1 -1
  5. package/dist/control-web/assets/{c4Diagram-LMCZKHZV-D557d6cD.js → c4Diagram-LMCZKHZV-BPRU9m-F.js} +1 -1
  6. package/dist/control-web/assets/channel-BFm2_j8K.js +1 -0
  7. package/dist/control-web/assets/{chunk-32BRIVSS-BfXRqSpF.js → chunk-32BRIVSS-Bbq3O54m.js} +1 -1
  8. package/dist/control-web/assets/{chunk-52WLFC77-BeKggk68.js → chunk-52WLFC77-sD4al-Ob.js} +1 -1
  9. package/dist/control-web/assets/{chunk-C7G6YPKG-D0W6xHti.js → chunk-C7G6YPKG-DqHaDS1O.js} +1 -1
  10. package/dist/control-web/assets/{chunk-EX3LRPZG-D-s3PHl_.js → chunk-EX3LRPZG-36GNQ5wp.js} +1 -1
  11. package/dist/control-web/assets/{chunk-HOUHSVGY-Cz-4R9wb.js → chunk-HOUHSVGY-DeZ63q1j.js} +1 -1
  12. package/dist/control-web/assets/{chunk-ICXQ74PX-Jh1cDG2d.js → chunk-ICXQ74PX-D2yF6lD_.js} +1 -1
  13. package/dist/control-web/assets/{chunk-MOJQB5TN-Bne3Czi6.js → chunk-MOJQB5TN-CdaKyBz5.js} +1 -1
  14. package/dist/control-web/assets/{chunk-OGEWGWER-B3ysgOD5.js → chunk-OGEWGWER-eOXC7kiH.js} +1 -1
  15. package/dist/control-web/assets/{chunk-PUDLZKDR-CcXYtnKN.js → chunk-PUDLZKDR-6w0LILL_.js} +1 -1
  16. package/dist/control-web/assets/{chunk-Q4XR5HBZ-puXEgeqv.js → chunk-Q4XR5HBZ-CXgD5TGL.js} +1 -1
  17. package/dist/control-web/assets/{chunk-V7JOEXUC-C3tvTxnz.js → chunk-V7JOEXUC-BfoZ78b6.js} +1 -1
  18. package/dist/control-web/assets/{chunk-VR4S4FIN-DW_38atK.js → chunk-VR4S4FIN-BW2gCEMQ.js} +1 -1
  19. package/dist/control-web/assets/{chunk-WYO6CB5R-Bbp0lWuA.js → chunk-WYO6CB5R-CHn7hulw.js} +3 -3
  20. package/dist/control-web/assets/{chunk-ZGVPDNZ5-EJHunJJc.js → chunk-ZGVPDNZ5-CmFP0-fr.js} +1 -1
  21. package/dist/control-web/assets/classDiagram-OUVF2IWQ-CUe_bETB.js +1 -0
  22. package/dist/control-web/assets/classDiagram-v2-EOCWNBFH-CUe_bETB.js +1 -0
  23. package/dist/control-web/assets/{cynefin-VYW2F7L2-BaT-TB_Y.js → cynefin-VYW2F7L2-NaLr7Qlq.js} +1 -1
  24. package/dist/control-web/assets/{cynefinDiagram-TSTJHNR4-sW0lbfzR.js → cynefinDiagram-TSTJHNR4-C90wpjAI.js} +1 -1
  25. package/dist/control-web/assets/{dagre-VKFMJZFB-DszFEQ50.js → dagre-VKFMJZFB-BxrPFWTb.js} +1 -1
  26. package/dist/control-web/assets/{diagram-FQU43EPY-BPueVs2y.js → diagram-FQU43EPY-VIQINsvz.js} +1 -1
  27. package/dist/control-web/assets/{diagram-G47NLZAW-BSgnrddi.js → diagram-G47NLZAW-DFeAfYXn.js} +1 -1
  28. package/dist/control-web/assets/{diagram-NH7WQ7WH-1WyXWZVF.js → diagram-NH7WQ7WH-ATzdppDd.js} +1 -1
  29. package/dist/control-web/assets/{diagram-OA4YK3LP--O72jADz.js → diagram-OA4YK3LP-DJZpWmbl.js} +1 -1
  30. package/dist/control-web/assets/{diagram-WEI45ONY-B2QpDXZ9.js → diagram-WEI45ONY-CqDHcW3g.js} +1 -1
  31. package/dist/control-web/assets/{dist-Dv-qw3vC.js → dist-Dx9YWgcW.js} +1 -1
  32. package/dist/control-web/assets/{ebnfDiagram-CCIWWBDH-DSs-ystO.js → ebnfDiagram-CCIWWBDH-DnP6dHND.js} +1 -1
  33. package/dist/control-web/assets/{erDiagram-Q63AITRT-Dd6hMu35.js → erDiagram-Q63AITRT-Bo8bFqrZ.js} +1 -1
  34. package/dist/control-web/assets/eventmodeling-45OFAUF4-DTN8wqsu.js +1 -0
  35. package/dist/control-web/assets/flowDiagram-23GEKE2U-BwjqRyvd.js +1 -0
  36. package/dist/control-web/assets/{ganttDiagram-NO4QXBWP-COHCKhqO.js → ganttDiagram-NO4QXBWP-kxQaBTzd.js} +1 -1
  37. package/dist/control-web/assets/{gitGraph-TEB2WS4Q-CysYOV0u.js → gitGraph-TEB2WS4Q-a646NnuJ.js} +1 -1
  38. package/dist/control-web/assets/{gitGraphDiagram-IHSO6WYX-RDxV8G1J.js → gitGraphDiagram-IHSO6WYX-BBe0q0At.js} +1 -1
  39. package/dist/control-web/assets/{highlighted-body-OFNGDK62-D0q5eseS.js → highlighted-body-OFNGDK62-DVCHy9oI.js} +1 -1
  40. package/dist/control-web/assets/{index-CfRLpoTC.js → index-D7URT6K_.js} +23 -23
  41. package/dist/control-web/assets/{info-DKCQHKI2-C8E_rmu3.js → info-DKCQHKI2-Cp_JrY-b.js} +1 -1
  42. package/dist/control-web/assets/{infoDiagram-FWYZ7A6U-BmH8cm18.js → infoDiagram-FWYZ7A6U-CAm-h4F_.js} +1 -1
  43. package/dist/control-web/assets/{ishikawaDiagram-FXEZZL3T-DR1Vhzx7.js → ishikawaDiagram-FXEZZL3T-O17pqcWr.js} +1 -1
  44. package/dist/control-web/assets/{journeyDiagram-5HDEW3XC-C7gr8G1Z.js → journeyDiagram-5HDEW3XC-DQ-JuSoR.js} +1 -1
  45. package/dist/control-web/assets/{kanban-definition-HUTT4EX6-Cg_3ELUy.js → kanban-definition-HUTT4EX6-Cgv3kPbm.js} +1 -1
  46. package/dist/control-web/assets/katex-CyHG4N6b.js +1 -0
  47. package/dist/control-web/assets/{line-Dt86r9Z9.js → line-DUsXzGJZ.js} +1 -1
  48. package/dist/control-web/assets/{markdown-streamdown-OYO-HVz6.js → markdown-streamdown-4N4AcNp5.js} +8 -8
  49. package/dist/control-web/assets/mermaid-GHXKKRXX-BElZGKGd.js +1 -0
  50. package/dist/control-web/assets/{mermaid-parser.core-pxV_1Fef.js → mermaid-parser.core-VMln12Kd.js} +3 -3
  51. package/dist/control-web/assets/{mindmap-definition-LN4V7U3C-CBevDwDF.js → mindmap-definition-LN4V7U3C-Bn0lH-UM.js} +1 -1
  52. package/dist/control-web/assets/{packet-7NZHBO7P-A9sKZzEL.js → packet-7NZHBO7P-Zw8iJ8lo.js} +1 -1
  53. package/dist/control-web/assets/{pegDiagram-2B236MQR-BTIvrl_V.js → pegDiagram-2B236MQR-CtF7vVs_.js} +1 -1
  54. package/dist/control-web/assets/{pie-RZYD4A2V-DUzb4QBE.js → pie-RZYD4A2V-su_3QGU2.js} +1 -1
  55. package/dist/control-web/assets/{pieDiagram-ENE6RG2P-CzmJJofh.js → pieDiagram-ENE6RG2P-BkMUBLE1.js} +1 -1
  56. package/dist/control-web/assets/{quadrantDiagram-ABIIQ3AL-D6bXvpAG.js → quadrantDiagram-ABIIQ3AL-ba-VaqMe.js} +1 -1
  57. package/dist/control-web/assets/{radar-I7S5WNFK-BV54nFux.js → radar-I7S5WNFK-Dat0rqem.js} +1 -1
  58. package/dist/control-web/assets/{railroad-3IZDKUUU-B2cY_UXv.js → railroad-3IZDKUUU-Dh2-s8r8.js} +1 -1
  59. package/dist/control-web/assets/railroad-abnf-AHOZXSZD-CNUwq0JR.js +1 -0
  60. package/dist/control-web/assets/railroad-ebnf-EBAXGLYW-RHr0duea.js +1 -0
  61. package/dist/control-web/assets/railroad-peg-LSFZ7HO6-CVBWS2eU.js +1 -0
  62. package/dist/control-web/assets/{railroadDiagram-RFXS5EU6-D_fo16vD.js → railroadDiagram-RFXS5EU6-BiJX3SY-.js} +1 -1
  63. package/dist/control-web/assets/{requirementDiagram-TGXJPOKE-D2yP7TUp.js → requirementDiagram-TGXJPOKE-CFfakCzs.js} +1 -1
  64. package/dist/control-web/assets/{sankeyDiagram-HTMAVEWB-By_qJ9BM.js → sankeyDiagram-HTMAVEWB-jgDi0God.js} +1 -1
  65. package/dist/control-web/assets/{sequenceDiagram-DBY2YBRQ-DgX1K1l4.js → sequenceDiagram-DBY2YBRQ-DBfyyb0N.js} +1 -1
  66. package/dist/control-web/assets/{stateDiagram-2N3HPSRC-CeUgjm3D.js → stateDiagram-2N3HPSRC-Dwb_ncVi.js} +1 -1
  67. package/dist/control-web/assets/stateDiagram-v2-6OUMAXLB-DI0opaA1.js +1 -0
  68. package/dist/control-web/assets/{swimlanes-5IMT3BWC-CTlZZmMW.js → swimlanes-5IMT3BWC-DW0LuiIV.js} +1 -1
  69. package/dist/control-web/assets/swimlanesDiagram-G3AALYLV-DDTVjcOR.js +8 -0
  70. package/dist/control-web/assets/{timeline-definition-FHXFAJF6-Cq3seh59.js → timeline-definition-FHXFAJF6-DeX4B_B8.js} +1 -1
  71. package/dist/control-web/assets/{treeView-QDETBFTQ-Cumz-G5q.js → treeView-QDETBFTQ-D1UXtICj.js} +1 -1
  72. package/dist/control-web/assets/{treemap-6X3UGDF4-Bq5v5pf6.js → treemap-6X3UGDF4-CrppzjZH.js} +1 -1
  73. package/dist/control-web/assets/{vennDiagram-L72KCM5P-Bx9PN1Pw.js → vennDiagram-L72KCM5P-BYpf-7VR.js} +1 -1
  74. package/dist/control-web/assets/{wardley-OPB4EBWU-DDRYn_rw.js → wardley-OPB4EBWU-B9rA0UsV.js} +1 -1
  75. package/dist/control-web/assets/{wardleyDiagram-EHGQE667-DOHp80dH.js → wardleyDiagram-EHGQE667-BdvAUa5F.js} +1 -1
  76. package/dist/control-web/assets/{xychartDiagram-FW5EYKEG-kaZ1Sn-e.js → xychartDiagram-FW5EYKEG-BaEV3pGm.js} +1 -1
  77. package/dist/control-web/index.html +1 -1
  78. package/dist/machine-session-service.d.ts +7 -1
  79. package/dist/machine-session-service.js +12 -1
  80. package/dist/remote-runtime-session-projection.js +6 -0
  81. package/dist/server.d.ts +17 -3
  82. package/dist/server.js +155 -32
  83. package/dist/session-pending-inputs.d.ts +2 -0
  84. package/dist/session-pending-inputs.js +6 -3
  85. package/dist/session-runtime-index.d.ts +1 -0
  86. package/dist/session-runtime-index.js +28 -2
  87. package/package.json +7 -7
  88. package/dist/control-web/assets/architecture-TIHT7OUA-BA6n-Dzl.js +0 -1
  89. package/dist/control-web/assets/channel-CvSP6WIQ.js +0 -1
  90. package/dist/control-web/assets/classDiagram-OUVF2IWQ-CbpSOtxx.js +0 -1
  91. package/dist/control-web/assets/classDiagram-v2-EOCWNBFH-CbpSOtxx.js +0 -1
  92. package/dist/control-web/assets/eventmodeling-45OFAUF4-B4JspsqG.js +0 -1
  93. package/dist/control-web/assets/flowDiagram-23GEKE2U-Ium-zrch.js +0 -1
  94. package/dist/control-web/assets/katex-BSYj5LXs.js +0 -1
  95. package/dist/control-web/assets/mermaid-GHXKKRXX-BybKptJt.js +0 -1
  96. package/dist/control-web/assets/railroad-abnf-AHOZXSZD-C6gyeMKO.js +0 -1
  97. package/dist/control-web/assets/railroad-ebnf-EBAXGLYW-CTnsShXg.js +0 -1
  98. package/dist/control-web/assets/railroad-peg-LSFZ7HO6-NgilCakK.js +0 -1
  99. package/dist/control-web/assets/stateDiagram-v2-6OUMAXLB-CsJT1Rvc.js +0 -1
  100. package/dist/control-web/assets/swimlanesDiagram-G3AALYLV-CzaTMmXS.js +0 -8
package/dist/server.js CHANGED
@@ -5,6 +5,7 @@ import Router from "@koa/router";
5
5
  import { loadConfig, listAgentSpecs, loadAgentSpec, resolveSessionExecution, resolveRuntimeModel, ConversationRuntime, InMemorySessionBus, persistSessionEvent, } from "@rynx-ai/core";
6
6
  import { RunnerManager, FileCodexSessionStore, resolveCodexSessionStorePath, } from "@rynx-ai/runtime";
7
7
  import { createControlRouter, } from "./control-api.js";
8
+ import { MachineSessionServiceFailure, MachineSessionServiceInputError, } from "./machine-session-service.js";
8
9
  import { attachEmulatorTouchWs } from "./emulator-touch-ws.js";
9
10
  import { attachTerminalWs } from "./terminal-ws.js";
10
11
  import { attachBrowserSurfaceWs } from "./browser-surface-ws.js";
@@ -29,25 +30,74 @@ export { attachDirectRuntimeBrowserInspectServer, DirectRuntimeBrowserInspectHos
29
30
  export { RemoteRuntimeDispatcher } from "./remote-runtime-dispatcher.js";
30
31
  export { createRunnerSessionTerminalHost, SessionTerminalOpenError, SessionTerminalStreamError, } from "./session-terminal-host.js";
31
32
  export { listRuntimeModels } from "@rynx-ai/runtime";
32
- /** Preserve one session's mirror order without serializing unrelated sessions. */
33
+ /** Bound one mirror write at ten seconds. After that uncertainty boundary,
34
+ * later records may start instead
35
+ * of waiting forever behind a persistence call whose result is unknown. */
36
+ const SESSION_MIRROR_STALL_TIMEOUT_MS = 10_000;
37
+ class SessionMirrorAmbiguousError extends Error {
38
+ abandonToken;
39
+ mirrorClassification = "ambiguous";
40
+ constructor(sessionId, abandonToken) {
41
+ super(`Session mirror persistence did not settle before its response deadline: ${sessionId}`);
42
+ this.abandonToken = abandonToken;
43
+ this.name = "SessionMirrorAmbiguousError";
44
+ }
45
+ }
46
+ function preparedMirrorPersistence(persistence) {
47
+ if (typeof persistence === "object" &&
48
+ persistence !== null &&
49
+ "promise" in persistence)
50
+ return persistence;
51
+ return { promise: persistence };
52
+ }
53
+ /** Preserve one session's mirror order without serializing unrelated sessions.
54
+ * A timed-out operation is not cancelled: like an HTTP read timeout, it may
55
+ * still commit later. Its caller receives `ambiguous` and must not retry. */
33
56
  export class SessionMirrorQueue {
34
57
  persist;
58
+ stallTimeoutMs;
35
59
  tails = new Map();
36
- constructor(persist) {
60
+ constructor(persist, stallTimeoutMs = SESSION_MIRROR_STALL_TIMEOUT_MS) {
37
61
  this.persist = persist;
62
+ this.stallTimeoutMs = stallTimeoutMs;
38
63
  }
39
64
  enqueue(sessionId, event) {
40
65
  const previous = this.tails.get(sessionId) ?? Promise.resolve();
41
- const next = previous
66
+ const bounded = previous
42
67
  .catch(() => undefined)
43
- .then(() => this.persist(sessionId, event));
44
- this.tails.set(sessionId, next);
68
+ .then(() => {
69
+ // Prepare only when this record reaches the head of its Session lane.
70
+ // Starting either persistence or its deadline while an older record is
71
+ // still ahead would shorten queued records' attempt windows and allow a
72
+ // burst of them to time out concurrently.
73
+ const prepared = preparedMirrorPersistence(this.persist(sessionId, event));
74
+ let timer;
75
+ return new Promise((resolve, reject) => {
76
+ timer = setTimeout(() => {
77
+ // A user echo claims one exact optimistic pending input while the
78
+ // write starts. Retire it synchronously before rejecting this tail:
79
+ // the next echo must never peek the same content or attachments.
80
+ prepared.onAmbiguous?.();
81
+ reject(new SessionMirrorAmbiguousError(sessionId, prepared.abandonToken));
82
+ }, this.stallTimeoutMs);
83
+ timer.unref?.();
84
+ void prepared.promise.then(resolve, reject);
85
+ }).finally(() => {
86
+ if (timer)
87
+ clearTimeout(timer);
88
+ });
89
+ });
90
+ // The bounded result, not the potentially immortal persistence promise,
91
+ // gates the next record. `operation` remains observed by `bounded`, so a
92
+ // late rejection cannot become an unhandled promise rejection.
93
+ const tail = bounded.catch(() => undefined);
94
+ this.tails.set(sessionId, tail);
45
95
  const cleanup = () => {
46
- if (this.tails.get(sessionId) === next)
96
+ if (this.tails.get(sessionId) === tail)
47
97
  this.tails.delete(sessionId);
48
98
  };
49
- void next.then(cleanup, cleanup);
50
- return next;
99
+ void tail.then(cleanup, cleanup);
100
+ return bounded;
51
101
  }
52
102
  }
53
103
  /**
@@ -68,31 +118,57 @@ export function createSessionRuntimeServices(input) {
68
118
  });
69
119
  const sessionBus = new InMemorySessionBus();
70
120
  const sessionRuntimeIndex = new SessionRuntimeIndex();
71
- const sessionMirrorQueue = new SessionMirrorQueue(async (sessionId, event) => {
72
- const generatedImageFinalized = await finalizeGeneratedImageEvent(sessionId, event, input);
73
- const pendingFinalized = resolvePendingInputEvent(sessionId, generatedImageFinalized, input.pendingInputs);
74
- const finalized = pendingFinalized.event;
75
- sessionRuntimeIndex.observe(sessionId, finalized);
76
- await persistSessionEvent(sessionId, finalized, {
77
- sessionLog: input.sessionLog,
78
- sessionBus,
79
- ...(input.onPendingInputPersisted
121
+ const sessionMirrorQueue = new SessionMirrorQueue((sessionId, event) => {
122
+ // Claim the exact pending projection synchronously when this record reaches
123
+ // the head of its Session lane. Its id remains stable even if persistence
124
+ // later times out or fails after the result becomes ambiguous.
125
+ const pendingResolved = resolvePendingInputEvent(sessionId, event, input.pendingInputs);
126
+ const pendingId = pendingResolved.pendingId;
127
+ const promise = (async () => {
128
+ const finalized = await finalizeGeneratedImageEvent(sessionId, pendingResolved.event, input);
129
+ try {
130
+ await persistSessionEvent(sessionId, finalized, {
131
+ sessionLog: input.sessionLog,
132
+ sessionBus,
133
+ strictPersistence: true,
134
+ ...(pendingId
135
+ ? {
136
+ onInputPersisted: async (persistedEvent) => {
137
+ if (persistedEvent.clearedPendingId !== pendingId)
138
+ return;
139
+ input.pendingInputs?.resolve(sessionId, pendingId);
140
+ if (!input.onPendingInputPersisted || !pendingResolved.clientMessageId)
141
+ return;
142
+ await input.onPendingInputPersisted({
143
+ sessionId,
144
+ pendingId: persistedEvent.clearedPendingId,
145
+ clientMessageId: pendingResolved.clientMessageId,
146
+ responseId: persistedEvent.item.responseId,
147
+ messageItemId: persistedEvent.item.id,
148
+ });
149
+ },
150
+ }
151
+ : {}),
152
+ });
153
+ }
154
+ catch (error) {
155
+ if (pendingId) {
156
+ throw new PendingInputMirrorPersistenceError(error, pendingId);
157
+ }
158
+ throw error;
159
+ }
160
+ sessionRuntimeIndex.observe(sessionId, finalized);
161
+ })();
162
+ return {
163
+ promise,
164
+ ...(pendingId
80
165
  ? {
81
- onInputPersisted: async (persistedEvent) => {
82
- if (!persistedEvent.clearedPendingId || !pendingFinalized.clientMessageId)
83
- return;
84
- await input.onPendingInputPersisted({
85
- sessionId,
86
- pendingId: persistedEvent.clearedPendingId,
87
- clientMessageId: pendingFinalized.clientMessageId,
88
- responseId: persistedEvent.item.responseId,
89
- messageItemId: persistedEvent.item.id,
90
- });
91
- },
166
+ abandonToken: pendingId,
167
+ onAmbiguous: () => input.pendingInputs?.resolve(sessionId, pendingId),
92
168
  }
93
169
  : {}),
94
- });
95
- });
170
+ };
171
+ }, input.mirrorStallTimeoutMs);
96
172
  const mirrorSessionEvent = (sessionId, event) => {
97
173
  return sessionMirrorQueue.enqueue(sessionId, event);
98
174
  };
@@ -104,8 +180,15 @@ export function createSessionRuntimeServices(input) {
104
180
  runnerManager.onMirror((sessionId, event) => {
105
181
  return mirrorSessionEvent(sessionId, event).catch((error) => {
106
182
  input.onMirrorError?.(error, sessionId);
183
+ throw error;
107
184
  });
108
185
  });
186
+ runnerManager.onMirrorAbandon((sessionId, pendingId) => {
187
+ input.pendingInputs?.resolve(sessionId, pendingId);
188
+ });
189
+ runnerManager.onMirrorSupersede((sessionId) => {
190
+ input.pendingInputs?.deleteSession(sessionId);
191
+ });
109
192
  return {
110
193
  sessionStore,
111
194
  runnerManager,
@@ -115,6 +198,20 @@ export function createSessionRuntimeServices(input) {
115
198
  publishSessionEvent: mirrorSessionEvent,
116
199
  };
117
200
  }
201
+ class PendingInputMirrorPersistenceError extends Error {
202
+ abandonToken;
203
+ statusCode;
204
+ constructor(error, abandonToken) {
205
+ super(error instanceof Error ? error.message : String(error), { cause: error });
206
+ this.abandonToken = abandonToken;
207
+ this.name = "PendingInputMirrorPersistenceError";
208
+ if (typeof error === "object" &&
209
+ error !== null &&
210
+ typeof error.statusCode === "number") {
211
+ this.statusCode = error.statusCode;
212
+ }
213
+ }
214
+ }
118
215
  /** Drain the oldest pending web input when the Provider transcript produces a
119
216
  * real user record. The transcript is the only durable writer; the returned id
120
217
  * lets clients replace the exact temporary bubble. */
@@ -122,10 +219,11 @@ function resolvePendingInputEvent(sessionId, event, pendingInputs) {
122
219
  if (event.type !== "session.input.consumed" ||
123
220
  event.item.type !== "message" ||
124
221
  event.item.data.role !== "user" ||
125
- event.item.data.isMeta) {
222
+ event.item.data.isMeta ||
223
+ event.item.parentToolCallId !== undefined) {
126
224
  return { event };
127
225
  }
128
- const pending = pendingInputs?.resolveOldest(sessionId);
226
+ const pending = pendingInputs?.peekOldest(sessionId);
129
227
  if (!pending)
130
228
  return { event };
131
229
  const fileParts = pending.content.filter((part) => part.type === "input_image" || part.type === "input_file");
@@ -138,6 +236,7 @@ function resolvePendingInputEvent(sessionId, event, pendingInputs) {
138
236
  ? [...fileParts, ...event.item.data.content]
139
237
  : event.item.data.content;
140
238
  return {
239
+ pendingId: pending.pendingId,
141
240
  clientMessageId: pending.clientMessageId,
142
241
  event: {
143
242
  ...event,
@@ -199,6 +298,14 @@ async function finalizeGeneratedImageEvent(sessionId, event, input) {
199
298
  }
200
299
  catch (error) {
201
300
  input.onMirrorError?.(error, sessionId);
301
+ // The source bytes are part of an ordinary durable delivery.
302
+ // Only deterministic validation/conflict failures may discard their
303
+ // preview; infrastructure and explicitly retryable resource failures must
304
+ // NACK so the child retries the exact image commit.
305
+ if (!(error instanceof MachineSessionServiceFailure) ||
306
+ error.retryable) {
307
+ throw error;
308
+ }
202
309
  return {
203
310
  ...event,
204
311
  item: {
@@ -421,6 +528,22 @@ export async function startServer({ config = loadConfig(), control, sessionLog,
421
528
  sourceSessionId: r.from,
422
529
  targetSessionId: r.to,
423
530
  kind: r.kind,
531
+ }).catch((error) => {
532
+ if (error instanceof MachineSessionServiceInputError) {
533
+ throw Object.assign(error, { statusCode: 400 });
534
+ }
535
+ if (error instanceof MachineSessionServiceFailure) {
536
+ // A mismatched pre-existing target is a deterministic payload conflict,
537
+ // not an HTTP-style concurrent 409. Surface it as permanent so the
538
+ // three-attempt permanent-error budget can stop the rotation lane.
539
+ const statusCode = error.code === "outcome_unknown"
540
+ ? 503
541
+ : error.code === "not_found"
542
+ ? 404
543
+ : 422;
544
+ throw Object.assign(error, { statusCode });
545
+ }
546
+ throw error;
424
547
  });
425
548
  });
426
549
  if (localMachineSessions) {
@@ -13,6 +13,7 @@ export interface MachineSessionDrainedInput extends MachineSessionPendingInput {
13
13
  export interface MachineSessionPendingInputPort {
14
14
  record(sessionId: string, input: MachineSessionPendingInputRecord): string;
15
15
  resolve(sessionId: string, pendingId: string): void;
16
+ peekOldest(sessionId: string): MachineSessionDrainedInput | undefined;
16
17
  resolveOldest(sessionId: string): MachineSessionDrainedInput | undefined;
17
18
  findByClientMessageId(sessionId: string, clientMessageId: string): MachineSessionPendingInput | undefined;
18
19
  snapshotFor(sessionId: string): MachineSessionPendingInput[];
@@ -33,6 +34,7 @@ export declare class SessionPendingInputIndex implements MachineSessionPendingIn
33
34
  record(sessionId: string, input: MachineSessionPendingInputRecord): string;
34
35
  resolve(sessionId: string, pendingId: string): void;
35
36
  resolveOldest(sessionId: string): MachineSessionDrainedInput | undefined;
37
+ peekOldest(sessionId: string): MachineSessionDrainedInput | undefined;
36
38
  findByClientMessageId(sessionId: string, clientMessageId: string): MachineSessionPendingInput | undefined;
37
39
  snapshotFor(sessionId: string): MachineSessionPendingInput[];
38
40
  deleteSession(sessionId: string): void;
@@ -37,15 +37,18 @@ export class SessionPendingInputIndex {
37
37
  this.entries.delete(sessionId);
38
38
  }
39
39
  resolveOldest(sessionId) {
40
+ const input = this.peekOldest(sessionId);
41
+ if (input)
42
+ this.resolve(sessionId, input.pendingId);
43
+ return input;
44
+ }
45
+ peekOldest(sessionId) {
40
46
  this.evictStale(sessionId);
41
47
  const pending = this.entries.get(sessionId);
42
48
  const pendingId = pending?.keys().next().value;
43
49
  if (!pending || !pendingId)
44
50
  return undefined;
45
51
  const input = pending.get(pendingId);
46
- pending.delete(pendingId);
47
- if (pending.size === 0)
48
- this.entries.delete(sessionId);
49
52
  return input ? cloneDrainedInput(input) : undefined;
50
53
  }
51
54
  findByClientMessageId(sessionId, clientMessageId) {
@@ -23,4 +23,5 @@ export declare class SessionRuntimeIndex {
23
23
  private hasRunningTurn;
24
24
  private statusAfterResponseTerminal;
25
25
  private retireStatusResponses;
26
+ private markResponseTerminal;
26
27
  }
@@ -1,3 +1,4 @@
1
+ const MAX_TERMINAL_RESPONSE_IDS = 2_000;
1
2
  /**
2
3
  * Process-local index of live Session state. Durable transcript items explain
3
4
  * history; this index says which historical requests still have a real runtime
@@ -10,18 +11,22 @@ export class SessionRuntimeIndex {
10
11
  const state = this.state(sessionId);
11
12
  switch (event.type) {
12
13
  case "response.created":
14
+ if (state.terminalResponseIds.has(event.responseId))
15
+ return;
13
16
  state.activeResponseIds.add(event.responseId);
14
17
  state.status = "running";
15
18
  state.statusKind = undefined;
16
19
  state.backgroundTaskCount = undefined;
17
20
  return;
18
21
  case "response.completed":
22
+ this.markResponseTerminal(state, event.responseId);
19
23
  state.activeResponseIds.delete(event.responseId);
20
24
  this.removeResponseInteractions(state, event.responseId);
21
25
  state.status = this.statusAfterResponseTerminal(state);
22
26
  state.statusKind = undefined;
23
27
  return;
24
28
  case "response.failed":
29
+ this.markResponseTerminal(state, event.responseId);
25
30
  state.activeResponseIds.delete(event.responseId);
26
31
  this.removeResponseInteractions(state, event.responseId);
27
32
  // Response failure is Turn-local. It must not poison a reusable Session.
@@ -29,6 +34,7 @@ export class SessionRuntimeIndex {
29
34
  state.statusKind = undefined;
30
35
  return;
31
36
  case "session.interrupted":
37
+ this.markResponseTerminal(state, event.responseId);
32
38
  state.activeResponseIds.delete(event.responseId);
33
39
  this.removeResponseInteractions(state, event.responseId);
34
40
  state.status = this.statusAfterResponseTerminal(state);
@@ -44,6 +50,8 @@ export class SessionRuntimeIndex {
44
50
  state.backgroundTaskCount = undefined;
45
51
  }
46
52
  if (event.status === "running") {
53
+ if (event.responseId && state.terminalResponseIds.has(event.responseId))
54
+ return;
47
55
  if (event.responseId)
48
56
  state.activeResponseIds.add(event.responseId);
49
57
  state.status = "running";
@@ -58,7 +66,7 @@ export class SessionRuntimeIndex {
58
66
  state.statusKind = undefined;
59
67
  return;
60
68
  }
61
- this.retireStatusResponses(state);
69
+ this.retireStatusResponses(state, event.responseId);
62
70
  if (event.status === "failed") {
63
71
  state.status = "failed";
64
72
  }
@@ -72,6 +80,8 @@ export class SessionRuntimeIndex {
72
80
  return;
73
81
  }
74
82
  case "session.interaction.requested":
83
+ if (state.terminalResponseIds.has(event.responseId))
84
+ return;
75
85
  state.pendingInteractions.set(event.interaction.interactionId, {
76
86
  responseId: event.responseId,
77
87
  interaction: event.interaction,
@@ -132,6 +142,7 @@ export class SessionRuntimeIndex {
132
142
  state = {
133
143
  status: "idle",
134
144
  activeResponseIds: new Set(),
145
+ terminalResponseIds: new Set(),
135
146
  pendingInteractions: new Map(),
136
147
  };
137
148
  this.sessions.set(sessionId, state);
@@ -152,13 +163,28 @@ export class SessionRuntimeIndex {
152
163
  return "running";
153
164
  return state.status === "failed" ? "failed" : "idle";
154
165
  }
155
- retireStatusResponses(state) {
166
+ retireStatusResponses(state, responseId) {
156
167
  // session.status is Session-wide. Match reference implementation's
157
168
  // _session_active_response_cache: every idle/failed edge closes the one
158
169
  // in-flight native response projection, even when the edge happens to be
159
170
  // stamped with that response id. A late transcript event may still carry
160
171
  // the old id for grouping, but it cannot keep activity alive.
172
+ for (const activeResponseId of state.activeResponseIds) {
173
+ this.markResponseTerminal(state, activeResponseId);
174
+ }
175
+ if (responseId)
176
+ this.markResponseTerminal(state, responseId);
161
177
  state.activeResponseIds.clear();
162
178
  state.pendingInteractions.clear();
163
179
  }
180
+ markResponseTerminal(state, responseId) {
181
+ state.terminalResponseIds.delete(responseId);
182
+ state.terminalResponseIds.add(responseId);
183
+ while (state.terminalResponseIds.size > MAX_TERMINAL_RESPONSE_IDS) {
184
+ const oldest = state.terminalResponseIds.values().next().value;
185
+ if (oldest === undefined)
186
+ break;
187
+ state.terminalResponseIds.delete(oldest);
188
+ }
189
+ }
164
190
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rynx-ai/server",
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",
@@ -27,15 +27,15 @@
27
27
  "@koa/router": "^15.4.0",
28
28
  "koa": "^3.2.0",
29
29
  "ws": "^8.21.0",
30
- "@rynx-ai/plugin-sdk": "0.1.11-beta.37",
31
- "@rynx-ai/core": "0.1.11-beta.37",
32
- "@rynx-ai/protocol": "0.1.11-beta.37",
33
- "@rynx-ai/remote-runtime-e2ee": "0.1.11-beta.37",
34
- "@rynx-ai/runtime": "0.1.11-beta.37"
30
+ "@rynx-ai/core": "0.1.11-beta.39",
31
+ "@rynx-ai/plugin-sdk": "0.1.11-beta.39",
32
+ "@rynx-ai/protocol": "0.1.11-beta.39",
33
+ "@rynx-ai/remote-runtime-e2ee": "0.1.11-beta.39",
34
+ "@rynx-ai/runtime": "0.1.11-beta.39"
35
35
  },
36
36
  "devDependencies": {
37
37
  "@types/ws": "^8.18.1",
38
- "@rynx-ai/control-web": "0.1.11-beta.37"
38
+ "@rynx-ai/control-web": "0.1.11-beta.39"
39
39
  },
40
40
  "scripts": {
41
41
  "build": "rm -rf dist && tsc -p tsconfig.json && mkdir -p dist/control-web && cp -R ../control-web/dist/. dist/control-web/",
@@ -1 +0,0 @@
1
- import"./chunk-KEIR6QF5-BICK3FdT.js";import{M as e}from"./mermaid-parser.core-pxV_1Fef.js";export{e as createArchitectureServices};
@@ -1 +0,0 @@
1
- import{at as e,ot as t}from"./chunk-WYO6CB5R-Bbp0lWuA.js";var n=(n,r)=>t.lang.round(e.parse(n)[r]);export{n as t};
@@ -1 +0,0 @@
1
- import{n as e}from"./chunk-Y2CYZVJY-DsF7k-Jl.js";import"./src-B6xuSHsQ.js";import"./chunk-WYO6CB5R-Bbp0lWuA.js";import"./chunk-ICXQ74PX-Jh1cDG2d.js";import"./chunk-HOUHSVGY-Cz-4R9wb.js";import"./chunk-Q4XR5HBZ-puXEgeqv.js";import"./chunk-7BUUIJ7U-Bb538aSH.js";import"./chunk-OGEWGWER-B3ysgOD5.js";import"./chunk-32BRIVSS-BfXRqSpF.js";import"./chunk-XXDRQBXY-DqAYeljN.js";import"./chunk-VR4S4FIN-DW_38atK.js";import"./chunk-C7G6YPKG-D0W6xHti.js";import"./chunk-ZGVPDNZ5-EJHunJJc.js";import"./chunk-52WLFC77-BeKggk68.js";import{i as t,n,r,t as i}from"./chunk-V7JOEXUC-C3tvTxnz.js";import"./markdown-streamdown-OYO-HVz6.js";var a={parser:n,get db(){return new i},renderer:r,styles:t,init:e(e=>{e.class||={},e.class.arrowMarkerAbsolute=e.arrowMarkerAbsolute},`init`)};export{a as diagram};
@@ -1 +0,0 @@
1
- import{n as e}from"./chunk-Y2CYZVJY-DsF7k-Jl.js";import"./src-B6xuSHsQ.js";import"./chunk-WYO6CB5R-Bbp0lWuA.js";import"./chunk-ICXQ74PX-Jh1cDG2d.js";import"./chunk-HOUHSVGY-Cz-4R9wb.js";import"./chunk-Q4XR5HBZ-puXEgeqv.js";import"./chunk-7BUUIJ7U-Bb538aSH.js";import"./chunk-OGEWGWER-B3ysgOD5.js";import"./chunk-32BRIVSS-BfXRqSpF.js";import"./chunk-XXDRQBXY-DqAYeljN.js";import"./chunk-VR4S4FIN-DW_38atK.js";import"./chunk-C7G6YPKG-D0W6xHti.js";import"./chunk-ZGVPDNZ5-EJHunJJc.js";import"./chunk-52WLFC77-BeKggk68.js";import{i as t,n,r,t as i}from"./chunk-V7JOEXUC-C3tvTxnz.js";import"./markdown-streamdown-OYO-HVz6.js";var a={parser:n,get db(){return new i},renderer:r,styles:t,init:e(e=>{e.class||={},e.class.arrowMarkerAbsolute=e.arrowMarkerAbsolute},`init`)};export{a as diagram};
@@ -1 +0,0 @@
1
- import"./chunk-KEIR6QF5-BICK3FdT.js";import{O as e}from"./mermaid-parser.core-pxV_1Fef.js";export{e as createEventModelingServices};
@@ -1 +0,0 @@
1
- import"./src-B6xuSHsQ.js";import"./chunk-WYO6CB5R-Bbp0lWuA.js";import"./chunk-ICXQ74PX-Jh1cDG2d.js";import"./chunk-HOUHSVGY-Cz-4R9wb.js";import"./chunk-Q4XR5HBZ-puXEgeqv.js";import"./chunk-7BUUIJ7U-Bb538aSH.js";import"./chunk-OGEWGWER-B3ysgOD5.js";import"./chunk-32BRIVSS-BfXRqSpF.js";import"./chunk-XXDRQBXY-DqAYeljN.js";import"./chunk-VR4S4FIN-DW_38atK.js";import"./chunk-C7G6YPKG-D0W6xHti.js";import"./chunk-ZGVPDNZ5-EJHunJJc.js";import"./chunk-52WLFC77-BeKggk68.js";import{n as e}from"./chunk-PUDLZKDR-CcXYtnKN.js";import"./markdown-streamdown-OYO-HVz6.js";export{e as diagram};
@@ -1 +0,0 @@
1
- import{_ as e}from"./markdown-streamdown-OYO-HVz6.js";export{e as default};
@@ -1 +0,0 @@
1
- import{i as e}from"./markdown-streamdown-OYO-HVz6.js";export{e as Mermaid};
@@ -1 +0,0 @@
1
- import"./chunk-KEIR6QF5-BICK3FdT.js";import{m as e}from"./mermaid-parser.core-pxV_1Fef.js";export{e as createRailroadAbnfServices};
@@ -1 +0,0 @@
1
- import"./chunk-KEIR6QF5-BICK3FdT.js";import{f as e}from"./mermaid-parser.core-pxV_1Fef.js";export{e as createRailroadEbnfServices};
@@ -1 +0,0 @@
1
- import"./chunk-KEIR6QF5-BICK3FdT.js";import{u as e}from"./mermaid-parser.core-pxV_1Fef.js";export{e as createRailroadPegServices};
@@ -1 +0,0 @@
1
- import{n as e}from"./chunk-Y2CYZVJY-DsF7k-Jl.js";import"./src-B6xuSHsQ.js";import"./chunk-WYO6CB5R-Bbp0lWuA.js";import"./chunk-ICXQ74PX-Jh1cDG2d.js";import"./chunk-HOUHSVGY-Cz-4R9wb.js";import"./chunk-Q4XR5HBZ-puXEgeqv.js";import"./chunk-7BUUIJ7U-Bb538aSH.js";import"./chunk-OGEWGWER-B3ysgOD5.js";import"./chunk-32BRIVSS-BfXRqSpF.js";import"./chunk-XXDRQBXY-DqAYeljN.js";import"./chunk-VR4S4FIN-DW_38atK.js";import"./chunk-C7G6YPKG-D0W6xHti.js";import"./chunk-ZGVPDNZ5-EJHunJJc.js";import"./chunk-52WLFC77-BeKggk68.js";import{i as t,n,r,t as i}from"./chunk-EX3LRPZG-D-s3PHl_.js";import"./markdown-streamdown-OYO-HVz6.js";var a={parser:n,get db(){return new i(2)},renderer:r,styles:t,init:e(e=>{e.state||={},e.state.arrowMarkerAbsolute=e.arrowMarkerAbsolute},`init`)};export{a as diagram};
@@ -1,8 +0,0 @@
1
- import{n as e}from"./chunk-Y2CYZVJY-DsF7k-Jl.js";import"./src-B6xuSHsQ.js";import"./chunk-WYO6CB5R-Bbp0lWuA.js";import"./chunk-ICXQ74PX-Jh1cDG2d.js";import"./chunk-HOUHSVGY-Cz-4R9wb.js";import"./chunk-Q4XR5HBZ-puXEgeqv.js";import"./chunk-7BUUIJ7U-Bb538aSH.js";import"./chunk-OGEWGWER-B3ysgOD5.js";import"./chunk-32BRIVSS-BfXRqSpF.js";import"./chunk-XXDRQBXY-DqAYeljN.js";import"./chunk-VR4S4FIN-DW_38atK.js";import"./chunk-C7G6YPKG-D0W6xHti.js";import"./chunk-ZGVPDNZ5-EJHunJJc.js";import"./chunk-52WLFC77-BeKggk68.js";import{r as t,t as n}from"./chunk-PUDLZKDR-CcXYtnKN.js";import"./markdown-streamdown-OYO-HVz6.js";var r=n({defaultLayout:`swimlane`,styles:e(e=>`${t(e)}
2
- .swimlane.cluster rect {
3
- stroke: ${e.clusterBorder} !important;
4
- }
5
- [data-look="neo"].cluster rect {
6
- filter: none;
7
- }
8
- `,`getStyles`)});export{r as diagram};