clay-server 4.1.0-beta.6 → 4.1.0-beta.7
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/LICENSE +661 -21
- package/LICENSES/MIT-legacy.txt +21 -0
- package/LICENSES/README.md +7 -0
- package/README.md +2 -2
- package/bin/cli.js +0 -2
- package/lib/daemon.js +0 -2
- package/lib/default-ai-runtime.js +117 -0
- package/lib/git-cli.js +1 -0
- package/lib/issue-work-session.js +10 -0
- package/lib/issues-comments.js +40 -0
- package/lib/issues-commit-verification.js +30 -0
- package/lib/issues-mcp-server.js +27 -0
- package/lib/issues-schema.js +119 -0
- package/lib/issues-service.js +339 -0
- package/lib/issues-store.js +210 -0
- package/lib/model-entry.js +15 -0
- package/lib/project-capsule-turn.js +1 -0
- package/lib/project-connection.js +2 -0
- package/lib/project-delegated-session.js +1 -0
- package/lib/project-filesystem.js +10 -10
- package/lib/project-issue-launch.js +154 -0
- package/lib/project-issues.js +58 -0
- package/lib/project-logs-mcp-server.js +8 -12
- package/lib/project-message-delivery.js +4 -2
- package/lib/project-models.js +71 -3
- package/lib/project-pending-message-queue.js +264 -0
- package/lib/project-scheduled-tasks.js +2 -4
- package/lib/project-sessions.js +82 -31
- package/lib/project-tool-llm-runtime.js +48 -0
- package/lib/project-user-message.js +178 -42
- package/lib/project-workspace-query.js +25 -0
- package/lib/project.js +102 -18
- package/lib/public/app.js +38 -4
- package/lib/public/css/default-ai.css +33 -0
- package/lib/public/css/input.css +89 -5
- package/lib/public/css/issues.css +101 -0
- package/lib/public/css/messages.css +19 -0
- package/lib/public/css/pending-message-queue.css +187 -0
- package/lib/public/css/scheduled-tasks.css +3 -4
- package/lib/public/index.html +25 -17
- package/lib/public/modules/app-connection.js +3 -0
- package/lib/public/modules/app-cursors.js +29 -12
- package/lib/public/modules/app-message-router.js +8 -0
- package/lib/public/modules/app-messages.js +12 -3
- package/lib/public/modules/clay-file-links.js +111 -0
- package/lib/public/modules/clay-issue-links.js +131 -0
- package/lib/public/modules/clay-log-links.js +2 -0
- package/lib/public/modules/cursor-sharing-preference.js +38 -0
- package/lib/public/modules/default-ai.js +377 -0
- package/lib/public/modules/filebrowser.js +43 -2
- package/lib/public/modules/input.js +7 -17
- package/lib/public/modules/issues-context.js +117 -0
- package/lib/public/modules/issues-render.js +29 -0
- package/lib/public/modules/issues.js +270 -0
- package/lib/public/modules/markdown.js +4 -0
- package/lib/public/modules/message-delivery.js +5 -3
- package/lib/public/modules/pending-message-image-editor.js +143 -0
- package/lib/public/modules/pending-message-queue-model.js +22 -0
- package/lib/public/modules/pending-message-queue.js +449 -0
- package/lib/public/modules/scheduled-task-engine.js +0 -50
- package/lib/public/modules/scheduled-tasks.js +7 -16
- package/lib/public/modules/sidebar-mobile.js +3 -0
- package/lib/public/modules/sticky-note-markdown.js +1 -0
- package/lib/public/modules/terminal.js +2 -0
- package/lib/public/modules/tool-palette-order.js +1 -0
- package/lib/public/style.css +2 -0
- package/lib/sdk-bridge.js +34 -11
- package/lib/server-cursor-sharing.js +65 -0
- package/lib/server-default-ai.js +265 -0
- package/lib/server-home-chat.js +29 -9
- package/lib/server-home-clay-entry.js +4 -1
- package/lib/server-home-debate-planning.js +32 -14
- package/lib/server-home-mate-creation.js +30 -11
- package/lib/server-home-models.js +14 -6
- package/lib/server-issue-links.js +18 -0
- package/lib/server.js +41 -0
- package/lib/session-notes-mcp-server.js +1 -1
- package/lib/session-search.js +4 -1
- package/lib/tool-llm.js +1 -0
- package/lib/users-default-ai-preferences.js +80 -0
- package/lib/users-preferences.js +35 -0
- package/lib/users.js +10 -0
- package/lib/workspace-query-projection.js +29 -0
- package/lib/workspace-query-service.js +19 -29
- package/lib/ws-schema.js +36 -1
- package/lib/yoke/adapters/claude-worker.js +4 -2
- package/lib/yoke/adapters/claude.js +5 -1
- package/lib/yoke/package.json +1 -1
- package/package.json +5 -6
- package/bin/claude-relay.js +0 -6
|
@@ -21,6 +21,7 @@ var fs = require("fs");
|
|
|
21
21
|
* sendExtensionCommandAny, requestTabContext,
|
|
22
22
|
* startFileWatch, stopFileWatch,
|
|
23
23
|
* scheduleMessage, cancelScheduledMessage,
|
|
24
|
+
* pendingMessageQueue,
|
|
24
25
|
* loadContextSources, saveContextSources,
|
|
25
26
|
* digestDmTurn, gateMemory,
|
|
26
27
|
* adapter - YOKE adapter instance
|
|
@@ -42,7 +43,7 @@ function attachUserMessage(ctx) {
|
|
|
42
43
|
var sendTo = ctx.sendTo;
|
|
43
44
|
var sendToSession = ctx.sendToSession;
|
|
44
45
|
var sendToSessionOthers = ctx.sendToSessionOthers;
|
|
45
|
-
var messageDelivery = createProjectMessageDelivery(sendTo);
|
|
46
|
+
var messageDelivery = createProjectMessageDelivery(sendTo, slug);
|
|
46
47
|
|
|
47
48
|
var clients = ctx.clients;
|
|
48
49
|
var opts = ctx.opts;
|
|
@@ -71,6 +72,7 @@ function attachUserMessage(ctx) {
|
|
|
71
72
|
var scheduleMessage = ctx.scheduleMessage;
|
|
72
73
|
var cancelScheduledMessage = ctx.cancelScheduledMessage;
|
|
73
74
|
var sendScheduledMessageNow = ctx.sendScheduledMessageNow;
|
|
75
|
+
var pendingMessageQueue = ctx.pendingMessageQueue;
|
|
74
76
|
|
|
75
77
|
var loadContextSources = ctx.loadContextSources;
|
|
76
78
|
var saveContextSources = ctx.saveContextSources;
|
|
@@ -99,7 +101,27 @@ function attachUserMessage(ctx) {
|
|
|
99
101
|
|
|
100
102
|
// --------------- Main handler ---------------
|
|
101
103
|
|
|
102
|
-
function handleUserMessage(ws, msg) {
|
|
104
|
+
function handleUserMessage(ws, msg, forcedSession, internal) {
|
|
105
|
+
if (msg.type === "pending_message_get" || msg.type === "pending_message_edit" || msg.type === "pending_message_cancel" || msg.type === "pending_message_reorder" || msg.type === "pending_message_resume") {
|
|
106
|
+
var queueSession = forcedSession || getSessionForWs(ws);
|
|
107
|
+
if (!queueSession || !pendingMessageQueue) return true;
|
|
108
|
+
var queueActor = ws && ws._clayUser;
|
|
109
|
+
var queueResult;
|
|
110
|
+
if (msg.projectSlug !== slug) queueResult = { ok: false, error: "Project changed", sessionId: queueSession.localId, projectSlug: slug };
|
|
111
|
+
else if (msg.type === "pending_message_resume") {
|
|
112
|
+
queueResult = pendingMessageQueue.resume(queueSession, queueActor, msg.revision, msg.sessionId);
|
|
113
|
+
if (queueResult.ok && !queueResult.paused) {
|
|
114
|
+
queueSession._pendingMessageDrainPaused = false;
|
|
115
|
+
setImmediate(function () { consumePendingMessage(queueSession); });
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
else if (msg.type === "pending_message_get") queueResult = pendingMessageQueue.hydrate(queueSession, queueActor);
|
|
119
|
+
else if (msg.type === "pending_message_edit") queueResult = pendingMessageQueue.edit(queueSession, queueActor, msg.id, msg.revision, msg.sessionId, msg.message || {});
|
|
120
|
+
else if (msg.type === "pending_message_cancel") queueResult = pendingMessageQueue.cancel(queueSession, queueActor, msg.id, msg.revision, msg.sessionId);
|
|
121
|
+
else queueResult = pendingMessageQueue.reorder(queueSession, queueActor, msg.ids || [], msg.revision, msg.sessionId);
|
|
122
|
+
sendTo(ws, { type: "pending_message_result", requestId: msg.requestId || null, result: queueResult });
|
|
123
|
+
return true;
|
|
124
|
+
}
|
|
103
125
|
// --- Sticky notes ---
|
|
104
126
|
if (msg.type === "note_create") {
|
|
105
127
|
var note = nm.create(msg);
|
|
@@ -303,17 +325,29 @@ function attachUserMessage(ctx) {
|
|
|
303
325
|
if (msg.type !== "message") return false;
|
|
304
326
|
if (!msg.text && (!msg.images || msg.images.length === 0) && (!msg.pastes || msg.pastes.length === 0)) return true;
|
|
305
327
|
|
|
306
|
-
var session = getSessionForWs(ws);
|
|
328
|
+
var session = forcedSession || getSessionForWs(ws);
|
|
307
329
|
if (!session) return true;
|
|
330
|
+
var pendingInternal = internal && internal.pendingConsumed ? internal : null;
|
|
331
|
+
function finishPendingEarly(outcome, reason) {
|
|
332
|
+
if (pendingInternal && typeof pendingInternal.complete === "function") pendingInternal.complete(outcome, reason);
|
|
333
|
+
}
|
|
308
334
|
|
|
309
335
|
if (session.loopInterviewHandoff && session.loopInterviewHandoff.state === "starting") {
|
|
310
336
|
sendTo(ws, { type: "error", text: "Wait for the approved Loop handoff to finish." });
|
|
337
|
+
finishPendingEarly("release", "Loop handoff is still starting");
|
|
311
338
|
return true;
|
|
312
339
|
}
|
|
313
340
|
|
|
341
|
+
var queuedReplay = pendingMessageQueue && pendingMessageQueue.inspect(session, ws && ws._clayUser, msg.clientMessageId);
|
|
342
|
+
if (!pendingInternal && queuedReplay) {
|
|
343
|
+
messageDelivery.acknowledge(ws, { clientMessageId: queuedReplay.clientMessageId }, null, session);
|
|
344
|
+
sendTo(ws, { type: "message_queued", clientMessageId: queuedReplay.clientMessageId, sessionId: session.localId, projectSlug: slug, state: queuedReplay.state, paused: pendingMessageQueue.isPaused(session), revision: pendingMessageQueue.getRevision() });
|
|
345
|
+
return true;
|
|
346
|
+
}
|
|
314
347
|
var deliveryReceipt = messageDelivery.inspect(ws, session, msg);
|
|
315
348
|
if (deliveryReceipt.duplicate) {
|
|
316
|
-
messageDelivery.acknowledge(ws, deliveryReceipt, hydrateImageRefs(deliveryReceipt.recordedMessage));
|
|
349
|
+
messageDelivery.acknowledge(ws, deliveryReceipt, hydrateImageRefs(deliveryReceipt.recordedMessage), session);
|
|
350
|
+
finishPendingEarly(true);
|
|
317
351
|
return true;
|
|
318
352
|
}
|
|
319
353
|
|
|
@@ -330,23 +364,37 @@ function attachUserMessage(ctx) {
|
|
|
330
364
|
type: "error",
|
|
331
365
|
text: "This Split Worker is controlled by its Driver. Send the instruction to the Driver session instead.",
|
|
332
366
|
});
|
|
367
|
+
finishPendingEarly(false, "Pending messages cannot be delivered to a Driver-operated Split Worker");
|
|
368
|
+
return true;
|
|
369
|
+
}
|
|
370
|
+
if (!pendingInternal && pendingMessageQueue && (pendingMessageQueue.hasActive(session) || session.isProcessing || session._queryStarting || session._awaitingTurnResult)) {
|
|
371
|
+
var queuedResult = pendingMessageQueue.admit(session, msg, ws && ws._clayUser);
|
|
372
|
+
if (queuedResult.ok) {
|
|
373
|
+
messageDelivery.acknowledge(ws, deliveryReceipt, null, session);
|
|
374
|
+
sendTo(ws, { type: "message_queued", clientMessageId: msg.clientMessageId || null, sessionId: session.localId, projectSlug: slug, state: "pending", paused: pendingMessageQueue.isPaused(session), revision: pendingMessageQueue.getRevision() });
|
|
375
|
+
} else {
|
|
376
|
+
sendTo(ws, { type: "error", text: queuedResult.error || "Unable to queue message" });
|
|
377
|
+
}
|
|
333
378
|
return true;
|
|
334
379
|
}
|
|
380
|
+
if (!pendingInternal) session._pendingMessageDrainPaused = false;
|
|
335
381
|
if (msg.autonomousRunToken) {
|
|
336
382
|
if (!ctx.autonomousRun || !ctx.autonomousRun.consume(session, msg)) {
|
|
337
383
|
sendTo(ws, { type: "error", text: "This Until complete submission is stale or no longer armed." });
|
|
384
|
+
finishPendingEarly(false, "Until complete submission is stale or no longer armed");
|
|
338
385
|
return true;
|
|
339
386
|
}
|
|
340
387
|
} else if (session.autonomousRun && session.autonomousRun.state === "armed") {
|
|
341
388
|
sendTo(ws, { type: "error", text: "Until complete is still being armed. Wait for its acknowledgement before sending." });
|
|
389
|
+
finishPendingEarly("release", "Until complete is still being armed");
|
|
342
390
|
return true;
|
|
343
391
|
} else if (ctx.autonomousRun) {
|
|
344
392
|
ctx.autonomousRun.onHumanMessage(session, msg);
|
|
345
393
|
}
|
|
346
394
|
var autonomousDeliveryMeta = session.autonomousRun && ["running", "reviewing", "waiting-worker", "waiting-user"].indexOf(session.autonomousRun.state) !== -1
|
|
347
395
|
? { autonomousRunId: session.autonomousRun.id } : undefined;
|
|
348
|
-
if (typeof ctx.beginHumanPairTurn === "function") ctx.beginHumanPairTurn(session);
|
|
349
|
-
if (gitAttribution) gitAttribution.beginTurn(session);
|
|
396
|
+
if (!pendingInternal && typeof ctx.beginHumanPairTurn === "function") ctx.beginHumanPairTurn(session);
|
|
397
|
+
if (!pendingInternal && gitAttribution) gitAttribution.beginTurn(session);
|
|
350
398
|
|
|
351
399
|
// Bind vendor to session on first message (if not already set)
|
|
352
400
|
if (!session.vendor && msg.vendor) {
|
|
@@ -363,7 +411,7 @@ function attachUserMessage(ctx) {
|
|
|
363
411
|
|
|
364
412
|
// Keep any pending scheduled message alive when user sends a regular message
|
|
365
413
|
|
|
366
|
-
var userMsg2 = { type: "user_message", text: msg.text || "" };
|
|
414
|
+
var userMsg2 = { type: "user_message", text: msg.text || "", projectSlug: slug, sessionId: session.localId };
|
|
367
415
|
// Attach sender info for multi-user attribution (backward-compatible: old clients ignore these)
|
|
368
416
|
if (ws._clayUser) {
|
|
369
417
|
userMsg2.from = ws._clayUser.id;
|
|
@@ -391,24 +439,51 @@ function attachUserMessage(ctx) {
|
|
|
391
439
|
}
|
|
392
440
|
if (deliveryReceipt.clientMessageId) userMsg2.clientMessageId = deliveryReceipt.clientMessageId;
|
|
393
441
|
if (!userMsg2._ts) userMsg2._ts = Date.now();
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
session.
|
|
402
|
-
sm.
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
442
|
+
var transcriptRecorded = false;
|
|
443
|
+
var pendingContextPreviews = [];
|
|
444
|
+
function recordTranscript() {
|
|
445
|
+
if (transcriptRecorded) return;
|
|
446
|
+
transcriptRecorded = true;
|
|
447
|
+
if (pendingInternal && typeof ctx.beginHumanPairTurn === "function") ctx.beginHumanPairTurn(session);
|
|
448
|
+
if (pendingInternal && gitAttribution) gitAttribution.beginTurn(session);
|
|
449
|
+
session.history.push(userMsg2);
|
|
450
|
+
sm.appendToSessionFile(session, userMsg2);
|
|
451
|
+
messageDelivery.markRecorded(session, deliveryReceipt, userMsg2);
|
|
452
|
+
if (!pendingInternal) messageDelivery.acknowledge(ws, deliveryReceipt, hydrateImageRefs(userMsg2), session);
|
|
453
|
+
sendToSessionOthers(pendingInternal ? null : ws, session.localId, hydrateImageRefs(userMsg2));
|
|
454
|
+
for (var previewIndex = 0; previewIndex < pendingContextPreviews.length; previewIndex++) recordContextPreview(pendingContextPreviews[previewIndex]);
|
|
455
|
+
recordSessionTitle();
|
|
456
|
+
}
|
|
457
|
+
if (!pendingInternal) recordTranscript();
|
|
458
|
+
|
|
459
|
+
function recordSessionTitle() {
|
|
460
|
+
if (!session.title) {
|
|
461
|
+
session.title = (msg.text || "Image").substring(0, 50);
|
|
462
|
+
sm.saveSessionFile(session);
|
|
463
|
+
sm.broadcastSessionList();
|
|
464
|
+
// Sync auto-title to SDK
|
|
465
|
+
if (session.cliSessionId) {
|
|
466
|
+
adapter.renameSession(session.cliSessionId, session.title, { dir: cwd }).catch(function(e) {
|
|
467
|
+
console.error("[project] SDK renameSession failed:", e.message);
|
|
468
|
+
});
|
|
469
|
+
}
|
|
409
470
|
}
|
|
410
471
|
}
|
|
411
472
|
|
|
473
|
+
function recordContextPreview(previewEntry) {
|
|
474
|
+
session.history.push(previewEntry);
|
|
475
|
+
sendToSession(session.localId, {
|
|
476
|
+
type: "context_preview",
|
|
477
|
+
tab: {
|
|
478
|
+
title: previewEntry.tab.title,
|
|
479
|
+
url: previewEntry.tab.url,
|
|
480
|
+
favIconUrl: previewEntry.tab.favIconUrl,
|
|
481
|
+
screenshotUrl: "/p/" + slug + "/images/" + previewEntry.tab.screenshotFile,
|
|
482
|
+
},
|
|
483
|
+
});
|
|
484
|
+
sm.saveSessionFile(session);
|
|
485
|
+
}
|
|
486
|
+
|
|
412
487
|
var fullText = msg.text || "";
|
|
413
488
|
// Prepend saved image paths so Claude can copy/save them
|
|
414
489
|
if (savedImagePaths.length > 0) {
|
|
@@ -423,16 +498,20 @@ function attachUserMessage(ctx) {
|
|
|
423
498
|
}
|
|
424
499
|
|
|
425
500
|
// Inject pending @mention context so the current agent sees the exchange
|
|
501
|
+
var pendingMentionCount = 0;
|
|
426
502
|
if (session.pendingMentionContexts && session.pendingMentionContexts.length > 0) {
|
|
427
503
|
var mentionPrefix = session.pendingMentionContexts.join("\n\n");
|
|
428
|
-
session.pendingMentionContexts
|
|
504
|
+
pendingMentionCount = session.pendingMentionContexts.length;
|
|
505
|
+
if (!pendingInternal) session.pendingMentionContexts = [];
|
|
429
506
|
fullText = mentionPrefix + "\n\n" + fullText;
|
|
430
507
|
}
|
|
431
508
|
|
|
432
509
|
// Inject one-shot shell results captured from the composer command mode.
|
|
510
|
+
var pendingShellCount = 0;
|
|
433
511
|
if (session.pendingShellContexts && session.pendingShellContexts.length > 0) {
|
|
434
512
|
var shellPrefix = session.pendingShellContexts.join("\n\n");
|
|
435
|
-
session.pendingShellContexts
|
|
513
|
+
pendingShellCount = session.pendingShellContexts.length;
|
|
514
|
+
if (!pendingInternal) session.pendingShellContexts = [];
|
|
436
515
|
fullText = shellPrefix + "\n\n" + fullText;
|
|
437
516
|
}
|
|
438
517
|
if (msg.shellCommandResponse) {
|
|
@@ -444,6 +523,7 @@ function attachUserMessage(ctx) {
|
|
|
444
523
|
var TERM_HEAD_SIZE = 2048; // keep first 2KB for error context
|
|
445
524
|
var TERM_TAIL_SIZE = 6144; // keep last 6KB for recent state
|
|
446
525
|
var ctxSources = loadContextSources(slug, session.localId);
|
|
526
|
+
var pendingTermCursors = {};
|
|
447
527
|
if (ctxSources.length > 0) {
|
|
448
528
|
if (!session._termContextCursors) session._termContextCursors = {};
|
|
449
529
|
var termContextParts = [];
|
|
@@ -463,7 +543,8 @@ function attachUserMessage(ctx) {
|
|
|
463
543
|
lastCursor = Math.max(0, sb.totalBytesWritten - TERM_CONTEXT_MAX);
|
|
464
544
|
}
|
|
465
545
|
var newBytes = sb.totalBytesWritten - lastCursor;
|
|
466
|
-
|
|
546
|
+
if (pendingInternal) pendingTermCursors[termId] = sb.totalBytesWritten;
|
|
547
|
+
else session._termContextCursors[termId] = sb.totalBytesWritten;
|
|
467
548
|
if (newBytes <= 0) continue;
|
|
468
549
|
// Build timestamped delta from chunks
|
|
469
550
|
var deltaChunks = [];
|
|
@@ -543,10 +624,51 @@ function attachUserMessage(ctx) {
|
|
|
543
624
|
return !!_browserTabList[tid];
|
|
544
625
|
});
|
|
545
626
|
|
|
627
|
+
function pendingBlockReason() {
|
|
628
|
+
if (!pendingInternal) return null;
|
|
629
|
+
if (!pendingMessageQueue.isClaimed(session, pendingInternal.item.id)) return "Pending message was cancelled";
|
|
630
|
+
if (pendingMessageQueue.isPaused(session) || session._pendingMessageDrainPaused || session.taskStopRequested) return "Pending message dispatch paused";
|
|
631
|
+
if (typeof ctx.authorizePendingDispatch !== "function" || !ctx.authorizePendingDispatch(session, ws && ws._clayUser, pendingInternal.item)) return "Pending message authorization changed";
|
|
632
|
+
return null;
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
function pendingStillAuthorized() {
|
|
636
|
+
return !pendingBlockReason();
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
function acceptPendingDispatch() {
|
|
640
|
+
if (!pendingInternal) return true;
|
|
641
|
+
var blockReason = pendingBlockReason();
|
|
642
|
+
if (blockReason) {
|
|
643
|
+
rejectPendingDispatch(blockReason);
|
|
644
|
+
return false;
|
|
645
|
+
}
|
|
646
|
+
if (pendingMentionCount && Array.isArray(session.pendingMentionContexts)) session.pendingMentionContexts.splice(0, pendingMentionCount);
|
|
647
|
+
if (pendingShellCount && Array.isArray(session.pendingShellContexts)) session.pendingShellContexts.splice(0, pendingShellCount);
|
|
648
|
+
Object.keys(pendingTermCursors).forEach(function (termId) { session._termContextCursors[termId] = pendingTermCursors[termId]; });
|
|
649
|
+
recordTranscript();
|
|
650
|
+
pendingInternal.complete(true);
|
|
651
|
+
return true;
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
function rejectPendingDispatch(error) {
|
|
655
|
+
if (!pendingInternal) return;
|
|
656
|
+
var reason = error && error.message ? error.message : error || "Pending message dispatch was rejected";
|
|
657
|
+
if (reason === "Pending message dispatch paused") pendingInternal.complete("release", reason);
|
|
658
|
+
else pendingInternal.complete(false, reason);
|
|
659
|
+
}
|
|
660
|
+
|
|
546
661
|
function startFreshQuery(finalText) {
|
|
547
662
|
session._queryStartTs = Date.now();
|
|
548
663
|
console.log("[PERF] project.js: startQuery called, localId=" + session.localId + " t=0ms");
|
|
549
|
-
sdk.startQuery(session, finalText, msg.images, ensureProjectAccessForSession(session))
|
|
664
|
+
var started = sdk.startQuery(session, finalText, msg.images, ensureProjectAccessForSession(session), function () {
|
|
665
|
+
var blockReason = pendingBlockReason();
|
|
666
|
+
if (blockReason) rejectPendingDispatch(blockReason);
|
|
667
|
+
return !blockReason;
|
|
668
|
+
}, acceptPendingDispatch);
|
|
669
|
+
if (pendingInternal && started && typeof started.then === "function") started.then(function (accepted) {
|
|
670
|
+
if (accepted === false) rejectPendingDispatch("Pending message dispatch was rejected");
|
|
671
|
+
}).catch(rejectPendingDispatch);
|
|
550
672
|
}
|
|
551
673
|
|
|
552
674
|
function dispatchToSdk(finalText) {
|
|
@@ -565,16 +687,18 @@ function attachUserMessage(ctx) {
|
|
|
565
687
|
// which used to leave the processing indicator bouncing with no
|
|
566
688
|
// response until the user sent the message again.
|
|
567
689
|
startFreshQuery(finalText);
|
|
568
|
-
}
|
|
690
|
+
} else acceptPendingDispatch();
|
|
569
691
|
} else if (!sdk.pushMessage(session, finalText, msg.images, autonomousDeliveryMeta)) {
|
|
570
692
|
// isProcessing was stuck true with no live query: same recovery.
|
|
571
693
|
startFreshQuery(finalText);
|
|
572
|
-
}
|
|
694
|
+
} else acceptPendingDispatch();
|
|
573
695
|
sm.broadcastSessionList();
|
|
574
696
|
}
|
|
575
697
|
|
|
576
698
|
// Wait for email context, then proceed with browser tab context and dispatch
|
|
577
699
|
emailContextPromise.then(function (emailCtxText) {
|
|
700
|
+
var emailBlockReason = pendingBlockReason();
|
|
701
|
+
if (emailBlockReason) { rejectPendingDispatch(emailBlockReason); return; }
|
|
578
702
|
if (emailCtxText) {
|
|
579
703
|
fullText = emailCtxText + "\n\n" + fullText;
|
|
580
704
|
}
|
|
@@ -586,6 +710,8 @@ function attachUserMessage(ctx) {
|
|
|
586
710
|
return requestTabContext(tabId);
|
|
587
711
|
});
|
|
588
712
|
Promise.all(tabPromises).then(function(results) {
|
|
713
|
+
var tabBlockReason = pendingBlockReason();
|
|
714
|
+
if (tabBlockReason) { rejectPendingDispatch(tabBlockReason); return; }
|
|
589
715
|
var tabContextParts = [];
|
|
590
716
|
var screenshotImages = [];
|
|
591
717
|
|
|
@@ -703,38 +829,48 @@ function attachUserMessage(ctx) {
|
|
|
703
829
|
screenshotFile: ss.file
|
|
704
830
|
}
|
|
705
831
|
};
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
sendToSession(session.localId, {
|
|
709
|
-
type: "context_preview",
|
|
710
|
-
tab: {
|
|
711
|
-
title: ss.tabTitle || "",
|
|
712
|
-
url: ss.tabUrl || "",
|
|
713
|
-
favIconUrl: ss.tabFavIconUrl || "",
|
|
714
|
-
screenshotUrl: "/p/" + slug + "/images/" + ss.file
|
|
715
|
-
}
|
|
716
|
-
});
|
|
832
|
+
if (pendingInternal) pendingContextPreviews.push(previewEntry);
|
|
833
|
+
else recordContextPreview(previewEntry);
|
|
717
834
|
// Add to SDK images for multimodal
|
|
718
835
|
msg.images.push({ mediaType: ss.mediaType, data: ss.data });
|
|
719
836
|
}
|
|
720
|
-
sm.saveSessionFile(session);
|
|
721
837
|
}
|
|
722
838
|
|
|
723
839
|
dispatchToSdk(fullText);
|
|
724
|
-
});
|
|
840
|
+
}).catch(rejectPendingDispatch);
|
|
725
841
|
} else {
|
|
726
842
|
dispatchToSdk(fullText);
|
|
727
843
|
}
|
|
728
844
|
|
|
729
|
-
}); // emailContextPromise.then
|
|
845
|
+
}).catch(rejectPendingDispatch); // emailContextPromise.then
|
|
730
846
|
|
|
731
847
|
return true;
|
|
732
848
|
}
|
|
733
849
|
|
|
850
|
+
function consumePendingMessage(session) {
|
|
851
|
+
if (!pendingMessageQueue) return false;
|
|
852
|
+
var consumed = pendingMessageQueue.consumeOne(session, function (item, complete) {
|
|
853
|
+
var actor = item.actorId && usersModule.findUserById(item.actorId);
|
|
854
|
+
if ((!actor && item.actorId !== "default") || typeof ctx.authorizePendingDispatch !== "function" || !ctx.authorizePendingDispatch(session, actor || null, item)) {
|
|
855
|
+
complete(false, "Pending message authorization changed");
|
|
856
|
+
return true;
|
|
857
|
+
}
|
|
858
|
+
var source = { _clayUser: actor || null, readyState: 1, send: function () {} };
|
|
859
|
+
handleUserMessage(source, copyMessage(item.message), session, { pendingConsumed: true, item: item, complete: complete });
|
|
860
|
+
return true;
|
|
861
|
+
});
|
|
862
|
+
return !!consumed;
|
|
863
|
+
}
|
|
864
|
+
|
|
734
865
|
return {
|
|
735
866
|
handleUserMessage: handleUserMessage,
|
|
867
|
+
consumePendingMessage: consumePendingMessage,
|
|
736
868
|
syncNotesKnowledge: syncNotesKnowledge,
|
|
737
869
|
};
|
|
738
870
|
}
|
|
739
871
|
|
|
872
|
+
function copyMessage(message) {
|
|
873
|
+
return JSON.parse(JSON.stringify(message || {}));
|
|
874
|
+
}
|
|
875
|
+
|
|
740
876
|
module.exports = { attachUserMessage: attachUserMessage };
|
|
@@ -31,6 +31,7 @@ function attachProjectWorkspaceQuery(ctx) {
|
|
|
31
31
|
function createMcpServer(adapter, session) {
|
|
32
32
|
if (!service || isMate !== true) return null;
|
|
33
33
|
var bound = session ? binding(session) : null;
|
|
34
|
+
if (session && !bound) return null;
|
|
34
35
|
var projectBound = binding(null);
|
|
35
36
|
if (!projectBound) return null;
|
|
36
37
|
return workspaceMcp.createMcpServer(adapter, bound, projectBound.isClay);
|
|
@@ -39,6 +40,7 @@ function attachProjectWorkspaceQuery(ctx) {
|
|
|
39
40
|
function createHistoryMcpServer(adapter, session) {
|
|
40
41
|
if (!adapter || typeof adapter.createToolServer !== "function") return null;
|
|
41
42
|
var bound = session ? binding(session) : null;
|
|
43
|
+
if (session && !bound) return null;
|
|
42
44
|
var projectBound = binding(null);
|
|
43
45
|
if (!projectBound || projectBound.isClay !== true) return null;
|
|
44
46
|
return adapter.createToolServer({
|
|
@@ -48,6 +50,28 @@ function attachProjectWorkspaceQuery(ctx) {
|
|
|
48
50
|
});
|
|
49
51
|
}
|
|
50
52
|
|
|
53
|
+
function getLateSessionMcpServers(adapter, session, startupServers, boundServers) {
|
|
54
|
+
var servers = {};
|
|
55
|
+
if (!session) return servers;
|
|
56
|
+
if (isMate && !Object.prototype.hasOwnProperty.call(startupServers || {}, "clay-workspace") && !boundServers["clay-workspace"]) {
|
|
57
|
+
try {
|
|
58
|
+
var workspace = createMcpServer(adapter, session);
|
|
59
|
+
if (workspace) servers["clay-workspace"] = workspace;
|
|
60
|
+
} catch (e) {
|
|
61
|
+
console.error("[project-workspace-query] Failed to late-bind clay-workspace MCP server:", e.message);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
if (isMate && !Object.prototype.hasOwnProperty.call(startupServers || {}, "clay-history") && !boundServers["clay-history"]) {
|
|
65
|
+
try {
|
|
66
|
+
var history = createHistoryMcpServer(adapter, session);
|
|
67
|
+
if (history) servers["clay-history"] = history;
|
|
68
|
+
} catch (e) {
|
|
69
|
+
console.error("[project-workspace-query] Failed to late-bind clay-history MCP server:", e.message);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return servers;
|
|
73
|
+
}
|
|
74
|
+
|
|
51
75
|
function isAuthoritativeClay() {
|
|
52
76
|
var bound = binding(null);
|
|
53
77
|
return !!(bound && bound.isClay === true);
|
|
@@ -130,6 +154,7 @@ function attachProjectWorkspaceQuery(ctx) {
|
|
|
130
154
|
return {
|
|
131
155
|
createMcpServer: createMcpServer,
|
|
132
156
|
createHistoryMcpServer: createHistoryMcpServer,
|
|
157
|
+
getLateSessionMcpServers: getLateSessionMcpServers,
|
|
133
158
|
isAuthoritativeClay: isAuthoritativeClay,
|
|
134
159
|
getBridgeTools: getBridgeTools,
|
|
135
160
|
getHistoryBridgeTools: getHistoryBridgeTools,
|