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
package/lib/daemon-projects.js
CHANGED
|
@@ -41,10 +41,10 @@ function scanAndRegisterWorktrees(relay, parentPath, parentSlug, parentIcon, par
|
|
|
41
41
|
if (worktreeRegistry[parentSlug][j] === wtSlug) { alreadyRegistered = true; break; }
|
|
42
42
|
}
|
|
43
43
|
if (alreadyRegistered) continue;
|
|
44
|
-
var wtMeta = { parentSlug: parentSlug, branch: wt.branch || wt.dirName,
|
|
44
|
+
var wtMeta = { parentSlug: parentSlug, branch: wt.branch || wt.dirName, external: wt.external };
|
|
45
45
|
relay.addProject(wt.path, wtSlug, wt.branch || wt.dirName, parentIcon, parentOwnerId, wtMeta);
|
|
46
46
|
worktreeRegistry[parentSlug].push(wtSlug);
|
|
47
|
-
console.log("[daemon] Registered worktree:", wtSlug, "->", wt.path, wt.
|
|
47
|
+
console.log("[daemon] Registered worktree:", wtSlug, "->", wt.path, wt.external ? "(external)" : "(inside project folder)");
|
|
48
48
|
}
|
|
49
49
|
daemonSync.register(getWorktreeSyncKey(parentSlug), function () {
|
|
50
50
|
rescanWorktrees(relay, parentPath, parentSlug, parentIcon, parentOwnerId);
|
|
@@ -79,7 +79,7 @@ function rescanWorktrees(relay, parentPath, parentSlug, parentIcon, parentOwnerI
|
|
|
79
79
|
if (existingSlugs[ei] === wtSlug) { found = true; break; }
|
|
80
80
|
}
|
|
81
81
|
if (!found) {
|
|
82
|
-
var wtMeta = { parentSlug: parentSlug, branch: wt.branch || wt.dirName,
|
|
82
|
+
var wtMeta = { parentSlug: parentSlug, branch: wt.branch || wt.dirName, external: wt.external };
|
|
83
83
|
relay.addProject(wt.path, wtSlug, wt.branch || wt.dirName, parentIcon, parentOwnerId, wtMeta);
|
|
84
84
|
if (!worktreeRegistry[parentSlug]) worktreeRegistry[parentSlug] = [];
|
|
85
85
|
worktreeRegistry[parentSlug].push(wtSlug);
|
package/lib/daemon.js
CHANGED
|
@@ -978,7 +978,7 @@ var relay = createServer({
|
|
|
978
978
|
if (!result.ok) return result;
|
|
979
979
|
// Register the new worktree as ephemeral project
|
|
980
980
|
var wtSlug = parentSlug + "--" + dirName;
|
|
981
|
-
var wtMeta = { parentSlug: parentSlug, branch: branchName,
|
|
981
|
+
var wtMeta = { parentSlug: parentSlug, branch: branchName, external: false };
|
|
982
982
|
relay.addProject(result.path, wtSlug, branchName, parent.icon, parent.ownerId, wtMeta);
|
|
983
983
|
registerWorktreeSlug(parentSlug, wtSlug);
|
|
984
984
|
console.log("[daemon] Created worktree:", wtSlug, "->", result.path);
|
|
@@ -1041,64 +1041,6 @@ function shutdownProjects() {
|
|
|
1041
1041
|
|
|
1042
1042
|
// Worktree tracking extracted to daemon-projects.js
|
|
1043
1043
|
|
|
1044
|
-
// --- Register projects ---
|
|
1045
|
-
var projects = config.projects || [];
|
|
1046
|
-
for (var i = 0; i < projects.length; i++) {
|
|
1047
|
-
var p = projects[i];
|
|
1048
|
-
if (fs.existsSync(p.path)) {
|
|
1049
|
-
console.log("[daemon] Adding project:", p.slug, "→", p.path);
|
|
1050
|
-
relay.addProject(p.path, p.slug, p.title, p.icon, p.ownerId);
|
|
1051
|
-
// Discover and register worktrees for this project
|
|
1052
|
-
scanAndRegisterWorktrees(relay, p.path, p.slug, p.icon, p.ownerId);
|
|
1053
|
-
} else {
|
|
1054
|
-
console.log("[daemon] Skipping missing project:", p.path);
|
|
1055
|
-
}
|
|
1056
|
-
}
|
|
1057
|
-
|
|
1058
|
-
// Migrate legacy mates storage before registration
|
|
1059
|
-
mates.migrateLegacyMates();
|
|
1060
|
-
|
|
1061
|
-
// Register existing mates as projects
|
|
1062
|
-
if (usersModule.isMultiUser()) {
|
|
1063
|
-
// Multi-user mode: iterate over all users and load each user's mates
|
|
1064
|
-
var allUsers = usersModule.getAllUsers();
|
|
1065
|
-
for (var ui = 0; ui < allUsers.length; ui++) {
|
|
1066
|
-
var mateCtx = mates.buildMateCtx(allUsers[ui].id);
|
|
1067
|
-
var userMates = mates.getAllMates(mateCtx);
|
|
1068
|
-
for (var mi = 0; mi < userMates.length; mi++) {
|
|
1069
|
-
var m = userMates[mi];
|
|
1070
|
-
var mateDir = mates.getMateDir(mateCtx, m.id);
|
|
1071
|
-
var mateSlug = "mate-" + m.id;
|
|
1072
|
-
var mateName = (m.profile && m.profile.displayName) || m.name || "New Mate";
|
|
1073
|
-
if (fs.existsSync(mateDir)) {
|
|
1074
|
-
var mateDef = m.builtinKey ? require("./builtin-mates").getBuiltinByKey(m.builtinKey) : null;
|
|
1075
|
-
var isHost = !!(mateDef && mateDef.hostAgent);
|
|
1076
|
-
console.log("[daemon] Adding mate project:", mateSlug + (isHost ? " (host agent)" : ""));
|
|
1077
|
-
relay.addProject(mateDir, mateSlug, mateName, null, m.createdBy, null, { isMate: true, mateDisplayName: mateName, isHostAgent: isHost });
|
|
1078
|
-
}
|
|
1079
|
-
}
|
|
1080
|
-
}
|
|
1081
|
-
} else {
|
|
1082
|
-
// Single-user mode: use null ctx
|
|
1083
|
-
var mateCtx = mates.buildMateCtx(null);
|
|
1084
|
-
var allMates = mates.getAllMates(mateCtx);
|
|
1085
|
-
for (var mi = 0; mi < allMates.length; mi++) {
|
|
1086
|
-
var m = allMates[mi];
|
|
1087
|
-
var mateDir = mates.getMateDir(mateCtx, m.id);
|
|
1088
|
-
var mateSlug = "mate-" + m.id;
|
|
1089
|
-
var mateName = (m.profile && m.profile.displayName) || m.name || "New Mate";
|
|
1090
|
-
if (fs.existsSync(mateDir)) {
|
|
1091
|
-
var mateDef2 = m.builtinKey ? require("./builtin-mates").getBuiltinByKey(m.builtinKey) : null;
|
|
1092
|
-
var isHost2 = !!(mateDef2 && mateDef2.hostAgent);
|
|
1093
|
-
console.log("[daemon] Adding mate project:", mateSlug + (isHost2 ? " (host agent)" : ""));
|
|
1094
|
-
relay.addProject(mateDir, mateSlug, mateName, null, m.createdBy, null, { isMate: true, mateDisplayName: mateName, isHostAgent: isHost2 });
|
|
1095
|
-
}
|
|
1096
|
-
}
|
|
1097
|
-
}
|
|
1098
|
-
|
|
1099
|
-
// Sync ~/.clayrc on startup
|
|
1100
|
-
try { syncClayrc(config.projects); } catch (e) {}
|
|
1101
|
-
|
|
1102
1044
|
// --- IPC server ---
|
|
1103
1045
|
// Clean up stale socket/config left by a previously killed daemon
|
|
1104
1046
|
var existingConfig = loadConfig();
|
|
@@ -1334,6 +1276,64 @@ var ipc = createIPCServer(socketPath(), function (msg) {
|
|
|
1334
1276
|
}
|
|
1335
1277
|
});
|
|
1336
1278
|
|
|
1279
|
+
// --- Register projects ---
|
|
1280
|
+
var projects = config.projects || [];
|
|
1281
|
+
for (var i = 0; i < projects.length; i++) {
|
|
1282
|
+
var p = projects[i];
|
|
1283
|
+
if (fs.existsSync(p.path)) {
|
|
1284
|
+
console.log("[daemon] Adding project:", p.slug, "→", p.path);
|
|
1285
|
+
relay.addProject(p.path, p.slug, p.title, p.icon, p.ownerId);
|
|
1286
|
+
// Discover and register worktrees for this project
|
|
1287
|
+
scanAndRegisterWorktrees(relay, p.path, p.slug, p.icon, p.ownerId);
|
|
1288
|
+
} else {
|
|
1289
|
+
console.log("[daemon] Skipping missing project:", p.path);
|
|
1290
|
+
}
|
|
1291
|
+
}
|
|
1292
|
+
|
|
1293
|
+
// Migrate legacy mates storage before registration
|
|
1294
|
+
mates.migrateLegacyMates();
|
|
1295
|
+
|
|
1296
|
+
// Register existing mates as projects
|
|
1297
|
+
if (usersModule.isMultiUser()) {
|
|
1298
|
+
// Multi-user mode: iterate over all users and load each user's mates
|
|
1299
|
+
var allUsers = usersModule.getAllUsers();
|
|
1300
|
+
for (var ui = 0; ui < allUsers.length; ui++) {
|
|
1301
|
+
var mateCtx = mates.buildMateCtx(allUsers[ui].id);
|
|
1302
|
+
var userMates = mates.getAllMates(mateCtx);
|
|
1303
|
+
for (var mi = 0; mi < userMates.length; mi++) {
|
|
1304
|
+
var m = userMates[mi];
|
|
1305
|
+
var mateDir = mates.getMateDir(mateCtx, m.id);
|
|
1306
|
+
var mateSlug = "mate-" + m.id;
|
|
1307
|
+
var mateName = (m.profile && m.profile.displayName) || m.name || "New Mate";
|
|
1308
|
+
if (fs.existsSync(mateDir)) {
|
|
1309
|
+
var mateDef = m.builtinKey ? require("./builtin-mates").getBuiltinByKey(m.builtinKey) : null;
|
|
1310
|
+
var isHost = !!(mateDef && mateDef.hostAgent);
|
|
1311
|
+
console.log("[daemon] Adding mate project:", mateSlug + (isHost ? " (host agent)" : ""));
|
|
1312
|
+
relay.addProject(mateDir, mateSlug, mateName, null, m.createdBy, null, { isMate: true, mateDisplayName: mateName, isHostAgent: isHost });
|
|
1313
|
+
}
|
|
1314
|
+
}
|
|
1315
|
+
}
|
|
1316
|
+
} else {
|
|
1317
|
+
// Single-user mode: use null ctx
|
|
1318
|
+
var mateCtx = mates.buildMateCtx(null);
|
|
1319
|
+
var allMates = mates.getAllMates(mateCtx);
|
|
1320
|
+
for (var mi = 0; mi < allMates.length; mi++) {
|
|
1321
|
+
var m = allMates[mi];
|
|
1322
|
+
var mateDir = mates.getMateDir(mateCtx, m.id);
|
|
1323
|
+
var mateSlug = "mate-" + m.id;
|
|
1324
|
+
var mateName = (m.profile && m.profile.displayName) || m.name || "New Mate";
|
|
1325
|
+
if (fs.existsSync(mateDir)) {
|
|
1326
|
+
var mateDef2 = m.builtinKey ? require("./builtin-mates").getBuiltinByKey(m.builtinKey) : null;
|
|
1327
|
+
var isHost2 = !!(mateDef2 && mateDef2.hostAgent);
|
|
1328
|
+
console.log("[daemon] Adding mate project:", mateSlug + (isHost2 ? " (host agent)" : ""));
|
|
1329
|
+
relay.addProject(mateDir, mateSlug, mateName, null, m.createdBy, null, { isMate: true, mateDisplayName: mateName, isHostAgent: isHost2 });
|
|
1330
|
+
}
|
|
1331
|
+
}
|
|
1332
|
+
}
|
|
1333
|
+
|
|
1334
|
+
// Sync ~/.clayrc on startup
|
|
1335
|
+
try { syncClayrc(config.projects); } catch (e) {}
|
|
1336
|
+
|
|
1337
1337
|
// --- Cleanup stale SDK warmup CLI session files ---
|
|
1338
1338
|
//
|
|
1339
1339
|
// On adapter init the SDK is given a dummy "hi" prompt to discover models,
|
|
@@ -22,6 +22,28 @@ function dispatchMessageSafely(handleMessage, sendTo, slug, ws, msg) {
|
|
|
22
22
|
}
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
+
function buildInitialModelInfo(sm, initialVendor, initialModel, initialModels, sessionId) {
|
|
26
|
+
var model = initialModel || "";
|
|
27
|
+
var models = initialModels || [];
|
|
28
|
+
if (model && models.length > 0) {
|
|
29
|
+
var found = false;
|
|
30
|
+
for (var i = 0; i < models.length; i++) {
|
|
31
|
+
var entry = models[i];
|
|
32
|
+
if (typeof entry === "string" ? entry === model : (entry.value === model || entry.id === model || entry.resolvedModel === model)) found = true;
|
|
33
|
+
}
|
|
34
|
+
if (!found) model = typeof models[0] === "string" ? models[0] : (models[0].value || models[0].id || "");
|
|
35
|
+
}
|
|
36
|
+
return {
|
|
37
|
+
type: "model_info",
|
|
38
|
+
model: model,
|
|
39
|
+
models: models,
|
|
40
|
+
vendor: initialVendor,
|
|
41
|
+
sessionId: sessionId || null,
|
|
42
|
+
availableVendors: sm.availableVendors || [],
|
|
43
|
+
installedVendors: sm.installedVendors || [],
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
|
|
25
47
|
/**
|
|
26
48
|
* Attach connection/disconnection handlers to a project context.
|
|
27
49
|
*
|
|
@@ -116,7 +138,7 @@ function attachConnection(ctx) {
|
|
|
116
138
|
if (!_warmedUp) {
|
|
117
139
|
_warmedUp = true;
|
|
118
140
|
if (typeof warmup === "function") {
|
|
119
|
-
try { warmup(); }
|
|
141
|
+
try { warmup(wsUser && wsUser.linuxUser ? wsUser.linuxUser : null); }
|
|
120
142
|
catch (e) { console.error("[project-connection] warmup failed for " + slug + ":", e && e.message ? e.message : e); }
|
|
121
143
|
}
|
|
122
144
|
}
|
|
@@ -166,11 +188,12 @@ function attachConnection(ctx) {
|
|
|
166
188
|
if (sm.slashCommands) {
|
|
167
189
|
sendTo(ws, { type: "slash_commands", commands: sm.slashCommands });
|
|
168
190
|
}
|
|
169
|
-
var initialModel = (restoredActive && restoredActive.model) || sm.
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
sendTo(ws,
|
|
191
|
+
var initialModel = (restoredActive && restoredActive.model) || ((sm.defaultModelByVendor || {})[initialVendor]) || "";
|
|
192
|
+
// Vendor installation state is needed even before a new project has a
|
|
193
|
+
// model. Always send it so reconnects cannot fall back to "Not installed."
|
|
194
|
+
var initialModelInfo = buildInitialModelInfo(sm, initialVendor, initialModel, initialModels, restoredActive ? restoredActive.localId : null);
|
|
195
|
+
sendTo(ws, initialModelInfo);
|
|
196
|
+
sendTo(ws, { type: "config_state", model: initialModelInfo.model, vendor: initialVendor, sessionId: restoredActive ? restoredActive.localId : null, mode: sm.currentPermissionMode || "default", effort: initialEffort || "medium", betas: sm.currentBetas || [], thinking: sm.currentThinking || "adaptive", thinkingBudget: sm.currentThinkingBudget || 10000 });
|
|
174
197
|
sendTo(ws, { type: "last_vendor", vendor: sm.lastVendor || "" });
|
|
175
198
|
sendTo(ws, Object.assign({ type: "codex_config" }, getCodexConfig(sm)));
|
|
176
199
|
sendTo(ws, { type: "term_list", terminals: tm.list() });
|
|
@@ -271,6 +294,7 @@ function attachConnection(ctx) {
|
|
|
271
294
|
if (active.isProcessing) {
|
|
272
295
|
sendTo(ws, { type: "status", status: "processing" });
|
|
273
296
|
}
|
|
297
|
+
sendTo(ws, { type: "active_background_tasks", tasks: active.activeBackgroundTasks || [] });
|
|
274
298
|
var pendingIds = Object.keys(active.pendingPermissions);
|
|
275
299
|
for (var pi = 0; pi < pendingIds.length; pi++) {
|
|
276
300
|
var p = active.pendingPermissions[pendingIds[pi]];
|
|
@@ -333,4 +357,8 @@ function attachConnection(ctx) {
|
|
|
333
357
|
};
|
|
334
358
|
}
|
|
335
359
|
|
|
336
|
-
module.exports = {
|
|
360
|
+
module.exports = {
|
|
361
|
+
attachConnection: attachConnection,
|
|
362
|
+
buildInitialModelInfo: buildInitialModelInfo,
|
|
363
|
+
dispatchMessageSafely: dispatchMessageSafely,
|
|
364
|
+
};
|
package/lib/project-debate.js
CHANGED
|
@@ -604,6 +604,7 @@ function attachDebate(ctx) {
|
|
|
604
604
|
var _modMate = matesModule.getMate(mateCtx, debate.moderatorId);
|
|
605
605
|
ctx.sdk.createMentionSession({
|
|
606
606
|
vendor: _modMate ? _modMate.vendor : null,
|
|
607
|
+
linuxUser: mateCtx.linuxUser || undefined,
|
|
607
608
|
claudeMd: claudeMd,
|
|
608
609
|
initialContext: digests,
|
|
609
610
|
initialMessage: quickBriefPrompt,
|
|
@@ -799,6 +800,7 @@ function attachDebate(ctx) {
|
|
|
799
800
|
var _modMate2 = matesModule.getMate(mateCtx, debate.moderatorId);
|
|
800
801
|
ctx.sdk.createMentionSession({
|
|
801
802
|
vendor: _modMate2 ? _modMate2.vendor : null,
|
|
803
|
+
linuxUser: mateCtx.linuxUser || undefined,
|
|
802
804
|
claudeMd: claudeMd,
|
|
803
805
|
initialContext: moderatorContext,
|
|
804
806
|
initialMessage: "Begin the debate on: " + debate.topic,
|
|
@@ -994,6 +996,7 @@ function attachDebate(ctx) {
|
|
|
994
996
|
var _panMate = matesModule.getMate(debate.mateCtx, mateId);
|
|
995
997
|
ctx.sdk.createMentionSession({
|
|
996
998
|
vendor: _panMate ? _panMate.vendor : null,
|
|
999
|
+
linuxUser: debate.mateCtx && debate.mateCtx.linuxUser ? debate.mateCtx.linuxUser : undefined,
|
|
997
1000
|
claudeMd: claudeMd,
|
|
998
1001
|
initialContext: panelistContext + historyContext,
|
|
999
1002
|
initialMessage: "The moderator addresses you:\n\n" + moderatorText,
|
|
@@ -1530,6 +1533,7 @@ function attachDebate(ctx) {
|
|
|
1530
1533
|
var _modMate3 = matesModule.getMate(mateCtx, debate.moderatorId);
|
|
1531
1534
|
ctx.sdk.createMentionSession({
|
|
1532
1535
|
vendor: _modMate3 ? _modMate3.vendor : null,
|
|
1536
|
+
linuxUser: mateCtx.linuxUser || undefined,
|
|
1533
1537
|
claudeMd: claudeMd,
|
|
1534
1538
|
initialContext: moderatorContext,
|
|
1535
1539
|
initialMessage: resumePrompt,
|
|
@@ -179,6 +179,7 @@ function attachMateInteraction(ctx) {
|
|
|
179
179
|
var _digestQueue = [];
|
|
180
180
|
var _digestBusy = false;
|
|
181
181
|
var _digestWorkerTurns = 0;
|
|
182
|
+
var _digestWorkerLinuxUser = null;
|
|
182
183
|
var DIGEST_WORKER_MAX_TURNS = 20;
|
|
183
184
|
|
|
184
185
|
function enqueueDigest(job) {
|
|
@@ -191,6 +192,13 @@ function attachMateInteraction(ctx) {
|
|
|
191
192
|
_digestBusy = true;
|
|
192
193
|
var job = _digestQueue.shift();
|
|
193
194
|
|
|
195
|
+
if (_digestWorker && _digestWorkerLinuxUser !== (job.linuxUser || null)) {
|
|
196
|
+
try { _digestWorker.close(); } catch (e) {}
|
|
197
|
+
_digestWorker = null;
|
|
198
|
+
_digestWorkerTurns = 0;
|
|
199
|
+
_digestWorkerLinuxUser = null;
|
|
200
|
+
}
|
|
201
|
+
|
|
194
202
|
var mateDir = matesModule.getMateDir(job.mateCtx, job.mateId);
|
|
195
203
|
var knowledgeDir = path.join(mateDir, "knowledge");
|
|
196
204
|
|
|
@@ -315,6 +323,7 @@ function attachMateInteraction(ctx) {
|
|
|
315
323
|
try { _digestWorker.close(); } catch (e) {}
|
|
316
324
|
_digestWorker = null;
|
|
317
325
|
_digestWorkerTurns = 0;
|
|
326
|
+
_digestWorkerLinuxUser = null;
|
|
318
327
|
}
|
|
319
328
|
|
|
320
329
|
var responseText = "";
|
|
@@ -328,12 +337,14 @@ function attachMateInteraction(ctx) {
|
|
|
328
337
|
console.error("[digest-worker] Error:", err);
|
|
329
338
|
_digestWorker = null;
|
|
330
339
|
_digestWorkerTurns = 0;
|
|
340
|
+
_digestWorkerLinuxUser = null;
|
|
331
341
|
if (job.onError) job.onError(err);
|
|
332
342
|
processDigestQueue();
|
|
333
343
|
},
|
|
334
344
|
});
|
|
335
345
|
} else {
|
|
336
346
|
sdk.createMentionSession({
|
|
347
|
+
linuxUser: job.linuxUser || undefined,
|
|
337
348
|
claudeMd: "",
|
|
338
349
|
model: "haiku",
|
|
339
350
|
initialContext: "[Digest Worker] You generate memory digests. Respond with ONLY JSON.",
|
|
@@ -344,10 +355,15 @@ function attachMateInteraction(ctx) {
|
|
|
344
355
|
onError: function (err) {
|
|
345
356
|
console.error("[digest-worker] Create error:", err);
|
|
346
357
|
_digestWorker = null;
|
|
358
|
+
_digestWorkerLinuxUser = null;
|
|
347
359
|
if (job.onError) job.onError(err);
|
|
348
360
|
processDigestQueue();
|
|
349
361
|
},
|
|
350
|
-
}).then(function (ws) {
|
|
362
|
+
}).then(function (ws) {
|
|
363
|
+
_digestWorker = ws;
|
|
364
|
+
_digestWorkerTurns = 1;
|
|
365
|
+
_digestWorkerLinuxUser = job.linuxUser || null;
|
|
366
|
+
}).catch(function (err) {
|
|
351
367
|
if (job.onError) job.onError(err || new Error("digest worker creation failed"));
|
|
352
368
|
processDigestQueue();
|
|
353
369
|
});
|
|
@@ -378,6 +394,7 @@ function attachMateInteraction(ctx) {
|
|
|
378
394
|
|
|
379
395
|
enqueueDigest({
|
|
380
396
|
mateCtx: mateCtx,
|
|
397
|
+
linuxUser: mateCtx.linuxUser || undefined,
|
|
381
398
|
mateId: mateId,
|
|
382
399
|
type: "mention",
|
|
383
400
|
conversationContent: conversationContent,
|
|
@@ -473,6 +490,7 @@ function attachMateInteraction(ctx) {
|
|
|
473
490
|
|
|
474
491
|
enqueueDigest({
|
|
475
492
|
mateCtx: mateCtx,
|
|
493
|
+
linuxUser: mateCtx.linuxUser || undefined,
|
|
476
494
|
mateId: mateId,
|
|
477
495
|
type: "dm",
|
|
478
496
|
priorSummary: priorSummary || "",
|
|
@@ -716,6 +734,7 @@ function attachMateInteraction(ctx) {
|
|
|
716
734
|
// Create new persistent mention session
|
|
717
735
|
sdk.createMentionSession({
|
|
718
736
|
vendor: mate.vendor || null,
|
|
737
|
+
linuxUser: getLinuxUserForSession(session) || undefined,
|
|
719
738
|
claudeMd: claudeMd,
|
|
720
739
|
initialContext: mentionContext,
|
|
721
740
|
initialMessage: mentionFullInput,
|
|
@@ -887,6 +906,7 @@ function attachMateInteraction(ctx) {
|
|
|
887
906
|
].join("\n");
|
|
888
907
|
|
|
889
908
|
sdk.createMentionSession({
|
|
909
|
+
linuxUser: mateCtx.linuxUser || undefined,
|
|
890
910
|
claudeMd: "",
|
|
891
911
|
model: "haiku",
|
|
892
912
|
initialContext: synthesisContext,
|
package/lib/project-memory.js
CHANGED
|
@@ -339,6 +339,7 @@ function attachMemory(ctx) {
|
|
|
339
339
|
var gateText = "";
|
|
340
340
|
var _gateSession = null;
|
|
341
341
|
sdk.createMentionSession({
|
|
342
|
+
linuxUser: mateCtx.linuxUser || undefined,
|
|
342
343
|
claudeMd: "",
|
|
343
344
|
model: "haiku",
|
|
344
345
|
initialContext: gateContext,
|
|
@@ -448,6 +449,7 @@ function attachMemory(ctx) {
|
|
|
448
449
|
var updateText = "";
|
|
449
450
|
var _updateSession = null;
|
|
450
451
|
sdk.createMentionSession({
|
|
452
|
+
linuxUser: mateCtx.linuxUser || undefined,
|
|
451
453
|
claudeMd: "",
|
|
452
454
|
model: "haiku",
|
|
453
455
|
initialContext: updateContext,
|
|
@@ -553,6 +555,7 @@ function attachMemory(ctx) {
|
|
|
553
555
|
var initText = "";
|
|
554
556
|
var _initSession = null;
|
|
555
557
|
sdk.createMentionSession({
|
|
558
|
+
linuxUser: mateCtx.linuxUser || undefined,
|
|
556
559
|
claudeMd: "",
|
|
557
560
|
model: "haiku",
|
|
558
561
|
initialContext: initContext,
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
var yoke = require("./yoke");
|
|
2
|
+
|
|
3
|
+
function modelEntryValue(entry) {
|
|
4
|
+
if (!entry) return "";
|
|
5
|
+
if (typeof entry === "string") return entry;
|
|
6
|
+
return entry.value || entry.id || "";
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
function modelEntryMatches(entry, value) {
|
|
10
|
+
if (!entry || !value) return false;
|
|
11
|
+
if (typeof entry === "string") return entry === value;
|
|
12
|
+
return entry.value === value || entry.id === value || entry.resolvedModel === value;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function attachModels(ctx) {
|
|
16
|
+
var cwd = ctx.cwd;
|
|
17
|
+
var slug = ctx.slug;
|
|
18
|
+
var sm = ctx.sm;
|
|
19
|
+
var sdk = ctx.sdk;
|
|
20
|
+
var adapters = ctx.adapters;
|
|
21
|
+
var sendTo = ctx.sendTo;
|
|
22
|
+
var getSessionForWs = ctx.getSessionForWs;
|
|
23
|
+
var getLinuxUserForWs = ctx.getLinuxUserForWs;
|
|
24
|
+
var serverPort = ctx.serverPort;
|
|
25
|
+
var serverTls = ctx.serverTls;
|
|
26
|
+
var serverAuthToken = ctx.serverAuthToken;
|
|
27
|
+
|
|
28
|
+
async function loadVendorModels(ws, msg) {
|
|
29
|
+
var vendor = msg.vendor || "";
|
|
30
|
+
var requestId = msg.requestId || null;
|
|
31
|
+
var loadError = null;
|
|
32
|
+
var vendorAdapter = null;
|
|
33
|
+
var session = getSessionForWs(ws);
|
|
34
|
+
|
|
35
|
+
if (!vendor) {
|
|
36
|
+
sendTo(ws, {
|
|
37
|
+
type: "model_info",
|
|
38
|
+
vendor: vendor,
|
|
39
|
+
requestId: requestId,
|
|
40
|
+
modelStatus: "error",
|
|
41
|
+
error: "No vendor was selected.",
|
|
42
|
+
model: "",
|
|
43
|
+
models: [],
|
|
44
|
+
sessionId: session ? session.localId : null,
|
|
45
|
+
});
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
try {
|
|
50
|
+
var modelLinuxUser = getLinuxUserForWs(ws);
|
|
51
|
+
vendorAdapter = adapters[vendor] || null;
|
|
52
|
+
if (!vendorAdapter) {
|
|
53
|
+
vendorAdapter = await yoke.lazyCreateAdapter(adapters, vendor, {
|
|
54
|
+
cwd: cwd,
|
|
55
|
+
linuxUser: modelLinuxUser || undefined,
|
|
56
|
+
clayPort: serverPort,
|
|
57
|
+
clayTls: serverTls,
|
|
58
|
+
clayAuthToken: serverAuthToken,
|
|
59
|
+
slug: slug,
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
var cachedModels = (sm.modelsByVendor && sm.modelsByVendor[vendor]) || [];
|
|
64
|
+
var cachedCapabilities = sm.capabilitiesByVendor && sm.capabilitiesByVendor[vendor];
|
|
65
|
+
var needsReadyMetadata = !cachedCapabilities || cachedModels.length === 0;
|
|
66
|
+
if (vendorAdapter && needsReadyMetadata && typeof vendorAdapter.init === "function") {
|
|
67
|
+
try {
|
|
68
|
+
var readyResult = await vendorAdapter.init({
|
|
69
|
+
cwd: cwd,
|
|
70
|
+
linuxUser: modelLinuxUser || undefined,
|
|
71
|
+
clayPort: serverPort,
|
|
72
|
+
clayTls: serverTls,
|
|
73
|
+
clayAuthToken: serverAuthToken,
|
|
74
|
+
slug: slug,
|
|
75
|
+
});
|
|
76
|
+
sm.capabilitiesByVendor = sm.capabilitiesByVendor || {};
|
|
77
|
+
sm.capabilitiesByVendor[vendor] = readyResult.capabilities || {};
|
|
78
|
+
sm.modelsByVendor = sm.modelsByVendor || {};
|
|
79
|
+
if (Array.isArray(readyResult.models) && readyResult.models.length > 0) {
|
|
80
|
+
sm.modelsByVendor[vendor] = readyResult.models;
|
|
81
|
+
}
|
|
82
|
+
} catch (e) {
|
|
83
|
+
loadError = e;
|
|
84
|
+
console.error("[project-models] " + vendor + " init failed:", e.message || e);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (vendorAdapter) {
|
|
89
|
+
sm.availableVendors = Object.keys(adapters);
|
|
90
|
+
sm.modelsByVendor = sm.modelsByVendor || {};
|
|
91
|
+
var currentModels = sm.modelsByVendor[vendor] || [];
|
|
92
|
+
if (currentModels.length === 0 && typeof vendorAdapter.supportedModels === "function") {
|
|
93
|
+
try {
|
|
94
|
+
var supported = await vendorAdapter.supportedModels();
|
|
95
|
+
if (Array.isArray(supported) && supported.length > 0) {
|
|
96
|
+
sm.modelsByVendor[vendor] = supported;
|
|
97
|
+
loadError = null;
|
|
98
|
+
}
|
|
99
|
+
} catch (e) {
|
|
100
|
+
if (!loadError) loadError = e;
|
|
101
|
+
console.error("[project-models] " + vendor + " model listing failed:", e.message || e);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
} else {
|
|
105
|
+
loadError = new Error("The vendor adapter is unavailable.");
|
|
106
|
+
}
|
|
107
|
+
} catch (e) {
|
|
108
|
+
loadError = e;
|
|
109
|
+
console.error("[project-models] model loading failed for " + vendor + ":", e.message || e);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
var vendorModels = (sm.modelsByVendor && sm.modelsByVendor[vendor]) || [];
|
|
113
|
+
var modelToSend = modelEntryValue(vendorModels[0]);
|
|
114
|
+
var preferredModel = (session && session.vendor === vendor && session.model) || (sm.defaultModelByVendor && sm.defaultModelByVendor[vendor]) || "";
|
|
115
|
+
if (preferredModel) {
|
|
116
|
+
for (var i = 0; i < vendorModels.length; i++) {
|
|
117
|
+
if (modelEntryMatches(vendorModels[i], preferredModel)) {
|
|
118
|
+
modelToSend = modelEntryValue(vendorModels[i]);
|
|
119
|
+
break;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
var vendorInfo = yoke.getVendorInfo(vendor);
|
|
124
|
+
var displayName = vendorInfo ? vendorInfo.displayName : vendor;
|
|
125
|
+
var status = vendorModels.length > 0 ? "ready" : (loadError ? "error" : "empty");
|
|
126
|
+
var errorText = "";
|
|
127
|
+
if (status === "error") {
|
|
128
|
+
errorText = "Could not load " + displayName + " models: " + (loadError.message || loadError);
|
|
129
|
+
} else if (status === "empty") {
|
|
130
|
+
errorText = displayName + " returned no models. Check CLI authentication and retry.";
|
|
131
|
+
}
|
|
132
|
+
sendTo(ws, {
|
|
133
|
+
type: "model_info",
|
|
134
|
+
model: modelToSend,
|
|
135
|
+
models: vendorModels,
|
|
136
|
+
vendor: vendor,
|
|
137
|
+
sessionId: session ? session.localId : null,
|
|
138
|
+
capabilities: (sm.capabilitiesByVendor && sm.capabilitiesByVendor[vendor]) || {},
|
|
139
|
+
availableVendors: sm.availableVendors || [],
|
|
140
|
+
installedVendors: sm.installedVendors || [],
|
|
141
|
+
requestId: requestId,
|
|
142
|
+
modelStatus: status,
|
|
143
|
+
error: errorText,
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
async function selectModel(ws, msg) {
|
|
148
|
+
var session = getSessionForWs(ws);
|
|
149
|
+
if (!session) {
|
|
150
|
+
sendTo(ws, { type: "model_selection_result", requestId: msg.requestId || null, ok: false, error: "No active session." });
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
var sessionVendor = session.vendor || sm.defaultVendor || "claude";
|
|
154
|
+
if (msg.vendor && msg.vendor !== sessionVendor) {
|
|
155
|
+
sendTo(ws, {
|
|
156
|
+
type: "model_selection_result",
|
|
157
|
+
requestId: msg.requestId || null,
|
|
158
|
+
ok: false,
|
|
159
|
+
error: "The session vendor changed before the model selection completed.",
|
|
160
|
+
});
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
var knownModels = (sm.modelsByVendor && sm.modelsByVendor[sessionVendor]) || [];
|
|
164
|
+
if (knownModels.length > 0) {
|
|
165
|
+
var known = false;
|
|
166
|
+
for (var i = 0; i < knownModels.length; i++) {
|
|
167
|
+
if (modelEntryMatches(knownModels[i], msg.model)) {
|
|
168
|
+
known = true;
|
|
169
|
+
break;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
if (!known) {
|
|
173
|
+
sendTo(ws, {
|
|
174
|
+
type: "model_selection_result",
|
|
175
|
+
requestId: msg.requestId || null,
|
|
176
|
+
ok: false,
|
|
177
|
+
error: "That model is not available for the selected vendor.",
|
|
178
|
+
});
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
var result = await sdk.setModel(session, msg.model);
|
|
183
|
+
sendTo(ws, {
|
|
184
|
+
type: "model_selection_result",
|
|
185
|
+
requestId: msg.requestId || null,
|
|
186
|
+
ok: !!(result && result.ok),
|
|
187
|
+
model: result && result.model ? result.model : msg.model,
|
|
188
|
+
vendor: sessionVendor,
|
|
189
|
+
error: result && result.error ? result.error : "",
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
function handleMessage(ws, msg) {
|
|
194
|
+
if (msg.type === "get_vendor_models") {
|
|
195
|
+
loadVendorModels(ws, msg).catch(function(e) {
|
|
196
|
+
console.error("[project-models] Unexpected model loading failure:", e.message || e);
|
|
197
|
+
});
|
|
198
|
+
return true;
|
|
199
|
+
}
|
|
200
|
+
if (msg.type === "set_model" && msg.model) {
|
|
201
|
+
selectModel(ws, msg).catch(function(e) {
|
|
202
|
+
sendTo(ws, { type: "model_selection_result", requestId: msg.requestId || null, ok: false, error: e.message || String(e) });
|
|
203
|
+
});
|
|
204
|
+
return true;
|
|
205
|
+
}
|
|
206
|
+
return false;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
return {
|
|
210
|
+
handleMessage: handleMessage,
|
|
211
|
+
loadVendorModels: loadVendorModels,
|
|
212
|
+
selectModel: selectModel,
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
module.exports = {
|
|
217
|
+
attachModels: attachModels,
|
|
218
|
+
modelEntryMatches: modelEntryMatches,
|
|
219
|
+
modelEntryValue: modelEntryValue,
|
|
220
|
+
};
|