clay-server 3.4.0-beta.8 → 3.4.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.
Files changed (73) hide show
  1. package/lib/daemon-projects.js +3 -3
  2. package/lib/daemon.js +59 -59
  3. package/lib/project-connection.js +19 -6
  4. package/lib/project-models.js +220 -0
  5. package/lib/project-session-handoff.js +162 -0
  6. package/lib/project-session-pair.js +14 -7
  7. package/lib/project-session-spawn.js +1 -1
  8. package/lib/project-sessions.js +13 -45
  9. package/lib/project-worker-proposal.js +51 -14
  10. package/lib/project.js +35 -79
  11. package/lib/public/app.js +11 -0
  12. package/lib/public/copilot-avatar.svg +5 -0
  13. package/lib/public/css/command-palette.css +22 -2
  14. package/lib/public/css/filebrowser.css +6 -1
  15. package/lib/public/css/icon-strip.css +29 -13
  16. package/lib/public/css/input.css +56 -0
  17. package/lib/public/css/mates.css +6 -0
  18. package/lib/public/css/menus.css +35 -56
  19. package/lib/public/css/pane.css +18 -3
  20. package/lib/public/css/session-actions.css +98 -0
  21. package/lib/public/grok-avatar.svg +4 -0
  22. package/lib/public/index.html +26 -9
  23. package/lib/public/junie-avatar.svg +11 -0
  24. package/lib/public/kimi-avatar.svg +4 -0
  25. package/lib/public/modules/agent-config-selects.js +69 -0
  26. package/lib/public/modules/app-connection.js +6 -0
  27. package/lib/public/modules/app-header.js +0 -22
  28. package/lib/public/modules/app-messages.js +48 -9
  29. package/lib/public/modules/app-panels.js +33 -82
  30. package/lib/public/modules/app-projects.js +3 -1
  31. package/lib/public/modules/app-rendering.js +16 -1
  32. package/lib/public/modules/background-tasks-ui.js +55 -0
  33. package/lib/public/modules/filebrowser-tabs.js +65 -0
  34. package/lib/public/modules/filebrowser.js +23 -3
  35. package/lib/public/modules/mate-sidebar.js +10 -2
  36. package/lib/public/modules/model-picker.js +263 -0
  37. package/lib/public/modules/pane-bridge.js +6 -0
  38. package/lib/public/modules/project-switcher.js +7 -6
  39. package/lib/public/modules/session-actions.js +294 -0
  40. package/lib/public/modules/sidebar-mobile.js +2 -1
  41. package/lib/public/modules/sidebar-projects.js +34 -43
  42. package/lib/public/modules/sidebar-sessions.js +17 -1
  43. package/lib/public/modules/split-pair-ui.js +16 -77
  44. package/lib/public/modules/split-view.js +11 -1
  45. package/lib/public/modules/tools.js +5 -0
  46. package/lib/public/modules/vendor-priority.js +6 -1
  47. package/lib/public/modules/worker-proposal.js +6 -1
  48. package/lib/public/modules/worktree-location.js +17 -0
  49. package/lib/public/qwen-avatar.svg +4 -0
  50. package/lib/public/style.css +1 -0
  51. package/lib/sdk-bridge.js +57 -34
  52. package/lib/sdk-message-processor.js +26 -4
  53. package/lib/session-handoff-context.js +110 -0
  54. package/lib/session-notes-mcp-server.js +1 -0
  55. package/lib/sessions.js +4 -0
  56. package/lib/worktree.js +9 -4
  57. package/lib/ws-schema.js +9 -1
  58. package/lib/yoke/acp-agent-profiles.js +64 -0
  59. package/lib/yoke/adapters/acp.js +2 -1
  60. package/lib/yoke/adapters/claude.js +34 -0
  61. package/lib/yoke/adapters/codex.js +38 -3
  62. package/lib/yoke/adapters/copilot.js +7 -0
  63. package/lib/yoke/adapters/grok.js +7 -0
  64. package/lib/yoke/adapters/junie.js +7 -0
  65. package/lib/yoke/adapters/kimi.js +7 -0
  66. package/lib/yoke/adapters/kiro.js +4 -3
  67. package/lib/yoke/adapters/qwen.js +7 -0
  68. package/lib/yoke/codex-background-tasks.js +84 -0
  69. package/lib/yoke/index.js +43 -14
  70. package/lib/yoke/instructions.js +3 -0
  71. package/lib/yoke/interface.js +21 -0
  72. package/lib/yoke/vendor-registry.js +55 -0
  73. package/package.json +1 -1
