clay-server 3.4.0-beta.16 → 3.4.0-beta.18

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.
@@ -154,6 +154,18 @@ function attachWorkerProposal(ctx) {
154
154
  }, patch));
155
155
  }
156
156
 
157
+ function skipPermissionsEnabled(session) {
158
+ return !!session && (session.permissionMode === "bypassPermissions" || session.dangerouslySkipPermissions === true);
159
+ }
160
+
161
+ function autoApprovalWs(session) {
162
+ return {
163
+ _clayActiveSession: session.localId,
164
+ _clayUser: session.ownerId ? { id: session.ownerId } : null,
165
+ _autoApproval: true,
166
+ };
167
+ }
168
+
157
169
  async function propose(args, session) {
158
170
  if (!isEligible(session)) return toolResult({ error: "Worker suggestions are only available in an unpaired Fable session." });
159
171
  if (hasPendingProposal(session)) return toolResult({ error: "A Worker suggestion is already awaiting a decision." });
@@ -166,6 +178,7 @@ function attachWorkerProposal(ctx) {
166
178
  var options = proposalOptions();
167
179
  if (options.installedVendors.length === 0) return toolResult({ error: "No coding agent is installed for a Worker session." });
168
180
  var recommendation = chooseRecommendation(args, session, options);
181
+ var autoApprove = skipPermissionsEnabled(session);
169
182
  var proposal = {
170
183
  type: "worker_proposal",
171
184
  proposalId: "worker_" + crypto.randomUUID(),
@@ -178,7 +191,22 @@ function attachWorkerProposal(ctx) {
178
191
  recommendedEffort: recommendation.effort,
179
192
  options: options,
180
193
  };
194
+ if (autoApprove) proposal.autoApproved = true;
181
195
  sm.sendAndRecord(session, proposal);
196
+ if (autoApprove) {
197
+ var accepted = await acceptProposal(session, proposal, {
198
+ vendor: recommendation.vendor,
199
+ model: recommendation.model,
200
+ effort: recommendation.effort,
201
+ autoApproved: true,
202
+ }, autoApprovalWs(session));
203
+ if (!accepted.ok) return toolResult({ error: accepted.error || "Could not start the Worker." });
204
+ return toolResult({
205
+ status: "running",
206
+ proposalId: proposal.proposalId,
207
+ instruction: "The Worker was auto-approved and started because skip permissions is enabled. Its result will return for review.",
208
+ });
209
+ }
182
210
  return toolResult({
183
211
  status: "posted",
184
212
  proposalId: proposal.proposalId,
@@ -238,30 +266,24 @@ function attachWorkerProposal(ctx) {
238
266
  return session;
239
267
  }
240
268
 
241
- async function respondToProposal(ws, msg) {
242
- var session = sessionForResponse(ws);
243
- var proposal = findProposal(session, msg.proposalId);
244
- if (!proposal) throw new Error("Worker suggestion not found");
245
- if (proposal.status !== "pending") throw new Error("Worker suggestion has already been resolved");
246
- if (!msg.accepted) {
247
- updateProposal(session, proposal, { status: "declined" });
248
- await resumeDriver(session, "[Worker suggestion declined]\nContinue this task in the current session using the plan you already prepared.");
249
- return { ok: true, status: "declined" };
250
- }
269
+ async function acceptProposal(session, proposal, msg, ws) {
251
270
  var options = proposal.options || proposalOptions();
252
271
  var vendor = msg.vendor || proposal.recommendedVendor;
253
272
  var model = msg.model || "";
254
273
  if (options.installedVendors.indexOf(vendor) === -1) throw new Error("Selected Worker vendor is not installed");
255
274
  if (!modelIsAvailable(options, vendor, model)) throw new Error("Selected Worker model is unavailable");
256
275
  var effort = yoke.clampEffort(vendor, msg.effort || proposal.recommendedEffort || "medium") || "";
257
- updateProposal(session, proposal, { status: "starting", selectedVendor: vendor, selectedModel: model, selectedEffort: effort });
276
+ var startingPatch = { status: "starting", selectedVendor: vendor, selectedModel: model, selectedEffort: effort };
277
+ if (msg.autoApproved) startingPatch.autoApproved = true;
278
+ updateProposal(session, proposal, startingPatch);
258
279
  try {
259
280
  var created = ctx.createPairRecord(ws, {
260
281
  driver: { sessionId: session.localId },
261
282
  worker: { vendor: vendor, model: model, effort: effort },
262
283
  });
263
284
  updateProposal(session, proposal, { status: "running", groupId: created.group.id, workerId: created.worker.localId });
264
- ctx.sendTo(ws, { type: "pair_session_created", ok: true, group: created.group });
285
+ if (ws._autoApproval) sm.sendToSession(session, { type: "pair_session_created", ok: true, group: created.group });
286
+ else ctx.sendTo(ws, { type: "pair_session_created", ok: true, group: created.group });
265
287
  runWorker(session, proposal).catch(function (err) {
266
288
  updateProposal(session, proposal, { status: "error", error: err.message || String(err) });
267
289
  resumeDriver(session, "[Worker execution failed]\n" + (err.message || String(err))).catch(function () {});
@@ -273,6 +295,19 @@ function attachWorkerProposal(ctx) {
273
295
  }
274
296
  }
275
297
 
298
+ async function respondToProposal(ws, msg) {
299
+ var session = sessionForResponse(ws);
300
+ var proposal = findProposal(session, msg.proposalId);
301
+ if (!proposal) throw new Error("Worker suggestion not found");
302
+ if (proposal.status !== "pending") throw new Error("Worker suggestion has already been resolved");
303
+ if (!msg.accepted) {
304
+ updateProposal(session, proposal, { status: "declined" });
305
+ await resumeDriver(session, "[Worker suggestion declined]\nContinue this task in the current session using the plan you already prepared.");
306
+ return { ok: true, status: "declined" };
307
+ }
308
+ return acceptProposal(session, proposal, msg, ws);
309
+ }
310
+
276
311
  function handleMessage(ws, msg) {
277
312
  if (msg.type !== "worker_proposal_response") return false;
278
313
  respondToProposal(ws, msg).catch(function (err) {
@@ -103,8 +103,10 @@ function statusLabel(status) {
103
103
  function applyState(card, msg) {
104
104
  var status = msg.status || "pending";
105
105
  card.dataset.status = status;
106
+ if (msg.autoApproved) card.dataset.autoApproved = "true";
107
+ var autoApproved = card.dataset.autoApproved === "true";
106
108
  var badge = card.querySelector(".worker-proposal-status");
107
- if (badge) badge.textContent = statusLabel(status);
109
+ if (badge) badge.textContent = statusLabel(status) + (autoApproved ? " · auto-approved" : "");
108
110
  var error = card.querySelector(".worker-proposal-error");
109
111
  if (error) {
110
112
  error.textContent = msg.error || "";
@@ -116,6 +118,8 @@ function applyState(card, msg) {
116
118
  preview.classList.remove("hidden");
117
119
  }
118
120
  setControlsDisabled(card, status !== "pending");
121
+ var actions = card.querySelector(".worker-proposal-actions");
122
+ if (actions) actions.classList.toggle("hidden", autoApproved);
119
123
  }
120
124
 
121
125
  function sendDecision(card, accepted) {
@@ -7,6 +7,7 @@ var MEMORY_CONTRACT =
7
7
  "Default to not writing. Create a note proactively only when the user explicitly asks to remember or track something, or when all of these are true: it will remain useful after the current task and session, it is not already adequately recorded in the repository or another note, and the user would likely be glad to find it on the board a week later. " +
8
8
  "Good notes capture an unresolved commitment, durable product decision, user preference, constraint, or handoff that will materially change future work. " +
9
9
  "Important exception for deferred defects: while doing code or technical work, actively create a sticky note when you discover a concrete defect, regression risk, security issue, or data-loss risk that is outside the current session goal and will remain unfixed when the turn ends. Do not wait for the user to ask. Include the observable evidence, affected component, likely impact, and a clear next action. Check active notes first when a duplicate is plausible. Do not create defect notes for speculation, general cleanup ideas, or problems you fixed in the current session. " +
10
+ "Important exception for deferred proposals: when you propose work (a fix, follow-up, improvement, or next step) and the user defers it rather than declining it (\"let's do that later\", \"next time\", \"after this\"), actively create a sticky note before the topic moves on. Do not wait to be asked. Capture what was proposed, why it mattered, and the agreed timing if any. Deferred agreements scroll out of chat history quickly and are hard to track; the board is where they survive. If the user declines the idea outright, do not write a note. " +
10
11
  "Never create a note merely because work is important, lengthy, spans agents or restarts, or might help another agent. Do not record completed work, implementation details, test results, investigation logs, transient blockers, conversation summaries, or announcements of your own activity. When uncertain, do not write. " +
11
12
  "Updates are visible too: update only when durable state materially changes, and remove a note created by your session when it stops being useful instead of turning it into a completion log. Put a concise plain-text title on the first line, stay focused on one topic, and include only the context needed for future action.";
12
13
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "clay-server",
3
- "version": "3.4.0-beta.16",
3
+ "version": "3.4.0-beta.18",
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",