clay-server 2.26.0-beta.8 → 2.26.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -6,6 +6,7 @@ function attachMateInteraction(ctx) {
6
6
  var cwd = ctx.cwd;
7
7
  var sm = ctx.sm;
8
8
  var sdk = ctx.sdk;
9
+ var send = ctx.send;
9
10
  var sendTo = ctx.sendTo;
10
11
  var sendToSession = ctx.sendToSession;
11
12
  var sendToSessionOthers = ctx.sendToSessionOthers;
@@ -455,6 +456,9 @@ function attachMateInteraction(ctx) {
455
456
  avatarSeed: avatarSeed,
456
457
  });
457
458
 
459
+ // Broadcast to all tabs so mate avatar shows activity indicator
460
+ send({ type: "mention_processing", mateId: msg.mateId, active: true });
461
+
458
462
  // Shared callbacks for both new and continued sessions
459
463
  var mentionCallbacks = {
460
464
  onActivity: function (activity) {
@@ -498,6 +502,7 @@ function attachMateInteraction(ctx) {
498
502
  );
499
503
 
500
504
  sendToSession(session.localId, { type: "mention_done", mateId: msg.mateId });
505
+ send({ type: "mention_processing", mateId: msg.mateId, active: false });
501
506
 
502
507
  // Check if the mate wrote a debate brief during this turn
503
508
  ctx.checkForDmDebateBrief(session, msg.mateId, mateCtx);
@@ -513,6 +518,7 @@ function attachMateInteraction(ctx) {
513
518
  }
514
519
  console.error("[mention] Error for mate " + msg.mateId + ":", errMsg);
515
520
  sendToSession(session.localId, { type: "mention_error", mateId: msg.mateId, error: errMsg });
521
+ send({ type: "mention_processing", mateId: msg.mateId, active: false });
516
522
  },
517
523
  };
518
524
 
