@rynx-ai/server 0.1.11-beta.30 → 0.1.11-beta.32

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 (85) hide show
  1. package/dist/control-api.js +37 -0
  2. package/dist/control-web/assets/{abnfDiagram-VRR7QNED-COu02MlJ.js → abnfDiagram-VRR7QNED-8c99_3I5.js} +1 -1
  3. package/dist/control-web/assets/{arc-DqiFeYAM.js → arc-FWW96Tzo.js} +1 -1
  4. package/dist/control-web/assets/{architectureDiagram-ZJ3FMSHR-BWFGgUV4.js → architectureDiagram-ZJ3FMSHR-B5FrqoRB.js} +1 -1
  5. package/dist/control-web/assets/{blockDiagram-677ZJIJ3-ZC0bXDzB.js → blockDiagram-677ZJIJ3-DJRwOGMa.js} +1 -1
  6. package/dist/control-web/assets/{c4Diagram-LMCZKHZV-Uupq5I-s.js → c4Diagram-LMCZKHZV-B8FCUurl.js} +1 -1
  7. package/dist/control-web/assets/channel-Bnyhn1Qs.js +1 -0
  8. package/dist/control-web/assets/{chunk-2Q5K7J3B-C77vwuDM.js → chunk-2Q5K7J3B-DC5xexY2.js} +1 -1
  9. package/dist/control-web/assets/{chunk-32BRIVSS-BkfLE_TX.js → chunk-32BRIVSS-DAzT69zb.js} +1 -1
  10. package/dist/control-web/assets/{chunk-5VM5RSS4-By6zfDWW.js → chunk-5VM5RSS4-CXKpL77L.js} +1 -1
  11. package/dist/control-web/assets/{chunk-EX3LRPZG-CO439p_3.js → chunk-EX3LRPZG-DQKQvoPv.js} +1 -1
  12. package/dist/control-web/assets/{chunk-JWPE2WC7-B9BPEwug.js → chunk-JWPE2WC7-CWMqpJoT.js} +1 -1
  13. package/dist/control-web/assets/{chunk-MOJQB5TN-CYKygWZz.js → chunk-MOJQB5TN-Bd8jn7RZ.js} +1 -1
  14. package/dist/control-web/assets/{chunk-RYQCIY6F-ug-An4HG.js → chunk-RYQCIY6F-DKXEMFO6.js} +1 -1
  15. package/dist/control-web/assets/{chunk-V7JOEXUC-3UFQGnKX.js → chunk-V7JOEXUC-BUDLq5nU.js} +1 -1
  16. package/dist/control-web/assets/{chunk-VR4S4FIN-DjspjYUb.js → chunk-VR4S4FIN-C65l7VIz.js} +1 -1
  17. package/dist/control-web/assets/{chunk-XXDRQBXY-3znnutie.js → chunk-XXDRQBXY-Y2498HkC.js} +1 -1
  18. package/dist/control-web/assets/classDiagram-OUVF2IWQ-CBtXSdwW.js +1 -0
  19. package/dist/control-web/assets/classDiagram-v2-EOCWNBFH-CBtXSdwW.js +1 -0
  20. package/dist/control-web/assets/{cose-bilkent-JH36ORCC-Dr28goCd.js → cose-bilkent-JH36ORCC-C9aVXb8E.js} +1 -1
  21. package/dist/control-web/assets/{cynefin-VYW2F7L2-Be5uJhnw.js → cynefin-VYW2F7L2-BsyMhvG0.js} +1 -1
  22. package/dist/control-web/assets/{cynefinDiagram-TSTJHNR4-BLxtSvL6.js → cynefinDiagram-TSTJHNR4-DwY43f3-.js} +1 -1
  23. package/dist/control-web/assets/{dagre-VKFMJZFB-DPRuDV5p.js → dagre-VKFMJZFB-CP1xk7Ox.js} +1 -1
  24. package/dist/control-web/assets/{diagram-FQU43EPY-Cybg22U0.js → diagram-FQU43EPY-D_yRnH1b.js} +1 -1
  25. package/dist/control-web/assets/{diagram-G47NLZAW-CGeIKUfW.js → diagram-G47NLZAW-DpTmLmbm.js} +1 -1
  26. package/dist/control-web/assets/{diagram-NH7WQ7WH-BJq5MBRm.js → diagram-NH7WQ7WH-Wh_P26Dr.js} +1 -1
  27. package/dist/control-web/assets/{diagram-OA4YK3LP-DquOQGwg.js → diagram-OA4YK3LP-BMMaO78f.js} +1 -1
  28. package/dist/control-web/assets/{diagram-WEI45ONY-Cblb4xP2.js → diagram-WEI45ONY-D8smty9F.js} +1 -1
  29. package/dist/control-web/assets/{ebnfDiagram-CCIWWBDH-CQjHiOqj.js → ebnfDiagram-CCIWWBDH-BqVsmEYh.js} +1 -1
  30. package/dist/control-web/assets/{erDiagram-Q63AITRT-Bspr3uKJ.js → erDiagram-Q63AITRT-CxaumIzW.js} +1 -1
  31. package/dist/control-web/assets/{flowDiagram-23GEKE2U-CxcZyBA-.js → flowDiagram-23GEKE2U-DZhUBne-.js} +1 -1
  32. package/dist/control-web/assets/{ganttDiagram-NO4QXBWP-qBdh7A-P.js → ganttDiagram-NO4QXBWP-B20dY8SF.js} +1 -1
  33. package/dist/control-web/assets/{gitGraphDiagram-IHSO6WYX-BTYub2KT.js → gitGraphDiagram-IHSO6WYX-CAlnqFfW.js} +1 -1
  34. package/dist/control-web/assets/{highlighted-body-OFNGDK62-DyksUrwu.js → highlighted-body-OFNGDK62-vBlGjC8F.js} +1 -1
  35. package/dist/control-web/assets/{index-BDaSctIT.js → index-B3g4Kfko.js} +1 -1
  36. package/dist/control-web/assets/index-BKqZNg1a.js +718 -0
  37. package/dist/control-web/assets/index-CZrE_F_i.css +32 -0
  38. package/dist/control-web/assets/{infoDiagram-FWYZ7A6U-ChbQR3Zo.js → infoDiagram-FWYZ7A6U-PaEyeSke.js} +1 -1
  39. package/dist/control-web/assets/{ishikawaDiagram-FXEZZL3T-C-4_qGzn.js → ishikawaDiagram-FXEZZL3T-BwsgPXGC.js} +1 -1
  40. package/dist/control-web/assets/{journeyDiagram-5HDEW3XC-B090zN18.js → journeyDiagram-5HDEW3XC-BUEf1NEq.js} +1 -1
  41. package/dist/control-web/assets/{kanban-definition-HUTT4EX6-RlTcRbPs.js → kanban-definition-HUTT4EX6-CiTCwnJ7.js} +1 -1
  42. package/dist/control-web/assets/{linear-DTuNxDqu.js → linear-Cs4GSPvX.js} +1 -1
  43. package/dist/control-web/assets/{mermaid-GHXKKRXX-BkcV3dgg.js → mermaid-GHXKKRXX-C_lUqoOB.js} +6 -6
  44. package/dist/control-web/assets/{mindmap-definition-LN4V7U3C-DYby41E9.js → mindmap-definition-LN4V7U3C-B8TkylOR.js} +1 -1
  45. package/dist/control-web/assets/{pegDiagram-2B236MQR-CPK6GaEv.js → pegDiagram-2B236MQR-Bnge_jzz.js} +1 -1
  46. package/dist/control-web/assets/{pieDiagram-ENE6RG2P-DLCmXuji.js → pieDiagram-ENE6RG2P-DOP7FAKG.js} +1 -1
  47. package/dist/control-web/assets/{quadrantDiagram-ABIIQ3AL-RIS8r7CA.js → quadrantDiagram-ABIIQ3AL-Cqqu5uu-.js} +1 -1
  48. package/dist/control-web/assets/{railroadDiagram-RFXS5EU6-B1HB7xsg.js → railroadDiagram-RFXS5EU6-1bk56BMW.js} +1 -1
  49. package/dist/control-web/assets/{requirementDiagram-TGXJPOKE-Inh_4hU0.js → requirementDiagram-TGXJPOKE-BHVwG79A.js} +1 -1
  50. package/dist/control-web/assets/{sankeyDiagram-HTMAVEWB-DPF33kPB.js → sankeyDiagram-HTMAVEWB-ghjBFqfs.js} +1 -1
  51. package/dist/control-web/assets/{sequenceDiagram-DBY2YBRQ-iywKWoYc.js → sequenceDiagram-DBY2YBRQ-DChtyqq5.js} +1 -1
  52. package/dist/control-web/assets/{sizeCapture-X5ZJPWSS-DlnHubz9.js → sizeCapture-X5ZJPWSS-BNp94nCS.js} +1 -1
  53. package/dist/control-web/assets/{stateDiagram-2N3HPSRC-CShzll2N.js → stateDiagram-2N3HPSRC-CaFYkZZ2.js} +1 -1
  54. package/dist/control-web/assets/stateDiagram-v2-6OUMAXLB-CCvEZFYI.js +1 -0
  55. package/dist/control-web/assets/{swimlanes-5IMT3BWC-CxlQpFBv.js → swimlanes-5IMT3BWC-CV2YWeYv.js} +2 -2
  56. package/dist/control-web/assets/swimlanesDiagram-G3AALYLV-BFAdJHEB.js +8 -0
  57. package/dist/control-web/assets/{timeline-definition-FHXFAJF6-Dlx59Xc3.js → timeline-definition-FHXFAJF6-Cq32UWLH.js} +1 -1
  58. package/dist/control-web/assets/{vennDiagram-L72KCM5P-DLsv4i_t.js → vennDiagram-L72KCM5P-BgBoqPth.js} +1 -1
  59. package/dist/control-web/assets/{wardleyDiagram-EHGQE667-BazV8fpH.js → wardleyDiagram-EHGQE667-VsC2Gfpu.js} +1 -1
  60. package/dist/control-web/assets/{xychartDiagram-FW5EYKEG-Bx9uDlYu.js → xychartDiagram-FW5EYKEG-CyFiAunt.js} +1 -1
  61. package/dist/control-web/index.html +2 -2
  62. package/dist/desktop-browser-host.d.ts +3 -1
  63. package/dist/desktop-browser-host.js +39 -4
  64. package/dist/machine-session-service.d.ts +12 -0
  65. package/dist/machine-session-service.js +31 -7
  66. package/dist/remote-runtime-dispatcher.d.ts +1 -1
  67. package/dist/remote-runtime-dispatcher.js +26 -1
  68. package/dist/remote-runtime-session-projection.d.ts +1 -0
  69. package/dist/remote-runtime-session-projection.js +1 -0
  70. package/dist/server.d.ts +23 -1
  71. package/dist/server.js +94 -11
  72. package/dist/session-browser-service.d.ts +24 -4
  73. package/dist/session-browser-service.js +127 -1
  74. package/dist/session-pending-inputs.d.ts +40 -0
  75. package/dist/session-pending-inputs.js +84 -0
  76. package/dist/session-portal.d.ts +14 -2
  77. package/dist/session-portal.js +268 -31
  78. package/package.json +7 -7
  79. package/dist/control-web/assets/channel-3HG15wfa.js +0 -1
  80. package/dist/control-web/assets/classDiagram-OUVF2IWQ-WmU4L9YT.js +0 -1
  81. package/dist/control-web/assets/classDiagram-v2-EOCWNBFH-WmU4L9YT.js +0 -1
  82. package/dist/control-web/assets/index-B9SAq_63.js +0 -718
  83. package/dist/control-web/assets/index-CtaYRNgJ.css +0 -32
  84. package/dist/control-web/assets/stateDiagram-v2-6OUMAXLB-Cdx0fWna.js +0 -1
  85. package/dist/control-web/assets/swimlanesDiagram-G3AALYLV-CoX0jnUu.js +0 -8
