clay-server 3.4.0-beta.2 → 3.4.0-beta.21
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 +35 -7
- package/lib/project-debate.js +4 -0
- package/lib/project-mate-interaction.js +21 -1
- package/lib/project-memory.js +3 -0
- 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 +38 -80
- package/lib/public/antigravity-avatar.png +0 -0
- package/lib/public/app.js +29 -0
- package/lib/public/copilot-avatar.svg +5 -0
- package/lib/public/css/command-palette.css +22 -2
- 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 +38 -25
- package/lib/public/css/messages.css +9 -0
- package/lib/public/css/pane.css +16 -3
- package/lib/public/css/pwa-mobile.css +17 -0
- package/lib/public/css/session-actions.css +98 -0
- package/lib/public/css/title-bar.css +19 -0
- package/lib/public/grok-avatar.svg +4 -0
- package/lib/public/index.html +29 -7
- 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-header.js +4 -22
- package/lib/public/modules/app-messages.js +54 -11
- package/lib/public/modules/app-panels.js +36 -83
- package/lib/public/modules/app-projects.js +3 -1
- package/lib/public/modules/app-rendering.js +19 -4
- package/lib/public/modules/background-tasks-ui.js +55 -0
- package/lib/public/modules/mate-sidebar.js +11 -3
- package/lib/public/modules/model-picker.js +263 -0
- package/lib/public/modules/notifications.js +7 -1
- package/lib/public/modules/pane-bridge.js +11 -0
- package/lib/public/modules/pane-links.js +23 -0
- package/lib/public/modules/project-switcher.js +7 -6
- package/lib/public/modules/session-actions.js +294 -0
- package/lib/public/modules/sidebar-mates.js +1 -1
- 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 +20 -6
- package/lib/public/modules/split-pair-ui.js +16 -77
- package/lib/public/modules/split-view.js +3 -0
- package/lib/public/modules/tools.js +6 -1
- package/lib/public/modules/vendor-priority.js +14 -0
- package/lib/public/modules/vendor-selection.js +20 -0
- 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 +4 -2
- package/lib/sdk-bridge.js +103 -56
- package/lib/sdk-message-processor.js +26 -4
- package/lib/session-handoff-context.js +110 -0
- package/lib/session-notes-mcp-server.js +8 -1
- 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 -14
- package/lib/yoke/adapters/antigravity.js +417 -0
- package/lib/yoke/adapters/claude.js +34 -0
- package/lib/yoke/adapters/codex.js +101 -5
- 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 +2 -2
- package/lib/yoke/adapters/qwen.js +7 -0
- package/lib/yoke/codex-app-server.js +25 -8
- package/lib/yoke/index.js +67 -28
- package/lib/yoke/instructions.js +3 -0
- package/lib/yoke/interface.js +12 -0
- package/lib/yoke/vendor-registry.js +61 -6
- package/package.json +1 -1
- package/lib/public/gemini-avatar.svg +0 -11
- package/lib/yoke/adapters/gemini.js +0 -7
|
@@ -7,6 +7,7 @@ var path = require("path");
|
|
|
7
7
|
var fs = require("fs");
|
|
8
8
|
var { CodexAppServer } = require("../codex-app-server");
|
|
9
9
|
var skillDiscovery = require("../skill-discovery");
|
|
10
|
+
var { resolveOsUserInfo } = require("../../os-users");
|
|
10
11
|
|
|
11
12
|
// --- Event flattening ---
|
|
12
13
|
// Converts app-server JSON-RPC notifications into flat objects with a yokeType field.
|
|
@@ -1071,6 +1072,7 @@ function createCodexQueryHandle(appServer, queryOpts) {
|
|
|
1071
1072
|
await appServer.send("turn/start", {
|
|
1072
1073
|
threadId: state.threadId,
|
|
1073
1074
|
input: input,
|
|
1075
|
+
model: state.model,
|
|
1074
1076
|
}, 60000);
|
|
1075
1077
|
|
|
1076
1078
|
// Wait for turn to complete
|
|
@@ -1143,7 +1145,7 @@ function createCodexQueryHandle(appServer, queryOpts) {
|
|
|
1143
1145
|
},
|
|
1144
1146
|
|
|
1145
1147
|
setModel: function(model) {
|
|
1146
|
-
|
|
1148
|
+
state.model = model || "gpt-5.6-terra";
|
|
1147
1149
|
return Promise.resolve();
|
|
1148
1150
|
},
|
|
1149
1151
|
|
|
@@ -1220,6 +1222,13 @@ function createCodexAdapter(opts) {
|
|
|
1220
1222
|
var _cwd = (opts && opts.cwd) || process.cwd();
|
|
1221
1223
|
var _slug = (opts && opts.slug) || "";
|
|
1222
1224
|
var _defaultInitOpts = Object.assign({}, opts || {});
|
|
1225
|
+
var _runtimeLinuxUser = (opts && opts.runtimeLinuxUser) || null;
|
|
1226
|
+
var _requiresLinuxUser = !!(opts && opts.osUsers) && !_runtimeLinuxUser;
|
|
1227
|
+
var _userRuntimes = Object.create(null);
|
|
1228
|
+
var _resolveOsUserInfo = (opts && opts.resolveOsUserInfo) || resolveOsUserInfo;
|
|
1229
|
+
var _createAppServer = (opts && opts.createAppServer) || function(serverOpts) {
|
|
1230
|
+
return new CodexAppServer(null, serverOpts);
|
|
1231
|
+
};
|
|
1223
1232
|
// Codex models are a fixed list (the app-server doesn't enumerate them), so
|
|
1224
1233
|
// model listing must not depend on a successful app-server init — otherwise a
|
|
1225
1234
|
// slow/failed `initialize` leaves the picker empty and the chip shows the
|
|
@@ -1244,6 +1253,45 @@ function createCodexAdapter(opts) {
|
|
|
1244
1253
|
var _shuttingDown = false;
|
|
1245
1254
|
var _activeQueries = [];
|
|
1246
1255
|
|
|
1256
|
+
function withoutLinuxUser(callOpts) {
|
|
1257
|
+
var cleaned = Object.assign({}, callOpts || {});
|
|
1258
|
+
delete cleaned.linuxUser;
|
|
1259
|
+
return cleaned;
|
|
1260
|
+
}
|
|
1261
|
+
|
|
1262
|
+
function getUserRuntime(linuxUser) {
|
|
1263
|
+
if (!linuxUser) return null;
|
|
1264
|
+
if (_runtimeLinuxUser) {
|
|
1265
|
+
if (_runtimeLinuxUser !== linuxUser) {
|
|
1266
|
+
throw new Error("Codex runtime user mismatch: expected " + _runtimeLinuxUser + ", received " + linuxUser);
|
|
1267
|
+
}
|
|
1268
|
+
return adapter;
|
|
1269
|
+
}
|
|
1270
|
+
if (!_userRuntimes[linuxUser]) {
|
|
1271
|
+
var runtimeOpts = Object.assign({}, _defaultInitOpts, {
|
|
1272
|
+
runtimeLinuxUser: linuxUser,
|
|
1273
|
+
});
|
|
1274
|
+
delete runtimeOpts.linuxUser;
|
|
1275
|
+
_userRuntimes[linuxUser] = createCodexAdapter(runtimeOpts);
|
|
1276
|
+
}
|
|
1277
|
+
return _userRuntimes[linuxUser];
|
|
1278
|
+
}
|
|
1279
|
+
|
|
1280
|
+
function shutdownUserRuntimes(method, idleMs) {
|
|
1281
|
+
var users = Object.keys(_userRuntimes);
|
|
1282
|
+
if (!users.length) return Promise.resolve([]);
|
|
1283
|
+
return Promise.all(users.map(function(linuxUser) {
|
|
1284
|
+
var runtime = _userRuntimes[linuxUser];
|
|
1285
|
+
var result = method === "shutdownIfIdle"
|
|
1286
|
+
? runtime.shutdownIfIdle(idleMs)
|
|
1287
|
+
: runtime.shutdown();
|
|
1288
|
+
return Promise.resolve(result).then(function(stopped) {
|
|
1289
|
+
if (method === "shutdown" || stopped) delete _userRuntimes[linuxUser];
|
|
1290
|
+
return stopped;
|
|
1291
|
+
});
|
|
1292
|
+
}));
|
|
1293
|
+
}
|
|
1294
|
+
|
|
1247
1295
|
function updateLastActiveAt() {
|
|
1248
1296
|
_lastActiveAt = Date.now();
|
|
1249
1297
|
}
|
|
@@ -1279,7 +1327,7 @@ function createCodexAdapter(opts) {
|
|
|
1279
1327
|
fastModeState: null,
|
|
1280
1328
|
capabilities: {
|
|
1281
1329
|
effort: true,
|
|
1282
|
-
midSessionModelSwitch:
|
|
1330
|
+
midSessionModelSwitch: true,
|
|
1283
1331
|
fork: true,
|
|
1284
1332
|
rollback: true,
|
|
1285
1333
|
sessionListing: false,
|
|
@@ -1396,6 +1444,16 @@ function createCodexAdapter(opts) {
|
|
|
1396
1444
|
vendor: "codex",
|
|
1397
1445
|
|
|
1398
1446
|
init: function(initOpts) {
|
|
1447
|
+
var requestedLinuxUser = initOpts && initOpts.linuxUser;
|
|
1448
|
+
if (!_runtimeLinuxUser && requestedLinuxUser) {
|
|
1449
|
+
return getUserRuntime(requestedLinuxUser).init(withoutLinuxUser(initOpts));
|
|
1450
|
+
}
|
|
1451
|
+
if (_requiresLinuxUser) {
|
|
1452
|
+
return Promise.reject(new Error("Codex requires a mapped Linux user while OS-user isolation is enabled"));
|
|
1453
|
+
}
|
|
1454
|
+
if (_runtimeLinuxUser && requestedLinuxUser && requestedLinuxUser !== _runtimeLinuxUser) {
|
|
1455
|
+
return Promise.reject(new Error("Codex runtime user mismatch"));
|
|
1456
|
+
}
|
|
1399
1457
|
if (_shuttingDown) {
|
|
1400
1458
|
return Promise.reject(createShutdownError());
|
|
1401
1459
|
}
|
|
@@ -1412,6 +1470,9 @@ function createCodexAdapter(opts) {
|
|
|
1412
1470
|
|
|
1413
1471
|
_initPromise = (async function() {
|
|
1414
1472
|
var serverOpts = { cwd: _cwd };
|
|
1473
|
+
if (_runtimeLinuxUser) {
|
|
1474
|
+
serverOpts.osUserInfo = _resolveOsUserInfo(_runtimeLinuxUser);
|
|
1475
|
+
}
|
|
1415
1476
|
|
|
1416
1477
|
// Extract adapter options
|
|
1417
1478
|
if (effectiveInitOpts && effectiveInitOpts.adapterOptions && effectiveInitOpts.adapterOptions.CODEX) {
|
|
@@ -1478,7 +1539,7 @@ function createCodexAdapter(opts) {
|
|
|
1478
1539
|
}
|
|
1479
1540
|
|
|
1480
1541
|
// Spawn and initialize app-server
|
|
1481
|
-
_appServer =
|
|
1542
|
+
_appServer = _createAppServer(serverOpts);
|
|
1482
1543
|
await _appServer.start();
|
|
1483
1544
|
|
|
1484
1545
|
await _appServer.send("initialize", {
|
|
@@ -1565,6 +1626,16 @@ function createCodexAdapter(opts) {
|
|
|
1565
1626
|
},
|
|
1566
1627
|
|
|
1567
1628
|
createQuery: async function(queryOpts) {
|
|
1629
|
+
var requestedLinuxUser = queryOpts && queryOpts.linuxUser;
|
|
1630
|
+
if (!_runtimeLinuxUser && requestedLinuxUser) {
|
|
1631
|
+
return getUserRuntime(requestedLinuxUser).createQuery(withoutLinuxUser(queryOpts));
|
|
1632
|
+
}
|
|
1633
|
+
if (_requiresLinuxUser) {
|
|
1634
|
+
throw new Error("Codex requires a mapped Linux user while OS-user isolation is enabled");
|
|
1635
|
+
}
|
|
1636
|
+
if (_runtimeLinuxUser && requestedLinuxUser && requestedLinuxUser !== _runtimeLinuxUser) {
|
|
1637
|
+
throw new Error("Codex runtime user mismatch");
|
|
1638
|
+
}
|
|
1568
1639
|
if (_shuttingDown) {
|
|
1569
1640
|
throw createShutdownError();
|
|
1570
1641
|
}
|
|
@@ -1661,6 +1732,13 @@ function createCodexAdapter(opts) {
|
|
|
1661
1732
|
|
|
1662
1733
|
// --- Title generation ---
|
|
1663
1734
|
generateTitle: async function(messages, opts) {
|
|
1735
|
+
var requestedLinuxUser = opts && opts.linuxUser;
|
|
1736
|
+
if (!_runtimeLinuxUser && requestedLinuxUser) {
|
|
1737
|
+
return getUserRuntime(requestedLinuxUser).generateTitle(messages, withoutLinuxUser(opts));
|
|
1738
|
+
}
|
|
1739
|
+
if (_requiresLinuxUser) {
|
|
1740
|
+
throw new Error("Codex requires a mapped Linux user while OS-user isolation is enabled");
|
|
1741
|
+
}
|
|
1664
1742
|
var systemPrompt = "You are a title generator. Output only a short title (3-8 words). No quotes, no punctuation at the end, no explanation.";
|
|
1665
1743
|
var prompt = "Below is a conversation between a user and an AI assistant. Generate a short, descriptive title (3-8 words) that captures the main topic. Reply with ONLY the title, nothing else.\n\n";
|
|
1666
1744
|
for (var i = 0; i < messages.length; i++) {
|
|
@@ -1708,6 +1786,11 @@ function createCodexAdapter(opts) {
|
|
|
1708
1786
|
listSessions: function() { return Promise.resolve([]); },
|
|
1709
1787
|
renameSession: function() { return Promise.resolve(); },
|
|
1710
1788
|
forkSession: function(threadId, opts) {
|
|
1789
|
+
var requestedLinuxUser = opts && opts.linuxUser;
|
|
1790
|
+
if (!_runtimeLinuxUser && requestedLinuxUser) {
|
|
1791
|
+
return getUserRuntime(requestedLinuxUser).forkSession(threadId, withoutLinuxUser(opts));
|
|
1792
|
+
}
|
|
1793
|
+
if (_requiresLinuxUser) return Promise.reject(new Error("Codex requires a mapped Linux user while OS-user isolation is enabled"));
|
|
1711
1794
|
if (!_appServer || !_appServer.started) return Promise.resolve(null);
|
|
1712
1795
|
return _appServer.send("thread/fork", { threadId: threadId }, 30000).then(function(result) {
|
|
1713
1796
|
var newThreadId = (result && result.thread) ? result.thread.id : null;
|
|
@@ -1715,17 +1798,30 @@ function createCodexAdapter(opts) {
|
|
|
1715
1798
|
return { sessionId: newThreadId };
|
|
1716
1799
|
});
|
|
1717
1800
|
},
|
|
1718
|
-
rollbackThread: function(threadId, numTurns) {
|
|
1801
|
+
rollbackThread: function(threadId, numTurns, opts) {
|
|
1802
|
+
var requestedLinuxUser = opts && opts.linuxUser;
|
|
1803
|
+
if (!_runtimeLinuxUser && requestedLinuxUser) {
|
|
1804
|
+
return getUserRuntime(requestedLinuxUser).rollbackThread(threadId, numTurns, withoutLinuxUser(opts));
|
|
1805
|
+
}
|
|
1806
|
+
if (_requiresLinuxUser) return Promise.reject(new Error("Codex requires a mapped Linux user while OS-user isolation is enabled"));
|
|
1719
1807
|
if (!_appServer || !_appServer.started) return Promise.resolve(null);
|
|
1720
1808
|
return _appServer.send("thread/rollback", { threadId: threadId, numTurns: numTurns }, 30000);
|
|
1721
1809
|
},
|
|
1722
1810
|
|
|
1723
1811
|
// Shutdown the app-server process
|
|
1724
1812
|
shutdown: function() {
|
|
1725
|
-
return
|
|
1813
|
+
return Promise.all([
|
|
1814
|
+
beginShutdown(true),
|
|
1815
|
+
shutdownUserRuntimes("shutdown"),
|
|
1816
|
+
]).then(function() { return true; });
|
|
1726
1817
|
},
|
|
1727
1818
|
|
|
1728
1819
|
shutdownIfIdle: function(idleMs) {
|
|
1820
|
+
if (!_runtimeLinuxUser && Object.keys(_userRuntimes).length > 0) {
|
|
1821
|
+
return shutdownUserRuntimes("shutdownIfIdle", idleMs).then(function(results) {
|
|
1822
|
+
return results.some(function(stopped) { return !!stopped; });
|
|
1823
|
+
});
|
|
1824
|
+
}
|
|
1729
1825
|
if (_shuttingDown || _shutdownPromise) return Promise.resolve(false);
|
|
1730
1826
|
if (_initPromise) return Promise.resolve(false);
|
|
1731
1827
|
if (!_appServer) return Promise.resolve(false);
|
|
@@ -762,7 +762,7 @@ function createKiroQueryHandle(acp, queryOpts) {
|
|
|
762
762
|
|
|
763
763
|
setModel: function(model) {
|
|
764
764
|
state.model = model;
|
|
765
|
-
return setSessionModel(model)
|
|
765
|
+
return setSessionModel(model);
|
|
766
766
|
},
|
|
767
767
|
|
|
768
768
|
setEffort: function() { return Promise.resolve(); },
|
|
@@ -867,7 +867,7 @@ function createKiroAdapter(opts) {
|
|
|
867
867
|
fastModeState: null,
|
|
868
868
|
capabilities: {
|
|
869
869
|
effort: false,
|
|
870
|
-
midSessionModelSwitch:
|
|
870
|
+
midSessionModelSwitch: true,
|
|
871
871
|
fork: false,
|
|
872
872
|
rollback: false,
|
|
873
873
|
sessionListing: false,
|
|
@@ -9,6 +9,8 @@ var readline = require("readline");
|
|
|
9
9
|
var path = require("path");
|
|
10
10
|
var fs = require("fs");
|
|
11
11
|
var { createRequire } = require("module");
|
|
12
|
+
var { buildUserEnv } = require("../build-user-env");
|
|
13
|
+
var { wrapSpawnAsUser } = require("../os-users");
|
|
12
14
|
|
|
13
15
|
// --- Find the codex binary path ---
|
|
14
16
|
// Mirrors the logic from @openai/codex-sdk findCodexPath()
|
|
@@ -120,14 +122,30 @@ function CodexAppServer(executablePath, opts) {
|
|
|
120
122
|
this._stderrBuf = "";
|
|
121
123
|
}
|
|
122
124
|
|
|
125
|
+
function buildSpawnSpec(executablePath, args, opts, wrapFn) {
|
|
126
|
+
opts = opts || {};
|
|
127
|
+
var osUserInfo = opts.osUserInfo || null;
|
|
128
|
+
var env = osUserInfo
|
|
129
|
+
? Object.assign(buildUserEnv(osUserInfo), opts.env || {})
|
|
130
|
+
: Object.assign({}, process.env, opts.env || {});
|
|
131
|
+
var spawnOptions = {
|
|
132
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
133
|
+
env: env,
|
|
134
|
+
cwd: opts.cwd || process.cwd(),
|
|
135
|
+
};
|
|
136
|
+
if (osUserInfo) {
|
|
137
|
+
spawnOptions.uid = osUserInfo.uid;
|
|
138
|
+
spawnOptions.gid = osUserInfo.gid;
|
|
139
|
+
}
|
|
140
|
+
return (wrapFn || wrapSpawnAsUser)(executablePath, args, spawnOptions);
|
|
141
|
+
}
|
|
142
|
+
|
|
123
143
|
CodexAppServer.prototype.start = function() {
|
|
124
144
|
var self = this;
|
|
125
145
|
|
|
126
146
|
return new Promise(function(resolve, reject) {
|
|
127
147
|
try {
|
|
128
148
|
var args = ["app-server"];
|
|
129
|
-
var env = Object.assign({}, process.env, self.opts.env || {});
|
|
130
|
-
|
|
131
149
|
// Pass config overrides via --config key=value flags
|
|
132
150
|
if (self.opts.config) {
|
|
133
151
|
var configArgs = serializeConfig(self.opts.config, "");
|
|
@@ -136,13 +154,11 @@ CodexAppServer.prototype.start = function() {
|
|
|
136
154
|
}
|
|
137
155
|
}
|
|
138
156
|
|
|
139
|
-
|
|
157
|
+
var spawnSpec = buildSpawnSpec(self.executablePath, args, self.opts);
|
|
158
|
+
var userSuffix = self.opts.osUserInfo ? " as " + self.opts.osUserInfo.user : "";
|
|
159
|
+
console.log("[codex-app-server] Spawning" + userSuffix + ":", spawnSpec.command, spawnSpec.args.join(" "));
|
|
140
160
|
|
|
141
|
-
self.proc = spawn(
|
|
142
|
-
stdio: ["pipe", "pipe", "pipe"],
|
|
143
|
-
env: env,
|
|
144
|
-
cwd: self.opts.cwd || process.cwd(),
|
|
145
|
-
});
|
|
161
|
+
self.proc = spawn(spawnSpec.command, spawnSpec.args, spawnSpec.options);
|
|
146
162
|
|
|
147
163
|
self.proc.on("error", function(err) {
|
|
148
164
|
console.error("[codex-app-server] Process error:", err.message);
|
|
@@ -383,4 +399,5 @@ CodexAppServer.prototype.stop = function() {
|
|
|
383
399
|
module.exports = {
|
|
384
400
|
CodexAppServer: CodexAppServer,
|
|
385
401
|
findCodexPath: findCodexPath,
|
|
402
|
+
buildSpawnSpec: buildSpawnSpec,
|
|
386
403
|
};
|
package/lib/yoke/index.js
CHANGED
|
@@ -6,13 +6,18 @@ var instructions = require("./instructions");
|
|
|
6
6
|
var vendorRegistry = require("./vendor-registry");
|
|
7
7
|
var createClaudeAdapter = require("./adapters/claude").createClaudeAdapter;
|
|
8
8
|
var createCodexAdapter = require("./adapters/codex").createCodexAdapter;
|
|
9
|
-
var
|
|
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", "
|
|
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 || {};
|
|
@@ -73,10 +78,20 @@ function createAdapter(opts) {
|
|
|
73
78
|
adapter = createClaudeAdapter(opts);
|
|
74
79
|
} else if (vendor === "codex") {
|
|
75
80
|
adapter = createCodexAdapter(opts);
|
|
76
|
-
} else if (vendor === "
|
|
77
|
-
adapter =
|
|
81
|
+
} else if (vendor === "antigravity") {
|
|
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() {
|
|
@@ -236,8 +251,13 @@ function checkAuth() {
|
|
|
236
251
|
_authCache = {
|
|
237
252
|
claude: checkClaude(),
|
|
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,
|
|
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"] });
|
|
@@ -290,10 +310,19 @@ function checkInstalled() {
|
|
|
290
310
|
}
|
|
291
311
|
} catch (e) {}
|
|
292
312
|
try {
|
|
293
|
-
var
|
|
294
|
-
|
|
295
|
-
|
|
313
|
+
var antigravityBinary = process.platform === "win32"
|
|
314
|
+
? execFileSync("where", ["agy"], { timeout: 3000, encoding: "utf8", stdio: ["pipe", "pipe", "pipe"] })
|
|
315
|
+
: execFileSync("which", ["agy"], { timeout: 3000, encoding: "utf8", stdio: ["pipe", "pipe", "pipe"] });
|
|
316
|
+
result.antigravity = !!antigravityBinary.trim();
|
|
296
317
|
} 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
|
+
}
|
|
297
326
|
_installedCache = result;
|
|
298
327
|
return result;
|
|
299
328
|
}
|
|
@@ -317,8 +346,10 @@ function createAdapters(opts) {
|
|
|
317
346
|
// Claude Code supports multiple auth modes (OAuth, Bedrock, Vertex, API key)
|
|
318
347
|
// that `claude auth status` does not always detect. Runtime auth failures are
|
|
319
348
|
// handled downstream via query-level error detection.
|
|
320
|
-
|
|
321
|
-
|
|
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 };
|
|
322
353
|
var adapters = {};
|
|
323
354
|
|
|
324
355
|
function supportsConfiguredIsolation(vendor) {
|
|
@@ -334,10 +365,10 @@ function createAdapters(opts) {
|
|
|
334
365
|
// per-project teardown never shuts it down on behalf of one project
|
|
335
366
|
// (see destroy() in lib/project.js).
|
|
336
367
|
_sharedClaudeAdapter.shared = true;
|
|
368
|
+
console.log("[yoke] Shared adapter created: claude");
|
|
337
369
|
}
|
|
338
370
|
adapters.claude = _sharedClaudeAdapter;
|
|
339
371
|
auth.claude = true;
|
|
340
|
-
console.log("[yoke] Adapter created: claude");
|
|
341
372
|
} catch (e) {
|
|
342
373
|
console.error("[yoke] Failed to create adapter for claude:", e.message);
|
|
343
374
|
}
|
|
@@ -345,28 +376,26 @@ function createAdapters(opts) {
|
|
|
345
376
|
|
|
346
377
|
if (installed.codex && supportsConfiguredIsolation("codex")) {
|
|
347
378
|
try {
|
|
348
|
-
adapters.codex = createAdapter({ vendor: "codex", cwd: opts.cwd, slug: opts.slug });
|
|
379
|
+
adapters.codex = createAdapter({ vendor: "codex", cwd: opts.cwd, slug: opts.slug, osUsers: !!opts.osUsers });
|
|
349
380
|
auth.codex = true;
|
|
350
|
-
console.log("[yoke] Adapter created: codex");
|
|
351
381
|
} catch (e) {
|
|
352
382
|
console.error("[yoke] Failed to create adapter for codex:", e.message);
|
|
353
383
|
}
|
|
354
384
|
}
|
|
355
385
|
|
|
356
|
-
var
|
|
357
|
-
for (var
|
|
358
|
-
var
|
|
359
|
-
var
|
|
360
|
-
if (installed[
|
|
386
|
+
var subprocessVendors = ["antigravity", "opencode", "kimi", "grok", "copilot", "qwen", "junie"];
|
|
387
|
+
for (var subprocessIndex = 0; subprocessIndex < subprocessVendors.length; subprocessIndex++) {
|
|
388
|
+
var subprocessVendor = subprocessVendors[subprocessIndex];
|
|
389
|
+
var subprocessInfo = vendorRegistry.getVendorInfo(subprocessVendor);
|
|
390
|
+
if (installed[subprocessVendor] && supportsConfiguredIsolation(subprocessVendor)) {
|
|
361
391
|
try {
|
|
362
|
-
adapters[
|
|
363
|
-
auth[
|
|
364
|
-
console.log("[yoke] Adapter created: " + acpVendor);
|
|
392
|
+
adapters[subprocessVendor] = createAdapter({ vendor: subprocessVendor, cwd: opts.cwd, slug: opts.slug });
|
|
393
|
+
auth[subprocessVendor] = true;
|
|
365
394
|
} catch (e) {
|
|
366
|
-
console.error("[yoke] Failed to create adapter for " +
|
|
395
|
+
console.error("[yoke] Failed to create adapter for " + subprocessVendor + ":", e.message);
|
|
367
396
|
}
|
|
368
|
-
} else if (installed[
|
|
369
|
-
console.log("[yoke] " +
|
|
397
|
+
} else if (installed[subprocessVendor] && opts.osUsers) {
|
|
398
|
+
console.log("[yoke] " + subprocessInfo.displayName + " adapter disabled: OS-user isolation requires per-user spawning");
|
|
370
399
|
}
|
|
371
400
|
}
|
|
372
401
|
|
|
@@ -375,7 +404,6 @@ function createAdapters(opts) {
|
|
|
375
404
|
try {
|
|
376
405
|
adapters.kiro = createAdapter({ vendor: "kiro", cwd: opts.cwd, slug: opts.slug });
|
|
377
406
|
auth.kiro = true;
|
|
378
|
-
console.log("[yoke] Adapter created: kiro");
|
|
379
407
|
} catch (e) {
|
|
380
408
|
console.error("[yoke] Failed to create adapter for kiro:", e.message);
|
|
381
409
|
}
|
|
@@ -383,6 +411,12 @@ function createAdapters(opts) {
|
|
|
383
411
|
console.log("[yoke] " + kiroInfo.displayName + " adapter disabled: OS-user isolation requires per-user spawning");
|
|
384
412
|
}
|
|
385
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
|
+
|
|
386
420
|
return { adapters: adapters, auth: auth };
|
|
387
421
|
}
|
|
388
422
|
|
|
@@ -406,7 +440,12 @@ async function lazyCreateAdapter(adapters, vendor, opts) {
|
|
|
406
440
|
if (!installed[vendor]) return null;
|
|
407
441
|
|
|
408
442
|
try {
|
|
409
|
-
var ad = createAdapter({
|
|
443
|
+
var ad = createAdapter({
|
|
444
|
+
vendor: vendor,
|
|
445
|
+
cwd: opts.cwd,
|
|
446
|
+
slug: opts.slug,
|
|
447
|
+
osUsers: !!(opts.osUsers || opts.linuxUser),
|
|
448
|
+
});
|
|
410
449
|
if (typeof ad.init === "function") {
|
|
411
450
|
await ad.init(opts || {});
|
|
412
451
|
}
|
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,17 @@
|
|
|
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
|
+
/*
|
|
15
|
+
* Optional adapter event contract:
|
|
16
|
+
* { yokeType: "background_tasks_changed", tasks: [{ task_id, task_type, description }] }
|
|
17
|
+
*
|
|
18
|
+
* Each event replaces the complete live set. An adapter that emits this event
|
|
19
|
+
* must reset it to an empty set when its CLI process restarts. task_type uses
|
|
20
|
+
* BACKGROUND_TASK_TYPES; adapters without a level signal may synthesize one,
|
|
21
|
+
* and adapters that emit nothing simply have no background-task indicator.
|
|
22
|
+
*/
|
|
12
23
|
|
|
13
24
|
/**
|
|
14
25
|
* Validate that an adapter object implements all required methods.
|
|
@@ -93,6 +104,7 @@ function validateQueryHandle(handle) {
|
|
|
93
104
|
|
|
94
105
|
module.exports = {
|
|
95
106
|
TOOL_POLICIES: TOOL_POLICIES,
|
|
107
|
+
BACKGROUND_TASK_TYPES: BACKGROUND_TASK_TYPES,
|
|
96
108
|
ADAPTER_METHODS: ADAPTER_METHODS,
|
|
97
109
|
QUERY_HANDLE_METHODS: QUERY_HANDLE_METHODS,
|
|
98
110
|
validateAdapter: validateAdapter,
|
|
@@ -40,13 +40,13 @@ var VENDOR_REGISTRY = {
|
|
|
40
40
|
},
|
|
41
41
|
rateLimitTracking: true,
|
|
42
42
|
},
|
|
43
|
-
|
|
44
|
-
displayName: "
|
|
45
|
-
loginCommand: "
|
|
46
|
-
binaryName: "
|
|
47
|
-
avatar: "/
|
|
43
|
+
antigravity: {
|
|
44
|
+
displayName: "Antigravity CLI",
|
|
45
|
+
loginCommand: "agy",
|
|
46
|
+
binaryName: "agy",
|
|
47
|
+
avatar: "/antigravity-avatar.png",
|
|
48
48
|
sessionModes: ["gui"],
|
|
49
|
-
effortLevels: [],
|
|
49
|
+
effortLevels: ["low", "medium", "high"],
|
|
50
50
|
osUserIsolation: false,
|
|
51
51
|
usageDashboard: null,
|
|
52
52
|
rateLimitTracking: false,
|
|
@@ -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