clay-server 2.13.1-beta.1 → 2.14.0-beta.2

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/daemon.js CHANGED
@@ -1421,6 +1421,19 @@ if (process.platform === "win32") {
1421
1421
  }
1422
1422
 
1423
1423
  process.on("uncaughtException", function (err) {
1424
+ var errMsg = err ? (err.message || String(err)) : "";
1425
+ var isAbort = errMsg.indexOf("Operation aborted") !== -1
1426
+ || errMsg.indexOf("AbortError") !== -1
1427
+ || (err && err.name === "AbortError");
1428
+
1429
+ if (isAbort) {
1430
+ // A single session's SDK write was aborted (e.g. stream closed before
1431
+ // write completed). This is recoverable, so do NOT tear down the whole
1432
+ // daemon and kill every other session.
1433
+ console.error("[daemon] Suppressed AbortError (single-session failure):", errMsg);
1434
+ return;
1435
+ }
1436
+
1424
1437
  console.error("[daemon] Uncaught exception:", err);
1425
1438
  writeCrashInfo({
1426
1439
  reason: err ? (err.stack || err.message || String(err)) : "unknown",
@@ -1429,3 +1442,17 @@ process.on("uncaughtException", function (err) {
1429
1442
  });
1430
1443
  gracefulShutdown();
1431
1444
  });
1445
+
1446
+ process.on("unhandledRejection", function (reason) {
1447
+ var errMsg = reason ? (reason.message || String(reason)) : "";
1448
+ var isAbort = errMsg.indexOf("Operation aborted") !== -1
1449
+ || errMsg.indexOf("AbortError") !== -1
1450
+ || (reason && reason.name === "AbortError");
1451
+
1452
+ if (isAbort) {
1453
+ console.error("[daemon] Suppressed unhandled rejection (AbortError):", errMsg);
1454
+ return;
1455
+ }
1456
+
1457
+ console.error("[daemon] Unhandled rejection:", reason);
1458
+ });
package/lib/project.js CHANGED
@@ -1065,6 +1065,8 @@ function createProjectContext(opts) {
1065
1065
  latestVersion = v;
1066
1066
  sendToAdmins({ type: "update_available", version: v });
1067
1067
  }
1068
+ }).catch(function (e) {
1069
+ console.error("[project] Background version check failed:", e.message || e);
1068
1070
  });
1069
1071
 
1070
1072
  // --- WS connection handler ---
@@ -2025,6 +2027,8 @@ function createProjectContext(opts) {
2025
2027
  console.error("[project] Error starting plan execution:", e);
2026
2028
  sendTo(ws, { type: "error", text: "Failed to start plan execution: " + (e.message || e) });
2027
2029
  }
2030
+ }).catch(function (e) {
2031
+ console.error("[project] Plan execution stream wait failed:", e.message || e);
2028
2032
  });
2029
2033
  return;
2030
2034
  }
@@ -3502,23 +3506,31 @@ function createProjectContext(opts) {
3502
3506
  }
3503
3507
  var child = spawn("npx", ["skills", "add", url, "--skill", skill, "--yes", scopeFlag], skillSpawnOpts);
3504
3508
  child.on("close", function (code) {
3505
- var success = code === 0;
3506
- send({
3507
- type: "skill_installed",
3508
- skill: skill,
3509
- scope: scope,
3510
- success: success,
3511
- error: success ? null : "Process exited with code " + code,
3512
- });
3509
+ try {
3510
+ var success = code === 0;
3511
+ send({
3512
+ type: "skill_installed",
3513
+ skill: skill,
3514
+ scope: scope,
3515
+ success: success,
3516
+ error: success ? null : "Process exited with code " + code,
3517
+ });
3518
+ } catch (e) {
3519
+ console.error("[project] skill_installed send failed:", e.message || e);
3520
+ }
3513
3521
  });
