opencami 1.8.9 → 1.9.0

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 (103) hide show
  1. package/README.md +4 -2
  2. package/dist/client/assets/{CSPContext-DI-5GAnQ.js → CSPContext-TfUptlEu.js} +1 -1
  3. package/dist/client/assets/{DirectionContext-CrIsc5n9.js → DirectionContext-CQMv7g2N.js} +1 -1
  4. package/dist/client/assets/_sessionKey-DYknvaDS.js +23 -0
  5. package/dist/client/assets/agents-DNywJUai.js +2 -0
  6. package/dist/client/assets/agents-screen-fSZJpRi_.js +1 -0
  7. package/dist/client/assets/bots-Bqjqhws8.js +2 -0
  8. package/dist/client/assets/{bots-screen-BTKCOohV.js → bots-screen-4yT-e3cM.js} +1 -1
  9. package/dist/client/assets/{button-8ab4wOwy.js → button-DqP4GZwZ.js} +1 -1
  10. package/dist/client/assets/{composite-B2qsrzf3.js → composite-BLgu_EOL.js} +1 -1
  11. package/dist/client/assets/{connect-B3_p7C4I.js → connect-CiqRvR6s.js} +1 -1
  12. package/dist/client/assets/{dashboard-BtClHYpn.js → dashboard-CyWDWpbj.js} +1 -1
  13. package/dist/client/assets/event-2_Dxdv7h.js +1 -0
  14. package/dist/client/assets/file-explorer-screen-CZ2QKk-0.js +1 -0
  15. package/dist/client/assets/files-Cbhud0J8.js +2 -0
  16. package/dist/client/assets/follow-up-suggestions-Bi3Ci2my.js +5 -0
  17. package/dist/client/assets/{index-BXiha-Vz.js → index-C_gsW9fo.js} +1 -1
  18. package/dist/client/assets/{index-CtlYu8Ug.js → index-ygitKeM-.js} +1 -1
  19. package/dist/client/assets/keyboard-shortcuts-dialog-z-amTZVi.js +1 -0
  20. package/dist/client/assets/main-ZBMVSJTF.js +212 -0
  21. package/dist/client/assets/markdown-CHUjmWcv.js +87 -0
  22. package/dist/client/assets/memory-BRa-0plj.js +2 -0
  23. package/dist/client/assets/memory-screen-C_ZNDGLd.js +1 -0
  24. package/dist/client/assets/menu-CB88T7R1.js +1 -0
  25. package/dist/client/assets/{opencami-logo-BSed2Wez.js → opencami-logo-C0Kj1DiT.js} +1 -1
  26. package/dist/client/assets/proxy-D-juuhw6.js +9 -0
  27. package/dist/client/assets/{react-WkSlhZJd.js → react-Akh4y69S.js} +1 -1
  28. package/dist/client/assets/search-dialog-BasfzCyM.js +1 -0
  29. package/dist/client/assets/{search-sources-badge-Du8KpUEb.js → search-sources-badge-DwFHWd7S.js} +1 -1
  30. package/dist/client/assets/session-export-dialog-CAl3iJnD.js +1 -0
  31. package/dist/client/assets/settings-dialog-C8OoRXwX.js +1 -0
  32. package/dist/client/assets/skills-Cx12984a.js +2 -0
  33. package/dist/client/assets/{skills-panel-CVh1I-7D.js → skills-panel-B7BRAofP.js} +1 -1
  34. package/dist/client/assets/styles-CXa-SiWC.css +1 -0
  35. package/dist/client/assets/switch-DYEbEgy5.js +1 -0
  36. package/dist/client/assets/tabs-eiBvL0H7.js +1 -0
  37. package/dist/client/assets/thinking-CariuioI.js +1 -0
  38. package/dist/client/assets/tooltip-CekkGEYG.js +1 -0
  39. package/dist/client/assets/use-file-explorer-state-Dfyh4GwR.js +12 -0
  40. package/dist/client/assets/{useBaseUiId-DiwX_3so.js → useBaseUiId-DLhdkHJl.js} +1 -1
  41. package/dist/client/assets/{useCompositeItem-UPIPwR9H.js → useCompositeItem-DTSTTR0Z.js} +1 -1
  42. package/dist/client/assets/{useControlled-CT2hRlcU.js → useControlled-CpliTEve.js} +1 -1
  43. package/dist/client/assets/{useMutation-rx8UH99I.js → useMutation-CpD2Pn0F.js} +1 -1
  44. package/dist/client/assets/useOnFirstRender-DsFYFJoB.js +1 -0
  45. package/dist/client/assets/{useQuery-Boaa6oF3.js → useQuery-DMTgpIql.js} +1 -1
  46. package/dist/server/assets/{_sessionKey-B6iYeyCS.js → _sessionKey-Bhksr7VP.js} +267 -156
  47. package/dist/server/assets/{_tanstack-start-manifest_v-C9chPgNH.js → _tanstack-start-manifest_v-D-5ReiD4.js} +1 -1
  48. package/dist/server/assets/{agents-CmQ4vvXm.js → agents-BuE0Yum3.js} +1 -1
  49. package/dist/server/assets/{agents-screen-bmrIyFbk.js → agents-screen-CEQhbEwf.js} +3 -3
  50. package/dist/server/assets/{bots-Byt6jv0a.js → bots-BDHeSvSQ.js} +1 -1
  51. package/dist/server/assets/{bots-screen-C2TGFv42.js → bots-screen-C0NRS526.js} +2 -2
  52. package/dist/server/assets/{button-CwY2OHFj.js → button-kI8fEIZQ.js} +1 -1
  53. package/dist/server/assets/{connect-BNabuqpW.js → connect-CTVBm0Vc.js} +2 -2
  54. package/dist/server/assets/{file-explorer-screen-DH4UFK03.js → file-explorer-screen-FU_NhZmS.js} +4 -4
  55. package/dist/server/assets/{files-DYdXlQDr.js → files-DLxqp-h5.js} +1 -1
  56. package/dist/server/assets/{follow-up-suggestions-mzRQIB0k.js → follow-up-suggestions-B3hol2KT.js} +8 -8
  57. package/dist/server/assets/{index-COElhwGA.js → index-4G_4vZNY.js} +1 -1
  58. package/dist/server/assets/{index-BEWnDAH6.js → index-B_F4DTUu.js} +1 -1
  59. package/dist/server/assets/{keyboard-shortcuts-dialog-Cr6fOqHz.js → keyboard-shortcuts-dialog-Cp3ECNNi.js} +2 -2
  60. package/dist/server/assets/{markdown-DoX5Q7qh.js → markdown-CFdYXCRQ.js} +3 -3
  61. package/dist/server/assets/{memory-Cxu7i8ej.js → memory-rBB015W-.js} +1 -1
  62. package/dist/server/assets/{memory-screen-B5l1NZRY.js → memory-screen-vqXczcVo.js} +4 -4
  63. package/dist/server/assets/{menu-D90CDTi2.js → menu-D8cKTpmN.js} +1 -1
  64. package/dist/server/assets/{router-BqLGFd4L.js → router-C9JRmWMm.js} +142 -103
  65. package/dist/server/assets/{search-dialog-CmI7naPN.js → search-dialog-CTJULPB8.js} +8 -8
  66. package/dist/server/assets/{search-sources-badge-B0rAEDs_.js → search-sources-badge-B0t8Qffy.js} +1 -1
  67. package/dist/server/assets/{session-export-dialog-C53RRAah.js → session-export-dialog-CgtlOnwf.js} +2 -2
  68. package/dist/server/assets/{settings-dialog-BZ67gr9N.js → settings-dialog-B5yR2pBy.js} +35 -18
  69. package/dist/server/assets/{skills-Cy8xclXY.js → skills-BXUivxuo.js} +1 -1
  70. package/dist/server/assets/{skills-panel-BnRNb7u9.js → skills-panel-CDUp4jvw.js} +2 -2
  71. package/dist/server/assets/{switch-BbkUeVDV.js → switch-BZzwkgAQ.js} +1 -1
  72. package/dist/server/assets/{tabs-DDFZob0m.js → tabs-CWbp3mT4.js} +1 -1
  73. package/dist/server/assets/{thinking-CA8PSwKJ.js → thinking-CHx4Oouj.js} +8 -8
  74. package/dist/server/assets/{tooltip-DgsSPocE.js → tooltip-DOvOrSSS.js} +1 -1
  75. package/dist/server/assets/{use-file-explorer-state-s7CS50ho.js → use-file-explorer-state-E6cUvMva.js} +1 -1
  76. package/dist/server/server.js +195 -38
  77. package/package.json +1 -1
  78. package/dist/client/assets/_sessionKey-B4NZmxf3.js +0 -21
  79. package/dist/client/assets/agents-bptidK8z.js +0 -2
  80. package/dist/client/assets/agents-screen-6qdnPmx2.js +0 -1
  81. package/dist/client/assets/bots-BWpbaQ-E.js +0 -2
  82. package/dist/client/assets/event-DG3RKJz8.js +0 -1
  83. package/dist/client/assets/file-explorer-screen-Djl8x-8P.js +0 -1
  84. package/dist/client/assets/files-CjbCJDgC.js +0 -2
  85. package/dist/client/assets/follow-up-suggestions-BSCMXRXh.js +0 -5
  86. package/dist/client/assets/keyboard-shortcuts-dialog-HAufCn9C.js +0 -1
  87. package/dist/client/assets/main-CQKtcNr3.js +0 -210
  88. package/dist/client/assets/markdown-DFJF-FsV.js +0 -87
  89. package/dist/client/assets/memory-DnJOmcwU.js +0 -2
  90. package/dist/client/assets/memory-screen-Bm4NMAnU.js +0 -1
  91. package/dist/client/assets/menu-D26Vmgxl.js +0 -1
  92. package/dist/client/assets/popupStateMapping-DkI2OCkW.js +0 -1
  93. package/dist/client/assets/proxy-CHQ-VCN1.js +0 -9
  94. package/dist/client/assets/search-dialog-CCl4d0Pi.js +0 -1
  95. package/dist/client/assets/session-export-dialog-io9FvLKq.js +0 -1
  96. package/dist/client/assets/settings-dialog-B93qswor.js +0 -1
  97. package/dist/client/assets/skills-BNDGnHwM.js +0 -2
  98. package/dist/client/assets/styles-Ce2xZzc4.css +0 -1
  99. package/dist/client/assets/switch-CSnzINDW.js +0 -1
  100. package/dist/client/assets/tabs-CWfn44FL.js +0 -1
  101. package/dist/client/assets/thinking-BmoLlbFC.js +0 -1
  102. package/dist/client/assets/tooltip-CSGMH2t4.js +0 -1
  103. package/dist/client/assets/use-file-explorer-state-BYVzjwPA.js +0 -12
