clay-server 3.4.0-beta.1 → 3.4.0-beta.11
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 +1 -1
- package/lib/project-connection.js +20 -5
- 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-shell-command.js +160 -0
- package/lib/project-user-message.js +10 -0
- package/lib/project.js +19 -4
- package/lib/public/antigravity-avatar.png +0 -0
- package/lib/public/app.js +28 -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 +75 -0
- package/lib/public/css/mates.css +6 -0
- package/lib/public/css/menus.css +48 -14
- package/lib/public/css/messages.css +9 -0
- package/lib/public/css/pane.css +4 -0
- package/lib/public/css/pwa-mobile.css +17 -0
- package/lib/public/css/title-bar.css +19 -0
- package/lib/public/grok-avatar.svg +4 -0
- package/lib/public/index.html +38 -7
- package/lib/public/junie-avatar.svg +11 -0
- package/lib/public/kimi-avatar.svg +4 -0
- package/lib/public/modules/app-header.js +4 -0
- package/lib/public/modules/app-messages.js +28 -5
- package/lib/public/modules/app-panels.js +28 -8
- package/lib/public/modules/app-projects.js +3 -1
- package/lib/public/modules/app-rendering.js +19 -4
- package/lib/public/modules/input.js +58 -28
- package/lib/public/modules/mate-sidebar.js +11 -3
- 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/shell-command.js +148 -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 +6 -6
- package/lib/public/modules/split-pair-ui.js +3 -2
- 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/worktree-location.js +17 -0
- package/lib/public/qwen-avatar.svg +4 -0
- package/lib/public/style.css +3 -2
- package/lib/sdk-bridge.js +49 -25
- package/lib/sdk-message-processor.js +10 -1
- package/lib/session-notes-mcp-server.js +7 -1
- package/lib/worktree.js +9 -4
- package/lib/ws-schema.js +3 -0
- package/lib/yoke/acp-agent-profiles.js +64 -14
- package/lib/yoke/adapters/antigravity.js +417 -0
- package/lib/yoke/adapters/codex.js +98 -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/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/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
|
@@ -12,7 +12,7 @@ import { dismissOverlayPanels, closeSidebar, updatePageTitle, spawnDustParticles
|
|
|
12
12
|
import { showConfirm } from './app-misc.js';
|
|
13
13
|
import { getUpcomingSchedules } from './scheduler.js';
|
|
14
14
|
import { refreshMobileChatSheet } from './sidebar-mobile.js';
|
|
15
|
-
import { VENDOR_AVATARS, VENDOR_NAMES, VENDOR_ORDER, VENDOR_HOMEPAGES } from './app-rendering.js';
|
|
15
|
+
import { VENDOR_AVATARS, VENDOR_NAMES, VENDOR_ORDER, VENDOR_HOMEPAGES, isExperimentalVendor } from './app-rendering.js';
|
|
16
16
|
import { openGroup, separateGroup } from './split-view.js';
|
|
17
17
|
import { groupedSessionIds } from './split-group-helpers.js';
|
|
18
18
|
import { openPairDialog } from './split-pair-ui.js';
|
|
@@ -299,13 +299,10 @@ function appendSessionCloseButton(el, session) {
|
|
|
299
299
|
el.appendChild(closeBtn);
|
|
300
300
|
}
|
|
301
301
|
|
|
302
|
-
// Resolve the vendor the "New session" button should launch
|
|
303
|
-
//
|
|
304
|
-
// vendor, else Claude (so the button always has a sane label to render).
|
|
302
|
+
// Resolve the vendor the "New session" button should launch from the stable
|
|
303
|
+
// installed-vendor priority (Claude, then Codex, then the remaining vendors).
|
|
305
304
|
export function resolveDefaultVendor() {
|
|
306
305
|
var installed = store.get('installedVendors') || [];
|
|
307
|
-
var last = store.get('lastVendor') || "";
|
|
308
|
-
if (last && installed.indexOf(last) !== -1) return last;
|
|
309
306
|
for (var i = 0; i < VENDOR_ORDER.length; i++) {
|
|
310
307
|
if (installed.indexOf(VENDOR_ORDER[i]) !== -1) return VENDOR_ORDER[i];
|
|
311
308
|
}
|
|
@@ -330,6 +327,7 @@ export function startNewSession(vendor, extra) {
|
|
|
330
327
|
currentModel: "",
|
|
331
328
|
currentModels: [],
|
|
332
329
|
vendorSelectionLocked: true,
|
|
330
|
+
sessionVendorBound: true,
|
|
333
331
|
});
|
|
334
332
|
}
|
|
335
333
|
|
|
@@ -396,10 +394,12 @@ function showNewSessionMenu(anchorBtn, defaultVendor) {
|
|
|
396
394
|
item.type = "button";
|
|
397
395
|
item.innerHTML = '<img src="' + VENDOR_AVATARS[vendor] + '" class="session-top-action-icon" alt="">' +
|
|
398
396
|
'<span class="session-new-vendor-name">' + name + '</span>' +
|
|
397
|
+
(isExperimentalVendor(vendor) ? '<span class="vendor-experimental-badge" aria-label="Experimental integration" title="Experimental integration; not yet validated through direct use testing">🧪</span>' : '') +
|
|
399
398
|
(isInstalled ? "" : '<span class="session-new-vendor-note">Not installed</span>' + iconHtml("external-link"));
|
|
400
399
|
item.title = isInstalled
|
|
401
400
|
? "New " + name + " session"
|
|
402
401
|
: name + " is not installed - open " + VENDOR_HOMEPAGES[vendor];
|
|
402
|
+
if (isExperimentalVendor(vendor)) item.title += " · Experimental integration; not yet validated through direct use testing";
|
|
403
403
|
item.addEventListener("click", function (e) {
|
|
404
404
|
e.stopPropagation();
|
|
405
405
|
closeSessionCtxMenu();
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { store } from './store.js';
|
|
2
2
|
import { getWs } from './ws-ref.js';
|
|
3
|
-
import { VENDOR_AVATARS, VENDOR_NAMES, VENDOR_ORDER } from './app-rendering.js';
|
|
3
|
+
import { VENDOR_AVATARS, VENDOR_NAMES, VENDOR_ORDER, isExperimentalVendor } from './app-rendering.js';
|
|
4
4
|
import { effortLevelsFor, effortDisplayName } from './app-panels.js';
|
|
5
5
|
import { showToast } from './utils.js';
|
|
6
6
|
|
|
@@ -46,7 +46,8 @@ function buildVendorSelect(installed, preferred) {
|
|
|
46
46
|
if (installed.indexOf(VENDOR_ORDER[i]) === -1) continue;
|
|
47
47
|
var option = document.createElement("option");
|
|
48
48
|
option.value = VENDOR_ORDER[i];
|
|
49
|
-
option.textContent = VENDOR_NAMES[VENDOR_ORDER[i]] || VENDOR_ORDER[i];
|
|
49
|
+
option.textContent = (isExperimentalVendor(VENDOR_ORDER[i]) ? "🧪 " : "") + (VENDOR_NAMES[VENDOR_ORDER[i]] || VENDOR_ORDER[i]);
|
|
50
|
+
if (isExperimentalVendor(VENDOR_ORDER[i])) option.title = "Experimental integration; not yet validated through direct use testing";
|
|
50
51
|
select.appendChild(option);
|
|
51
52
|
}
|
|
52
53
|
if (preferred && installed.indexOf(preferred) !== -1) select.value = preferred;
|
|
@@ -817,8 +817,13 @@ function resolvePermissionIdentity(mateId, vendor) {
|
|
|
817
817
|
var vendorAvatars = {
|
|
818
818
|
claude: "/claude-code-avatar.png",
|
|
819
819
|
codex: "/codex-avatar.png",
|
|
820
|
-
|
|
820
|
+
antigravity: "/antigravity-avatar.png",
|
|
821
821
|
opencode: "/opencode-avatar.svg",
|
|
822
|
+
kimi: "/kimi-avatar.svg",
|
|
823
|
+
grok: "/grok-avatar.svg",
|
|
824
|
+
copilot: "/copilot-avatar.svg",
|
|
825
|
+
qwen: "/qwen-avatar.svg",
|
|
826
|
+
junie: "/junie-avatar.svg",
|
|
822
827
|
kiro: "/kiro-avatar.svg",
|
|
823
828
|
};
|
|
824
829
|
var vendorName = (vendor && VENDOR_NAMES[vendor]) || VENDOR_NAMES.claude;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export var VENDOR_ORDER = ["claude", "codex", "grok", "kimi", "copilot", "qwen", "junie", "antigravity", "opencode", "kiro"];
|
|
2
|
+
export var EXPERIMENTAL_VENDORS = ["grok", "kimi", "copilot", "qwen", "junie", "antigravity", "opencode"];
|
|
3
|
+
|
|
4
|
+
export function isExperimentalVendor(vendor) {
|
|
5
|
+
return EXPERIMENTAL_VENDORS.indexOf(vendor) !== -1;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export function firstInstalledVendor(installedVendors) {
|
|
9
|
+
var installed = Array.isArray(installedVendors) ? installedVendors : [];
|
|
10
|
+
for (var i = 0; i < VENDOR_ORDER.length; i++) {
|
|
11
|
+
if (installed.indexOf(VENDOR_ORDER[i]) !== -1) return VENDOR_ORDER[i];
|
|
12
|
+
}
|
|
13
|
+
return "";
|
|
14
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { store } from './store.js';
|
|
2
|
+
import { firstInstalledVendor } from './vendor-priority.js';
|
|
3
|
+
|
|
4
|
+
export function selectDefaultVendorForBlankSession() {
|
|
5
|
+
var state = store.snap();
|
|
6
|
+
if (!state.activeSessionId || state.sessionHasHistory || state.sessionVendorBound || state.dmMode) return "";
|
|
7
|
+
var vendor = firstInstalledVendor(state.installedVendors);
|
|
8
|
+
if (!vendor) return "";
|
|
9
|
+
if (vendor === state.currentVendor) {
|
|
10
|
+
if (state.vendorSelectionLocked) store.set({ vendorSelectionLocked: false });
|
|
11
|
+
return vendor;
|
|
12
|
+
}
|
|
13
|
+
store.set({
|
|
14
|
+
currentVendor: vendor,
|
|
15
|
+
currentModel: "",
|
|
16
|
+
currentModels: [],
|
|
17
|
+
vendorSelectionLocked: false,
|
|
18
|
+
});
|
|
19
|
+
return vendor;
|
|
20
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export function isExternalWorktree(project) {
|
|
2
|
+
return !!(project && project.isWorktree && project.worktreeExternal === true);
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
export function externalWorktreeTooltip(name) {
|
|
6
|
+
return name + " — outside the main project folder";
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function appendExternalWorktreeBadge(container, className) {
|
|
10
|
+
var badge = document.createElement("span");
|
|
11
|
+
badge.className = className;
|
|
12
|
+
badge.setAttribute("role", "img");
|
|
13
|
+
badge.setAttribute("aria-label", "Outside the main project folder");
|
|
14
|
+
badge.innerHTML = '<i data-lucide="external-link"></i>';
|
|
15
|
+
container.appendChild(badge);
|
|
16
|
+
return badge;
|
|
17
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" role="img" aria-label="Qwen Code">
|
|
2
|
+
<rect width="64" height="64" rx="14" fill="#6b4eff"/>
|
|
3
|
+
<path d="M32 12a20 20 0 1 0 12 36l7 5-3-11a20 20 0 0 0-16-30zm0 9a11 11 0 1 1-7.8 18.8A11 11 0 0 1 32 21z" fill="#fff"/>
|
|
4
|
+
</svg>
|
package/lib/public/style.css
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
@import url("css/base.css");
|
|
2
2
|
@import url("css/icon-strip.css");
|
|
3
|
-
@import url("css/title-bar.css");
|
|
3
|
+
@import url("css/title-bar.css?v=20260824");
|
|
4
4
|
@import url("css/pane.css");
|
|
5
5
|
@import url("css/worker-proposal.css");
|
|
6
6
|
@import url("css/sidebar.css");
|
|
7
7
|
@import url("css/overlays.css");
|
|
8
8
|
@import url("css/menus.css");
|
|
9
|
-
@import url("css/messages.css?v=
|
|
9
|
+
@import url("css/messages.css?v=20260824");
|
|
10
10
|
@import url("css/rewind.css");
|
|
11
11
|
@import url("css/input.css");
|
|
12
12
|
@import url("css/filebrowser.css");
|
|
@@ -35,3 +35,4 @@
|
|
|
35
35
|
@import url("css/debate.css");
|
|
36
36
|
@import url("css/notifications-center.css");
|
|
37
37
|
@import url("css/tui-attention.css");
|
|
38
|
+
@import url("css/pwa-mobile.css?v=20260824f");
|
package/lib/sdk-bridge.js
CHANGED
|
@@ -110,6 +110,7 @@ function createSDKBridge(opts) {
|
|
|
110
110
|
var adapters = opts.adapters || {};
|
|
111
111
|
var mateDisplayName = opts.mateDisplayName || "";
|
|
112
112
|
var isMate = opts.isMate || (slug.indexOf("mate-") === 0);
|
|
113
|
+
var osUsers = !!opts.osUsers;
|
|
113
114
|
var dangerouslySkipPermissions = opts.dangerouslySkipPermissions || false;
|
|
114
115
|
// mcpServers may be either a static object or a getter function. The
|
|
115
116
|
// getter form lets callers gate individual servers at call time (e.g.
|
|
@@ -323,6 +324,14 @@ function createSDKBridge(opts) {
|
|
|
323
324
|
var mergeSkills = skills.mergeSkills;
|
|
324
325
|
|
|
325
326
|
// --- Message processing (extracted to sdk-message-processor.js) ---
|
|
327
|
+
function getSessionLinuxUser(session) {
|
|
328
|
+
if (!osUsers || !session) return null;
|
|
329
|
+
if (session.lastLinuxUser) return session.lastLinuxUser;
|
|
330
|
+
if (!session.ownerId) return null;
|
|
331
|
+
var owner = usersModule.findUserById(session.ownerId);
|
|
332
|
+
return owner && owner.linuxUser ? owner.linuxUser : null;
|
|
333
|
+
}
|
|
334
|
+
|
|
326
335
|
// Auto-generate a session title via YOKE adapter.generateTitle().
|
|
327
336
|
// Triggered by sdk-message-processor after AUTO_TITLE_TURN_THRESHOLD turns.
|
|
328
337
|
function autoGenerateTitle(session) {
|
|
@@ -345,7 +354,10 @@ function createSDKBridge(opts) {
|
|
|
345
354
|
}
|
|
346
355
|
console.log("[auto-title] Calling adapter.generateTitle with " + userMessages.length + " messages for session " + session.localId);
|
|
347
356
|
|
|
348
|
-
sessionAdapter.generateTitle(userMessages, {
|
|
357
|
+
sessionAdapter.generateTitle(userMessages, {
|
|
358
|
+
cwd: cwd,
|
|
359
|
+
linuxUser: getSessionLinuxUser(session) || undefined,
|
|
360
|
+
}).then(function(title) {
|
|
349
361
|
if (!title || title.length < 2) return;
|
|
350
362
|
title = title.substring(0, 100);
|
|
351
363
|
if (!session.titleManuallySet) {
|
|
@@ -1131,7 +1143,9 @@ function createSDKBridge(opts) {
|
|
|
1131
1143
|
async function rollbackConversation(session, numTurns) {
|
|
1132
1144
|
var sessionAdapter = getAdapterForSession(session);
|
|
1133
1145
|
if (sessionAdapter && typeof sessionAdapter.rollbackThread === "function") {
|
|
1134
|
-
await sessionAdapter.rollbackThread(session.cliSessionId, numTurns
|
|
1146
|
+
await sessionAdapter.rollbackThread(session.cliSessionId, numTurns, {
|
|
1147
|
+
linuxUser: getSessionLinuxUser(session) || undefined,
|
|
1148
|
+
});
|
|
1135
1149
|
}
|
|
1136
1150
|
// Claude: conversation rollback is handled by rewindFiles + local history trim
|
|
1137
1151
|
}
|
|
@@ -1143,7 +1157,11 @@ function createSDKBridge(opts) {
|
|
|
1143
1157
|
|
|
1144
1158
|
async function forkSessionUnified(session, uuid) {
|
|
1145
1159
|
var sessionAdapter = getAdapterForSession(session);
|
|
1146
|
-
var result = await sessionAdapter.forkSession(session.cliSessionId, {
|
|
1160
|
+
var result = await sessionAdapter.forkSession(session.cliSessionId, {
|
|
1161
|
+
upToMessageId: uuid,
|
|
1162
|
+
dir: cwd,
|
|
1163
|
+
linuxUser: getSessionLinuxUser(session) || undefined,
|
|
1164
|
+
});
|
|
1147
1165
|
if (!result || !result.sessionId) throw new Error("Fork returned no session id");
|
|
1148
1166
|
|
|
1149
1167
|
// Adapters with rollbackThread (e.g. Codex) use local history copy
|
|
@@ -1471,6 +1489,7 @@ function createSDKBridge(opts) {
|
|
|
1471
1489
|
|
|
1472
1490
|
var queryOpts = {
|
|
1473
1491
|
cwd: cwd,
|
|
1492
|
+
linuxUser: linuxUser || undefined,
|
|
1474
1493
|
// appendSystemPrompt, NOT systemPrompt: a plain-string systemPrompt
|
|
1475
1494
|
// REPLACES the entire Claude Code system prompt (sdk.d.ts:2096), which
|
|
1476
1495
|
// would strip the driver session down to the two-sentence pair preset.
|
|
@@ -1528,6 +1547,10 @@ function createSDKBridge(opts) {
|
|
|
1528
1547
|
ACP: {
|
|
1529
1548
|
mcpServers: getAcpMcpServers(session),
|
|
1530
1549
|
},
|
|
1550
|
+
ANTIGRAVITY: {
|
|
1551
|
+
dangerouslySkipPermissions: dangerouslySkipPermissions
|
|
1552
|
+
|| session.permissionMode === "bypassPermissions",
|
|
1553
|
+
},
|
|
1531
1554
|
KIRO: {
|
|
1532
1555
|
engine: kiroConfig.engine,
|
|
1533
1556
|
mode: kiroConfig.mode,
|
|
@@ -1790,16 +1813,20 @@ function createSDKBridge(opts) {
|
|
|
1790
1813
|
} catch (e) {}
|
|
1791
1814
|
if ((codexBin && fs.existsSync(codexBin)) || tryLookup(yoke.getVendorInfo("codex").binaryName)) result.push("codex");
|
|
1792
1815
|
|
|
1793
|
-
var
|
|
1816
|
+
var subprocessVendorKeys = ["antigravity", "opencode", "kimi", "grok", "copilot", "qwen", "junie"];
|
|
1794
1817
|
var acpProfiles = require("./yoke/acp-agent-profiles");
|
|
1795
|
-
for (var
|
|
1796
|
-
var
|
|
1797
|
-
var
|
|
1798
|
-
if (!linuxUser ||
|
|
1799
|
-
|
|
1800
|
-
|
|
1818
|
+
for (var subprocessVendorIndex = 0; subprocessVendorIndex < subprocessVendorKeys.length; subprocessVendorIndex++) {
|
|
1819
|
+
var subprocessVendor = subprocessVendorKeys[subprocessVendorIndex];
|
|
1820
|
+
var subprocessInfo = yoke.getVendorInfo(subprocessVendor);
|
|
1821
|
+
if (!linuxUser || subprocessInfo.osUserIsolation) {
|
|
1822
|
+
if (subprocessVendor === "antigravity") {
|
|
1823
|
+
if (tryLookup(subprocessInfo.binaryName)) result.push(subprocessVendor);
|
|
1824
|
+
} else {
|
|
1825
|
+
var acpProfile = acpProfiles.getAcpAgentProfile(subprocessVendor);
|
|
1826
|
+
if (acpProfiles.findAcpAgentPath(acpProfile)) result.push(subprocessVendor);
|
|
1827
|
+
}
|
|
1801
1828
|
} else {
|
|
1802
|
-
console.log("[sdk-bridge] " +
|
|
1829
|
+
console.log("[sdk-bridge] " + subprocessInfo.displayName + " hidden for OS-isolated user " + linuxUser + ": per-user spawning is not implemented");
|
|
1803
1830
|
}
|
|
1804
1831
|
}
|
|
1805
1832
|
|
|
@@ -1832,6 +1859,13 @@ function createSDKBridge(opts) {
|
|
|
1832
1859
|
var defaultVendor = adapter ? adapter.vendor : "claude";
|
|
1833
1860
|
sm.defaultVendor = defaultVendor;
|
|
1834
1861
|
|
|
1862
|
+
// Installation detection is independent of adapter initialization. Publish
|
|
1863
|
+
// it before warmup so a new project does not render the initial empty state
|
|
1864
|
+
// as if every CLI were missing while the default adapter starts up.
|
|
1865
|
+
sm.installedVendors = detectInstalledVendors(linuxUser);
|
|
1866
|
+
sm.availableVendors = getAvailableVendors(linuxUser);
|
|
1867
|
+
sendModelInfoForVendor(defaultVendor, sm.currentModel || "");
|
|
1868
|
+
|
|
1835
1869
|
// Initialize default adapter first (provides skills, slash commands, etc.)
|
|
1836
1870
|
if (adapter) {
|
|
1837
1871
|
try {
|
|
@@ -1883,19 +1917,8 @@ function createSDKBridge(opts) {
|
|
|
1883
1917
|
// actually issues a query with it.
|
|
1884
1918
|
sm.modelsByVendor = sm.modelsByVendor || {};
|
|
1885
1919
|
|
|
1886
|
-
//
|
|
1887
|
-
sm.
|
|
1888
|
-
sm.availableVendors = getAvailableVendors(linuxUser);
|
|
1889
|
-
|
|
1890
|
-
// Send initial state to client
|
|
1891
|
-
send({
|
|
1892
|
-
type: "model_info",
|
|
1893
|
-
model: sm.currentModel || "",
|
|
1894
|
-
models: getModelsForVendor(defaultVendor),
|
|
1895
|
-
vendor: defaultVendor,
|
|
1896
|
-
availableVendors: sm.availableVendors,
|
|
1897
|
-
installedVendors: sm.installedVendors,
|
|
1898
|
-
});
|
|
1920
|
+
// Send the fully initialized state to the client.
|
|
1921
|
+
sendModelInfoForVendor(defaultVendor, sm.currentModel || "");
|
|
1899
1922
|
}
|
|
1900
1923
|
|
|
1901
1924
|
async function setModel(session, model) {
|
|
@@ -2009,7 +2032,7 @@ function createSDKBridge(opts) {
|
|
|
2009
2032
|
// Creates a mention session that can be reused across multiple mentions
|
|
2010
2033
|
// within a conversation flow (session continuity).
|
|
2011
2034
|
async function createMentionSession(opts) {
|
|
2012
|
-
// opts: { vendor, claudeMd, initialContext, initialMessage, onDelta, onDone, onError, onActivity }
|
|
2035
|
+
// opts: { vendor, linuxUser, claudeMd, initialContext, initialMessage, onDelta, onDone, onError, onActivity }
|
|
2013
2036
|
var abortController = new AbortController();
|
|
2014
2037
|
|
|
2015
2038
|
// Current response callbacks (swapped on each pushMessage)
|
|
@@ -2029,6 +2052,7 @@ function createSDKBridge(opts) {
|
|
|
2029
2052
|
try {
|
|
2030
2053
|
handle = await mentionAdapter.createQuery({
|
|
2031
2054
|
cwd: cwd,
|
|
2055
|
+
linuxUser: opts.linuxUser || undefined,
|
|
2032
2056
|
systemPrompt: opts.claudeMd,
|
|
2033
2057
|
model: opts.model || undefined,
|
|
2034
2058
|
toolServers: opts.includeMcpServers ? (mergeMcpServers(getMcpServers(), getRemoteMcpServers) || undefined) : undefined,
|
|
@@ -792,7 +792,16 @@ function attachMessageProcessor(ctx) {
|
|
|
792
792
|
// processQueryStream can finish the turn when the iterator closes.
|
|
793
793
|
var adapterErrorText = parsed.text || parsed.message || parsed.error || "Agent runtime error";
|
|
794
794
|
session._lastAdapterError = adapterErrorText;
|
|
795
|
-
|
|
795
|
+
var isSessionWriterConflict = /thread-store conflict|already has an active writer/i.test(adapterErrorText);
|
|
796
|
+
if (isSessionWriterConflict) {
|
|
797
|
+
sendAndRecord(session, {
|
|
798
|
+
type: "session_writer_conflict",
|
|
799
|
+
vendor: session.vendor || "codex",
|
|
800
|
+
text: "This Codex session is already open in another Clay or Codex process. Stop the other server or close the other session, then try again.",
|
|
801
|
+
});
|
|
802
|
+
} else {
|
|
803
|
+
sendAndRecord(session, { type: "error", text: adapterErrorText });
|
|
804
|
+
}
|
|
796
805
|
|
|
797
806
|
} else if (parsed.yokeType === "model_refusal") {
|
|
798
807
|
// Model declined the request. "fallback" => the CLI retried on another
|
|
@@ -2,7 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
var buildShape = require("./session-spawn-mcp-server").buildShape;
|
|
4
4
|
|
|
5
|
-
var MEMORY_CONTRACT =
|
|
5
|
+
var MEMORY_CONTRACT =
|
|
6
|
+
"The sticky-note board persists across sessions and is a user-facing artifact shared with people and Clay agents, not private agent scratch space. " +
|
|
7
|
+
"Default to not writing. Create a note proactively only when the user explicitly asks to remember or track something, or when all of these are true: it will remain useful after the current task and session, it is not already adequately recorded in the repository or another note, and the user would likely be glad to find it on the board a week later. " +
|
|
8
|
+
"Good notes capture an unresolved commitment, durable product decision, user preference, constraint, or handoff that will materially change future work. " +
|
|
9
|
+
"Important exception for deferred defects: while doing code or technical work, actively create a sticky note when you discover a concrete defect, regression risk, security issue, or data-loss risk that is outside the current session goal and will remain unfixed when the turn ends. Do not wait for the user to ask. Include the observable evidence, affected component, likely impact, and a clear next action. Check active notes first when a duplicate is plausible. Do not create defect notes for speculation, general cleanup ideas, or problems you fixed in the current session. " +
|
|
10
|
+
"Never create a note merely because work is important, lengthy, spans agents or restarts, or might help another agent. Do not record completed work, implementation details, test results, investigation logs, transient blockers, conversation summaries, or announcements of your own activity. When uncertain, do not write. " +
|
|
11
|
+
"Updates are visible too: update only when durable state materially changes, and remove a note created by your session when it stops being useful instead of turning it into a completion log. Put a concise plain-text title on the first line, stay focused on one topic, and include only the context needed for future action.";
|
|
6
12
|
|
|
7
13
|
function getToolDefs(handlers) {
|
|
8
14
|
return [
|
package/lib/worktree.js
CHANGED
|
@@ -43,9 +43,14 @@ function isWorktree(projectPath) {
|
|
|
43
43
|
}
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
+
function isPathInside(parentPath, candidatePath) {
|
|
47
|
+
var relative = path.relative(path.resolve(parentPath), path.resolve(candidatePath));
|
|
48
|
+
return relative !== "" && relative !== ".." && relative.indexOf(".." + path.sep) !== 0 && !path.isAbsolute(relative);
|
|
49
|
+
}
|
|
50
|
+
|
|
46
51
|
// Scan worktrees for a given project path
|
|
47
|
-
// Returns array of { path, branch, bare, detached,
|
|
48
|
-
//
|
|
52
|
+
// Returns array of { path, branch, bare, detached, external }
|
|
53
|
+
// external = true when Git registered the worktree outside the main project folder
|
|
49
54
|
function scanWorktrees(projectPath) {
|
|
50
55
|
var resolvedParent = path.resolve(projectPath);
|
|
51
56
|
try {
|
|
@@ -63,7 +68,7 @@ function scanWorktrees(projectPath) {
|
|
|
63
68
|
if (wt.bare) continue;
|
|
64
69
|
var resolvedWt = path.resolve(wt.path);
|
|
65
70
|
if (resolvedWt === resolvedParent) continue;
|
|
66
|
-
wt.
|
|
71
|
+
wt.external = !isPathInside(resolvedParent, resolvedWt);
|
|
67
72
|
wt.dirName = path.basename(wt.path);
|
|
68
73
|
results.push(wt);
|
|
69
74
|
}
|
|
@@ -131,4 +136,4 @@ function removeWorktree(projectPath, worktreeDirName) {
|
|
|
131
136
|
}
|
|
132
137
|
}
|
|
133
138
|
|
|
134
|
-
module.exports = { scanWorktrees: scanWorktrees, createWorktree: createWorktree, removeWorktree: removeWorktree, isWorktree: isWorktree };
|
|
139
|
+
module.exports = { scanWorktrees: scanWorktrees, createWorktree: createWorktree, removeWorktree: removeWorktree, isWorktree: isWorktree, isPathInside: isPathInside };
|
package/lib/ws-schema.js
CHANGED
|
@@ -145,6 +145,7 @@ var schema = {
|
|
|
145
145
|
"kill_process": { direction: "c2s", handler: "lib/project-sessions.js", description: "Kill a system process by PID" },
|
|
146
146
|
"process_killed": { direction: "s2c", handler: "lib/public/modules/app-messages.js", description: "Process was successfully killed" },
|
|
147
147
|
"process_conflict": { direction: "s2c", handler: "lib/public/modules/app-messages.js", description: "Conflict: another process is using the session" },
|
|
148
|
+
"session_writer_conflict": { direction: "s2c", handler: "lib/public/modules/app-messages.js", description: "Codex session is already open in another process" },
|
|
148
149
|
|
|
149
150
|
// -----------------------------------------------------------------------
|
|
150
151
|
// Context / usage
|
|
@@ -353,6 +354,8 @@ var schema = {
|
|
|
353
354
|
"term_closed": { direction: "s2c", handler: "lib/public/modules/app-messages.js", description: "Terminal session was closed" },
|
|
354
355
|
"term_list": { direction: "s2c", handler: "lib/public/modules/app-messages.js", description: "Full list of open terminals" },
|
|
355
356
|
"term_error": { direction: "s2c", handler: "lib/public/modules/app-messages.js", description: "Terminal error (e.g. access denied)" },
|
|
357
|
+
"shell_command": { direction: "c2s", handler: "lib/project-shell-command.js", description: "Run a one-shot shell command for agent context" },
|
|
358
|
+
"shell_command_result": { direction: "s2c", handler: "lib/public/modules/app-messages.js", description: "One-shot shell command output and exit status" },
|
|
356
359
|
|
|
357
360
|
// -----------------------------------------------------------------------
|
|
358
361
|
// Sticky notes
|
|
@@ -96,21 +96,16 @@ function validateOpenCodeConfig(config) {
|
|
|
96
96
|
}
|
|
97
97
|
}
|
|
98
98
|
|
|
99
|
+
function trustDefaultWhenModeIsNotExposed(ctx, next) {
|
|
100
|
+
var options = ctx.state && ctx.state.configOptions;
|
|
101
|
+
for (var i = 0; i < (options || []).length; i++) {
|
|
102
|
+
if (options[i].category === "mode" || options[i].id === "mode") return next();
|
|
103
|
+
}
|
|
104
|
+
if (ctx.state && ctx.state.modes) return next();
|
|
105
|
+
return Promise.resolve();
|
|
106
|
+
}
|
|
107
|
+
|
|
99
108
|
var ACP_AGENT_PROFILES = {
|
|
100
|
-
gemini: {
|
|
101
|
-
vendor: "gemini",
|
|
102
|
-
displayName: "Gemini CLI",
|
|
103
|
-
binaryName: "gemini",
|
|
104
|
-
overrideName: "GEMINI_CLI_PATH",
|
|
105
|
-
args: ["--acp", "--approval-mode=default"],
|
|
106
|
-
defaultModels: ["auto"],
|
|
107
|
-
defaultModel: "auto",
|
|
108
|
-
// Gemini advertises loadSession, but releases through 0.46.0 could load
|
|
109
|
-
// the transcript without restoring the model's conversation memory.
|
|
110
|
-
// Keep resume gated until Clay's live contract test proves it reliable.
|
|
111
|
-
sessionResume: false,
|
|
112
|
-
permissionModeGuaranteed: true,
|
|
113
|
-
},
|
|
114
109
|
opencode: {
|
|
115
110
|
vendor: "opencode",
|
|
116
111
|
displayName: "OpenCode",
|
|
@@ -161,6 +156,61 @@ var ACP_AGENT_PROFILES = {
|
|
|
161
156
|
return Promise.resolve();
|
|
162
157
|
},
|
|
163
158
|
},
|
|
159
|
+
kimi: {
|
|
160
|
+
vendor: "kimi",
|
|
161
|
+
displayName: "Kimi Code",
|
|
162
|
+
binaryName: "kimi",
|
|
163
|
+
overrideName: "KIMI_CLI_PATH",
|
|
164
|
+
args: ["acp"],
|
|
165
|
+
defaultModels: ["auto"],
|
|
166
|
+
defaultModel: "auto",
|
|
167
|
+
sessionResume: null,
|
|
168
|
+
ensureSafePermissionMode: trustDefaultWhenModeIsNotExposed,
|
|
169
|
+
},
|
|
170
|
+
grok: {
|
|
171
|
+
vendor: "grok",
|
|
172
|
+
displayName: "Grok Build",
|
|
173
|
+
binaryName: "grok",
|
|
174
|
+
overrideName: "GROK_CLI_PATH",
|
|
175
|
+
args: ["--no-auto-update", "--permission-mode", "ask", "agent", "stdio"],
|
|
176
|
+
defaultModels: ["auto"],
|
|
177
|
+
defaultModel: "auto",
|
|
178
|
+
sessionResume: null,
|
|
179
|
+
permissionModeGuaranteed: true,
|
|
180
|
+
},
|
|
181
|
+
copilot: {
|
|
182
|
+
vendor: "copilot",
|
|
183
|
+
displayName: "GitHub Copilot CLI",
|
|
184
|
+
binaryName: "copilot",
|
|
185
|
+
overrideName: "COPILOT_CLI_PATH",
|
|
186
|
+
args: ["--acp"],
|
|
187
|
+
defaultModels: ["auto"],
|
|
188
|
+
defaultModel: "auto",
|
|
189
|
+
sessionResume: null,
|
|
190
|
+
ensureSafePermissionMode: trustDefaultWhenModeIsNotExposed,
|
|
191
|
+
},
|
|
192
|
+
qwen: {
|
|
193
|
+
vendor: "qwen",
|
|
194
|
+
displayName: "Qwen Code",
|
|
195
|
+
binaryName: "qwen",
|
|
196
|
+
overrideName: "QWEN_CLI_PATH",
|
|
197
|
+
args: ["--acp", "--approval-mode", "default"],
|
|
198
|
+
defaultModels: ["auto"],
|
|
199
|
+
defaultModel: "auto",
|
|
200
|
+
sessionResume: null,
|
|
201
|
+
permissionModeGuaranteed: true,
|
|
202
|
+
},
|
|
203
|
+
junie: {
|
|
204
|
+
vendor: "junie",
|
|
205
|
+
displayName: "Junie CLI",
|
|
206
|
+
binaryName: "junie",
|
|
207
|
+
overrideName: "JUNIE_CLI_PATH",
|
|
208
|
+
args: ["--acp", "true"],
|
|
209
|
+
defaultModels: ["auto"],
|
|
210
|
+
defaultModel: "auto",
|
|
211
|
+
sessionResume: null,
|
|
212
|
+
ensureSafePermissionMode: trustDefaultWhenModeIsNotExposed,
|
|
213
|
+
},
|
|
164
214
|
};
|
|
165
215
|
|
|
166
216
|
function getAcpAgentProfile(vendor) {
|