@ai-matrx/desktop-protocol 0.21.78 → 0.21.80

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.
package/dist/client.cjs CHANGED
@@ -144,7 +144,7 @@ var import_zod2 = require("zod");
144
144
  var import_zod = require("zod");
145
145
  var import_uuid = require("@ai-matrx/kit/uuid");
146
146
  var PROTOCOL_MAJOR = 1;
147
- var PROTOCOL_MINOR = 76;
147
+ var PROTOCOL_MINOR = 77;
148
148
  var PROTOCOL_VERSION = `${PROTOCOL_MAJOR}.${PROTOCOL_MINOR}`;
149
149
  var ProtocolVersion = import_zod.z.string().regex(/^[1-9][0-9]{0,2}\.[0-9]{1,3}$/);
150
150
  var RequestId = import_zod.z.string().min(1).max(64).regex(/^[A-Za-z0-9_-]+$/);
@@ -2479,7 +2479,13 @@ var CodingServiceCredentials = import_zod27.z.strictObject({
2479
2479
  access_token: import_zod27.z.string().min(16).max(16384),
2480
2480
  user_id: import_zod27.z.string().regex(import_uuid4.UUID_SHAPE_PATTERN),
2481
2481
  /** The AI Matrx server this app talks to (production, or a local server on the nightly copy). */
2482
- server_url: import_zod27.z.string().min(1).max(2048).regex(/^https?:\/\/[^\s/?#]+(\/[^\s?#]*)?$/)
2482
+ server_url: import_zod27.z.string().min(1).max(2048).regex(/^https?:\/\/[^\s/?#]+(\/[^\s?#]*)?$/),
2483
+ /**
2484
+ * The organization the person works in (the core's active organization): the service sends it as
2485
+ * X-Organization-Id on every AI Matrx request, as the app's own calls do. Absent: the
2486
+ * service sends nothing and says an organization is needed (1.77).
2487
+ */
2488
+ organization_id: import_zod27.z.string().regex(import_uuid4.UUID_SHAPE_PATTERN).optional()
2483
2489
  });
2484
2490
  var CodingHistoryBlocked = import_zod27.z.enum([
2485
2491
  "signed_out",
@@ -5200,6 +5206,7 @@ function createDesktopClient(options) {
5200
5206
  const makeSocket = options.webSocket ?? defaultWebSocket();
5201
5207
  const heartbeatMs = options.heartbeatMs ?? 25e3;
5202
5208
  const requestTimeoutMs = options.requestTimeoutMs ?? 7e4;
5209
+ const firstFrameMs = options.firstFrameMs ?? requestTimeoutMs;
5203
5210
  const protocolVersion = options.protocolVersion ?? `${PROTOCOL_MAJOR}.${PROTOCOL_MINOR}`;
5204
5211
  const backoff = (0, import_realtime.createBackoff)({ ...options.backoff, now: timers.now });
5205
5212
  const diagnose = options.onDiagnostic ?? ((d) => {
@@ -5237,6 +5244,7 @@ function createDesktopClient(options) {
5237
5244
  let probeTimer = null;
5238
5245
  let lastHeardAt = 0;
5239
5246
  let heardCount = 0;
5247
+ let pongs = 0;
5240
5248
  let closedByUser = false;
5241
5249
  const newId = (prefix) => `${prefix}${(++nextId).toString(36)}`;
5242
5250
  function setState(patch) {
@@ -5284,6 +5292,7 @@ function createDesktopClient(options) {
5284
5292
  ws.onopen = () => {
5285
5293
  if (generation !== socketGeneration) return;
5286
5294
  lastHeardAt = timers.now();
5295
+ pongs = 0;
5287
5296
  helloId = newId("h");
5288
5297
  setState({ status: "handshaking" });
5289
5298
  sendJson({ type: "hello", id: helloId, protocolVersion, clientType: options.clientType, clientVersion: options.clientVersion });
@@ -5299,6 +5308,8 @@ function createDesktopClient(options) {
5299
5308
  };
5300
5309
  ws.onclose = (event) => {
5301
5310
  if (generation !== socketGeneration) return;
5311
+ const ev = event;
5312
+ reportClose(ev.code, ev.reason ?? "", ev.wasClean ?? false);
5302
5313
  onSocketGone(generation, event.code, null);
5303
5314
  };
5304
5315
  }
@@ -5310,6 +5321,7 @@ function createDesktopClient(options) {
5310
5321
  const silentMs = timers.now() - lastHeardAt;
5311
5322
  if (silentMs >= heartbeatMs * 2) {
5312
5323
  diagnose({ kind: "heartbeat_timeout", silentMs });
5324
+ reportClose(null, "heartbeat timeout", false);
5313
5325
  const dead = socket;
5314
5326
  onSocketGone(generation, null, new DesktopProtocolError("TIMEOUT", "Connection went silent", { retryable: true }));
5315
5327
  try {
@@ -5322,6 +5334,9 @@ function createDesktopClient(options) {
5322
5334
  scheduleHeartbeat(generation);
5323
5335
  }, heartbeatMs);
5324
5336
  }
5337
+ function reportClose(code, reason, wasClean) {
5338
+ diagnose({ kind: "socket_closed", code, reason: reason.slice(0, 120), wasClean, idleSinceLastRxMs: Math.max(0, timers.now() - lastHeardAt), pongs });
5339
+ }
5325
5340
  function onSocketGone(generation, code, error) {
5326
5341
  if (generation !== socketGeneration) return;
5327
5342
  socketGeneration++;
@@ -5410,7 +5425,10 @@ function createDesktopClient(options) {
5410
5425
  }
5411
5426
  function onMessage(data) {
5412
5427
  if (typeof data === "string") {
5413
- if (data === PONG_TEXT) return;
5428
+ if (data === PONG_TEXT) {
5429
+ pongs++;
5430
+ return;
5431
+ }
5414
5432
  let json;
5415
5433
  try {
5416
5434
  json = JSON.parse(data);
@@ -5441,6 +5459,10 @@ function createDesktopClient(options) {
5441
5459
  onFrame(frame.header, frame.payload);
5442
5460
  }
5443
5461
  function onEnvelope(msg) {
5462
+ if (msg.type !== "event") {
5463
+ const heard = streams.get(msg.id);
5464
+ if (heard) clearFirstFrame(heard);
5465
+ }
5444
5466
  switch (msg.type) {
5445
5467
  case "response": {
5446
5468
  if (msg.id === helloId) return onWelcome(msg.result);
@@ -5582,6 +5604,7 @@ function createDesktopClient(options) {
5582
5604
  diagnose({ kind: "unknown_stream", sid: header.sid });
5583
5605
  return;
5584
5606
  }
5607
+ clearFirstFrame(s);
5585
5608
  const isData = header.channel === FrameChannel.DATA || header.channel === FrameChannel.STDERR;
5586
5609
  const isSnapshot = header.channel === FrameChannel.SNAPSHOT;
5587
5610
  if (!isData && !isSnapshot) {
@@ -5627,8 +5650,29 @@ function createDesktopClient(options) {
5627
5650
  socket.send(encodeFrame({ version: FRAME_VERSION, flags: 0, channel: FrameChannel.STDIN, cid: 0, sid: s.sid, seq: s.sendSeq }, chunk));
5628
5651
  }
5629
5652
  }
5653
+ function clearFirstFrame(s) {
5654
+ if (s.firstFrameTimer === null) return;
5655
+ timers.clearTimeout(s.firstFrameTimer);
5656
+ s.firstFrameTimer = null;
5657
+ }
5658
+ function armFirstFrame(s) {
5659
+ clearFirstFrame(s);
5660
+ const waitMs = s.options.firstFrameMs ?? firstFrameMs;
5661
+ const id = s.id;
5662
+ s.firstFrameTimer = timers.setTimeout(() => {
5663
+ s.firstFrameTimer = null;
5664
+ if (s.ended || s.id !== id) return;
5665
+ diagnose({ kind: "first_frame_timeout", id, op: s.op, waitedMs: waitMs });
5666
+ streams.delete(id);
5667
+ if (s.sid !== null) bySid.delete(s.sid);
5668
+ abandon(id);
5669
+ endStream(s, null, new DesktopProtocolError("TIMEOUT", `${s.op}: no answer within ${waitMs} ms`, { retryable: true }));
5670
+ wake();
5671
+ }, waitMs);
5672
+ }
5630
5673
  function endStream(s, reason, error) {
5631
5674
  if (s.ended) return;
5675
+ clearFirstFrame(s);
5632
5676
  s.ended = true;
5633
5677
  s.sendQueue = [];
5634
5678
  if (!s.resultSettled) {
@@ -5651,7 +5695,7 @@ function createDesktopClient(options) {
5651
5695
  const params = { resource_id: s.resourceId, mode: s.options.mode ?? "control" };
5652
5696
  if (s.lastSeq > 0) params.since_seq = s.lastSeq;
5653
5697
  if (s.options.windowBytes !== void 0) params.window_bytes = s.options.windowBytes;
5654
- sendJson({ id, type: "request", cap: "session", op: "attach", params });
5698
+ if (sendJson({ id, type: "request", cap: "session", op: "attach", params })) armFirstFrame(s);
5655
5699
  }
5656
5700
  function safeCall(where, fn) {
5657
5701
  try {
@@ -5760,7 +5804,8 @@ function createDesktopClient(options) {
5760
5804
  resolveDone,
5761
5805
  rejectDone,
5762
5806
  ended: false,
5763
- reattaching: false
5807
+ reattaching: false,
5808
+ firstFrameTimer: null
5764
5809
  };
5765
5810
  const start = async () => {
5766
5811
  let welcome;
@@ -5779,7 +5824,9 @@ function createDesktopClient(options) {
5779
5824
  if (!sendJson({ id: s.id, type: "request", ...capOf(op), params: wire })) {
5780
5825
  streams.delete(s.id);
5781
5826
  endStream(s, null, new DesktopProtocolError("DEVICE_OFFLINE", "Connection lost", { retryable: true }));
5827
+ return;
5782
5828
  }
5829
+ armFirstFrame(s);
5783
5830
  };
5784
5831
  void start();
5785
5832
  opts.signal?.addEventListener("abort", () => handle.cancel(), { once: true });
@@ -5855,6 +5902,7 @@ function createDesktopClient(options) {
5855
5902
  if (heardCount > heardBefore) return;
5856
5903
  const silentMs = timers.now() - lastHeardAt;
5857
5904
  diagnose({ kind: "heartbeat_timeout", silentMs });
5905
+ reportClose(null, "wake probe timeout", false);
5858
5906
  const dead = socket;
5859
5907
  onSocketGone(generation, null, new DesktopProtocolError("TIMEOUT", "Connection went silent", { retryable: true }));
5860
5908
  try {