clay-server 2.44.1-beta.1 → 2.45.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.
@@ -12,6 +12,12 @@ var crypto = require("crypto");
12
12
  var { spawn } = require("child_process");
13
13
  var { resolveOsUserInfo } = require("../../os-users");
14
14
 
15
+ // SDK-specific knowledge stays in the adapter (not the relay): the host dialog
16
+ // kinds this client can render, declared to the SDK when an onUserDialog
17
+ // callback is wired. The CLI fails closed — kinds not listed here are never
18
+ // emitted. Keep in sync with the client's generic user-dialog renderer.
19
+ var SUPPORTED_DIALOG_KINDS = ["refusal_fallback_prompt"];
20
+
15
21
  // --- SDK loading ---
16
22
  // Async loader (ESM dynamic import, same pattern as current project.js getSDK)
17
23
  var _sdkPromise = null;
@@ -182,6 +188,68 @@ function flattenEvent(raw) {
182
188
  base.summary = raw.summary || null;
183
189
  return base;
184
190
  }
191
+ // Model refusal: the model declined and the CLI either fell back to
192
+ // another model or ended the turn. Translate to a vendor-neutral
193
+ // yokeType so the relay never sees SDK subtype strings.
194
+ if (raw.subtype === "model_refusal_fallback") {
195
+ base.yokeType = "model_refusal";
196
+ base.refusalKind = "fallback";
197
+ base.originalModel = raw.original_model || null;
198
+ base.fallbackModel = raw.fallback_model || null;
199
+ base.direction = raw.direction || null;
200
+ base.category = raw.api_refusal_category || null;
201
+ return base;
202
+ }
203
+ if (raw.subtype === "model_refusal_no_fallback") {
204
+ base.yokeType = "model_refusal";
205
+ base.refusalKind = "no_fallback";
206
+ base.originalModel = raw.original_model || null;
207
+ base.category = raw.api_refusal_category || null;
208
+ base.explanation = raw.api_refusal_explanation || null;
209
+ base.content = raw.content || null;
210
+ return base;
211
+ }
212
+ // Slash-command list changed mid-session (e.g. skills discovered dynamically).
213
+ if (raw.subtype === "commands_changed") {
214
+ base.yokeType = "commands_changed";
215
+ base.commandNames = Array.isArray(raw.commands)
216
+ ? raw.commands.map(function(c) { return (c && typeof c === "object") ? c.name : c; }).filter(Boolean)
217
+ : [];
218
+ return base;
219
+ }
220
+ // The session worker is shutting down (reason is a host-set snake_case code).
221
+ if (raw.subtype === "worker_shutting_down") {
222
+ base.yokeType = "worker_shutting_down";
223
+ base.reason = raw.reason || "";
224
+ return base;
225
+ }
226
+ // Live thinking-token estimate (ephemeral progress).
227
+ if (raw.subtype === "thinking_tokens") {
228
+ base.yokeType = "thinking_tokens";
229
+ base.estimatedTokens = raw.estimated_tokens || 0;
230
+ base.estimatedTokensDelta = raw.estimated_tokens_delta || 0;
231
+ return base;
232
+ }
233
+ // Informational system message (render-leveled: info/notice/suggestion/warning).
234
+ if (raw.subtype === "informational") {
235
+ base.yokeType = "informational";
236
+ base.level = raw.level || "info";
237
+ base.content = raw.content || "";
238
+ base.toolUseId = raw.tool_use_id || null;
239
+ base.preventContinuation = !!raw.prevent_continuation;
240
+ return base;
241
+ }
242
+ // A tool call was denied (by classifier, rule, mode, or async agent).
243
+ if (raw.subtype === "permission_denied") {
244
+ base.yokeType = "permission_denied";
245
+ base.toolName = raw.tool_name || "";
246
+ base.toolUseId = raw.tool_use_id || null;
247
+ base.agentId = raw.agent_id || null;
248
+ base.reasonType = raw.decision_reason_type || null;
249
+ base.reason = raw.decision_reason || null;
250
+ base.message = raw.message || "";
251
+ return base;
252
+ }
185
253
  // Catch-all system event
