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
|
|
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
|
-
|
|
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
|
-
|
|
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