clay-server 2.13.0 → 2.14.0-beta.1

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,123 @@ 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 normal thinking 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
+ }
1386
+
1387
+ /* --- Mate Tool Group: subtle, compact --- */
1388
+ body.mate-dm-active .mate-tool-group .tool-item {
1389
+ border: none;
1390
+ background: none;
1391
+ padding: 2px 0;
1392
+ }
1393
+ body.mate-dm-active .mate-tool-group .tool-name {
1394
+ font-size: 13px;
1395
+ color: var(--text-muted);
1396
+ }
1397
+
1398
+ /* --- Mate Permission: conversational style --- */
1399
+ body.mate-dm-active .mate-permission {
1400
+ border-radius: 12px;
1401
+ border: 1px solid var(--border);
1402
+ background: var(--bg-alt);
1403
+ overflow: hidden;
1404
+ }
1405
+ body.mate-dm-active .mate-permission .permission-header {
1406
+ display: flex;
1407
+ align-items: center;
1408
+ gap: 10px;
1409
+ padding: 12px 16px 8px;
1410
+ }
1411
+ .mate-permission-avatar {
1412
+ width: 24px;
1413
+ height: 24px;
1414
+ border-radius: 6px;
1415
+ flex-shrink: 0;
1416
+ }
1417
+ body.mate-dm-active .mate-permission .permission-title {
1418
+ font-size: 14px;
1419
+ font-weight: 600;
1420
+ }
1421
+ body.mate-dm-active .mate-permission .permission-actions {
1422
+ padding: 8px 16px 12px;
1423
+ gap: 6px;
1424
+ }
1425
+
1426
+ /* --- Mate Activity: avatar + text --- */
1427
+ body.mate-dm-active .mate-activity {
1428
+ display: flex;
1429
+ align-items: center;
1430
+ gap: 8px;
1431
+ }
1432
+ .mate-activity-avatar {
1433
+ width: 20px;
1434
+ height: 20px;
1435
+ border-radius: 4px;
1436
+ flex-shrink: 0;
1437
+ }
1438
+ body.mate-dm-active .mate-activity .activity-text {
1439
+ font-size: 13px;
1440
+ color: var(--text-muted);
1441
+ }
1442
+
1326
1443
  /* --- Interstitial elements: indent to align with message content (16px pad + 36px avatar + 8px gap = 60px) --- */
1327
1444
  body.mate-dm-active .thinking-item,
1328
1445
  body.mate-dm-active .tool-item,
@@ -1336,4 +1453,6 @@ body.mate-dm-active .context-overflow-msg,
1336
1453
  body.mate-dm-active .sys-msg,
1337
1454
  body.mate-dm-active .activity-inline {
1338
1455
  padding-left: 60px;
1456
+ margin-left: 0;
1457
+ margin-right: 0;
1339
1458
  }
