clay-server 2.18.0-beta.7 → 2.18.0-beta.9

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/public/app.js CHANGED
@@ -29,6 +29,7 @@ import { initMateWizard, openMateWizard, closeMateWizard, handleMateCreated } fr
29
29
  import { initCommandPalette, handlePaletteSessionSwitch, setPaletteVersion } from './modules/command-palette.js';
30
30
  import { initLongPress } from './modules/longpress.js';
31
31
  import { initMention, handleMentionStart, handleMentionStream, handleMentionDone, handleMentionError, handleMentionActivity, renderMentionUser, renderMentionResponse } from './modules/mention.js';
32
+ import { initDebate, handleDebateStarted, handleDebateTurn, handleDebateActivity, handleDebateStream, handleDebateTurnDone, handleDebateCommentQueued, handleDebateCommentInjected, handleDebateEnded, handleDebateError, renderDebateStarted, renderDebateTurnDone, renderDebateEnded, renderDebateCommentInjected, openDebateModal, closeDebateModal } from './modules/debate.js';
32
33
 
33
34
  // --- Base path for multi-project routing ---
34
35
  var slugMatch = location.pathname.match(/^\/p\/([a-z0-9_-]+)/);
@@ -907,6 +908,13 @@ import { initMention, handleMentionStart, handleMentionStream, handleMentionDone
907
908
  var mateSessionDot = document.querySelector(".mate-session-item.active .session-processing");
908
909
  if (mateSessionDot) mateSessionDot.style.display = "none";
909
910
  }
911
+ // Handle skill_installed in mate DM context (for skill install modal)
912
+ if (msg.type === "skill_installed") {
913
+ handleSkillInstalled(msg);
914
+ if (msg.success) knownInstalledSkills[msg.skill] = true;
915
+ handleSkillInstallWs(msg);
916
+ }
917
+
910
918
  // Intercept session_list for mate sidebar
