clay-server 5.2.0-beta.1 → 5.2.0-beta.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.
- package/lib/dm-digest-cursor.js +120 -0
- package/lib/knowledge-db-import-validation.js +46 -0
- package/lib/knowledge-db-import.js +98 -0
- package/lib/knowledge-db-service.js +18 -30
- package/lib/knowledge-db-storage-nedb-maintenance.js +99 -0
- package/lib/knowledge-db-storage-nedb.js +66 -0
- package/lib/knowledge-db-storage.js +72 -0
- package/lib/knowledge-db-validation.js +0 -0
- package/lib/mate-knowledge-mate-bound.js +53 -0
- package/lib/mate-knowledge-mcp-server.js +55 -8
- package/lib/mate-knowledge-service.js +9 -30
- package/lib/mate-knowledge-text-tools.js +106 -0
- package/lib/project-connection.js +1 -1
- package/lib/project-mate-interaction.js +32 -62
- package/lib/project-sessions.js +3 -1
- package/lib/public/app.js +2 -0
- package/lib/public/css/command-palette.css +12 -33
- package/lib/public/modules/app-header.js +3 -2
- package/lib/public/modules/app-messages.js +12 -4
- package/lib/public/modules/app-rendering.js +4 -3
- package/lib/public/modules/chat-render-runtime.js +8 -0
- package/lib/public/modules/clay-log-links.js +23 -0
- package/lib/public/modules/command-palette.js +94 -5
- package/lib/public/modules/markdown.js +1 -1
- package/lib/server-home-chat.js +3 -3
- package/lib/server-home-clay-session-links.js +17 -9
- package/lib/server.js +1 -1
- package/lib/sessions.js +3 -1
- package/lib/workspace-query-service.js +13 -2
- package/package.json +1 -1
|
@@ -40,7 +40,7 @@ import { handleHomeMateMemoryState, handleHomeMateKnowledgeState, syncHomeMateSe
|
|
|
40
40
|
import { renderKnowledgeList, handleKnowledgeContent, handleKnowledgeMutation } from './mate-knowledge.js';
|
|
41
41
|
import { handleKnowledgeWorkbenchMessage } from './mate-knowledge-workbench.js';
|
|
42
42
|
import { renderMemoryList } from './mate-memory.js';
|
|
43
|
-
import { handlePaletteSessionSwitch, setPaletteVersion } from './command-palette.js';
|
|
43
|
+
import { handlePaletteSessionSwitch, handlePaletteHistoryDone, handlePaletteHistoryPrepended, setPaletteVersion } from './command-palette.js';
|
|
44
44
|
import { handleFindInSessionResults } from './session-search.js';
|
|
45
45
|
import { syncPaneTitles, maybeRestoreSplitGroup, openGroup } from './split-view.js';
|
|
46
46
|
import { showPairDialog, handlePairCreated, handleSplitDelegation, showWorkerDelegationNotice, hideWorkerDelegationNotice } from './split-pair-ui.js';
|
|
@@ -224,6 +224,7 @@ export function processMessage(msg) {
|
|
|
224
224
|
}
|
|
225
225
|
}
|
|
226
226
|
|
|
227
|
+
store.set({ currentHistoryIndex: Number.isSafeInteger(msg._historyIndex) && msg._historyIndex >= 0 ? msg._historyIndex : null });
|
|
227
228
|
switch (msg.type) {
|
|
228
229
|
case "message_ack":
|
|
229
230
|
var acknowledgedEntry = acknowledgeMessage(msg.clientMessageId);
|
|
@@ -250,6 +251,7 @@ export function processMessage(msg) {
|
|
|
250
251
|
|
|
251
252
|
case "history_prepend":
|
|
252
253
|
prependOlderHistory(msg.items, msg.meta);
|
|
254
|
+
handlePaletteHistoryPrepended();
|
|
253
255
|
break;
|
|
254
256
|
|
|
255
257
|
case "history_done":
|
|
@@ -311,11 +313,16 @@ export function processMessage(msg) {
|
|
|
311
313
|
// re-pins on every height change while armed. Disarms early on
|
|
312
314
|
// any real user scroll input.
|
|
313
315
|
// Skip arming when we have a pending in-conversation navigate
|
|
314
|
-
// target (file-edit deeplink) — the navigate block
|
|
315
|
-
// that element into view, and sticky-bottom would
|
|
316
|
+
// target (file-edit or Session-entry deeplink) — the navigate block
|
|
317
|
+
// below scrolls that element into view, and sticky-bottom would
|
|
318
|
+
// fight it.
|
|
316
319
|
var nav = getPendingNavigate();
|
|
317
320
|
var hasNavTarget = nav && (nav.toolId || nav.assistantUuid);
|
|
318
|
-
|
|
321
|
+
var sessionEntryNav = store.get('pendingSessionEntryNavigation');
|
|
322
|
+
var hasSessionEntryNav = sessionEntryNav
|
|
323
|
+
&& String(sessionEntryNav.projectSlug) === String(store.get('currentSlug'))
|
|
324
|
+
&& String(sessionEntryNav.sessionId) === String(store.get('activeSessionId'));
|
|
325
|
+
if (hasNavTarget || hasSessionEntryNav) {
|
|
319
326
|
// Navigate block below will scrollIntoView on the target — don't
|
|
320
327
|
// arm sticky-bottom or it would fight that scroll.
|
|
321
328
|
scrollToBottom();
|
|
@@ -343,6 +350,7 @@ export function processMessage(msg) {
|
|
|
343
350
|
}
|
|
344
351
|
});
|
|
345
352
|
}
|
|
353
|
+
requestAnimationFrame(function () { handlePaletteHistoryDone(); });
|
|
346
354
|
break;
|
|
347
355
|
|
|
348
356
|
case "restore_mate_dm":
|
|
@@ -8,10 +8,10 @@ import { refreshIcons } from './icons.js';
|
|
|
8
8
|
import { closeToolGroup } from './tools.js';
|
|
9
9
|
import { sendMessage, hasSendableContent } from './input.js';
|
|
10
10
|
import { getChatLayout } from './theme.js';
|
|
11
|
-
import { addToMessages, scrollToBottom, forceScrollToBottom, getMsgTime, shouldGroupMessage, getTurnCounter, getActivityEl } from './chat-render-runtime.js';
|
|
11
|
+
import { addToMessages, addHistoryIndex, scrollToBottom, forceScrollToBottom, getMsgTime, shouldGroupMessage, getTurnCounter, getActivityEl } from './chat-render-runtime.js';
|
|
12
12
|
import { resolveProjectAssistantIdentity, applyProjectAssistantIdentity } from './project-chat-identity.js';
|
|
13
13
|
export { VENDOR_ORDER, EXPERIMENTAL_VENDORS, isExperimentalVendor } from './vendor-priority.js';
|
|
14
|
-
export { initRendering, addToMessages, scrollToBottom, forceScrollToBottom, getMsgTime, shouldGroupMessage, getTurnCounter, setTurnCounter, getPrependAnchor, setPrependAnchor, getActivityEl, setActivityEl, getIsUserScrolledUp, setIsUserScrolledUp, getStickyBottom, armStickyBottom, disarmStickyBottom, VENDOR_AVATARS, VENDOR_NAMES, VENDOR_HOMEPAGES } from './chat-render-runtime.js';
|
|
14
|
+
export { initRendering, addToMessages, addHistoryIndex, scrollToBottom, forceScrollToBottom, getMsgTime, shouldGroupMessage, getTurnCounter, setTurnCounter, getPrependAnchor, setPrependAnchor, getActivityEl, setActivityEl, getIsUserScrolledUp, setIsUserScrolledUp, getStickyBottom, armStickyBottom, disarmStickyBottom, VENDOR_AVATARS, VENDOR_NAMES, VENDOR_HOMEPAGES } from './chat-render-runtime.js';
|
|
15
15
|
export { addUserMessage, addSystemMessage, addConflictMessage, addContextOverflowMessage } from './app-message-cards.js';
|
|
16
16
|
|
|
17
17
|
// --- Module-owned state (not in store) ---
|
|
@@ -44,7 +44,8 @@ export function addCopyHandler(msgEl, rawText) {
|
|
|
44
44
|
}
|
|
45
45
|
|
|
46
46
|
export function appendDelta(text) {
|
|
47
|
-
ensureAssistantBlock();
|
|
47
|
+
var assistantEl = ensureAssistantBlock();
|
|
48
|
+
if (store.get('replayingHistory')) addHistoryIndex(assistantEl, store.get('currentHistoryIndex'));
|
|
48
49
|
streamBuffer += text;
|
|
49
50
|
if (store.get('replayingHistory')) {
|
|
50
51
|
store.set({ currentFullText: store.get('currentFullText') + streamBuffer });
|
|
@@ -155,11 +155,19 @@ export function setIsUserScrolledUp(value) { isUserScrolledUp = value; }
|
|
|
155
155
|
|
|
156
156
|
export function addToMessages(element) {
|
|
157
157
|
var messagesEl = getMessagesEl();
|
|
158
|
+
addHistoryIndex(element, store.get('currentHistoryIndex'));
|
|
158
159
|
if (prependAnchor) messagesEl.insertBefore(element, prependAnchor);
|
|
159
160
|
else messagesEl.appendChild(element);
|
|
160
161
|
keepActiveThinkingAtTail();
|
|
161
162
|
}
|
|
162
163
|
|
|
164
|
+
export function addHistoryIndex(element, historyIndex) {
|
|
165
|
+
if (!element || !Number.isSafeInteger(historyIndex) || historyIndex < 0) return;
|
|
166
|
+
var existing = element.dataset.historyIndexes || "";
|
|
167
|
+
if (existing.split(",").indexOf(String(historyIndex)) !== -1) return;
|
|
168
|
+
element.dataset.historyIndexes = existing ? existing + "," + historyIndex : String(historyIndex);
|
|
169
|
+
}
|
|
170
|
+
|
|
163
171
|
// Thinking is a single accumulating record for the active turn. Other live
|
|
164
172
|
// transcript entries may arrive between reasoning segments, so keep only the
|
|
165
173
|
// currently active record after the tail activity indicator.
|
|
@@ -1,10 +1,33 @@
|
|
|
1
1
|
import { enhanceClayIssueLinks, isExactClayIssueReference } from './clay-issue-links.js';
|
|
2
2
|
import { enhanceGithubLinks } from './github-links.js';
|
|
3
|
+
import { escapeHtml } from './utils.js';
|
|
3
4
|
|
|
4
5
|
export function renderClayRecordLink(href, text) {
|
|
6
|
+
if (isExactClaySessionReference(href)) return renderClaySessionLink(href.trim(), text);
|
|
5
7
|
if (!isExactClayLogReference(href) && !isExactClayIssueReference(href)) return null;
|
|
6
8
|
return '<a href="#" data-clay-record-ref="' + href.trim() + '">' + text + '</a>';
|
|
7
9
|
}
|
|
10
|
+
|
|
11
|
+
export function isExactClaySessionReference(text) {
|
|
12
|
+
return parseClaySessionReference(text) !== null;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function renderClaySessionLink(ref, text) {
|
|
16
|
+
var parsed = parseClaySessionReference(ref);
|
|
17
|
+
if (!parsed) return null;
|
|
18
|
+
var label = text ? String(text).trim() : "";
|
|
19
|
+
var safeLabel = escapeHtml(label);
|
|
20
|
+
var entryAttr = parsed.entryIndex === null ? "" : ' data-session-entry-index="' + parsed.entryIndex + '"';
|
|
21
|
+
return '<button type="button" class="clayos-session-link clayos-record-link" data-session-ref="' + parsed.ref + '"' + entryAttr + ' aria-label="Open session' + (safeLabel ? " from " + safeLabel : "") + '"><span>Session</span>' + (safeLabel ? '<small>' + safeLabel + '</small>' : '') + '</button>';
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function parseClaySessionReference(text) {
|
|
25
|
+
var match = /^((?:session:[A-Za-z0-9_-]{24}))(?:#entry-([0-9]+))?$/.exec((text || "").trim());
|
|
26
|
+
if (!match) return null;
|
|
27
|
+
var entryIndex = match[2] === undefined ? null : Number(match[2]);
|
|
28
|
+
if (entryIndex !== null && (!Number.isSafeInteger(entryIndex) || entryIndex < 0)) return null;
|
|
29
|
+
return { ref: match[1], entryIndex: entryIndex };
|
|
30
|
+
}
|
|
8
31
|
export function parseClayLogReferences(text) {
|
|
9
32
|
var pattern = /\[clayos\/(log:[A-Za-z0-9_-]{24})(?:\s+[\u2014-]\s+([^\]\n]{1,80}))?\]|(^|[^A-Za-z0-9_/:])(log:[A-Za-z0-9_-]{24})(?![A-Za-z0-9_-])/g;
|
|
10
33
|
var results = [];
|
|
@@ -5,6 +5,8 @@ import { openSearch as openSessionSearch } from './session-search.js';
|
|
|
5
5
|
import { startSearchClayChat, attachSearchClayChat, detachSearchClayChat, getSearchClayChatSummary } from './search-clay-chat.js';
|
|
6
6
|
import { getWs } from './ws-ref.js';
|
|
7
7
|
import { openProjectLog } from './project-logs.js';
|
|
8
|
+
import { store } from './store.js';
|
|
9
|
+
import { disarmStickyBottom } from './chat-render-runtime.js';
|
|
8
10
|
|
|
9
11
|
function formatRelativeDate(ts) {
|
|
10
12
|
if (!ts) return "";
|
|
@@ -70,6 +72,10 @@ function handleClaySessionLinkClick(event) {
|
|
|
70
72
|
var requestId = "clayos-" + (isLog ? "log-" : "session-") + Date.now() + "-" + Math.random().toString(36).slice(2, 9);
|
|
71
73
|
if (isLog) link.dataset.logRequest = requestId;
|
|
72
74
|
else link.dataset.sessionRequest = requestId;
|
|
75
|
+
if (!isLog) store.set({ sessionNavigationRequestId: requestId, sessionNavigationRequestContext: {
|
|
76
|
+
projectSlug: store.get('currentSlug'),
|
|
77
|
+
sessionId: store.get('activeSessionId')
|
|
78
|
+
} });
|
|
73
79
|
link.classList.remove("is-error");
|
|
74
80
|
link.classList.add("is-loading");
|
|
75
81
|
var request = { requestId: requestId, surface: link.closest(".search-clay-chat") ? "search" : "home" };
|
|
@@ -79,6 +85,7 @@ function handleClaySessionLinkClick(event) {
|
|
|
79
85
|
} else {
|
|
80
86
|
request.type = "home_clay_session_resolve";
|
|
81
87
|
request.sessionRef = link.dataset.sessionRef;
|
|
88
|
+
if (link.dataset.sessionEntryIndex !== undefined) request.entryIndex = Number(link.dataset.sessionEntryIndex);
|
|
82
89
|
}
|
|
83
90
|
ws.send(JSON.stringify(request));
|
|
84
91
|
}
|
|
@@ -91,12 +98,31 @@ export function handleClaySessionTarget(msg) {
|
|
|
91
98
|
for (var i = 0; i < links.length; i++) {
|
|
92
99
|
if ((isLog ? links[i].dataset.logRequest : links[i].dataset.sessionRequest) === msg.requestId) link = links[i];
|
|
93
100
|
}
|
|
101
|
+
if (!isLog && store.get('sessionNavigationRequestId') !== msg.requestId) {
|
|
102
|
+
if (link) {
|
|
103
|
+
link.classList.remove("is-loading");
|
|
104
|
+
delete link.dataset.sessionRequest;
|
|
105
|
+
}
|
|
106
|
+
return true;
|
|
107
|
+
}
|
|
108
|
+
if (!isLog) {
|
|
109
|
+
var requestContext = store.get('sessionNavigationRequestContext');
|
|
110
|
+
if (requestContext && (String(store.get('currentSlug')) !== String(requestContext.projectSlug) || String(store.get('activeSessionId')) !== String(requestContext.sessionId))) {
|
|
111
|
+
if (link) {
|
|
112
|
+
link.classList.remove("is-loading");
|
|
113
|
+
delete link.dataset.sessionRequest;
|
|
114
|
+
}
|
|
115
|
+
store.set({ pendingSessionEntryNavigation: null, sessionNavigationRequestContext: null });
|
|
116
|
+
return true;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
94
119
|
if (link) {
|
|
95
120
|
link.classList.remove("is-loading");
|
|
96
121
|
if (isLog) delete link.dataset.logRequest;
|
|
97
122
|
else delete link.dataset.sessionRequest;
|
|
98
123
|
}
|
|
99
124
|
if (msg.status !== "ready" || !msg.target) {
|
|
125
|
+
if (!isLog) store.set({ pendingSessionEntryNavigation: null });
|
|
100
126
|
if (link) {
|
|
101
127
|
link.classList.add("is-error");
|
|
102
128
|
link.title = msg.error || "Item not available";
|
|
@@ -108,7 +134,7 @@ export function handleClaySessionTarget(msg) {
|
|
|
108
134
|
navigateToLog(msg.target);
|
|
109
135
|
return true;
|
|
110
136
|
}
|
|
111
|
-
navigateToSession({ projectSlug: msg.target.projectSlug, sessionId: msg.target.sessionId }, null);
|
|
137
|
+
navigateToSession({ projectSlug: msg.target.projectSlug, sessionId: msg.target.sessionId }, null, msg.target.entryIndex);
|
|
112
138
|
return true;
|
|
113
139
|
}
|
|
114
140
|
|
|
@@ -151,9 +177,61 @@ export function handlePaletteSessionSwitch() {
|
|
|
151
177
|
var nav = pendingNav;
|
|
152
178
|
pendingNav = null;
|
|
153
179
|
if (ctx.currentSlug && ctx.currentSlug() === nav.slug) {
|
|
154
|
-
|
|
155
|
-
|
|
180
|
+
navigateToSession({ projectSlug: nav.slug, sessionId: nav.sessionId }, nav.query, nav.entryIndex);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
export function handlePaletteHistoryDone() {
|
|
185
|
+
var pending = store.get('pendingSessionEntryNavigation');
|
|
186
|
+
if (!pending) return;
|
|
187
|
+
if (!isActiveEntryNavigation(pending)) {
|
|
188
|
+
store.set({ pendingSessionEntryNavigation: null });
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
var historyFrom = store.get('historyFrom') || 0;
|
|
192
|
+
if (pending.entryIndex < historyFrom) {
|
|
193
|
+
var ws = getWs();
|
|
194
|
+
if (ws && ws.readyState === 1) ws.send(JSON.stringify({ type: "load_more_history", before: historyFrom, target: pending.entryIndex }));
|
|
195
|
+
return;
|
|
156
196
|
}
|
|
197
|
+
jumpToSessionEntry(pending.entryIndex);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
export function handlePaletteHistoryPrepended() {
|
|
201
|
+
var pending = store.get('pendingSessionEntryNavigation');
|
|
202
|
+
if (!pending) return;
|
|
203
|
+
if (!isActiveEntryNavigation(pending)) {
|
|
204
|
+
store.set({ pendingSessionEntryNavigation: null });
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
requestAnimationFrame(function () {
|
|
208
|
+
if (store.get('pendingSessionEntryNavigation') === pending) jumpToSessionEntry(pending.entryIndex, pending);
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
function isActiveEntryNavigation(pending) {
|
|
213
|
+
return String(store.get('currentSlug') || (ctx && ctx.currentSlug ? ctx.currentSlug() : "")) === String(pending.projectSlug) && String(store.get('activeSessionId')) === String(pending.sessionId);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
function jumpToSessionEntry(entryIndex, pending) {
|
|
217
|
+
if (pending && !isActiveEntryNavigation(pending)) return;
|
|
218
|
+
var messages = document.querySelectorAll("#messages [data-history-index], #messages [data-history-indexes]");
|
|
219
|
+
var target = null;
|
|
220
|
+
for (var i = 0; i < messages.length; i++) {
|
|
221
|
+
var indexes = (messages[i].dataset.historyIndexes || messages[i].dataset.historyIndex || "").split(",");
|
|
222
|
+
if (indexes.indexOf(String(entryIndex)) !== -1) { target = messages[i]; break; }
|
|
223
|
+
}
|
|
224
|
+
store.set({ pendingSessionEntryNavigation: null });
|
|
225
|
+
if (!target) return;
|
|
226
|
+
disarmStickyBottom();
|
|
227
|
+
var oldTargets = document.querySelectorAll("#messages .session-entry-target");
|
|
228
|
+
for (var oldIndex = 0; oldIndex < oldTargets.length; oldIndex++) oldTargets[oldIndex].classList.remove("session-entry-target");
|
|
229
|
+
var reducedMotion = window.matchMedia && window.matchMedia("(prefers-reduced-motion: reduce)").matches;
|
|
230
|
+
target.scrollIntoView({ behavior: reducedMotion ? "auto" : "smooth", block: "center" });
|
|
231
|
+
target.classList.remove("session-entry-target");
|
|
232
|
+
void target.offsetWidth;
|
|
233
|
+
target.classList.add("session-entry-target");
|
|
234
|
+
setTimeout(function () { target.classList.remove("session-entry-target"); }, reducedMotion ? 700 : 1800);
|
|
157
235
|
}
|
|
158
236
|
|
|
159
237
|
function navigateToLog(target) {
|
|
@@ -357,13 +435,24 @@ function activateItem(entry) {
|
|
|
357
435
|
if (entry.type === "mate-session" || entry.type === "session") navigateToSession(entry.data, entry.query);
|
|
358
436
|
}
|
|
359
437
|
|
|
360
|
-
function navigateToSession(item, query) {
|
|
438
|
+
function navigateToSession(item, query, entryIndex) {
|
|
361
439
|
closeCommandPalette();
|
|
440
|
+
pendingNav = null;
|
|
441
|
+
var hasEntry = entryIndex !== null && entryIndex !== undefined && Number.isSafeInteger(Number(entryIndex)) && Number(entryIndex) >= 0;
|
|
442
|
+
store.set({ pendingSessionEntryNavigation: hasEntry ? { projectSlug: item.projectSlug, sessionId: item.sessionId, entryIndex: Number(entryIndex) } : null });
|
|
362
443
|
if (ctx.currentSlug && ctx.currentSlug() === item.projectSlug) {
|
|
444
|
+
var alreadyActive = String(store.get('activeSessionId')) === String(item.sessionId);
|
|
445
|
+
if (alreadyActive) {
|
|
446
|
+
if (query) setTimeout(function () { openSessionSearch(query); }, 400);
|
|
447
|
+
if (hasEntry) requestAnimationFrame(function () {
|
|
448
|
+
if (!store.get('replayingHistory')) handlePaletteHistoryDone();
|
|
449
|
+
});
|
|
450
|
+
return;
|
|
451
|
+
}
|
|
363
452
|
ctx.selectSession(item.sessionId);
|
|
364
453
|
if (query) setTimeout(function () { openSessionSearch(query); }, 400);
|
|
365
454
|
} else {
|
|
366
|
-
pendingNav = { slug: item.projectSlug, sessionId: item.sessionId, query: query };
|
|
455
|
+
pendingNav = { slug: item.projectSlug, sessionId: item.sessionId, query: query, entryIndex: hasEntry ? Number(entryIndex) : null };
|
|
367
456
|
ctx.switchProject(item.projectSlug);
|
|
368
457
|
}
|
|
369
458
|
}
|
|
@@ -84,7 +84,7 @@ export function enhanceClaySessionLinks(root) {
|
|
|
84
84
|
fragment.appendChild(document.createTextNode(text.slice(offset, match.index)));
|
|
85
85
|
var button = document.createElement("button");
|
|
86
86
|
button.type = "button";
|
|
87
|
-
button.className = "clayos-session-link";
|
|
87
|
+
button.className = "clayos-session-link clayos-record-link";
|
|
88
88
|
button.dataset.sessionRef = match[1];
|
|
89
89
|
button.setAttribute("aria-label", "Open session" + (match[2] ? " from " + match[2].trim() : ""));
|
|
90
90
|
var label = document.createElement("span");
|
package/lib/server-home-chat.js
CHANGED
|
@@ -418,7 +418,7 @@ function attachHomeChat(deps) {
|
|
|
418
418
|
return sessionReferenceValue;
|
|
419
419
|
}
|
|
420
420
|
|
|
421
|
-
function handleMessage(ws, msg) {
|
|
421
|
+
function handleMessage(ws, msg, routedProjectSlug) {
|
|
422
422
|
if (!msg || typeof msg.type !== "string") return false;
|
|
423
423
|
if (msg.type === "home_mate_stop" || msg.type === "home_mate_permission_response") {
|
|
424
424
|
var controlUserId = ws._clayUser ? ws._clayUser.id : null;
|
|
@@ -477,8 +477,8 @@ function attachHomeChat(deps) {
|
|
|
477
477
|
if (msg.type === "issue_reference_resolve") { resolveIssueLink(ws, msg); return true; }
|
|
478
478
|
if (homeDebates.handle(ws, userId, msg)) return true;
|
|
479
479
|
if (msg.type === "home_clay_ask") { homeClayEntry.start(ws, userId, msg); return true; }
|
|
480
|
-
if (msg.type === "home_clay_session_resolve") { homeClaySessionLinks.resolve(ws, msg); return true; }
|
|
481
|
-
if (msg.type === "home_clay_log_resolve") { homeClaySessionLinks.resolveLog(ws, msg); return true; }
|
|
480
|
+
if (msg.type === "home_clay_session_resolve") { homeClaySessionLinks.resolve(ws, msg, routedProjectSlug); return true; }
|
|
481
|
+
if (msg.type === "home_clay_log_resolve") { homeClaySessionLinks.resolveLog(ws, msg, routedProjectSlug); return true; }
|
|
482
482
|
|
|
483
483
|
if (msg.type === "home_mate_debate_plan") {
|
|
484
484
|
homeDebate.start(ws, userId, msg);
|
|
@@ -2,23 +2,31 @@
|
|
|
2
2
|
// owner-bound source session.
|
|
3
3
|
|
|
4
4
|
function attachHomeClaySessionLinks(ctx) {
|
|
5
|
-
function source(ws, msg) {
|
|
5
|
+
function source(ws, msg, routedProjectSlug) {
|
|
6
6
|
var tap = msg.surface === "search" ? ws._searchClayTap : ws._homeChatTap;
|
|
7
|
+
if (!tap && routedProjectSlug) {
|
|
8
|
+
var routedProject = ctx.projects.get(routedProjectSlug);
|
|
9
|
+
var routedManager = routedProject && routedProject.getSessionManager ? routedProject.getSessionManager() : null;
|
|
10
|
+
var routedSession = routedManager && ws._clayActiveSession ? routedManager.sessions.get(ws._clayActiveSession) : null;
|
|
11
|
+
return { tap: { mateSlug: routedProjectSlug, sessionId: ws._clayActiveSession }, project: routedProject, session: routedSession };
|
|
12
|
+
}
|
|
7
13
|
var sourceProject = tap && tap.mateSlug ? ctx.projects.get(tap.mateSlug) : null;
|
|
8
14
|
var sourceManager = sourceProject && sourceProject.getSessionManager ? sourceProject.getSessionManager() : null;
|
|
9
15
|
var sourceSession = sourceManager && tap ? sourceManager.sessions.get(tap.sessionId) : null;
|
|
10
16
|
return { tap: tap, project: sourceProject, session: sourceSession };
|
|
11
17
|
}
|
|
12
18
|
|
|
13
|
-
function resolve(ws, msg) {
|
|
14
|
-
var selected = source(ws, msg);
|
|
15
|
-
var bound = ctx.workspaceQueryService && selected.session
|
|
16
|
-
? ctx.workspaceQueryService.bindProjectSession({ projectSlug: selected.tap.mateSlug, session: selected.session })
|
|
17
|
-
: null;
|
|
19
|
+
function resolve(ws, msg, routedProjectSlug) {
|
|
20
|
+
var selected = source(ws, msg, routedProjectSlug);
|
|
18
21
|
var payload = { type: "home_clay_session_target", requestId: msg.requestId || null, sessionRef: msg.sessionRef || null, status: "error" };
|
|
19
22
|
try {
|
|
23
|
+
var binding = selected.session ? { projectSlug: selected.tap.mateSlug, session: selected.session } : null;
|
|
24
|
+
if (binding && ws && ws._clayUser) binding.viewerId = ws._clayUser.id;
|
|
25
|
+
var bound = ctx.workspaceQueryService && binding
|
|
26
|
+
? ctx.workspaceQueryService.bindProjectSession(binding)
|
|
27
|
+
: null;
|
|
20
28
|
if (!bound) throw new Error("The source conversation is no longer available.");
|
|
21
|
-
payload.target = bound.resolveSessionNavigation({ sessionRef: msg.sessionRef });
|
|
29
|
+
payload.target = bound.resolveSessionNavigation({ sessionRef: msg.sessionRef, entryIndex: msg.entryIndex });
|
|
22
30
|
payload.status = "ready";
|
|
23
31
|
} catch (error) {
|
|
24
32
|
payload.error = error.message || "Session not available.";
|
|
@@ -26,8 +34,8 @@ function attachHomeClaySessionLinks(ctx) {
|
|
|
26
34
|
ctx.sendMessage(ws, payload);
|
|
27
35
|
}
|
|
28
36
|
|
|
29
|
-
function resolveLog(ws, msg) {
|
|
30
|
-
var selected = source(ws, msg);
|
|
37
|
+
function resolveLog(ws, msg, routedProjectSlug) {
|
|
38
|
+
var selected = source(ws, msg, routedProjectSlug);
|
|
31
39
|
var status = selected.project && selected.project.getStatus ? selected.project.getStatus() : null;
|
|
32
40
|
var bound = ctx.projectLogsService && selected.session && status
|
|
33
41
|
? ctx.projectLogsService.bindMate({
|
package/lib/server.js
CHANGED
|
@@ -1630,7 +1630,7 @@ function createServer(opts) {
|
|
|
1630
1630
|
if (dmHandler.handleMessage(ws, msg)) return true;
|
|
1631
1631
|
if (mateHandler && mateHandler.handleMessage(ws, msg)) return true;
|
|
1632
1632
|
if (emailHandler.handleMessage(ws, msg)) return true;
|
|
1633
|
-
if (homeChatHandler.handleMessage(ws, msg)) return true;
|
|
1633
|
+
if (homeChatHandler.handleMessage(ws, msg, routedProjectSlug)) return true;
|
|
1634
1634
|
if (homePreferencesHandler.handleMessage(ws, msg)) return true;
|
|
1635
1635
|
if (toolsHandler.handleMessage(ws, msg)) return true;
|
|
1636
1636
|
return false;
|
package/lib/sessions.js
CHANGED
|
@@ -864,7 +864,9 @@ function createSessionManager(opts) {
|
|
|
864
864
|
if (_item && (_item.type === "mention_user" || _item.type === "mention_response")) {
|
|
865
865
|
console.log("[DEBUG replayHistory] sending mention at index=" + i + " from=" + fromIndex + " total=" + total + " type=" + _item.type + " mate=" + (_item.mateName || ""));
|
|
866
866
|
}
|
|
867
|
-
|
|
867
|
+
var replayItem = transform ? transform(_item) : _item;
|
|
868
|
+
if (replayItem && typeof replayItem === "object") replayItem = Object.assign({}, replayItem, { _historyIndex: i });
|
|
869
|
+
_send(replayItem);
|
|
868
870
|
}
|
|
869
871
|
|
|
870
872
|
// Find the last result message in the full history for accurate context data
|
|
@@ -168,6 +168,13 @@ function attachWorkspaceQueryService(ctx) {
|
|
|
168
168
|
} else if (source.session.ownerId) {
|
|
169
169
|
return null;
|
|
170
170
|
}
|
|
171
|
+
if (source.viewerId !== undefined) {
|
|
172
|
+
var viewerPrincipal = {
|
|
173
|
+
userId: isMultiUser() ? source.viewerId : null,
|
|
174
|
+
singleUserOwnerId: isMultiUser() ? null : source.viewerId,
|
|
175
|
+
};
|
|
176
|
+
if (!ownsSession(viewerPrincipal, source.session)) return null;
|
|
177
|
+
}
|
|
171
178
|
return createBound({
|
|
172
179
|
userId: isMultiUser() ? source.session.ownerId : null,
|
|
173
180
|
sourceProjectSlug: source.projectSlug,
|
|
@@ -366,11 +373,13 @@ function attachWorkspaceQueryService(ctx) {
|
|
|
366
373
|
|
|
367
374
|
function resolveSessionNavigation(principal, ref) {
|
|
368
375
|
if (typeof ref !== "string" || !/^session:[A-Za-z0-9_-]{24}$/.test(ref)) throw new Error("Invalid session reference.");
|
|
376
|
+
var entryIndex = arguments.length > 2 ? arguments[2] : null;
|
|
377
|
+
if (entryIndex !== null && entryIndex !== undefined && (!Number.isSafeInteger(entryIndex) || entryIndex < 0)) throw new Error("Invalid session entry reference.");
|
|
369
378
|
var projects = collect(principal);
|
|
370
379
|
for (var p = 0; p < projects.length; p++) {
|
|
371
380
|
var session = findSession(projects[p], ref);
|
|
372
381
|
if (!session) continue;
|
|
373
|
-
|
|
382
|
+
var target = {
|
|
374
383
|
projectSlug: projects[p].projectSlug,
|
|
375
384
|
projectTitle: projects[p].projectTitle,
|
|
376
385
|
isMate: projects[p].isMate,
|
|
@@ -379,6 +388,8 @@ function attachWorkspaceQueryService(ctx) {
|
|
|
379
388
|
homeSessionId: session.cliSessionId || "local:" + session.localId,
|
|
380
389
|
title: cleanMetadata(session.title || "New Session", 160),
|
|
381
390
|
};
|
|
391
|
+
if (entryIndex !== null && entryIndex !== undefined) target.entryIndex = entryIndex;
|
|
392
|
+
return target;
|
|
382
393
|
}
|
|
383
394
|
throw new Error("Session not found in the owned workspace.");
|
|
384
395
|
}
|
|
@@ -450,7 +461,7 @@ function attachWorkspaceQueryService(ctx) {
|
|
|
450
461
|
if (!principal.isClay) throw new Error("Workspace-wide activity is available only to builtin Clay.");
|
|
451
462
|
return workspaceActivity(principal, args || {});
|
|
452
463
|
},
|
|
453
|
-
resolveSessionNavigation: function (args) { return resolveSessionNavigation(principal, args && args.sessionRef); },
|
|
464
|
+
resolveSessionNavigation: function (args) { return resolveSessionNavigation(principal, args && args.sessionRef, args && args.entryIndex); },
|
|
454
465
|
listRecentDecisions: function (args) {
|
|
455
466
|
if (!principal.isClay) throw new Error("Workspace decisions are available only to builtin Clay.");
|
|
456
467
|
return recentDecisions(principal, args || {});
|
package/package.json
CHANGED