@@ -115,7 +115,8 @@ export class MachineSessionService {
115
115
  limit: limit + 1,
116
116
  })).slice(0, limit + 1).map((item) => structuredClone(item));
117
117
  const runtime = cloneRuntimeSnapshot(this.ports.runtimeState.snapshot(sessionId));
118
- const empty = snapshotPage(sessionId, [], runtime, false);
118
+ const pendingInputs = this.ports.pendingInputs?.snapshotFor(sessionId) ?? [];
119
+ const empty = snapshotPage(sessionId, [], pendingInputs, runtime, false);
119
120
  const items = [];
120
121
  let itemsJsonBytes = 0;
121
122
  for (const item of candidates.slice(0, limit)) {
@@ -123,14 +124,14 @@ export class MachineSessionService {
123
124
  const nextCount = items.length + 1;
124
125
  const hasMore = candidates.length > nextCount;
125
126
  if (items.length > 0 &&
126
- snapshotPageJsonByteLength(sessionId, nextItemsJsonBytes, item.id, runtime, hasMore) > this.snapshotPageMaxBytes) {
127
+ snapshotPageJsonByteLength(sessionId, nextItemsJsonBytes, item.id, pendingInputs, runtime, hasMore) > this.snapshotPageMaxBytes) {
127
128
  break;
128
129
  }
129
130
  items.push(item);
130
131
  itemsJsonBytes = nextItemsJsonBytes;
131
132
  }
132
133
  return items.length > 0
133
- ? snapshotPage(sessionId, items, runtime, candidates.length > items.length)
134
+ ? snapshotPage(sessionId, items, pendingInputs, runtime, candidates.length > items.length)
134
135
  : empty;
135
136
  }