@@ -12,7 +12,7 @@ import { execFile, execSync } from "node:child_process";
12
12
  import { promisify } from "node:util";
13
13
  import { readFile, mkdir, writeFile, rename, stat, readdir, rm, realpath, lstat } from "node:fs/promises";
14
14
  import { posix } from "path";
15
- const appCss = "/assets/styles-Ce2xZzc4.css";
15
+ const appCss = "/assets/styles-CXa-SiWC.css";
16
16
  const swRegisterScript = `
17
17
  (() => {
18
18
  // Skip PWA service worker inside Capacitor native shell — they conflict
@@ -157,20 +157,22 @@ const accentColorScript = `
157
157
  try {
158
158
  const stored = localStorage.getItem('opencami-accent-color')
159
159
  const map = {
160
- green: { accent: '#22c55e', hover: '#16a34a', light: 'rgba(34, 197, 94, 0.10)' },
161
- blue: { accent: '#3b82f6', hover: '#2563eb', light: 'rgba(59, 130, 246, 0.10)' },
162
- purple: { accent: '#8b5cf6', hover: '#7c3aed', light: 'rgba(139, 92, 246, 0.10)' },
163
- orange: { accent: '#f97316', hover: '#ea580c', light: 'rgba(249, 115, 22, 0.10)' },
164
- pink: { accent: '#ec4899', hover: '#db2777', light: 'rgba(236, 72, 153, 0.10)' },
165
- red: { accent: '#ef4444', hover: '#dc2626', light: 'rgba(239, 68, 68, 0.10)' },
166
- cyan: { accent: '#06b6d4', hover: '#0891b2', light: 'rgba(6, 182, 212, 0.10)' },
167
- yellow: { accent: '#eab308', hover: '#ca8a04', light: 'rgba(234, 179, 8, 0.10)' },
160
+ green: { accent: '#22c55e', hover: '#16a34a', light: 'rgba(34, 197, 94, 0.10)', fg: '#ffffff' },
161
+ blue: { accent: '#3b82f6', hover: '#2563eb', light: 'rgba(59, 130, 246, 0.10)', fg: '#ffffff' },
162
+ purple: { accent: '#8b5cf6', hover: '#7c3aed', light: 'rgba(139, 92, 246, 0.10)', fg: '#ffffff' },
163
+ orange: { accent: '#f97316', hover: '#ea580c', light: 'rgba(249, 115, 22, 0.10)', fg: '#ffffff' },
164
+ pink: { accent: '#ec4899', hover: '#db2777', light: 'rgba(236, 72, 153, 0.10)', fg: '#ffffff' },
165
+ red: { accent: '#ef4444', hover: '#dc2626', light: 'rgba(239, 68, 68, 0.10)', fg: '#ffffff' },
166
+ cyan: { accent: '#06b6d4', hover: '#0891b2', light: 'rgba(6, 182, 212, 0.10)', fg: '#ffffff' },
167
+ yellow: { accent: '#eab308', hover: '#ca8a04', light: 'rgba(234, 179, 8, 0.10)', fg: '#ffffff' },
168
+ white: { accent: '#ffffff', hover: '#e5e5e5', light: 'rgba(255, 255, 255, 0.10)', fg: '#1a1a1a' },
168
169
  }
169
170
  const selected = map[stored] || map.green
170
171
  const root = document.documentElement
171
172
  root.style.setProperty('--opencami-accent', selected.accent)
172
173
  root.style.setProperty('--opencami-accent-hover', selected.hover)
173
174
  root.style.setProperty('--opencami-accent-light', selected.light)
175
+ root.style.setProperty('--opencami-accent-fg', selected.fg)
174
176
  } catch {}
