@cryptiklemur/lattice 5.13.1 → 5.13.3

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.
@@ -41,23 +41,27 @@ const sessionPermissionOverrides = new Map();
41
41
  export function getAvailableModels() {
42
42
  return getWarmupModels();
43
43
  }
44
- function createMessageQueue() {
44
+ function createMessageQueue(label) {
45
45
  const queue = [];
46
46
  let waiting = null;
47
47
  let ended = false;
48
+ let readCount = 0;
48
49
  return {
49
50
  push: function (msg) {
50
51
  if (waiting) {
52
+ log.chat("MQ[%s] push: SDK was waiting, delivering immediately (queued=%d)", label || "?", queue.length);
51
53
  const resolve = waiting;
52
54
  waiting = null;
53
55
  resolve({ value: msg, done: false });
54
56
  }
55
57
  else {
56
58
  queue.push(msg);
59
+ log.chat("MQ[%s] push: buffered (queued=%d)", label || "?", queue.length);
57
60
  }
58
61
  },
59
62
  end: function () {
60
63
  ended = true;
64
+ log.chat("MQ[%s] end called (queued=%d, waiting=%s)", label || "?", queue.length, String(!!waiting));
61
65
  if (waiting) {
62
66
  const resolve = waiting;
63
67
  waiting = null;
@@ -67,12 +71,16 @@ function createMessageQueue() {
67
71
  [Symbol.asyncIterator]: function () {
68
72
  return {
69
73
  next: function () {
74
+ readCount++;
70
75
  if (queue.length > 0) {
76
+ log.chat("MQ[%s] read #%d: from buffer (remaining=%d)", label || "?", readCount, queue.length - 1);
71
77
  return Promise.resolve({ value: queue.shift(), done: false });
72
78
  }
73
79
  if (ended) {
80
+ log.chat("MQ[%s] read #%d: ended", label || "?", readCount);
74
81
  return Promise.resolve({ value: undefined, done: true });
75
82
  }
83
+ log.chat("MQ[%s] read #%d: waiting for push", label || "?", readCount);
76
84
  return new Promise(function (resolve) {
77
85
  waiting = resolve;
78
86
  });
@@ -654,7 +662,7 @@ export function startChatStream(options) {
654
662
  text,
655
663
  uuid: crypto.randomUUID(),
656
664
  });
657
- const mq = createMessageQueue();
665
+ const mq = createMessageQueue(sessionId.slice(0, 8));
658
666
  const firstMsg = buildSDKUserMessage(prompt, attachments, sessionId);
659
667
  const stream = query({ prompt: mq, options: queryOptions });
660
668
  pendingStreams.delete(sessionId);
@@ -695,40 +703,48 @@ export function startChatStream(options) {
695
703
  catch (initErr) {
696
704
  log.chat("Session %s SDK initialization FAILED: %O", sessionId, initErr);
697
705
  }
698
- if (!shouldResume) {
699
- log.chat("Session %s pushing first message to queue", sessionId);
700
- mq.push(firstMsg);
706
+ log.chat("Session %s pushing first message to queue", sessionId);
707
+ mq.push(firstMsg);
708
+ let retrying = false;
709
+ const TURN_TIMEOUT_MS = 30000;
710
+ let turnTimer = null;
711
+ function resetTurnTimer() {
712
+ if (turnTimer)
713
+ clearTimeout(turnTimer);
714
+ turnTimer = setTimeout(function () {
715
+ if (!sessionStream.sawNewTurnContent && !sessionStream.ended) {
716
+ log.chat("Session %s turn timeout: no new content after %dms, aborting", sessionId, TURN_TIMEOUT_MS);
717
+ abortController.abort();
718
+ }
719
+ }, TURN_TIMEOUT_MS);
701
720
  }
721
+ resetTurnTimer();
702
722
  try {
703
723
  log.chat("Session %s entering stream loop", sessionId);
704
724
  let msgCount = 0;
705
- let replayDone = !shouldResume;
706
725
  for await (const msg of stream) {
707
726
  msgCount++;
708
- if (msgCount <= 5 || msg.type === "result") {
709
- log.chat("Session %s msg #%d type=%s", sessionId, msgCount, msg.type);
710
- }
711
- if (!replayDone && msg.type === "result") {
712
- replayDone = true;
713
- log.chat("Session %s replay complete at msg #%d, waiting for connection settle", sessionId, msgCount);
714
- await new Promise(function (resolve) { setTimeout(resolve, 200); });
715
- log.chat("Session %s pushing first message after replay", sessionId);
716
- mq.push(firstMsg);
717
- continue;
727
+ log.chat("Session %s msg #%d type=%s", sessionId, msgCount, msg.type);
728
+ if (msg.type === "stream_event" || (msg.type === "assistant" && msg.message?.model !== "<synthetic>")) {
729
+ resetTurnTimer();
718
730
  }
719
731
  processMessage(sessionStream, msg);
720
732
  }
733
+ if (turnTimer)
734
+ clearTimeout(turnTimer);
721
735
  log.chat("Session %s stream ended normally after %d messages", sessionId, msgCount);
722
736
  }
723
737
  catch (err) {
738
+ if (turnTimer)
739
+ clearTimeout(turnTimer);
724
740
  const errMsg = err instanceof Error ? err.message : String(err);
725
741
  log.chat("Session %s stream error: %s", sessionId, errMsg);
726
742
  if (errMsg.includes("aborted") || errMsg.includes("AbortError")) {
727
743
  log.chat("Session %s stream aborted", sessionId);
728
744
  }
729
- else if (errMsg.includes("Sent before connected")) {
730
- log.chat("Session %s SDK WebSocket race condition: %s", sessionId, errMsg);
731
- sendTo(sessionStream.clientId, { type: "chat:error", message: "Connection failed. Please try again." });
745
+ else if (errMsg.includes("Sent before connected") && shouldResume) {
746
+ log.chat("Session %s WebSocket not ready after resume, retrying in 500ms", sessionId);
747
+ retrying = true;
732
748
  }
733
749
  else {
734
750
  console.error("[lattice] SDK stream error: " + errMsg);
@@ -740,6 +756,12 @@ export function startChatStream(options) {
740
756
  pendingStreams.delete(sessionId);
741
757
  sessionStreams.delete(sessionId);
742
758
  persistStreamState();
759
+ if (retrying) {
760
+ log.chat("Session %s cleaning up for retry", sessionId);
761
+ await new Promise(function (resolve) { setTimeout(resolve, 500); });
762
+ startChatStream(options);
763
+ return;
764
+ }
743
765
  broadcast({ type: "session:busy", sessionId, busy: false }, sessionStream.clientId);
744
766
  const toCleanup = [];
745
767
  pendingPermissions.forEach(function (entry, reqId) {
@@ -812,6 +834,7 @@ function processMessage(ss, msg) {
812
834
  }
813
835
  if (msg.type === "assistant") {
814
836
  const assistantMsg = msg;
837
+ log.chat("Session %s assistant message: model=%s", sessionId, assistantMsg.message.model || "unknown");
815
838
  const msgUsage = assistantMsg.message.usage;
816
839
  if (msgUsage && msgUsage.input_tokens != null) {
817
840
  const ctxWindow = guessContextWindow(assistantMsg.message.model || "");
@@ -836,6 +859,8 @@ function processMessage(ss, msg) {
836
859
  const partial = msg;
837
860
  const evt = partial.event;
838
861
  if (evt.type === "content_block_start") {
862
+ const blockInfo = evt.content_block;
863
+ log.chat("Session %s content_block_start: type=%s name=%s", sessionId, blockInfo.type, blockInfo.name || "text");
839
864
  ss.sawNewTurnContent = true;
840
865
  const block = evt.content_block;
841
866
  const idx = evt.index;
@@ -967,11 +992,12 @@ function processMessage(ss, msg) {
967
992
  return;
968
993
  }
969
994
  if (msg.type === "result") {
995
+ const resultMsg = msg;
996
+ log.chat("Session %s result: cost=$%s, sawContent=%s", sessionId, String(resultMsg.total_cost_usd || 0), String(ss.sawNewTurnContent));
970
997
  if (!ss.sawNewTurnContent) {
971
998
  log.chat("Session %s ignoring replayed result (no new turn content seen)", sessionId);
972
999
  return;
973
1000
  }
974
- const resultMsg = msg;
975
1001
  const dur = Date.now() - ss.turnStartTime;
976
1002
  const cost = resultMsg.total_cost_usd || 0;
977
1003
  if (resultMsg.usage) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cryptiklemur/lattice",
3
- "version": "5.13.1",
3
+ "version": "5.13.3",
4
4
  "description": "Multi-machine agentic dashboard for Claude Code. Monitor sessions, manage MCP servers and skills, orchestrate across mesh-networked nodes.",
5
5
  "license": "MIT",
6
6
  "author": "Aaron Scherer <me@aaronscherer.me>",