3514
3522
  child.on("error", function (err) {
3515
- send({
3516
- type: "skill_installed",
3517
- skill: skill,
3518
- scope: scope,
3519
- success: false,
3520
- error: err.message,
3521
- });
3523
+ try {
3524
+ send({
3525
+ type: "skill_installed",
3526
+ skill: skill,
3527
+ scope: scope,
3528
+ success: false,
3529
+ error: err.message,
3530
+ });
3531
+ } catch (e) {
3532
+ console.error("[project] skill_installed error send failed:", e.message || e);
3533
+ }
3522
3534
  });
3523
3535
  res.writeHead(200, { "Content-Type": "application/json" });
3524
3536
  res.end('{"ok":true}');
@@ -3722,15 +3734,21 @@ function createProjectContext(opts) {
3722
3734
  var data = "";
3723
3735
  resp.on("data", function (chunk) { data += chunk; });
3724
3736
  resp.on("end", function () {
3725
- var remoteVer = parseVersionFromSkillMd(data);
3726
- var status = "ok";
3727
- if (!installed) {
3728
- status = "missing";
3729
- } else if (remoteVer && compareVersions(installedVer, remoteVer) < 0) {
3730
- status = "outdated";
3737
+ try {
3738
+ var remoteVer = parseVersionFromSkillMd(data);
3739
+ var status = "ok";
3740
+ if (!installed) {
3741
+ status = "missing";
3742
+ } else if (remoteVer && compareVersions(installedVer, remoteVer) < 0) {
3743
+ status = "outdated";
3744
+ }
3745
+ results.push({ name: skill.name, installed: installed, installedVersion: installedVer, remoteVersion: remoteVer, status: status });
3746
+ finishOne();
3747
+ } catch (e) {
3748
+ console.error("[project] Skill version check failed for " + skill.name + ":", e.message || e);
3749
+ results.push({ name: skill.name, installed: installed, installedVersion: installedVer, remoteVersion: "", status: installed ? "ok" : "error" });
3750
+ finishOne();
3731
3751
  }
3732
- results.push({ name: skill.name, installed: installed, installedVersion: installedVer, remoteVersion: remoteVer, status: status });
3733
- finishOne();
3734
3752
  });
3735
3753
  }).on("error", function () {
3736
3754
  results.push({ name: skill.name, installed: installed, installedVersion: installedVer, remoteVersion: "", status: installed ? "ok" : "missing" });
package/lib/public/app.js CHANGED
@@ -1779,9 +1779,18 @@ import { initCommandPalette, handlePaletteSessionSwitch, setPaletteVersion } fro
1779
1779
  if (!activityEl) {
1780
1780
  activityEl = document.createElement("div");
1781
1781
  activityEl.className = "activity-inline";
1782
- activityEl.innerHTML =
1783
- '<span class="activity-icon">' + iconHtml("sparkles") + '</span>' +
1784
- '<span class="activity-text"></span>';
1782
+ var isMateDmActive = dmMode && dmTargetUser && dmTargetUser.isMate;
1783
+ if (isMateDmActive) {
1784
+ activityEl.classList.add("mate-activity");
1785
+ var mateAvUrl = document.body.dataset.mateAvatarUrl || "";
1786
+ activityEl.innerHTML =
1787
+ '<img class="mate-activity-avatar" src="' + mateAvUrl + '" alt="">' +
1788
+ '<span class="activity-text"></span>';
1789
+ } else {
1790
+ activityEl.innerHTML =
1791
+ '<span class="activity-icon">' + iconHtml("sparkles") + '</span>' +
1792
+ '<span class="activity-text"></span>';
1793
+ }
1785
1794
  addToMessages(activityEl);
1786
1795
  refreshIcons();
1787
1796
  }
@@ -4519,6 +4528,9 @@ import { initCommandPalette, handlePaletteSessionSwitch, setPaletteVersion } fro
4519
4528
  isDmMode: function () { return dmMode && !(dmTargetUser && dmTargetUser.isMate); },
4520
4529
  getDmKey: function () { return dmKey; },
4521
4530
  handleDmSend: function () { handleDmSend(); },
4531
+ isMateDm: function () { return dmMode && dmTargetUser && dmTargetUser.isMate; },
4532
+ getMateName: function () { return dmTargetUser ? (dmTargetUser.displayName || "Mate") : "Mate"; },
4533
+ getMateAvatarUrl: function () { return document.body.dataset.mateAvatarUrl || ""; },
4522
4534
  });