911
919
  if (msg.type === "init" && msg.sessions) {
912
920
  renderMateSessionList(msg.sessions);
@@ -3684,10 +3692,25 @@ import { initMention, handleMentionStart, handleMentionStream, handleMentionDone
3684
3692
  ws.send(JSON.stringify({ type: "mate_list" }));
3685
3693
  } catch(e) {}
3686
3694
 
3687
- // Restore mate DM after hard refresh
3695
+ // Restore the last active session for this project (overrides server global)
3696
+ try {
3697
+ var savedSessionId = localStorage.getItem("clay_active_session_" + basePath);
3698
+ if (savedSessionId) {
3699
+ ws.send(JSON.stringify({ type: "switch_session", id: parseInt(savedSessionId, 10) }));
3700
+ }
3701
+ } catch(e) {}
3702
+
3703
+ // Restore mate DM after hard refresh or server restart
3688
3704
  try {
3689
3705
  var savedMateDm = localStorage.getItem("clay_active_mate_dm");
3690
- if (savedMateDm && !dmMode) {
3706
+ if (savedMateDm) {
3707
+ // If dmMode is stale (server restarted while in mate DM), clean up first
3708
+ if (dmMode) {
3709
+ dmMode = false;
3710
+ savedMainWs = null;
3711
+ mateWs = null;
3712
+ document.body.classList.remove("mate-dm-active");
3713
+ }
3691
3714
  pendingMateDmRestore = true;
3692
3715
  messagesEl.innerHTML = ""; // prevent regular history flash
3693
3716
  openDm(savedMateDm);
@@ -3729,8 +3752,18 @@ import { initMention, handleMentionStart, handleMentionStream, handleMentionDone
3729
3752
  };
3730
3753
 
3731
3754
  ws.onmessage = function (event) {
3732
- // If this WS is stashed while in mate DM, ignore its messages
3733
- if (savedMainWs === this) return;
3755
+ // If this WS is stashed while in mate DM, only allow skill_installed through
3756
+ if (savedMainWs === this) {
3757
+ try {
3758
+ var stashedMsg = JSON.parse(event.data);
3759
+ if (stashedMsg.type === "skill_installed") {
3760
+ handleSkillInstalled(stashedMsg);
3761
+ if (stashedMsg.success) knownInstalledSkills[stashedMsg.skill] = true;
3762
+ handleSkillInstallWs(stashedMsg);
3763
+ }
3764
+ } catch (e) {}
3765
+ return;
3766
+ }
3734
3767
 
3735
3768
  // Backup: if we're receiving messages, we're connected
3736
3769
  if (!connected) {
@@ -4048,6 +4081,8 @@ import { initMention, handleMentionStart, handleMentionStream, handleMentionDone
4048
4081
  }
4049
4082
  activeSessionId = msg.id;
4050
4083
  cliSessionId = msg.cliSessionId || null;
4084
+ // Persist active session per project so reconnect restores it
4085
+ try { localStorage.setItem("clay_active_session_" + basePath, String(msg.id)); } catch(e) {}
4051
4086
  clearRemoteCursors();
4052
4087
  resetClientState();
4053
4088
  updateRalphBars();
@@ -4676,6 +4711,51 @@ import { initMention, handleMentionStart, handleMentionStream, handleMentionDone
4676
4711
  renderMentionResponse(msg);
4677
4712
  break;
4678
4713
 
4714
+ // --- Debate ---
4715
+ case "debate_preparing":
4716
+ showDebateSticky("preparing", msg);
4717
+ break;
4718
+
4719
+ case "debate_started":
4720
+ showDebateSticky("live", msg);
4721
+ handleDebateStarted(msg);
4722
+ break;
4723
+
4724
+ case "debate_turn":
4725
+ handleDebateTurn(msg);
4726
+ if (msg.round) updateDebateRound(msg.round);
4727
+ break;
4728
+
4729
+ case "debate_activity":
4730
+ handleDebateActivity(msg);
4731
+ break;
4732
+
4733
+ case "debate_stream":
4734
+ handleDebateStream(msg);
4735
+ break;
4736
+
4737
+ case "debate_turn_done":
4738
+ handleDebateTurnDone(msg);
4739
+ break;
4740
+
4741
+ case "debate_comment_queued":
4742
+ handleDebateCommentQueued(msg);
4743
+ break;
4744
+
4745
+ case "debate_comment_injected":
4746
+ handleDebateCommentInjected(msg);
4747
+ break;
4748
+
4749
+ case "debate_ended":
4750
+ showDebateSticky("ended", msg);
4751
+ handleDebateEnded(msg);
4752
+ break;
4753
+
4754
+ case "debate_error":
4755
+ handleDebateError(msg);
4756
+ if (msg.error) showToast("Debate: " + msg.error, "error");
4757
+ break;
4758
+
4679
4759
  case "daemon_config":
4680
4760
  updateDaemonConfig(msg.config);
4681
4761
  break;
@@ -5008,6 +5088,16 @@ import { initMention, handleMentionStart, handleMentionStream, handleMentionDone
5008
5088
  addCopyHandler: addCopyHandler,
5009
5089
  });
5010
5090
 
5091
+ // --- Debate module ---
5092
+ initDebate({
5093
+ get ws() { return ws; },
5094
+ messagesEl: messagesEl,
5095
+ scrollToBottom: scrollToBottom,
5096
+ addCopyHandler: addCopyHandler,
5097
+ matesList: function () { return cachedMatesList || []; },
5098
+ currentMateId: function () { return (dmTargetUser && dmTargetUser.isMate) ? dmTargetUser.id : null; },
5099
+ });
5100
+
5011
5101
  // --- STT module (voice input via Web Speech API) ---
5012
5102
  initSTT({
5013
5103
  inputEl: inputEl,
@@ -5499,6 +5589,13 @@ import { initMention, handleMentionStart, handleMentionStream, handleMentionDone
5499
5589
  updateLoopBanner(loopIteration, loopMaxIterations, "running");
5500
5590
  }
5501
5591
  }
5592
+
5593
+ // Restore debate sticky on session switch
5594
+ if (debateStickyState && debateStickyState.phase) {
5595
+ showDebateSticky(debateStickyState.phase, debateStickyState.msg);
5596
+ } else {
5597
+ showDebateSticky("hide", null);
5598
+ }
5502
5599
  }
5503
5600
 
5504
5601
  // --- Skill install dialog (generic) ---
@@ -5727,6 +5824,24 @@ import { initMention, handleMentionStart, handleMentionStream, handleMentionDone
5727
5824
  }, cb);
5728
5825
  }
5729
5826
 
5827
+ function requireClayDebateSetup(cb) {
5828
+ requireSkills({
5829
+ title: "Skill Installation Required",
5830
+ reason: "The Debate Setup skill is required to start a debate.",
5831
+ skills: [{ name: "clay-debate-setup", url: "https://github.com/chadbyte/clay-debate-setup", scope: "global" }]
5832
+ }, cb);
5833
+ }
5834
+
5835
+ // Debate button in mate sidebar
5836
+ var debateBtn = document.getElementById("mate-debate-btn");
5837
+ if (debateBtn) {
5838
+ debateBtn.addEventListener("click", function () {
5839
+ requireClayDebateSetup(function () {
5840
+ openDebateModal();
5841
+ });
5842
+ });
5843
+ }
5844
+
5730
5845
  // --- Ralph Wizard ---
5731
5846
 
5732
5847
  var wizardMode = "draft"; // "draft" or "own"
@@ -6174,6 +6289,130 @@ import { initMention, handleMentionStart, handleMentionStream, handleMentionDone
6174
6289
  }
6175
6290
  }
6176
6291
 
6292
+ // --- Debate Sticky Banner ---
6293
+ var debateStickyState = null;
6294
+ var debateHandRaiseOpen = false;
6295
+
6296
+ function showDebateSticky(phase, msg) {
6297
+ if (phase === "ended" || phase === "hide") {
6298
+ debateStickyState = null;
6299
+ } else {
6300
+ debateStickyState = { phase: phase, msg: msg };
6301
+ }
6302
+ var stickyEl = document.getElementById("debate-sticky");
6303
+ if (!stickyEl) return;
6304
+
6305
+ if (phase === "ended" || phase === "hide") {
6306
+ stickyEl.classList.add("hidden");
6307
+ stickyEl.innerHTML = "";
6308
+ debateHandRaiseOpen = false;
6309
+ return;
6310
+ }
6311
+
6312
+ var topic = (msg && msg.topic) || "Debate";
6313
+ var truncTopic = topic.length > 30 ? topic.slice(0, 30) + "\u2026" : topic;
6314
+
6315
+ if (phase === "preparing") {
6316
+ stickyEl.innerHTML =
6317
+ '<div class="debate-sticky-inner">' +
6318
+ '<div class="debate-sticky-header">' +
6319
+ '<span class="debate-sticky-icon">' + iconHtml("mic") + '</span>' +
6320
+ '<span class="debate-sticky-label">' + escapeHtml(truncTopic) + '</span>' +
6321
+ '<span class="debate-sticky-status" id="debate-sticky-status">Setting up\u2026</span>' +
6322
+ '<button class="debate-sticky-action debate-sticky-cancel" title="Cancel debate">' + iconHtml("x") + '</button>' +
6323
+ '</div>' +
6324
+ '</div>';
6325
+ stickyEl.classList.remove("hidden");
6326
+ stickyEl.className = "debate-sticky preparing";
6327
+ refreshIcons();
6328
+
6329
+ stickyEl.querySelector(".debate-sticky-cancel").addEventListener("click", function (e) {
6330
+ e.stopPropagation();
6331
+ if (ws && ws.readyState === 1) {
6332
+ ws.send(JSON.stringify({ type: "debate_stop" }));
6333
+ }
6334
+ stickyEl.classList.add("hidden");
6335
+ stickyEl.innerHTML = "";
6336
+ });
6337
+ } else if (phase === "live") {
6338
+ stickyEl.innerHTML =
6339
+ '<div class="debate-sticky-inner">' +
6340
+ '<div class="debate-sticky-header">' +
6341
+ '<span class="debate-sticky-icon">' + iconHtml("mic") + '</span>' +
6342
+ '<span class="debate-sticky-label">' + escapeHtml(truncTopic) + '</span>' +
6343
+ '<span class="debate-sticky-status" id="debate-sticky-status">Live</span>' +
6344
+ '<span class="debate-sticky-round" id="debate-sticky-round">R1</span>' +
6345
+ '<button class="debate-sticky-action debate-sticky-hand" title="Raise hand">' + iconHtml("hand") + '</button>' +
6346
+ '<button class="debate-sticky-action debate-sticky-stop" title="Stop debate">' + iconHtml("square") + '</button>' +
6347
+ '</div>' +
6348
+ '<div class="debate-sticky-hand-input hidden" id="debate-sticky-hand-input">' +
6349
+ '<textarea id="debate-sticky-comment" rows="1" placeholder="Your comment\u2026"></textarea>' +
6350
+ '<button class="debate-sticky-send" id="debate-sticky-send">Send</button>' +
6351
+ '<button class="debate-sticky-send-cancel" id="debate-sticky-send-cancel">Cancel</button>' +
6352
+ '</div>' +
6353
+ '</div>';
6354
+ stickyEl.classList.remove("hidden");
6355
+ stickyEl.className = "debate-sticky live";
6356
+ refreshIcons();
6357
+ debateHandRaiseOpen = false;
6358
+
6359
+ stickyEl.querySelector(".debate-sticky-hand").addEventListener("click", function (e) {
6360
+ e.stopPropagation();
6361
+ toggleDebateHandRaise();
6362
+ });
6363
+
6364
+ stickyEl.querySelector(".debate-sticky-stop").addEventListener("click", function (e) {
6365
+ e.stopPropagation();
6366
+ if (ws && ws.readyState === 1) {
6367
+ ws.send(JSON.stringify({ type: "debate_stop" }));
6368
+ }
6369
+ });
6370
+
6371
+ var sendBtn = document.getElementById("debate-sticky-send");
6372
+ var cancelBtn = document.getElementById("debate-sticky-send-cancel");
6373
+ var commentInput = document.getElementById("debate-sticky-comment");
6374
+
6375
+ if (sendBtn) sendBtn.addEventListener("click", function () { sendDebateStickyComment(); });
6376
+ if (cancelBtn) cancelBtn.addEventListener("click", function () { toggleDebateHandRaise(false); });
6377
+ if (commentInput) {
6378
+ commentInput.addEventListener("keydown", function (e) {
6379
+ if (e.key === "Enter" && !e.shiftKey) { e.preventDefault(); sendDebateStickyComment(); }
6380
+ if (e.key === "Escape") { toggleDebateHandRaise(false); }
6381
+ });
6382
+ }
6383
+ }
6384
+ }
6385
+
6386
+ function toggleDebateHandRaise(forceState) {
6387
+ var inputWrap = document.getElementById("debate-sticky-hand-input");
6388
+ var commentInput = document.getElementById("debate-sticky-comment");
6389
+ if (!inputWrap) return;
6390
+ var show = typeof forceState === "boolean" ? forceState : !debateHandRaiseOpen;
6391
+ debateHandRaiseOpen = show;
6392
+ if (show) {
6393
+ inputWrap.classList.remove("hidden");
6394
+ if (commentInput) { commentInput.value = ""; commentInput.focus(); }
6395
+ } else {
6396
+ inputWrap.classList.add("hidden");
6397
+ }
6398
+ }
6399
+
6400
+ function sendDebateStickyComment() {
6401
+ var commentInput = document.getElementById("debate-sticky-comment");
6402
+ if (!commentInput) return;
6403
+ var text = commentInput.value.trim();
6404
+ if (!text) return;
6405
+ if (ws && ws.readyState === 1) {
6406
+ ws.send(JSON.stringify({ type: "debate_comment", text: text }));
6407
+ }
6408
+ toggleDebateHandRaise(false);
6409
+ }
6410
+
6411
+ function updateDebateRound(round) {
6412
+ var roundEl = document.getElementById("debate-sticky-round");
6413
+ if (roundEl) roundEl.textContent = "R" + round;
6414
+ }
6415
+
6177
6416
  // --- Ralph Preview Modal ---
6178
6417
  function openRalphPreviewModal() {
6179
6418
  var modal = document.getElementById("ralph-preview-modal");
package/lib/sdk-bridge.js CHANGED
@@ -1116,10 +1116,10 @@ function createSDKBridge(opts) {
1116
1116
  return Promise.resolve({ behavior: "allow", updatedInput: input });
1117
1117
  }
1118
1118
 
1119
- // Mate sessions (DM): auto-approve read-only tools
1119
+ // Mate sessions (DM, debate, mention): auto-approve read + fetch tools
1120
1120
  if (isMate || mateDisplayName) {
1121
- var mateReadTools = { Read: true, Glob: true, Grep: true };
1122
- if (mateReadTools[toolName]) {
1121
+ var mateAutoTools = { Read: true, Glob: true, Grep: true, WebFetch: true, WebSearch: true };
1122
+ if (mateAutoTools[toolName]) {
1123
1123
  return Promise.resolve({ behavior: "allow", updatedInput: input });
1124
1124
  }
1125
1125
  }
@@ -1775,7 +1775,7 @@ function createSDKBridge(opts) {
1775
1775
  includePartialMessages: true,
1776
1776
  abortController: abortController,
1777
1777
  canUseTool: opts.canUseTool || function (toolName, input) {
1778
- var allowed = { Read: true, Glob: true, Grep: true };
1778
+ var allowed = { Read: true, Glob: true, Grep: true, WebFetch: true, WebSearch: true };
1779
1779
  if (allowed[toolName]) {
1780
1780
  return Promise.resolve({ behavior: "allow", updatedInput: input });
1781
1781
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "clay-server",
3
- "version": "2.18.0-beta.7",
3
+ "version": "2.18.0-beta.9",
4
4
  "description": "Web UI for Claude Code. Any device. Push notifications.",
5
5
  "bin": {
6
6
  "clay-server": "./bin/cli.js",