clay-server 2.33.0-beta.2 → 2.33.0

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.
@@ -89,6 +89,23 @@ function generateUuid() {
89
89
  return "codex-" + ts + "-" + cnt + "-" + rnd;
90
90
  }
91
91
 
92
+ function normalizePlanStatus(status) {
93
+ if (status === "inProgress") return "in_progress";
94
+ if (status === "completed") return "completed";
95
+ return "pending";
96
+ }
97
+
98
+ function extractPromptSuggestion(params) {
99
+ if (!params) return "";
100
+ if (typeof params.suggestion === "string") return params.suggestion;
101
+ if (typeof params.promptSuggestion === "string") return params.promptSuggestion;
102
+ if (typeof params.suggestedPrompt === "string") return params.suggestedPrompt;
103
+ if (Array.isArray(params.suggestions) && typeof params.suggestions[0] === "string") return params.suggestions[0];
104
+ if (Array.isArray(params.promptSuggestions) && typeof params.promptSuggestions[0] === "string") return params.promptSuggestions[0];
105
+ if (Array.isArray(params.followUpSuggestions) && typeof params.followUpSuggestions[0] === "string") return params.followUpSuggestions[0];
106
+ return "";
107
+ }
108
+
92
109
  function flattenEvent(notification, state) {
93
110
  var events = [];
94
111
  var method = notification.method;
@@ -142,6 +159,21 @@ function flattenEvent(notification, state) {
142
159
  return events;
143
160
  }
144
161
 
162
+ if (method === "turn/plan/updated") {
163
+ events.push({
164
+ yokeType: "plan_updated",
165
+ turnId: params.turnId || null,
166
+ explanation: params.explanation || "",
167
+ plan: Array.isArray(params.plan) ? params.plan.map(function(step) {
168
+ return {
169
+ step: step && step.step ? step.step : "",
170
+ status: normalizePlanStatus(step && step.status),
171
+ };
172
+ }) : [],
173
+ });
174
+ return events;
175
+ }
176
+
145
177
  if (method === "turn/failed") {
146
178
  events.push({
147
179
  yokeType: "error",
@@ -202,6 +234,20 @@ function flattenEvent(notification, state) {
202
234
  return events;
203
235
  }
204
236
 
237
+ if (method === "item/plan/delta") {
238
+ var planDeltaItemId = params.itemId || params.id;
239
+ var nextPlanText = (state.planTexts[planDeltaItemId] || "") + (params.delta || "");
240
+ if (planDeltaItemId) state.planTexts[planDeltaItemId] = nextPlanText;
241
+ if (nextPlanText) {
242
+ events.push({
243
+ yokeType: "plan_content",
244
+ content: nextPlanText,
245
+ itemId: planDeltaItemId || null,
246
+ });
247
+ }
248
+ return events;
249
+ }
250
+
205
251
  // serverRequest/resolved - confirmation that an approval was processed
206
252
  if (method === "serverRequest/resolved") {
207
253
  return events; // no-op, approval already handled
@@ -214,6 +260,27 @@ function flattenEvent(notification, state) {
214
260
 
215
261
  var evtPhase = method.split("/")[1]; // "started", "updated", "completed"
216
262
 
263
+ if (item.type === "plan") {
264
+ if (typeof item.text === "string") {
265
+ state.planTexts[item.id] = item.text;
266
+ events.push({
267
+ yokeType: "plan_content",
268
+ content: item.text,
269
+ itemId: item.id,
270
+ final: evtPhase === "completed",
271
+ });
272
+ }
273
+ return events;
274
+ }
275
+
276
+ if (item.type === "contextCompaction" || item.type === "context_compaction") {
277
+ events.push({
278
+ yokeType: "status",
279
+ status: evtPhase === "completed" ? "processing" : "compacting",
280
+ });
281
+ return events;
282
+ }
283
+
217
284
  // Agent message (text response)
218
285
  if (item.type === "agentMessage" || item.type === "agent_message") {
219
286
  if (!state.textBlocks[item.id]) {
@@ -262,6 +329,8 @@ function flattenEvent(notification, state) {
262
329
 
263
330
  // Command execution (bash/shell)
264
331
  if (item.type === "commandExecution" || item.type === "command_execution") {
332
+ var commandText = item.command || state.commandInputs[item.id] || "";
333
+ if (commandText) state.commandInputs[item.id] = commandText;
265
334
  if (!state.toolBlocks[item.id]) {
266
335
  state.blockCounter++;
267
336
  state.toolBlocks[item.id] = "blk_" + state.blockCounter;
@@ -277,7 +346,7 @@ function flattenEvent(notification, state) {
277
346
  blockId: toolBlockId,
278
347
  toolId: item.id,
279
348
  toolName: "Bash",
280
- input: { command: item.command },
349
+ input: { command: commandText },
281
350
  });
282
351
  }
283
352
  if (evtPhase === "completed") {
@@ -294,6 +363,9 @@ function flattenEvent(notification, state) {
294
363
 
295
364
  // File change
296
365
  if (item.type === "fileChange" || item.type === "file_change") {
366
+ var changes = item.changes || [];
367
+ var changeDesc = changes.map(function(c) { return c.kind + " " + c.path; }).join(", ");
368
+ var primaryPath = changes.length === 1 ? (changes[0].path || "") : "";
297
369
  if (!state.toolBlocks[item.id]) {
298
370
  state.blockCounter++;
299
371
  state.toolBlocks[item.id] = "blk_" + state.blockCounter;
@@ -304,21 +376,26 @@ function flattenEvent(notification, state) {
304
376
  toolId: item.id,
305
377
  toolName: "Edit",
306
378
  });
307
- var changeDesc = (item.changes || []).map(function(c) { return c.kind + " " + c.path; }).join(", ");
308
379
  events.push({
309
380
  yokeType: "tool_executing",
310
381
  blockId: fcBlockId,
311
382
  toolId: item.id,
312
383
  toolName: "Edit",
313
- input: { changes: changeDesc },
384
+ input: {
385
+ changes: changeDesc,
386
+ file_path: primaryPath || undefined,
387
+ },
314
388
  });
315
389
  }
316
390
  if (evtPhase === "completed") {
391
+ var diffText = changes.map(function(c) {
392
+ return c && c.diff ? c.diff : "";
393
+ }).filter(Boolean).join("\n\n");
317
394
  events.push({
318
395
  yokeType: "tool_result",
319
396
  toolId: item.id,
320
397
  blockId: state.toolBlocks[item.id],
321
- content: item.status === "completed" ? "Changes applied" : "Changes failed",
398
+ content: diffText || (item.status === "completed" ? "Changes applied" : "Changes failed"),
322
399
  isError: item.status === "failed",
323
400
  });
324
401
  }
@@ -397,6 +474,15 @@ function flattenEvent(notification, state) {
397
474
  return events;
398
475
  }
399
476
 
477
+ var promptSuggestion = extractPromptSuggestion(params);
478
+ if (promptSuggestion) {
479
+ events.push({
480
+ yokeType: "prompt_suggestion",
481
+ suggestion: promptSuggestion,
482
+ });
483
+ return events;
484
+ }
485
+
400
486
  // Unknown event type - pass through
401
487
  console.log("[yoke/codex] UNHANDLED event:", method, JSON.stringify(params).substring(0, 200));
402
488
  events.push({
@@ -415,6 +501,7 @@ function createCodexQueryHandle(appServer, queryOpts) {
415
501
  var abortController = queryOpts.abortController;
416
502
  var systemPrompt = queryOpts.systemPrompt || "";
417
503
  var canUseTool = queryOpts.canUseTool || null;
504
+ var onElicitation = queryOpts.onElicitation || null;
418
505
 
419
506
  // Check if the query was cancelled (either via handle.abort() or direct signal abort)
420
507
  function isCancelled() {
@@ -437,6 +524,8 @@ function createCodexQueryHandle(appServer, queryOpts) {
437
524
  thinkingBlocks: {}, // itemId -> blockId
438
525
  thinkingLengths: {}, // itemId -> last sent length
439
526
  toolBlocks: {}, // itemId -> blockId (for tool_start dedup)
527
+ commandInputs: {}, // itemId -> command captured from approval/start events
528
+ planTexts: {}, // itemId -> streamed plan text
440
529
  };
441
530
 
442
531
  // Internal event buffer for async iterator
@@ -512,6 +601,9 @@ function createCodexQueryHandle(appServer, queryOpts) {
512
601
  // Command approval request
513
602
  if (method === "item/commandExecution/requestApproval") {
514
603
  var cmdParams = msg.params || {};
604
+ if (cmdParams.itemId && cmdParams.command) {
605
+ state.commandInputs[cmdParams.itemId] = cmdParams.command;
606
+ }
515
607
  if (canUseTool) {
516
608
  canUseTool("Bash", { command: cmdParams.command }, {}).then(function(decision) {
517
609
  var approved = isApproved(decision);
@@ -549,7 +641,45 @@ function createCodexQueryHandle(appServer, queryOpts) {
549
641
  var mcpParams = msg.params || {};
550
642
  var mcpMeta = mcpParams._meta || {};
551
643
  console.log("[yoke/codex] MCP approval request:", (mcpMeta.tool || "?"), "server=" + (mcpParams.serverName || "?"));
552
- if (canUseTool) {
644
+ if (onElicitation) {
645
+ var request = {
646
+ serverName: mcpParams.serverName || (mcpMeta.tool || "Tool"),
647
+ message: mcpParams.message || mcpParams.prompt || "",
648
+ mode: mcpParams.url ? "url" : "form",
649
+ url: mcpParams.url || null,
650
+ elicitationId: mcpParams.elicitationId || null,
651
+ requestedSchema: mcpParams.requestedSchema || null,
652
+ };
653
+ if (!request.requestedSchema && Array.isArray(mcpParams.questions) && mcpParams.questions.length > 0) {
654
+ var schema = { type: "object", properties: {}, required: [] };
655
+ for (var qi = 0; qi < mcpParams.questions.length; qi++) {
656
+ var q = mcpParams.questions[qi];
657
+ var qid = q.id || ("question_" + (qi + 1));
658
+ schema.required.push(qid);
659
+ if (Array.isArray(q.options) && q.options.length > 0) {
660
+ schema.properties[qid] = {
661
+ type: "string",
662
+ description: q.question || q.prompt || qid,
663
+ enum: q.options.map(function(opt) { return opt && (opt.value || opt.label) ? (opt.value || opt.label) : ""; }).filter(Boolean),
664
+ };
665
+ } else {
666
+ schema.properties[qid] = {
667
+ type: "string",
668
+ description: q.question || q.prompt || qid,
669
+ };
670
+ }
671
+ }
672
+ request.requestedSchema = schema;
673
+ }
674
+ onElicitation(request, {
675
+ signal: { addEventListener: function() {} },
676
+ }).then(function(result) {
677
+ appServer.respond(msg.id, result || { action: "reject" });
678
+ }).catch(function(err) {
679
+ console.error("[yoke/codex] elicitation_response send failed:", err.message || err);
680
+ appServer.respond(msg.id, { action: "reject" });
681
+ });
682
+ } else if (canUseTool) {
553
683
  canUseTool("mcp__" + (mcpParams.serverName || "unknown") + "__" + (mcpMeta.tool || "call"), mcpParams, {}).then(function(decision) {
554
684
  appServer.respond(msg.id, { action: isApproved(decision) ? "accept" : "decline" });
555
685
  }).catch(function(err) {
@@ -592,9 +722,9 @@ function createCodexQueryHandle(appServer, queryOpts) {
592
722
  // Start or resume thread
593
723
  var threadParams = {
594
724
  model: queryOpts.model || "gpt-5.4",
595
- sandboxMode: queryOpts.sandboxMode || "workspace-write",
725
+ sandbox: queryOpts.sandboxMode || "workspace-write",
596
726
  approvalPolicy: queryOpts.approvalPolicy || "on-failure",
597
- workingDirectory: queryOpts.cwd,
727
+ cwd: queryOpts.cwd,
598
728
  skipGitRepoCheck: true,
599
729
  };
600
730
  if (queryOpts.modelReasoningEffort) {
@@ -609,6 +739,9 @@ function createCodexQueryHandle(appServer, queryOpts) {
609
739
  threadResult = await appServer.send("thread/resume", {
610
740
  threadId: queryOpts.resumeSessionId,
611
741
  model: threadParams.model,
742
+ sandbox: threadParams.sandbox,
743
+ approvalPolicy: threadParams.approvalPolicy,
744
+ cwd: threadParams.cwd,
612
745
  }, 60000);
613
746
  } else {
614
747
  threadResult = await appServer.send("thread/start", threadParams, 60000);
@@ -626,6 +759,8 @@ function createCodexQueryHandle(appServer, queryOpts) {
626
759
  state.thinkingBlocks = {};
627
760
  state.thinkingLengths = {};
628
761
  state.toolBlocks = {};
762
+ state.commandInputs = {};
763
+ state.planTexts = {};
629
764
 
630
765
  // Start turn
631
766
  var turnPromise = new Promise(function(resolve) { turnResolve = resolve; });
@@ -822,7 +957,7 @@ function createCodexAdapter(opts) {
822
957
  betas: false,
823
958
  rewind: false,
824
959
  sessionResume: true,
825
- promptSuggestions: false,
960
+ promptSuggestions: true,
826
961
  elicitation: true,
827
962
  fileCheckpointing: false,
828
963
  contextCompacting: false,
@@ -974,7 +1109,7 @@ function createCodexAdapter(opts) {
974
1109
  betas: false,
975
1110
  rewind: false,
976
1111
  sessionResume: true,
977
- promptSuggestions: false,
1112
+ promptSuggestions: true,
978
1113
  elicitation: true,
979
1114
  fileCheckpointing: false,
980
1115
  contextCompacting: false,
@@ -1014,6 +1149,7 @@ function createCodexAdapter(opts) {
1014
1149
  systemPrompt: queryOpts.systemPrompt || "",
1015
1150
  abortController: ac,
1016
1151
  canUseTool: queryOpts.canUseTool || null,
1152
+ onElicitation: queryOpts.onElicitation || null,
1017
1153
  resumeSessionId: queryOpts.resumeSessionId || null,
1018
1154
  };
1019
1155
 
package/lib/yoke/index.js CHANGED
@@ -162,7 +162,7 @@ function createAdapters(opts) {
162
162
  * Re-checks auth, creates adapter if now logged in.
163
163
  * Returns the adapter or null.
164
164
  */
165
- function lazyCreateAdapter(adapters, vendor, opts) {
165
+ async function lazyCreateAdapter(adapters, vendor, opts) {
166
166
  if (_sharedAdapters && _sharedAdapters[vendor]) {
167
167
  adapters[vendor] = _sharedAdapters[vendor];
168
168
  return adapters[vendor];
@@ -177,8 +177,12 @@ function lazyCreateAdapter(adapters, vendor, opts) {
177
177
 
178
178
  try {
179
179
  var ad = createAdapter({ vendor: vendor, cwd: opts.cwd });
180
+ if (typeof ad.init === "function") {
181
+ await ad.init(opts || {});
182
+ }
180
183
  console.log("[yoke] Lazy adapter created: " + vendor);
181
184
  if (_sharedAdapters) _sharedAdapters[vendor] = ad;
185
+ if (_sharedAuth) _sharedAuth[vendor] = true;
182
186
  adapters[vendor] = ad;
183
187
  return ad;
184
188
  } catch (e) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "clay-server",
3
- "version": "2.33.0-beta.2",
3
+ "version": "2.33.0",
4
4
  "description": "Self-hosted Claude Code in your browser. Multi-session, multi-user, push notifications.",
5
5
  "bin": {
6
6
  "clay-server": "./bin/cli.js",