@cryptiklemur/lattice 5.4.2 → 5.5.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 (86) hide show
  1. package/README.md +114 -0
  2. package/dist/client/assets/{angular-html-SJYR00xk.js → angular-html-Bmjy4gNc.js} +1 -1
  3. package/dist/client/assets/{angular-ts-D-0qcYO-.js → angular-ts-10l13C4C.js} +1 -1
  4. package/dist/client/assets/{apl-C_yUf_Q8.js → apl--Bn7yH0T.js} +1 -1
  5. package/dist/client/assets/{astro-DWb1sYof.js → astro-C8P9TQkP.js} +1 -1
  6. package/dist/client/assets/{blade-gpPhvMw5.js → blade-DQiHYnIS.js} +1 -1
  7. package/dist/client/assets/{c-Bll-v5tl.js → c-BFny-WnE.js} +1 -1
  8. package/dist/client/assets/{cobol-BtwRKHKx.js → cobol-hGEexMYH.js} +1 -1
  9. package/dist/client/assets/{coffee-lhi7pwZU.js → coffee-j3W4ZALV.js} +1 -1
  10. package/dist/client/assets/{cpp-DhSfzDsk.js → cpp-DquWJNps.js} +1 -1
  11. package/dist/client/assets/{crystal-BDslEuCS.js → crystal-CWL31dIV.js} +1 -1
  12. package/dist/client/assets/{css-wZiH58D5.js → css-Di4GSq8V.js} +1 -1
  13. package/dist/client/assets/{dist-CSLVAWWj.js → dist-CaJdL3Va.js} +2 -2
  14. package/dist/client/assets/{edge-DYebWUkr.js → edge-rK4d050l.js} +1 -1
  15. package/dist/client/assets/{elixir-CdQhteX2.js → elixir-BMFKKu5z.js} +1 -1
  16. package/dist/client/assets/{elm-BHfFlGS-.js → elm-B0QVlW3-.js} +1 -1
  17. package/dist/client/assets/{erb-BUj390yA.js → erb-C9w9QbhL.js} +1 -1
  18. package/dist/client/assets/{git-rebase-D8IOT_TG.js → git-rebase-FaylS7Vl.js} +1 -1
  19. package/dist/client/assets/{glimmer-js-DQa2GaPP.js → glimmer-js-Bi2XnbHi.js} +1 -1
  20. package/dist/client/assets/{glimmer-ts-yWWiDGaM.js → glimmer-ts-DOdxK6E6.js} +1 -1
  21. package/dist/client/assets/{glsl-C_ZnI9md.js → glsl-BiGDIGbZ.js} +1 -1
  22. package/dist/client/assets/{graphql-BaUc-XOO.js → graphql-CZJB8KIP.js} +1 -1
  23. package/dist/client/assets/{hack-Chs6cNkF.js → hack-CNGYBFqH.js} +1 -1
  24. package/dist/client/assets/{haml-D538SVOD.js → haml-pf3Zh5MH.js} +1 -1
  25. package/dist/client/assets/{handlebars-B8nrl3PR.js → handlebars-BsQwsvPC.js} +1 -1
  26. package/dist/client/assets/{html-DsesQty8.js → html-Dj4Hvv69.js} +1 -1
  27. package/dist/client/assets/{html-derivative-Dznd2N7i.js → html-derivative-CE7dQ_mI.js} +1 -1
  28. package/dist/client/assets/{http-DdUXd6Cv.js → http-DjOuMI1u.js} +1 -1
  29. package/dist/client/assets/{hurl-BSNmPifU.js → hurl-BH_MQrQo.js} +1 -1
  30. package/dist/client/assets/{index-DQRuYWBh.js → index-DA8ZBSYW.js} +321 -88
  31. package/dist/client/assets/{index-2jfMHAda.css → index-DcwXjiH0.css} +1 -1
  32. package/dist/client/assets/{java-closdFmr.js → java-CnpykAva.js} +1 -1
  33. package/dist/client/assets/{javascript-C6pRyT7u.js → javascript-BbqQ1sQM.js} +1 -1
  34. package/dist/client/assets/{jinja-29z7zfl-.js → jinja-DddpvWwk.js} +1 -1
  35. package/dist/client/assets/{jison-BlzhS9df.js → jison-Bd3G2n4x.js} +1 -1
  36. package/dist/client/assets/{json-BVYyKt19.js → json-F0eTFQKP.js} +1 -1
  37. package/dist/client/assets/{jsx-78E4xoje.js → jsx-B-wB5nUs.js} +1 -1
  38. package/dist/client/assets/{julia-B6DwXmVv.js → julia-9Lfz4b5s.js} +1 -1
  39. package/dist/client/assets/{just-ht50WZ6C.js → just-BVE1sBJx.js} +1 -1
  40. package/dist/client/assets/{latex-CjZuet49.js → latex-SF3xMSWG.js} +1 -1
  41. package/dist/client/assets/{liquid-Cjfh-yZc.js → liquid-CLkbZ0ZV.js} +1 -1
  42. package/dist/client/assets/{lua-CVSB6AEI.js → lua-DNph6TL8.js} +1 -1
  43. package/dist/client/assets/{marko-BFrF6uWg.js → marko-CGqdEtbF.js} +1 -1
  44. package/dist/client/assets/{mdc-qvNjCqiX.js → mdc-BLpzcq82.js} +1 -1
  45. package/dist/client/assets/{nginx-MsabrNG_.js → nginx-s7ciyZD1.js} +1 -1
  46. package/dist/client/assets/{nim-Cc2JeGZC.js → nim-C4FeZI3R.js} +1 -1
  47. package/dist/client/assets/{perl-Bvo-EvFm.js → perl-GDfD5AIi.js} +1 -1
  48. package/dist/client/assets/{php-C7efK-fE.js → php-CusAuy6y.js} +1 -1
  49. package/dist/client/assets/{pug-BHu66rzG.js → pug-DCKuXO7x.js} +1 -1
  50. package/dist/client/assets/{qml-BLhrl-5q.js → qml-rp1ZHoeo.js} +1 -1
  51. package/dist/client/assets/{r-B2ehj4h6.js → r-CwNbYaVb.js} +1 -1
  52. package/dist/client/assets/{razor-D6r43dAd.js → razor-CinY5yf4.js} +1 -1
  53. package/dist/client/assets/{regexp-hbR6TLtq.js → regexp-CxVI4rKV.js} +1 -1
  54. package/dist/client/assets/{rst-BolhcSc3.js → rst-BrBmBgUr.js} +1 -1
  55. package/dist/client/assets/{ruby-CBN1zxC_.js → ruby-BzNCpRio.js} +1 -1
  56. package/dist/client/assets/{sas-JEvub8hp.js → sas-B4IAap7m.js} +1 -1
  57. package/dist/client/assets/{scss-D4arWi-Q.js → scss-eNAsUEA1.js} +1 -1
  58. package/dist/client/assets/{shellscript-v9q5xlF5.js → shellscript-BwH0aChW.js} +1 -1
  59. package/dist/client/assets/{shellsession-DT_8eUZ3.js → shellsession-B5ibJBbh.js} +1 -1
  60. package/dist/client/assets/{soy-CitjdPtE.js → soy-BrqReQP3.js} +1 -1
  61. package/dist/client/assets/{sql-YlhezUxw.js → sql-KIDgTieS.js} +1 -1
  62. package/dist/client/assets/{stata-50KEEAuW.js → stata-D6yim7Rr.js} +1 -1
  63. package/dist/client/assets/{surrealql-CHxisHFK.js → surrealql-C51iMPEA.js} +1 -1
  64. package/dist/client/assets/{svelte-D9-Nhiy4.js → svelte-DYs5QSVt.js} +1 -1
  65. package/dist/client/assets/{templ-CD-Nb-TG.js → templ-CLDdaEXl.js} +1 -1
  66. package/dist/client/assets/{tex-BJjdCsnl.js → tex-D16HBCoj.js} +1 -1
  67. package/dist/client/assets/{ts-tags-Cij8ie91.js → ts-tags-DTcB2bBd.js} +1 -1
  68. package/dist/client/assets/{tsx-DEOZxt7J.js → tsx-BxGfVt8a.js} +1 -1
  69. package/dist/client/assets/{twig-B_IYmySH.js → twig-BUC08jwe.js} +1 -1
  70. package/dist/client/assets/{typescript-CdNmBQel.js → typescript-CuPGT2MR.js} +1 -1
  71. package/dist/client/assets/{vue-DmoveTFa.js → vue-_ip8-SIk.js} +1 -1
  72. package/dist/client/assets/{vue-html-CYcJvbOP.js → vue-html-D-I9-U-x.js} +1 -1
  73. package/dist/client/assets/{vue-vine-SDsNLzWn.js → vue-vine-7AONB1tt.js} +1 -1
  74. package/dist/client/assets/{xml-BmY74qrp.js → xml-D2ZUDsPU.js} +1 -1
  75. package/dist/client/assets/{xsl-10UVJPoU.js → xsl-GlFKKlLy.js} +1 -1
  76. package/dist/client/assets/{yaml-DrtC5l50.js → yaml-BnuTxWck.js} +1 -1
  77. package/dist/client/index.html +2 -2
  78. package/dist/client/sw.js +1 -1
  79. package/dist/server/daemon.js +30 -20
  80. package/dist/server/features/brainstorm.js +267 -0
  81. package/dist/server/handlers/brainstorm.js +33 -0
  82. package/dist/server/handlers/fs.js +3 -0
  83. package/dist/server/handlers/session.js +13 -5
  84. package/dist/server/project/sdk-bridge.js +2 -22
  85. package/dist/server/project/session.js +92 -11
  86. package/package.json +1 -1
