clay-server 3.4.0-beta.2 → 3.4.0-beta.20
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-sessions.js +13 -45
- package/lib/project-worker-proposal.js +49 -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 +5 -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 +99 -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
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { store } from './store.js';
|
|
2
2
|
import { getWs } from './ws-ref.js';
|
|
3
|
-
import { VENDOR_AVATARS
|
|
4
|
-
import {
|
|
3
|
+
import { VENDOR_AVATARS } from './app-rendering.js';
|
|
4
|
+
import { buildAgentVendorSelect, fillAgentModels, buildAgentEffortSelect, fillAgentEffort } from './agent-config-selects.js';
|
|
5
5
|
import { showToast } from './utils.js';
|
|
6
6
|
|
|
7
7
|
var pairDialog = null;
|
|
@@ -9,77 +9,14 @@ var pairDialog = null;
|
|
|
9
9
|
// that session becomes the Driver and the dialog only configures the Worker.
|
|
10
10
|
var pendingDriver = null;
|
|
11
11
|
|
|
12
|
-
function optionValue(entry) {
|
|
13
|
-
if (typeof entry === "string") return entry;
|
|
14
|
-
return entry && (entry.value || entry.id) || "";
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
function optionLabel(entry) {
|
|
18
|
-
if (typeof entry === "string") return entry;
|
|
19
|
-
return entry && (entry.displayName || entry.name || entry.value || entry.id) || "";
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
function fillModels(select, vendor, options) {
|
|
23
|
-
var models = (options.modelsByVendor && options.modelsByVendor[vendor]) || [];
|
|
24
|
-
select.innerHTML = "";
|
|
25
|
-
var automatic = document.createElement("option");
|
|
26
|
-
automatic.value = "";
|
|
27
|
-
automatic.textContent = "Automatic";
|
|
28
|
-
select.appendChild(automatic);
|
|
29
|
-
for (var i = 0; i < models.length; i++) {
|
|
30
|
-
var option = document.createElement("option");
|
|
31
|
-
option.value = optionValue(models[i]);
|
|
32
|
-
option.textContent = optionLabel(models[i]);
|
|
33
|
-
select.appendChild(option);
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
|
|
37
12
|
function closePairDialog() {
|
|
38
13
|
if (pairDialog) pairDialog.remove();
|
|
39
14
|
pairDialog = null;
|
|
40
15
|
}
|
|
41
16
|
|
|
42
|
-
function buildVendorSelect(installed, preferred) {
|
|
43
|
-
var select = document.createElement("select");
|
|
44
|
-
select.className = "wt-modal-input";
|
|
45
|
-
for (var i = 0; i < VENDOR_ORDER.length; i++) {
|
|
46
|
-
if (installed.indexOf(VENDOR_ORDER[i]) === -1) continue;
|
|
47
|
-
var option = document.createElement("option");
|
|
48
|
-
option.value = VENDOR_ORDER[i];
|
|
49
|
-
option.textContent = VENDOR_NAMES[VENDOR_ORDER[i]] || VENDOR_ORDER[i];
|
|
50
|
-
select.appendChild(option);
|
|
51
|
-
}
|
|
52
|
-
if (preferred && installed.indexOf(preferred) !== -1) select.value = preferred;
|
|
53
|
-
return select;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
17
|
// Effort levels are vendor- and model-specific (codex: minimal..xhigh,
|
|
57
18
|
// claude/kiro: low..max, plus per-model supportedEffortLevels overrides), so
|
|
58
19
|
// options are rebuilt whenever the role's vendor or model changes.
|
|
59
|
-
function fillEffortOptions(select, vendor, options, modelValue) {
|
|
60
|
-
var previous = select.value;
|
|
61
|
-
var models = (options.modelsByVendor && options.modelsByVendor[vendor]) || [];
|
|
62
|
-
var levels = effortLevelsFor(vendor, models, modelValue);
|
|
63
|
-
select.innerHTML = "";
|
|
64
|
-
var automatic = document.createElement("option");
|
|
65
|
-
automatic.value = "";
|
|
66
|
-
automatic.textContent = "Default";
|
|
67
|
-
select.appendChild(automatic);
|
|
68
|
-
for (var i = 0; i < levels.length; i++) {
|
|
69
|
-
var option = document.createElement("option");
|
|
70
|
-
option.value = levels[i];
|
|
71
|
-
option.textContent = effortDisplayName(levels[i]);
|
|
72
|
-
select.appendChild(option);
|
|
73
|
-
}
|
|
74
|
-
select.value = levels.indexOf(previous) !== -1 ? previous : "";
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
function buildEffortSelect() {
|
|
78
|
-
var select = document.createElement("select");
|
|
79
|
-
select.className = "wt-modal-input";
|
|
80
|
-
return select;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
20
|
function fieldLabel(text, control) {
|
|
84
21
|
var label = document.createElement("label");
|
|
85
22
|
label.className = "wt-modal-label";
|
|
@@ -154,25 +91,25 @@ export function showPairDialog(options) {
|
|
|
154
91
|
: "One Driver plans and directs a visible Worker session.";
|
|
155
92
|
modal.appendChild(desc);
|
|
156
93
|
|
|
157
|
-
var driverVendor =
|
|
94
|
+
var driverVendor = buildAgentVendorSelect(installed, installed.indexOf("claude") !== -1 ? "claude" : installed[0]);
|
|
158
95
|
var workerDefault = options.lastVendor && options.lastVendor !== driverVendor.value ? options.lastVendor : (installed.indexOf("codex") !== -1 ? "codex" : installed[0]);
|
|
159
|
-
var workerVendor =
|
|
96
|
+
var workerVendor = buildAgentVendorSelect(installed, workerDefault);
|
|
160
97
|
var driverModel = document.createElement("select");
|
|
161
98
|
driverModel.className = "wt-modal-input";
|
|
162
99
|
var workerModel = document.createElement("select");
|
|
163
100
|
workerModel.className = "wt-modal-input";
|
|
164
|
-
var driverEffort =
|
|
165
|
-
var workerEffort =
|
|
166
|
-
function refreshDriverEffort() {
|
|
167
|
-
function refreshWorkerEffort() {
|
|
168
|
-
|
|
169
|
-
|
|
101
|
+
var driverEffort = buildAgentEffortSelect();
|
|
102
|
+
var workerEffort = buildAgentEffortSelect();
|
|
103
|
+
function refreshDriverEffort() { fillAgentEffort(driverEffort, driverVendor.value, options, driverModel.value); }
|
|
104
|
+
function refreshWorkerEffort() { fillAgentEffort(workerEffort, workerVendor.value, options, workerModel.value); }
|
|
105
|
+
fillAgentModels(driverModel, driverVendor.value, options);
|
|
106
|
+
fillAgentModels(workerModel, workerVendor.value, options);
|
|
170
107
|
refreshDriverEffort();
|
|
171
108
|
refreshWorkerEffort();
|
|
172
109
|
workerEffort.value = "medium";
|
|
173
110
|
if (workerEffort.value !== "medium") workerEffort.value = "";
|
|
174
|
-
driverVendor.addEventListener("change", function () {
|
|
175
|
-
workerVendor.addEventListener("change", function () {
|
|
111
|
+
driverVendor.addEventListener("change", function () { fillAgentModels(driverModel, driverVendor.value, options); refreshDriverEffort(); });
|
|
112
|
+
workerVendor.addEventListener("change", function () { fillAgentModels(workerModel, workerVendor.value, options); refreshWorkerEffort(); });
|
|
176
113
|
driverModel.addEventListener("change", refreshDriverEffort);
|
|
177
114
|
workerModel.addEventListener("change", refreshWorkerEffort);
|
|
178
115
|
|
|
@@ -318,7 +255,8 @@ export function syncPairChrome(host, split) {
|
|
|
318
255
|
setBtn.title = "Make this session the Driver; the other pane becomes its Worker";
|
|
319
256
|
setBtn.addEventListener("click", function () { sendSetPair(group.id, paneSessionId); });
|
|
320
257
|
var titleEl = header.querySelector(".split-pane-title");
|
|
321
|
-
|
|
258
|
+
var accessEl = header.querySelector(".split-pane-full-access");
|
|
259
|
+
header.insertBefore(setBtn, accessEl ? accessEl.nextSibling : (titleEl ? titleEl.nextSibling : null));
|
|
322
260
|
})(split.panes[ai].sessionId, headers[ai]);
|
|
323
261
|
}
|
|
324
262
|
return;
|
|
@@ -338,7 +276,8 @@ export function syncPairChrome(host, split) {
|
|
|
338
276
|
sendSetPair(group.id, isDriver ? null : sessionId);
|
|
339
277
|
});
|
|
340
278
|
var title = header.querySelector(".split-pane-title");
|
|
341
|
-
|
|
279
|
+
var access = header.querySelector(".split-pane-full-access");
|
|
280
|
+
header.insertBefore(badge, access ? access.nextSibling : (title ? title.nextSibling : null));
|
|
342
281
|
})(split.panes[pi].sessionId, headers[pi]);
|
|
343
282
|
}
|
|
344
283
|
var active = (store.get('splitDelegations') || {})[group.id];
|
|
@@ -231,6 +231,9 @@ function createPane(pane, index) {
|
|
|
231
231
|
}, "Skip Permissions", true);
|
|
232
232
|
});
|
|
233
233
|
header.appendChild(fullAccess);
|
|
234
|
+
// Permission state belongs to the session identity on the left. Keep it
|
|
235
|
+
// immediately after the title instead of grouping it with usage and close.
|
|
236
|
+
header.insertBefore(fullAccess, ctxChip);
|
|
234
237
|
|
|
235
238
|
var close = document.createElement("button");
|
|
236
239
|
close.type = "button";
|
|
@@ -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
|
+
}
|
|
@@ -103,8 +103,10 @@ function statusLabel(status) {
|
|
|
103
103
|
function applyState(card, msg) {
|
|
104
104
|
var status = msg.status || "pending";
|
|
105
105
|
card.dataset.status = status;
|
|
106
|
+
if (msg.autoApproved) card.dataset.autoApproved = "true";
|
|
107
|
+
var autoApproved = card.dataset.autoApproved === "true";
|
|
106
108
|
var badge = card.querySelector(".worker-proposal-status");
|
|
107
|
-
if (badge) badge.textContent = statusLabel(status);
|
|
109
|
+
if (badge) badge.textContent = statusLabel(status) + (autoApproved ? " · auto-approved" : "");
|
|
108
110
|
var error = card.querySelector(".worker-proposal-error");
|
|
109
111
|
if (error) {
|
|
110
112
|
error.textContent = msg.error || "";
|
|
@@ -116,6 +118,8 @@ function applyState(card, msg) {
|
|
|
116
118
|
preview.classList.remove("hidden");
|
|
117
119
|
}
|
|
118
120
|
setControlsDisabled(card, status !== "pending");
|
|
121
|
+
var actions = card.querySelector(".worker-proposal-actions");
|
|
122
|
+
if (actions) actions.classList.toggle("hidden", autoApproved);
|
|
119
123
|
}
|
|
120
124
|
|
|
121
125
|
function sendDecision(card, accepted) {
|
|
@@ -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,13 @@
|
|
|
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
|
+
@import url("css/session-actions.css");
|
|
6
7
|
@import url("css/sidebar.css");
|
|
7
8
|
@import url("css/overlays.css");
|
|
8
9
|
@import url("css/menus.css");
|
|
9
|
-
@import url("css/messages.css?v=
|
|
10
|
+
@import url("css/messages.css?v=20260824");
|
|
10
11
|
@import url("css/rewind.css");
|
|
11
12
|
@import url("css/input.css");
|
|
12
13
|
@import url("css/filebrowser.css");
|
|
@@ -35,3 +36,4 @@
|
|
|
35
36
|
@import url("css/debate.css");
|
|
36
37
|
@import url("css/notifications-center.css");
|
|
37
38
|
@import url("css/tui-attention.css");
|
|
39
|
+
@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.
|
|
@@ -244,17 +245,24 @@ function createSDKBridge(opts) {
|
|
|
244
245
|
return null;
|
|
245
246
|
}
|
|
246
247
|
|
|
247
|
-
function
|
|
248
|
+
function defaultModelForVendor(vendor) {
|
|
249
|
+
return (sm.defaultModelByVendor && sm.defaultModelByVendor[vendor]) || "";
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
function sendModelInfoForVendor(session, vendor, model) {
|
|
248
253
|
var resolvedVendor = vendor || (adapter && adapter.vendor) || "claude";
|
|
249
|
-
|
|
254
|
+
var message = {
|
|
250
255
|
type: "model_info",
|
|
251
256
|
model: model || "",
|
|
252
257
|
models: getModelsForVendor(resolvedVendor),
|
|
253
258
|
vendor: resolvedVendor,
|
|
259
|
+
sessionId: session ? session.localId : null,
|
|
254
260
|
capabilities: (sm.capabilitiesByVendor && sm.capabilitiesByVendor[resolvedVendor]) || {},
|
|
255
261
|
availableVendors: sm.availableVendors || [],
|
|
256
262
|
installedVendors: sm.installedVendors || [],
|
|
257
|
-
}
|
|
263
|
+
};
|
|
264
|
+
if (session) sendToSession(session, message);
|
|
265
|
+
else send(message);
|
|
258
266
|
}
|
|
259
267
|
|
|
260
268
|
function rememberAdapterReady(vendor, result) {
|
|
@@ -323,6 +331,14 @@ function createSDKBridge(opts) {
|
|
|
323
331
|
var mergeSkills = skills.mergeSkills;
|
|
324
332
|
|
|
325
333
|
// --- Message processing (extracted to sdk-message-processor.js) ---
|
|
334
|
+
function getSessionLinuxUser(session) {
|
|
335
|
+
if (!osUsers || !session) return null;
|
|
336
|
+
if (session.lastLinuxUser) return session.lastLinuxUser;
|
|
337
|
+
if (!session.ownerId) return null;
|
|
338
|
+
var owner = usersModule.findUserById(session.ownerId);
|
|
339
|
+
return owner && owner.linuxUser ? owner.linuxUser : null;
|
|
340
|
+
}
|
|
341
|
+
|
|
326
342
|
// Auto-generate a session title via YOKE adapter.generateTitle().
|
|
327
343
|
// Triggered by sdk-message-processor after AUTO_TITLE_TURN_THRESHOLD turns.
|
|
328
344
|
function autoGenerateTitle(session) {
|
|
@@ -345,7 +361,10 @@ function createSDKBridge(opts) {
|
|
|
345
361
|
}
|
|
346
362
|
console.log("[auto-title] Calling adapter.generateTitle with " + userMessages.length + " messages for session " + session.localId);
|
|
347
363
|
|
|
348
|
-
sessionAdapter.generateTitle(userMessages, {
|
|
364
|
+
sessionAdapter.generateTitle(userMessages, {
|
|
365
|
+
cwd: cwd,
|
|
366
|
+
linuxUser: getSessionLinuxUser(session) || undefined,
|
|
367
|
+
}).then(function(title) {
|
|
349
368
|
if (!title || title.length < 2) return;
|
|
350
369
|
title = title.substring(0, 100);
|
|
351
370
|
if (!session.titleManuallySet) {
|
|
@@ -837,19 +856,20 @@ function createSDKBridge(opts) {
|
|
|
837
856
|
case "model_changed":
|
|
838
857
|
session.model = metaData.model;
|
|
839
858
|
sm.saveSessionFile(session);
|
|
840
|
-
sm.
|
|
841
|
-
|
|
842
|
-
|
|
859
|
+
sm.defaultModelByVendor = sm.defaultModelByVendor || {};
|
|
860
|
+
sm.defaultModelByVendor[session.vendor || "claude"] = metaData.model;
|
|
861
|
+
sendModelInfoForVendor(session, session.vendor || (adapter && adapter.vendor) || "claude", metaData.model);
|
|
862
|
+
sendToSession(session, { type: "config_state", model: session.model || defaultModelForVendor(session.vendor || "claude"), vendor: session.vendor || "claude", sessionId: session.localId, mode: session.permissionMode || sm.currentPermissionMode || "default", effort: session.effort || sm.currentEffort || "medium", betas: sm.currentBetas || [] });
|
|
843
863
|
break;
|
|
844
864
|
case "effort_changed":
|
|
845
865
|
session.effort = metaData.effort;
|
|
846
866
|
sm.saveSessionFile(session);
|
|
847
867
|
sm.currentEffort = metaData.effort;
|
|
848
|
-
sendToSession(session, { type: "config_state", model: session.model ||
|
|
868
|
+
sendToSession(session, { type: "config_state", model: session.model || defaultModelForVendor(session.vendor || "claude"), vendor: session.vendor || "claude", sessionId: session.localId, mode: session.permissionMode || sm.currentPermissionMode || "default", effort: session.effort, betas: sm.currentBetas || [] });
|
|
849
869
|
break;
|
|
850
870
|
case "permission_mode_changed":
|
|
851
871
|
sm.currentPermissionMode = metaData.mode;
|
|
852
|
-
sendToSession(session, { type: "config_state", model: session.model ||
|
|
872
|
+
sendToSession(session, { type: "config_state", model: session.model || defaultModelForVendor(session.vendor || "claude"), vendor: session.vendor || "claude", sessionId: session.localId, mode: session.permissionMode || sm.currentPermissionMode, effort: session.effort || sm.currentEffort || "medium", betas: sm.currentBetas || [] });
|
|
853
873
|
break;
|
|
854
874
|
case "worker_error":
|
|
855
875
|
send({ type: "error", text: metaData.error });
|
|
@@ -866,6 +886,8 @@ function createSDKBridge(opts) {
|
|
|
866
886
|
var stillOwnsRuntime = session.queryInstance === myQueryInstance;
|
|
867
887
|
if (session.isProcessing && session.taskStopRequested && stillOwnsRuntime) {
|
|
868
888
|
session.isProcessing = false;
|
|
889
|
+
session._awaitingTurnResult = false;
|
|
890
|
+
session._queuedTurnCount = 0;
|
|
869
891
|
onProcessingChanged();
|
|
870
892
|
send({ type: "status", processing: false });
|
|
871
893
|
sendAndRecord(session, { type: "thinking_stop" });
|
|
@@ -880,6 +902,8 @@ function createSDKBridge(opts) {
|
|
|
880
902
|
// iterator without a result event. Treat any result-less stream end as
|
|
881
903
|
// a failed turn so the UI cannot remain on processing forever.
|
|
882
904
|
session.isProcessing = false;
|
|
905
|
+
session._awaitingTurnResult = false;
|
|
906
|
+
session._queuedTurnCount = 0;
|
|
883
907
|
onProcessingChanged();
|
|
884
908
|
if (!session._lastAdapterError) {
|
|
885
909
|
sendAndRecord(session, {
|
|
@@ -894,6 +918,8 @@ function createSDKBridge(opts) {
|
|
|
894
918
|
} catch (err) {
|
|
895
919
|
if (session.isProcessing && session.queryInstance === myQueryInstance) {
|
|
896
920
|
session.isProcessing = false;
|
|
921
|
+
session._awaitingTurnResult = false;
|
|
922
|
+
session._queuedTurnCount = 0;
|
|
897
923
|
onProcessingChanged();
|
|
898
924
|
if (err.name === "AbortError" || (myAbortController && myAbortController.signal.aborted) || session.taskStopRequested) {
|
|
899
925
|
if (!session.destroying) {
|
|
@@ -1131,7 +1157,9 @@ function createSDKBridge(opts) {
|
|
|
1131
1157
|
async function rollbackConversation(session, numTurns) {
|
|
1132
1158
|
var sessionAdapter = getAdapterForSession(session);
|
|
1133
1159
|
if (sessionAdapter && typeof sessionAdapter.rollbackThread === "function") {
|
|
1134
|
-
await sessionAdapter.rollbackThread(session.cliSessionId, numTurns
|
|
1160
|
+
await sessionAdapter.rollbackThread(session.cliSessionId, numTurns, {
|
|
1161
|
+
linuxUser: getSessionLinuxUser(session) || undefined,
|
|
1162
|
+
});
|
|
1135
1163
|
}
|
|
1136
1164
|
// Claude: conversation rollback is handled by rewindFiles + local history trim
|
|
1137
1165
|
}
|
|
@@ -1143,7 +1171,11 @@ function createSDKBridge(opts) {
|
|
|
1143
1171
|
|
|
1144
1172
|
async function forkSessionUnified(session, uuid) {
|
|
1145
1173
|
var sessionAdapter = getAdapterForSession(session);
|
|
1146
|
-
var result = await sessionAdapter.forkSession(session.cliSessionId, {
|
|
1174
|
+
var result = await sessionAdapter.forkSession(session.cliSessionId, {
|
|
1175
|
+
upToMessageId: uuid,
|
|
1176
|
+
dir: cwd,
|
|
1177
|
+
linuxUser: getSessionLinuxUser(session) || undefined,
|
|
1178
|
+
});
|
|
1147
1179
|
if (!result || !result.sessionId) throw new Error("Fork returned no session id");
|
|
1148
1180
|
|
|
1149
1181
|
// Adapters with rollbackThread (e.g. Codex) use local history copy
|
|
@@ -1208,10 +1240,10 @@ function createSDKBridge(opts) {
|
|
|
1208
1240
|
sm.modelsByVendor[vendor] = await vendorAdapter.supportedModels();
|
|
1209
1241
|
}
|
|
1210
1242
|
var vendorModels = getModelsForVendor(vendor);
|
|
1211
|
-
var modelForVendor = resolveModelInList(vendorModels,
|
|
1243
|
+
var modelForVendor = resolveModelInList(vendorModels, defaultModelForVendor(vendor))
|
|
1212
1244
|
|| modelEntryValue(vendorModels[0])
|
|
1213
1245
|
|| "";
|
|
1214
|
-
sendModelInfoForVendor(vendor, modelForVendor);
|
|
1246
|
+
sendModelInfoForVendor(session, vendor, modelForVendor);
|
|
1215
1247
|
}
|
|
1216
1248
|
return vendorAdapter;
|
|
1217
1249
|
}
|
|
@@ -1414,14 +1446,9 @@ function createSDKBridge(opts) {
|
|
|
1414
1446
|
}
|
|
1415
1447
|
}
|
|
1416
1448
|
|
|
1417
|
-
// Pick a model
|
|
1418
|
-
//
|
|
1419
|
-
|
|
1420
|
-
// project (or in another session that switches vendor to claude) and
|
|
1421
|
-
// Claude would reject the unknown model. We validate against the
|
|
1422
|
-
// session vendor's model list regardless of which vendor happens to be
|
|
1423
|
-
// the project's default adapter.
|
|
1424
|
-
var queryModel = (ls.model && ls.model !== "default" ? ls.model : null) || session.model || sm.currentModel || undefined;
|
|
1449
|
+
// Pick a model from the session or its vendor-specific default, then
|
|
1450
|
+
// validate it against the session vendor's known model list.
|
|
1451
|
+
var queryModel = (ls.model && ls.model !== "default" ? ls.model : null) || session.model || defaultModelForVendor(session.vendor || "claude") || undefined;
|
|
1425
1452
|
var sessionVendor = session.vendor || (adapter && adapter.vendor) || null;
|
|
1426
1453
|
if (sessionVendor) {
|
|
1427
1454
|
var vendorModels = (sm.modelsByVendor && sm.modelsByVendor[sessionVendor]) || [];
|
|
@@ -1437,8 +1464,7 @@ function createSDKBridge(opts) {
|
|
|
1437
1464
|
}
|
|
1438
1465
|
|
|
1439
1466
|
// Bind the resolved model to the session so it survives a daemon
|
|
1440
|
-
// restart.
|
|
1441
|
-
// in-memory value) came back after restart on the adapter default.
|
|
1467
|
+
// restart. This preserves a session-specific selection across restarts.
|
|
1442
1468
|
if (queryModel && session.model !== queryModel) {
|
|
1443
1469
|
session.model = queryModel;
|
|
1444
1470
|
sm.saveSessionFile(session);
|
|
@@ -1471,6 +1497,7 @@ function createSDKBridge(opts) {
|
|
|
1471
1497
|
|
|
1472
1498
|
var queryOpts = {
|
|
1473
1499
|
cwd: cwd,
|
|
1500
|
+
linuxUser: linuxUser || undefined,
|
|
1474
1501
|
// appendSystemPrompt, NOT systemPrompt: a plain-string systemPrompt
|
|
1475
1502
|
// REPLACES the entire Claude Code system prompt (sdk.d.ts:2096), which
|
|
1476
1503
|
// would strip the driver session down to the two-sentence pair preset.
|
|
@@ -1528,6 +1555,10 @@ function createSDKBridge(opts) {
|
|
|
1528
1555
|
ACP: {
|
|
1529
1556
|
mcpServers: getAcpMcpServers(session),
|
|
1530
1557
|
},
|
|
1558
|
+
ANTIGRAVITY: {
|
|
1559
|
+
dangerouslySkipPermissions: dangerouslySkipPermissions
|
|
1560
|
+
|| session.permissionMode === "bypassPermissions",
|
|
1561
|
+
},
|
|
1531
1562
|
KIRO: {
|
|
1532
1563
|
engine: kiroConfig.engine,
|
|
1533
1564
|
mode: kiroConfig.mode,
|
|
@@ -1561,6 +1592,8 @@ function createSDKBridge(opts) {
|
|
|
1561
1592
|
console.error("[sdk-bridge] cliSessionId:", session.cliSessionId, "resume:", !!session.cliSessionId);
|
|
1562
1593
|
console.error("[sdk-bridge] Stack:", e.stack || "(no stack)");
|
|
1563
1594
|
session.isProcessing = false;
|
|
1595
|
+
session._awaitingTurnResult = false;
|
|
1596
|
+
session._queuedTurnCount = 0;
|
|
1564
1597
|
onProcessingChanged();
|
|
1565
1598
|
session.queryInstance = null;
|
|
1566
1599
|
session.messageQueue = null;
|
|
@@ -1602,6 +1635,8 @@ function createSDKBridge(opts) {
|
|
|
1602
1635
|
session.messageQueue = null;
|
|
1603
1636
|
session.abortController = null;
|
|
1604
1637
|
session.isProcessing = false;
|
|
1638
|
+
session._awaitingTurnResult = false;
|
|
1639
|
+
session._queuedTurnCount = 0;
|
|
1605
1640
|
onProcessingChanged();
|
|
1606
1641
|
sendAndRecord(session, { type: "error", text: "The agent connection closed before it received your message. Please send it again." });
|
|
1607
1642
|
sendAndRecord(session, { type: "done", code: 1 });
|
|
@@ -1790,16 +1825,20 @@ function createSDKBridge(opts) {
|
|
|
1790
1825
|
} catch (e) {}
|
|
1791
1826
|
if ((codexBin && fs.existsSync(codexBin)) || tryLookup(yoke.getVendorInfo("codex").binaryName)) result.push("codex");
|
|
1792
1827
|
|
|
1793
|
-
var
|
|
1828
|
+
var subprocessVendorKeys = ["antigravity", "opencode", "kimi", "grok", "copilot", "qwen", "junie"];
|
|
1794
1829
|
var acpProfiles = require("./yoke/acp-agent-profiles");
|
|
1795
|
-
for (var
|
|
1796
|
-
var
|
|
1797
|
-
var
|
|
1798
|
-
if (!linuxUser ||
|
|
1799
|
-
|
|
1800
|
-
|
|
1830
|
+
for (var subprocessVendorIndex = 0; subprocessVendorIndex < subprocessVendorKeys.length; subprocessVendorIndex++) {
|
|
1831
|
+
var subprocessVendor = subprocessVendorKeys[subprocessVendorIndex];
|
|
1832
|
+
var subprocessInfo = yoke.getVendorInfo(subprocessVendor);
|
|
1833
|
+
if (!linuxUser || subprocessInfo.osUserIsolation) {
|
|
1834
|
+
if (subprocessVendor === "antigravity") {
|
|
1835
|
+
if (tryLookup(subprocessInfo.binaryName)) result.push(subprocessVendor);
|
|
1836
|
+
} else {
|
|
1837
|
+
var acpProfile = acpProfiles.getAcpAgentProfile(subprocessVendor);
|
|
1838
|
+
if (acpProfiles.findAcpAgentPath(acpProfile)) result.push(subprocessVendor);
|
|
1839
|
+
}
|
|
1801
1840
|
} else {
|
|
1802
|
-
console.log("[sdk-bridge] " +
|
|
1841
|
+
console.log("[sdk-bridge] " + subprocessInfo.displayName + " hidden for OS-isolated user " + linuxUser + ": per-user spawning is not implemented");
|
|
1803
1842
|
}
|
|
1804
1843
|
}
|
|
1805
1844
|
|
|
@@ -1832,6 +1871,13 @@ function createSDKBridge(opts) {
|
|
|
1832
1871
|
var defaultVendor = adapter ? adapter.vendor : "claude";
|
|
1833
1872
|
sm.defaultVendor = defaultVendor;
|
|
1834
1873
|
|
|
1874
|
+
// Installation detection is independent of adapter initialization. Publish
|
|
1875
|
+
// it before warmup so a new project does not render the initial empty state
|
|
1876
|
+
// as if every CLI were missing while the default adapter starts up.
|
|
1877
|
+
sm.installedVendors = detectInstalledVendors(linuxUser);
|
|
1878
|
+
sm.availableVendors = getAvailableVendors(linuxUser);
|
|
1879
|
+
sendModelInfoForVendor(null, defaultVendor, defaultModelForVendor(defaultVendor));
|
|
1880
|
+
|
|
1835
1881
|
// Initialize default adapter first (provides skills, slash commands, etc.)
|
|
1836
1882
|
if (adapter) {
|
|
1837
1883
|
try {
|
|
@@ -1862,7 +1908,8 @@ function createSDKBridge(opts) {
|
|
|
1862
1908
|
send({ type: "slash_commands", commands: combined, vendor: defaultVendor });
|
|
1863
1909
|
}
|
|
1864
1910
|
if (result.defaultModel) {
|
|
1865
|
-
sm.
|
|
1911
|
+
sm.defaultModelByVendor = sm.defaultModelByVendor || {};
|
|
1912
|
+
sm.defaultModelByVendor[defaultVendor] = sm.defaultModelByVendor[defaultVendor] || result.defaultModel;
|
|
1866
1913
|
}
|
|
1867
1914
|
sm.availableModels = result.models || [];
|
|
1868
1915
|
// Store per-vendor models and capabilities
|
|
@@ -1883,19 +1930,8 @@ function createSDKBridge(opts) {
|
|
|
1883
1930
|
// actually issues a query with it.
|
|
1884
1931
|
sm.modelsByVendor = sm.modelsByVendor || {};
|
|
1885
1932
|
|
|
1886
|
-
//
|
|
1887
|
-
|
|
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
|
-
});
|
|
1933
|
+
// Send the fully initialized state to the client.
|
|
1934
|
+
sendModelInfoForVendor(null, defaultVendor, defaultModelForVendor(defaultVendor));
|
|
1899
1935
|
}
|
|
1900
1936
|
|
|
1901
1937
|
async function setModel(session, model) {
|
|
@@ -1907,22 +1943,28 @@ function createSDKBridge(opts) {
|
|
|
1907
1943
|
// No active query — just store the model for next startQuery
|
|
1908
1944
|
session.model = model;
|
|
1909
1945
|
sm.saveSessionFile(session);
|
|
1910
|
-
sm.
|
|
1911
|
-
//
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1946
|
+
sm.defaultModelByVendor = sm.defaultModelByVendor || {};
|
|
1947
|
+
// Confirm against the session vendor so a non-default vendor does not
|
|
1948
|
+
// receive model metadata labeled as the project's default adapter.
|
|
1949
|
+
var pendingVendor = session.vendor || (adapter && adapter.vendor) || "claude";
|
|
1950
|
+
sm.defaultModelByVendor[pendingVendor] = model;
|
|
1951
|
+
sendModelInfoForVendor(session, pendingVendor, model);
|
|
1952
|
+
sendToSession(session, { type: "config_state", model: model, vendor: pendingVendor, sessionId: session.localId, mode: session.permissionMode || sm.currentPermissionMode || "default", effort: session.effort || sm.currentEffort || "medium", betas: sm.currentBetas || [] });
|
|
1953
|
+
return { ok: true, model: model };
|
|
1915
1954
|
}
|
|
1916
1955
|
try {
|
|
1917
1956
|
await session.queryInstance.setModel(model);
|
|
1918
1957
|
session.model = model;
|
|
1919
1958
|
sm.saveSessionFile(session);
|
|
1920
|
-
sm.
|
|
1959
|
+
sm.defaultModelByVendor = sm.defaultModelByVendor || {};
|
|
1921
1960
|
var sessionVendor = session.vendor || (adapter && adapter.vendor) || "claude";
|
|
1922
|
-
|
|
1923
|
-
|
|
1961
|
+
sm.defaultModelByVendor[sessionVendor] = model;
|
|
1962
|
+
sendModelInfoForVendor(session, sessionVendor, model);
|
|
1963
|
+
sendToSession(session, { type: "config_state", model: model, vendor: sessionVendor, sessionId: session.localId, mode: session.permissionMode || sm.currentPermissionMode || "default", effort: session.effort || sm.currentEffort || "medium", betas: sm.currentBetas || [] });
|
|
1964
|
+
return { ok: true, model: model };
|
|
1924
1965
|
} catch (e) {
|
|
1925
1966
|
send({ type: "error", text: "Failed to switch model: " + (e.message || e) });
|
|
1967
|
+
return { ok: false, model: session.model || "", error: e.message || String(e) };
|
|
1926
1968
|
}
|
|
1927
1969
|
}
|
|
1928
1970
|
|
|
@@ -1951,14 +1993,14 @@ function createSDKBridge(opts) {
|
|
|
1951
1993
|
if (!session.queryInstance) {
|
|
1952
1994
|
// No active query — just store the mode for next startQuery
|
|
1953
1995
|
sm.currentPermissionMode = mode;
|
|
1954
|
-
sendToSession(session, { type: "config_state", model: session.model ||
|
|
1996
|
+
sendToSession(session, { type: "config_state", model: session.model || defaultModelForVendor(session.vendor || "claude"), vendor: session.vendor || "claude", sessionId: session.localId, mode: sm.currentPermissionMode, effort: session.effort || sm.currentEffort || "medium", betas: sm.currentBetas || [] });
|
|
1955
1997
|
return;
|
|
1956
1998
|
}
|
|
1957
1999
|
try {
|
|
1958
2000
|
// Route through QueryHandle (works for both in-process and worker paths)
|
|
1959
2001
|
await session.queryInstance.setPermissionMode(mode);
|
|
1960
2002
|
sm.currentPermissionMode = mode;
|
|
1961
|
-
sendToSession(session, { type: "config_state", model: session.model ||
|
|
2003
|
+
sendToSession(session, { type: "config_state", model: session.model || defaultModelForVendor(session.vendor || "claude"), vendor: session.vendor || "claude", sessionId: session.localId, mode: sm.currentPermissionMode, effort: session.effort || sm.currentEffort || "medium", betas: sm.currentBetas || [] });
|
|
1962
2004
|
} catch (e) {
|
|
1963
2005
|
sendToSession(session, { type: "error", text: "Failed to set permission mode: " + (e.message || e) });
|
|
1964
2006
|
}
|
|
@@ -2009,7 +2051,7 @@ function createSDKBridge(opts) {
|
|
|
2009
2051
|
// Creates a mention session that can be reused across multiple mentions
|
|
2010
2052
|
// within a conversation flow (session continuity).
|
|
2011
2053
|
async function createMentionSession(opts) {
|
|
2012
|
-
// opts: { vendor, claudeMd, initialContext, initialMessage, onDelta, onDone, onError, onActivity }
|
|
2054
|
+
// opts: { vendor, linuxUser, claudeMd, initialContext, initialMessage, onDelta, onDone, onError, onActivity }
|
|
2013
2055
|
var abortController = new AbortController();
|
|
2014
2056
|
|
|
2015
2057
|
// Current response callbacks (swapped on each pushMessage)
|
|
@@ -2029,6 +2071,7 @@ function createSDKBridge(opts) {
|
|
|
2029
2071
|
try {
|
|
2030
2072
|
handle = await mentionAdapter.createQuery({
|
|
2031
2073
|
cwd: cwd,
|
|
2074
|
+
linuxUser: opts.linuxUser || undefined,
|
|
2032
2075
|
systemPrompt: opts.claudeMd,
|
|
2033
2076
|
model: opts.model || undefined,
|
|
2034
2077
|
toolServers: opts.includeMcpServers ? (mergeMcpServers(getMcpServers(), getRemoteMcpServers) || undefined) : undefined,
|