@@ -197,6 +197,26 @@ function flattenEvent(raw) {
197
197
  base.summary = raw.summary || null;
198
198
  return base;
199
199
  }
200
+ if (raw.subtype === "background_tasks_changed") {
201
+ base.yokeType = "background_tasks_changed";
202
+ base.tasks = normalizeBackgroundTasks(raw.tasks);
203
+ return base;
204
+ }
205
+ if (raw.subtype === "task_updated") {
206
+ base.yokeType = "task_updated";
207
+ base.task_id = raw.task_id;
208
+ base.patch = raw.patch || {};
209
+ return base;
210
+ }
211
+ if (raw.subtype === "task_notification") {
212
+ base.yokeType = "task_notification";
213
+ base.parentToolId = raw.parent_tool_use_id;
214
+ base.taskId = raw.task_id;
215
+ base.status = raw.status || "completed";
216
+ base.summary = raw.summary || "";
217
+ base.usage = raw.usage || null;
218
+ return base;
219
+ }
200
220
  // Model refusal: the model declined and the CLI either fell back to
201
221
  // another model or ended the turn. Translate to a vendor-neutral
202
222
  // yokeType so the relay never sees SDK subtype strings.
@@ -343,6 +363,20 @@ function flattenEvent(raw) {
343
363
  return base;
344
364
  }
345
365
 
366
+ function normalizeBackgroundTasks(tasks) {
367
+ if (!Array.isArray(tasks)) return [];
368
+ return tasks.map(function(task) {
369
+ var nativeType = task && task.task_type;
370
+ var taskType = nativeType === "local_bash" ? "shell"
371
+ : nativeType === "local_agent" ? "agent" : "other";
372
+ return {
373
+ task_id: task && task.task_id,
374
+ task_type: taskType,
375
+ description: (task && task.description) || "",
376
+ };
377
+ });
378
+ }
379
+
346
380
  // --- QueryHandle ---
347
381
  // Wraps a raw SDK query object with the YOKE QueryHandle interface.
348
382
  // Events are flattened via flattenEvent before yielding.
@@ -6,8 +6,10 @@
6
6
  var path = require("path");
7
7
  var fs = require("fs");
8
8
  var { CodexAppServer } = require("../codex-app-server");
9
+ var INITIALIZE_TIMEOUT_MS = require("../interface").INITIALIZE_TIMEOUT_MS;
9
10
  var skillDiscovery = require("../skill-discovery");
10
11
  var { resolveOsUserInfo } = require("../../os-users");
12
+ var backgroundTasks = require("../codex-background-tasks");
11
13
 
12
14
  // --- Event flattening ---
13
15
  // Converts app-server JSON-RPC notifications into flat objects with a yokeType field.
@@ -672,6 +674,7 @@ function createEventState(model) {
672
674
  toolBlocks: {},
673
675
  commandInputs: {},
674
676
  planTexts: {},
677
+ backgroundTasks: backgroundTasks.createState(),
675
678
  };
676
679
  }
677
680
 