175
177
  })()
176
178
  `;
@@ -329,7 +331,7 @@ function RootDocument({ children }) {
329
331
  ] })
330
332
  ] });
331
333
  }
332
- const $$splitComponentImporter$9 = () => import("./skills-Cy8xclXY.js");
334
+ const $$splitComponentImporter$9 = () => import("./skills-BXUivxuo.js");
333
335
  const Route$A = createFileRoute("/skills")({
334
336
  component: lazyRouteComponent($$splitComponentImporter$9, "component")
335
337
  });
@@ -346,11 +348,11 @@ const Route$z = createFileRoute("/new")({
346
348
  },
347
349
  component: lazyRouteComponent($$splitComponentImporter$8, "component")
348
350
  });
349
- const $$splitComponentImporter$7 = () => import("./memory-Cxu7i8ej.js");
351
+ const $$splitComponentImporter$7 = () => import("./memory-rBB015W-.js");
350
352
  const Route$y = createFileRoute("/memory")({
351
353
  component: lazyRouteComponent($$splitComponentImporter$7, "component")
352
354
  });
353
- const $$splitComponentImporter$6 = () => import("./files-DYdXlQDr.js");
355
+ const $$splitComponentImporter$6 = () => import("./files-DLxqp-h5.js");
354
356
  const Route$x = createFileRoute("/files")({
355
357
  component: lazyRouteComponent($$splitComponentImporter$6, "component")
356
358
  });
@@ -358,23 +360,23 @@ const $$splitComponentImporter$5 = () => import("./dashboard-UYRCu_mQ.js");
358
360
  const Route$w = createFileRoute("/dashboard")({
359
361
  component: lazyRouteComponent($$splitComponentImporter$5, "component")
360
362
  });
361
- const $$splitComponentImporter$4 = () => import("./connect-BNabuqpW.js");
363
+ const $$splitComponentImporter$4 = () => import("./connect-CTVBm0Vc.js");
362
364
  const Route$v = createFileRoute("/connect")({
363
365
  component: lazyRouteComponent($$splitComponentImporter$4, "component")
364
366
  });
365
- const $$splitComponentImporter$3 = () => import("./bots-Byt6jv0a.js");
367
+ const $$splitComponentImporter$3 = () => import("./bots-BDHeSvSQ.js");
366
368
  const Route$u = createFileRoute("/bots")({
367
369
  component: lazyRouteComponent($$splitComponentImporter$3, "component")
368
370
  });
369
- const $$splitComponentImporter$2 = () => import("./agents-CmQ4vvXm.js");
371
+ const $$splitComponentImporter$2 = () => import("./agents-BuE0Yum3.js");
370
372
  const Route$t = createFileRoute("/agents")({
371
373
  component: lazyRouteComponent($$splitComponentImporter$2, "component")
372
374
  });
373
- const $$splitComponentImporter$1 = () => import("./index-COElhwGA.js");
375
+ const $$splitComponentImporter$1 = () => import("./index-4G_4vZNY.js");
374
376
  const Route$s = createFileRoute("/")({
375
377
  component: lazyRouteComponent($$splitComponentImporter$1, "component")
376
378
  });
377
- const $$splitComponentImporter = () => import("./_sessionKey-B6iYeyCS.js").then((n) => n.$);
379
+ const $$splitComponentImporter = () => import("./_sessionKey-Bhksr7VP.js").then((n) => n.$);
378
380
  const Route$r = createFileRoute("/chat/$sessionKey")({
379
381
  component: lazyRouteComponent($$splitComponentImporter, "component")
380
382
  });
@@ -527,7 +529,7 @@ function buildConnectParams(url, token, password, nonce) {
527
529
  const clientId = "openclaw-control-ui";
528
530
  const clientMode = "webchat";
529
531
  const role = "operator";
530
- const scopes = ["operator.admin", "operator.approvals", "operator.pairing"];
532
+ const scopes = ["operator.read", "operator.write", "operator.admin", "operator.approvals", "operator.pairing"];
531
533
  if (!nonce) {
532
534
  throw new Error(
533
535
  "OpenClaw did not send connect.challenge nonce in time. If you are connecting cross-origin, ensure your origin is allowed (gateway.controlUi.allowedOrigins)."
@@ -558,7 +560,7 @@ function buildConnectParams(url, token, password, nonce) {
558
560
  mode: clientMode,
559
561
  instanceId: loadOrCreateInstanceId()
560
562
  },
561
- caps: [],
563
+ caps: ["tool-events"],
562
564
  auth: {
563
565
  token: token || void 0,
564
566
  password: password || void 0,
@@ -656,6 +658,7 @@ class PersistentGatewayConnection {
656
658
  const timer = setTimeout(() => {
657
659
  if (done) return;
658
660
  done = true;
661
+ ws.off("message", onMessage);
659
662
  resolve2("");
660
663
  }, 3e3);
661
664
  const onMessage = (data) => {
@@ -729,13 +732,13 @@ class PersistentGatewayConnection {
729
732
  mode: "webchat",
730
733
  instanceId: loadOrCreateInstanceId()
731
734
  },
732
- caps: [],
735
+ caps: ["tool-events"],
733
736
  auth: {
734
737
  token: token || void 0,
735
738
  password: password || void 0
736
739
  },
737
740
  role: "operator",
738
- scopes: ["operator.admin", "operator.approvals", "operator.pairing"],
741
+ scopes: ["operator.read", "operator.write", "operator.admin", "operator.approvals", "operator.pairing"],
739
742
  userAgent: `opencami/${process.env.npm_package_version ?? "dev"} (node ${process.version})`,
740
743
  locale: process.env.LANG || "en"
741
744
  };
@@ -775,7 +778,8 @@ class PersistentGatewayConnection {
775
778
  const event = {
776
779
  event: parsed.event,
777
780
  payload: parsed.payload ?? {},
778
- seq: parsed.seq
781
+ seq: parsed.seq,
782
+ stateVersion: typeof parsed.stateVersion === "number" ? parsed.stateVersion : void 0
779
783
  };
780
784
  const sessionKey = this._extractSessionKey(event);
781
785
  for (const listener of this.globalListeners) {
@@ -785,15 +789,20 @@ class PersistentGatewayConnection {
785
789
  }
786
790
  }
787
791
  if (sessionKey) {
788
- const listeners = this.sessionListeners.get(sessionKey);
789
- if (listeners && listeners.size > 0) {
790
- for (const listener of listeners) {
791
- try {
792
- listener(event);
793
- } catch {
792
+ let dispatched = false;
793
+ const eventSegments = sessionKey.split(":");
794
+ for (const [subKey, listeners] of this.sessionListeners) {
795
+ if (sessionKey === subKey || eventSegments.includes(subKey)) {
796
+ dispatched = true;
797
+ for (const listener of listeners) {
798
+ try {
799
+ listener(event);
800
+ } catch {
801
+ }
794
802
  }
795
803
  }
796
- } else {
804
+ }
805
+ if (!dispatched) {
797
806
  let buf = this.eventBuffer.get(sessionKey);
798
807
  if (!buf) {
799
808
  const timer = setTimeout(() => {
@@ -874,14 +883,22 @@ class PersistentGatewayConnection {
874
883
  this.sessionListeners.set(sessionKey, listeners);
875
884
  }
876
885
  listeners.add(listener);
877
- const buf = this.eventBuffer.get(sessionKey);
878
- if (buf) {
879
- this.eventBuffer.delete(sessionKey);
880
- clearTimeout(buf.timer);
881
- for (const event of buf.events) {
882
- try {
883
- listener(event);
884
- } catch {
886
+ const keysToFlush = [];
887
+ for (const bufKey of this.eventBuffer.keys()) {
888
+ if (bufKey === sessionKey || bufKey.split(":").includes(sessionKey)) {
889
+ keysToFlush.push(bufKey);
890
+ }
891
+ }
892
+ for (const bufKey of keysToFlush) {
893
+ const buf = this.eventBuffer.get(bufKey);
894
+ if (buf) {
895
+ this.eventBuffer.delete(bufKey);
896
+ clearTimeout(buf.timer);
897
+ for (const event of buf.events) {
898
+ try {
899
+ listener(event);
900
+ } catch {
901
+ }
885
902
  }
886
903
  }
887
904
  }
@@ -921,13 +938,34 @@ function getPersistentConnection() {
921
938
  }
922
939
  return _proc.__opencamiGatewayInstance;
923
940
  }
924
- async function gatewayRpc(method, params) {
941
+ async function acquireGatewayClient(key, callbacks) {
925
942
  const conn = getPersistentConnection();
926
- return conn.rpc(method, params);
943
+ await conn.ensureConnected();
944
+ let unsubscribe = null;
945
+ if (callbacks?.onEvent) {
946
+ unsubscribe = conn.subscribe(key, callbacks.onEvent);
947
+ }
948
+ return {
949
+ client: {
950
+ connect: () => conn.ensureConnected(),
951
+ sendReq: (method, params) => conn.rpc(method, params),
952
+ close: () => {
953
+ },
954
+ isClosed: () => !conn.isConnected,
955
+ addCallbacks: () => () => {
956
+ }
957
+ },
958
+ release: () => {
959
+ unsubscribe?.();
960
+ }
961
+ };
962
+ }
963
+ async function gatewayRpcShared(method, params, _key) {
964
+ return gatewayRpc(method, params);
927
965
  }
928
- function subscribeGatewayEvents(sessionKey, listener) {
966
+ async function gatewayRpc(method, params) {
929
967
  const conn = getPersistentConnection();
930
- return conn.subscribe(sessionKey, listener);
968
+ return conn.rpc(method, params);
931
969
  }
932
970
  function getDeviceStatus() {
933
971
  const conn = getPersistentConnection();
@@ -1286,91 +1324,92 @@ const Route$o = createFileRoute("/api/stream")({
1286
1324
  handlers: {
1287
1325
  GET: async ({ request }) => {
1288
1326
  const url = new URL(request.url);
1289
- const sessionKey = url.searchParams.get("sessionKey");
1290
- if (!sessionKey) {
1327
+ const sessionKey = url.searchParams.get("sessionKey")?.trim() || "";
1328
+ const friendlyId = url.searchParams.get("friendlyId")?.trim() || "";
1329
+ const key = sessionKey || friendlyId;
1330
+ if (!key) {
1291
1331
  return new Response(
1292
- JSON.stringify({ ok: false, error: "sessionKey required" }),
1332
+ JSON.stringify({ ok: false, error: "sessionKey or friendlyId required" }),
1293
1333
  { status: 400, headers: { "content-type": "application/json" } }
1294
1334
  );
1295
1335
  }
1296
1336
  const pass = new PassThrough();
1297
1337
  const encoder = new TextEncoder();
1298
1338
  let closed = false;
1299
- let unsubscribe = null;
1300
- function sendSSE(event, data) {
1339
+ let heartbeat = null;
1340
+ let releaseClient = null;
1341
+ let lastSeq = -1;
1342
+ let lastEventFingerprint = "";
1343
+ function writeChunk(chunk) {
1301
1344
  if (closed) return;
1302
1345
  try {
1303
- pass.write(encoder.encode(`event: ${event}
1304
- data: ${JSON.stringify(data)}
1305
-
1306
- `));
1346
+ pass.write(encoder.encode(chunk));
1307
1347
  } catch {
1348
+ cleanup();
1308
1349
  }
1309
1350
  }
1351
+ function send(data) {
1352
+ writeChunk(`data: ${JSON.stringify(data)}
1353
+
1354
+ `);
1355
+ }
1310
1356
  function cleanup() {
1311
1357
  if (closed) return;
1312
1358
  closed = true;
1313
- if (unsubscribe) {
1314
- unsubscribe();
1315
- unsubscribe = null;
1359
+ if (heartbeat) {
1360
+ clearInterval(heartbeat);
1361
+ heartbeat = null;
1362
+ }
1363
+ if (releaseClient) {
1364
+ releaseClient();
1365
+ releaseClient = null;
1316
1366
  }
1317
1367
  try {
1318
1368
  pass.end();
1319
1369
  } catch {
1320
1370
  }
1321
1371
  }
1322
- pass.write(encoder.encode(": connected\n\n"));
1323
- let gotAgentStream = false;
1324
- unsubscribe = subscribeGatewayEvents(sessionKey, (evt) => {
1325
- if (evt.event === "agent") {
1326
- const payload = evt.payload;
1327
- const agentStream = payload.stream;
1328
- if (agentStream === "assistant") {
1329
- gotAgentStream = true;
1330
- const data = payload.data ?? payload;
1331
- const text = typeof data.delta === "string" ? data.delta : typeof data.text === "string" ? data.text : typeof payload.text === "string" ? payload.text : typeof payload.delta === "string" ? payload.delta : "";
1332
- if (text) {
1333
- sendSSE("delta", { text, sessionKey });
1334
- }
1335
- } else if (agentStream === "tool") {
1336
- gotAgentStream = true;
1337
- const tdata = payload.data ?? payload;
1338
- sendSSE("tool", {
1339
- name: tdata.name ?? tdata.toolName ?? payload.name ?? "",
1340
- status: tdata.phase ?? tdata.status ?? payload.phase ?? "running",
1341
- id: tdata.id ?? tdata.toolCallId ?? payload.id ?? "",
1342
- sessionKey
1343
- });
1344
- } else if (agentStream === "lifecycle") {
1345
- const ldata = payload.data ?? payload;
1346
- const phase = ldata.phase ?? payload.phase;
1347
- if (phase === "end" || phase === "error") {
1348
- sendSSE("done", {
1349
- sessionKey,
1350
- status: phase,
1351
- error: phase === "error" ? payload.error : void 0
1352
- });
1353
- cleanup();
1372
+ writeChunk(": connected\n\n");
1373
+ heartbeat = setInterval(() => {
1374
+ writeChunk("event: ping\ndata: {}\n\n");
1375
+ }, 15e3);
1376
+ try {
1377
+ const handle = await acquireGatewayClient(key, {
1378
+ onEvent(event) {
1379
+ if (typeof event.seq === "number") {
1380
+ if (event.seq <= lastSeq) return;
1381
+ lastSeq = event.seq;
1354
1382
  }
1355
- }
1356
- } else if (evt.event === "chat") {
1357
- const payload = evt.payload;
1358
- const state = payload.state ?? payload.kind;
1359
- const msg = payload.message;
1360
- if (state === "delta" && !gotAgentStream) {
1361
- const content = Array.isArray(msg?.content) ? msg.content : [];
1362
- const firstBlock = content[0];
1363
- const text = typeof firstBlock?.text === "string" ? firstBlock.text : typeof payload.text === "string" ? payload.text : typeof payload.delta === "string" ? payload.delta : "";
1364
- if (text) {
1365
- sendSSE("delta", { text, sessionKey });
1383
+ const fp = event.event + ":" + JSON.stringify(event.payload);
1384
+ if (fp === lastEventFingerprint) return;
1385
+ lastEventFingerprint = fp;
1386
+ const p = event.payload;
1387
+ const eventSessionKey = typeof p?.sessionKey === "string" ? p.sessionKey : "";
1388
+ if (eventSessionKey && eventSessionKey !== key && !eventSessionKey.split(":").includes(key)) {
1389
+ return;
1366
1390
  }
1367
- } else if (state === "final") {
1368
- sendSSE("done", { sessionKey, status: "end" });
1369
- cleanup();
1391
+ send({
1392
+ event: event.event,
1393
+ payload: event.payload,
1394
+ seq: event.seq,
1395
+ stateVersion: event.stateVersion
1396
+ });
1397
+ },
1398
+ onError(error) {
1399
+ send({ event: "error", payload: error.message });
1370
1400
  }
1401
+ });
1402
+ if (closed) {
1403
+ handle.release();
1404
+ } else {
1405
+ releaseClient = handle.release;
1371
1406
  }
1372
- });
1373
- request.signal?.addEventListener("abort", cleanup);
1407
+ } catch (error) {
1408
+ const message = error instanceof Error ? error.message : String(error);
1409
+ send({ event: "error", payload: message });
1410
+ cleanup();
1411
+ }
1412
+ request.signal.addEventListener("abort", cleanup, { once: true });
1374
1413
  const webStream = Readable.toWeb(pass);
1375
1414
  return new Response(webStream, {
1376
1415
  headers: {
@@ -1772,7 +1811,7 @@ const Route$l = createFileRoute("/api/send")({
1772
1811
  if (sessionKey.length === 0) {
1773
1812
  sessionKey = "main";
1774
1813
  }
1775
- const res = await gatewayRpc("chat.send", {
1814
+ const res = await gatewayRpcShared("chat.send", {
1776
1815
  sessionKey,
1777
1816
  message,
1778
1817
  thinking,
@@ -1781,7 +1820,7 @@ const Route$l = createFileRoute("/api/send")({
1781
1820
  deliver: false,
1782
1821
  timeoutMs: 12e4,
1783
1822
  idempotencyKey: typeof body.idempotencyKey === "string" ? body.idempotencyKey : randomUUID()
1784
- });
1823
+ }, sessionKey);
1785
1824
  return json({ ok: true, ...res, sessionKey });
1786
1825
  } catch (err) {
1787
1826
  return json(
@@ -3,30 +3,30 @@ import { useState, useRef, useEffect, useCallback, useMemo } from "react";
3
3
  import { useNavigate } from "@tanstack/react-router";
4
4
  import { HugeiconsIcon } from "@hugeicons/react";
5
5
  import { Search01Icon, Cancel01Icon, Loading03Icon } from "@hugeicons/core-free-icons";
6
- import { D as DialogRoot, a as DialogContent } from "./use-file-explorer-state-s7CS50ho.js";
6
+ import { D as DialogRoot, a as DialogContent } from "./use-file-explorer-state-E6cUvMva.js";
7
7
  import { useQueryClient } from "@tanstack/react-query";
8
- import { c as chatQueryKeys } from "./_sessionKey-B6iYeyCS.js";
9
- import { c as cn } from "./button-CwY2OHFj.js";
8
+ import { c as chatQueryKeys } from "./_sessionKey-Bhksr7VP.js";
9
+ import { c as cn } from "./button-kI8fEIZQ.js";
10
10
  import "@base-ui/react/dialog";
11
11
  import "zustand";
12
- import "./tooltip-DgsSPocE.js";
12
+ import "./tooltip-DOvOrSSS.js";
13
13
  import "@base-ui/react/tooltip";
14
14
  import "motion/react";
15
15
  import "@base-ui/react/alert-dialog";
16
16
  import "@base-ui/react/collapsible";
17
17
  import "@base-ui/react/scroll-area";
18
- import "./menu-D90CDTi2.js";
18
+ import "./menu-D8cKTpmN.js";
19
19
  import "@base-ui/react/menu";
20
20
  import "./opencami-logo-C-43FL3R.js";
21
- import "./markdown-DoX5Q7qh.js";
21
+ import "./markdown-CFdYXCRQ.js";
22
22
  import "marked";
23
23
  import "react-markdown";
24
24
  import "remark-breaks";
25
25
  import "remark-gfm";
26
- import "./index-BEWnDAH6.js";
26
+ import "./index-B_F4DTUu.js";
27
27
  import "zustand/middleware";
28
28
  import "react-dom";
29
- import "./router-BqLGFd4L.js";
29
+ import "./router-C9JRmWMm.js";
30
30
  import "node:crypto";
31
31
  import "node:fs";
32
32
  import "node:os";
@@ -2,7 +2,7 @@ import { jsxs, jsx } from "react/jsx-runtime";
2
2
  import { useState } from "react";
3
3
  import { HugeiconsIcon } from "@hugeicons/react";
4
4
  import { Search01Icon, ArrowRight01Icon } from "@hugeicons/core-free-icons";
5
- import { c as cn } from "./button-CwY2OHFj.js";
5
+ import { c as cn } from "./button-kI8fEIZQ.js";
6
6
  import "@base-ui/react/merge-props";
7
7
  import "@base-ui/react/use-render";
8
8
  import "class-variance-authority";
@@ -1,7 +1,7 @@
1
1
  import { jsx, jsxs } from "react/jsx-runtime";
2
2
  import { useState } from "react";
3
- import { D as DialogRoot, a as DialogContent, b as DialogTitle, c as DialogDescription, d as DialogClose } from "./use-file-explorer-state-s7CS50ho.js";
4
- import { B as Button } from "./button-CwY2OHFj.js";
3
+ import { D as DialogRoot, a as DialogContent, b as DialogTitle, c as DialogDescription, d as DialogClose } from "./use-file-explorer-state-E6cUvMva.js";
4
+ import { B as Button } from "./button-kI8fEIZQ.js";
5
5
  import "@base-ui/react/dialog";
6
6
  import "zustand";
7
7
  import "@base-ui/react/merge-props";
@@ -3,12 +3,12 @@ import { useState, useEffect, useRef } from "react";
3
3
  import { create } from "zustand";
4
4
  import { HugeiconsIcon } from "@hugeicons/react";
5
5
  import { Cancel01Icon, Link01Icon, PaintBoardIcon, MessageEdit01Icon, Settings02Icon, UserIcon, VoiceIcon, AiBrain01Icon, InformationCircleIcon, ComputerIcon, Sun01Icon, Moon01Icon, Leaf01Icon, DropletIcon, Loading02Icon, Tick01Icon, Cancel02Icon } from "@hugeicons/core-free-icons";
6
- import { B as Button, c as cn } from "./button-CwY2OHFj.js";
7
- import { D as DialogRoot, a as DialogContent, b as DialogTitle, c as DialogDescription, d as DialogClose } from "./use-file-explorer-state-s7CS50ho.js";
8
- import { S as Switch } from "./switch-BbkUeVDV.js";
9
- import { T as Tabs, a as TabsList, b as TabsTab } from "./tabs-DDFZob0m.js";
10
- import { u as useChatSettings } from "./index-BEWnDAH6.js";
11
- import { u as useLlmSettings, g as getLlmProviderDefaults } from "./_sessionKey-B6iYeyCS.js";
6
+ import { B as Button, c as cn } from "./button-kI8fEIZQ.js";
7
+ import { D as DialogRoot, a as DialogContent, b as DialogTitle, c as DialogDescription, d as DialogClose } from "./use-file-explorer-state-E6cUvMva.js";
8
+ import { S as Switch } from "./switch-BZzwkgAQ.js";
9
+ import { T as Tabs, a as TabsList, b as TabsTab } from "./tabs-CWbp3mT4.js";
10
+ import { u as useChatSettings } from "./index-B_F4DTUu.js";
11
+ import { u as useLlmSettings, g as getLlmProviderDefaults } from "./_sessionKey-Bhksr7VP.js";
12
12
  import "@base-ui/react/merge-props";
13
13
  import "@base-ui/react/use-render";
14
14
  import "class-variance-authority";
@@ -20,22 +20,22 @@ import "@base-ui/react/tabs";
20
20
  import "zustand/middleware";
21
21
  import "@tanstack/react-router";
22
22
  import "@tanstack/react-query";
23
- import "./tooltip-DgsSPocE.js";
23
+ import "./tooltip-DOvOrSSS.js";
24
24
  import "@base-ui/react/tooltip";
25
25
  import "motion/react";
26
26
  import "@base-ui/react/alert-dialog";
27
27
  import "@base-ui/react/collapsible";
28
28
  import "@base-ui/react/scroll-area";
29
- import "./menu-D90CDTi2.js";
29
+ import "./menu-D8cKTpmN.js";
30
30
  import "@base-ui/react/menu";
31
31
  import "./opencami-logo-C-43FL3R.js";
32
- import "./markdown-DoX5Q7qh.js";
32
+ import "./markdown-CFdYXCRQ.js";
33
33
  import "marked";
34
34
  import "react-markdown";
35
35
  import "remark-breaks";
36
36
  import "remark-gfm";
37
37
  import "react-dom";
38
- import "./router-BqLGFd4L.js";
38
+ import "./router-C9JRmWMm.js";
39
39
  import "node:crypto";
40
40
  import "node:fs";
41
41
  import "node:os";
@@ -163,56 +163,72 @@ const accentColorOptions = [
163
163
  label: "Green",
164
164
  accent: "#22c55e",
165
165
  hover: "#16a34a",
166
- light: "rgba(34, 197, 94, 0.10)"
166
+ light: "rgba(34, 197, 94, 0.10)",
167
+ fg: "#ffffff"
167
168
  },
168
169
  {
169
170
  value: "blue",
170
171
  label: "Blue",
171
172
  accent: "#3b82f6",
172
173
  hover: "#2563eb",
173
- light: "rgba(59, 130, 246, 0.10)"
174
+ light: "rgba(59, 130, 246, 0.10)",
175
+ fg: "#ffffff"
174
176
  },
175
177
  {
176
178
  value: "purple",
177
179
  label: "Purple",
178
180
  accent: "#8b5cf6",
179
181
  hover: "#7c3aed",
180
- light: "rgba(139, 92, 246, 0.10)"
182
+ light: "rgba(139, 92, 246, 0.10)",
183
+ fg: "#ffffff"
181
184
  },
182
185
  {
183
186
  value: "orange",
184
187
  label: "Orange",
185
188
  accent: "#f97316",
186
189
  hover: "#ea580c",
187
- light: "rgba(249, 115, 22, 0.10)"
190
+ light: "rgba(249, 115, 22, 0.10)",
191
+ fg: "#ffffff"
188
192
  },
189
193
  {
190
194
  value: "pink",
191
195
  label: "Pink",
192
196
  accent: "#ec4899",
193
197
  hover: "#db2777",
194
- light: "rgba(236, 72, 153, 0.10)"
198
+ light: "rgba(236, 72, 153, 0.10)",
199
+ fg: "#ffffff"
195
200
  },
196
201
  {
197
202
  value: "red",
198
203
  label: "Red",
199
204
  accent: "#ef4444",
200
205
  hover: "#dc2626",
201
- light: "rgba(239, 68, 68, 0.10)"
206
+ light: "rgba(239, 68, 68, 0.10)",
207
+ fg: "#ffffff"
202
208
  },
203
209
  {
204
210
  value: "cyan",
205
211
  label: "Cyan",
206
212
  accent: "#06b6d4",
207
213
  hover: "#0891b2",
208
- light: "rgba(6, 182, 212, 0.10)"
214
+ light: "rgba(6, 182, 212, 0.10)",
215
+ fg: "#ffffff"
209
216
  },
210
217
  {
211
218
  value: "yellow",
212
219
  label: "Yellow",
213
220
  accent: "#eab308",
214
221
  hover: "#ca8a04",
215
- light: "rgba(234, 179, 8, 0.10)"
222
+ light: "rgba(234, 179, 8, 0.10)",
223
+ fg: "#ffffff"
224
+ },
225
+ {
226
+ value: "white",
227
+ label: "White",
228
+ accent: "#ffffff",
229
+ hover: "#e5e5e5",
230
+ light: "rgba(255, 255, 255, 0.10)",
231
+ fg: "#1a1a1a"
216
232
  }
217
233
  ];
218
234
  const chatWidthOptions = [
@@ -458,6 +474,7 @@ function SettingsDialog({
458
474
  root.style.setProperty("--opencami-accent", selected.accent);
459
475
  root.style.setProperty("--opencami-accent-hover", selected.hover);
460
476
  root.style.setProperty("--opencami-accent-light", selected.light);
477
+ root.style.setProperty("--opencami-accent-fg", selected.fg);
461
478
  }
462
479
  function applyChatWidth(value) {
463
480
  if (typeof document === "undefined") return;
@@ -1,6 +1,6 @@
1
1
  import { jsx } from "react/jsx-runtime";
2
2
  import { Suspense, lazy } from "react";
3
- const SkillsPanel = lazy(() => import("./skills-panel-BnRNb7u9.js").then((m) => ({
3
+ const SkillsPanel = lazy(() => import("./skills-panel-CDUp4jvw.js").then((m) => ({
4
4
  default: m.SkillsPanel
5
5
  })));
6
6
  function SkillsRoute() {
@@ -3,8 +3,8 @@ import { useState, useCallback, useEffect, useRef, useMemo } from "react";
3
3
  import { HugeiconsIcon } from "@hugeicons/react";
4
4
  import { ArrowLeft01Icon, Loading02Icon, ArrowUpRight01Icon, Download04Icon, StarIcon, Tick01Icon, Calendar01Icon, Search01Icon, Shield01Icon } from "@hugeicons/core-free-icons";
5
5
  import { Link } from "@tanstack/react-router";
6
- import { B as Button } from "./button-CwY2OHFj.js";
7
- import { T as Tabs, a as TabsList, b as TabsTab } from "./tabs-DDFZob0m.js";
6
+ import { B as Button } from "./button-kI8fEIZQ.js";
7
+ import { T as Tabs, a as TabsList, b as TabsTab } from "./tabs-CWbp3mT4.js";
8
8
  import "@base-ui/react/merge-props";
9
9
  import "@base-ui/react/use-render";
10
10
  import "class-variance-authority";
@@ -1,6 +1,6 @@
1
1
  import { jsx } from "react/jsx-runtime";
2
2
  import { Switch as Switch$1 } from "@base-ui/react/switch";
3
- import { c as cn } from "./button-CwY2OHFj.js";
3
+ import { c as cn } from "./button-kI8fEIZQ.js";
4
4
  function Switch({ className, ...props }) {
5
5
  return /* @__PURE__ */ jsx(
6
6
  Switch$1.Root,
@@ -1,6 +1,6 @@
1
1
  import { jsx, jsxs } from "react/jsx-runtime";
2
2
  import { Tabs as Tabs$1 } from "@base-ui/react/tabs";
3
- import { c as cn } from "./button-CwY2OHFj.js";
3
+ import { c as cn } from "./button-kI8fEIZQ.js";
4
4
  function Tabs({ className, ...props }) {
5
5
  return /* @__PURE__ */ jsx(
6
6
  Tabs$1.Root,