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.
- package/lib/daemon-projects.js +3 -3
- package/lib/daemon.js +59 -59
- package/lib/project-connection.js +19 -6
- package/lib/project-models.js +220 -0
- package/lib/project-session-handoff.js +162 -0
- package/lib/project-session-pair.js +14 -7
- package/lib/project-session-spawn.js +1 -1
- package/lib/project-sessions.js +13 -45
- package/lib/project-worker-proposal.js +51 -14
- package/lib/project.js +35 -79
- package/lib/public/app.js +11 -0
- package/lib/public/copilot-avatar.svg +5 -0
- package/lib/public/css/command-palette.css +22 -2
- package/lib/public/css/filebrowser.css +6 -1
- package/lib/public/css/icon-strip.css +29 -13
- package/lib/public/css/input.css +56 -0
- package/lib/public/css/mates.css +6 -0
- package/lib/public/css/menus.css +35 -56
- package/lib/public/css/pane.css +18 -3
- package/lib/public/css/session-actions.css +98 -0
- package/lib/public/grok-avatar.svg +4 -0
- package/lib/public/index.html +26 -9
- package/lib/public/junie-avatar.svg +11 -0
- package/lib/public/kimi-avatar.svg +4 -0
- package/lib/public/modules/agent-config-selects.js +69 -0
- package/lib/public/modules/app-connection.js +6 -0
- package/lib/public/modules/app-header.js +0 -22
- package/lib/public/modules/app-messages.js +48 -9
- package/lib/public/modules/app-panels.js +33 -82
- package/lib/public/modules/app-projects.js +3 -1
- package/lib/public/modules/app-rendering.js +16 -1
- package/lib/public/modules/background-tasks-ui.js +55 -0
- package/lib/public/modules/filebrowser-tabs.js +65 -0
- package/lib/public/modules/filebrowser.js +23 -3
- package/lib/public/modules/mate-sidebar.js +10 -2
- package/lib/public/modules/model-picker.js +263 -0
- package/lib/public/modules/pane-bridge.js +6 -0
- package/lib/public/modules/project-switcher.js +7 -6
- package/lib/public/modules/session-actions.js +294 -0
- package/lib/public/modules/sidebar-mobile.js +2 -1
- package/lib/public/modules/sidebar-projects.js +34 -43
- package/lib/public/modules/sidebar-sessions.js +17 -1
- package/lib/public/modules/split-pair-ui.js +16 -77
- package/lib/public/modules/split-view.js +11 -1
- package/lib/public/modules/tools.js +5 -0
- package/lib/public/modules/vendor-priority.js +6 -1
- package/lib/public/modules/worker-proposal.js +6 -1
- package/lib/public/modules/worktree-location.js +17 -0
- package/lib/public/qwen-avatar.svg +4 -0
- package/lib/public/style.css +1 -0
- package/lib/sdk-bridge.js +57 -34
- package/lib/sdk-message-processor.js +26 -4
- package/lib/session-handoff-context.js +110 -0
- package/lib/session-notes-mcp-server.js +1 -0
- package/lib/sessions.js +4 -0
- package/lib/worktree.js +9 -4
- package/lib/ws-schema.js +9 -1
- package/lib/yoke/acp-agent-profiles.js +64 -0
- package/lib/yoke/adapters/acp.js +2 -1
- package/lib/yoke/adapters/claude.js +34 -0
- package/lib/yoke/adapters/codex.js +38 -3
- package/lib/yoke/adapters/copilot.js +7 -0
- package/lib/yoke/adapters/grok.js +7 -0
- package/lib/yoke/adapters/junie.js +7 -0
- package/lib/yoke/adapters/kimi.js +7 -0
- package/lib/yoke/adapters/kiro.js +4 -3
- package/lib/yoke/adapters/qwen.js +7 -0
- package/lib/yoke/codex-background-tasks.js +84 -0
- package/lib/yoke/index.js +43 -14
- package/lib/yoke/instructions.js +3 -0
- package/lib/yoke/interface.js +21 -0
- package/lib/yoke/vendor-registry.js +55 -0
- 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
|
-
|
|
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:
|
|
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
|
|
@@ -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)
|
|
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:
|
|
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
|
-
},
|
|
1004
|
+
}, INITIALIZE_TIMEOUT_MS);
|
|
1004
1005
|
_initialized = true;
|
|
1005
1006
|
|
|
1006
1007
|
if (_shuttingDown) throw createShutdownError();
|
|
@@ -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:
|
|
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
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
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
|
-
|
|
326
|
-
|
|
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
|
|
package/lib/yoke/instructions.js
CHANGED
|
@@ -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.
|
package/lib/yoke/interface.js
CHANGED
|
@@ -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