clay-server 4.7.0-beta.3 → 4.7.0-beta.4

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.
@@ -387,6 +387,7 @@ function attachPairResultOutbox(options) {
387
387
  markRestartUncertain: markRestartUncertain,
388
388
  authorizeHumanTurn: authorizeHumanTurn,
389
389
  grantManualRetry: grantManualRetry,
390
+ dismissNotice: function (key, revision) { return transition(key, { dismissedNoticeRevision: revision }); },
390
391
  status: status,
391
392
  list: function () { return Object.keys(records).map(function (key) { return copy(records[key]); }); },
392
393
  get: function (key) { return records[key] ? copy(records[key]) : null; },
@@ -146,6 +146,11 @@ function attachPairResultRecovery(ctx) {
146
146
  confirmDuplicate: completed && state === "uncertain",
147
147
  };
148
148
  }
149
+ function noticeItem(record) {
150
+ var item = publicItem(record);
151
+ item.revision = opaqueId(JSON.stringify(item));
152
+ return item;
153
+ }
149
154
  function canRead(ws, session) {
150
155
  if (!ws || !session || ws._clayActiveSession !== session.localId) return false;
151
156
  if (ownerId(session)) return !!(ws._clayUser && String(ws._clayUser.id) === String(ownerId(session)));
@@ -153,7 +158,9 @@ function attachPairResultRecovery(ctx) {
153
158
  }
154
159
  function sendState(session, ws, sendFn) {
155
160
  if (!canRead(ws, session)) return false;
156
- var items = recordsFor(session).map(publicItem);
161
+ var items = recordsFor(session).filter(function (record) {
162
+ return record.dismissedNoticeRevision !== noticeItem(record).revision;
163
+ }).map(noticeItem);
157
164
  (sendFn || ctx.sendTo.bind(null, ws))({ type: "pair_result_status", sessionId: session.localId, items: items });
158
165
  return true;
159
166
  }
@@ -204,7 +211,7 @@ function attachPairResultRecovery(ctx) {
204
211
  changed(session);
205
212
  }
206
213
  function handleMessage(ws, msg) {
207
- if (!msg || (msg.type !== "pair_result_state_request" && msg.type !== "pair_result_retry")) return false;
214
+ if (!msg || (msg.type !== "pair_result_state_request" && msg.type !== "pair_result_retry" && msg.type !== "pair_result_dismiss")) return false;
208
215
  var session = ctx.sm.sessions.get(ws._clayActiveSession);
209
216
  if (!canRead(ws, session)) return true;
210
217
  if (msg.sessionId != null && msg.sessionId !== session.localId) return true;
@@ -213,6 +220,12 @@ function attachPairResultRecovery(ctx) {
213
220
  var records = recordsFor(session), record = null;
214
221
  for (var i = 0; i < records.length; i++) if (opaqueId(records[i].key) === msg.id) record = records[i];
215
222
  if (!record) { sendState(session, ws); return true; }
223
+ if (msg.type === "pair_result_dismiss") {
224
+ if (msg.revision === noticeItem(record).revision) ctx.outbox.dismissNotice(record.key, msg.revision);
225
+ changed(session);
226
+ sendState(session, ws);
227
+ return true;
228
+ }
216
229
  if (record.deliveryState === "uncertain" && msg.confirmDuplicate !== true) { sendState(session, ws); return true; }
217
230
  var granted = ctx.outbox.grantManualRetry(record.key);
218
231
  if (granted.ok) schedule(record.key);
@@ -1387,17 +1387,24 @@ pre.mermaid-error {
1387
1387
  .plan-banner.plan-exit { color: var(--success); }
1388
1388
  .plan-banner.plan-exit .plan-banner-icon { color: var(--success); }
1389
1389
 
1390
- .pair-result-status { display: grid; gap: 8px; padding: 10px 16px 0; }
1391
- .pair-result-status-card { border: 1px solid var(--border); border-radius: 10px; background: var(--bg-alt); padding: 10px 12px; color: var(--text-secondary); font-size: 12px; }
1390
+ /* Recovery notices use the same quiet surface as session work references. */
1391
+ .pair-result-status { flex: 0 0 auto; margin: 8px 16px 0; padding: 8px 10px; border: 1px solid var(--border); border-radius: 6px; background: var(--bg-alt); color: var(--text-muted); font-size: 12px; line-height: 1.5; max-height: 35vh; overflow-y: auto; }
1392
+ .pair-result-status-header { display: flex; align-items: center; justify-content: space-between; gap: 12px; color: var(--text-secondary); }
1393
+ .pair-result-status button { font: inherit; color: var(--text-secondary); background: transparent; border: 1px solid var(--border); border-radius: 5px; cursor: pointer; }
1394
+ .pair-result-status .pair-result-status-dismiss { display: grid; place-items: center; flex: 0 0 28px; width: 28px; height: 28px; padding: 0; border: 0; font-size: 20px; line-height: 1; }
1395
+ .pair-result-status button:hover { background: var(--input-bg); color: var(--text); }
1396
+ .pair-result-status button:focus-visible, .pair-result-status summary:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
1397
+ .pair-result-status-details > summary { cursor: pointer; width: fit-content; }
1398
+ .pair-result-status-card { padding: 10px 0; border-top: 1px solid var(--border); margin-top: 8px; }
1392
1399
  .pair-result-status-card > strong, .pair-result-status-card > span { display: block; }
1393
- .pair-result-status-card > strong { color: var(--text); margin-bottom: 3px; }
1400
+ .pair-result-status-card > strong { color: var(--text-secondary); font-weight: 500; margin-bottom: 3px; }
1394
1401
  .pair-result-status-card details { margin-top: 7px; }
1395
- .pair-result-status-card summary { cursor: pointer; color: var(--accent); }
1396
- .pair-result-status-card pre { max-height: 180px; overflow: auto; white-space: pre-wrap; font: inherit; }
1397
- .pair-result-status-card button { margin-top: 8px; }
1398
- .pair-result-status-retry:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
1402
+ .pair-result-status-card summary { cursor: pointer; }
1403
+ .pair-result-status-card pre { max-height: 180px; overflow: auto; white-space: pre-wrap; overflow-wrap: anywhere; font: inherit; }
1404
+ .pair-result-status-retry { margin-top: 8px; padding: 4px 8px; }
1399
1405
  .pair-result-status-retry:disabled { cursor: not-allowed; opacity: 0.55; }
1400
- .pair-result-status-uncertain, .pair-result-status-blocked { border-color: color-mix(in srgb, var(--warning) 45%, var(--border)); }
1406
+ @media (max-width: 600px) { .pair-result-status { margin: 6px 8px 0; } .pair-result-status .pair-result-status-dismiss { min-width: 36px; height: 36px; } }
1407
+
1401
1408
 
1402
1409
  /* --- Plan card --- */
1403
1410
  .plan-card {
@@ -33,6 +33,16 @@ function retry(item) {
33
33
  sendRetry(source, false);
34
34
  }
35
35
 
36
+ function dismiss(items) {
37
+ var ws = getWs();
38
+ if (!ws || ws.readyState !== 1) return;
39
+ items.forEach(function (item) {
40
+ ws.send(JSON.stringify({ type: "pair_result_dismiss", id: item.id, revision: item.revision,
41
+ sessionId: store.get("activeSessionId"),
42
+ projectSlug: store.get("currentSlug") || store.get("activeProjectSlug") || null }));
43
+ });
44
+ }
45
+
36
46
  function renderPairResultStatus() {
37
47
  var existing = document.getElementById("pair-result-status");
38
48
  if (existing) existing.remove();
@@ -44,6 +54,21 @@ function renderPairResultStatus() {
44
54
  section.id = "pair-result-status";
45
55
  section.className = "pair-result-status";
46
56
  section.setAttribute("role", "status");
57
+ var header = document.createElement("div");
58
+ header.className = "pair-result-status-header";
59
+ var heading = document.createElement("span");
60
+ heading.textContent = items.length === 1 ? "Worker update needs attention" : items.length + " Worker updates need attention";
61
+ var close = document.createElement("button");
62
+ close.type = "button"; close.className = "pair-result-status-dismiss";
63
+ close.textContent = "×";
64
+ close.setAttribute("aria-label", "Dismiss Worker notices");
65
+ close.title = "Dismiss notices. Retained tasks and results are kept.";
66
+ close.addEventListener("click", dismiss.bind(null, items));
67
+ header.appendChild(heading); header.appendChild(close); section.appendChild(header);
68
+ var content = document.createElement("details"), toggle = document.createElement("summary");
69
+ content.className = "pair-result-status-details";
70
+ toggle.textContent = "Review updates";
71
+ content.appendChild(toggle); section.appendChild(content);
47
72
  for (var i = 0; i < items.length; i++) {
48
73
  var item = items[i], card = document.createElement("div");
49
74
  card.className = "pair-result-status-card pair-result-status-" + item.state;
@@ -61,11 +86,11 @@ function renderPairResultStatus() {
61
86
  }
62
87
  if (item.canRetry) {
63
88
  var button = document.createElement("button");
64
- button.type = "button"; button.className = "confirm-btn confirm-ok pair-result-status-retry"; button.textContent = "Retry delivery";
89
+ button.type = "button"; button.className = "pair-result-status-retry"; button.textContent = "Retry delivery";
65
90
  button.addEventListener("click", retry.bind(null, item));
66
91
  card.appendChild(button);
67
92
  }
68
- section.appendChild(card);
93
+ content.appendChild(card);
69
94
  }
70
95
  messages.parentElement.insertBefore(section, messages);
71
96
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "clay-server",
3
- "version": "4.7.0-beta.3",
3
+ "version": "4.7.0-beta.4",
4
4
  "description": "Self-hosted team workspace for Claude Code and Codex. Multi-user, browser-based, with persistent AI mates.",
5
5
  "bin": {
6
6
  "clay-server": "./bin/cli.js"