4523
4535
 
4524
4536
  // --- STT module (voice input via Web Speech API) ---
@@ -1323,6 +1323,147 @@ body.mate-dm-active .turn-meta {
1323
1323
  display: none;
1324
1324
  }
1325
1325
 
1326
+ /* ==========================================================================
1327
+ Mate DM: Humanized thinking / tools / permissions
1328
+ ========================================================================== */
1329
+
1330
+ /* --- Mate Thinking: avatar + name + bouncing dots --- */
1331
+ .mate-thinking-row {
1332
+ display: none;
1333
+ align-items: center;
1334
+ gap: 10px;
1335
+ padding: 6px 0;
1336
+ }
1337
+ body.mate-dm-active .mate-thinking:not(.done) .mate-thinking-row {
1338
+ display: flex;
1339
+ }
1340
+ .mate-thinking-avatar {
1341
+ width: 28px;
1342
+ height: 28px;
1343
+ border-radius: 6px;
1344
+ flex-shrink: 0;
1345
+ }
1346
+ .mate-thinking-body {
1347
+ display: flex;
1348
+ align-items: center;
1349
+ gap: 8px;
1350
+ }
1351
+ .mate-thinking-name {
1352
+ font-weight: 700;
1353
+ font-size: 14px;
1354
+ color: var(--text);
1355
+ }
1356
+ .mate-thinking-dots {
1357
+ display: inline-flex;
1358
+ align-items: center;
1359
+ gap: 3px;
1360
+ }
1361
+ .mate-thinking-dots span {
1362
+ width: 6px;
1363
+ height: 6px;
1364
+ border-radius: 50%;
1365
+ background: var(--text-muted);
1366
+ animation: mate-dot-bounce 1.4s ease-in-out infinite;
1367
+ }
1368
+ .mate-thinking-dots span:nth-child(2) {
1369
+ animation-delay: 0.2s;
1370
+ }
1371
+ .mate-thinking-dots span:nth-child(3) {
1372
+ animation-delay: 0.4s;
1373
+ }
1374
+ @keyframes mate-dot-bounce {
1375
+ 0%, 60%, 100% { transform: translateY(0); opacity: 0.4; }
1376
+ 30% { transform: translateY(-4px); opacity: 1; }
1377
+ }
1378
+
1379
+ /* When done, hide mate row (JS does this too), show compact expandable header */
1380
+ body.mate-dm-active .mate-thinking.done .mate-thinking-row {
1381
+ display: none;
1382
+ }
1383
+ body.mate-dm-active .mate-thinking.done .thinking-header {
1384
+ display: inline-flex !important;
1385
+ font-size: 12px;
1386
+ padding: 4px 10px;
1387
+ background: rgba(var(--overlay-rgb), 0.04);
1388
+ border-radius: 6px;
1389
+ opacity: 0.7;
1390
+ transition: opacity 0.15s;
1391
+ }
1392
+ body.mate-dm-active .mate-thinking.done .thinking-header:hover {
1393
+ opacity: 1;
1394
+ background: rgba(var(--overlay-rgb), 0.08);
1395
+ }
1396
+ body.mate-dm-active .mate-thinking .thinking-content {
1397
+ max-height: 0;
1398
+ overflow: hidden;
1399
+ transition: max-height 0.25s ease;
1400
+ }
1401
+ body.mate-dm-active .mate-thinking.expanded .thinking-content {
1402
+ max-height: 2000px;
1403
+ }
1404
+
1405
+ /* --- Mate Tool Group: compact, always collapsed with summary header --- */
1406
+ body.mate-dm-active .mate-tool-group {
1407
+ margin: 2px 0;
1408
+ }
1409
+ body.mate-dm-active .mate-tool-group .tool-group-header {
1410
+ font-size: 12px;
1411
+ padding: 4px 10px;
1412
+ background: rgba(var(--overlay-rgb), 0.04);
1413
+ border-radius: 6px;
1414
+ opacity: 0.7;
1415
+ transition: opacity 0.15s;
1416
+ }
1417
+ body.mate-dm-active .mate-tool-group .tool-group-header:hover {
1418
+ opacity: 1;
1419
+ background: rgba(var(--overlay-rgb), 0.08);
1420
+ }
1421
+ body.mate-dm-active .mate-tool-group .tool-group-label {
1422
+ font-size: 12px;
1423
+ }
1424
+ body.mate-dm-active .mate-tool-group .tool-item {
1425
+ border: none;
1426
+ background: none;
1427
+ padding: 2px 0;
1428
+ }
1429
+ body.mate-dm-active .mate-tool-group .tool-name {
1430
+ font-size: 13px;
1431
+ color: var(--text-muted);
1432
+ }
1433
+
1434
+ /* --- Mate Permission: conversational style --- */
1435
+ body.mate-dm-active .mate-permission {
1436
+ border-radius: 12px;
1437
+ border: 1px solid var(--border);
1438
+ background: var(--bg-alt);
1439
+ overflow: hidden;
1440
+ }
1441
+ body.mate-dm-active .mate-permission .permission-header {
1442
+ display: flex;
1443
+ align-items: center;
1444
+ gap: 10px;
1445
+ padding: 12px 16px 8px;
1446
+ }
1447
+ .mate-permission-avatar {
1448
+ width: 24px;
1449
+ height: 24px;
1450
+ border-radius: 6px;
1451
+ flex-shrink: 0;
1452
+ }
1453
+ body.mate-dm-active .mate-permission .permission-title {
1454
+ font-size: 14px;
1455
+ font-weight: 600;
1456
+ }
1457
+ body.mate-dm-active .mate-permission .permission-actions {
1458
+ padding: 8px 16px 12px;
1459
+ gap: 6px;
1460
+ }
1461
+
1462
+ /* --- Mate Activity: avatar + text (hidden, dots row is enough) --- */
1463
+ body.mate-dm-active .activity-inline {
1464
+ display: none;
1465
+ }
1466
+
1326
1467
  /* --- Interstitial elements: indent to align with message content (16px pad + 36px avatar + 8px gap = 60px) --- */