@@ -380,14 +380,23 @@ export function renderPermissionRequest(requestId, toolName, toolInput, decision
380
380
 
381
381
  var container = document.createElement("div");
382
382
  container.className = "permission-container";
383
+ if (ctx.isMateDm()) container.classList.add("mate-permission");
383
384
  container.dataset.requestId = requestId;
384
385
 
385
386
  // Header
386
387
  var header = document.createElement("div");
387
388
  header.className = "permission-header";
388
- header.innerHTML =
389
- '<span class="permission-icon">' + iconHtml("shield") + '</span>' +
390
- '<span class="permission-title">Permission Required</span>';
389
+ if (ctx.isMateDm()) {
390
+ var mateName = ctx.getMateName();
391
+ var mateAvatar = ctx.getMateAvatarUrl();
392
+ header.innerHTML =
393
+ '<img class="mate-permission-avatar" src="' + escapeHtml(mateAvatar) + '" alt="">' +
394
+ '<span class="permission-title">' + escapeHtml(mateName) + ' wants to do something</span>';
395
+ } else {
396
+ header.innerHTML =
397
+ '<span class="permission-icon">' + iconHtml("shield") + '</span>' +
398
+ '<span class="permission-title">Permission Required</span>';
399
+ }
391
400
 
392
401
  // Body
393
402
  var body = document.createElement("div");
@@ -1182,14 +1191,36 @@ export function startThinking() {
1182
1191
 
1183
1192
  var el = document.createElement("div");
1184
1193
  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>';
1194
+
1195
+ if (ctx.isMateDm()) {
1196
+ var mateName = ctx.getMateName();
1197
+ var mateAvatar = ctx.getMateAvatarUrl();
1198
+ el.classList.add("mate-thinking");
1199
+ el.innerHTML =
1200
+ '<div class="mate-thinking-row">' +
1201
+ '<img class="mate-thinking-avatar" src="' + escapeHtml(mateAvatar) + '" alt="">' +
1202
+ '<div class="mate-thinking-body">' +
1203
+ '<span class="mate-thinking-name">' + escapeHtml(mateName) + '</span>' +
1204
+ '<span class="mate-thinking-dots"><span></span><span></span><span></span></span>' +
1205
+ '</div>' +
1206
+ '</div>' +
1207
+ '<div class="thinking-header" style="display:none">' +
1208
+ '<span class="thinking-chevron">' + iconHtml("chevron-right") + '</span>' +
1209
+ '<span class="thinking-label">Thinking</span>' +
1210
+ '<span class="thinking-duration"></span>' +
1211
+ '<span class="thinking-spinner">' + iconHtml("loader", "icon-spin") + '</span>' +
1212
+ '</div>' +
1213
+ '<div class="thinking-content"></div>';
1214
+ } else {
1215
+ el.innerHTML =
1216
+ '<div class="thinking-header">' +
1217
+ '<span class="thinking-chevron">' + iconHtml("chevron-right") + '</span>' +
1218
+ '<span class="thinking-label">Thinking</span>' +
1219
+ '<span class="thinking-duration"></span>' +
1220
+ '<span class="thinking-spinner">' + iconHtml("loader", "icon-spin") + '</span>' +
1221
+ '</div>' +
1222
+ '<div class="thinking-content"></div>';
1223
+ }
1193
1224
 
1194
1225
  el.querySelector(".thinking-header").addEventListener("click", function () {
1195
1226
  el.classList.toggle("expanded");
@@ -1221,6 +1252,13 @@ export function stopThinking(duration) {
1221
1252
  } else {
1222
1253
  currentThinking.el.querySelector(".thinking-duration").textContent = " " + secs.toFixed(1) + "s";
1223
1254
  }
1255
+ // In mate mode: hide dots, show expandable thinking header
1256
+ if (currentThinking.el.classList.contains("mate-thinking")) {
1257
+ var dotsRow = currentThinking.el.querySelector(".mate-thinking-row");
1258
+ if (dotsRow) dotsRow.style.display = "none";
1259
+ var header = currentThinking.el.querySelector(".thinking-header");
1260
+ if (header) header.style.display = "";
1261
+ }
1224
1262
  currentThinking = null;
1225
1263
  }
1226
1264
 
@@ -1238,6 +1276,7 @@ export function createToolItem(id, name) {
1238
1276
  toolGroupCounter++;
1239
1277
  var groupEl = document.createElement("div");
1240
1278
  groupEl.className = "tool-group";
1279
+ if (ctx.isMateDm()) groupEl.classList.add("mate-tool-group");
1241
1280
  groupEl.dataset.groupId = "g" + toolGroupCounter;
1242
1281
  groupEl.innerHTML =
1243
1282
  '<div class="tool-group-header" style="display:none">' +
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.0",
3
+ "version": "2.14.0-beta.1",
4
4
  "description": "Web UI for Claude Code. Any device. Push notifications.",
5
5
  "bin": {
6
6
  "clay-server": "./bin/cli.js",