136
137
  /**
@@ -264,7 +265,7 @@ export class MachineSessionService {
264
265
  statusCode: 409,
265
266
  });
266
267
  }
267
- // Omnigent parity: native state is authoritative. Do not make a failed
268
+ // Native state is authoritative. Do not make a failed
268
269
  // toggle survive reload when the loaded thread rejected it.
269
270
  try {
270
271
  await updateMode.call(executionPort.runner, sessionId, collaborationMode);
@@ -670,9 +671,14 @@ export class MachineSessionService {
670
671
  }
671
672
  if (prepared.operationState === "injected" ||
672
673
  prepared.operationState === "mirrored") {
674
+ const replayPendingId = prepared.operationState === "injected" && request.clientMessageId
675
+ ? this.ports.pendingInputs
676
+ ?.findByClientMessageId(sessionId, request.clientMessageId)?.pendingId
677
+ : undefined;
673
678
  return {
674
679
  ok: true,
675
680
  injected: true,
681
+ ...(replayPendingId ? { pendingId: replayPendingId } : {}),
676
682
  ...(includeResponseId && prepared.responseId
677
683
  ? { responseId: prepared.responseId }
678
684
  : {}),
@@ -732,6 +738,18 @@ export class MachineSessionService {
732
738
  throw abortReason(signal);
733
739
  }
734
740
  let result;
741
+ const pendingId = this.ports.pendingInputs && request.clientMessageId
742
+ ? this.ports.pendingInputs.record(sessionId, {
743
+ clientMessageId: request.clientMessageId,
744
+ content: (hasContent
745
+ ? request.content
746
+ : [{ type: "input_text", text: request.message }]).map((part) => ({ ...part })),
747
+ })
748
+ : undefined;
749
+ const rollbackPendingInput = () => {
750
+ if (pendingId)
751
+ this.ports.pendingInputs?.resolve(sessionId, pendingId);
752
+ };
735
753
  try {
736
754
  const injectionInput = needsPreparedOperation ? prepared.input : request.message;
737
755
  result = turnOptions
@@ -739,6 +757,7 @@ export class MachineSessionService {
739
757
  : await execution.runner.injectMessage(sessionId, injectionInput);
740
758
  }
741
759
  catch (error) {
760
+ rollbackPendingInput();
742
761
  const failure = execution.runner.lastLiveSessionFailure?.(sessionId)
743
762
  ?? liveSessionFailureFrom(error);
744
763
  const reason = failure?.message
@@ -750,6 +769,7 @@ export class MachineSessionService {
750
769
  }
751
770
  const outcome = result.outcome;
752
771
  if (outcome === "failed") {
772
+ rollbackPendingInput();
753
773
  const failure = execution.runner.lastLiveSessionFailure?.(sessionId);
754
774
  const reason = failure?.message
755
775
  ?? execution.runner.lastLiveSessionError?.(sessionId)
@@ -760,6 +780,7 @@ export class MachineSessionService {
760
780
  throw new MachineSessionServiceFailure("outcome_unknown", `live injection ${outcome}`, reason, failure);
761
781
  }
762
782
  if (outcome !== "injected" && outcome !== "steered") {
783
+ rollbackPendingInput();
763
784
  const failure = execution.runner.lastLiveSessionFailure?.(sessionId);
764
785
  const reason = failure?.message
765
786
  ?? execution.runner.lastLiveSessionError?.(sessionId)
@@ -789,6 +810,7 @@ export class MachineSessionService {
789
810
  return {
790
811
  ok: true,
791
812
  injected: true,
813
+ ...(pendingId ? { pendingId } : {}),
792
814
  ...(includeResponseId && result.responseId ? { responseId: result.responseId } : {}),
793
815
  ...(operationExecution ? { operationExecution } : {}),
794
816
  };
@@ -987,6 +1009,7 @@ export class MachineSessionService {
987
1009
  this.ports.execution?.runner.stopRunner(sessionId);
988
1010
  this.ports.events.close?.(sessionId);
989
1011
  await this.ports.pendingMessages?.delete(sessionId);
1012
+ this.ports.pendingInputs?.deleteSession(sessionId);
990
1013
  await this.ports.resources?.deleteSession(sessionId);
991
1014
  this.ports.registry.remove(sessionId);
992
1015
  await this.ports.log.deleteSession(sessionId);
@@ -1372,10 +1395,11 @@ function decodeListCursor(value) {
1372
1395
  throw new MachineSessionServiceInputError("cursor is invalid");
1373
1396
  }
1374
1397
  }
1375
- function snapshotPage(sessionId, items, runtime, hasMore) {
1398
+ function snapshotPage(sessionId, items, pendingInputs, runtime, hasMore) {
1376
1399
  return {
1377
1400
  sessionId,
1378
1401
  items,
1402
+ pendingInputs,
1379
1403
  runtime,
1380
1404
  hasMore,
1381
1405
  ...(hasMore && items.length > 0 ? { nextAfterId: items.at(-1).id } : {}),
@@ -1383,10 +1407,10 @@ function snapshotPage(sessionId, items, runtime, hasMore) {
1383
1407
  }
1384
1408
  /** Exact UTF-8 size of {@link snapshotPage} without repeatedly serializing the
1385
1409
  * complete item array while searching for a page boundary. */