1327
1468
  body.mate-dm-active .thinking-item,
1328
1469
  body.mate-dm-active .tool-item,
@@ -112,9 +112,16 @@ function updateToolGroupHeader(group) {
112
112
  refreshIcons();
113
113
  }
114
114
 
115
- // Show group header only when 2+ visible tools
115
+ // Show group header only when 2+ visible tools (or always in mate DM)
116
116
  var header = group.el.querySelector(".tool-group-header");
117
- if (group.toolCount >= 2) {
117
+ var isMate = group.el.classList.contains("mate-tool-group");
118
+ if (isMate) {
119
+ // Mate DM: always show header and collapse
120
+ header.style.display = "";
121
+ if (!group.userToggled) {
122
+ group.el.classList.add("collapsed");
123
+ }
124
+ } else if (group.toolCount >= 2) {
118
125
  header.style.display = "";
119
126
  // When 2+ tools, ensure collapsed by default (unless user already toggled)
120
127
  if (!group.userToggled && !group.el.classList.contains("expanded-by-user")) {
@@ -380,14 +387,23 @@ export function renderPermissionRequest(requestId, toolName, toolInput, decision
380
387
 
381
388
  var container = document.createElement("div");
382
389
  container.className = "permission-container";
390
+ if (ctx.isMateDm()) container.classList.add("mate-permission");
383
391
  container.dataset.requestId = requestId;
384
392
 
385
393
  // Header
386
394
  var header = document.createElement("div");
387
395
  header.className = "permission-header";
388
- header.innerHTML =
389
- '<span class="permission-icon">' + iconHtml("shield") + '</span>' +
390
- '<span class="permission-title">Permission Required</span>';
396
+ if (ctx.isMateDm()) {
397
+ var mateName = ctx.getMateName();
398
+ var mateAvatar = ctx.getMateAvatarUrl();
399
+ header.innerHTML =
400
+ '<img class="mate-permission-avatar" src="' + escapeHtml(mateAvatar) + '" alt="">' +
401
+ '<span class="permission-title">' + escapeHtml(mateName) + ' wants to do something</span>';
402
+ } else {
403
+ header.innerHTML =
404
+ '<span class="permission-icon">' + iconHtml("shield") + '</span>' +
405
+ '<span class="permission-title">Permission Required</span>';
406
+ }
391
407
 
392
408
  // Body
393
409
  var body = document.createElement("div");
@@ -1182,14 +1198,36 @@ export function startThinking() {
1182
1198
 
1183
1199
  var el = document.createElement("div");
1184
1200
  el.className = "thinking-item";
1185
- el.innerHTML =
1186
- '<div class="thinking-header">' +
1187
- '<span class="thinking-chevron">' + iconHtml("chevron-right") + '</span>' +
1188
- '<span class="thinking-label">Thinking</span>' +
1189
- '<span class="thinking-duration"></span>' +
1190
- '<span class="thinking-spinner">' + iconHtml("loader", "icon-spin") + '</span>' +
1191
- '</div>' +
1192
- '<div class="thinking-content"></div>';
1201
+
1202
+ if (ctx.isMateDm()) {
1203
+ var mateName = ctx.getMateName();
1204
+ var mateAvatar = ctx.getMateAvatarUrl();
1205
+ el.classList.add("mate-thinking");
1206
+ el.innerHTML =
1207
+ '<div class="mate-thinking-row">' +
1208
+ '<img class="mate-thinking-avatar" src="' + escapeHtml(mateAvatar) + '" alt="">' +
1209
+ '<div class="mate-thinking-body">' +
1210
+ '<span class="mate-thinking-name">' + escapeHtml(mateName) + '</span>' +
1211
+ '<span class="mate-thinking-dots"><span></span><span></span><span></span></span>' +
1212
+ '</div>' +
1213
+ '</div>' +
1214
+ '<div class="thinking-header" style="display:none">' +
1215
+ '<span class="thinking-chevron">' + iconHtml("chevron-right") + '</span>' +
1216
+ '<span class="thinking-label">Thinking</span>' +
1217
+ '<span class="thinking-duration"></span>' +
1218
+ '<span class="thinking-spinner">' + iconHtml("loader", "icon-spin") + '</span>' +
1219
+ '</div>' +
1220
+ '<div class="thinking-content"></div>';
1221
+ } else {
1222
+ el.innerHTML =
1223
+ '<div class="thinking-header">' +
1224
+ '<span class="thinking-chevron">' + iconHtml("chevron-right") + '</span>' +
1225
+ '<span class="thinking-label">Thinking</span>' +
1226
+ '<span class="thinking-duration"></span>' +
1227
+ '<span class="thinking-spinner">' + iconHtml("loader", "icon-spin") + '</span>' +
1228
+ '</div>' +
1229
+ '<div class="thinking-content"></div>';
1230
+ }
1193
1231
 
1194
1232
  el.querySelector(".thinking-header").addEventListener("click", function () {
1195
1233
  el.classList.toggle("expanded");
@@ -1221,6 +1259,16 @@ export function stopThinking(duration) {
1221
1259
  } else {
1222
1260
  currentThinking.el.querySelector(".thinking-duration").textContent = " " + secs.toFixed(1) + "s";
1223
1261
  }
1262
+ // In mate mode: hide dots, show compact expandable thinking header
1263
+ if (currentThinking.el.classList.contains("mate-thinking")) {
1264
+ var dotsRow = currentThinking.el.querySelector(".mate-thinking-row");
1265
+ if (dotsRow) dotsRow.style.display = "none";
1266
+ var header = currentThinking.el.querySelector(".thinking-header");
1267
+ if (header) {
1268
+ header.style.display = "";
1269
+ header.style.cursor = "pointer";
1270
+ }
1271
+ }
1224
1272
  currentThinking = null;
1225
1273
  }
1226
1274
 
@@ -1238,6 +1286,7 @@ export function createToolItem(id, name) {
1238
1286
  toolGroupCounter++;
1239
1287
  var groupEl = document.createElement("div");
1240
1288
  groupEl.className = "tool-group";
1289
+ if (ctx.isMateDm()) groupEl.classList.add("mate-tool-group");
1241
1290
  groupEl.dataset.groupId = "g" + toolGroupCounter;
1242
1291
  groupEl.innerHTML =
1243
1292
  '<div class="tool-group-header" style="display:none">' +
@@ -1250,6 +1299,7 @@ export function createToolItem(id, name) {
1250
1299
 
1251
1300
  groupEl.querySelector(".tool-group-header").addEventListener("click", function () {
1252
1301
  groupEl.classList.toggle("collapsed");
1302
+ if (currentToolGroup) currentToolGroup.userToggled = true;
1253
1303
  });
1254
1304
 
1255
1305
  ctx.addToMessages(groupEl);
package/lib/sdk-bridge.js CHANGED
@@ -857,6 +857,8 @@ function createSDKBridge(opts) {
857
857
  signal: session.abortController ? { addEventListener: function() {} } : undefined,
858
858
  }).then(function(result) {
859
859
  worker.send({ type: "permission_response", requestId: msg.requestId, result: result });
860
+ }).catch(function(e) {
861
+ console.error("[sdk-bridge] permission_response send failed:", e.message || e);
860
862
  });
861
863
  break;
862
864
 
@@ -867,6 +869,8 @@ function createSDKBridge(opts) {
867
869
  signal: session.abortController ? { addEventListener: function() {} } : undefined,
868
870
  }).then(function(result) {
869
871
  worker.send({ type: "ask_user_response", toolUseId: msg.toolUseId, result: result });
872
+ }).catch(function(e) {
873
+ console.error("[sdk-bridge] ask_user_response send failed:", e.message || e);
870
874
  });
871
875
  break;
872
876
 
@@ -882,6 +886,8 @@ function createSDKBridge(opts) {
882
886
  signal: session.abortController ? { addEventListener: function() {} } : undefined,
883
887
  }).then(function(result) {
884
888
  worker.send({ type: "elicitation_response", requestId: msg.requestId, result: result });
889
+ }).catch(function(e) {
890
+ console.error("[sdk-bridge] elicitation_response send failed:", e.message || e);
885
891
  });
886
892
  break;
887
893
 
package/lib/sessions.js CHANGED
@@ -628,6 +628,8 @@ function createSessionManager(opts) {
628
628
  }
629
629
  chain.then(function() {
630
630
  console.log("[session] Migrated " + toMigrate.length + " session title(s) to SDK format");
631
+ }).catch(function(e) {
632
+ console.error("[session] Migration chain failed:", e.message || e);
631
633
  });
632
634
  }).catch(function() {});
633
635
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "clay-server",
3
- "version": "2.13.1-beta.1",
3
+ "version": "2.14.0-beta.2",
4
4
  "description": "Web UI for Claude Code. Any device. Push notifications.",
5
5
  "bin": {
6
6
  "clay-server": "./bin/cli.js",