186
254
  base.yokeType = "system";
187
255
  base.subtype = raw.subtype;
@@ -338,6 +406,20 @@ function createQueryHandle(rawQuery, messageQueue, abortController) {
338
406
  return Promise.resolve();
339
407
  },
340
408
 
409
+ reloadSkills: function() {
410
+ if (rawQuery && typeof rawQuery.reloadSkills === "function") {
411
+ return rawQuery.reloadSkills();
412
+ }
413
+ return Promise.resolve(null);
414
+ },
415
+
416
+ setMcpPermissionModeOverride: function(serverName, mode) {
417
+ if (rawQuery && typeof rawQuery.setMcpPermissionModeOverride === "function") {
418
+ return rawQuery.setMcpPermissionModeOverride(serverName, mode);
419
+ }
420
+ return Promise.resolve(null);
421
+ },
422
+
341
423
  getContextUsage: function() {
342
424
  if (rawQuery && typeof rawQuery.getContextUsage === "function") {
343
425
  return rawQuery.getContextUsage();
@@ -517,13 +599,16 @@ function spawnWorker(linuxUser, workerScriptPath, cwd) {
517
599
  console.log("[yoke/claude] Spawning worker: uid=" + userInfo.uid + " gid=" + userInfo.gid + " cwd=" + cwd + " socket=" + socketPath);
518
600
  console.log("[yoke/claude] Worker script: " + workerScriptPath);
519
601
  console.log("[yoke/claude] Node: " + process.execPath);
520
- worker.process = spawn(process.execPath, [workerScriptPath, socketPath], {
602
+ // Route through setpriv when group inheritance is enabled so the worker
603
+ // (and the agent CLI it spawns) inherits the user's supplementary groups.
604
+ var workerSpawn = require("../../os-users").wrapSpawnAsUser(process.execPath, [workerScriptPath, socketPath], {
521
605
  uid: userInfo.uid,
522
606
  gid: userInfo.gid,
523
607
  env: workerEnv,
524
608
  cwd: cwd,
525
609
  stdio: ["ignore", "pipe", "pipe"],
526
610
  });
611
+ worker.process = spawn(workerSpawn.command, workerSpawn.args, workerSpawn.options);
527
612
 
528
613
  worker.process.stdout.on("data", function(data) {
529
614
  console.log("[sdk-worker:" + linuxUser + "] " + data.toString().trim());
@@ -681,7 +766,7 @@ function serializeWorkerValue(value, seen) {
681
766
  // in-process QueryHandle. This allows processQueryStream to iterate a worker
682
767
  // query identically to an in-process query.
683
768
 
684
- function createWorkerQueryHandle(worker, canUseTool, onElicitation, callMcpTool) {
769
+ function createWorkerQueryHandle(worker, canUseTool, onElicitation, callMcpTool, onUserDialog) {
685
770
  // Async iterable state
686
771
  var iterQueue = [];
687
772
  var iterWaiting = null;
@@ -779,6 +864,24 @@ function createWorkerQueryHandle(worker, canUseTool, onElicitation, callMcpTool)
779
864
  }
780
865
  break;
781
866
 
867
+ case "user_dialog_request":
868
+ if (onUserDialog) {
869
+ onUserDialog({
870
+ dialogKind: msg.dialogKind,
871
+ payload: msg.payload || {},
872
+ toolUseID: msg.toolUseId || undefined,
873
+ }, {
874
+ signal: { addEventListener: function() {} },
875
+ }).then(function(result) {
876
+ worker.send({ type: "user_dialog_response", requestId: msg.requestId, result: result });
877
+ }).catch(function(e) {
878
+ console.error("[yoke/claude] user_dialog_response send failed:", e.message || e);
879
+ });
880
+ } else {
881
+ worker.send({ type: "user_dialog_response", requestId: msg.requestId, result: { behavior: "cancelled" } });
882
+ }
883
+ break;
884
+
782
885
  case "mcp_tool_call":
783
886
  if (callMcpTool) {
784
887
  callMcpTool(msg.serverName, msg.toolName, msg.args || {}).then(function(result) {
@@ -916,6 +1019,16 @@ function createWorkerQueryHandle(worker, canUseTool, onElicitation, callMcpTool)
916
1019
  return Promise.resolve();
917
1020
  },
918
1021
 
1022
+ reloadSkills: function() {
1023
+ worker.send({ type: "reload_skills" });
1024
+ return Promise.resolve(null);
1025
+ },
1026
+
1027
+ setMcpPermissionModeOverride: function(serverName, mode) {
1028
+ worker.send({ type: "set_mcp_permission_mode_override", serverName: serverName, mode: mode });
1029
+ return Promise.resolve(null);
1030
+ },
1031
+
919
1032
  getContextUsage: function() {
920
1033
  return Promise.resolve(null);
921
1034
  },
@@ -1215,6 +1328,10 @@ function createClaudeAdapter(opts) {
1215
1328
  if (queryOpts.toolServers) sdkOptions.mcpServers = queryOpts.toolServers;
1216
1329
  if (queryOpts.canUseTool) sdkOptions.canUseTool = queryOpts.canUseTool;
1217
1330
  if (queryOpts.onElicitation) sdkOptions.onElicitation = queryOpts.onElicitation;
1331
+ if (queryOpts.onUserDialog) {
1332
+ sdkOptions.onUserDialog = queryOpts.onUserDialog;
1333
+ sdkOptions.supportedDialogKinds = SUPPORTED_DIALOG_KINDS;
1334
+ }
1218
1335
  if (queryOpts.resumeSessionId) sdkOptions.resume = queryOpts.resumeSessionId;
1219
1336
 
1220
1337
  // Claude-specific options from adapterOptions.CLAUDE
@@ -1363,7 +1480,7 @@ function createClaudeAdapter(opts) {
1363
1480
  }
1364
1481
 
1365
1482
  // Create the worker query handle (sets up message handler on worker)
1366
- var handle = createWorkerQueryHandle(worker, queryOpts.canUseTool, queryOpts.onElicitation, queryOpts.callMcpTool);
1483
+ var handle = createWorkerQueryHandle(worker, queryOpts.canUseTool, queryOpts.onElicitation, queryOpts.callMcpTool, queryOpts.onUserDialog);
1367
1484
 
1368
1485
  // Wait for worker to be ready before sending query_start
1369
1486
  if (!reusingWorker) {
@@ -1390,6 +1507,7 @@ function createClaudeAdapter(opts) {
1390
1507
  if (claudeOpts.settings) queryOptions.settings = claudeOpts.settings;
1391
1508
 
1392
1509
  if (queryOpts.toolServerDescriptors) queryOptions.mcpServerDescriptors = queryOpts.toolServerDescriptors;
1510
+ if (queryOpts.onUserDialog) queryOptions.supportedDialogKinds = SUPPORTED_DIALOG_KINDS;
1393
1511
  if (queryOpts.model) queryOptions.model = queryOpts.model;
1394
1512
  if (queryOpts.effort) queryOptions.effort = queryOpts.effort;
1395
1513
  if (queryOpts.resumeSessionId) queryOptions.resume = queryOpts.resumeSessionId;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "clay-server",
3
- "version": "2.44.1-beta.1",
3
+ "version": "2.45.0-beta.1",
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",
@@ -48,7 +48,7 @@
48
48
  "homepage": "https://github.com/chadbyte/clay#readme",
49
49
  "author": "Chad",
50
50
  "dependencies": {
51
- "@anthropic-ai/claude-agent-sdk": "^0.2.132",
51
+ "@anthropic-ai/claude-agent-sdk": "^0.3.196",
52
52
  "@lydell/node-pty": "^1.2.0-beta.3",
53
53
  "@openai/codex": "^0.124.0",
54
54
  "imapflow": "^1.3.1",