1386
- function snapshotPageJsonByteLength(sessionId, itemsJsonBytes, finalItemId, runtime, hasMore) {
1410
+ function snapshotPageJsonByteLength(sessionId, itemsJsonBytes, finalItemId, pendingInputs, runtime, hasMore) {
1387
1411
  return Buffer.byteLength(`{"sessionId":${JSON.stringify(sessionId)},"items":[`, "utf8")
1388
1412
  + itemsJsonBytes
1389
- + Buffer.byteLength(`],"runtime":${JSON.stringify(runtime)},"hasMore":${hasMore}${hasMore ? `,"nextAfterId":${JSON.stringify(finalItemId)}` : ""}}`, "utf8");
1413
+ + Buffer.byteLength(`],"pendingInputs":${JSON.stringify(pendingInputs)},"runtime":${JSON.stringify(runtime)},"hasMore":${hasMore}${hasMore ? `,"nextAfterId":${JSON.stringify(finalItemId)}` : ""}}`, "utf8");
1390
1414
  }
1391
1415
  function sessionSummary(id, meta, log, runtimeState, pendingState) {
1392
1416
  const createdAt = meta?.createdAt ?? timestampFromEpoch(log?.createdAt, "createdAt");
@@ -35,7 +35,7 @@ export interface RemoteRuntimeProjectHost {
35
35
  remove(projectId: string): boolean | Promise<boolean>;
36
36
  }
37
37
  export type RemoteRuntimeSessionHost = Pick<MachineSessionService, "list" | "snapshot" | "watch" | "interrupt" | "agentOptions" | "launchOptions" | "executionSettings" | "updateExecutionSettings" | "create" | "fork" | "sendMessage" | "resourcePolicy" | "beginResourceUpload" | "writeResourceUploadChunk" | "commitResourceUpload" | "getResource" | "readResource" | "deleteResource" | "getFile" | "readFile" | "getWorkspaceFile" | "readWorkspaceFile" | "enqueueMessage" | "startTerminal" | "delete" | "resolveInteraction">;
38
- export type RemoteRuntimeBrowserHost = Pick<SessionBrowserService, "getState" | "open" | "close" | "createPage" | "closePage" | "activatePage" | "navigatePage" | "goBack" | "goForward" | "reload">;
38
+ export type RemoteRuntimeBrowserHost = Pick<SessionBrowserService, "getState" | "getRequestHeaders" | "setRequestHeaders" | "open" | "close" | "createPage" | "closePage" | "activatePage" | "navigatePage" | "goBack" | "goForward" | "reload">;
39
39
  export type RemoteRuntimeSessionEmulatorHost = Pick<SessionEmulatorService, "getState" | "listDevices" | "attach" | "detach" | "tap" | "type" | "button" | "rotate" | "launch" | "gesture" | "releaseDevice" | "stopDevice">;
40
40
  export interface OpenRemoteRuntimeSessionEvents {
41
41
  sessionId: string;
@@ -176,6 +176,10 @@ export class RemoteRuntimeDispatcher {
176
176
  return await this.withSessions(request, (sessions) => sessions.resolveInteraction(request.params.sessionId, request.params.interactionId, request.params.resolution));
177
177
  case "browser.state.get":
178
178
  return await this.withBrowsers(request, (browsers) => browsers.getState(request.params));
179
+ case "browser.request-headers.get":
180
+ return await this.withBrowsers(request, (browsers) => browsers.getRequestHeaders(request.params));
181
+ case "browser.request-headers.set":
182
+ return await this.withBrowsers(request, (browsers) => browsers.setRequestHeaders(request.params));
179
183
  case "browser.open":
180
184
  return await this.withBrowsers(request, (browsers) => browsers.open(request.params));
181
185
  case "browser.close":
@@ -198,7 +202,7 @@ export class RemoteRuntimeDispatcher {
198
202
  return rpcError(request.id, "method_not_found", "Remote Runtime method is not supported");
199
203
  }
200
204
  catch (error) {
201
- this.options.onError?.(error, request);
205
+ this.options.onError?.(error, diagnosticRequest(request));
202
206
  const mapped = mapHostError(request, error);
203
207
  return rpcError(request.id, mapped.code, mapped.message);
204
208
  }
@@ -302,6 +306,20 @@ export class RemoteRuntimeDispatcher {
302
306
  return rpcResult(request, await invoke(browsers));
303
307
  }
304
308
  }
309
+ function diagnosticRequest(request) {
310
+ if (request.method !== "browser.request-headers.set")
311
+ return request;
312
+ return {
313
+ ...request,
314
+ params: {
315
+ ...request.params,
316
+ headers: request.params.headers.map((header) => ({
317
+ ...header,
318
+ value: "[redacted]",
319
+ })),
320
+ },
321
+ };
322
+ }
305
323
  function mapHostError(request, error) {
306
324
  if (error instanceof ProjectNotFoundError) {
307
325
  return { code: "not_found", message: "Runtime Project was not found" };
@@ -322,6 +340,13 @@ function mapHostError(request, error) {
322
340
  return { code: "not_found", message: "Browser resource was not found" };
323
341
  case "generation_mismatch":
324
342
  return { code: "conflict", message: "Browser generation is stale" };
343
+ case "revision_mismatch":
344
+ return { code: "conflict", message: "Browser request headers revision is stale" };
345
+ case "unsupported":
346
+ return {
347
+ code: "failed_precondition",
348
+ message: "Browser request headers are unsupported by the active Browser Host",
349
+ };
325
350
  case "unavailable":
326
351
  case "capacity":
327
352
  case "shutting_down":
@@ -9,6 +9,7 @@ export declare function projectRemoteRuntimeSessionListResult(value: unknown): R
9
9
  export declare function projectRemoteRuntimeSessionSnapshotResult(value: {
10
10
  sessionId: string;
11
11
  items: SessionItem[];
12
+ pendingInputs: NonNullable<RemoteRuntimeSessionSnapshotResult["pendingInputs"]>;
12
13
  runtime: unknown;
13
14
  hasMore: boolean;
14
15
  nextAfterId?: string;
@@ -27,6 +27,7 @@ export function projectRemoteRuntimeSessionSnapshotResult(value) {
27
27
  const candidate = {
28
28
  sessionId: value.sessionId,
29
29
  items,
30
+ pendingInputs: structuredClone(value.pendingInputs),
30
31
  runtime,
31
32
  hasMore: value.hasMore,
32
33
  ...(value.nextAfterId === undefined ? {} : { nextAfterId: value.nextAfterId }),
package/dist/server.d.ts CHANGED
@@ -15,6 +15,7 @@ import type { RemoteRuntimeRpcMethod, RemoteRuntimeRpcParams, RemoteRuntimeRpcRe
15
15
  import { RunnerManager, type CodexSessionStore, type RunnerSessionContextProvider } from "@rynx-ai/runtime";
16
16
  import { type ControlPlaneDeps, type LocalSessionResourceHost, type SessionResourceLifecycle } from "./control-api.js";
17
17
  import type { MachineSessionService } from "./machine-session-service.js";
18
+ import type { MachineSessionPendingInputPort } from "./session-pending-inputs.js";
18
19
  import type { DaemonRuntimeHost } from "./remote-runtime.js";
19
20
  import { type AttachDesktopBrowserHostServerOptions } from "./desktop-browser-host.js";
20
21
  import { type SessionPortalAuthorizationPorts } from "./session-portal.js";
@@ -25,11 +26,14 @@ import type { SessionEmulatorService } from "./session-emulator-service.js";
25
26
  import { type SessionTerminalHost } from "./session-terminal-host.js";
26
27
  import type { ProviderCliManagementHost } from "./provider-cli-host.js";
27
28
  import { type RuntimeWebAccessPolicy } from "./runtime-web-auth.js";
29
+ import type { SessionBrowserRequestHeadersPort } from "./session-browser-service.js";
28
30
  export type { ProviderCliManagementHost, ProviderCliStatusHost, } from "./provider-cli-host.js";
29
31
  export { MachineSessionService, MachineSessionServiceFailure, MachineSessionServiceInputError, synthesizeSessionTitle, } from "./machine-session-service.js";
32
+ export { SessionPendingInputIndex } from "./session-pending-inputs.js";
30
33
  export type { MachineSessionAgentCatalogPort, MachineSessionCreateInput, MachineSessionCreateResult, MachineSessionDeleteResult, MachineSessionForkInput, MachineSessionForkOperationRecord, MachineSessionForkOperationState, MachineSessionForkResult, MachineSessionForkStorePort, MachineSessionNativeRotationInput, MachineSessionInterruptPort, MachineSessionInterruptResult, MachineSessionInteractionResult, MachineSessionListInput, MachineSessionListPage, MachineSessionMessageResult, MachineSessionMessageEnqueueResult, MachineSessionMessageInput, MachineSessionMessageOperationState, MachineSessionPendingMessage, MachineSessionPendingMessagePort, MachineSessionPreparedInput, MachineSessionResourcePort, MachineSessionRunExecutionSelection, MachineSessionRunExecutionSnapshot, MachineSessionRunnerPort, MachineSessionRuntimeStatePort, MachineSessionServiceFailureCode, MachineSessionServiceOptions, MachineSessionServicePorts, MachineSessionSnapshotInput, MachineSessionSnapshotPage, MachineSessionWatch, StartedMachineSessionRun, } from "./machine-session-service.js";
34
+ export type { MachineSessionPendingInput, MachineSessionPendingInputPort, } from "./session-pending-inputs.js";
31
35
  export { SessionBrowserHostError, SessionBrowserService, SessionBrowserServiceError, SessionBrowserSurfaceUnsupportedError, } from "./session-browser-service.js";
32
- export type { SessionBrowserHost, SessionBrowserHostCreateInput, SessionBrowserHostErrorCode, SessionBrowserHostEvent, SessionBrowserHostHandle, SessionBrowserHostPage, SessionBrowserHostSurfaceFrame, SessionBrowserHostSurfaceOpenInput, SessionBrowserHostSurfaceSource, SessionBrowserHostSurfaceTrustedInput, SessionBrowserHostSurfaceViewport, SessionBrowserHostSnapshot, SessionBrowserInspectionEndpoint, SessionBrowserInspectionTarget, SessionBrowserRegistryPort, SessionBrowserServiceErrorCode, SessionBrowserServiceOptions, SessionBrowserServicePorts, SessionBrowserSurfaceOpenInput, SessionBrowserSurfaceSource, } from "./session-browser-service.js";
36
+ export type { SessionBrowserHost, SessionBrowserHostCreateInput, SessionBrowserHostErrorCode, SessionBrowserHostEvent, SessionBrowserHostHandle, SessionBrowserHostPage, SessionBrowserHostSurfaceFrame, SessionBrowserHostSurfaceOpenInput, SessionBrowserHostSurfaceSource, SessionBrowserHostSurfaceTrustedInput, SessionBrowserHostSurfaceViewport, SessionBrowserHostSnapshot, SessionBrowserInspectionEndpoint, SessionBrowserInspectionTarget, SessionBrowserRegistryPort, SessionBrowserRequestHeadersPort, SessionBrowserServiceErrorCode, SessionBrowserServiceOptions, SessionBrowserServicePorts, SessionBrowserSurfaceOpenInput, SessionBrowserSurfaceSource, } from "./session-browser-service.js";
33
37
  export { SessionEmulatorBindingConflictError, SessionEmulatorService, SessionEmulatorServiceError, } from "./session-emulator-service.js";
34
38
  export type { SessionEmulatorBindingRecord, SessionEmulatorBindingStore, SessionEmulatorServiceErrorCode, SessionEmulatorServiceOptions, SessionEmulatorSurface, } from "./session-emulator-service.js";
35
39
  export { SessionBrowserSurfaceCoordinator } from "./session-browser-surface-coordinator.js";
@@ -152,6 +156,7 @@ export interface PluginRuntimeHostServices {
152
156
  sessionRegistry: SessionRegistry;
153
157
  sessionRuns: Pick<MachineSessionService, "startRun">;
154
158
  sessionFiles: Pick<MachineSessionService, "resolveResourcePath">;
159
+ sessionBrowserRequestHeaders?: SessionBrowserRequestHeadersPort;
155
160
  agentCatalog: {
156
161
  list(): Promise<PluginAgentSummary[]>;
157
162
  get(id: string): Promise<PluginAgentSummary | null>;
@@ -163,6 +168,15 @@ export interface PluginRuntimeHostServices {
163
168
  sessionActivity(sessionId: string): PluginSessionActivitySnapshot;
164
169
  interruptSession(sessionId: string): Promise<boolean>;
165
170
  terminateSession?(sessionId: string): Promise<boolean>;
171
+ sessionTimeline?: {
172
+ hasItem(sessionId: string, itemId: string): Promise<boolean>;
173
+ projectText(input: {
174
+ sessionId: string;
175
+ responseId: string;
176
+ itemId: string;
177
+ text: string;
178
+ }): Promise<void>;
179
+ };
166
180
  resolveSessionInteraction(sessionId: string, interactionId: string, resolution: SessionInteractionResolution): Promise<{
167
181
  disposition: "applied" | "already_resolved" | "not_found" | "invalid";
168
182
  }>;
@@ -434,6 +448,14 @@ export declare class SessionMirrorQueue {
434
448
  export declare function createSessionRuntimeServices(input: {
435
449
  config: AppConfig;
436
450
  sessionLog?: SessionLogStore;
451
+ pendingInputs?: MachineSessionPendingInputPort;
452
+ onPendingInputPersisted?: (input: {
453
+ sessionId: string;
454
+ pendingId: string;
455
+ clientMessageId: string;
456
+ responseId: string;
457
+ messageItemId: string;
458
+ }) => void | Promise<void>;
437
459
  onMirrorError?: (error: unknown, sessionId: string) => void;
438
460
  sessionContextProvider?: RunnerSessionContextProvider;
439
461
  admissionOpen?: () => boolean;
package/dist/server.js CHANGED
@@ -17,6 +17,7 @@ import { SessionRuntimeIndex } from "./session-runtime-index.js";
17
17
  import { createRunnerSessionTerminalHost, } from "./session-terminal-host.js";
18
18
  import { runtimeWebAccessPolicyForBindHost, } from "./runtime-web-auth.js";
19
19
  export { MachineSessionService, MachineSessionServiceFailure, MachineSessionServiceInputError, synthesizeSessionTitle, } from "./machine-session-service.js";
20
+ export { SessionPendingInputIndex } from "./session-pending-inputs.js";
20
21
  export { SessionBrowserHostError, SessionBrowserService, SessionBrowserServiceError, SessionBrowserSurfaceUnsupportedError, } from "./session-browser-service.js";
21
22
  export { SessionEmulatorBindingConflictError, SessionEmulatorService, SessionEmulatorServiceError, } from "./session-emulator-service.js";
22
23
  export { SessionBrowserSurfaceCoordinator } from "./session-browser-surface-coordinator.js";
@@ -68,11 +69,28 @@ export function createSessionRuntimeServices(input) {
68
69
  const sessionBus = new InMemorySessionBus();
69
70
  const sessionRuntimeIndex = new SessionRuntimeIndex();
70
71
  const sessionMirrorQueue = new SessionMirrorQueue(async (sessionId, event) => {
71
- const finalized = await finalizeGeneratedImageEvent(sessionId, event, input);
72
+ const generatedImageFinalized = await finalizeGeneratedImageEvent(sessionId, event, input);
73
+ const pendingFinalized = resolvePendingInputEvent(sessionId, generatedImageFinalized, input.pendingInputs);
74
+ const finalized = pendingFinalized.event;
72
75
  sessionRuntimeIndex.observe(sessionId, finalized);
73
76
  await persistSessionEvent(sessionId, finalized, {
74
77
  sessionLog: input.sessionLog,
75
78
  sessionBus,
79
+ ...(input.onPendingInputPersisted
80
+ ? {
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
+ },
92
+ }
93
+ : {}),
76
94
  });
77
95
  });
78
96
  const mirrorSessionEvent = (sessionId, event) => {
@@ -97,6 +115,47 @@ export function createSessionRuntimeServices(input) {
97
115
  publishSessionEvent: mirrorSessionEvent,
98
116
  };
99
117
  }
118
+ /** Drain the oldest pending web input when the Provider transcript produces a
119
+ * real user record. The transcript is the only durable writer; the returned id
120
+ * lets clients replace the exact temporary bubble. */
121
+ function resolvePendingInputEvent(sessionId, event, pendingInputs) {
122
+ if (event.type !== "session.input.consumed" ||
123
+ event.item.type !== "message" ||
124
+ event.item.data.role !== "user" ||
125
+ event.item.data.isMeta) {
126
+ return { event };
127
+ }
128
+ const pending = pendingInputs?.resolveOldest(sessionId);
129
+ if (!pending)
130
+ return { event };
131
+ const fileParts = pending.content.filter((part) => part.type === "input_image" || part.type === "input_file");
132
+ const alreadyHasFiles = event.item.data.content.some((part) => part.type === "input_image" || part.type === "input_file");
133
+ const containsInternalResourceMarker = event.item.data.content.some((part) => part.type === "input_text" &&
134
+ /\[\[RYNX_(?:IMAGE|FILE)_RESOURCE\s/.test(part.text));
135
+ const content = containsInternalResourceMarker
136
+ ? pending.content
137
+ : fileParts.length > 0 && !alreadyHasFiles
138
+ ? [...fileParts, ...event.item.data.content]
139
+ : event.item.data.content;
140
+ return {
141
+ clientMessageId: pending.clientMessageId,
142
+ event: {
143
+ ...event,
144
+ clearedPendingId: pending.pendingId,
145
+ ...(content !== event.item.data.content
146
+ ? {
147
+ item: {
148
+ ...event.item,
149
+ data: {
150
+ ...event.item.data,
151
+ content,
152
+ },
153
+ },
154
+ }
155
+ : {}),
156
+ },
157
+ };
158
+ }
100
159
  async function finalizeGeneratedImageEvent(sessionId, event, input) {
101
160
  if (event.type !== "response.output_item.done" ||
102
161
  event.item.type !== "function_call_output") {
@@ -309,6 +368,36 @@ export async function startServer({ config = loadConfig(), control, sessionLog,
309
368
  },
310
369
  interruptSession: (sessionId) => runnerManager.interruptLiveSession(sessionId),
311
370
  terminateSession: (sessionId) => runnerManager.terminateLiveSession(sessionId),
371
+ ...(sessionLog
372
+ ? {
373
+ sessionTimeline: {
374
+ hasItem: async (sessionId, itemId) => Boolean(await sessionLog.get(sessionId, itemId)),
375
+ projectText: async ({ sessionId, responseId, itemId, text }) => {
376
+ const event = {
377
+ type: "response.output_item.done",
378
+ responseId,
379
+ item: {
380
+ id: itemId,
381
+ sessionId,
382
+ position: -1,
383
+ responseId,
384
+ status: "completed",
385
+ createdAt: Date.now(),
386
+ createdBy: "plugin_projection",
387
+ type: "message",
388
+ data: { role: "assistant", content: [{ type: "output_text", text }] },
389
+ },
390
+ };
391
+ // Projection is an acknowledged durable operation, unlike the
392
+ // provider mirror's deliberately best-effort live fan-out.
393
+ // Append first and only then publish; a failed append leaves the
394
+ // projection ledger pending so the Plugin can retry safely.
395
+ await sessionLog.append(sessionId, [event.item]);
396
+ sessionServices.sessionBus.publish(sessionId, event);
397
+ },
398
+ },
399
+ }
400
+ : {}),
312
401
  resolveSessionInteraction: (sessionId, interactionId, resolution) => runnerManager.resolveInteraction(sessionId, interactionId, resolution),
313
402
  ...(sessionPortalConfig
314
403
  ? {
@@ -392,6 +481,10 @@ export async function startServer({ config = loadConfig(), control, sessionLog,
392
481
  webSocketServers.push(attachEmulatorTouchWs(server, { emulator: control.emulator }));
393
482
  }
394
483
  startupWebSocketServers = webSocketServers;
484
+ // Plugin recovery fences and revokes any product-owned Portal authority.
485
+ // Do not expose the listener until every enabled Plugin got its serialized
486
+ // startup attempt; startPluginRuntimes absorbs individual failures.
487
+ await (pluginRuntime?.startPluginRuntimes() ?? Promise.resolve());
395
488
  if (sessionPortalConfig) {
396
489
  sessionPortalServer = await startSessionPortal({
397
490
  host: config.RYNX_SESSION_PORTAL_HOST,
@@ -403,15 +496,6 @@ export async function startServer({ config = loadConfig(), control, sessionLog,
403
496
  shutdownSignal: controlShutdown.signal,
404
497
  });
405
498
  }
406
- const startup = pluginRuntime?.startPluginRuntimes() ?? Promise.resolve();
407
- void startup.catch((error) => {
408
- console.error(JSON.stringify({
409
- level: "error",
410
- type: "plugin-runtime",
411
- event: "startup_failed",
412
- error: error instanceof Error ? error.message : String(error),
413
- }));
414
- });
415
499
  resolve(bindServerLifecycle(server, {
416
500
  fence: () => {
417
501
  controlShutdown.abort(new Error("Rynx server is shutting down"));
@@ -422,7 +506,6 @@ export async function startServer({ config = loadConfig(), control, sessionLog,
422
506
  sessionPortalServer?.shutdown() ?? Promise.resolve(),
423
507
  ]).then(() => undefined),
424
508
  cleanup: async () => runCleanupSteps([
425
- () => startup.catch(() => undefined),
426
509
  () => Promise.resolve(daemonManagement?.pluginManagement?.dispose?.()),
427
510
  () => pluginRuntime?.dispose() ?? Promise.resolve(),
428
511
  () => runnerManager.stop(),
@@ -1,10 +1,14 @@
1
1
  import type { AdmissionReservation } from "@rynx-ai/core";
2
- import { type RuntimeBrowserCloseParams, type RuntimeBrowserCloseResult, type RuntimeBrowserExecutionBackend, type RuntimeBrowserOpenParams, type RuntimeBrowserPageCreateParams, type RuntimeBrowserPageMutationParams, type RuntimeBrowserPageNavigateParams, type RuntimeBrowserState, type RuntimeBrowserStateGetParams } from "@rynx-ai/protocol/runtime-browser";
2
+ import { type RuntimeBrowserCloseParams, type RuntimeBrowserCloseResult, type RuntimeBrowserExecutionBackend, type RuntimeBrowserOpenParams, type RuntimeBrowserPageCreateParams, type RuntimeBrowserPageMutationParams, type RuntimeBrowserPageNavigateParams, type RuntimeBrowserRequestHeadersConfig, type RuntimeBrowserRequestHeadersGetParams, type RuntimeBrowserRequestHeadersPolicy, type RuntimeBrowserRequestHeadersSetParams, type RuntimeBrowserState, type RuntimeBrowserStateGetParams } from "@rynx-ai/protocol/runtime-browser";
3
3
  import type { RuntimeBrowserSurfaceFormat, RuntimeBrowserSurfaceInputEvent } from "@rynx-ai/protocol/runtime-browser-surface";
4
4
  import { type RuntimeBrowserEndpointDescriptor } from "@rynx-ai/protocol/runtime-browser-bootstrap";
5
5
  export interface SessionBrowserHostCreateInput {
6
6
  sessionId: string;
7
7
  browserGeneration: number;
8
+ /** Required at this internal boundary so every Host installs the policy
9
+ * before it permits the first navigation. Wire-version compatibility stays
10
+ * inside the concrete Host adapter. */
11
+ requestHeaders: RuntimeBrowserRequestHeadersConfig;
8
12
  url?: string;
9
13
  }
10
14
  /** Generation-scoped physical page state. `hostPageId` never leaves the service. */
@@ -136,6 +140,8 @@ export interface SessionBrowserHostHandle {
136
140
  goBack(hostPageId: string): Promise<void>;
137
141
  goForward(hostPageId: string): Promise<void>;
138
142
  reload(hostPageId: string): Promise<void>;
143
+ /** Replaces the Session-wide desired request Header projection for this generation. */
144
+ setRequestHeaders(config: RuntimeBrowserRequestHeadersConfig): Promise<void>;
139
145
  close(): Promise<void>;
140
146
  }
141
147
  /** Physical Browser factory selected by the Runtime composition root. */
@@ -156,19 +162,26 @@ export interface SessionBrowserServiceOptions {
156
162
  pageIdFactory?: () => string;
157
163
  /** Injectable boot nonce; production randomization fences stale pre-restart mutations. */
158
164
  bootGenerationSeed?: number;
165
+ /** Injectable only for deterministic CAS tests; production uses 128 random bits. */
166
+ requestHeaderRevisionSeed?: string;
159
167
  /**
160
168
  * Synchronous daemon admission fence. A successful reservation is held until
161
169
  * Browser open has either failed or registered the physical Host handle.
162
170
  */
163
171
  admissionReserve?: () => AdmissionReservation | undefined;
164
172
  }
165
- export type SessionBrowserHostErrorCode = "unavailable" | "page_not_found" | "capacity" | "outcome_unknown";
173
+ export type SessionBrowserHostErrorCode = "unavailable" | "unsupported" | "page_not_found" | "capacity" | "outcome_unknown";
166
174
  /** Expected failure reported by a Browser Host implementation. */
167
175
  export declare class SessionBrowserHostError extends Error {
168
176
  readonly code: SessionBrowserHostErrorCode;
169
177
  constructor(code: SessionBrowserHostErrorCode, message: string, options?: ErrorOptions);
170
178
  }
171
- export type SessionBrowserServiceErrorCode = "invalid_request" | "not_found" | "generation_mismatch" | "unavailable" | "capacity" | "host_failure" | "outcome_unknown" | "shutting_down";
179
+ /** Narrow Session-owned policy port used by transports and Plugin hosting. */
180
+ export interface SessionBrowserRequestHeadersPort {
181
+ getRequestHeaders(input: RuntimeBrowserRequestHeadersGetParams): Promise<RuntimeBrowserRequestHeadersPolicy>;
182
+ setRequestHeaders(input: RuntimeBrowserRequestHeadersSetParams): Promise<RuntimeBrowserRequestHeadersPolicy>;
183
+ }
184
+ export type SessionBrowserServiceErrorCode = "invalid_request" | "not_found" | "generation_mismatch" | "revision_mismatch" | "unsupported" | "unavailable" | "capacity" | "host_failure" | "outcome_unknown" | "shutting_down";
172
185
  /** Stable application error; transports map the code without exposing Host diagnostics. */
173
186
  export declare class SessionBrowserServiceError extends Error {
174
187
  readonly code: SessionBrowserServiceErrorCode;
@@ -178,15 +191,18 @@ export declare class SessionBrowserServiceError extends Error {
178
191
  * Transport-neutral owner of the Runtime -> Session -> Browser -> Page model.
179
192
  * Every operation for one Session shares one queue; different Sessions remain independent.
180
193
  */
181
- export declare class SessionBrowserService {
194
+ export declare class SessionBrowserService implements SessionBrowserRequestHeadersPort {
182
195
  private readonly ports;
183
196
  private readonly records;
184
197
  private readonly generations;
198
+ private readonly requestHeaderPolicies;
185
199
  private readonly queues;
186
200
  private readonly deletingSessions;
187
201
  private readonly observers;
188
202
  private readonly pageIdFactory;
189
203
  private readonly bootGenerationSeed;
204
+ private readonly requestHeaderRevisionSeed;
205
+ private requestHeaderRevision;
190
206
  private readonly admissionReserve?;
191
207
  private shuttingDown;
192
208
  private closeAllPromise?;
@@ -205,6 +221,8 @@ export declare class SessionBrowserService {
205
221
  */
206
222
  getInspectionPageEndpoint(input: RuntimeBrowserPageMutationParams): Promise<SessionBrowserInspectionEndpoint>;
207
223
  getState(input: RuntimeBrowserStateGetParams): Promise<RuntimeBrowserState | null>;
224
+ getRequestHeaders(input: RuntimeBrowserRequestHeadersGetParams): Promise<RuntimeBrowserRequestHeadersPolicy>;
225
+ setRequestHeaders(input: RuntimeBrowserRequestHeadersSetParams): Promise<RuntimeBrowserRequestHeadersPolicy>;
208
226
  /**
209
227
  * Runtime-local automation handoff. This method is deliberately absent from
210
228
  * RemoteRuntimeBrowserHost and Direct RPC so a CDP endpoint cannot cross a
@@ -233,6 +251,8 @@ export declare class SessionBrowserService {
233
251
  private closeEveryRecord;
234
252
  private createBrowser;
235
253
  private reserveOpenAdmission;
254
+ private currentRequestHeaders;
255
+ private nextRequestHeadersRevision;
236
256
  private closeUnavailableForRecreate;
237
257
  private pageMutation;
238
258
  private pageMutationParsed;