@cryptiklemur/lattice 1.43.5 → 1.43.6
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.
|
@@ -23,7 +23,7 @@ import { useBookmarks } from "../../hooks/useBookmarks";
|
|
|
23
23
|
import { formatSessionTitle } from "../../utils/formatSessionTitle";
|
|
24
24
|
|
|
25
25
|
export function ChatView({ sessionId: tabSessionId, projectSlug: tabProjectSlug }: { sessionId?: string; projectSlug?: string } = {}) {
|
|
26
|
-
var { messages, isProcessing, sendMessage, activeSessionId, activeSessionTitle, currentStatus, contextUsage, contextBreakdown, lastResponseCost, lastResponseDuration, historyLoading, wasInterrupted, promptSuggestion, failedInput, clearFailedInput, messageQueue, enqueueMessage, removeQueuedMessage, updateQueuedMessage, isBusy, busyOwner, isPlanMode, pendingPrefill, activateSession, budgetStatus, budgetExceeded, sendBudgetOverride, dismissBudgetExceeded } = useSession();
|
|
26
|
+
var { messages, isProcessing, sendMessage, activeSessionId, activeSessionTitle, currentStatus, contextUsage, contextBreakdown, lastResponseCost, lastResponseDuration, historyLoading, historyHasMore, loadMoreHistory, wasInterrupted, promptSuggestion, failedInput, clearFailedInput, messageQueue, enqueueMessage, removeQueuedMessage, updateQueuedMessage, isBusy, busyOwner, isPlanMode, pendingPrefill, activateSession, budgetStatus, budgetExceeded, sendBudgetOverride, dismissBudgetExceeded } = useSession();
|
|
27
27
|
var { activeProject } = useProjects();
|
|
28
28
|
var { toggleDrawer } = useSidebar();
|
|
29
29
|
|
|
@@ -735,6 +735,16 @@ export function ChatView({ sessionId: tabSessionId, projectSlug: tabProjectSlug
|
|
|
735
735
|
aria-relevant="additions"
|
|
736
736
|
style={{ WebkitOverflowScrolling: "touch", touchAction: "pan-y" }}
|
|
737
737
|
>
|
|
738
|
+
{historyHasMore && messages.length > 0 && (
|
|
739
|
+
<div className="flex justify-center py-3">
|
|
740
|
+
<button
|
|
741
|
+
onClick={loadMoreHistory}
|
|
742
|
+
className="text-[11px] text-base-content/30 hover:text-base-content/50 font-mono transition-colors"
|
|
743
|
+
>
|
|
744
|
+
Load older messages
|
|
745
|
+
</button>
|
|
746
|
+
</div>
|
|
747
|
+
)}
|
|
738
748
|
{messages.length === 0 && historyLoading ? (
|
|
739
749
|
<div className="flex items-center justify-center h-full">
|
|
740
750
|
<div className="flex flex-col items-center gap-3">
|
|
@@ -78,6 +78,8 @@ export interface UseSessionReturn extends SessionState {
|
|
|
78
78
|
clearMessageQueue: () => void;
|
|
79
79
|
sendBudgetOverride: () => void;
|
|
80
80
|
dismissBudgetExceeded: () => void;
|
|
81
|
+
loadMoreHistory: () => void;
|
|
82
|
+
historyHasMore: boolean;
|
|
81
83
|
}
|
|
82
84
|
|
|
83
85
|
export function useSession(): UseSessionReturn {
|
|
@@ -276,6 +278,19 @@ export function useSession(): UseSessionReturn {
|
|
|
276
278
|
updatePermissionStatus(m.requestId, m.status);
|
|
277
279
|
}
|
|
278
280
|
|
|
281
|
+
function handleHistoryPage(msg: ServerMessage) {
|
|
282
|
+
var m = msg as { type: string; sessionId: string; messages: HistoryMessage[]; hasMore: boolean };
|
|
283
|
+
var state = getSessionStore().state;
|
|
284
|
+
if (m.sessionId !== state.activeSessionId) return;
|
|
285
|
+
getSessionStore().setState(function (s) {
|
|
286
|
+
return {
|
|
287
|
+
...s,
|
|
288
|
+
messages: mergeToolResults(m.messages).concat(s.messages),
|
|
289
|
+
historyHasMore: m.hasMore,
|
|
290
|
+
};
|
|
291
|
+
});
|
|
292
|
+
}
|
|
293
|
+
|
|
279
294
|
function handleHistory(msg: ServerMessage) {
|
|
280
295
|
var m = msg as SessionHistoryMessage;
|
|
281
296
|
setCurrentAssistantUuid(null);
|
|
@@ -303,6 +318,8 @@ export function useSession(): UseSessionReturn {
|
|
|
303
318
|
lastResponseDuration: null,
|
|
304
319
|
lastReadIndex: null,
|
|
305
320
|
historyLoading: false,
|
|
321
|
+
historyHasMore: m.hasMore || false,
|
|
322
|
+
historyTotalMessages: m.totalMessages || m.messages.length,
|
|
306
323
|
wasInterrupted: m.interrupted || false,
|
|
307
324
|
isBusy: m.busy || false,
|
|
308
325
|
busyOwner: m.busyOwner ?? null,
|
|
@@ -390,6 +407,7 @@ export function useSession(): UseSessionReturn {
|
|
|
390
407
|
subscribe("chat:context_usage", handleContextUsage);
|
|
391
408
|
subscribe("chat:context_breakdown", handleContextBreakdown);
|
|
392
409
|
subscribe("session:history", handleHistory);
|
|
410
|
+
subscribe("session:history_page_result", handleHistoryPage);
|
|
393
411
|
subscribe("chat:prompt_suggestion", handlePromptSuggestion);
|
|
394
412
|
subscribe("session:busy", handleSessionBusy);
|
|
395
413
|
subscribe("chat:prompt_request", handlePromptRequest);
|
|
@@ -413,6 +431,7 @@ export function useSession(): UseSessionReturn {
|
|
|
413
431
|
unsubscribe("chat:context_usage", handleContextUsage);
|
|
414
432
|
unsubscribe("chat:context_breakdown", handleContextBreakdown);
|
|
415
433
|
unsubscribe("session:history", handleHistory);
|
|
434
|
+
unsubscribe("session:history_page_result", handleHistoryPage);
|
|
416
435
|
unsubscribe("chat:prompt_suggestion", handlePromptSuggestion);
|
|
417
436
|
unsubscribe("session:busy", handleSessionBusy);
|
|
418
437
|
unsubscribe("chat:prompt_request", handlePromptRequest);
|
|
@@ -441,6 +460,15 @@ export function useSession(): UseSessionReturn {
|
|
|
441
460
|
lastResponseDuration: state.lastResponseDuration,
|
|
442
461
|
lastReadIndex: state.lastReadIndex,
|
|
443
462
|
historyLoading: state.historyLoading,
|
|
463
|
+
historyHasMore: state.historyHasMore,
|
|
464
|
+
historyTotalMessages: state.historyTotalMessages,
|
|
465
|
+
loadMoreHistory: function () {
|
|
466
|
+
if (!state.historyHasMore || !state.activeSessionId) return;
|
|
467
|
+
var totalMessages = state.historyTotalMessages;
|
|
468
|
+
var loadedCount = state.messages.length;
|
|
469
|
+
var beforeIndex = totalMessages - loadedCount;
|
|
470
|
+
sendRef.current({ type: "session:history_page", sessionId: state.activeSessionId, before: beforeIndex, limit: 100 } as any);
|
|
471
|
+
},
|
|
444
472
|
wasInterrupted: state.wasInterrupted,
|
|
445
473
|
promptSuggestion: state.promptSuggestion,
|
|
446
474
|
failedInput: state.failedInput,
|
|
@@ -42,6 +42,8 @@ export interface SessionState {
|
|
|
42
42
|
lastResponseDuration: number | null;
|
|
43
43
|
lastReadIndex: number | null;
|
|
44
44
|
historyLoading: boolean;
|
|
45
|
+
historyHasMore: boolean;
|
|
46
|
+
historyTotalMessages: number;
|
|
45
47
|
wasInterrupted: boolean;
|
|
46
48
|
promptSuggestion: string | null;
|
|
47
49
|
failedInput: string | null;
|
|
@@ -68,6 +70,8 @@ var sessionStore = new Store<SessionState>({
|
|
|
68
70
|
lastResponseDuration: null,
|
|
69
71
|
lastReadIndex: null,
|
|
70
72
|
historyLoading: false,
|
|
73
|
+
historyHasMore: false,
|
|
74
|
+
historyTotalMessages: 0,
|
|
71
75
|
wasInterrupted: false,
|
|
72
76
|
promptSuggestion: null,
|
|
73
77
|
failedInput: null,
|
|
@@ -293,6 +297,8 @@ export function clearSession(): void {
|
|
|
293
297
|
lastResponseDuration: null,
|
|
294
298
|
lastReadIndex: null,
|
|
295
299
|
historyLoading: false,
|
|
300
|
+
historyHasMore: false,
|
|
301
|
+
historyTotalMessages: 0,
|
|
296
302
|
wasInterrupted: false,
|
|
297
303
|
promptSuggestion: null,
|
|
298
304
|
failedInput: null,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cryptiklemur/lattice",
|
|
3
|
-
"version": "1.43.
|
|
3
|
+
"version": "1.43.6",
|
|
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>",
|
|
@@ -20,6 +20,7 @@ import {
|
|
|
20
20
|
getSessionUsage,
|
|
21
21
|
listSessions,
|
|
22
22
|
invalidateSessionCache,
|
|
23
|
+
getSessionHistoryPage,
|
|
23
24
|
loadSessionHistory,
|
|
24
25
|
renameSession,
|
|
25
26
|
} from "../project/session";
|
|
@@ -79,6 +80,18 @@ registerHandler("session", function (clientId: string, message: ClientMessage) {
|
|
|
79
80
|
return;
|
|
80
81
|
}
|
|
81
82
|
|
|
83
|
+
if (message.type === "session:history_page") {
|
|
84
|
+
var pageMsg = message as { type: "session:history_page"; sessionId: string; before: number; limit: number };
|
|
85
|
+
var page = getSessionHistoryPage(pageMsg.sessionId, pageMsg.before, pageMsg.limit);
|
|
86
|
+
sendTo(clientId, {
|
|
87
|
+
type: "session:history_page_result",
|
|
88
|
+
sessionId: pageMsg.sessionId,
|
|
89
|
+
messages: page.messages,
|
|
90
|
+
hasMore: page.hasMore,
|
|
91
|
+
});
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
|
|
82
95
|
if (message.type === "session:create") {
|
|
83
96
|
var createMsg = message as SessionCreateMessage;
|
|
84
97
|
var session = createSession(createMsg.projectSlug);
|
|
@@ -116,15 +129,18 @@ registerHandler("session", function (clientId: string, message: ClientMessage) {
|
|
|
116
129
|
}
|
|
117
130
|
var busy = isSessionBusy(activateMsg.sessionId);
|
|
118
131
|
var busyOwner = busy ? getBusyOwner(activateMsg.sessionId) : undefined;
|
|
132
|
+
var historyResult = results[0] || { messages: [], totalMessages: 0, hasMore: false };
|
|
119
133
|
sendTo(clientId, {
|
|
120
134
|
type: "session:history",
|
|
121
135
|
projectSlug: activateMsg.projectSlug,
|
|
122
136
|
sessionId: activateMsg.sessionId,
|
|
123
|
-
messages:
|
|
137
|
+
messages: historyResult.messages,
|
|
124
138
|
title: results[1],
|
|
125
139
|
interrupted: interrupted || undefined,
|
|
126
140
|
busy: busy || undefined,
|
|
127
141
|
busyOwner: busyOwner,
|
|
142
|
+
totalMessages: historyResult.totalMessages,
|
|
143
|
+
hasMore: historyResult.hasMore,
|
|
128
144
|
});
|
|
129
145
|
} catch (err) {
|
|
130
146
|
log.session("Error sending session history: %O", err);
|
|
@@ -457,19 +457,55 @@ export async function getSessionTitle(projectSlug: string, sessionId: string): P
|
|
|
457
457
|
return "Untitled";
|
|
458
458
|
}
|
|
459
459
|
|
|
460
|
-
|
|
460
|
+
var historyCache = new Map<string, { messages: HistoryMessage[]; time: number }>();
|
|
461
|
+
var HISTORY_CACHE_TTL = 30000;
|
|
462
|
+
var INITIAL_MESSAGE_COUNT = 100;
|
|
463
|
+
|
|
464
|
+
export async function loadSessionHistory(projectSlug: string, sessionId: string): Promise<{ messages: HistoryMessage[]; totalMessages: number; hasMore: boolean }> {
|
|
461
465
|
var projectPath = getProjectPath(projectSlug);
|
|
462
466
|
var options = projectPath ? { dir: projectPath } : undefined;
|
|
463
467
|
|
|
464
468
|
try {
|
|
465
|
-
var
|
|
466
|
-
|
|
469
|
+
var t0 = Date.now();
|
|
470
|
+
var cached = historyCache.get(sessionId);
|
|
471
|
+
var allMessages: HistoryMessage[];
|
|
472
|
+
|
|
473
|
+
if (cached && Date.now() - cached.time < HISTORY_CACHE_TTL) {
|
|
474
|
+
allMessages = cached.messages;
|
|
475
|
+
} else {
|
|
476
|
+
var rawMessages = await getSessionMessages(sessionId, options);
|
|
477
|
+
allMessages = convertSessionMessages(rawMessages);
|
|
478
|
+
historyCache.set(sessionId, { messages: allMessages, time: Date.now() });
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
log.session("loadSessionHistory %s: %dms, %d total messages", sessionId.slice(0, 8), Date.now() - t0, allMessages.length);
|
|
482
|
+
|
|
483
|
+
var tail = allMessages.length > INITIAL_MESSAGE_COUNT
|
|
484
|
+
? allMessages.slice(allMessages.length - INITIAL_MESSAGE_COUNT)
|
|
485
|
+
: allMessages;
|
|
486
|
+
|
|
487
|
+
return {
|
|
488
|
+
messages: tail,
|
|
489
|
+
totalMessages: allMessages.length,
|
|
490
|
+
hasMore: allMessages.length > INITIAL_MESSAGE_COUNT,
|
|
491
|
+
};
|
|
467
492
|
} catch (err) {
|
|
468
493
|
log.session("Failed to load session history: %O", err);
|
|
469
|
-
return [];
|
|
494
|
+
return { messages: [], totalMessages: 0, hasMore: false };
|
|
470
495
|
}
|
|
471
496
|
}
|
|
472
497
|
|
|
498
|
+
export function getSessionHistoryPage(sessionId: string, beforeIndex: number, limit: number): { messages: HistoryMessage[]; hasMore: boolean } {
|
|
499
|
+
var cached = historyCache.get(sessionId);
|
|
500
|
+
if (!cached) return { messages: [], hasMore: false };
|
|
501
|
+
|
|
502
|
+
var endIdx = Math.max(0, beforeIndex);
|
|
503
|
+
var startIdx = Math.max(0, endIdx - limit);
|
|
504
|
+
var page = cached.messages.slice(startIdx, endIdx);
|
|
505
|
+
|
|
506
|
+
return { messages: page, hasMore: startIdx > 0 };
|
|
507
|
+
}
|
|
508
|
+
|
|
473
509
|
export function createSession(projectSlug: string): SessionSummary {
|
|
474
510
|
var sessionId = randomUUID();
|
|
475
511
|
var now = Date.now();
|
package/shared/src/messages.ts
CHANGED
|
@@ -50,6 +50,20 @@ export interface SessionListRequestMessage {
|
|
|
50
50
|
limit?: number;
|
|
51
51
|
}
|
|
52
52
|
|
|
53
|
+
export interface SessionHistoryPageMessage {
|
|
54
|
+
type: "session:history_page";
|
|
55
|
+
sessionId: string;
|
|
56
|
+
before: number;
|
|
57
|
+
limit: number;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export interface SessionHistoryPageResultMessage {
|
|
61
|
+
type: "session:history_page_result";
|
|
62
|
+
sessionId: string;
|
|
63
|
+
messages: HistoryMessage[];
|
|
64
|
+
hasMore: boolean;
|
|
65
|
+
}
|
|
66
|
+
|
|
53
67
|
export interface SessionPreviewRequestMessage {
|
|
54
68
|
type: "session:preview_request";
|
|
55
69
|
projectSlug: string;
|
|
@@ -578,6 +592,7 @@ export type ClientMessage =
|
|
|
578
592
|
| EditorEnsureProjectMessage
|
|
579
593
|
| ChatPromptResponseMessage
|
|
580
594
|
| AnalyticsRequestMessage
|
|
595
|
+
| SessionHistoryPageMessage
|
|
581
596
|
| SessionPreviewRequestMessage
|
|
582
597
|
| BookmarkListMessage
|
|
583
598
|
| BookmarkAddMessage
|
|
@@ -623,6 +638,8 @@ export interface SessionHistoryMessage {
|
|
|
623
638
|
interrupted?: boolean;
|
|
624
639
|
busy?: boolean;
|
|
625
640
|
busyOwner?: "cli" | "lattice";
|
|
641
|
+
totalMessages?: number;
|
|
642
|
+
hasMore?: boolean;
|
|
626
643
|
}
|
|
627
644
|
|
|
628
645
|
export interface SessionBusyMessage {
|
|
@@ -1076,6 +1093,7 @@ export type ServerMessage =
|
|
|
1076
1093
|
| AnalyticsDataMessage
|
|
1077
1094
|
| AnalyticsErrorMessage
|
|
1078
1095
|
| SessionPreviewMessage
|
|
1096
|
+
| SessionHistoryPageResultMessage
|
|
1079
1097
|
| BookmarkListResultMessage
|
|
1080
1098
|
| BudgetStatusMessage
|
|
1081
1099
|
| BudgetExceededMessage
|