@@ -698,6 +701,7 @@ function createCodexQueryHandle(appServer, queryOpts) {
698
701
  var eventWaiting = null;
699
702
  var iteratorDone = false;
700
703
  var finishedNotified = false;
704
+ var removeBackgroundTaskReset = null;
701
705
 
702
706
  function notifyFinished() {
703
707
  if (finishedNotified) return;
@@ -722,6 +726,12 @@ function createCodexQueryHandle(appServer, queryOpts) {
722
726
  }
723
727
  }
724
728
 
729
+ if (typeof queryOpts.registerBackgroundTaskReset === "function") {
730
+ removeBackgroundTaskReset = queryOpts.registerBackgroundTaskReset(function() {
731
+ backgroundTasks.emitReset(state.backgroundTasks, pushEvent);
732
+ });
733
+ }
734
+
725
735
  function endIterator() {
726
736
  iteratorDone = true;
727
737
  if (eventWaiting) {
@@ -729,6 +739,10 @@ function createCodexQueryHandle(appServer, queryOpts) {
729
739
  eventWaiting = null;
730
740
  resolve({ value: undefined, done: true });
731
741
  }
742
+ if (removeBackgroundTaskReset) {
743
+ removeBackgroundTaskReset();
744
+ removeBackgroundTaskReset = null;
745
+ }
732
746
  notifyFinished();
733
747
  }
734
748
 
@@ -934,6 +948,10 @@ function createCodexQueryHandle(appServer, queryOpts) {
934
948
  pushEvent(yokeEvents[i]);
935
949
  }
936
950
 
951
+ if (method === "turn/completed" || method === "turn/failed") {
952
+ backgroundTasks.poll(appServer, state.threadId, state.backgroundTasks, pushEvent).catch(function() {});
953
+ }
954
+
937
955
  // Resolve turn promise when turn ends
938
956
  if (method === "turn/completed" || method === "turn/failed") {
939
957
  if (turnResolve) {
@@ -1072,6 +1090,7 @@ function createCodexQueryHandle(appServer, queryOpts) {
1072
1090
  await appServer.send("turn/start", {
1073
1091
  threadId: state.threadId,
1074
1092
  input: input,
1093
+ model: state.model,
1075
1094
  }, 60000);
1076
1095
 
1077
1096
  // Wait for turn to complete
@@ -1144,7 +1163,7 @@ function createCodexQueryHandle(appServer, queryOpts) {
1144
1163
  },
1145
1164
 
1146
1165
  setModel: function(model) {
1147
- // Model is set at thread creation. Cannot change mid-thread.
1166
+ state.model = model || "gpt-5.6-terra";
1148
1167
  return Promise.resolve();
1149
1168
  },
1150
1169
 
@@ -1245,6 +1264,20 @@ function createCodexAdapter(opts) {
1245
1264
  ];
1246
1265
  var _cachedModels = CODEX_MODELS.slice();
1247
1266
  var _appServer = null;
1267
+ var _backgroundTaskResetListeners = [];
1268
+
1269
+ function registerBackgroundTaskReset(listener) {
1270
+ _backgroundTaskResetListeners.push(listener);
1271
+ return function() {
1272
+ var index = _backgroundTaskResetListeners.indexOf(listener);
1273
+ if (index !== -1) _backgroundTaskResetListeners.splice(index, 1);
1274
+ };
1275
+ }
1276
+
1277
+ function emitBackgroundTaskReset() {
1278
+ var listeners = _backgroundTaskResetListeners.slice();
1279
+ for (var i = 0; i < listeners.length; i++) listeners[i]();
1280
+ }
1248
1281
  var _initPromise = null;
1249
1282
  var _shutdownPromise = null;
1250
1283
  var _refCount = 0;
@@ -1326,7 +1359,7 @@ function createCodexAdapter(opts) {
1326
1359
  fastModeState: null,
1327
1360
  capabilities: {
1328
1361
  effort: true,
1329
- midSessionModelSwitch: false,
1362
+ midSessionModelSwitch: true,
1330
1363
  fork: true,
1331
1364
  rollback: true,
1332
1365
  sessionListing: false,
@@ -1540,11 +1573,12 @@ function createCodexAdapter(opts) {
1540
1573
  // Spawn and initialize app-server
1541
1574
  _appServer = _createAppServer(serverOpts);
1542
1575
  await _appServer.start();
1576
+ emitBackgroundTaskReset();
1543
1577
 
1544
1578
  await _appServer.send("initialize", {
1545
1579
  clientInfo: { name: "clay", title: "Clay", version: "1.0.0" },
1546
1580
  capabilities: { experimentalApi: true },
1547
- });
1581
+ }, INITIALIZE_TIMEOUT_MS);
1548
1582
  _appServer.notify("initialized", {});
1549
1583
 
1550
1584
  if (_shuttingDown) {
@@ -1675,6 +1709,7 @@ function createCodexAdapter(opts) {
1675
1709
  canUseTool: queryOpts.canUseTool || null,
1676
1710
  onElicitation: queryOpts.onElicitation || null,
1677
1711
  resumeSessionId: queryOpts.resumeSessionId || null,
1712
+ registerBackgroundTaskReset: registerBackgroundTaskReset,
1678
1713
  };
1679
1714
 
1680
1715
  // Reasoning effort
@@ -0,0 +1,7 @@
1
+ var createAcpAdapter = require("./acp").createAcpAdapter;
2
+
3
+ function createCopilotAdapter(opts) {
4
+ return createAcpAdapter("copilot", opts);
5
+ }
6
+
7
+ module.exports = { createCopilotAdapter: createCopilotAdapter };
@@ -0,0 +1,7 @@
1
+ var createAcpAdapter = require("./acp").createAcpAdapter;
2
+
3
+ function createGrokAdapter(opts) {
4
+ return createAcpAdapter("grok", opts);
5
+ }
6
+
7
+ module.exports = { createGrokAdapter: createGrokAdapter };
@@ -0,0 +1,7 @@
1
+ var createAcpAdapter = require("./acp").createAcpAdapter;
2
+
3
+ function createJunieAdapter(opts) {
4
+ return createAcpAdapter("junie", opts);
5
+ }
6
+
7
+ module.exports = { createJunieAdapter: createJunieAdapter };
@@ -0,0 +1,7 @@
1
+ var createAcpAdapter = require("./acp").createAcpAdapter;
2
+
3
+ function createKimiAdapter(opts) {
4
+ return createAcpAdapter("kimi", opts);
5
+ }
6
+
7
+ module.exports = { createKimiAdapter: createKimiAdapter };
@@ -14,6 +14,7 @@
14
14
 
15
15
  var { execFile } = require("child_process");
16
16
  var { KiroAcpServer, findKiroPath } = require("../kiro-acp-server");
17
+ var INITIALIZE_TIMEOUT_MS = require("../interface").INITIALIZE_TIMEOUT_MS;
17
18
  var skillDiscovery = require("../skill-discovery");
18
19
  var { KIRO_DEFAULTS } = require("../../kiro-defaults");
19
20
 
@@ -762,7 +763,7 @@ function createKiroQueryHandle(acp, queryOpts) {
762
763
 
763
764
  setModel: function(model) {
764
765
  state.model = model;
765
- return setSessionModel(model).catch(function() {});
766
+ return setSessionModel(model);
766
767
  },
767
768
 
768
769
  setEffort: function() { return Promise.resolve(); },
@@ -867,7 +868,7 @@ function createKiroAdapter(opts) {
867
868
  fastModeState: null,
868
869
  capabilities: {
869
870
  effort: false,
870
- midSessionModelSwitch: false,
871
+ midSessionModelSwitch: true,
871
872
  fork: false,
872
873
  rollback: false,
873
874
  sessionListing: false,
@@ -1000,7 +1001,7 @@ function createKiroAdapter(opts) {
1000
1001
  // calls, so implementing them later means bypassing canUseTool and
1001
1002
  // needs explicit cwd confinement first.
1002
1003
  clientCapabilities: { fs: { readTextFile: false, writeTextFile: false } },
1003
- }, 30000);
1004
+ }, INITIALIZE_TIMEOUT_MS);
1004
1005
  _initialized = true;
1005
1006
 
1006
1007
  if (_shuttingDown) throw createShutdownError();
@@ -0,0 +1,7 @@
1
+ var createAcpAdapter = require("./acp").createAcpAdapter;
2
+
3
+ function createQwenAdapter(opts) {
4
+ return createAcpAdapter("qwen", opts);
5
+ }
6
+
7
+ module.exports = { createQwenAdapter: createQwenAdapter };
@@ -0,0 +1,84 @@
1
+ function terminalList(result) {
2
+ if (Array.isArray(result)) return result;
3
+ if (!result || typeof result !== "object") return [];
4
+ if (Array.isArray(result.terminals)) return result.terminals;
5
+ if (Array.isArray(result.backgroundTerminals)) return result.backgroundTerminals;
6
+ if (Array.isArray(result.items)) return result.items;
7
+ return [];
8
+ }
9
+
10
+ function isLiveTerminal(terminal) {
11
+ var status = String((terminal && (terminal.status || terminal.state)) || "").toLowerCase();
12
+ return status !== "exited" && status !== "terminated" && status !== "completed" && status !== "failed";
13
+ }
14
+
15
+ function mapTerminals(result) {
16
+ var terminals = terminalList(result);
17
+ var tasks = [];
18
+ for (var i = 0; i < terminals.length; i++) {
19
+ var terminal = terminals[i] || {};
20
+ var taskId = terminal.id || terminal.terminalId || terminal.taskId || "";
21
+ if (!taskId || !isLiveTerminal(terminal)) continue;
22
+ tasks.push({
23
+ task_id: String(taskId),
24
+ task_type: "shell",
25
+ description: terminal.commandLine || terminal.command || terminal.name || "",
26
+ });
27
+ }
28
+ return tasks;
29
+ }
30
+
31
+ function taskIds(tasks) {
32
+ var ids = {};
33
+ for (var i = 0; i < tasks.length; i++) ids[tasks[i].task_id] = true;
34
+ return ids;
35
+ }
36
+
37
+ function sameMembership(first, second) {
38
+ var firstKeys = Object.keys(first || {});
39
+ var secondKeys = Object.keys(second || {});
40
+ if (firstKeys.length !== secondKeys.length) return false;
41
+ for (var i = 0; i < firstKeys.length; i++) {
42
+ if (!second[firstKeys[i]]) return false;
43
+ }
44
+ return true;
45
+ }
46
+
47
+ function createState() {
48
+ return { taskIds: null };
49
+ }
50
+
51
+ function emitIfChanged(state, tasks, pushEvent) {
52
+ var ids = taskIds(tasks);
53
+ if (state.taskIds === null && tasks.length === 0) {
54
+ state.taskIds = ids;
55
+ return false;
56
+ }
57
+ if (state.taskIds && sameMembership(state.taskIds, ids)) return false;
58
+ state.taskIds = ids;
59
+ pushEvent({ yokeType: "background_tasks_changed", tasks: tasks });
60
+ return true;
61
+ }
62
+
63
+ function poll(appServer, threadId, state, pushEvent) {
64
+ if (!appServer || !threadId || appServer._clayBackgroundTasksPollingDisabled) return Promise.resolve(false);
65
+ return appServer.send("thread/backgroundTerminals/list", { threadId: threadId }).then(function(result) {
66
+ return emitIfChanged(state, mapTerminals(result), pushEvent);
67
+ }).catch(function() {
68
+ appServer._clayBackgroundTasksPollingDisabled = true;
69
+ return false;
70
+ });
71
+ }
72
+
73
+ function emitReset(state, pushEvent) {
74
+ state.taskIds = {};
75
+ pushEvent({ yokeType: "background_tasks_changed", tasks: [] });
76
+ }
77
+
78
+ module.exports = {
79
+ createState: createState,
80
+ mapTerminals: mapTerminals,
81
+ emitIfChanged: emitIfChanged,
82
+ poll: poll,
83
+ emitReset: emitReset,
84
+ };
package/lib/yoke/index.js CHANGED
@@ -8,11 +8,16 @@ var createClaudeAdapter = require("./adapters/claude").createClaudeAdapter;
8
8
  var createCodexAdapter = require("./adapters/codex").createCodexAdapter;
9
9
  var createAntigravityAdapter = require("./adapters/antigravity").createAntigravityAdapter;
10
10
  var createOpenCodeAdapter = require("./adapters/opencode").createOpenCodeAdapter;
11
+ var createKimiAdapter = require("./adapters/kimi").createKimiAdapter;
12
+ var createGrokAdapter = require("./adapters/grok").createGrokAdapter;
13
+ var createCopilotAdapter = require("./adapters/copilot").createCopilotAdapter;
14
+ var createQwenAdapter = require("./adapters/qwen").createQwenAdapter;
15
+ var createJunieAdapter = require("./adapters/junie").createJunieAdapter;
11
16
  var createKiroAdapter = require("./adapters/kiro").createKiroAdapter;
12
17
 
13
18
  // Keep the first session in a new project predictable. This order is also
14
19
  // used by the UI when a project has no remembered vendor yet.
15
- var DEFAULT_VENDOR_ORDER = ["claude", "codex", "antigravity", "opencode", "kiro"];
20
+ var DEFAULT_VENDOR_ORDER = ["claude", "codex", "grok", "kimi", "copilot", "qwen", "junie", "antigravity", "opencode", "kiro"];
16
21
 
17
22
  function resolveDefaultVendor(availableVendors) {
18
23
  availableVendors = availableVendors || {};
@@ -77,6 +82,16 @@ function createAdapter(opts) {
77
82
  adapter = createAntigravityAdapter(opts);
78
83
  } else if (vendor === "opencode") {
79
84
  adapter = createOpenCodeAdapter(opts);
85
+ } else if (vendor === "kimi") {
86
+ adapter = createKimiAdapter(opts);
87
+ } else if (vendor === "grok") {
88
+ adapter = createGrokAdapter(opts);
89
+ } else if (vendor === "copilot") {
90
+ adapter = createCopilotAdapter(opts);
91
+ } else if (vendor === "qwen") {
92
+ adapter = createQwenAdapter(opts);
93
+ } else if (vendor === "junie") {
94
+ adapter = createJunieAdapter(opts);
80
95
  } else if (vendor === "kiro") {
81
96
  adapter = createKiroAdapter(opts);
82
97
  } else {
@@ -102,7 +117,7 @@ function logAuthCheck(auth) {
102
117
  if (_lastAuthLogKey === key && now - _lastAuthLogAt < 30000) return;
103
118
  _lastAuthLogKey = key;
104
119
  _lastAuthLogAt = now;
105
- console.log("[yoke] Auth check: claude=" + auth.claude + " codex=" + auth.codex + " antigravity=" + auth.antigravity + " opencode=" + auth.opencode + " kiro=" + auth.kiro);
120
+ console.log("[yoke] Auth check: " + Object.keys(auth).map(function(vendor) { return vendor + "=" + auth[vendor]; }).join(" "));
106
121
  }
107
122
 
108
123
  function checkAuth() {
@@ -238,6 +253,11 @@ function checkAuth() {
238
253
  codex: checkCodex(),
239
254
  antigravity: !!lookupBinary("agy"),
240
255
  opencode: !!lookupBinary("opencode"),
256
+ kimi: !!lookupBinary("kimi"),
257
+ grok: !!lookupBinary("grok"),
258
+ copilot: !!lookupBinary("copilot"),
259
+ qwen: !!lookupBinary("qwen"),
260
+ junie: !!lookupBinary("junie"),
241
261
  kiro: checkKiro(),
242
262
  };
243
263
  logAuthCheck(_authCache);
@@ -261,7 +281,7 @@ function checkInstalled() {
261
281
 
262
282
  var fs = require("fs");
263
283
  var execFileSync = require("child_process").execFileSync;
264
- var result = { claude: false, codex: false, antigravity: false, opencode: false, kiro: false };
284
+ var result = { claude: false, codex: false, antigravity: false, opencode: false, kimi: false, grok: false, copilot: false, qwen: false, junie: false, kiro: false };
265
285
  try {
266
286
  if (process.platform === "win32") execFileSync("where", ["claude"], { timeout: 3000, stdio: ["pipe", "pipe", "pipe"] });
267
287
  else execFileSync("which", ["claude"], { timeout: 3000, stdio: ["pipe", "pipe", "pipe"] });
@@ -295,10 +315,14 @@ function checkInstalled() {
295
315
  : execFileSync("which", ["agy"], { timeout: 3000, encoding: "utf8", stdio: ["pipe", "pipe", "pipe"] });
296
316
  result.antigravity = !!antigravityBinary.trim();
297
317
  } catch (e) {}
298
- try {
299
- var acpProfiles = require("./acp-agent-profiles");
300
- result.opencode = !!acpProfiles.findAcpAgentPath(acpProfiles.getAcpAgentProfile("opencode"));
301
- } catch (e) {}
318
+ var acpVendorKeys = ["opencode", "kimi", "grok", "copilot", "qwen", "junie"];
319
+ for (var acpIndex = 0; acpIndex < acpVendorKeys.length; acpIndex++) {
320
+ try {
321
+ var acpProfiles = require("./acp-agent-profiles");
322
+ var acpVendor = acpVendorKeys[acpIndex];
323
+ result[acpVendor] = !!acpProfiles.findAcpAgentPath(acpProfiles.getAcpAgentProfile(acpVendor));
324
+ } catch (e) {}
325
+ }
302
326
  _installedCache = result;
303
327
  return result;
304
328
  }
@@ -322,8 +346,10 @@ function createAdapters(opts) {
322
346
  // Claude Code supports multiple auth modes (OAuth, Bedrock, Vertex, API key)
323
347
  // that `claude auth status` does not always detect. Runtime auth failures are
324
348
  // handled downstream via query-level error detection.
325
- var installed = checkInstalled();
326
- var auth = { claude: false, codex: false, antigravity: false, opencode: false, kiro: false };
349
+ // Tests can supply a fixed installation snapshot without probing or spawning
350
+ // vendor binaries; production callers always use the cached host scan.
351
+ var installed = opts._installed || checkInstalled();
352
+ var auth = { claude: false, codex: false, antigravity: false, opencode: false, kimi: false, grok: false, copilot: false, qwen: false, junie: false, kiro: false };
327
353
  var adapters = {};
328
354
 
329
355
  function supportsConfiguredIsolation(vendor) {
@@ -339,10 +365,10 @@ function createAdapters(opts) {
339
365
  // per-project teardown never shuts it down on behalf of one project
340
366
  // (see destroy() in lib/project.js).
341
367
  _sharedClaudeAdapter.shared = true;
368
+ console.log("[yoke] Shared adapter created: claude");
342
369
  }
343
370
  adapters.claude = _sharedClaudeAdapter;
344
371
  auth.claude = true;
345
- console.log("[yoke] Adapter created: claude");
346
372
  } catch (e) {
347
373
  console.error("[yoke] Failed to create adapter for claude:", e.message);
348
374
  }
@@ -352,13 +378,12 @@ function createAdapters(opts) {
352
378
  try {
353
379
  adapters.codex = createAdapter({ vendor: "codex", cwd: opts.cwd, slug: opts.slug, osUsers: !!opts.osUsers });
354
380
  auth.codex = true;
355
- console.log("[yoke] Adapter created: codex");
356
381
  } catch (e) {
357
382
  console.error("[yoke] Failed to create adapter for codex:", e.message);
358
383
  }
359
384
  }
360
385
 
361
- var subprocessVendors = ["antigravity", "opencode"];
386
+ var subprocessVendors = ["antigravity", "opencode", "kimi", "grok", "copilot", "qwen", "junie"];
362
387
  for (var subprocessIndex = 0; subprocessIndex < subprocessVendors.length; subprocessIndex++) {
363
388
  var subprocessVendor = subprocessVendors[subprocessIndex];
364
389
  var subprocessInfo = vendorRegistry.getVendorInfo(subprocessVendor);
@@ -366,7 +391,6 @@ function createAdapters(opts) {
366
391
  try {
367
392
  adapters[subprocessVendor] = createAdapter({ vendor: subprocessVendor, cwd: opts.cwd, slug: opts.slug });
368
393
  auth[subprocessVendor] = true;
369
- console.log("[yoke] Adapter created: " + subprocessVendor);
370
394
  } catch (e) {
371
395
  console.error("[yoke] Failed to create adapter for " + subprocessVendor + ":", e.message);
372
396
  }
@@ -380,7 +404,6 @@ function createAdapters(opts) {
380
404
  try {
381
405
  adapters.kiro = createAdapter({ vendor: "kiro", cwd: opts.cwd, slug: opts.slug });
382
406
  auth.kiro = true;
383
- console.log("[yoke] Adapter created: kiro");
384
407
  } catch (e) {
385
408
  console.error("[yoke] Failed to create adapter for kiro:", e.message);
386
409
  }
@@ -388,6 +411,12 @@ function createAdapters(opts) {
388
411
  console.log("[yoke] " + kiroInfo.displayName + " adapter disabled: OS-user isolation requires per-user spawning");
389
412
  }
390
413
 
414
+ var registeredVendors = Object.keys(adapters);
415
+ if (registeredVendors.length > 0) {
416
+ var registrationScope = opts.slug ? " for " + opts.slug : "";
417
+ console.log("[yoke] Adapters registered" + registrationScope + ": " + registeredVendors.join(", "));
418
+ }
419
+
391
420
  return { adapters: adapters, auth: auth };
392
421
  }
393
422
 
@@ -18,12 +18,15 @@ var KNOWN_FILES = [
18
18
  { file: ".cursorrules", label: ".cursorrules" },
19
19
  { file: ".github/copilot-instructions.md", label: ".github/copilot-instructions.md" },
20
20
  { file: "COPILOT.md", label: "COPILOT.md" },
21
+ { file: "QWEN.md", label: "QWEN.md" },
21
22
  ];
22
23
 
23
24
  // Files each vendor reads natively (skip these to avoid duplication).
24
25
  var NATIVE_FILES = {
25
26
  claude: ["CLAUDE.md"],
26
27
  codex: ["AGENTS.md"],
28
+ copilot: [".github/copilot-instructions.md", "COPILOT.md"],
29
+ qwen: ["QWEN.md"],
27
30
  };
28
31
 
29
32
  // Scan projectDir for instruction files and return merged text.
@@ -9,6 +9,25 @@
9
9
  // 2. QueryHandle (returned by adapter.createQuery)
10
10
 
11
11
  var TOOL_POLICIES = ["ask", "allow-all"];
12
+ var BACKGROUND_TASK_TYPES = ["shell", "agent", "monitor", "other"];
13
+
14
+ // Shared budget for the initialize handshake every vendor process manager
15
+ // performs. Agents do their cold start before answering it: kiro-cli boots the
16
+ // KAS server, MCP subsystem and SQLite first, which measures ~20s, and Codex
17
+ // launches its configured MCP servers. The cost is asymmetric, since too short
18
+ // a window makes a working agent unusable while too long a one only delays the
19
+ // error for an agent that is already broken, so keep it generous.
20
+ var INITIALIZE_TIMEOUT_MS = 90000;
21
+
22
+ /*
23
+ * Optional adapter event contract:
24
+ * { yokeType: "background_tasks_changed", tasks: [{ task_id, task_type, description }] }
25
+ *
26
+ * Each event replaces the complete live set. An adapter that emits this event
27
+ * must reset it to an empty set when its CLI process restarts. task_type uses
28
+ * BACKGROUND_TASK_TYPES; adapters without a level signal may synthesize one,
29
+ * and adapters that emit nothing simply have no background-task indicator.
30
+ */
12
31
 
13
32
  /**
14
33
  * Validate that an adapter object implements all required methods.
@@ -93,6 +112,8 @@ function validateQueryHandle(handle) {
93
112
 
94
113
  module.exports = {
95
114
  TOOL_POLICIES: TOOL_POLICIES,
115
+ INITIALIZE_TIMEOUT_MS: INITIALIZE_TIMEOUT_MS,
116
+ BACKGROUND_TASK_TYPES: BACKGROUND_TASK_TYPES,
96
117
  ADAPTER_METHODS: ADAPTER_METHODS,
97
118
  QUERY_HANDLE_METHODS: QUERY_HANDLE_METHODS,
98
119
  validateAdapter: validateAdapter,
@@ -62,6 +62,61 @@ var VENDOR_REGISTRY = {
62
62
  usageDashboard: null,
63
63
  rateLimitTracking: false,
64
64
  },
65
+ kimi: {
66
+ displayName: "Kimi Code",
67
+ loginCommand: "kimi login",
68
+ binaryName: "kimi",
69
+ avatar: "/kimi-avatar.svg",
70
+ sessionModes: ["gui"],
71
+ effortLevels: [],
72
+ osUserIsolation: false,
73
+ usageDashboard: null,
74
+ rateLimitTracking: false,
75
+ },
76
+ grok: {
77
+ displayName: "Grok Build",
78
+ loginCommand: "grok login --device-auth",
79
+ binaryName: "grok",
80
+ avatar: "/grok-avatar.svg",
81
+ sessionModes: ["gui"],
82
+ effortLevels: [],
83
+ osUserIsolation: false,
84
+ usageDashboard: null,
85
+ rateLimitTracking: false,
86
+ },
87
+ copilot: {
88
+ displayName: "GitHub Copilot CLI",
89
+ loginCommand: "copilot login",
90
+ binaryName: "copilot",
91
+ avatar: "/copilot-avatar.svg",
92
+ sessionModes: ["gui"],
93
+ effortLevels: [],
94
+ osUserIsolation: false,
95
+ usageDashboard: null,
96
+ rateLimitTracking: false,
97
+ },
98
+ qwen: {
99
+ displayName: "Qwen Code",
100
+ loginCommand: "qwen",
101
+ binaryName: "qwen",
102
+ avatar: "/qwen-avatar.svg",
103
+ sessionModes: ["gui"],
104
+ effortLevels: [],
105
+ osUserIsolation: false,
106
+ usageDashboard: null,
107
+ rateLimitTracking: false,
108
+ },
109
+ junie: {
110
+ displayName: "Junie CLI",
111
+ loginCommand: "junie",
112
+ binaryName: "junie",
113
+ avatar: "/junie-avatar.svg",
114
+ sessionModes: ["gui"],
115
+ effortLevels: [],
116
+ osUserIsolation: false,
117
+ usageDashboard: null,
118
+ rateLimitTracking: false,
119
+ },
65
120
  kiro: {
66
121
  displayName: "Kiro CLI",
67
122
  loginCommand: "kiro-cli login",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "clay-server",
3
- "version": "3.4.0-beta.8",
3
+ "version": "3.4.0",
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",