@@ -563,11 +569,10 @@ function attachMateInteraction(ctx) {
563
569
  onDone: mentionCallbacks.onDone,
564
570
  onError: mentionCallbacks.onError,
565
571
  canUseTool: function (toolName, input, toolOpts) {
566
- var autoAllow = { Read: true, Glob: true, Grep: true, WebFetch: true, WebSearch: true };
567
- if (autoAllow[toolName]) {
568
- return Promise.resolve({ behavior: "allow", updatedInput: input });
569
- }
570
- // Route through the project session's permission system
572
+ // Use the shared whitelist from sdk-bridge (read-only tools + safe bash commands)
573
+ var whitelisted = sdk.checkToolWhitelist(toolName, input);
574
+ if (whitelisted) return Promise.resolve(whitelisted);
575
+ // Not whitelisted: route through the project session's permission system
571
576
  return new Promise(function (resolve) {
572
577
  var requestId = crypto.randomUUID();
573
578
  session.pendingPermissions[requestId] = {
package/lib/project.js CHANGED
@@ -293,6 +293,7 @@ function createProjectContext(opts) {
293
293
 
294
294
  // --- Browser extension state ---
295
295
  var _browserTabList = {}; // tabId -> { id, url, title, favIconUrl }
296
+ var _pendingDebateProposals = {}; // proposalId -> { resolve, briefData }
296
297
  var _extensionWs = null; // WebSocket of the client with the Chrome extension
297
298
  var _extToken = crypto.randomUUID(); // Auth token for MCP server bridge
298
299
  var pendingExtensionRequests = {}; // requestId -> { resolve, timer }
@@ -557,47 +558,72 @@ function createProjectContext(opts) {
557
558
  mateDisplayName: opts.mateDisplayName || "",
558
559
  isMate: isMate,
559
560
  dangerouslySkipPermissions: dangerouslySkipPermissions,
560
- mcpServers: isMate ? undefined : (function () {
561
+ mcpServers: (function () {
562
+ var servers = {};
563
+
564
+ // Debate MCP server (available to both mates and main project)
561
565
  try {
562
- var browserMcp = require("./browser-mcp-server");
563
- var mcpConfig = browserMcp.create(sendExtensionCommandAny, function () {
564
- return Object.values(_browserTabList || {});
565
- }, {
566
- watchTab: function (tabId) {
567
- var key = "tab:" + tabId;
568
- var active = loadContextSources(slug);
569
- if (active.indexOf(key) === -1) {
570
- active.push(key);
571
- saveContextSources(slug, active);
572
- var msg = JSON.stringify({ type: "context_sources_state", active: active });
573
- for (var c of clients) { if (c.readyState === 1) c.send(msg); }
574
- }
575
- return active;
576
- },
577
- unwatchTab: function (tabId) {
578
- var key = "tab:" + tabId;
579
- var active = loadContextSources(slug);
580
- var idx = active.indexOf(key);
581
- if (idx !== -1) {
582
- active.splice(idx, 1);
583
- saveContextSources(slug, active);
584
- var msg = JSON.stringify({ type: "context_sources_state", active: active });
585
- for (var c of clients) { if (c.readyState === 1) c.send(msg); }
586
- }
587
- return active;
588
- },
566
+ var debateMcp = require("./debate-mcp-server");
567
+ var debateMcpConfig = debateMcp.create(function onPropose(briefData) {
568
+ return new Promise(function (resolve) {
569
+ var proposalId = "dp_" + Date.now() + "_" + Math.random().toString(36).slice(2, 8);
570
+ briefData.proposalId = proposalId;
571
+ _pendingDebateProposals[proposalId] = {
572
+ resolve: resolve,
573
+ briefData: briefData,
574
+ };
575
+ // The SDK sends tool_executing with briefData as input.
576
+ // Client renders the debate brief card when it sees propose_debate.
577
+ });
589
578
  });
590
- if (!mcpConfig) return undefined;
591
- var servers = {};
592
- servers[mcpConfig.name || "clay-browser"] = mcpConfig;
593
- return servers;
579
+ if (debateMcpConfig) servers[debateMcpConfig.name || "clay-debate"] = debateMcpConfig;
594
580
  } catch (e) {
595
- console.error("[project] Failed to create browser MCP server:", e.message);
596
- return undefined;
581
+ console.error("[project] Failed to create debate MCP server:", e.message);
597
582
  }
583
+
584
+ // Browser MCP server (main project only, not mates)
585
+ if (!isMate) {
586
+ try {
587
+ var browserMcp = require("./browser-mcp-server");
588
+ var mcpConfig = browserMcp.create(sendExtensionCommandAny, function () {
589
+ return Object.values(_browserTabList || {});
590
+ }, {
591
+ watchTab: function (tabId) {
592
+ var key = "tab:" + tabId;
593
+ var active = loadContextSources(slug);
594
+ if (active.indexOf(key) === -1) {
595
+ active.push(key);
596
+ saveContextSources(slug, active);
597
+ var _msg = JSON.stringify({ type: "context_sources_state", active: active });
598
+ for (var c of clients) { if (c.readyState === 1) c.send(_msg); }
599
+ }
600
+ return active;
601
+ },
602
+ unwatchTab: function (tabId) {
603
+ var key = "tab:" + tabId;
604
+ var active = loadContextSources(slug);
605
+ var idx = active.indexOf(key);
606
+ if (idx !== -1) {
607
+ active.splice(idx, 1);
608
+ saveContextSources(slug, active);
609
+ var _msg = JSON.stringify({ type: "context_sources_state", active: active });
610
+ for (var c of clients) { if (c.readyState === 1) c.send(_msg); }
611
+ }
612
+ return active;
613
+ },
614
+ });
615
+ if (mcpConfig) servers[mcpConfig.name || "clay-browser"] = mcpConfig;
616
+ } catch (e) {
617
+ console.error("[project] Failed to create browser MCP server:", e.message);
618
+ }
619
+ }
620
+
621
+ return Object.keys(servers).length > 0 ? servers : undefined;
598
622
  })(),
599
623
  onProcessingChanged: onProcessingChanged,
600
- onTurnDone: isMate ? function (session, preview) { digestDmTurn(session, preview); } : null,
624
+ onTurnDone: isMate ? function (session, preview) {
625
+ digestDmTurn(session, preview);
626
+ } : null,
601
627
  scheduleMessage: function (session, text, resetsAt) {
602
628
  scheduleMessage(session, text, resetsAt);
603
629
  },
@@ -1591,11 +1617,13 @@ function createProjectContext(opts) {
1591
1617
  if (session.scheduledMessage && session.scheduledMessage.timer) {
1592
1618
  clearTimeout(session.scheduledMessage.timer);
1593
1619
  }
1594
- var schedDelay = Math.max(0, resetsAt - Date.now()) + 180000; // +3min buffer after reset
1620
+ var isPastReset = resetsAt <= Date.now();
1621
+ var schedDelay = isPastReset ? 5000 : Math.max(0, resetsAt - Date.now()) + 60000; // +1min buffer after reset, or 5s for immediate
1622
+ var sendsAt = Date.now() + schedDelay;
1595
1623
  var schedEntry = {
1596
1624
  type: "scheduled_message_queued",
1597
1625
  text: text,
1598
- resetsAt: resetsAt,
1626
+ resetsAt: sendsAt,
1599
1627
  scheduledAt: Date.now(),
1600
1628
  };
1601
1629
  sm.sendAndRecord(session, schedEntry);
@@ -1657,6 +1685,7 @@ function createProjectContext(opts) {
1657
1685
  }
1658
1686
  session._mentionInProgress = false;
1659
1687
  sendToSession(session.localId, { type: "mention_done", mateId: mateId, stopped: true });
1688
+ send({ type: "mention_processing", mateId: mateId, active: false });
1660
1689
  }
1661
1690
  return;
1662
1691
  }
@@ -1666,6 +1695,10 @@ function createProjectContext(opts) {
1666
1695
  handleDebateStart(ws, msg);
1667
1696
  return;
1668
1697
  }
1698
+ if (msg.type === "debate_hand_raise") {
1699
+ handleDebateHandRaise(ws);
1700
+ return;
1701
+ }
1669
1702
  if (msg.type === "debate_comment") {
1670
1703
  handleDebateComment(ws, msg);
1671
1704
  return;
@@ -1682,6 +1715,32 @@ function createProjectContext(opts) {
1682
1715
  handleDebateConfirmBrief(ws);
1683
1716
  return;
1684
1717
  }
1718
+ if (msg.type === "debate_proposal_response") {
1719
+ // Match the most recent pending proposal (proposalId may not be
1720
+ // available on the client since it's not part of the tool input)
1721
+ var _dpKeys = Object.keys(_pendingDebateProposals);
1722
+ if (_dpKeys.length === 0) return;
1723
+ var _dpKey = msg.proposalId || _dpKeys[_dpKeys.length - 1];
1724
+ var pending = _pendingDebateProposals[_dpKey];
1725
+ if (!pending) return;
1726
+ delete _pendingDebateProposals[_dpKey];
1727
+ if (msg.action === "start") {
1728
+ // Set up debate state on the session, then transition to live
1729
+ var _dpSession = getSessionForWs(ws);
1730
+ if (_dpSession) {
1731
+ var _dpMateId = isMate ? path.basename(cwd) : null;
1732
+ handleMcpDebateApproval(_dpSession, pending.briefData, _dpMateId, ws);
1733
+ }
1734
+ pending.resolve({ action: "start" });
1735
+ } else {
1736
+ pending.resolve({ action: "cancel" });
1737
+ }
1738
+ return;
1739
+ }
1740
+ if (msg.type === "debate_user_floor_response") {
1741
+ handleDebateUserFloorResponse(ws, msg);
1742
+ return;
1743
+ }
1685
1744
 
1686
1745
  // --- Knowledge file management ---
1687
1746
  if (msg.type === "knowledge_list") {
@@ -3884,6 +3943,26 @@ function createProjectContext(opts) {
3884
3943
  return;
3885
3944
  }
3886
3945
 
3946
+ if (msg.type === "send_scheduled_now") {
3947
+ var nowSession = getSessionForWs(ws);
3948
+ if (!nowSession || !nowSession.scheduledMessage) return;
3949
+ var schedText = nowSession.scheduledMessage.text;
3950
+ clearTimeout(nowSession.scheduledMessage.timer);
3951
+ nowSession.scheduledMessage = null;
3952
+ console.log("[project] Scheduled message sent immediately for session " + nowSession.localId);
3953
+ sm.sendAndRecord(nowSession, { type: "scheduled_message_sent" });
3954
+ var userMsg = { type: "user_message", text: schedText };
3955
+ nowSession.history.push(userMsg);
3956
+ sm.appendToSessionFile(nowSession, userMsg);
3957
+ sendToSession(nowSession.localId, userMsg);
3958
+ nowSession.isProcessing = true;
3959
+ onProcessingChanged();
3960
+ sendToSession(nowSession.localId, { type: "status", status: "processing" });
3961
+ sdk.startQuery(nowSession, schedText, null, getLinuxUserForSession(nowSession));
3962
+ sm.broadcastSessionList();
3963
+ return;
3964
+ }
3965
+
3887
3966
  if (msg.type !== "message") return;
3888
3967
  if (!msg.text && (!msg.images || msg.images.length === 0) && (!msg.pastes || msg.pastes.length === 0)) return;
3889
3968
 
@@ -4157,7 +4236,8 @@ function createProjectContext(opts) {
4157
4236
  data: screenshotData,
4158
4237
  file: screenshotName,
4159
4238
  tabTitle: tabLabel,
4160
- tabUrl: tabInfo ? tabInfo.url : ""
4239
+ tabUrl: tabInfo ? tabInfo.url : "",
4240
+ tabFavIconUrl: tabInfo ? tabInfo.favIconUrl : ""
4161
4241
  });
4162
4242
  parts.push("[Screenshot saved: " + screenshotPath + "]");
4163
4243
  }
@@ -4179,7 +4259,8 @@ function createProjectContext(opts) {
4179
4259
  }
4180
4260
 
4181
4261
  if (tabContextParts.length > 0) {
4182
- fullText = tabContextParts.join("\n\n---\n\n") + "\n\n" + fullText;
4262
+ fullText = "[The following browser tab data is automatically attached as context sources. Do NOT call browser_read_page, browser_console, browser_network, or browser_screenshot for these tabs — the data is already here.]\n\n" +
4263
+ tabContextParts.join("\n\n---\n\n") + "\n\n" + fullText;
4183
4264
  }
4184
4265
 
4185
4266
  // If screenshots were captured, send context preview cards and add to SDK images
@@ -4193,6 +4274,7 @@ function createProjectContext(opts) {
4193
4274
  tab: {
4194
4275
  title: ss.tabTitle || "",
4195
4276
  url: ss.tabUrl || "",
4277
+ favIconUrl: ss.tabFavIconUrl || "",
4196
4278
  screenshotFile: ss.file
4197
4279
  }
4198
4280
  };
@@ -4203,6 +4285,7 @@ function createProjectContext(opts) {
4203
4285
  tab: {
4204
4286
  title: ss.tabTitle || "",
4205
4287
  url: ss.tabUrl || "",
4288
+ favIconUrl: ss.tabFavIconUrl || "",
4206
4289
  screenshotUrl: "/p/" + slug + "/images/" + ss.file
4207
4290
  }
4208
4291
  });
@@ -4249,6 +4332,7 @@ function createProjectContext(opts) {
4249
4332
  cwd: cwd,
4250
4333
  sm: sm,
4251
4334
  sdk: sdk,
4335
+ send: send,
4252
4336
  sendTo: sendTo,
4253
4337
  sendToSession: sendToSession,
4254
4338
  sendToSessionOthers: sendToSessionOthers,
@@ -4275,6 +4359,8 @@ function createProjectContext(opts) {
4275
4359
  var _debate = attachDebate({
4276
4360
  cwd: cwd,
4277
4361
  slug: slug,
4362
+ isMate: isMate,
4363
+ projectOwnerId: projectOwnerId,
4278
4364
  send: send,
4279
4365
  sendTo: sendTo,
4280
4366
  sendToSession: sendToSession,
@@ -4291,12 +4377,15 @@ function createProjectContext(opts) {
4291
4377
  initMemorySummary: initMemorySummary,
4292
4378
  });
4293
4379
  var handleDebateStart = _debate.handleDebateStart;
4380
+ var handleDebateHandRaise = _debate.handleDebateHandRaise;
4294
4381
  var handleDebateComment = _debate.handleDebateComment;
4295
4382
  var handleDebateStop = _debate.handleDebateStop;
4296
4383
  var handleDebateConcludeResponse = _debate.handleDebateConcludeResponse;
4297
4384
  var handleDebateConfirmBrief = _debate.handleDebateConfirmBrief;
4385
+ var handleDebateUserFloorResponse = _debate.handleDebateUserFloorResponse;
4298
4386
  var restoreDebateState = _debate.restoreDebateState;
4299
4387
  var checkForDmDebateBrief = _debate.checkForDmDebateBrief;
4388
+ var handleMcpDebateApproval = _debate.handleMcpDebateApproval;
4300
4389
 
4301
4390
  // --- Session presence (who is viewing which session) ---
4302
4391
  function broadcastPresence() {