clay-server 3.4.0-beta.17 → 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) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "clay-server",
3
- "version": "3.4.0-beta.17",
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",