@cryptiklemur/lattice 5.12.8 → 5.13.1
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/server/daemon.js +2 -1
- package/dist/server/logger.js +47 -18
- package/dist/server/project/sdk-bridge.js +35 -5
- package/package.json +1 -1
package/dist/server/daemon.js
CHANGED
|
@@ -15,7 +15,7 @@ import { startDiscovery } from "./mesh/discovery.js";
|
|
|
15
15
|
import { startMeshConnections, onPeerConnected, onPeerDisconnected, onPeerMessage, getAllRemoteProjects } from "./mesh/connector.js";
|
|
16
16
|
import { handleProxyRequest, handleProxyResponse } from "./mesh/proxy.js";
|
|
17
17
|
import { verifyPassphrase, generateSessionToken, addSession, isValidSession } from "./auth/passphrase.js";
|
|
18
|
-
import { log } from "./logger.js";
|
|
18
|
+
import { log, initFileLogger } from "./logger.js";
|
|
19
19
|
import { detectIdeProjectName } from "./handlers/settings.js";
|
|
20
20
|
import "./handlers/session.js";
|
|
21
21
|
import "./handlers/chat.js";
|
|
@@ -326,6 +326,7 @@ export async function startDaemon(portOverride, tlsOverride) {
|
|
|
326
326
|
if (tlsOverride !== null && tlsOverride !== undefined) {
|
|
327
327
|
config.tls = tlsOverride;
|
|
328
328
|
}
|
|
329
|
+
initFileLogger(getLatticeHome());
|
|
329
330
|
const identity = loadOrCreateIdentity();
|
|
330
331
|
log.server("Node: %s (%s)", config.name, identity.id);
|
|
331
332
|
log.server("Home: %s", getLatticeHome());
|
package/dist/server/logger.js
CHANGED
|
@@ -1,21 +1,50 @@
|
|
|
1
1
|
import createDebug from "debug";
|
|
2
|
+
import { createWriteStream, existsSync, mkdirSync } from "node:fs";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { format } from "node:util";
|
|
5
|
+
let fileStream = null;
|
|
6
|
+
export function initFileLogger(latticeHome) {
|
|
7
|
+
const logsDir = join(latticeHome, "logs");
|
|
8
|
+
if (!existsSync(logsDir)) {
|
|
9
|
+
mkdirSync(logsDir, { recursive: true });
|
|
10
|
+
}
|
|
11
|
+
const logPath = join(logsDir, "debug.log");
|
|
12
|
+
fileStream = createWriteStream(logPath, { flags: "a" });
|
|
13
|
+
fileStream.on("error", function () {
|
|
14
|
+
fileStream = null;
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
function writeToFile(namespace, fmt, ...args) {
|
|
18
|
+
if (!fileStream)
|
|
19
|
+
return;
|
|
20
|
+
const timestamp = new Date().toISOString();
|
|
21
|
+
const message = format(fmt, ...args);
|
|
22
|
+
fileStream.write(timestamp + " " + namespace + " " + message + "\n");
|
|
23
|
+
}
|
|
24
|
+
function createLogger(namespace) {
|
|
25
|
+
const debugInstance = createDebug(namespace);
|
|
26
|
+
return function (fmt, ...args) {
|
|
27
|
+
writeToFile(namespace, fmt, ...args);
|
|
28
|
+
debugInstance(fmt, ...args);
|
|
29
|
+
};
|
|
30
|
+
}
|
|
2
31
|
export const log = {
|
|
3
|
-
server:
|
|
4
|
-
ws:
|
|
5
|
-
chat:
|
|
6
|
-
session:
|
|
7
|
-
mesh:
|
|
8
|
-
meshConnect:
|
|
9
|
-
meshHello:
|
|
10
|
-
meshProxy:
|
|
11
|
-
router:
|
|
12
|
-
broadcast:
|
|
13
|
-
auth:
|
|
14
|
-
fs:
|
|
15
|
-
analytics:
|
|
16
|
-
plugins:
|
|
17
|
-
update:
|
|
18
|
-
terminal:
|
|
19
|
-
settings:
|
|
20
|
-
superpowers:
|
|
32
|
+
server: createLogger("lattice:server"),
|
|
33
|
+
ws: createLogger("lattice:ws"),
|
|
34
|
+
chat: createLogger("lattice:chat"),
|
|
35
|
+
session: createLogger("lattice:session"),
|
|
36
|
+
mesh: createLogger("lattice:mesh"),
|
|
37
|
+
meshConnect: createLogger("lattice:mesh:connect"),
|
|
38
|
+
meshHello: createLogger("lattice:mesh:hello"),
|
|
39
|
+
meshProxy: createLogger("lattice:mesh:proxy"),
|
|
40
|
+
router: createLogger("lattice:router"),
|
|
41
|
+
broadcast: createLogger("lattice:broadcast"),
|
|
42
|
+
auth: createLogger("lattice:auth"),
|
|
43
|
+
fs: createLogger("lattice:fs"),
|
|
44
|
+
analytics: createLogger("lattice:analytics"),
|
|
45
|
+
plugins: createLogger("lattice:plugins"),
|
|
46
|
+
update: createLogger("lattice:update"),
|
|
47
|
+
terminal: createLogger("lattice:terminal"),
|
|
48
|
+
settings: createLogger("lattice:settings"),
|
|
49
|
+
superpowers: createLogger("lattice:superpowers"),
|
|
21
50
|
};
|
|
@@ -376,9 +376,11 @@ function buildSDKUserMessage(prompt, attachments, sessionId) {
|
|
|
376
376
|
}
|
|
377
377
|
function pushToExistingStream(session, options) {
|
|
378
378
|
const { text, attachments, clientId, sessionId, model } = options;
|
|
379
|
+
log.chat("Session %s pushing to existing stream (ended=%s)", sessionId, String(session.ended));
|
|
379
380
|
session.clientId = clientId;
|
|
380
381
|
session.turnStartTime = Date.now();
|
|
381
382
|
session.turnDoneSent = false;
|
|
383
|
+
session.sawNewTurnContent = false;
|
|
382
384
|
session.activeToolBlocks = {};
|
|
383
385
|
const prompt = resolvePromptText(text);
|
|
384
386
|
const userMsg = buildSDKUserMessage(prompt, attachments, sessionId);
|
|
@@ -398,6 +400,7 @@ function pushToExistingStream(session, options) {
|
|
|
398
400
|
}
|
|
399
401
|
export function startChatStream(options) {
|
|
400
402
|
const { projectSlug, sessionId, text, attachments, clientId, cwd, env, model, effort, isNewSession } = options;
|
|
403
|
+
log.chat("startChatStream called: session=%s project=%s client=%s", sessionId, projectSlug, clientId);
|
|
401
404
|
const existing = sessionStreams.get(sessionId);
|
|
402
405
|
if (existing && !existing.ended) {
|
|
403
406
|
pushToExistingStream(existing, options);
|
|
@@ -470,9 +473,7 @@ export function startChatStream(options) {
|
|
|
470
473
|
additionalDirectories: savedAdditionalDirs.length > 0 ? savedAdditionalDirs : undefined,
|
|
471
474
|
mcpServers: Object.keys(mcpServers).length > 0 ? mcpServers : undefined,
|
|
472
475
|
stderr: function (data) {
|
|
473
|
-
|
|
474
|
-
log.chat("SDK stderr: %s", data.trim());
|
|
475
|
-
}
|
|
476
|
+
log.chat("Session %s SDK stderr: %s", sessionId, data.trim());
|
|
476
477
|
},
|
|
477
478
|
};
|
|
478
479
|
queryOptions.toolConfig = {
|
|
@@ -675,6 +676,7 @@ export function startChatStream(options) {
|
|
|
675
676
|
ended: false,
|
|
676
677
|
accumulatedText: "",
|
|
677
678
|
specId: options.specId,
|
|
679
|
+
sawNewTurnContent: false,
|
|
678
680
|
analyzer: new ContextAnalyzer(function (msg) {
|
|
679
681
|
const ss = sessionStreams.get(sessionId);
|
|
680
682
|
if (ss)
|
|
@@ -685,25 +687,48 @@ export function startChatStream(options) {
|
|
|
685
687
|
persistStreamState();
|
|
686
688
|
broadcast({ type: "session:busy", sessionId, busy: true }, clientId);
|
|
687
689
|
void (async function () {
|
|
690
|
+
log.chat("Session %s stream starting (resume=%s, model=%s)", sessionId, String(shouldResume), model || "default");
|
|
688
691
|
try {
|
|
689
692
|
await stream.initializationResult();
|
|
693
|
+
log.chat("Session %s SDK initialized", sessionId);
|
|
690
694
|
}
|
|
691
695
|
catch (initErr) {
|
|
692
|
-
log.chat("Session %s SDK initialization
|
|
696
|
+
log.chat("Session %s SDK initialization FAILED: %O", sessionId, initErr);
|
|
697
|
+
}
|
|
698
|
+
if (!shouldResume) {
|
|
699
|
+
log.chat("Session %s pushing first message to queue", sessionId);
|
|
700
|
+
mq.push(firstMsg);
|
|
693
701
|
}
|
|
694
|
-
mq.push(firstMsg);
|
|
695
702
|
try {
|
|
703
|
+
log.chat("Session %s entering stream loop", sessionId);
|
|
704
|
+
let msgCount = 0;
|
|
705
|
+
let replayDone = !shouldResume;
|
|
696
706
|
for await (const msg of stream) {
|
|
707
|
+
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;
|
|
718
|
+
}
|
|
697
719
|
processMessage(sessionStream, msg);
|
|
698
720
|
}
|
|
721
|
+
log.chat("Session %s stream ended normally after %d messages", sessionId, msgCount);
|
|
699
722
|
}
|
|
700
723
|
catch (err) {
|
|
701
724
|
const errMsg = err instanceof Error ? err.message : String(err);
|
|
725
|
+
log.chat("Session %s stream error: %s", sessionId, errMsg);
|
|
702
726
|
if (errMsg.includes("aborted") || errMsg.includes("AbortError")) {
|
|
703
727
|
log.chat("Session %s stream aborted", sessionId);
|
|
704
728
|
}
|
|
705
729
|
else if (errMsg.includes("Sent before connected")) {
|
|
706
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." });
|
|
707
732
|
}
|
|
708
733
|
else {
|
|
709
734
|
console.error("[lattice] SDK stream error: " + errMsg);
|
|
@@ -811,6 +836,7 @@ function processMessage(ss, msg) {
|
|
|
811
836
|
const partial = msg;
|
|
812
837
|
const evt = partial.event;
|
|
813
838
|
if (evt.type === "content_block_start") {
|
|
839
|
+
ss.sawNewTurnContent = true;
|
|
814
840
|
const block = evt.content_block;
|
|
815
841
|
const idx = evt.index;
|
|
816
842
|
if (block.type === "tool_use" && block.id && block.name) {
|
|
@@ -941,6 +967,10 @@ function processMessage(ss, msg) {
|
|
|
941
967
|
return;
|
|
942
968
|
}
|
|
943
969
|
if (msg.type === "result") {
|
|
970
|
+
if (!ss.sawNewTurnContent) {
|
|
971
|
+
log.chat("Session %s ignoring replayed result (no new turn content seen)", sessionId);
|
|
972
|
+
return;
|
|
973
|
+
}
|
|
944
974
|
const resultMsg = msg;
|
|
945
975
|
const dur = Date.now() - ss.turnStartTime;
|
|
946
976
|
const cost = resultMsg.total_cost_usd || 0;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cryptiklemur/lattice",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.13.1",
|
|
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>",
|