@@ -13,6 +13,9 @@ export function setActiveProject(clientId, projectSlug) {
13
13
  export function clearActiveProject(clientId) {
14
14
  activeProjectByClient.delete(clientId);
15
15
  }
16
+ export function getActiveProjectForClient(clientId) {
17
+ return activeProjectByClient.get(clientId);
18
+ }
16
19
  registerHandler("fs", function (clientId, message) {
17
20
  if (message.type === "fs:list") {
18
21
  var listMsg = message;
@@ -1,7 +1,7 @@
1
1
  import { registerHandler } from "../ws/router.js";
2
- import { sendTo } from "../ws/broadcast.js";
2
+ import { sendTo, broadcast } from "../ws/broadcast.js";
3
3
  import { loadConfig } from "../config.js";
4
- import { createSession, deleteSession, findProjectSlugForSession, getSessionPreview, getSessionTitle, getSessionUsage, listSessions, invalidateSessionCache, invalidateHistoryCache, getSessionHistoryPage, loadSessionHistory, renameSession, getSessionFileSizeBytes, } from "../project/session.js";
4
+ import { createSession, deleteSession, findProjectSlugForSession, getSessionPreview, getSessionTitle, getSessionUsage, listSessions, invalidateSessionCache, invalidateHistoryCache, getSessionHistoryPage, loadSessionHistory, renameSession, getSessionFileSizeBytes, updateSessionInIndex, removeSessionFromIndex, } from "../project/session.js";
5
5
  import { getContextBreakdown } from "../project/context-breakdown.js";
6
6
  import { setActiveSession, getActiveSession } from "./chat.js";
7
7
  import { setActiveProject } from "./fs.js";
@@ -71,7 +71,15 @@ registerHandler("session", function (clientId, message) {
71
71
  if (message.type === "session:create") {
72
72
  var createMsg = message;
73
73
  var session = createSession(createMsg.projectSlug);
74
+ updateSessionInIndex(createMsg.projectSlug, session);
74
75
  sendTo(clientId, { type: "session:created", session });
76
+ broadcast({
77
+ type: "session:list",
78
+ projectSlug: createMsg.projectSlug,
79
+ sessions: [session],
80
+ totalCount: undefined,
81
+ offset: 0,
82
+ });
75
83
  return;
76
84
  }
77
85
  if (message.type === "session:activate") {
@@ -160,7 +168,7 @@ registerHandler("session", function (clientId, message) {
160
168
  void renameSession(projectSlug, renameMsg.sessionId, renameMsg.title).then(function () {
161
169
  invalidateSessionCache(projectSlug);
162
170
  void listSessions(projectSlug, { limit: 40 }).then(function (result) {
163
- sendTo(clientId, {
171
+ broadcast({
164
172
  type: "session:list",
165
173
  projectSlug,
166
174
  sessions: result.sessions,
@@ -179,9 +187,9 @@ registerHandler("session", function (clientId, message) {
179
187
  return;
180
188
  }
181
189
  void deleteSession(deleteProjectSlug, deleteMsg.sessionId).then(function () {
182
- invalidateSessionCache(deleteProjectSlug);
190
+ removeSessionFromIndex(deleteProjectSlug, deleteMsg.sessionId);
183
191
  void listSessions(deleteProjectSlug, { limit: 40 }).then(function (result) {
184
- sendTo(clientId, {
192
+ broadcast({
185
193
  type: "session:list",
186
194
  projectSlug: deleteProjectSlug,
187
195
  sessions: result.sessions,
@@ -671,7 +671,6 @@ export function startChatStream(options) {
671
671
  persistStreamState();
672
672
  broadcast({ type: "session:busy", sessionId, busy: true }, clientId);
673
673
  void (async function () {
674
- var retried = false;
675
674
  try {
676
675
  await stream.initializationResult();
677
676
  }
@@ -689,27 +688,8 @@ export function startChatStream(options) {
689
688
  if (errMsg.includes("aborted") || errMsg.includes("AbortError")) {
690
689
  log.chat("Session %s stream aborted", sessionId);
691
690
  }
692
- else if (errMsg.includes("Sent before connected") && !retried) {
693
- retried = true;
694
- log.chat("Session %s SDK WebSocket race condition, retrying after delay...", sessionId);
695
- await new Promise(function (r) { setTimeout(r, 500); });
696
- try {
697
- var retryMq = createMessageQueue();
698
- var retryStream = query({ prompt: retryMq, options: queryOptions });
699
- retryMq.push(firstMsg);
700
- sessionStream.queryInstance = retryStream;
701
- sessionStream.messageQueue = retryMq;
702
- for await (var retryMsg of retryStream) {
703
- processMessage(sessionStream, retryMsg);
704
- }
705
- }
706
- catch (retryErr) {
707
- var retryErrMsg = retryErr instanceof Error ? retryErr.message : String(retryErr);
708
- if (!retryErrMsg.includes("aborted") && !retryErrMsg.includes("AbortError")) {
709
- console.error("[lattice] SDK stream retry error: " + retryErrMsg);
710
- sendTo(sessionStream.clientId, { type: "chat:error", message: retryErrMsg });
711
- }
712
- }
691
+ else if (errMsg.includes("Sent before connected")) {
692
+ log.chat("Session %s SDK WebSocket race condition: %s", sessionId, errMsg);
713
693
  }
714
694
  else {
715
695
  console.error("[lattice] SDK stream error: " + errMsg);
@@ -1,10 +1,10 @@
1
1
  import { listSessions as sdkListSessions, getSessionInfo, getSessionMessages, renameSession as sdkRenameSession, } from "@anthropic-ai/claude-agent-sdk";
2
- import { existsSync, unlinkSync, readFileSync, statSync } from "node:fs";
2
+ import { existsSync, unlinkSync, readFileSync, statSync, writeFileSync } from "node:fs";
3
3
  import * as fsPromises from "node:fs/promises";
4
4
  import { join } from "node:path";
5
5
  import { randomUUID } from "node:crypto";
6
6
  import { homedir } from "node:os";
7
- import { loadConfig } from "../config.js";
7
+ import { loadConfig, getLatticeHome } from "../config.js";
8
8
  import { log } from "../logger.js";
9
9
  function getProjectPath(projectSlug) {
10
10
  var config = loadConfig();
@@ -428,7 +428,81 @@ export async function getSessionPreview(projectSlug, sessionId) {
428
428
  }
429
429
  }
430
430
  var sessionListCache = new Map();
431
- var SESSION_CACHE_TTL = 5000;
431
+ var SESSION_CACHE_TTL = 60000;
432
+ var RECONCILE_INTERVAL = 5 * 60 * 1000;
433
+ var lastReconcile = new Map();
434
+ function getIndexPath() {
435
+ return join(getLatticeHome(), "session-index.json");
436
+ }
437
+ function loadSessionIndex() {
438
+ var indexPath = getIndexPath();
439
+ if (!existsSync(indexPath))
440
+ return {};
441
+ try {
442
+ return JSON.parse(readFileSync(indexPath, "utf-8"));
443
+ }
444
+ catch {
445
+ return {};
446
+ }
447
+ }
448
+ function saveSessionIndex(index) {
449
+ try {
450
+ writeFileSync(getIndexPath(), JSON.stringify(index), "utf-8");
451
+ }
452
+ catch (err) {
453
+ log.session("Failed to save session index: %O", err);
454
+ }
455
+ }
456
+ export function updateSessionInIndex(projectSlug, session) {
457
+ var index = loadSessionIndex();
458
+ var sessions = index[projectSlug] || [];
459
+ var existing = -1;
460
+ for (var i = 0; i < sessions.length; i++) {
461
+ if (sessions[i].id === session.id) {
462
+ existing = i;
463
+ break;
464
+ }
465
+ }
466
+ if (existing >= 0) {
467
+ sessions[existing] = session;
468
+ }
469
+ else {
470
+ sessions.push(session);
471
+ }
472
+ sessions.sort(function (a, b) { return b.updatedAt - a.updatedAt; });
473
+ index[projectSlug] = sessions;
474
+ saveSessionIndex(index);
475
+ sessionListCache.set(projectSlug, { sessions, time: Date.now() });
476
+ }
477
+ export function removeSessionFromIndex(projectSlug, sessionId) {
478
+ var index = loadSessionIndex();
479
+ var sessions = index[projectSlug] || [];
480
+ index[projectSlug] = sessions.filter(function (s) { return s.id !== sessionId; });
481
+ saveSessionIndex(index);
482
+ sessionListCache.set(projectSlug, { sessions: index[projectSlug], time: Date.now() });
483
+ }
484
+ async function reconcileWithSDK(projectSlug) {
485
+ var projectPath = getProjectPath(projectSlug);
486
+ if (!projectPath)
487
+ return [];
488
+ var sdkT0 = Date.now();
489
+ var sdkSessions = await sdkListSessions({ dir: projectPath });
490
+ log.session("sdkListSessions for %s: %dms (%d sessions)", projectSlug, Date.now() - sdkT0, sdkSessions.length);
491
+ var summaries = sdkSessions.map(function (s) { return mapSDKSession(s, projectSlug); });
492
+ summaries.sort(function (a, b) { return b.updatedAt - a.updatedAt; });
493
+ var index = loadSessionIndex();
494
+ index[projectSlug] = summaries;
495
+ saveSessionIndex(index);
496
+ sessionListCache.set(projectSlug, { sessions: summaries, time: Date.now() });
497
+ lastReconcile.set(projectSlug, Date.now());
498
+ return summaries;
499
+ }
500
+ function needsReconcile(projectSlug) {
501
+ var last = lastReconcile.get(projectSlug);
502
+ if (!last)
503
+ return true;
504
+ return Date.now() - last > RECONCILE_INTERVAL;
505
+ }
432
506
  export async function listSessions(projectSlug, options) {
433
507
  var projectPath = getProjectPath(projectSlug);
434
508
  if (!projectPath) {
@@ -441,15 +515,22 @@ export async function listSessions(projectSlug, options) {
441
515
  var sliced = limit > 0 ? cached.sessions.slice(offset, offset + limit) : cached.sessions;
442
516
  return { sessions: sliced, totalCount: cached.sessions.length };
443
517
  }
518
+ var index = loadSessionIndex();
519
+ var indexed = index[projectSlug];
520
+ if (indexed && indexed.length > 0) {
521
+ sessionListCache.set(projectSlug, { sessions: indexed, time: Date.now() });
522
+ if (needsReconcile(projectSlug)) {
523
+ reconcileWithSDK(projectSlug).catch(function (err) {
524
+ log.session("Background reconcile failed: %O", err);
525
+ });
526
+ }
527
+ var offset3 = options?.offset ?? 0;
528
+ var limit3 = options?.limit ?? 0;
529
+ var sliced3 = limit3 > 0 ? indexed.slice(offset3, offset3 + limit3) : indexed;
530
+ return { sessions: sliced3, totalCount: indexed.length };
531
+ }
444
532
  try {
445
- var sdkT0 = Date.now();
446
- var sdkSessions = await sdkListSessions({ dir: projectPath });
447
- log.session("sdkListSessions for %s: %dms (%d sessions)", projectSlug, Date.now() - sdkT0, sdkSessions.length);
448
- var summaries = sdkSessions.map(function (s) {
449
- return mapSDKSession(s, projectSlug);
450
- });
451
- summaries.sort(function (a, b) { return b.updatedAt - a.updatedAt; });
452
- sessionListCache.set(projectSlug, { sessions: summaries, time: Date.now() });
533
+ var summaries = await reconcileWithSDK(projectSlug);
453
534
  var offset2 = options?.offset ?? 0;
454
535
  var limit2 = options?.limit ?? 0;
455
536
  var sliced2 = limit2 > 0 ? summaries.slice(offset2, offset2 + limit2) : summaries;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cryptiklemur/lattice",
3
- "version": "5.4.2",
3
+ "version": "5.5.0",
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>",