clay-server 3.4.0-beta.9 → 3.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/daemon.js +58 -58
- package/lib/project-connection.js +19 -6
- package/lib/project-models.js +220 -0
- package/lib/project-session-handoff.js +162 -0
- package/lib/project-session-pair.js +14 -7
- package/lib/project-session-spawn.js +1 -1
- package/lib/project-sessions.js +13 -45
- package/lib/project-worker-proposal.js +51 -14
- package/lib/project.js +33 -77
- package/lib/public/app.js +11 -0
- package/lib/public/copilot-avatar.svg +5 -0
- package/lib/public/css/filebrowser.css +6 -1
- package/lib/public/css/input.css +56 -0
- package/lib/public/css/mates.css +6 -0
- package/lib/public/css/menus.css +35 -56
- package/lib/public/css/pane.css +18 -3
- package/lib/public/css/session-actions.css +98 -0
- package/lib/public/grok-avatar.svg +4 -0
- package/lib/public/index.html +26 -9
- package/lib/public/junie-avatar.svg +11 -0
- package/lib/public/kimi-avatar.svg +4 -0
- package/lib/public/modules/agent-config-selects.js +69 -0
- package/lib/public/modules/app-connection.js +6 -0
- package/lib/public/modules/app-header.js +0 -22
- package/lib/public/modules/app-messages.js +48 -9
- package/lib/public/modules/app-panels.js +33 -82
- package/lib/public/modules/app-projects.js +2 -0
- package/lib/public/modules/app-rendering.js +16 -1
- package/lib/public/modules/background-tasks-ui.js +55 -0
- package/lib/public/modules/filebrowser-tabs.js +65 -0
- package/lib/public/modules/filebrowser.js +23 -3
- package/lib/public/modules/mate-sidebar.js +10 -2
- package/lib/public/modules/model-picker.js +263 -0
- package/lib/public/modules/pane-bridge.js +6 -0
- package/lib/public/modules/session-actions.js +294 -0
- package/lib/public/modules/sidebar-mobile.js +2 -1
- package/lib/public/modules/sidebar-sessions.js +17 -1
- package/lib/public/modules/split-pair-ui.js +16 -77
- package/lib/public/modules/split-view.js +11 -1
- package/lib/public/modules/tools.js +5 -0
- package/lib/public/modules/vendor-priority.js +6 -1
- package/lib/public/modules/worker-proposal.js +6 -1
- package/lib/public/qwen-avatar.svg +4 -0
- package/lib/public/style.css +1 -0
- package/lib/sdk-bridge.js +57 -34
- package/lib/sdk-message-processor.js +26 -4
- package/lib/session-handoff-context.js +110 -0
- package/lib/session-notes-mcp-server.js +1 -0
- package/lib/sessions.js +4 -0
- package/lib/ws-schema.js +9 -1
- package/lib/yoke/acp-agent-profiles.js +64 -0
- package/lib/yoke/adapters/acp.js +2 -1
- package/lib/yoke/adapters/claude.js +34 -0
- package/lib/yoke/adapters/codex.js +38 -3
- package/lib/yoke/adapters/copilot.js +7 -0
- package/lib/yoke/adapters/grok.js +7 -0
- package/lib/yoke/adapters/junie.js +7 -0
- package/lib/yoke/adapters/kimi.js +7 -0
- package/lib/yoke/adapters/kiro.js +4 -3
- package/lib/yoke/adapters/qwen.js +7 -0
- package/lib/yoke/codex-background-tasks.js +84 -0
- package/lib/yoke/index.js +43 -14
- package/lib/yoke/instructions.js +3 -0
- package/lib/yoke/interface.js +21 -0
- package/lib/yoke/vendor-registry.js +55 -0
- package/package.json +1 -1
|
@@ -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];
|
|
@@ -10,6 +10,7 @@ import { VENDOR_AVATARS, VENDOR_NAMES } from './app-rendering.js';
|
|
|
10
10
|
import { groupedSessionIds, findSplitGroup } from './split-group-helpers.js';
|
|
11
11
|
import { showConfirm } from './app-misc.js';
|
|
12
12
|
import { syncPairChrome } from './split-pair-ui.js';
|
|
13
|
+
import { presentMarkdownEdit } from './filebrowser.js';
|
|
13
14
|
|
|
14
15
|
var host = null;
|
|
15
16
|
var nativeApp = null;
|
|
@@ -231,6 +232,9 @@ function createPane(pane, index) {
|
|
|
231
232
|
}, "Skip Permissions", true);
|
|
232
233
|
});
|
|
233
234
|
header.appendChild(fullAccess);
|
|
235
|
+
// Permission state belongs to the session identity on the left. Keep it
|
|
236
|
+
// immediately after the title instead of grouping it with usage and close.
|
|
237
|
+
header.insertBefore(fullAccess, ctxChip);
|
|
234
238
|
|
|
235
239
|
var close = document.createElement("button");
|
|
236
240
|
close.type = "button";
|
|
@@ -266,7 +270,13 @@ function updatePaneContextChip(paneEl, msg) {
|
|
|
266
270
|
function handlePaneMessage(event) {
|
|
267
271
|
if (event.origin !== window.location.origin) return;
|
|
268
272
|
var msg = event.data;
|
|
269
|
-
if (!msg ||
|
|
273
|
+
if (!msg || !host) return;
|
|
274
|
+
if (msg.type === "clay-pane-present-markdown") {
|
|
275
|
+
var present = msg.message;
|
|
276
|
+
if (present) presentMarkdownEdit(present);
|
|
277
|
+
return;
|
|
278
|
+
}
|
|
279
|
+
if (msg.type !== "clay-pane-context") return;
|
|
270
280
|
var frames = host.querySelectorAll(".split-pane-frame");
|
|
271
281
|
for (var i = 0; i < frames.length; i++) {
|
|
272
282
|
if (frames[i].contentWindow === event.source) {
|
|
@@ -819,6 +819,11 @@ function resolvePermissionIdentity(mateId, vendor) {
|
|
|
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;
|
|
@@ -1,4 +1,9 @@
|
|
|
1
|
-
export var VENDOR_ORDER = ["claude", "codex", "antigravity", "opencode", "kiro"];
|
|
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
|
+
}
|
|
2
7
|
|
|
3
8
|
export function firstInstalledVendor(installedVendors) {
|
|
4
9
|
var installed = Array.isArray(installedVendors) ? installedVendors : [];
|
|
@@ -95,6 +95,7 @@ function statusLabel(status) {
|
|
|
95
95
|
if (status === "starting") return "Starting Worker";
|
|
96
96
|
if (status === "running") return "Worker running";
|
|
97
97
|
if (status === "completed") return "Completed";
|
|
98
|
+
if (status === "interrupted") return "Interrupted";
|
|
98
99
|
if (status === "declined") return "Continuing here";
|
|
99
100
|
if (status === "error") return "Needs attention";
|
|
100
101
|
return "Suggested by Fable";
|
|
@@ -103,8 +104,10 @@ function statusLabel(status) {
|
|
|
103
104
|
function applyState(card, msg) {
|
|
104
105
|
var status = msg.status || "pending";
|
|
105
106
|
card.dataset.status = status;
|
|
107
|
+
if (msg.autoApproved) card.dataset.autoApproved = "true";
|
|
108
|
+
var autoApproved = card.dataset.autoApproved === "true";
|
|
106
109
|
var badge = card.querySelector(".worker-proposal-status");
|
|
107
|
-
if (badge) badge.textContent = statusLabel(status);
|
|
110
|
+
if (badge) badge.textContent = statusLabel(status) + (autoApproved ? " · auto-approved" : "");
|
|
108
111
|
var error = card.querySelector(".worker-proposal-error");
|
|
109
112
|
if (error) {
|
|
110
113
|
error.textContent = msg.error || "";
|
|
@@ -116,6 +119,8 @@ function applyState(card, msg) {
|
|
|
116
119
|
preview.classList.remove("hidden");
|
|
117
120
|
}
|
|
118
121
|
setControlsDisabled(card, status !== "pending");
|
|
122
|
+
var actions = card.querySelector(".worker-proposal-actions");
|
|
123
|
+
if (actions) actions.classList.toggle("hidden", autoApproved);
|
|
119
124
|
}
|
|
120
125
|
|
|
121
126
|
function sendDecision(card, accepted) {
|
|
@@ -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
package/lib/sdk-bridge.js
CHANGED
|
@@ -245,17 +245,24 @@ function createSDKBridge(opts) {
|
|
|
245
245
|
return null;
|
|
246
246
|
}
|
|
247
247
|
|
|
248
|
-
function
|
|
248
|
+
function defaultModelForVendor(vendor) {
|
|
249
|
+
return (sm.defaultModelByVendor && sm.defaultModelByVendor[vendor]) || "";
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
function sendModelInfoForVendor(session, vendor, model) {
|
|
249
253
|
var resolvedVendor = vendor || (adapter && adapter.vendor) || "claude";
|
|
250
|
-
|
|
254
|
+
var message = {
|
|
251
255
|
type: "model_info",
|
|
252
256
|
model: model || "",
|
|
253
257
|
models: getModelsForVendor(resolvedVendor),
|
|
254
258
|
vendor: resolvedVendor,
|
|
259
|
+
sessionId: session ? session.localId : null,
|
|
255
260
|
capabilities: (sm.capabilitiesByVendor && sm.capabilitiesByVendor[resolvedVendor]) || {},
|
|
256
261
|
availableVendors: sm.availableVendors || [],
|
|
257
262
|
installedVendors: sm.installedVendors || [],
|
|
258
|
-
}
|
|
263
|
+
};
|
|
264
|
+
if (session) sendToSession(session, message);
|
|
265
|
+
else send(message);
|
|
259
266
|
}
|
|
260
267
|
|
|
261
268
|
function rememberAdapterReady(vendor, result) {
|
|
@@ -849,19 +856,20 @@ function createSDKBridge(opts) {
|
|
|
849
856
|
case "model_changed":
|
|
850
857
|
session.model = metaData.model;
|
|
851
858
|
sm.saveSessionFile(session);
|
|
852
|
-
sm.
|
|
853
|
-
|
|
854
|
-
|
|
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 || [] });
|
|
855
863
|
break;
|
|
856
864
|
case "effort_changed":
|
|
857
865
|
session.effort = metaData.effort;
|
|
858
866
|
sm.saveSessionFile(session);
|
|
859
867
|
sm.currentEffort = metaData.effort;
|
|
860
|
-
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 || [] });
|
|
861
869
|
break;
|
|
862
870
|
case "permission_mode_changed":
|
|
863
871
|
sm.currentPermissionMode = metaData.mode;
|
|
864
|
-
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 || [] });
|
|
865
873
|
break;
|
|
866
874
|
case "worker_error":
|
|
867
875
|
send({ type: "error", text: metaData.error });
|
|
@@ -877,7 +885,10 @@ function createSDKBridge(opts) {
|
|
|
877
885
|
console.log("[sdk-bridge] processQueryStream ended: isProcessing=" + session.isProcessing + " taskStopRequested=" + session.taskStopRequested);
|
|
878
886
|
var stillOwnsRuntime = session.queryInstance === myQueryInstance;
|
|
879
887
|
if (session.isProcessing && session.taskStopRequested && stillOwnsRuntime) {
|
|
888
|
+
session._lastTurnInterrupted = true;
|
|
880
889
|
session.isProcessing = false;
|
|
890
|
+
session._awaitingTurnResult = false;
|
|
891
|
+
session._queuedTurnCount = 0;
|
|
881
892
|
onProcessingChanged();
|
|
882
893
|
send({ type: "status", processing: false });
|
|
883
894
|
sendAndRecord(session, { type: "thinking_stop" });
|
|
@@ -892,6 +903,8 @@ function createSDKBridge(opts) {
|
|
|
892
903
|
// iterator without a result event. Treat any result-less stream end as
|
|
893
904
|
// a failed turn so the UI cannot remain on processing forever.
|
|
894
905
|
session.isProcessing = false;
|
|
906
|
+
session._awaitingTurnResult = false;
|
|
907
|
+
session._queuedTurnCount = 0;
|
|
895
908
|
onProcessingChanged();
|
|
896
909
|
if (!session._lastAdapterError) {
|
|
897
910
|
sendAndRecord(session, {
|
|
@@ -906,8 +919,11 @@ function createSDKBridge(opts) {
|
|
|
906
919
|
} catch (err) {
|
|
907
920
|
if (session.isProcessing && session.queryInstance === myQueryInstance) {
|
|
908
921
|
session.isProcessing = false;
|
|
922
|
+
session._awaitingTurnResult = false;
|
|
923
|
+
session._queuedTurnCount = 0;
|
|
909
924
|
onProcessingChanged();
|
|
910
925
|
if (err.name === "AbortError" || (myAbortController && myAbortController.signal.aborted) || session.taskStopRequested) {
|
|
926
|
+
session._lastTurnInterrupted = true;
|
|
911
927
|
if (!session.destroying) {
|
|
912
928
|
sendAndRecord(session, { type: "thinking_stop" });
|
|
913
929
|
var interruptMsg2 = (session.vendor && session.vendor !== "claude")
|
|
@@ -1175,6 +1191,7 @@ function createSDKBridge(opts) {
|
|
|
1175
1191
|
// Wrapper: marks the boot window so pushMessage can buffer instead of
|
|
1176
1192
|
// dropping messages that arrive while createQuery is still awaiting.
|
|
1177
1193
|
async function startQuery(session, text, images, linuxUser) {
|
|
1194
|
+
delete session._lastTurnInterrupted;
|
|
1178
1195
|
session._queryStarting = true;
|
|
1179
1196
|
try {
|
|
1180
1197
|
return await startQueryInner(session, text, images, linuxUser);
|
|
@@ -1226,10 +1243,10 @@ function createSDKBridge(opts) {
|
|
|
1226
1243
|
sm.modelsByVendor[vendor] = await vendorAdapter.supportedModels();
|
|
1227
1244
|
}
|
|
1228
1245
|
var vendorModels = getModelsForVendor(vendor);
|
|
1229
|
-
var modelForVendor = resolveModelInList(vendorModels,
|
|
1246
|
+
var modelForVendor = resolveModelInList(vendorModels, defaultModelForVendor(vendor))
|
|
1230
1247
|
|| modelEntryValue(vendorModels[0])
|
|
1231
1248
|
|| "";
|
|
1232
|
-
sendModelInfoForVendor(vendor, modelForVendor);
|
|
1249
|
+
sendModelInfoForVendor(session, vendor, modelForVendor);
|
|
1233
1250
|
}
|
|
1234
1251
|
return vendorAdapter;
|
|
1235
1252
|
}
|
|
@@ -1432,14 +1449,9 @@ function createSDKBridge(opts) {
|
|
|
1432
1449
|
}
|
|
1433
1450
|
}
|
|
1434
1451
|
|
|
1435
|
-
// Pick a model
|
|
1436
|
-
//
|
|
1437
|
-
|
|
1438
|
-
// project (or in another session that switches vendor to claude) and
|
|
1439
|
-
// Claude would reject the unknown model. We validate against the
|
|
1440
|
-
// session vendor's model list regardless of which vendor happens to be
|
|
1441
|
-
// the project's default adapter.
|
|
1442
|
-
var queryModel = (ls.model && ls.model !== "default" ? ls.model : null) || session.model || sm.currentModel || undefined;
|
|
1452
|
+
// Pick a model from the session or its vendor-specific default, then
|
|
1453
|
+
// validate it against the session vendor's known model list.
|
|
1454
|
+
var queryModel = (ls.model && ls.model !== "default" ? ls.model : null) || session.model || defaultModelForVendor(session.vendor || "claude") || undefined;
|
|
1443
1455
|
var sessionVendor = session.vendor || (adapter && adapter.vendor) || null;
|
|
1444
1456
|
if (sessionVendor) {
|
|
1445
1457
|
var vendorModels = (sm.modelsByVendor && sm.modelsByVendor[sessionVendor]) || [];
|
|
@@ -1455,8 +1467,7 @@ function createSDKBridge(opts) {
|
|
|
1455
1467
|
}
|
|
1456
1468
|
|
|
1457
1469
|
// Bind the resolved model to the session so it survives a daemon
|
|
1458
|
-
// restart.
|
|
1459
|
-
// in-memory value) came back after restart on the adapter default.
|
|
1470
|
+
// restart. This preserves a session-specific selection across restarts.
|
|
1460
1471
|
if (queryModel && session.model !== queryModel) {
|
|
1461
1472
|
session.model = queryModel;
|
|
1462
1473
|
sm.saveSessionFile(session);
|
|
@@ -1584,6 +1595,8 @@ function createSDKBridge(opts) {
|
|
|
1584
1595
|
console.error("[sdk-bridge] cliSessionId:", session.cliSessionId, "resume:", !!session.cliSessionId);
|
|
1585
1596
|
console.error("[sdk-bridge] Stack:", e.stack || "(no stack)");
|
|
1586
1597
|
session.isProcessing = false;
|
|
1598
|
+
session._awaitingTurnResult = false;
|
|
1599
|
+
session._queuedTurnCount = 0;
|
|
1587
1600
|
onProcessingChanged();
|
|
1588
1601
|
session.queryInstance = null;
|
|
1589
1602
|
session.messageQueue = null;
|
|
@@ -1625,6 +1638,8 @@ function createSDKBridge(opts) {
|
|
|
1625
1638
|
session.messageQueue = null;
|
|
1626
1639
|
session.abortController = null;
|
|
1627
1640
|
session.isProcessing = false;
|
|
1641
|
+
session._awaitingTurnResult = false;
|
|
1642
|
+
session._queuedTurnCount = 0;
|
|
1628
1643
|
onProcessingChanged();
|
|
1629
1644
|
sendAndRecord(session, { type: "error", text: "The agent connection closed before it received your message. Please send it again." });
|
|
1630
1645
|
sendAndRecord(session, { type: "done", code: 1 });
|
|
@@ -1695,6 +1710,7 @@ function createSDKBridge(opts) {
|
|
|
1695
1710
|
console.error("[sdk-bridge] QueryHandle rejected message for session " + session.localId + ":", e.message || e);
|
|
1696
1711
|
}
|
|
1697
1712
|
if (delivered) {
|
|
1713
|
+
delete session._lastTurnInterrupted;
|
|
1698
1714
|
if (session._awaitingTurnResult) {
|
|
1699
1715
|
session._queuedTurnCount = (session._queuedTurnCount || 0) + 1;
|
|
1700
1716
|
} else {
|
|
@@ -1813,7 +1829,7 @@ function createSDKBridge(opts) {
|
|
|
1813
1829
|
} catch (e) {}
|
|
1814
1830
|
if ((codexBin && fs.existsSync(codexBin)) || tryLookup(yoke.getVendorInfo("codex").binaryName)) result.push("codex");
|
|
1815
1831
|
|
|
1816
|
-
var subprocessVendorKeys = ["antigravity", "opencode"];
|
|
1832
|
+
var subprocessVendorKeys = ["antigravity", "opencode", "kimi", "grok", "copilot", "qwen", "junie"];
|
|
1817
1833
|
var acpProfiles = require("./yoke/acp-agent-profiles");
|
|
1818
1834
|
for (var subprocessVendorIndex = 0; subprocessVendorIndex < subprocessVendorKeys.length; subprocessVendorIndex++) {
|
|
1819
1835
|
var subprocessVendor = subprocessVendorKeys[subprocessVendorIndex];
|
|
@@ -1864,7 +1880,7 @@ function createSDKBridge(opts) {
|
|
|
1864
1880
|
// as if every CLI were missing while the default adapter starts up.
|
|
1865
1881
|
sm.installedVendors = detectInstalledVendors(linuxUser);
|
|
1866
1882
|
sm.availableVendors = getAvailableVendors(linuxUser);
|
|
1867
|
-
sendModelInfoForVendor(defaultVendor,
|
|
1883
|
+
sendModelInfoForVendor(null, defaultVendor, defaultModelForVendor(defaultVendor));
|
|
1868
1884
|
|
|
1869
1885
|
// Initialize default adapter first (provides skills, slash commands, etc.)
|
|
1870
1886
|
if (adapter) {
|
|
@@ -1896,7 +1912,8 @@ function createSDKBridge(opts) {
|
|
|
1896
1912
|
send({ type: "slash_commands", commands: combined, vendor: defaultVendor });
|
|
1897
1913
|
}
|
|
1898
1914
|
if (result.defaultModel) {
|
|
1899
|
-
sm.
|
|
1915
|
+
sm.defaultModelByVendor = sm.defaultModelByVendor || {};
|
|
1916
|
+
sm.defaultModelByVendor[defaultVendor] = sm.defaultModelByVendor[defaultVendor] || result.defaultModel;
|
|
1900
1917
|
}
|
|
1901
1918
|
sm.availableModels = result.models || [];
|
|
1902
1919
|
// Store per-vendor models and capabilities
|
|
@@ -1918,7 +1935,7 @@ function createSDKBridge(opts) {
|
|
|
1918
1935
|
sm.modelsByVendor = sm.modelsByVendor || {};
|
|
1919
1936
|
|
|
1920
1937
|
// Send the fully initialized state to the client.
|
|
1921
|
-
sendModelInfoForVendor(defaultVendor,
|
|
1938
|
+
sendModelInfoForVendor(null, defaultVendor, defaultModelForVendor(defaultVendor));
|
|
1922
1939
|
}
|
|
1923
1940
|
|
|
1924
1941
|
async function setModel(session, model) {
|
|
@@ -1930,22 +1947,28 @@ function createSDKBridge(opts) {
|
|
|
1930
1947
|
// No active query — just store the model for next startQuery
|
|
1931
1948
|
session.model = model;
|
|
1932
1949
|
sm.saveSessionFile(session);
|
|
1933
|
-
sm.
|
|
1934
|
-
//
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
|
|
1950
|
+
sm.defaultModelByVendor = sm.defaultModelByVendor || {};
|
|
1951
|
+
// Confirm against the session vendor so a non-default vendor does not
|
|
1952
|
+
// receive model metadata labeled as the project's default adapter.
|
|
1953
|
+
var pendingVendor = session.vendor || (adapter && adapter.vendor) || "claude";
|
|
1954
|
+
sm.defaultModelByVendor[pendingVendor] = model;
|
|
1955
|
+
sendModelInfoForVendor(session, pendingVendor, model);
|
|
1956
|
+
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 || [] });
|
|
1957
|
+
return { ok: true, model: model };
|
|
1938
1958
|
}
|
|
1939
1959
|
try {
|
|
1940
1960
|
await session.queryInstance.setModel(model);
|
|
1941
1961
|
session.model = model;
|
|
1942
1962
|
sm.saveSessionFile(session);
|
|
1943
|
-
sm.
|
|
1963
|
+
sm.defaultModelByVendor = sm.defaultModelByVendor || {};
|
|
1944
1964
|
var sessionVendor = session.vendor || (adapter && adapter.vendor) || "claude";
|
|
1945
|
-
|
|
1946
|
-
|
|
1965
|
+
sm.defaultModelByVendor[sessionVendor] = model;
|
|
1966
|
+
sendModelInfoForVendor(session, sessionVendor, model);
|
|
1967
|
+
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 || [] });
|
|
1968
|
+
return { ok: true, model: model };
|
|
1947
1969
|
} catch (e) {
|
|
1948
1970
|
send({ type: "error", text: "Failed to switch model: " + (e.message || e) });
|
|
1971
|
+
return { ok: false, model: session.model || "", error: e.message || String(e) };
|
|
1949
1972
|
}
|
|
1950
1973
|
}
|
|
1951
1974
|
|
|
@@ -1974,14 +1997,14 @@ function createSDKBridge(opts) {
|
|
|
1974
1997
|
if (!session.queryInstance) {
|
|
1975
1998
|
// No active query — just store the mode for next startQuery
|
|
1976
1999
|
sm.currentPermissionMode = mode;
|
|
1977
|
-
sendToSession(session, { type: "config_state", model: session.model ||
|
|
2000
|
+
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 || [] });
|
|
1978
2001
|
return;
|
|
1979
2002
|
}
|
|
1980
2003
|
try {
|
|
1981
2004
|
// Route through QueryHandle (works for both in-process and worker paths)
|
|
1982
2005
|
await session.queryInstance.setPermissionMode(mode);
|
|
1983
2006
|
sm.currentPermissionMode = mode;
|
|
1984
|
-
sendToSession(session, { type: "config_state", model: session.model ||
|
|
2007
|
+
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 || [] });
|
|
1985
2008
|
} catch (e) {
|
|
1986
2009
|
sendToSession(session, { type: "error", text: "Failed to set permission mode: " + (e.message || e) });
|
|
1987
2010
|
}
|
|
@@ -175,6 +175,10 @@ function attachMessageProcessor(ctx) {
|
|
|
175
175
|
|
|
176
176
|
// Cache slash_commands and model from CLI init message
|
|
177
177
|
if (parsed.yokeType === "init") {
|
|
178
|
+
var previousBackgroundTaskCount = (session.activeBackgroundTasks || []).length;
|
|
179
|
+
session.activeBackgroundTasks = [];
|
|
180
|
+
sendToSession(session, { type: "active_background_tasks", tasks: [] });
|
|
181
|
+
if (previousBackgroundTaskCount !== 0) sm.broadcastSessionList();
|
|
178
182
|
var fsSkills = discoverSkillDirs();
|
|
179
183
|
sm.skillNames = mergeSkills(parsed.skills, fsSkills);
|
|
180
184
|
if (parsed.slashCommands) {
|
|
@@ -192,13 +196,20 @@ function attachMessageProcessor(ctx) {
|
|
|
192
196
|
send({ type: "slash_commands", commands: sm.slashCommands });
|
|
193
197
|
}
|
|
194
198
|
if (parsed.model) {
|
|
195
|
-
sm.currentModel = sm.currentModel || sm._savedDefaultModel || parsed.model;
|
|
196
199
|
var initVendor = session.vendor || (adapter && adapter.vendor) || "claude";
|
|
197
|
-
|
|
200
|
+
sm.defaultModelByVendor = sm.defaultModelByVendor || {};
|
|
201
|
+
sm.defaultModelByVendor[initVendor] = sm.defaultModelByVendor[initVendor] || parsed.model;
|
|
202
|
+
var initModels = getModelsForVendor(initVendor);
|
|
203
|
+
var initModel = session.model || sm.defaultModelByVendor[initVendor] || "";
|
|
204
|
+
if (initModels.length > 0 && !initModels.some(function(entry) {
|
|
205
|
+
return entry === initModel || (entry && (entry.value === initModel || entry.id === initModel || entry.resolvedModel === initModel));
|
|
206
|
+
})) initModel = typeof initModels[0] === "string" ? initModels[0] : (initModels[0].value || initModels[0].id || "");
|
|
207
|
+
sendToSession(session, {
|
|
198
208
|
type: "model_info",
|
|
199
|
-
model:
|
|
200
|
-
models:
|
|
209
|
+
model: initModel,
|
|
210
|
+
models: initModels,
|
|
201
211
|
vendor: initVendor,
|
|
212
|
+
sessionId: session.localId,
|
|
202
213
|
availableVendors: sm.availableVendors || [],
|
|
203
214
|
installedVendors: sm.installedVendors || [],
|
|
204
215
|
});
|
|
@@ -612,6 +623,15 @@ function attachMessageProcessor(ctx) {
|
|
|
612
623
|
});
|
|
613
624
|
}
|
|
614
625
|
|
|
626
|
+
} else if (parsed.yokeType === "background_tasks_changed") {
|
|
627
|
+
var previousBackgroundTaskCount = (session.activeBackgroundTasks || []).length;
|
|
628
|
+
var activeBackgroundTasks = Array.isArray(parsed.tasks) ? parsed.tasks : [];
|
|
629
|
+
session.activeBackgroundTasks = activeBackgroundTasks;
|
|
630
|
+
sendToSession(session, { type: "active_background_tasks", tasks: activeBackgroundTasks });
|
|
631
|
+
if (previousBackgroundTaskCount !== activeBackgroundTasks.length) {
|
|
632
|
+
sm.broadcastSessionList();
|
|
633
|
+
}
|
|
634
|
+
|
|
615
635
|
} else if (parsed.yokeType === "tool_progress") {
|
|
616
636
|
// Sub-agent tool_progress: forward as activity update
|
|
617
637
|
var parentId = parsed.parentToolId;
|
|
@@ -783,6 +803,8 @@ function attachMessageProcessor(ctx) {
|
|
|
783
803
|
// app-server returned an unauthorized/token-revoked error). Trigger the
|
|
784
804
|
// same login flow as the Claude login-prompt path.
|
|
785
805
|
session.isProcessing = false;
|
|
806
|
+
session._awaitingTurnResult = false;
|
|
807
|
+
session._queuedTurnCount = 0;
|
|
786
808
|
onProcessingChanged();
|
|
787
809
|
emitAuthRequired(session);
|
|
788
810
|
|