clay-server 2.31.0 → 2.32.0-beta.10
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/browser-mcp-server.js +32 -44
- package/lib/codex-defaults.js +18 -0
- package/lib/debate-mcp-server.js +14 -31
- package/lib/mcp-local.js +31 -1
- package/lib/project-connection.js +9 -6
- package/lib/project-debate.js +8 -0
- package/lib/project-filesystem.js +47 -1
- package/lib/project-http.js +75 -8
- package/lib/project-mate-interaction.js +102 -16
- package/lib/project-mcp.js +4 -0
- package/lib/project-notifications.js +9 -0
- package/lib/project-sessions.js +94 -51
- package/lib/project-user-message.js +12 -7
- package/lib/project.js +234 -99
- package/lib/public/app.js +135 -454
- package/lib/public/codex-avatar.png +0 -0
- package/lib/public/css/debate.css +3 -2
- package/lib/public/css/filebrowser.css +91 -1
- package/lib/public/css/icon-strip.css +21 -5
- package/lib/public/css/input.css +338 -104
- package/lib/public/css/mates.css +43 -0
- package/lib/public/css/mention.css +48 -4
- package/lib/public/css/menus.css +1 -1
- package/lib/public/css/messages.css +2 -0
- package/lib/public/css/notifications-center.css +26 -0
- package/lib/public/css/tooltip.css +47 -0
- package/lib/public/index.html +78 -26
- package/lib/public/modules/app-connection.js +138 -37
- package/lib/public/modules/app-cursors.js +18 -17
- package/lib/public/modules/app-debate-ui.js +9 -9
- package/lib/public/modules/app-dm.js +175 -131
- package/lib/public/modules/app-favicon.js +28 -26
- package/lib/public/modules/app-header.js +79 -68
- package/lib/public/modules/app-home-hub.js +55 -47
- package/lib/public/modules/app-loop-ui.js +34 -18
- package/lib/public/modules/app-loop-wizard.js +6 -6
- package/lib/public/modules/app-messages.js +199 -153
- package/lib/public/modules/app-misc.js +23 -12
- package/lib/public/modules/app-notifications.js +119 -9
- package/lib/public/modules/app-panels.js +203 -49
- package/lib/public/modules/app-projects.js +161 -150
- package/lib/public/modules/app-rate-limit.js +5 -4
- package/lib/public/modules/app-rendering.js +149 -101
- package/lib/public/modules/app-skills-install.js +4 -4
- package/lib/public/modules/context-sources.js +102 -66
- package/lib/public/modules/dom-refs.js +21 -0
- package/lib/public/modules/filebrowser.js +173 -2
- package/lib/public/modules/input.js +122 -0
- package/lib/public/modules/markdown.js +5 -1
- package/lib/public/modules/mate-sidebar.js +38 -0
- package/lib/public/modules/mention.js +24 -6
- package/lib/public/modules/scheduler.js +1 -1
- package/lib/public/modules/sidebar-mates.js +79 -35
- package/lib/public/modules/sidebar-mobile.js +34 -30
- package/lib/public/modules/sidebar-projects.js +60 -57
- package/lib/public/modules/sidebar-sessions.js +75 -69
- package/lib/public/modules/sidebar.js +12 -20
- package/lib/public/modules/skills.js +8 -9
- package/lib/public/modules/sticky-notes.js +1 -2
- package/lib/public/modules/store.js +9 -2
- package/lib/public/modules/stt.js +4 -1
- package/lib/public/modules/terminal.js +12 -0
- package/lib/public/modules/tools.js +18 -13
- package/lib/public/modules/tooltip.js +32 -5
- package/lib/sdk-bridge.js +562 -1114
- package/lib/sdk-message-processor.js +150 -135
- package/lib/sdk-worker.js +4 -0
- package/lib/server-dm.js +1 -0
- package/lib/server.js +86 -1
- package/lib/sessions.js +81 -37
- package/lib/ws-schema.js +2 -0
- package/lib/yoke/adapters/claude-worker.js +559 -0
- package/lib/yoke/adapters/claude.js +1483 -0
- package/lib/yoke/adapters/codex.js +1121 -0
- package/lib/yoke/adapters/gemini.js +709 -0
- package/lib/yoke/codex-app-server.js +307 -0
- package/lib/yoke/index.js +199 -0
- package/lib/yoke/instructions.js +62 -0
- package/lib/yoke/interface.js +98 -0
- package/lib/yoke/mcp-bridge-server.js +294 -0
- package/lib/yoke/package.json +7 -0
- package/package.json +3 -1
|
@@ -4,6 +4,7 @@ var crypto = require("crypto");
|
|
|
4
4
|
|
|
5
5
|
function attachMateInteraction(ctx) {
|
|
6
6
|
var cwd = ctx.cwd;
|
|
7
|
+
var slug = ctx.slug || "";
|
|
7
8
|
var sm = ctx.sm;
|
|
8
9
|
var sdk = ctx.sdk;
|
|
9
10
|
var send = ctx.send;
|
|
@@ -21,6 +22,7 @@ function attachMateInteraction(ctx) {
|
|
|
21
22
|
var loadMateDigests = ctx.loadMateDigests;
|
|
22
23
|
var updateMemorySummary = ctx.updateMemorySummary;
|
|
23
24
|
var initMemorySummary = ctx.initMemorySummary;
|
|
25
|
+
var getNotificationsModule = ctx.getNotificationsModule || function () { return null; };
|
|
24
26
|
// checkForDmDebateBrief is accessed via ctx at call time because
|
|
25
27
|
// it comes from the debate module initialized after this one.
|
|
26
28
|
|
|
@@ -104,6 +106,30 @@ function attachMateInteraction(ctx) {
|
|
|
104
106
|
return lines.join("\n") + "\n\n";
|
|
105
107
|
}
|
|
106
108
|
|
|
109
|
+
function getHostMateId() {
|
|
110
|
+
if (typeof slug === "string" && slug.indexOf("mate-") === 0) {
|
|
111
|
+
return slug.substring(5) || null;
|
|
112
|
+
}
|
|
113
|
+
return null;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function notifyMentionResponse(session, mentionMateId, mentionMateName, fullText) {
|
|
117
|
+
var notificationsModule = getNotificationsModule();
|
|
118
|
+
var hostMateId = getHostMateId();
|
|
119
|
+
var preview = (fullText || "").replace(/\s+/g, " ").trim();
|
|
120
|
+
if (preview.length > 140) preview = preview.substring(0, 140) + "...";
|
|
121
|
+
if (!notificationsModule || !hostMateId) return;
|
|
122
|
+
notificationsModule.notify("mention_response", {
|
|
123
|
+
title: (mentionMateName || "Mate") + " responded",
|
|
124
|
+
preview: preview,
|
|
125
|
+
slug: slug,
|
|
126
|
+
sessionId: session.localId,
|
|
127
|
+
mateId: hostMateId,
|
|
128
|
+
avatarMateId: mentionMateId || null,
|
|
129
|
+
ownerId: session.ownerId || null,
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
|
|
107
133
|
// --- Shared digest worker: one reusable Haiku session for gate+digest ---
|
|
108
134
|
// Combines gate check and digest generation into a single prompt,
|
|
109
135
|
// processes jobs sequentially from a queue, reuses the session across calls.
|
|
@@ -136,12 +162,19 @@ function attachMateInteraction(ctx) {
|
|
|
136
162
|
} catch (e) {}
|
|
137
163
|
|
|
138
164
|
// Combined gate + digest in one prompt (saves a full round-trip vs separate gate)
|
|
139
|
-
var
|
|
165
|
+
var promptParts = [
|
|
140
166
|
"[SYSTEM: Memory Gate + Digest]",
|
|
141
167
|
"You are a memory system for an AI Mate (role: " + (mateRole || "assistant") + ").",
|
|
142
168
|
"",
|
|
143
|
-
|
|
144
|
-
|
|
169
|
+
];
|
|
170
|
+
if (job.priorSummary) {
|
|
171
|
+
promptParts.push("Prior conversation context (memory summary so far):");
|
|
172
|
+
promptParts.push(job.priorSummary);
|
|
173
|
+
promptParts.push("");
|
|
174
|
+
}
|
|
175
|
+
promptParts.push("Conversation (" + job.type + "):");
|
|
176
|
+
promptParts.push(job.conversationContent);
|
|
177
|
+
var prompt = promptParts.concat([
|
|
145
178
|
"",
|
|
146
179
|
"STEP 1: Should this be saved to memory?",
|
|
147
180
|
'Answer "no" ONLY if the entire conversation is trivial (e.g. just "hi"/"hello").',
|
|
@@ -171,7 +204,7 @@ function attachMateInteraction(ctx) {
|
|
|
171
204
|
"user_observations: OPTIONAL array. Include ONLY if you noticed meaningful patterns about the USER themselves (not the topic).",
|
|
172
205
|
"Categories: pattern (repeated behavior 2+ times), decision (explicit choice with reasoning), reaction (emotional/attitude signal), preference (tool/style/communication preference).",
|
|
173
206
|
"Omit the field entirely if nothing notable about the user.",
|
|
174
|
-
].join("\n");
|
|
207
|
+
]).join("\n");
|
|
175
208
|
|
|
176
209
|
function handleResult(text) {
|
|
177
210
|
var cleaned = text.trim();
|
|
@@ -225,9 +258,14 @@ function attachMateInteraction(ctx) {
|
|
|
225
258
|
}
|
|
226
259
|
}
|
|
227
260
|
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
261
|
+
// Skip summary update for failed parses -- the fallback digest would degrade quality
|
|
262
|
+
if (digestObj.topic !== "parse_failed") {
|
|
263
|
+
updateMemorySummary(job.mateCtx, job.mateId, digestObj);
|
|
264
|
+
maybeSynthesizeUserProfile(job.mateCtx, job.mateId);
|
|
265
|
+
if (job.onDone) job.onDone();
|
|
266
|
+
} else {
|
|
267
|
+
if (job.onError) job.onError(new Error("parse_failed"));
|
|
268
|
+
}
|
|
231
269
|
processDigestQueue();
|
|
232
270
|
}
|
|
233
271
|
|
|
@@ -249,7 +287,7 @@ function attachMateInteraction(ctx) {
|
|
|
249
287
|
console.error("[digest-worker] Error:", err);
|
|
250
288
|
_digestWorker = null;
|
|
251
289
|
_digestWorkerTurns = 0;
|
|
252
|
-
if (job.
|
|
290
|
+
if (job.onError) job.onError(err);
|
|
253
291
|
processDigestQueue();
|
|
254
292
|
},
|
|
255
293
|
});
|
|
@@ -265,11 +303,11 @@ function attachMateInteraction(ctx) {
|
|
|
265
303
|
onError: function (err) {
|
|
266
304
|
console.error("[digest-worker] Create error:", err);
|
|
267
305
|
_digestWorker = null;
|
|
268
|
-
if (job.
|
|
306
|
+
if (job.onError) job.onError(err);
|
|
269
307
|
processDigestQueue();
|
|
270
308
|
},
|
|
271
|
-
}).then(function (ws) { _digestWorker = ws; _digestWorkerTurns = 1; }).catch(function () {
|
|
272
|
-
if (job.
|
|
309
|
+
}).then(function (ws) { _digestWorker = ws; _digestWorkerTurns = 1; }).catch(function (err) {
|
|
310
|
+
if (job.onError) job.onError(err || new Error("digest worker creation failed"));
|
|
273
311
|
processDigestQueue();
|
|
274
312
|
});
|
|
275
313
|
}
|
|
@@ -303,10 +341,14 @@ function attachMateInteraction(ctx) {
|
|
|
303
341
|
type: "mention",
|
|
304
342
|
conversationContent: conversationContent,
|
|
305
343
|
onDone: function () { mentionSession._digesting = false; },
|
|
344
|
+
onError: function () { mentionSession._digesting = false; },
|
|
306
345
|
});
|
|
307
346
|
}
|
|
308
347
|
|
|
309
|
-
// Digest DM turn for mate projects - uses shared digest worker
|
|
348
|
+
// Digest DM turn for mate projects - uses shared digest worker.
|
|
349
|
+
// Delta-based: only collects new turns since the last successful digest.
|
|
350
|
+
// Concurrency debounce: turns that arrive while a digest is in-flight
|
|
351
|
+
// are naturally batched into the next flush.
|
|
310
352
|
var _dmDigestPending = false;
|
|
311
353
|
function digestDmTurn(session, responsePreview) {
|
|
312
354
|
if (!isMate || _dmDigestPending) return;
|
|
@@ -314,11 +356,26 @@ function attachMateInteraction(ctx) {
|
|
|
314
356
|
var mateCtx = matesModule.buildMateCtx(projectOwnerId);
|
|
315
357
|
if (!matesModule.isMate(mateCtx, mateId)) return;
|
|
316
358
|
|
|
317
|
-
//
|
|
359
|
+
// Track digest index per session so switching sessions doesn't misalign.
|
|
360
|
+
// On resumed sessions (after restart), recover index from the last
|
|
361
|
+
// digest_checkpoint entry in history so undigested turns aren't lost.
|
|
362
|
+
if (typeof session._dmLastDigestedIndex !== "number") {
|
|
363
|
+
session._dmLastDigestedIndex = 0;
|
|
364
|
+
for (var ci = session.history.length - 1; ci >= 0; ci--) {
|
|
365
|
+
if (session.history[ci].type === "digest_checkpoint") {
|
|
366
|
+
session._dmLastDigestedIndex = session.history[ci].digestedIndex;
|
|
367
|
+
break;
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
// Collect only new turns since the last successful digest
|
|
318
373
|
var conversationParts = [];
|
|
319
374
|
var totalLen = 0;
|
|
320
375
|
var CONV_CAP = 6000;
|
|
321
|
-
|
|
376
|
+
var startIndex = session._dmLastDigestedIndex;
|
|
377
|
+
for (var hi = startIndex; hi < session.history.length; hi++) {
|
|
378
|
+
if (totalLen >= CONV_CAP) break;
|
|
322
379
|
var entry = session.history[hi];
|
|
323
380
|
if (entry.type === "user_message" && entry.text) {
|
|
324
381
|
var uText = entry.text;
|
|
@@ -335,8 +392,8 @@ function attachMateInteraction(ctx) {
|
|
|
335
392
|
conversationParts.push("Mate: " + aText);
|
|
336
393
|
totalLen += aText.length;
|
|
337
394
|
}
|
|
338
|
-
if (totalLen >= CONV_CAP) break;
|
|
339
395
|
}
|
|
396
|
+
// If the latest response hasn't landed in history yet, append it
|
|
340
397
|
var lastResponseText = responsePreview || "";
|
|
341
398
|
if (lastResponseText && conversationParts.length > 0) {
|
|
342
399
|
var lastPart = conversationParts[conversationParts.length - 1];
|
|
@@ -362,14 +419,34 @@ function attachMateInteraction(ctx) {
|
|
|
362
419
|
});
|
|
363
420
|
}
|
|
364
421
|
|
|
422
|
+
// Read existing summary to give the digest worker context for delta content
|
|
423
|
+
var priorSummary = "";
|
|
424
|
+
try {
|
|
425
|
+
if (fs.existsSync(summaryFile)) {
|
|
426
|
+
priorSummary = fs.readFileSync(summaryFile, "utf8").trim();
|
|
427
|
+
}
|
|
428
|
+
} catch (e) {}
|
|
429
|
+
|
|
365
430
|
_dmDigestPending = true;
|
|
431
|
+
var snapshotIndex = session.history.length;
|
|
366
432
|
|
|
367
433
|
enqueueDigest({
|
|
368
434
|
mateCtx: mateCtx,
|
|
369
435
|
mateId: mateId,
|
|
370
436
|
type: "dm",
|
|
437
|
+
priorSummary: priorSummary || "",
|
|
371
438
|
conversationContent: conversationParts.join("\n"),
|
|
372
|
-
onDone: function () {
|
|
439
|
+
onDone: function () {
|
|
440
|
+
session._dmLastDigestedIndex = snapshotIndex;
|
|
441
|
+
// Persist checkpoint so resumed sessions know where to continue
|
|
442
|
+
var checkpoint = { type: "digest_checkpoint", digestedIndex: snapshotIndex };
|
|
443
|
+
session.history.push(checkpoint);
|
|
444
|
+
sm.appendToSessionFile(session, checkpoint);
|
|
445
|
+
_dmDigestPending = false;
|
|
446
|
+
},
|
|
447
|
+
onError: function () {
|
|
448
|
+
_dmDigestPending = false;
|
|
449
|
+
},
|
|
373
450
|
});
|
|
374
451
|
}
|
|
375
452
|
|
|
@@ -445,6 +522,7 @@ function attachMateInteraction(ctx) {
|
|
|
445
522
|
}
|
|
446
523
|
|
|
447
524
|
session._mentionInProgress = true;
|
|
525
|
+
session._mentionActiveMateId = msg.mateId;
|
|
448
526
|
|
|
449
527
|
// Send mention start indicator
|
|
450
528
|
sendToSession(session.localId, {
|
|
@@ -478,6 +556,7 @@ function attachMateInteraction(ctx) {
|
|
|
478
556
|
},
|
|
479
557
|
onDone: function (fullText) {
|
|
480
558
|
session._mentionInProgress = false;
|
|
559
|
+
session._mentionActiveMateId = null;
|
|
481
560
|
|
|
482
561
|
// Save mention response to session history
|
|
483
562
|
var mentionResponseEntry = {
|
|
@@ -504,6 +583,10 @@ function attachMateInteraction(ctx) {
|
|
|
504
583
|
sendToSession(session.localId, { type: "mention_done", mateId: msg.mateId });
|
|
505
584
|
send({ type: "mention_processing", mateId: msg.mateId, active: false });
|
|
506
585
|
|
|
586
|
+
if (isMate) {
|
|
587
|
+
notifyMentionResponse(session, msg.mateId, mateName, fullText);
|
|
588
|
+
}
|
|
589
|
+
|
|
507
590
|
// Check if the mate wrote a debate brief during this turn
|
|
508
591
|
ctx.checkForDmDebateBrief(session, msg.mateId, mateCtx);
|
|
509
592
|
|
|
@@ -512,6 +595,7 @@ function attachMateInteraction(ctx) {
|
|
|
512
595
|
},
|
|
513
596
|
onError: function (errMsg) {
|
|
514
597
|
session._mentionInProgress = false;
|
|
598
|
+
session._mentionActiveMateId = null;
|
|
515
599
|
// Clean up dead session
|
|
516
600
|
if (session._mentionSessions && session._mentionSessions[msg.mateId]) {
|
|
517
601
|
delete session._mentionSessions[msg.mateId];
|
|
@@ -560,6 +644,7 @@ function attachMateInteraction(ctx) {
|
|
|
560
644
|
|
|
561
645
|
// Create new persistent mention session
|
|
562
646
|
sdk.createMentionSession({
|
|
647
|
+
vendor: mate.vendor || null,
|
|
563
648
|
claudeMd: claudeMd,
|
|
564
649
|
initialContext: mentionContext,
|
|
565
650
|
initialMessage: mentionFullInput,
|
|
@@ -614,6 +699,7 @@ function attachMateInteraction(ctx) {
|
|
|
614
699
|
}
|
|
615
700
|
}).catch(function (err) {
|
|
616
701
|
session._mentionInProgress = false;
|
|
702
|
+
session._mentionActiveMateId = null;
|
|
617
703
|
console.error("[mention] Failed to create session for mate " + msg.mateId + ":", err.message || err);
|
|
618
704
|
sendToSession(session.localId, { type: "mention_error", mateId: msg.mateId, error: "Failed to create mention session." });
|
|
619
705
|
});
|
package/lib/project-mcp.js
CHANGED
|
@@ -73,6 +73,7 @@ function attachMcp(ctx) {
|
|
|
73
73
|
|
|
74
74
|
function handleToolResult(msg) {
|
|
75
75
|
var callId = msg.callId;
|
|
76
|
+
console.log("[mcp-bridge] Tool result received: " + callId);
|
|
76
77
|
var pending = _pendingCalls[callId];
|
|
77
78
|
if (!pending) return;
|
|
78
79
|
if (pending.timer) clearTimeout(pending.timer);
|
|
@@ -82,6 +83,7 @@ function attachMcp(ctx) {
|
|
|
82
83
|
|
|
83
84
|
function handleToolError(msg) {
|
|
84
85
|
var callId = msg.callId;
|
|
86
|
+
console.log("[mcp-bridge] Tool error received: " + callId + " error=" + (msg.error || "unknown"));
|
|
85
87
|
var pending = _pendingCalls[callId];
|
|
86
88
|
if (!pending) return;
|
|
87
89
|
if (pending.timer) clearTimeout(pending.timer);
|
|
@@ -223,12 +225,14 @@ function attachMcp(ctx) {
|
|
|
223
225
|
var callId = "mc_" + Date.now() + "_" + crypto.randomUUID().slice(0, 8);
|
|
224
226
|
|
|
225
227
|
var timer = setTimeout(function () {
|
|
228
|
+
console.log("[mcp-bridge] Tool call TIMEOUT: " + callId + " server=" + serverName + " tool=" + toolName);
|
|
226
229
|
delete _pendingCalls[callId];
|
|
227
230
|
reject(new Error("MCP tool call timed out after " + (TOOL_TIMEOUT_MS / 1000) + "s"));
|
|
228
231
|
}, TOOL_TIMEOUT_MS);
|
|
229
232
|
|
|
230
233
|
_pendingCalls[callId] = { resolve: resolve, reject: reject, timer: timer };
|
|
231
234
|
|
|
235
|
+
console.log("[mcp-bridge] Sending tool call: " + callId + " server=" + serverName + " tool=" + toolName);
|
|
232
236
|
sendTo(extWs, {
|
|
233
237
|
type: "mcp_tool_call",
|
|
234
238
|
callId: callId,
|
|
@@ -60,6 +60,15 @@ var formatters = {
|
|
|
60
60
|
body: data.preview || "Sent you a message",
|
|
61
61
|
};
|
|
62
62
|
},
|
|
63
|
+
|
|
64
|
+
mention_response: function (data) {
|
|
65
|
+
return {
|
|
66
|
+
type: "mention_response",
|
|
67
|
+
title: data.title || "Mention response ready",
|
|
68
|
+
body: data.preview || "",
|
|
69
|
+
meta: { avatarMateId: data.avatarMateId || null },
|
|
70
|
+
};
|
|
71
|
+
},
|
|
63
72
|
};
|
|
64
73
|
|
|
65
74
|
// ========================================================
|
package/lib/project-sessions.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
var fs = require("fs");
|
|
2
2
|
var path = require("path");
|
|
3
3
|
var { execFileSync } = require("child_process");
|
|
4
|
+
var { CODEX_DEFAULTS, getCodexConfig } = require("./codex-defaults");
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
7
|
* Attach session management, config, project management, and mid-section
|
|
@@ -13,7 +14,7 @@ var { execFileSync } = require("child_process");
|
|
|
13
14
|
* opts, usersModule, userPresence, matesModule, pushModule,
|
|
14
15
|
* getSessionForWs, getLinuxUserForSession, getOsUserInfoForWs,
|
|
15
16
|
* hydrateImageRefs, onProcessingChanged, broadcastPresence,
|
|
16
|
-
*
|
|
17
|
+
* adapter, getProjectList, getProjectCount, getScheduleCount,
|
|
17
18
|
* moveScheduleToProject, moveAllSchedulesToProject, getHubSchedules,
|
|
18
19
|
* fetchVersion, isNewer, onCreateWorktree, IGNORED_DIRS,
|
|
19
20
|
* scheduleMessage, cancelScheduledMessage,
|
|
@@ -46,7 +47,7 @@ function attachSessions(ctx) {
|
|
|
46
47
|
var hydrateImageRefs = ctx.hydrateImageRefs;
|
|
47
48
|
var onProcessingChanged = ctx.onProcessingChanged;
|
|
48
49
|
var broadcastPresence = ctx.broadcastPresence;
|
|
49
|
-
var
|
|
50
|
+
var adapter = ctx.adapter;
|
|
50
51
|
var getProjectList = ctx.getProjectList;
|
|
51
52
|
var getProjectCount = ctx.getProjectCount;
|
|
52
53
|
var getScheduleCount = ctx.getScheduleCount;
|
|
@@ -96,6 +97,7 @@ function attachSessions(ctx) {
|
|
|
96
97
|
var sessionOpts = {};
|
|
97
98
|
if (ws._clayUser && usersModule.isMultiUser()) sessionOpts.ownerId = ws._clayUser.id;
|
|
98
99
|
if (msg.sessionVisibility) sessionOpts.sessionVisibility = msg.sessionVisibility;
|
|
100
|
+
if (msg.vendor) sessionOpts.vendor = msg.vendor;
|
|
99
101
|
var newSess = sm.createSession(sessionOpts, ws);
|
|
100
102
|
ws._clayActiveSession = newSess.localId;
|
|
101
103
|
// Apply project-level email defaults to new session
|
|
@@ -153,9 +155,7 @@ function attachSessions(ctx) {
|
|
|
153
155
|
if (!msg.cliSessionId) return true;
|
|
154
156
|
var cliSess = require("./cli-sessions");
|
|
155
157
|
// Try SDK for title first, then fall back to manual parsing
|
|
156
|
-
var titlePromise =
|
|
157
|
-
return sdkMod.getSessionInfo(msg.cliSessionId, { dir: cwd });
|
|
158
|
-
}).then(function(info) {
|
|
158
|
+
var titlePromise = adapter.getSessionInfo(msg.cliSessionId, { dir: cwd }).then(function(info) {
|
|
159
159
|
return (info && info.summary) ? info.summary.substring(0, 100) : null;
|
|
160
160
|
}).catch(function() { return null; });
|
|
161
161
|
|
|
@@ -200,9 +200,7 @@ function attachSessions(ctx) {
|
|
|
200
200
|
}
|
|
201
201
|
} catch (e) {}
|
|
202
202
|
|
|
203
|
-
|
|
204
|
-
return sdkMod.listSessions({ dir: cwd });
|
|
205
|
-
}).then(function(sdkSessions) {
|
|
203
|
+
adapter.listSessions({ dir: cwd }).then(function(sdkSessions) {
|
|
206
204
|
var filtered = sdkSessions.filter(function(s) {
|
|
207
205
|
return !relayIds[s.sessionId];
|
|
208
206
|
}).map(function(s) {
|
|
@@ -283,15 +281,14 @@ function attachSessions(ctx) {
|
|
|
283
281
|
if (msg.id && sm.sessions.has(msg.id) && msg.title) {
|
|
284
282
|
var s = sm.sessions.get(msg.id);
|
|
285
283
|
s.title = String(msg.title).substring(0, 100);
|
|
284
|
+
s.titleManuallySet = true;
|
|
286
285
|
sm.saveSessionFile(s);
|
|
287
286
|
sm.broadcastSessionList();
|
|
288
287
|
// Sync title to SDK session
|
|
289
288
|
if (s.cliSessionId) {
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
});
|
|
294
|
-
}).catch(function() {});
|
|
289
|
+
adapter.renameSession(s.cliSessionId, s.title, { dir: cwd }).catch(function(e) {
|
|
290
|
+
console.error("[project] SDK renameSession failed:", e.message);
|
|
291
|
+
});
|
|
295
292
|
}
|
|
296
293
|
}
|
|
297
294
|
return true;
|
|
@@ -381,8 +378,9 @@ function attachSessions(ctx) {
|
|
|
381
378
|
|
|
382
379
|
if (msg.type === "stop") {
|
|
383
380
|
var session = getSessionForWs(ws);
|
|
384
|
-
if (session && session.
|
|
385
|
-
session.
|
|
381
|
+
if (session && session.isProcessing) {
|
|
382
|
+
session.taskStopRequested = true;
|
|
383
|
+
if (session.abortController) session.abortController.abort();
|
|
386
384
|
}
|
|
387
385
|
return true;
|
|
388
386
|
}
|
|
@@ -521,32 +519,34 @@ function attachSessions(ctx) {
|
|
|
521
519
|
return true;
|
|
522
520
|
}
|
|
523
521
|
|
|
522
|
+
// Codex-specific settings (stored on sessionManager, passed to adapter via adapterOptions)
|
|
523
|
+
if (msg.type === "set_codex_approval") {
|
|
524
|
+
sm.codexApproval = msg.approval || CODEX_DEFAULTS.approval;
|
|
525
|
+
send(Object.assign({ type: "codex_config" }, getCodexConfig(sm)));
|
|
526
|
+
return true;
|
|
527
|
+
}
|
|
528
|
+
if (msg.type === "set_codex_sandbox") {
|
|
529
|
+
sm.codexSandbox = msg.sandbox || CODEX_DEFAULTS.sandbox;
|
|
530
|
+
send(Object.assign({ type: "codex_config" }, getCodexConfig(sm)));
|
|
531
|
+
return true;
|
|
532
|
+
}
|
|
533
|
+
if (msg.type === "set_codex_websearch") {
|
|
534
|
+
sm.codexWebSearch = msg.webSearch || CODEX_DEFAULTS.webSearch;
|
|
535
|
+
send(Object.assign({ type: "codex_config" }, getCodexConfig(sm)));
|
|
536
|
+
return true;
|
|
537
|
+
}
|
|
538
|
+
|
|
524
539
|
if (msg.type === "rewind_preview") {
|
|
525
540
|
var session = getSessionForWs(ws);
|
|
526
541
|
if (!session || !session.cliSessionId || !msg.uuid) return true;
|
|
527
|
-
// Reject preview requests while a rewind is executing
|
|
528
542
|
if (session._rewindInProgress) return true;
|
|
529
543
|
|
|
530
544
|
(async function () {
|
|
531
|
-
var result;
|
|
532
545
|
try {
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
var diffs = {};
|
|
536
|
-
var changedFiles = preview.filesChanged || [];
|
|
537
|
-
for (var f = 0; f < changedFiles.length; f++) {
|
|
538
|
-
try {
|
|
539
|
-
diffs[changedFiles[f]] = execFileSync(
|
|
540
|
-
"git", ["diff", "HEAD", "--", changedFiles[f]],
|
|
541
|
-
{ cwd: cwd, encoding: "utf8", timeout: 5000 }
|
|
542
|
-
) || "";
|
|
543
|
-
} catch (e) { diffs[changedFiles[f]] = ""; }
|
|
544
|
-
}
|
|
545
|
-
sendTo(ws, { type: "rewind_preview_result", preview: preview, diffs: diffs, uuid: msg.uuid });
|
|
546
|
+
var r = await sdk.rewindPreview(session, msg.uuid);
|
|
547
|
+
sendTo(ws, { type: "rewind_preview_result", preview: r.preview, diffs: r.diffs, uuid: msg.uuid, chatOnly: r.chatOnly || false });
|
|
546
548
|
} catch (err) {
|
|
547
549
|
sendTo(ws, { type: "rewind_error", text: "Failed to preview rewind: " + err.message });
|
|
548
|
-
} finally {
|
|
549
|
-
if (result && result.isTemp) result.cleanup();
|
|
550
550
|
}
|
|
551
551
|
})();
|
|
552
552
|
return true;
|
|
@@ -564,12 +564,10 @@ function attachSessions(ctx) {
|
|
|
564
564
|
var mode = msg.mode || "both";
|
|
565
565
|
|
|
566
566
|
(async function () {
|
|
567
|
-
var result;
|
|
568
567
|
try {
|
|
569
|
-
// File restoration (
|
|
568
|
+
// File restoration (delegated to adapter via sdk-bridge)
|
|
570
569
|
if (mode !== "chat") {
|
|
571
|
-
|
|
572
|
-
await result.query.rewindFiles(msg.uuid, { dryRun: false });
|
|
570
|
+
await sdk.rewindExecuteFiles(session, msg.uuid);
|
|
573
571
|
}
|
|
574
572
|
|
|
575
573
|
// Conversation rollback (skip for files-only mode)
|
|
@@ -582,6 +580,14 @@ function attachSessions(ctx) {
|
|
|
582
580
|
}
|
|
583
581
|
}
|
|
584
582
|
|
|
583
|
+
// Count turns to roll back BEFORE trimming local history
|
|
584
|
+
var turnsToRollBack = 0;
|
|
585
|
+
if (targetIdx >= 0) {
|
|
586
|
+
for (var ri = targetIdx; ri < session.messageUUIDs.length; ri++) {
|
|
587
|
+
if (session.messageUUIDs[ri].type === "user") turnsToRollBack++;
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
|
|
585
591
|
if (targetIdx >= 0) {
|
|
586
592
|
var trimTo = session.messageUUIDs[targetIdx].historyIndex;
|
|
587
593
|
for (var k = trimTo - 1; k >= 0; k--) {
|
|
@@ -592,6 +598,19 @@ function attachSessions(ctx) {
|
|
|
592
598
|
}
|
|
593
599
|
session.history = session.history.slice(0, trimTo);
|
|
594
600
|
session.messageUUIDs = session.messageUUIDs.slice(0, targetIdx);
|
|
601
|
+
// Reset digest checkpoint if it points past the trimmed history
|
|
602
|
+
if (typeof session._dmLastDigestedIndex === "number" && session._dmLastDigestedIndex > trimTo) {
|
|
603
|
+
session._dmLastDigestedIndex = trimTo;
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
// Notify adapter of conversation rollback (e.g. Codex thread/rollback)
|
|
608
|
+
if (turnsToRollBack > 0) {
|
|
609
|
+
try {
|
|
610
|
+
await sdk.rollbackConversation(session, turnsToRollBack);
|
|
611
|
+
} catch (rbErr) {
|
|
612
|
+
console.error("[project-sessions] conversation rollback failed:", rbErr.message || rbErr);
|
|
613
|
+
}
|
|
595
614
|
}
|
|
596
615
|
|
|
597
616
|
var kept = session.messageUUIDs;
|
|
@@ -622,7 +641,6 @@ function attachSessions(ctx) {
|
|
|
622
641
|
sendTo(ws, { type: "rewind_error", text: "Rewind failed: " + err.message });
|
|
623
642
|
} finally {
|
|
624
643
|
session._rewindInProgress = false;
|
|
625
|
-
if (result && result.isTemp) result.cleanup();
|
|
626
644
|
}
|
|
627
645
|
})();
|
|
628
646
|
return true;
|
|
@@ -634,22 +652,47 @@ function attachSessions(ctx) {
|
|
|
634
652
|
sendTo(ws, { type: "error", text: "Cannot fork: no CLI session" });
|
|
635
653
|
return true;
|
|
636
654
|
}
|
|
637
|
-
var forkCliId = session.cliSessionId;
|
|
638
655
|
var forkTitle = (session.title || "New Session") + " (fork)";
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
return cliSess.readCliSessionHistory(cwd, result.sessionId).then(function(history) {
|
|
647
|
-
var forked = sm.resumeSession(result.sessionId, { history: history, title: forkTitle }, ws);
|
|
648
|
-
if (forked) {
|
|
649
|
-
ws._clayActiveSession = forked.localId;
|
|
650
|
-
sendTo(ws, { type: "fork_complete", sessionId: forked.localId });
|
|
656
|
+
|
|
657
|
+
sdk.forkSession(session, msg.uuid).then(function(result) {
|
|
658
|
+
if (result.useLocalHistory) {
|
|
659
|
+
// Copy local history up to the target UUID
|
|
660
|
+
var targetIdx = -1;
|
|
661
|
+
for (var fi = 0; fi < session.messageUUIDs.length; fi++) {
|
|
662
|
+
if (session.messageUUIDs[fi].uuid === msg.uuid) { targetIdx = fi; break; }
|
|
651
663
|
}
|
|
652
|
-
|
|
664
|
+
var forkHistory = [];
|
|
665
|
+
if (targetIdx >= 0) {
|
|
666
|
+
var trimTo = session.messageUUIDs[targetIdx].historyIndex;
|
|
667
|
+
forkHistory = session.history.slice(0, trimTo);
|
|
668
|
+
} else {
|
|
669
|
+
forkHistory = session.history.slice();
|
|
670
|
+
}
|
|
671
|
+
var forked = sm.createSession({ vendor: session.vendor, ownerId: session.ownerId || null }, ws);
|
|
672
|
+
forked.cliSessionId = result.sessionId;
|
|
673
|
+
forked.title = forkTitle;
|
|
674
|
+
forked.history = forkHistory;
|
|
675
|
+
forked.messageUUIDs = [];
|
|
676
|
+
for (var hi = 0; hi < forkHistory.length; hi++) {
|
|
677
|
+
if (forkHistory[hi].type === "message_uuid") {
|
|
678
|
+
forked.messageUUIDs.push({ uuid: forkHistory[hi].uuid, type: forkHistory[hi].messageType, historyIndex: hi });
|
|
679
|
+
}
|
|
680
|
+
}
|
|
681
|
+
sm.saveSessionFile(forked);
|
|
682
|
+
sm.switchSession(forked.localId, ws, hydrateImageRefs);
|
|
683
|
+
sendTo(ws, { type: "fork_complete", sessionId: forked.localId });
|
|
684
|
+
sm.broadcastSessionList();
|
|
685
|
+
} else {
|
|
686
|
+
// Read history from CLI session files
|
|
687
|
+
var cliSess = require("./cli-sessions");
|
|
688
|
+
return cliSess.readCliSessionHistory(cwd, result.sessionId).then(function(history) {
|
|
689
|
+
var forked = sm.resumeSession(result.sessionId, { history: history, title: forkTitle }, ws);
|
|
690
|
+
if (forked) {
|
|
691
|
+
ws._clayActiveSession = forked.localId;
|
|
692
|
+
sendTo(ws, { type: "fork_complete", sessionId: forked.localId });
|
|
693
|
+
}
|
|
694
|
+
});
|
|
695
|
+
}
|
|
653
696
|
}).catch(function(e) {
|
|
654
697
|
sendTo(ws, { type: "error", text: "Fork failed: " + (e.message || e) });
|
|
655
698
|
});
|
|
@@ -23,7 +23,7 @@ var fs = require("fs");
|
|
|
23
23
|
* scheduleMessage, cancelScheduledMessage,
|
|
24
24
|
* loadContextSources, saveContextSources,
|
|
25
25
|
* digestDmTurn, gateMemory,
|
|
26
|
-
*
|
|
26
|
+
* adapter - YOKE adapter instance
|
|
27
27
|
*/
|
|
28
28
|
function attachUserMessage(ctx) {
|
|
29
29
|
var cwd = ctx.cwd;
|
|
@@ -69,7 +69,7 @@ function attachUserMessage(ctx) {
|
|
|
69
69
|
var loadContextSources = ctx.loadContextSources;
|
|
70
70
|
var saveContextSources = ctx.saveContextSources;
|
|
71
71
|
|
|
72
|
-
var
|
|
72
|
+
var adapter = ctx.adapter;
|
|
73
73
|
var _email = ctx._email;
|
|
74
74
|
|
|
75
75
|
// --------------- Sticky notes ---------------
|
|
@@ -292,6 +292,13 @@ function attachUserMessage(ctx) {
|
|
|
292
292
|
var session = getSessionForWs(ws);
|
|
293
293
|
if (!session) return true;
|
|
294
294
|
|
|
295
|
+
// Bind vendor to session on first message (if not already set)
|
|
296
|
+
if (!session.vendor && msg.vendor) {
|
|
297
|
+
session.vendor = msg.vendor;
|
|
298
|
+
sm.saveSessionFile(session);
|
|
299
|
+
sm.broadcastSessionList();
|
|
300
|
+
}
|
|
301
|
+
|
|
295
302
|
// Backfill ownerId for legacy sessions restored without one (multi-user only)
|
|
296
303
|
if (!session.ownerId && ws._clayUser && usersModule.isMultiUser()) {
|
|
297
304
|
session.ownerId = ws._clayUser.id;
|
|
@@ -337,11 +344,9 @@ function attachUserMessage(ctx) {
|
|
|
337
344
|
sm.broadcastSessionList();
|
|
338
345
|
// Sync auto-title to SDK
|
|
339
346
|
if (session.cliSessionId) {
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
});
|
|
344
|
-
}).catch(function() {});
|
|
347
|
+
adapter.renameSession(session.cliSessionId, session.title, { dir: cwd }).catch(function(e) {
|
|
348
|
+
console.error("[project] SDK renameSession failed:", e.message);
|
|
349
|
+
});
|
|
345
350
|
}
|
|
346
351
|
}
|
|
347
352
|
|