clay-server 5.2.0-beta.4 → 5.2.0-beta.6
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/account-auth-socket.js +1 -0
- package/lib/builtin-mates.js +17 -14
- package/lib/canonical-clay-record-authority.js +15 -0
- package/lib/canonical-clay-scheduling-authority.js +19 -0
- package/lib/claude-hook-installer.js +1 -239
- package/lib/claude-jsonl-watcher.js +1 -204
- package/lib/clay-mate-chat-receipts.js +26 -0
- package/lib/clay-mate-chat.js +87 -0
- package/lib/clay-mate-knowledge-input.js +26 -0
- package/lib/clay-mate-knowledge.js +95 -0
- package/lib/clay-mate-lifecycle.js +95 -0
- package/lib/clay-product-control-input.js +33 -0
- package/lib/clay-product-control-tools.js +51 -0
- package/lib/clay-product-control.js +237 -0
- package/lib/clay-project-lifecycle.js +154 -0
- package/lib/clay-project-record-input.js +44 -0
- package/lib/clay-project-records.js +83 -0
- package/lib/clay-scheduling-input.js +21 -0
- package/lib/clay-scheduling-receipts.js +26 -0
- package/lib/clay-scheduling.js +119 -0
- package/lib/clay-screen-controls.js +96 -0
- package/lib/clay-skill-install.js +1 -33
- package/lib/clay-workspace-controls.js +83 -0
- package/lib/clay-workspace-input.js +17 -0
- package/lib/clay-workspace-receipts.js +26 -0
- package/lib/cli-sessions.js +1 -300
- package/lib/codex-defaults.js +1 -28
- package/lib/context-usage.js +3 -2
- package/lib/daemon-project-access.js +11 -0
- package/lib/daemon-project-lifecycle-callbacks.js +15 -0
- package/lib/daemon-project-lifecycle.js +262 -0
- package/lib/daemon-projects.js +1 -1
- package/lib/daemon.js +78 -276
- package/lib/default-ai-runtime.js +4 -8
- package/lib/email-mcp-server.js +1 -1
- package/lib/issues-service.js +29 -15
- package/lib/kiro-defaults.js +1 -22
- package/lib/knowledge-db-import.js +5 -4
- package/lib/knowledge-db-service.js +9 -6
- package/lib/knowledge-document-index.js +2 -1
- package/lib/knowledge-product-operations.js +46 -0
- package/lib/mate-creation-mcp-server.js +2 -0
- package/lib/mate-knowledge-canonical-access.js +10 -0
- package/lib/mate-knowledge-service.js +7 -4
- package/lib/mate-lifecycle-service.js +94 -0
- package/lib/mate-message-delivery.js +43 -0
- package/lib/mate-ready-creation.js +9 -1
- package/lib/mate-schedule-input.js +29 -0
- package/lib/mate-schedule-lifecycle.js +151 -0
- package/lib/mate-scheduling.js +103 -0
- package/lib/mates.js +4 -3
- package/lib/mcp-proxy.js +2 -32
- package/lib/mcp-query-bridge.js +1 -1
- package/lib/mcp-source-adapters.js +1 -91
- package/lib/mcp-source-context.js +1 -42
- package/lib/mcp-source-discovery.js +1 -169
- package/lib/mcp-source-plugins.js +1 -68
- package/lib/mcp-source-restrictions.js +1 -47
- package/lib/mcp-tool-policy.js +1 -26
- package/lib/notes-checklist.js +50 -0
- package/lib/notes.js +49 -3
- package/lib/os-user-diagnostics.js +2 -4
- package/lib/os-users.js +24 -70
- package/lib/project-admin-overview.js +51 -0
- package/lib/project-ask-user.js +4 -2
- package/lib/project-connection.js +8 -7
- package/lib/project-delegated-follow-up.js +2 -1
- package/lib/project-directory-claim.js +65 -0
- package/lib/project-file-operations.js +61 -0
- package/lib/project-filesystem.js +13 -87
- package/lib/project-full-access.js +23 -2
- package/lib/project-http.js +4 -4
- package/lib/project-knowledge.js +32 -18
- package/lib/project-lifecycle-operations.js +98 -0
- package/lib/project-lifecycle-process-witness.js +32 -0
- package/lib/project-lifecycle-runtime.js +50 -0
- package/lib/project-linux-access.js +22 -0
- package/lib/project-logs-canonical-clay.js +27 -0
- package/lib/project-logs-clay-query.js +16 -0
- package/lib/project-logs-schema.js +4 -2
- package/lib/project-logs-service.js +8 -7
- package/lib/project-logs.js +1 -0
- package/lib/project-loop.js +1 -0
- package/lib/project-mate-chat-control.js +55 -0
- package/lib/project-mate-creation-proposal.js +18 -3
- package/lib/project-mcp.js +2 -2
- package/lib/project-models.js +14 -31
- package/lib/project-notifications.js +74 -16
- package/lib/project-ownership.js +77 -0
- package/lib/project-pair-result-delivery.js +14 -3
- package/lib/project-pair-usage.js +12 -13
- package/lib/project-pending-message-queue.js +4 -1
- package/lib/project-product-control.js +87 -0
- package/lib/project-product-session-queue.js +46 -0
- package/lib/project-push-access.js +28 -0
- package/lib/project-scheduled-message-queue.js +30 -0
- package/lib/project-scheduled-messages.js +63 -64
- package/lib/project-scheduled-task-control.js +83 -0
- package/lib/project-scheduled-tasks.js +13 -2
- package/lib/project-scheduling-access.js +10 -0
- package/lib/project-session-actions.js +65 -0
- package/lib/project-session-delete.js +8 -1
- package/lib/project-session-github.js +47 -12
- package/lib/project-session-history-actions.js +167 -0
- package/lib/project-session-pair.js +1 -1
- package/lib/project-session-runtime-actions.js +64 -0
- package/lib/project-session-spawn.js +2 -3
- package/lib/project-session-vendor.js +55 -0
- package/lib/project-sessions.js +82 -267
- package/lib/project-user-message.js +31 -1
- package/lib/project-vendor-login.js +6 -36
- package/lib/project-workspace-operations.js +126 -0
- package/lib/project-workspace-query.js +9 -2
- package/lib/project.js +95 -3
- package/lib/public/app.js +1 -0
- package/lib/public/css/clay-tool-activity.css +51 -0
- package/lib/public/css/home-mate-creation.css +5 -0
- package/lib/public/css/messages.css +6 -2
- package/lib/public/css/overlays.css +4 -2
- package/lib/public/css/project-admin-overview.css +13 -0
- package/lib/public/css/session-folders.css +11 -0
- package/lib/public/css/session-github.css +3 -0
- package/lib/public/css/sticky-notes.css +61 -1
- package/lib/public/index.html +1 -1
- package/lib/public/modules/admin-settings-projects.js +6 -2
- package/lib/public/modules/app-connection.js +8 -1
- package/lib/public/modules/app-favicon.js +11 -2
- package/lib/public/modules/app-header.js +4 -2
- package/lib/public/modules/app-loop-ui.js +2 -0
- package/lib/public/modules/app-message-router.js +1 -1
- package/lib/public/modules/app-messages.js +54 -8
- package/lib/public/modules/app-notifications.js +1 -2
- package/lib/public/modules/app-panels.js +61 -123
- package/lib/public/modules/app-projects.js +5 -0
- package/lib/public/modules/app-rate-limit.js +7 -27
- package/lib/public/modules/chat-render-runtime.js +3 -0
- package/lib/public/modules/clay-owner-navigation.js +27 -0
- package/lib/public/modules/clay-tool-activity.js +140 -0
- package/lib/public/modules/clay-tool-presentation.js +338 -0
- package/lib/public/modules/command-palette.js +1 -1
- package/lib/public/modules/default-ai.js +11 -3
- package/lib/public/modules/home-debate-planning.js +5 -5
- package/lib/public/modules/home-mate-chat.js +8 -6
- package/lib/public/modules/home-mate-creation.js +73 -1
- package/lib/public/modules/mcp-connection-view.js +1 -1
- package/lib/public/modules/permission-control.js +5 -4
- package/lib/public/modules/personal-settings-dialog.js +7 -1
- package/lib/public/modules/project-access-recovery.js +17 -0
- package/lib/public/modules/project-admin-overview.js +21 -0
- package/lib/public/modules/scheduler.js +2 -0
- package/lib/public/modules/session-create-form.js +10 -10
- package/lib/public/modules/session-folder-layout.js +1 -1
- package/lib/public/modules/session-folder-view-menu.js +9 -0
- package/lib/public/modules/session-github.js +65 -8
- package/lib/public/modules/settings-defaults.js +27 -28
- package/lib/public/modules/sidebar-session-folders-ui.js +1 -1
- package/lib/public/modules/sticky-note-markdown.js +72 -6
- package/lib/public/modules/sticky-notes-actions.js +30 -0
- package/lib/public/modules/sticky-notes-browser.js +9 -2
- package/lib/public/modules/sticky-notes-card.js +23 -7
- package/lib/public/modules/sticky-notes-checklist.js +71 -0
- package/lib/public/modules/sticky-notes-editor.js +108 -44
- package/lib/public/modules/sticky-notes-shared.js +50 -2
- package/lib/public/modules/sticky-notes-state.js +66 -0
- package/lib/public/modules/sticky-notes.js +34 -51
- package/lib/public/modules/thinking-lifecycle.js +33 -3
- package/lib/public/modules/thinking-view.js +23 -1
- package/lib/public/modules/tools.js +36 -7
- package/lib/public/modules/tui-grab.js +1 -2
- package/lib/public/modules/user-settings-appearance.js +10 -3
- package/lib/public/modules/user-settings-editors.js +1 -1
- package/lib/public/modules/user-settings-layout.js +11 -2
- package/lib/public/modules/user-settings-preferences.js +6 -2
- package/lib/public/modules/user-settings-profile.js +12 -2
- package/lib/public/modules/user-settings.js +3 -2
- package/lib/public/modules/vendor-login-browser.js +5 -3
- package/lib/public/modules/vendor-login.js +2 -2
- package/lib/public/modules/vendor-priority.js +15 -1
- package/lib/public/muse-avatar.svg +4 -0
- package/lib/public/style.css +2 -0
- package/lib/safe-bash-commands.js +3 -45
- package/lib/scheduled-task-records.js +3 -1
- package/lib/scheduler.js +2 -0
- package/lib/sdk-bridge.js +334 -403
- package/lib/sdk-foreground-admission.js +16 -0
- package/lib/sdk-mcp-descriptors.js +2 -37
- package/lib/sdk-message-processor.js +52 -17
- package/lib/sdk-quota-snapshot.js +1 -1
- package/lib/sdk-response-recovery.js +1 -0
- package/lib/sdk-worker.js +2 -677
- package/lib/server-admin.js +63 -16
- package/lib/server-dm.js +1 -1
- package/lib/server-global-ws.js +10 -20
- package/lib/server-home-chat-events.js +7 -3
- package/lib/server-home-chat.js +4 -16
- package/lib/server-home-mate-creation.js +1 -0
- package/lib/server-home-mcp-consent.js +3 -3
- package/lib/server-mates.js +25 -67
- package/lib/server-owner-ui.js +77 -0
- package/lib/server-path-access.js +38 -0
- package/lib/server-project-removal.js +22 -0
- package/lib/server.js +100 -30
- package/lib/session-config-transition.js +45 -0
- package/lib/session-folders.js +7 -2
- package/lib/session-fork-history.js +21 -0
- package/lib/session-github.js +9 -2
- package/lib/session-hygiene.js +3 -35
- package/lib/session-permission-mode.js +4 -3
- package/lib/session-skill-scope.js +11 -0
- package/lib/session-visibility.js +5 -2
- package/lib/sessions.js +27 -89
- package/lib/terminal-manager.js +52 -0
- package/lib/tool-llm.js +1 -1
- package/lib/tui-transcript-index.js +1 -132
- package/lib/users-permissions.js +2 -8
- package/lib/users.js +4 -0
- package/lib/vendor-login-browser.js +1 -11
- package/lib/workspace-query-access.js +4 -1
- package/lib/ws-schema.js +8 -2
- package/lib/yoke/acp-agent-profiles.js +5 -3
- package/lib/yoke/adapters/antigravity.js +3 -14
- package/lib/yoke/adapters/claude-query-contract.js +9 -0
- package/lib/yoke/adapters/claude.js +54 -50
- package/lib/yoke/adapters/codex-query-contract.js +24 -0
- package/lib/yoke/adapters/codex.js +53 -111
- package/lib/yoke/adapters/kiro.js +3 -1
- package/lib/yoke/adapters/muse-query-contract.js +7 -0
- package/lib/yoke/adapters/muse.js +122 -0
- package/lib/yoke/auth-events.js +17 -0
- package/lib/yoke/claude-hook-installer.js +267 -0
- package/lib/yoke/claude-jsonl-watcher.js +204 -0
- package/lib/yoke/claude-maintenance.js +55 -0
- package/lib/yoke/claude-permission-patterns.js +53 -0
- package/lib/yoke/claude-response-recovery.js +155 -0
- package/lib/yoke/claude-transcript-hygiene.js +34 -0
- package/lib/yoke/claude-worker-startup.js +28 -0
- package/lib/yoke/clay-skill-install.js +33 -0
- package/lib/yoke/cli-sessions.js +300 -0
- package/lib/yoke/codex-app-server.js +7 -12
- package/lib/yoke/codex-idle-recovery.js +81 -1
- package/lib/yoke/codex-recovery-admission.js +164 -0
- package/lib/yoke/configuration.js +13 -0
- package/lib/yoke/discovery.js +70 -0
- package/lib/yoke/errors.js +8 -0
- package/lib/yoke/host-environment.js +7 -0
- package/lib/yoke/index.js +9 -51
- package/lib/yoke/interface.js +116 -0
- package/lib/yoke/kiro-acp-server.js +5 -5
- package/lib/yoke/legacy-sessions.js +89 -0
- package/lib/yoke/login-protocol.js +25 -0
- package/lib/yoke/maintenance.js +12 -0
- package/lib/yoke/mcp-proxy.js +32 -0
- package/lib/yoke/mcp-source-adapters.js +91 -0
- package/lib/yoke/mcp-source-context.js +42 -0
- package/lib/yoke/mcp-source-discovery.js +169 -0
- package/lib/yoke/mcp-source-plugins.js +68 -0
- package/lib/yoke/mcp-source-restrictions.js +47 -0
- package/lib/yoke/mcp-tool-policy-normalize.js +28 -0
- package/lib/yoke/model-preferences.js +6 -0
- package/lib/yoke/muse-artifacts.js +36 -0
- package/lib/yoke/muse-controls.js +50 -0
- package/lib/yoke/muse-input.js +64 -0
- package/lib/yoke/muse-items.js +157 -0
- package/lib/yoke/muse-policy.js +52 -0
- package/lib/yoke/muse-query.js +428 -0
- package/lib/yoke/muse-runtime.js +211 -0
- package/lib/yoke/muse-services.js +79 -0
- package/lib/yoke/muse-skills.js +79 -0
- package/lib/yoke/muse-turns.js +71 -0
- package/lib/yoke/options/acp.js +2 -0
- package/lib/yoke/options/antigravity.js +2 -0
- package/lib/yoke/options/claude.js +21 -0
- package/lib/yoke/options/codex-defaults.js +29 -0
- package/lib/yoke/options/codex-settings.js +21 -0
- package/lib/yoke/options/codex.js +6 -0
- package/lib/yoke/options/kiro-defaults.js +22 -0
- package/lib/yoke/options/kiro.js +5 -0
- package/lib/yoke/options/muse.js +5 -0
- package/lib/yoke/provider-contract.js +5 -0
- package/lib/yoke/providers/antigravity-executable.js +8 -0
- package/lib/yoke/providers/claude-executable.js +40 -0
- package/lib/yoke/providers/claude-model-descriptions.js +14 -0
- package/lib/yoke/providers/claude-process.js +97 -0
- package/lib/yoke/providers/claude-provision.js +75 -0
- package/lib/yoke/providers/codex-errors.js +8 -0
- package/lib/yoke/providers/muse-executable.js +17 -0
- package/lib/yoke/provisioning.js +6 -0
- package/lib/yoke/query-options.js +10 -0
- package/lib/yoke/response-recovery.js +41 -0
- package/lib/yoke/sdk-mcp-descriptors.js +37 -0
- package/lib/yoke/sdk-server-factory.js +7 -0
- package/lib/yoke/session-history.js +8 -0
- package/lib/yoke/session-tool-transport.js +34 -13
- package/lib/yoke/settings.js +23 -0
- package/lib/yoke/source-locations.js +4 -0
- package/lib/yoke/title-query.js +18 -7
- package/lib/yoke/tui-transcript-index.js +132 -0
- package/lib/yoke/usage.js +48 -0
- package/lib/yoke/user-input.js +13 -0
- package/lib/yoke/vendor-registry.js +81 -1
- package/package.json +3 -2
package/lib/sdk-bridge.js
CHANGED
|
@@ -1,12 +1,9 @@
|
|
|
1
|
-
|
|
2
|
-
var codexQuestionGuidance = require("./yoke/user-input-guidance").codexQuestionGuidance;
|
|
1
|
+
var crypto = require("crypto");
|
|
3
2
|
var fs = require("fs");
|
|
4
3
|
var path = require("path");
|
|
5
4
|
var execSync = require("child_process").execSync;
|
|
6
5
|
var execFileSync = require("child_process").execFileSync;
|
|
7
6
|
var usersModule = require("./users");
|
|
8
|
-
var { getCodexConfig } = require("./codex-defaults");
|
|
9
|
-
var { getKiroConfig } = require("./kiro-defaults");
|
|
10
7
|
var { splitShellSegments, attachSkillDiscovery } = require("./sdk-skill-discovery");
|
|
11
8
|
var skillDiscovery = require("./yoke/skill-discovery");
|
|
12
9
|
var { isSafeBashSegment } = require("./safe-bash-commands");
|
|
@@ -19,7 +16,7 @@ var sessionTitlePolicy = require("./session-title-policy");
|
|
|
19
16
|
var sessionTitleGenerator = require("./session-title-generator");
|
|
20
17
|
var loopInterview = require("./project-loop-interview");
|
|
21
18
|
|
|
22
|
-
var extractMcpDescriptors = require("./sdk-mcp-descriptors").extractMcpDescriptors;
|
|
19
|
+
var extractMcpDescriptors = require("./yoke/sdk-mcp-descriptors").extractMcpDescriptors;
|
|
23
20
|
|
|
24
21
|
// Call an MCP tool handler by server name and tool name.
|
|
25
22
|
// Returns a promise that resolves with the tool result.
|
|
@@ -137,7 +134,7 @@ function createSDKBridge(opts) {
|
|
|
137
134
|
}];
|
|
138
135
|
}
|
|
139
136
|
|
|
140
|
-
function
|
|
137
|
+
function getSessionMcpServer(session, queryGeneration) {
|
|
141
138
|
if (!session || !Number.isInteger(queryGeneration)) return null;
|
|
142
139
|
var bridgeArgs = [
|
|
143
140
|
path.join(__dirname, "yoke", "mcp-bridge-server.js"),
|
|
@@ -165,25 +162,16 @@ function createSDKBridge(opts) {
|
|
|
165
162
|
return _cachedFreshAuthState;
|
|
166
163
|
}
|
|
167
164
|
|
|
168
|
-
|
|
169
|
-
if (!errDetail) return false;
|
|
170
|
-
var errLower = String(errDetail).toLowerCase();
|
|
171
|
-
return errLower.indexOf("not logged in") !== -1
|
|
172
|
-
|| errLower.indexOf("unauthenticated") !== -1
|
|
173
|
-
|| errLower.indexOf("authentication") !== -1
|
|
174
|
-
|| errLower.indexOf("sign in") !== -1
|
|
175
|
-
|| errLower.indexOf("log in") !== -1
|
|
176
|
-
|| errLower.indexOf("please login") !== -1;
|
|
177
|
-
}
|
|
165
|
+
var isAuthErrorMessage = require("./yoke/auth-events").isAuthenticationError;
|
|
178
166
|
|
|
179
167
|
function getLoginCommand(vendor) {
|
|
180
168
|
var info = yoke.getVendorInfo(vendor);
|
|
181
|
-
return (info && info.loginCommand) || "
|
|
169
|
+
return (info && info.loginCommand) || "";
|
|
182
170
|
}
|
|
183
171
|
|
|
184
172
|
function supportsOsUserIsolation(vendor) {
|
|
185
173
|
var info = yoke.getVendorInfo(vendor);
|
|
186
|
-
return
|
|
174
|
+
return !!info && info.osUserIsolation === true;
|
|
187
175
|
}
|
|
188
176
|
|
|
189
177
|
function notifyAuthRequired(session, title, body, authLinuxUser, canAutoLogin, loginCommand) {
|
|
@@ -323,6 +311,7 @@ function createSDKBridge(opts) {
|
|
|
323
311
|
}
|
|
324
312
|
|
|
325
313
|
function stopIdleReaper() {
|
|
314
|
+
responseRecovery.dispose();
|
|
326
315
|
if (_idleReaperTimer) {
|
|
327
316
|
clearInterval(_idleReaperTimer);
|
|
328
317
|
_idleReaperTimer = null;
|
|
@@ -389,6 +378,18 @@ function createSDKBridge(opts) {
|
|
|
389
378
|
var sendAndRecord = msgProcessor.sendAndRecord;
|
|
390
379
|
var sendToSession = msgProcessor.sendToSession;
|
|
391
380
|
|
|
381
|
+
var responseRecovery = require("./yoke/response-recovery").attachResponseRecovery(Object.assign({
|
|
382
|
+
getProvider: function(session) { var vendor = session.vendor || sm.defaultVendor || adapter && adapter.vendor; return adapters[vendor] || vendor || adapter; },
|
|
383
|
+
sessions: sm.sessions,
|
|
384
|
+
changed: onProcessingChanged,
|
|
385
|
+
record: sendAndRecord,
|
|
386
|
+
send: sendToSession,
|
|
387
|
+
broadcast: function() { if (sm.broadcastSessionList) sm.broadcastSessionList(); },
|
|
388
|
+
restart: function(session, payload, state) {
|
|
389
|
+
return startQuery(session, payload.text, payload.images, payload.linuxUser, payload.beforePush, null, state);
|
|
390
|
+
},
|
|
391
|
+
}, opts.responseRecovery || {}));
|
|
392
|
+
|
|
392
393
|
// --- MCP elicitation ---
|
|
393
394
|
|
|
394
395
|
function handleElicitation(session, request, opts) {
|
|
@@ -417,6 +418,7 @@ function createSDKBridge(opts) {
|
|
|
417
418
|
|
|
418
419
|
if (pushModule) {
|
|
419
420
|
pushModule.sendPush({
|
|
421
|
+
sessionId: session.localId,
|
|
420
422
|
type: "elicitation",
|
|
421
423
|
slug: slug,
|
|
422
424
|
title: (request.serverName || "MCP Server") + " needs input",
|
|
@@ -467,6 +469,7 @@ function createSDKBridge(opts) {
|
|
|
467
469
|
|
|
468
470
|
if (pushModule) {
|
|
469
471
|
pushModule.sendPush({
|
|
472
|
+
sessionId: session.localId,
|
|
470
473
|
type: "user_dialog",
|
|
471
474
|
slug: slug,
|
|
472
475
|
title: "Claude needs a decision",
|
|
@@ -489,44 +492,7 @@ function createSDKBridge(opts) {
|
|
|
489
492
|
// Ensures the linux user's .claude project directory exists and is writable,
|
|
490
493
|
// then pre-copies CLI session file if needed. Called before starting a query
|
|
491
494
|
// so the worker can resume from the correct session file.
|
|
492
|
-
|
|
493
|
-
try {
|
|
494
|
-
var configMod = require("./config");
|
|
495
|
-
var osUsersMod = require("./os-users");
|
|
496
|
-
var originalHome = configMod.REAL_HOME || require("os").homedir();
|
|
497
|
-
var linuxUserHome = osUsersMod.getLinuxUserHome(linuxUser);
|
|
498
|
-
var uid = osUsersMod.getLinuxUserUid(linuxUser);
|
|
499
|
-
if (originalHome !== linuxUserHome && uid != null) {
|
|
500
|
-
var projectSlug = (cwd || "").replace(/\//g, "-");
|
|
501
|
-
var dstDir = path.join(linuxUserHome, ".claude", "projects", projectSlug);
|
|
502
|
-
// Create and chown the project directory once
|
|
503
|
-
if (!fs.existsSync(dstDir)) {
|
|
504
|
-
fs.mkdirSync(dstDir, { recursive: true });
|
|
505
|
-
try { execFileSync("chown", ["-R", String(uid), path.join(linuxUserHome, ".claude")]); } catch (e2) {}
|
|
506
|
-
} else {
|
|
507
|
-
try {
|
|
508
|
-
var dirStat = fs.statSync(dstDir);
|
|
509
|
-
if (dirStat.uid !== uid) {
|
|
510
|
-
execFileSync("chown", [String(uid), dstDir]);
|
|
511
|
-
}
|
|
512
|
-
} catch (e2) {}
|
|
513
|
-
}
|
|
514
|
-
// Pre-copy CLI session file so the worker can resume the conversation
|
|
515
|
-
if (session.cliSessionId) {
|
|
516
|
-
var sessionFileName = session.cliSessionId + ".jsonl";
|
|
517
|
-
var srcFile = path.join(originalHome, ".claude", "projects", projectSlug, sessionFileName);
|
|
518
|
-
var dstFile = path.join(dstDir, sessionFileName);
|
|
519
|
-
if (fs.existsSync(srcFile) && !fs.existsSync(dstFile)) {
|
|
520
|
-
fs.copyFileSync(srcFile, dstFile);
|
|
521
|
-
try { execFileSync("chown", [String(uid), dstFile]); } catch (e2) {}
|
|
522
|
-
console.log("[sdk-bridge] Pre-copied CLI session " + session.cliSessionId + " to " + linuxUser);
|
|
523
|
-
}
|
|
524
|
-
}
|
|
525
|
-
}
|
|
526
|
-
} catch (copyErr) {
|
|
527
|
-
console.log("[sdk-bridge] Dir setup / session pre-copy skipped:", copyErr.message);
|
|
528
|
-
}
|
|
529
|
-
}
|
|
495
|
+
|
|
530
496
|
|
|
531
497
|
// --- SDK query lifecycle ---
|
|
532
498
|
|
|
@@ -745,6 +711,34 @@ function createSDKBridge(opts) {
|
|
|
745
711
|
// Workspace inspection and proposal posting are session-bound. Keep this
|
|
746
712
|
// list exact so a future execution/mutation tool cannot inherit approval.
|
|
747
713
|
var safeWorkspaceTools = {
|
|
714
|
+
"mcp__clay-workspace__read_owner_screen": true,
|
|
715
|
+
"mcp__clay-workspace__inspect_owner_screen_change": true,
|
|
716
|
+
"mcp__clay-workspace__control_owner_screen": true,
|
|
717
|
+
"mcp__clay-workspace__discover_product_controls": true,
|
|
718
|
+
"mcp__clay-workspace__list_owned_mates": true,
|
|
719
|
+
"mcp__clay-workspace__begin_schedule_control_interview": true,
|
|
720
|
+
"mcp__clay-workspace__read_schedule_controls": true,
|
|
721
|
+
"mcp__clay-workspace__inspect_schedule_change": true,
|
|
722
|
+
"mcp__clay-workspace__control_schedules": true,
|
|
723
|
+
"mcp__clay-workspace__read_mate_chat_control": true,
|
|
724
|
+
"mcp__clay-workspace__inspect_mate_chat_change": true,
|
|
725
|
+
"mcp__clay-workspace__control_mate_chat": true,
|
|
726
|
+
"mcp__clay-workspace__read_project_workspace": true,
|
|
727
|
+
"mcp__clay-workspace__read_workspace_receipt": true,
|
|
728
|
+
"mcp__clay-workspace__inspect_workspace_change": true,
|
|
729
|
+
"mcp__clay-workspace__control_project_workspace": true,
|
|
730
|
+
"mcp__clay-workspace__read_project_records": true,
|
|
731
|
+
"mcp__clay-workspace__inspect_project_record_change": true,
|
|
732
|
+
"mcp__clay-workspace__control_project_records": true,
|
|
733
|
+
"mcp__clay-workspace__read_mate_knowledge_control": true,
|
|
734
|
+
"mcp__clay-workspace__inspect_mate_knowledge_change": true,
|
|
735
|
+
"mcp__clay-workspace__control_mate_knowledge": true,
|
|
736
|
+
"mcp__clay-workspace__inspect_mate_control": true,
|
|
737
|
+
"mcp__clay-workspace__control_mate_lifecycle": true,
|
|
738
|
+
"mcp__clay-workspace__inspect_project_control": true,
|
|
739
|
+
"mcp__clay-workspace__control_project_lifecycle": true,
|
|
740
|
+
"mcp__clay-workspace__inspect_session_control": true,
|
|
741
|
+
"mcp__clay-workspace__control_project_session": true,
|
|
748
742
|
"mcp__clay-workspace__list_projects": true,
|
|
749
743
|
"mcp__clay-workspace__list_project_sessions": true,
|
|
750
744
|
"mcp__clay-workspace__search_project_history": true,
|
|
@@ -902,6 +896,7 @@ function createSDKBridge(opts) {
|
|
|
902
896
|
|
|
903
897
|
if (pushModule) {
|
|
904
898
|
pushModule.sendPush({
|
|
899
|
+
sessionId: session.localId,
|
|
905
900
|
type: "permission_request",
|
|
906
901
|
slug: slug,
|
|
907
902
|
requestId: requestId,
|
|
@@ -943,58 +938,13 @@ function createSDKBridge(opts) {
|
|
|
943
938
|
* Only returns processes whose cwd matches our project directory.
|
|
944
939
|
* Returns an array of { pid, command } for each conflicting process found.
|
|
945
940
|
*/
|
|
946
|
-
function
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
var lines = output.trim().split("\n");
|
|
950
|
-
var candidates = [];
|
|
951
|
-
for (var i = 1; i < lines.length; i++) { // skip header
|
|
952
|
-
var line = lines[i].trim();
|
|
953
|
-
var m = line.match(/^(\d+)\s+(.+)/);
|
|
954
|
-
if (!m) continue;
|
|
955
|
-
var pid = parseInt(m[1], 10);
|
|
956
|
-
var cmd = m[2];
|
|
957
|
-
// Skip our own process
|
|
958
|
-
if (pid === process.pid) continue;
|
|
959
|
-
// Skip node processes (our daemon, dev watchers, etc.)
|
|
960
|
-
if (/\bnode\b/.test(cmd.split(/\s/)[0])) continue;
|
|
961
|
-
// Match actual claude binary (e.g. /Users/x/.claude/local/claude, /usr/local/bin/claude)
|
|
962
|
-
if (/\/claude(\s|$)/.test(cmd) || /^claude(\s|$)/.test(cmd)) {
|
|
963
|
-
candidates.push({ pid: pid, command: cmd.substring(0, 200) });
|
|
964
|
-
}
|
|
965
|
-
}
|
|
966
|
-
|
|
967
|
-
// Filter to only processes whose cwd matches our project
|
|
968
|
-
var results = [];
|
|
969
|
-
for (var j = 0; j < candidates.length; j++) {
|
|
970
|
-
var c = candidates[j];
|
|
971
|
-
try {
|
|
972
|
-
// Use /proc/<pid>/cwd symlink (always available on Linux, no lsof dependency)
|
|
973
|
-
var procCwd = fs.readlinkSync("/proc/" + c.pid + "/cwd");
|
|
974
|
-
if (procCwd === cwd) {
|
|
975
|
-
results.push(c);
|
|
976
|
-
}
|
|
977
|
-
} catch (e) {
|
|
978
|
-
// /proc read failed — include as candidate anyway (conservative)
|
|
979
|
-
results.push(c);
|
|
980
|
-
}
|
|
981
|
-
}
|
|
982
|
-
return results;
|
|
983
|
-
} catch (e) {
|
|
984
|
-
return [];
|
|
985
|
-
}
|
|
941
|
+
function findConflictingProcesses(session, error) {
|
|
942
|
+
var service = require("./yoke/provider-contract").resolve(getAdapterForSession(session)).processControl;
|
|
943
|
+
return service && typeof service.shouldInspect === "function" && service.shouldInspect(error) && typeof service.findConflicts === "function" ? service.findConflicts(cwd) : [];
|
|
986
944
|
}
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
*/
|
|
991
|
-
function isClaudeProcess(pid) {
|
|
992
|
-
try {
|
|
993
|
-
var output = execFileSync("ps", ["-p", String(pid), "-o", "command="], { encoding: "utf8", timeout: 3000, stdio: ["pipe", "pipe", "pipe"] }).trim();
|
|
994
|
-
return /\/claude(\s|$)/.test(output) || /^claude(\s|$)/.test(output);
|
|
995
|
-
} catch (e) {
|
|
996
|
-
return false;
|
|
997
|
-
}
|
|
945
|
+
function isProviderProcess(pid, session) {
|
|
946
|
+
var service = require("./yoke/provider-contract").resolve(session ? getAdapterForSession(session) : adapter).processControl;
|
|
947
|
+
return !!(service && typeof service.verifyProcess === "function" && service.verifyProcess(pid));
|
|
998
948
|
}
|
|
999
949
|
|
|
1000
950
|
function ownsQueryRuntime(session, queryInstance, queryGeneration) {
|
|
@@ -1006,6 +956,7 @@ function createSDKBridge(opts) {
|
|
|
1006
956
|
function recordRuntimeEffectivePermissionMode(session, queryInstance, queryGeneration, message) {
|
|
1007
957
|
if (!message || (message.yokeType !== "init" && message.yokeType !== "status") || !message.permissionMode) return false;
|
|
1008
958
|
if (!ownsQueryRuntime(session, queryInstance, queryGeneration)) return false;
|
|
959
|
+
session._permissionObservationRevision = Number(session._permissionObservationRevision || 0) + 1;
|
|
1009
960
|
session.effectivePermissionMode = message.permissionMode;
|
|
1010
961
|
session.permissionModeFallbackReason = null;
|
|
1011
962
|
sm.saveSessionFile(session);
|
|
@@ -1036,7 +987,8 @@ function createSDKBridge(opts) {
|
|
|
1036
987
|
return true;
|
|
1037
988
|
}
|
|
1038
989
|
|
|
1039
|
-
async function processQueryStream(session,
|
|
990
|
+
async function processQueryStream(session, catalogCandidate, queryContract) {
|
|
991
|
+
queryContract = queryContract || yoke.sessionTools.contract(getAdapterForSession(session));
|
|
1040
992
|
// Capture references at start so we only clean up OUR resources in finally,
|
|
1041
993
|
// not resources from a newer query that may have been created after an abort.
|
|
1042
994
|
var myQueryInstance = session.queryInstance;
|
|
@@ -1047,7 +999,7 @@ function createSDKBridge(opts) {
|
|
|
1047
999
|
try {
|
|
1048
1000
|
for await (var msg of myQueryInstance) {
|
|
1049
1001
|
// An old iterator may drain after Stop or after a replacement query starts.
|
|
1050
|
-
if (!ownsQueryRuntime(session, myQueryInstance, myQueryGeneration)) continue;
|
|
1002
|
+
if (!ownsQueryRuntime(session, myQueryInstance, myQueryGeneration) || responseRecovery.stopping(session)) continue;
|
|
1051
1003
|
if ((session.taskStopRequested || (myAbortController && myAbortController.signal.aborted))
|
|
1052
1004
|
&& msg && msg.yokeType !== "error" && msg.yokeType !== "interrupted") continue;
|
|
1053
1005
|
if (msg && msg.yokeType !== "text_delta" && msg.yokeType !== "thinking_delta" && msg.yokeType !== "tool_input_delta") {
|
|
@@ -1091,6 +1043,7 @@ function createSDKBridge(opts) {
|
|
|
1091
1043
|
if (continuationIdentity && !ownsQueryRuntime(session, myQueryInstance, myQueryGeneration)) {
|
|
1092
1044
|
continue;
|
|
1093
1045
|
}
|
|
1046
|
+
responseRecovery.event(session, msg);
|
|
1094
1047
|
processSDKMessage(session, msg);
|
|
1095
1048
|
if (continuationIdentity) {
|
|
1096
1049
|
settleContinuationStartup(session, myQueryInstance, myQueryGeneration, true, null);
|
|
@@ -1101,26 +1054,21 @@ function createSDKBridge(opts) {
|
|
|
1101
1054
|
// permission state. Setter acknowledgement deliberately does not
|
|
1102
1055
|
// change this field.
|
|
1103
1056
|
recordRuntimeEffectivePermissionMode(session, myQueryInstance, myQueryGeneration, msg);
|
|
1104
|
-
if (
|
|
1057
|
+
if (catalogCandidate && msg && msg.yokeType === "session_started" && msg.sessionId
|
|
1105
1058
|
&& session.queryInstance === myQueryInstance
|
|
1106
|
-
&& Number(session._sdkQueryGeneration || 0) ===
|
|
1059
|
+
&& Number(session._sdkQueryGeneration || 0) === catalogCandidate.generation
|
|
1107
1060
|
&& session.cliSessionId === msg.sessionId
|
|
1108
1061
|
&& (!sm.sessions || typeof sm.sessions.get !== "function" || sm.sessions.get(session.localId) === session)) {
|
|
1109
|
-
|
|
1110
|
-
if (codexCatalogCandidate.userInputVersion) session.codexUserInputToolCatalogVersion = codexCatalogCandidate.userInputVersion;
|
|
1111
|
-
if (codexCatalogCandidate.sharedBrowserVersion) session.codexSharedBrowserToolCatalogVersion = codexCatalogCandidate.sharedBrowserVersion;
|
|
1112
|
-
if (codexCatalogCandidate.issuesVersion) session.codexIssuesToolCatalogVersion = codexCatalogCandidate.issuesVersion;
|
|
1113
|
-
if (codexCatalogCandidate.youVersion) session.codexYouToolCatalogVersion = codexCatalogCandidate.youVersion;
|
|
1114
|
-
if (codexCatalogCandidate.linearVersion) session.codexLinearToolCatalogVersion = codexCatalogCandidate.linearVersion;
|
|
1062
|
+
queryContract.recordCatalog(session, catalogCandidate.versions);
|
|
1115
1063
|
sm.saveSessionFile(session);
|
|
1116
|
-
|
|
1064
|
+
catalogCandidate = null;
|
|
1117
1065
|
}
|
|
1118
1066
|
}
|
|
1119
1067
|
// (getContextUsage moved to processSDKMessage result handler -- fire-and-forget)
|
|
1120
1068
|
// Stream ended normally after a task stop — no "result" message was sent,
|
|
1121
1069
|
// so the session is still marked as processing. Send interrupted feedback.
|
|
1122
1070
|
console.log("[sdk-bridge] processQueryStream ended: isProcessing=" + session.isProcessing + " taskStopRequested=" + session.taskStopRequested);
|
|
1123
|
-
var stillOwnsRuntime = session.queryInstance === myQueryInstance;
|
|
1071
|
+
var stillOwnsRuntime = session.queryInstance === myQueryInstance && !responseRecovery.stopping(session);
|
|
1124
1072
|
settleContinuationStartup(session, myQueryInstance, myQueryGeneration, false, "The successor query ended before initialization.");
|
|
1125
1073
|
if (session.isProcessing && session.taskStopRequested && stillOwnsRuntime) {
|
|
1126
1074
|
session._lastTurnInterrupted = true;
|
|
@@ -1131,9 +1079,7 @@ function createSDKBridge(opts) {
|
|
|
1131
1079
|
onProcessingChanged();
|
|
1132
1080
|
send({ type: "status", processing: false });
|
|
1133
1081
|
sendAndRecord(session, { type: "thinking_stop" });
|
|
1134
|
-
var interruptMsg = (
|
|
1135
|
-
? "\u25a0 Conversation interrupted - tell the model what to do differently."
|
|
1136
|
-
: "Interrupted \u00b7 What should Claude do instead?";
|
|
1082
|
+
var interruptMsg = (queryContract && queryContract.interruptionMessage) || "■ Conversation interrupted - tell the model what to do differently.";
|
|
1137
1083
|
sendAndRecord(session, { type: "info", text: interruptMsg });
|
|
1138
1084
|
sendAndRecord(session, { type: "done", code: 0 });
|
|
1139
1085
|
sm.broadcastSessionList();
|
|
@@ -1158,7 +1104,7 @@ function createSDKBridge(opts) {
|
|
|
1158
1104
|
}
|
|
1159
1105
|
} catch (err) {
|
|
1160
1106
|
settleContinuationStartup(session, myQueryInstance, myQueryGeneration, false, err.message || "The successor query failed before initialization.");
|
|
1161
|
-
if (session.isProcessing && session.queryInstance === myQueryInstance) {
|
|
1107
|
+
if (session.isProcessing && session.queryInstance === myQueryInstance && !responseRecovery.stopping(session)) {
|
|
1162
1108
|
session.isProcessing = false;
|
|
1163
1109
|
session._awaitingTurnResult = false;
|
|
1164
1110
|
session._queuedTurnCount = 0;
|
|
@@ -1168,9 +1114,7 @@ function createSDKBridge(opts) {
|
|
|
1168
1114
|
session._lastTurnInterrupted = true;
|
|
1169
1115
|
if (!session.destroying) {
|
|
1170
1116
|
sendAndRecord(session, { type: "thinking_stop" });
|
|
1171
|
-
var interruptMsg2 = (
|
|
1172
|
-
? "\u25a0 Conversation interrupted - tell the model what to do differently."
|
|
1173
|
-
: "Interrupted \u00b7 What should Claude do instead?";
|
|
1117
|
+
var interruptMsg2 = (queryContract && queryContract.interruptionMessage) || "■ Conversation interrupted - tell the model what to do differently.";
|
|
1174
1118
|
sendAndRecord(session, { type: "info", text: interruptMsg2 });
|
|
1175
1119
|
sendAndRecord(session, { type: "done", code: 0 });
|
|
1176
1120
|
}
|
|
@@ -1186,21 +1130,17 @@ function createSDKBridge(opts) {
|
|
|
1186
1130
|
console.error("[sdk-bridge] Query stream error for session " + session.localId + ":", errDetail);
|
|
1187
1131
|
console.error("[sdk-bridge] Stack:", err.stack || "(no stack)");
|
|
1188
1132
|
|
|
1189
|
-
|
|
1190
|
-
var isExitCode1 = err.exitCode === 1 || (err.message && err.message.indexOf("exited with code 1") !== -1);
|
|
1191
|
-
var conflicts = isExitCode1 ? findConflictingClaude() : [];
|
|
1133
|
+
var conflicts = findConflictingProcesses(session, err);
|
|
1192
1134
|
if (conflicts.length > 0) {
|
|
1193
|
-
console.error("[sdk-bridge] Found " + conflicts.length + " conflicting
|
|
1135
|
+
console.error("[sdk-bridge] Found " + conflicts.length + " conflicting provider process(es):", conflicts.map(function(c) { return "PID " + c.pid; }).join(", "));
|
|
1194
1136
|
sendAndRecord(session, {
|
|
1195
1137
|
type: "process_conflict",
|
|
1196
|
-
text: "Another
|
|
1138
|
+
text: "Another provider process is already running in this project.",
|
|
1197
1139
|
processes: conflicts,
|
|
1198
1140
|
});
|
|
1199
1141
|
} else {
|
|
1200
1142
|
var errLower = errDetail.toLowerCase();
|
|
1201
|
-
var isContextOverflow =
|
|
1202
|
-
|| errLower.indexOf("context_length") !== -1
|
|
1203
|
-
|| errLower.indexOf("maximum context length") !== -1;
|
|
1143
|
+
var isContextOverflow = require("./yoke/errors").contextOverflow(errDetail);
|
|
1204
1144
|
var isAuthError = isAuthErrorMessage(errDetail);
|
|
1205
1145
|
if (isContextOverflow) {
|
|
1206
1146
|
sendAndRecord(session, {
|
|
@@ -1247,7 +1187,7 @@ function createSDKBridge(opts) {
|
|
|
1247
1187
|
// chat message already sent above
|
|
1248
1188
|
}
|
|
1249
1189
|
} else {
|
|
1250
|
-
sendAndRecord(session, { type: "error", text: "
|
|
1190
|
+
sendAndRecord(session, { type: "error", text: "Provider process error: " + err.message });
|
|
1251
1191
|
}
|
|
1252
1192
|
}
|
|
1253
1193
|
sendAndRecord(session, { type: "done", code: 1 });
|
|
@@ -1272,7 +1212,8 @@ function createSDKBridge(opts) {
|
|
|
1272
1212
|
}
|
|
1273
1213
|
if (session.messageQueue === myMessageQueue) session.messageQueue = null;
|
|
1274
1214
|
if (session.abortController === myAbortController) session.abortController = null;
|
|
1275
|
-
if (ownsRuntime) {
|
|
1215
|
+
if (ownsRuntime && !responseRecovery.stopping(session)) {
|
|
1216
|
+
responseRecovery.cancel(session);
|
|
1276
1217
|
session.taskStopRequested = false;
|
|
1277
1218
|
session.pendingPermissions = {};
|
|
1278
1219
|
// Preserve only restored legacy MCP entries. Canonical Yoke requests
|
|
@@ -1321,7 +1262,7 @@ function createSDKBridge(opts) {
|
|
|
1321
1262
|
}
|
|
1322
1263
|
}
|
|
1323
1264
|
}
|
|
1324
|
-
if (!session.isProcessing && typeof opts.onSessionAvailable === "function") opts.onSessionAvailable(session);
|
|
1265
|
+
if (!responseRecovery.stopping(session) && !session.isProcessing && typeof opts.onSessionAvailable === "function") opts.onSessionAvailable(session);
|
|
1325
1266
|
}
|
|
1326
1267
|
}
|
|
1327
1268
|
|
|
@@ -1329,20 +1270,16 @@ function createSDKBridge(opts) {
|
|
|
1329
1270
|
if (session.queryInstance) return { query: session.queryInstance, isTemp: false, cleanup: function() {} };
|
|
1330
1271
|
|
|
1331
1272
|
var handle;
|
|
1273
|
+
var selectedAdapter = getAdapterForSession(session);
|
|
1332
1274
|
try {
|
|
1333
|
-
handle = await
|
|
1275
|
+
handle = await selectedAdapter.createQuery({
|
|
1334
1276
|
cwd: cwd,
|
|
1335
1277
|
resumeSessionId: session.cliSessionId,
|
|
1336
1278
|
env: getRuntimeEnv(session),
|
|
1337
|
-
adapterOptions: {
|
|
1338
|
-
CLAUDE: {
|
|
1339
|
-
settingSources: ["user", "project", "local"],
|
|
1340
|
-
enableFileCheckpointing: true,
|
|
1341
|
-
},
|
|
1342
|
-
},
|
|
1279
|
+
adapterOptions: require("./yoke/query-options").map(getAdapterForSession(session), { nativePermissionMode: "default" }),
|
|
1343
1280
|
});
|
|
1344
1281
|
} catch (e) {
|
|
1345
|
-
sendAndRecord(session, { type: "error", text: "Failed to load
|
|
1282
|
+
sendAndRecord(session, { type: "error", text: "Failed to load provider session: " + (e.message || e) });
|
|
1346
1283
|
throw e;
|
|
1347
1284
|
}
|
|
1348
1285
|
|
|
@@ -1409,7 +1346,7 @@ function createSDKBridge(opts) {
|
|
|
1409
1346
|
// abort the whole rewind instead of leaving local history trimmed
|
|
1410
1347
|
// ahead of the real, un-rolled-back provider thread.
|
|
1411
1348
|
if (!turnId) {
|
|
1412
|
-
throw new Error("No turn id recorded for this turn; cannot roll back the
|
|
1349
|
+
throw new Error("No turn id recorded for this turn; cannot roll back the provider thread.");
|
|
1413
1350
|
}
|
|
1414
1351
|
await sessionAdapter.rollbackThread(session.cliSessionId, turnId, {
|
|
1415
1352
|
linuxUser: getSessionLinuxUser(session) || undefined,
|
|
@@ -1425,6 +1362,8 @@ function createSDKBridge(opts) {
|
|
|
1425
1362
|
|
|
1426
1363
|
async function forkSessionUnified(session, uuid) {
|
|
1427
1364
|
var sessionAdapter = getAdapterForSession(session);
|
|
1365
|
+
var localHistoryEndIndex = require("./session-fork-history").endIndex(session, uuid,
|
|
1366
|
+
require("./yoke/provider-contract").resolve(sessionAdapter).forkHistoryBoundary);
|
|
1428
1367
|
var result = await sessionAdapter.forkSession(session.cliSessionId, {
|
|
1429
1368
|
upToMessageId: uuid,
|
|
1430
1369
|
dir: cwd,
|
|
@@ -1433,8 +1372,10 @@ function createSDKBridge(opts) {
|
|
|
1433
1372
|
if (!result || !result.sessionId) throw new Error("Fork returned no session id");
|
|
1434
1373
|
|
|
1435
1374
|
// Adapters with rollbackThread (e.g. Codex) use local history copy
|
|
1436
|
-
if (typeof sessionAdapter.rollbackThread === "function") {
|
|
1437
|
-
|
|
1375
|
+
if (result.useLocalHistory === true || typeof sessionAdapter.rollbackThread === "function") {
|
|
1376
|
+
var localFork = { sessionId: result.sessionId, useLocalHistory: true };
|
|
1377
|
+
if (localHistoryEndIndex !== undefined) localFork.localHistoryEndIndex = localHistoryEndIndex;
|
|
1378
|
+
return localFork;
|
|
1438
1379
|
}
|
|
1439
1380
|
// Claude: read history from CLI session files
|
|
1440
1381
|
return { sessionId: result.sessionId, useLocalHistory: false };
|
|
@@ -1442,22 +1383,69 @@ function createSDKBridge(opts) {
|
|
|
1442
1383
|
|
|
1443
1384
|
// Wrapper: marks the boot window so pushMessage can buffer instead of
|
|
1444
1385
|
// dropping messages that arrive while createQuery is still awaiting.
|
|
1445
|
-
async function startQuery(session, text, images, linuxUser, beforePush, onAccepted) {
|
|
1386
|
+
async function startQuery(session, text, images, linuxUser, beforePush, onAccepted, retryState) {
|
|
1387
|
+
if (opts.isLifecycleExecutionPaused && opts.isLifecycleExecutionPaused()) {
|
|
1388
|
+
sendAndRecord(session, { type: "error", text: "Project execution is paused for lifecycle recovery. Reconcile the saved operation, or send a new owner message after restoring the registration." });
|
|
1389
|
+
session.isProcessing = false; onProcessingChanged(); sendAndRecord(session, { type: "done", code: 1 }); return false;
|
|
1390
|
+
}
|
|
1391
|
+
if (session._responseRecoveryStopPending && !retryState) {
|
|
1392
|
+
session.isProcessing = false;
|
|
1393
|
+
onProcessingChanged();
|
|
1394
|
+
sendAndRecord(session, { type: "error", text: "The previous Claude attempt has not confirmed stopped. Check the connection before sending this message again." });
|
|
1395
|
+
sendAndRecord(session, { type: "done", code: 1 });
|
|
1396
|
+
return false;
|
|
1397
|
+
}
|
|
1398
|
+
if (responseRecovery.stopping(session) && !retryState) return false;
|
|
1446
1399
|
delete session._lastTurnInterrupted;
|
|
1400
|
+
var payload = { text: text, images: images, linuxUser: linuxUser, beforePush: beforePush };
|
|
1401
|
+
var recoveryState = responseRecovery.begin(session, payload, retryState);
|
|
1402
|
+
if (recoveryState) recoveryState.handle = null;
|
|
1403
|
+
var guardedPush = function() {
|
|
1404
|
+
return !session.destroying && !session.taskStopRequested
|
|
1405
|
+
&& (!recoveryState || responseRecovery.valid(session, recoveryState) && !recoveryState.stopping)
|
|
1406
|
+
&& (typeof beforePush !== "function" || beforePush() === true);
|
|
1407
|
+
};
|
|
1408
|
+
guardedPush.exactProductDelivery = !!(beforePush && beforePush.exactProductDelivery);
|
|
1447
1409
|
session._queryGeneration = (session._queryGeneration || 0) + 1;
|
|
1448
1410
|
session._queryStarting = true;
|
|
1411
|
+
var starting = startQueryInner(session, text, images, linuxUser, guardedPush, onAccepted, recoveryState);
|
|
1412
|
+
if (recoveryState) recoveryState.starting = starting;
|
|
1449
1413
|
try {
|
|
1450
|
-
|
|
1414
|
+
var accepted = await starting;
|
|
1415
|
+
if (accepted === false && (!recoveryState || !recoveryState.stopping)) {
|
|
1416
|
+
responseRecovery.cancel(session);
|
|
1417
|
+
session.isProcessing = false;
|
|
1418
|
+
onProcessingChanged();
|
|
1419
|
+
}
|
|
1420
|
+
return accepted;
|
|
1421
|
+
} catch (error) {
|
|
1422
|
+
// Includes synchronous input queue failures, before stream consumption.
|
|
1423
|
+
var failedHandle = session.queryInstance;
|
|
1424
|
+
if (failedHandle) {
|
|
1425
|
+
try { if (typeof failedHandle.abort === "function") failedHandle.abort(); } catch (e) {}
|
|
1426
|
+
try { if (typeof failedHandle.close === "function") failedHandle.close(); } catch (e) {}
|
|
1427
|
+
}
|
|
1428
|
+
session.queryInstance = null;
|
|
1429
|
+
session.abortController = null;
|
|
1430
|
+
session.messageQueue = null;
|
|
1431
|
+
session.isProcessing = false;
|
|
1432
|
+
session._awaitingTurnResult = false;
|
|
1433
|
+
session._queuedTurnCount = 0;
|
|
1434
|
+
taskLifecycle.clearAll(session);
|
|
1435
|
+
if (!recoveryState || !recoveryState.stopping) {
|
|
1436
|
+
responseRecovery.cancel(session);
|
|
1437
|
+
sendAndRecord(session, { type: "error", text: "The agent connection failed while delivering your message. Please retry or check the connection." });
|
|
1438
|
+
sendAndRecord(session, { type: "done", code: 1 });
|
|
1439
|
+
onProcessingChanged();
|
|
1440
|
+
sm.broadcastSessionList();
|
|
1441
|
+
}
|
|
1442
|
+
return false;
|
|
1451
1443
|
} finally {
|
|
1452
1444
|
session._queryStarting = false;
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
session.pendingPush = [];
|
|
1458
|
-
}
|
|
1459
|
-
if (!session.queryInstance && !session.isProcessing) session.taskStopRequested = false;
|
|
1460
|
-
if (!session.isProcessing && typeof opts.onSessionAvailable === "function") opts.onSessionAvailable(session);
|
|
1445
|
+
delete session._providerRecoveryWaiting;
|
|
1446
|
+
if (!session.queryInstance && session.pendingPush && session.pendingPush.length) session.pendingPush = [];
|
|
1447
|
+
if (!session.queryInstance && !session.isProcessing && !session._pendingMessageDrainPaused && !responseRecovery.stopping(session)) session.taskStopRequested = false;
|
|
1448
|
+
if (!responseRecovery.stopping(session) && !session.isProcessing && typeof opts.onSessionAvailable === "function") opts.onSessionAvailable(session);
|
|
1461
1449
|
}
|
|
1462
1450
|
}
|
|
1463
1451
|
|
|
@@ -1501,7 +1489,7 @@ function createSDKBridge(opts) {
|
|
|
1501
1489
|
};
|
|
1502
1490
|
}
|
|
1503
1491
|
|
|
1504
|
-
async function startQueryInner(session, text, images, linuxUser, beforePush, onAccepted) {
|
|
1492
|
+
async function startQueryInner(session, text, images, linuxUser, beforePush, onAccepted, recoveryState) {
|
|
1505
1493
|
var sessionRuntimeEnv = getRuntimeEnv(session);
|
|
1506
1494
|
var resumedAtQueryStart = !!session.cliSessionId;
|
|
1507
1495
|
async function ensureVendorReady(vendor) {
|
|
@@ -1653,7 +1641,8 @@ function createSDKBridge(opts) {
|
|
|
1653
1641
|
|
|
1654
1642
|
// Ensure Linux user project directory exists (runs in parallel with worker boot)
|
|
1655
1643
|
if (linuxUser) {
|
|
1656
|
-
|
|
1644
|
+
var preparation = require("./yoke/provider-contract").resolve(sessionAdapter).prepareIsolation;
|
|
1645
|
+
if (typeof preparation === "function") preparation(linuxUser, session, cwd);
|
|
1657
1646
|
}
|
|
1658
1647
|
|
|
1659
1648
|
session.blocks = {};
|
|
@@ -1670,100 +1659,27 @@ function createSDKBridge(opts) {
|
|
|
1670
1659
|
session.abortController = new AbortController();
|
|
1671
1660
|
}
|
|
1672
1661
|
|
|
1673
|
-
// Build Claude-specific adapter options
|
|
1674
|
-
var claudeOpts = {
|
|
1675
|
-
settingSources: ["user", "project", "local"],
|
|
1676
|
-
includePartialMessages: true,
|
|
1677
|
-
enableFileCheckpointing: true,
|
|
1678
|
-
extraArgs: { "replay-user-messages": null },
|
|
1679
|
-
promptSuggestions: true,
|
|
1680
|
-
agentProgressSummaries: true,
|
|
1681
|
-
};
|
|
1682
|
-
|
|
1683
|
-
// Per-loop settings override global defaults when present
|
|
1684
1662
|
var ls = session.loopSettings || {};
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
var thinkingMode = ls.thinking || sm.currentThinking;
|
|
1690
|
-
if (thinkingMode === "disabled") {
|
|
1691
|
-
claudeOpts.thinking = { type: "disabled" };
|
|
1692
|
-
} else if (thinkingMode === "budget") {
|
|
1693
|
-
var budgetTokens = ls.thinkingBudget || sm.currentThinkingBudget;
|
|
1694
|
-
if (budgetTokens) claudeOpts.thinking = { type: "enabled", budgetTokens: budgetTokens };
|
|
1695
|
-
} else {
|
|
1696
|
-
// Adaptive (the default). Request summarized display explicitly:
|
|
1697
|
-
// current models default thinking.display to "omitted", which streams
|
|
1698
|
-
// thinking blocks whose text is an EMPTY string -- the UI's thinking
|
|
1699
|
-
// section stays blank even though thinking happens and is billed.
|
|
1700
|
-
// (SDK ThinkingAdaptive: { type: "adaptive", display?: "summarized" | "omitted" })
|
|
1701
|
-
claudeOpts.thinking = { type: "adaptive", display: "summarized" };
|
|
1702
|
-
}
|
|
1703
|
-
|
|
1704
|
-
if (ls.permissionMode) {
|
|
1705
|
-
session._loopPermissionMode = ls.permissionMode;
|
|
1706
|
-
}
|
|
1707
|
-
|
|
1708
|
-
// Pass through any extra SDK settings from LOOP.json
|
|
1709
|
-
if (ls.disableAllHooks !== undefined) {
|
|
1710
|
-
claudeOpts.settings = Object.assign({}, claudeOpts.settings || {}, { disableAllHooks: ls.disableAllHooks });
|
|
1711
|
-
}
|
|
1712
|
-
|
|
1713
|
-
if (dangerouslySkipPermissions || (session.mode === "tui" && session.dangerouslySkipPermissions)) {
|
|
1714
|
-
claudeOpts.allowDangerouslySkipPermissions = true;
|
|
1715
|
-
claudeOpts.permissionMode = "bypassPermissions";
|
|
1716
|
-
} else {
|
|
1717
|
-
var globalMode = session.permissionMode || sm.currentPermissionMode || "default";
|
|
1718
|
-
var effectiveDefault;
|
|
1719
|
-
// GUI full access stays inside Clay's canUseTool callback. Passing the
|
|
1720
|
-
// SDK bypass mode would require its process-level dangerous escape hatch
|
|
1721
|
-
// and would skip Clay's permission policy entirely.
|
|
1722
|
-
if (globalMode === "bypassPermissions") effectiveDefault = "default";
|
|
1723
|
-
else if (session.acceptEditsAfterStart) effectiveDefault = "acceptEdits";
|
|
1724
|
-
else effectiveDefault = globalMode;
|
|
1725
|
-
var modeToApply = session._loopPermissionMode || effectiveDefault;
|
|
1726
|
-
// Always set permissionMode explicitly, including "default". SDK
|
|
1727
|
-
// 0.3.286+ leaves an omitted permissionMode to Claude Code itself,
|
|
1728
|
-
// which can apply a settings defaultMode or start in auto mode on
|
|
1729
|
-
// third-party providers / with telemetry off -- silently widening
|
|
1730
|
-
// past Clay's Ask policy. Sending "default" keeps the SDK's own
|
|
1731
|
-
// decision point at the same "default" behavior Clay has always run
|
|
1732
|
-
// under: a tool the SDK's own allowed/denied rules or allowedTools
|
|
1733
|
-
// list already settles is not re-asked, but anything left undecided
|
|
1734
|
-
// still reaches Clay's canUseTool callback, as before.
|
|
1735
|
-
claudeOpts.permissionMode = modeToApply || "default";
|
|
1736
|
-
}
|
|
1737
|
-
if (session.youCurator) {
|
|
1738
|
-
claudeOpts.permissionMode = "default";
|
|
1739
|
-
claudeOpts.allowDangerouslySkipPermissions = false;
|
|
1740
|
-
}
|
|
1741
|
-
// Clear one-shot acceptEditsAfterStart regardless of which branch ran above,
|
|
1742
|
-
// so the flag does not linger into subsequent turns.
|
|
1743
|
-
if (session.acceptEditsAfterStart) delete session.acceptEditsAfterStart;
|
|
1744
|
-
if (session.cliSessionId && session.lastRewindUuid) {
|
|
1745
|
-
claudeOpts.resumeSessionAt = session.lastRewindUuid;
|
|
1746
|
-
delete session.lastRewindUuid;
|
|
1747
|
-
sm.saveSessionFile(session);
|
|
1748
|
-
}
|
|
1749
|
-
|
|
1750
|
-
// Pass linuxUser to adapter for worker-based queries
|
|
1663
|
+
if (ls.permissionMode) session._loopPermissionMode = ls.permissionMode;
|
|
1664
|
+
var permissionMode = session.permissionMode || sm.currentPermissionMode || "default";
|
|
1665
|
+
var nativeMode = permissionMode === "bypassPermissions" ? "default" : session.acceptEditsAfterStart ? "acceptEdits" : permissionMode;
|
|
1666
|
+
var isolationHome = null;
|
|
1751
1667
|
if (linuxUser) {
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
}
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1668
|
+
try { isolationHome = require("./os-users").resolveOsUserInfo(linuxUser).home; } catch (error) {}
|
|
1669
|
+
}
|
|
1670
|
+
var executionIntent = {
|
|
1671
|
+
settings: sm, permissionMode: permissionMode, nativePermissionMode: session._loopPermissionMode || nativeMode,
|
|
1672
|
+
nativeBypass: dangerouslySkipPermissions || session.mode === "tui" && session.dangerouslySkipPermissions,
|
|
1673
|
+
readOnly: !!session.youCurator, betas: sm.currentBetas,
|
|
1674
|
+
thinking: { mode: ls.thinking || sm.currentThinking, budget: ls.thinkingBudget || sm.currentThinkingBudget },
|
|
1675
|
+
disableHooks: ls.disableAllHooks, resumeAt: session.cliSessionId && session.lastRewindUuid || undefined,
|
|
1676
|
+
singleTurn: !!session.singleTurn, startedAt: t0,
|
|
1677
|
+
isolation: { user: linuxUser, home: isolationHome, projectPath: session.cwd || null, workerState: session._adapterWorkerState },
|
|
1678
|
+
sessionBridge: null,
|
|
1679
|
+
};
|
|
1680
|
+
if (session.acceptEditsAfterStart) delete session.acceptEditsAfterStart;
|
|
1681
|
+
if (executionIntent.resumeAt) { delete session.lastRewindUuid; sm.saveSessionFile(session); }
|
|
1682
|
+
if (linuxUser && session._adapterWorkerState) session._adapterWorkerState = null;
|
|
1767
1683
|
|
|
1768
1684
|
// Pick a model from the session or its vendor-specific default, then
|
|
1769
1685
|
// validate it against the session vendor's known model list.
|
|
@@ -1789,20 +1705,21 @@ function createSDKBridge(opts) {
|
|
|
1789
1705
|
sm.saveSessionFile(session);
|
|
1790
1706
|
}
|
|
1791
1707
|
|
|
1792
|
-
var codexConfig = getCodexConfig(sm);
|
|
1793
|
-
var kiroConfig = getKiroConfig(sm);
|
|
1794
1708
|
session._sdkQueryGeneration = Number(session._sdkQueryGeneration || 0) + 1;
|
|
1795
1709
|
// Individual feature modules own their transport capability checks. Do not
|
|
1796
1710
|
// suppress unrelated session tools merely because one feature cannot be
|
|
1797
1711
|
// delivered by this provider.
|
|
1798
1712
|
var sessionToolDefs = getSessionToolDefs(session) || [];
|
|
1799
1713
|
var queryGeneration = Number(session._sdkQueryGeneration || 0);
|
|
1714
|
+
executionIntent.sessionBridge = getAcpMcpServers(session);
|
|
1715
|
+
var queryContract = yoke.sessionTools.contract(sessionAdapter);
|
|
1716
|
+
function needsCatalogRecovery(family, version) {
|
|
1717
|
+
return yoke.sessionTools.needsRecovery(sessionAdapter, session, family, version);
|
|
1718
|
+
}
|
|
1800
1719
|
var hasYouCatalog = sessionToolDefs.some(function (tool) { return tool.name === "search_user_knowledge"; });
|
|
1801
|
-
var
|
|
1802
|
-
&& hasYouCatalog && Number(session.codexYouToolCatalogVersion || 0) < 1;
|
|
1720
|
+
var recoverYouTools = needsCatalogRecovery("knowledge", 1) && hasYouCatalog;
|
|
1803
1721
|
var hasLinearCatalog = sessionToolDefs.some(function (tool) { return tool.name === "link_session_linear"; });
|
|
1804
|
-
var
|
|
1805
|
-
&& hasLinearCatalog && Number(session.codexLinearToolCatalogVersion || 0) < 1 && canUseSessionTools(session) === true;
|
|
1722
|
+
var recoverLinearTools = needsCatalogRecovery("linear", 1) && hasLinearCatalog && canUseSessionTools(session) === true;
|
|
1806
1723
|
var hasIssueCatalog = false;
|
|
1807
1724
|
for (var issueToolIndex = 0; issueToolIndex < sessionToolDefs.length; issueToolIndex++) {
|
|
1808
1725
|
if (sessionToolDefs[issueToolIndex].name === "create_issue") {
|
|
@@ -1811,24 +1728,23 @@ function createSDKBridge(opts) {
|
|
|
1811
1728
|
}
|
|
1812
1729
|
}
|
|
1813
1730
|
var hasSharedBrowserCatalog = sessionToolDefs.some(function (definition) { return definition.name === "shared_browser"; });
|
|
1814
|
-
var
|
|
1815
|
-
|
|
1816
|
-
var recoverCodexIssueTools = sessionAdapter.vendor === "codex"
|
|
1817
|
-
&& !!session.cliSessionId
|
|
1818
|
-
&& hasIssueCatalog
|
|
1819
|
-
&& Number(session.codexIssuesToolCatalogVersion || 0) < 1;
|
|
1731
|
+
var recoverBrowserTools = needsCatalogRecovery("browser", 6) && hasSharedBrowserCatalog;
|
|
1732
|
+
var recoverIssueTools = needsCatalogRecovery("issues", 1) && hasIssueCatalog;
|
|
1820
1733
|
function queryContextAvailable() {
|
|
1734
|
+
if (queryRetired()) return false;
|
|
1821
1735
|
if (Number(session._sdkQueryGeneration || 0) !== queryGeneration) return false;
|
|
1822
1736
|
if (sm && sm.sessions && typeof sm.sessions.get === "function" && sm.sessions.get(session.localId) !== session) return false;
|
|
1823
1737
|
return canUseSessionTools(session) === true;
|
|
1824
1738
|
}
|
|
1739
|
+
var hasMateScheduleTool = sessionToolDefs.some(function (tool) { return yoke.sessionTools.MATE_SCHEDULE_TOOLS.indexOf(tool.name) !== -1; });
|
|
1740
|
+
var recoverMateSchedules = needsCatalogRecovery() && hasMateScheduleTool && queryContextAvailable();
|
|
1825
1741
|
var hasScheduleTool = false;
|
|
1826
1742
|
var hasScheduleInterviewTool = false;
|
|
1827
1743
|
var scheduleInterviewActive = null;
|
|
1828
1744
|
var scheduleQuestionLimit = null;
|
|
1829
1745
|
for (var sessionToolIndex = 0; sessionToolIndex < sessionToolDefs.length; sessionToolIndex++) {
|
|
1830
1746
|
if (yoke.sessionTools.SCHEDULE_TOOLS.indexOf(sessionToolDefs[sessionToolIndex].name) !== -1) hasScheduleTool = true;
|
|
1831
|
-
if (sessionToolDefs[sessionToolIndex].name
|
|
1747
|
+
if (["begin_scheduled_task_interview", "begin_mate_schedule_interview", "begin_schedule_control_interview"].indexOf(sessionToolDefs[sessionToolIndex].name) >= 0) {
|
|
1832
1748
|
hasScheduleInterviewTool = true;
|
|
1833
1749
|
scheduleInterviewActive = sessionToolDefs[sessionToolIndex].scheduleInterviewActive || null;
|
|
1834
1750
|
scheduleQuestionLimit = sessionToolDefs[sessionToolIndex].structuredQuestionLimit || null;
|
|
@@ -1838,10 +1754,10 @@ function createSDKBridge(opts) {
|
|
|
1838
1754
|
var activeLoopInterview = !isDriverOperatedSession(session) && !session.loopInterviewHandoff && !activeRun && !!loopInterview.activeInterviewId(session);
|
|
1839
1755
|
var structuredInputSession = hasScheduleInterviewTool || activeLoopInterview || (!isDriverOperatedSession(session) && session.ralphCraftingMode === true && session.loop && session.loop.role === "crafting");
|
|
1840
1756
|
var requestedUserInputMode = session.debateSetupMode || session.mateCreationMode ? "fallback" : "auto";
|
|
1841
|
-
if (structuredInputSession
|
|
1757
|
+
if (structuredInputSession) requestedUserInputMode = "fallback";
|
|
1758
|
+
if (requestedUserInputMode === "auto" && queryContract.defaultUserInputMode) requestedUserInputMode = queryContract.defaultUserInputMode;
|
|
1842
1759
|
var selectedUserInputMode = yoke.userInput.selectMode(sessionAdapter, requestedUserInputMode);
|
|
1843
|
-
var
|
|
1844
|
-
&& Number(session.codexUserInputToolCatalogVersion || 0) < 1 && queryContextAvailable();
|
|
1760
|
+
var recoverQuestionTools = needsCatalogRecovery("questions", 1) && queryContextAvailable();
|
|
1845
1761
|
function structuredInputContextAvailable() {
|
|
1846
1762
|
if (!queryContextAvailable()) return false;
|
|
1847
1763
|
// A schedule-triggered fallback shares the schedule feature's live
|
|
@@ -1860,9 +1776,10 @@ function createSDKBridge(opts) {
|
|
|
1860
1776
|
return "Ask exactly one question at a time, wait for the answer, then ask the next question.";
|
|
1861
1777
|
}
|
|
1862
1778
|
var sessionUserInputHandler = function(request, respond) {
|
|
1863
|
-
if (!structuredInputContextAvailable()) {
|
|
1779
|
+
if (queryRetired() || !structuredInputContextAvailable()) {
|
|
1864
1780
|
return respond.cancel("This structured-input request belongs to an unavailable or older query.");
|
|
1865
1781
|
}
|
|
1782
|
+
responseRecovery.progress(session);
|
|
1866
1783
|
var questionError = scheduleQuestionError(request);
|
|
1867
1784
|
if (questionError) return respond.cancel(questionError);
|
|
1868
1785
|
if (request.presentation === "elicitation" && request.diagnostics && request.diagnostics.elicitation) {
|
|
@@ -1882,21 +1799,33 @@ function createSDKBridge(opts) {
|
|
|
1882
1799
|
return opts.onUserInputRequest(session, request, respond);
|
|
1883
1800
|
};
|
|
1884
1801
|
var localMcpServers = getMcpServers(session);
|
|
1885
|
-
if (
|
|
1802
|
+
if (queryContract.toolTransport === "sdk-mcp" && (hasScheduleTool || hasMateScheduleTool)
|
|
1803
|
+
&& typeof sessionAdapter.createToolServer !== "function") {
|
|
1804
|
+
throw new Error("The adapter cannot mount native schedule tools without createToolServer.");
|
|
1805
|
+
}
|
|
1806
|
+
if (queryContract.toolTransport === "sdk-mcp" && hasScheduleTool) {
|
|
1886
1807
|
var scheduledTaskToolDefs = [];
|
|
1887
1808
|
for (var scheduledToolIndex = 0; scheduledToolIndex < sessionToolDefs.length; scheduledToolIndex++) {
|
|
1888
1809
|
if (yoke.sessionTools.SCHEDULE_TOOLS.indexOf(sessionToolDefs[scheduledToolIndex].name) !== -1) scheduledTaskToolDefs.push(sessionToolDefs[scheduledToolIndex]);
|
|
1889
1810
|
}
|
|
1890
1811
|
if (scheduledTaskToolDefs.length) {
|
|
1891
1812
|
var scheduledTaskServer = sessionAdapter.createToolServer({ name: "clay-scheduled-tasks", version: "1.0.0", tools: scheduledTaskToolDefs });
|
|
1892
|
-
if (scheduledTaskServer)
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
}
|
|
1813
|
+
if (!scheduledTaskServer) throw new Error("The adapter could not mount scheduled-task tools.");
|
|
1814
|
+
localMcpServers = Object.assign({}, localMcpServers || {});
|
|
1815
|
+
localMcpServers["clay-scheduled-tasks"] = scheduledTaskServer;
|
|
1896
1816
|
}
|
|
1897
1817
|
}
|
|
1818
|
+
if (queryContract.toolTransport === "sdk-mcp" && hasMateScheduleTool) {
|
|
1819
|
+
localMcpServers = Object.assign({}, localMcpServers || {});
|
|
1820
|
+
localMcpServers["clay-mate-schedules"] = sessionAdapter.createToolServer({ name: "clay-mate-schedules", version: "1.0.0",
|
|
1821
|
+
tools: sessionToolDefs.filter(function (tool) { return yoke.sessionTools.MATE_SCHEDULE_TOOLS.indexOf(tool.name) !== -1; }) });
|
|
1822
|
+
if (!localMcpServers["clay-mate-schedules"]) throw new Error("The adapter could not mount Mate schedule tools.");
|
|
1823
|
+
}
|
|
1898
1824
|
if (selectedUserInputMode === "fallback") {
|
|
1899
1825
|
var userInputServer = yoke.userInput.createFallbackServer(sessionAdapter, sessionUserInputHandler, { force: true, maxQuestions: scheduleQuestionLimit });
|
|
1826
|
+
if (queryContract.toolTransport === "sdk-mcp" && !userInputServer) {
|
|
1827
|
+
throw new Error("The adapter could not mount structured-input tools.");
|
|
1828
|
+
}
|
|
1900
1829
|
if (userInputServer) {
|
|
1901
1830
|
localMcpServers = Object.assign({}, localMcpServers || {});
|
|
1902
1831
|
localMcpServers[userInputServer.name || "yoke-user-input"] = userInputServer;
|
|
@@ -1933,9 +1862,9 @@ function createSDKBridge(opts) {
|
|
|
1933
1862
|
|
|
1934
1863
|
var externalQueryBridge = require('./mcp-query-bridge').createQueryBridge({ valid: queryContextAvailable,
|
|
1935
1864
|
getServers: function () { return getRemoteMcpServers ? getRemoteMcpServers(session) || {} : {}; } });
|
|
1936
|
-
var externalToolDefs =
|
|
1865
|
+
var externalToolDefs = queryContract.externalToolTransport === "dynamic" ? externalQueryBridge.dynamicDefs() : [];
|
|
1937
1866
|
if (externalToolDefs.length) { sessionToolDefs = sessionToolDefs.concat(externalToolDefs); queryToolSnapshots.set(session, { generation: queryGeneration, defs: sessionToolDefs.slice() }); }
|
|
1938
|
-
var
|
|
1867
|
+
var recoverExternalTools = needsCatalogRecovery() && (externalToolDefs.length > 0 || isMate);
|
|
1939
1868
|
|
|
1940
1869
|
// Give fresh queries an immediate provisional label. The visible user
|
|
1941
1870
|
// message in history wins over `text`, which may contain an internal
|
|
@@ -1951,6 +1880,10 @@ function createSDKBridge(opts) {
|
|
|
1951
1880
|
}
|
|
1952
1881
|
|
|
1953
1882
|
var modelSwitchGeneration = session._queryGeneration;
|
|
1883
|
+
function queryRetired() {
|
|
1884
|
+
return Number(session._sdkQueryGeneration || 0) !== queryGeneration || session.taskStopRequested
|
|
1885
|
+
|| session.destroying || responseRecovery.stopping(session) || (handle && session.queryInstance !== handle);
|
|
1886
|
+
}
|
|
1954
1887
|
var queryOpts = {
|
|
1955
1888
|
onModelSwitch: function(change) {
|
|
1956
1889
|
if (session._queryGeneration !== modelSwitchGeneration || session.taskStopRequested) return;
|
|
@@ -1965,7 +1898,7 @@ function createSDKBridge(opts) {
|
|
|
1965
1898
|
clayBridgeToken: clayBridgeToken,
|
|
1966
1899
|
linuxUser: linuxUser || undefined,
|
|
1967
1900
|
env: sessionRuntimeEnv,
|
|
1968
|
-
skillOptions: getSessionSkillOptions(session),
|
|
1901
|
+
skillOptions: require("./session-skill-scope").bind(session, sm.sessions, getSessionSkillOptions(session), queryContextAvailable, usersModule.isMultiUser()),
|
|
1969
1902
|
// appendSystemPrompt, NOT systemPrompt: a plain-string systemPrompt
|
|
1970
1903
|
// REPLACES the entire Claude Code system prompt (sdk.d.ts:2096), which
|
|
1971
1904
|
// would strip the driver session down to the two-sentence pair preset.
|
|
@@ -1973,7 +1906,7 @@ function createSDKBridge(opts) {
|
|
|
1973
1906
|
// codex/kiro: prepend to the first message).
|
|
1974
1907
|
appendSystemPrompt: [
|
|
1975
1908
|
typeof opts.getSessionSystemPrompt === "function" ? opts.getSessionSystemPrompt(session) : "",
|
|
1976
|
-
|
|
1909
|
+
typeof queryContract.questionGuidance === "function" && queryContextAvailable() ? queryContract.questionGuidance() : "",
|
|
1977
1910
|
].filter(Boolean).join("\n\n") || undefined,
|
|
1978
1911
|
model: queryModel,
|
|
1979
1912
|
// Effort is remembered per vendor and clamped to the session vendor's
|
|
@@ -1990,10 +1923,22 @@ function createSDKBridge(opts) {
|
|
|
1990
1923
|
toolServers: mergedMcpServers,
|
|
1991
1924
|
toolServerDescriptors: extractMcpDescriptors(mergedMcpServers) || undefined,
|
|
1992
1925
|
dynamicTools: sessionToolDefs,
|
|
1993
|
-
//
|
|
1926
|
+
// Conversation-scoped catalogs persist definitions. A stale authorized Driver
|
|
1994
1927
|
// gets this exact-query bridge; its handlers still reauthorize live.
|
|
1995
|
-
sessionMcpServer: (
|
|
1928
|
+
sessionMcpServer: (recoverMateSchedules || recoverIssueTools || recoverQuestionTools || recoverBrowserTools || recoverLinearTools || recoverYouTools || recoverExternalTools) ? getSessionMcpServer(session, queryGeneration) : undefined,
|
|
1929
|
+
onRecoveryAdmission: function(waiting) {
|
|
1930
|
+
if (!queryContextAvailable()) return;
|
|
1931
|
+
if (waiting) session._providerRecoveryWaiting = queryGeneration;
|
|
1932
|
+
else delete session._providerRecoveryWaiting;
|
|
1933
|
+
},
|
|
1934
|
+
onRecoveryWaiting: function(waiting) {
|
|
1935
|
+
if (!queryContextAvailable()) return;
|
|
1936
|
+
sendAndRecord(session, { type: 'informational', level: 'info', content: waiting
|
|
1937
|
+
? 'Tool recovery is waiting for this conversation’s runtime to become safely available. Your message is retained; Stop cancels the wait.'
|
|
1938
|
+
: 'Tool recovery is ready. Continuing your retained message.' });
|
|
1939
|
+
},
|
|
1996
1940
|
callDynamicTool: function (toolName, args) {
|
|
1941
|
+
if (queryRetired() || !queryContextAvailable()) return Promise.reject(new Error("This session tool query is no longer available."));
|
|
1997
1942
|
var toolDef = findSessionToolDef(toolName);
|
|
1998
1943
|
if (toolDef && typeof toolDef.handler === "function") {
|
|
1999
1944
|
return toolDef.handler(args || {});
|
|
@@ -2003,7 +1948,10 @@ function createSDKBridge(opts) {
|
|
|
2003
1948
|
resumeSessionId: session.cliSessionId || undefined,
|
|
2004
1949
|
abortController: linuxUser ? undefined : session.abortController,
|
|
2005
1950
|
canUseTool: function(toolName, input, toolOpts) {
|
|
2006
|
-
|
|
1951
|
+
if (queryRetired()) return { behavior: "deny", message: "This attempt has stopped." };
|
|
1952
|
+
responseRecovery.progress(session);
|
|
1953
|
+
var questionInput = yoke.userInput.toolQuestions(sessionAdapter, toolName, input);
|
|
1954
|
+
var nativeQuestionError = questionInput ? scheduleQuestionError(questionInput) : null;
|
|
2007
1955
|
if (nativeQuestionError) return { behavior: "deny", message: nativeQuestionError };
|
|
2008
1956
|
var permissionName = toolName;
|
|
2009
1957
|
var permissionToolDef = findSessionToolDef(toolName);
|
|
@@ -2011,7 +1959,10 @@ function createSDKBridge(opts) {
|
|
|
2011
1959
|
return handleCanUseTool(session, permissionName, input, toolOpts);
|
|
2012
1960
|
},
|
|
2013
1961
|
preToolUsePolicy: function(toolName, input) {
|
|
2014
|
-
|
|
1962
|
+
if (queryRetired()) return Promise.resolve({ behavior: "deny", message: "This attempt has stopped." });
|
|
1963
|
+
responseRecovery.progress(session);
|
|
1964
|
+
var questionInput = yoke.userInput.toolQuestions(sessionAdapter, toolName, input);
|
|
1965
|
+
var nativeQuestionError = questionInput ? scheduleQuestionError(questionInput) : null;
|
|
2015
1966
|
if (nativeQuestionError) return Promise.resolve({ behavior: "deny", message: nativeQuestionError });
|
|
2016
1967
|
var permissionName = toolName;
|
|
2017
1968
|
var permissionToolDef = findSessionToolDef(toolName);
|
|
@@ -2024,47 +1975,45 @@ function createSDKBridge(opts) {
|
|
|
2024
1975
|
validateUserInputRequest: scheduleQuestionError,
|
|
2025
1976
|
userInputMode: selectedUserInputMode,
|
|
2026
1977
|
onElicitation: function(request, elicitOpts) {
|
|
1978
|
+
if (queryRetired()) return Promise.resolve({ action: "cancel" });
|
|
1979
|
+
responseRecovery.progress(session);
|
|
2027
1980
|
return handleElicitation(session, request, elicitOpts);
|
|
2028
1981
|
},
|
|
2029
1982
|
onUserDialog: function(dialogRequest, dialogOpts) {
|
|
1983
|
+
if (queryRetired()) return Promise.resolve({ behavior: "cancelled" });
|
|
1984
|
+
responseRecovery.progress(session);
|
|
2030
1985
|
return handleUserDialog(session, dialogRequest, dialogOpts);
|
|
2031
1986
|
},
|
|
2032
1987
|
callMcpTool: function(serverName, toolName, args) {
|
|
1988
|
+
if (queryRetired()) return Promise.reject(new Error("This attempt has stopped."));
|
|
1989
|
+
responseRecovery.progress(session);
|
|
2033
1990
|
return callMcpToolHandler(mergedMcpServers, serverName, toolName, args);
|
|
2034
1991
|
},
|
|
2035
|
-
adapterOptions:
|
|
2036
|
-
CLAUDE: claudeOpts,
|
|
2037
|
-
CODEX: {
|
|
2038
|
-
// Always use "never" (auto-approve) because Clay handles tool
|
|
2039
|
-
// permissions via its own UI (checkToolWhitelist + handleCanUseTool).
|
|
2040
|
-
// Codex's native approval prompts are terminal-based and cannot be
|
|
2041
|
-
// relayed through Clay's web UI, causing MCP tool calls to hang.
|
|
2042
|
-
approvalPolicy: "never",
|
|
2043
|
-
sandboxMode: session.youCurator ? "read-only" : codexConfig.sandbox,
|
|
2044
|
-
webSearchMode: session.youCurator ? "disabled" : codexConfig.webSearch,
|
|
2045
|
-
},
|
|
2046
|
-
ACP: {
|
|
2047
|
-
mcpServers: getAcpMcpServers(session),
|
|
2048
|
-
},
|
|
2049
|
-
ANTIGRAVITY: {
|
|
2050
|
-
dangerouslySkipPermissions: dangerouslySkipPermissions
|
|
2051
|
-
|| session.permissionMode === "bypassPermissions",
|
|
2052
|
-
},
|
|
2053
|
-
KIRO: {
|
|
2054
|
-
engine: kiroConfig.engine,
|
|
2055
|
-
mode: kiroConfig.mode,
|
|
2056
|
-
// ACP accepts additional stdio MCP servers per session. The bridge is
|
|
2057
|
-
// bound to this Clay session so partner calls cannot leak across
|
|
2058
|
-
// concurrent Kiro sessions or users.
|
|
2059
|
-
mcpServers: getAcpMcpServers(session),
|
|
2060
|
-
},
|
|
2061
|
-
},
|
|
1992
|
+
adapterOptions: require("./yoke/query-options").map(sessionAdapter, executionIntent),
|
|
2062
1993
|
};
|
|
2063
1994
|
|
|
1995
|
+
if (recoveryState) {
|
|
1996
|
+
if (!recoveryState.context) recoveryState.context = { appendSystemPrompt: queryOpts.appendSystemPrompt, model: queryOpts.model, effort: queryOpts.effort };
|
|
1997
|
+
Object.assign(queryOpts, recoveryState.context);
|
|
1998
|
+
}
|
|
2064
1999
|
var handle;
|
|
2000
|
+
var startupHandle = session.queryInstance;
|
|
2065
2001
|
console.log("[sdk-bridge] calling adapter.createQuery... vendor=" + sessionAdapter.vendor);
|
|
2066
2002
|
try {
|
|
2067
2003
|
handle = await sessionAdapter.createQuery(queryOpts);
|
|
2004
|
+
// A guarded delivery must still own the exact startup after provider
|
|
2005
|
+
// creation. Never replace a newer handle or publish to a deleted target.
|
|
2006
|
+
if (beforePush && beforePush.exactProductDelivery) {
|
|
2007
|
+
var startupCurrent = false;
|
|
2008
|
+
try { startupCurrent = sm.sessions.get(session.localId) === session && session.queryInstance === startupHandle && Number(session._sdkQueryGeneration || 0) === queryGeneration && beforePush() === true; } catch (guardError) {}
|
|
2009
|
+
if (!startupCurrent) {
|
|
2010
|
+
try { if (handle.abort) handle.abort(); } catch (abortError) {}
|
|
2011
|
+
try { if (handle.close) handle.close(); } catch (closeError) {}
|
|
2012
|
+
if (session.queryInstance === startupHandle) session.isProcessing = false;
|
|
2013
|
+
return false;
|
|
2014
|
+
}
|
|
2015
|
+
}
|
|
2016
|
+
if (recoveryState) recoveryState.handle = handle;
|
|
2068
2017
|
console.log("[sdk-bridge] createQuery returned handle, vendor=" + sessionAdapter.vendor);
|
|
2069
2018
|
// SDK accepted the explicit title. Provisional labels remain eligible
|
|
2070
2019
|
// for semantic generation after the first Home exchange (or the normal
|
|
@@ -2084,6 +2033,7 @@ function createSDKBridge(opts) {
|
|
|
2084
2033
|
console.error("[sdk-bridge] Failed to create query for session " + session.localId + ":", e.message || e);
|
|
2085
2034
|
console.error("[sdk-bridge] cliSessionId:", session.cliSessionId, "resume:", !!session.cliSessionId);
|
|
2086
2035
|
console.error("[sdk-bridge] Stack:", e.stack || "(no stack)");
|
|
2036
|
+
if (beforePush && beforePush.exactProductDelivery && (sm.sessions.get(session.localId) !== session || session.queryInstance !== startupHandle || Number(session._sdkQueryGeneration || 0) !== queryGeneration)) return false;
|
|
2087
2037
|
session.isProcessing = false;
|
|
2088
2038
|
session._awaitingTurnResult = false;
|
|
2089
2039
|
session._queuedTurnCount = 0;
|
|
@@ -2118,11 +2068,9 @@ function createSDKBridge(opts) {
|
|
|
2118
2068
|
|
|
2119
2069
|
// Store QueryHandle on session for iteration and control.
|
|
2120
2070
|
session.queryInstance = handle;
|
|
2121
|
-
//
|
|
2122
|
-
//
|
|
2123
|
-
|
|
2124
|
-
session.permissionCapabilities = require("./session-permission-mode").capabilities(session, sm.defaultVendor);
|
|
2125
|
-
session.permissionCapabilities.mcpOverride = sessionAdapter.vendor === "claude" && typeof handle.setMcpPermissionModeOverride === "function";
|
|
2071
|
+
// An installed setter and the adapter's explicit protocol contract are
|
|
2072
|
+
// both required; generic handle stubs do not establish runtime support.
|
|
2073
|
+
session.permissionCapabilities = require("./session-permission-mode").capabilities(session, sm.defaultVendor, sessionAdapter);
|
|
2126
2074
|
session.effectivePermissionMode = null;
|
|
2127
2075
|
sm.saveSessionFile(session);
|
|
2128
2076
|
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", effectivePermissionMode: null, permissionCapabilities: session.permissionCapabilities, mcpPermissionModeOverrides: session.mcpPermissionModeOverrides || {}, effort: session.effort || sm.currentEffort || "medium", betas: sm.currentBetas || [] });
|
|
@@ -2138,6 +2086,7 @@ function createSDKBridge(opts) {
|
|
|
2138
2086
|
}
|
|
2139
2087
|
|
|
2140
2088
|
if (typeof beforePush === "function" && beforePush() !== true) {
|
|
2089
|
+
try { if (typeof handle.abort === "function") handle.abort(); } catch (e) {}
|
|
2141
2090
|
try { handle.close(); } catch (e) {}
|
|
2142
2091
|
if (session.queryInstance === handle) session.queryInstance = null;
|
|
2143
2092
|
session.messageQueue = null;
|
|
@@ -2147,6 +2096,13 @@ function createSDKBridge(opts) {
|
|
|
2147
2096
|
session._queuedTurnCount = 0;
|
|
2148
2097
|
taskLifecycle.clearAll(session);
|
|
2149
2098
|
onProcessingChanged();
|
|
2099
|
+
if (recoveryState && !responseRecovery.stopping(session)) {
|
|
2100
|
+
sendAndRecord(session, { type: "thinking_stop" });
|
|
2101
|
+
sendAndRecord(session, { type: session.taskStopRequested ? "info" : "error", text: session.taskStopRequested
|
|
2102
|
+
? "Stopped before sending your message."
|
|
2103
|
+
: "This request is no longer eligible for delivery. Please review it and send it again." });
|
|
2104
|
+
sendAndRecord(session, { type: "done", code: session.taskStopRequested ? 0 : 1 });
|
|
2105
|
+
}
|
|
2150
2106
|
sm.broadcastSessionList();
|
|
2151
2107
|
return false;
|
|
2152
2108
|
}
|
|
@@ -2198,32 +2154,27 @@ function createSDKBridge(opts) {
|
|
|
2198
2154
|
}
|
|
2199
2155
|
|
|
2200
2156
|
session.lastActivityAt = Date.now();
|
|
2201
|
-
var
|
|
2202
|
-
if (
|
|
2157
|
+
var catalogCandidate = null;
|
|
2158
|
+
if (queryContract.catalogLifetime === "conversation" && typeof queryContract.recordCatalog === "function" && !resumedAtQueryStart) {
|
|
2203
2159
|
var requiredCatalogTools = { begin_scheduled_task_interview: false, propose_scheduled_task: false, list_scheduled_tasks: false, read_scheduled_task: false, update_scheduled_task: false, report_scheduled_task_outcome: false, ask_user_questions: false };
|
|
2204
2160
|
for (var catalogToolIndex = 0; catalogToolIndex < sessionToolDefs.length; catalogToolIndex++) {
|
|
2205
2161
|
if (Object.prototype.hasOwnProperty.call(requiredCatalogTools, sessionToolDefs[catalogToolIndex].name)) requiredCatalogTools[sessionToolDefs[catalogToolIndex].name] = true;
|
|
2206
2162
|
}
|
|
2207
2163
|
var completeInterviewCatalog = requiredCatalogTools.begin_scheduled_task_interview && requiredCatalogTools.propose_scheduled_task && requiredCatalogTools.list_scheduled_tasks && requiredCatalogTools.read_scheduled_task && requiredCatalogTools.update_scheduled_task && requiredCatalogTools.ask_user_questions;
|
|
2208
2164
|
if (completeInterviewCatalog || requiredCatalogTools.report_scheduled_task_outcome) {
|
|
2209
|
-
|
|
2165
|
+
catalogCandidate = { generation: queryGeneration, versions: { schedules: yoke.sessionTools.SCHEDULE_CATALOG_VERSION } };
|
|
2210
2166
|
}
|
|
2211
|
-
|
|
2212
|
-
|
|
2213
|
-
|
|
2214
|
-
if (hasYouCatalog) codexCatalogCandidate = Object.assign({}, codexCatalogCandidate || {}, { generation: queryGeneration, youVersion: 1 });
|
|
2215
|
-
if (hasLinearCatalog) codexCatalogCandidate = Object.assign({}, codexCatalogCandidate || {}, { generation: queryGeneration, linearVersion: 1 });
|
|
2216
|
-
if (hasSharedBrowserCatalog) {
|
|
2217
|
-
codexCatalogCandidate = Object.assign({}, codexCatalogCandidate || {}, { generation: queryGeneration, sharedBrowserVersion: 6 });
|
|
2218
|
-
}
|
|
2219
|
-
if (hasIssueCatalog) {
|
|
2220
|
-
codexCatalogCandidate = Object.assign({}, codexCatalogCandidate || {}, {
|
|
2221
|
-
generation: queryGeneration,
|
|
2222
|
-
issuesVersion: 1,
|
|
2223
|
-
});
|
|
2167
|
+
function markCatalog(family, version) {
|
|
2168
|
+
if (!catalogCandidate) catalogCandidate = { generation: queryGeneration, versions: {} };
|
|
2169
|
+
catalogCandidate.versions[family] = version;
|
|
2224
2170
|
}
|
|
2171
|
+
if (requiredCatalogTools.ask_user_questions) markCatalog("questions", 1);
|
|
2172
|
+
if (hasYouCatalog) markCatalog("knowledge", 1);
|
|
2173
|
+
if (hasLinearCatalog) markCatalog("linear", 1);
|
|
2174
|
+
if (hasSharedBrowserCatalog) markCatalog("browser", 6);
|
|
2175
|
+
if (hasIssueCatalog) markCatalog("issues", 1);
|
|
2225
2176
|
}
|
|
2226
|
-
session.streamPromise = processQueryStream(session,
|
|
2177
|
+
session.streamPromise = processQueryStream(session, catalogCandidate, queryContract).catch(function(err) {
|
|
2227
2178
|
});
|
|
2228
2179
|
return true;
|
|
2229
2180
|
}
|
|
@@ -2235,6 +2186,19 @@ function createSDKBridge(opts) {
|
|
|
2235
2186
|
// dropped messages in both gap windows, which is what made a session show
|
|
2236
2187
|
// the processing indicator without ever answering until a second send.
|
|
2237
2188
|
function pushMessage(session, text, images, deliveryMeta) {
|
|
2189
|
+
if (session._responseRecoveryStopPending && !responseRecovery.stopping(session)) return false;
|
|
2190
|
+
function trackDelivery() {
|
|
2191
|
+
responseRecovery.enqueue(session, {
|
|
2192
|
+
text: text, images: images, linuxUser: session.lastLinuxUser || null,
|
|
2193
|
+
});
|
|
2194
|
+
}
|
|
2195
|
+
if (responseRecovery.stopping(session)) {
|
|
2196
|
+
// Keep admission serialized while the old execution is terminating.
|
|
2197
|
+
session.pendingPush = session.pendingPush || [];
|
|
2198
|
+
session.pendingPush.push(Object.assign({ text: text, images: images || null }, deliveryMeta || {}));
|
|
2199
|
+
trackDelivery();
|
|
2200
|
+
return true;
|
|
2201
|
+
}
|
|
2238
2202
|
session.lastActivityAt = Date.now();
|
|
2239
2203
|
if (session._runtimeRefreshRequested && !session._queryStarting) {
|
|
2240
2204
|
session._runtimeRefreshRequested = false;
|
|
@@ -2261,6 +2225,8 @@ function createSDKBridge(opts) {
|
|
|
2261
2225
|
console.error("[sdk-bridge] QueryHandle rejected message for session " + session.localId + ":", e.message || e);
|
|
2262
2226
|
}
|
|
2263
2227
|
if (delivered) {
|
|
2228
|
+
require('./sdk-foreground-admission').take(session);
|
|
2229
|
+
trackDelivery();
|
|
2264
2230
|
delete session._lastTurnInterrupted;
|
|
2265
2231
|
if (session._awaitingTurnResult) {
|
|
2266
2232
|
session._queuedTurnCount = (session._queuedTurnCount || 0) + 1;
|
|
@@ -2285,6 +2251,7 @@ function createSDKBridge(opts) {
|
|
|
2285
2251
|
if (session._queryStarting) {
|
|
2286
2252
|
session.pendingPush = session.pendingPush || [];
|
|
2287
2253
|
session.pendingPush.push(Object.assign({ text: text, images: images || null }, deliveryMeta || {}));
|
|
2254
|
+
trackDelivery();
|
|
2288
2255
|
return true;
|
|
2289
2256
|
}
|
|
2290
2257
|
return false;
|
|
@@ -2365,65 +2332,8 @@ function createSDKBridge(opts) {
|
|
|
2365
2332
|
// Detect which vendor binaries are installed for this user.
|
|
2366
2333
|
// In multi-user mode, runs checks as the specific Linux user.
|
|
2367
2334
|
function detectInstalledVendors(linuxUser) {
|
|
2368
|
-
var
|
|
2369
|
-
|
|
2370
|
-
var result = [];
|
|
2371
|
-
|
|
2372
|
-
function tryLookup(name) {
|
|
2373
|
-
try {
|
|
2374
|
-
if (linuxUser) {
|
|
2375
|
-
execFileSync("su", ["-", linuxUser, "-c", "which " + name], { timeout: 3000, stdio: ["pipe", "pipe", "pipe"] });
|
|
2376
|
-
} else {
|
|
2377
|
-
if (process.platform === "win32") execFileSync("where", [name], { timeout: 3000, stdio: ["pipe", "pipe", "pipe"] });
|
|
2378
|
-
else execFileSync("which", [name], { timeout: 3000, stdio: ["pipe", "pipe", "pipe"] });
|
|
2379
|
-
}
|
|
2380
|
-
return true;
|
|
2381
|
-
} catch (e) {
|
|
2382
|
-
return false;
|
|
2383
|
-
}
|
|
2384
|
-
}
|
|
2385
|
-
|
|
2386
|
-
// Claude: check if binary is in PATH
|
|
2387
|
-
if (tryLookup(yoke.getVendorInfo("claude").binaryName)) result.push("claude");
|
|
2388
|
-
|
|
2389
|
-
// Codex: check bundled binary or PATH
|
|
2390
|
-
var codexBin = null;
|
|
2391
|
-
try {
|
|
2392
|
-
codexBin = require("./yoke/codex-app-server").findCodexPath();
|
|
2393
|
-
} catch (e) {}
|
|
2394
|
-
if ((codexBin && fs.existsSync(codexBin)) || tryLookup(yoke.getVendorInfo("codex").binaryName)) result.push("codex");
|
|
2395
|
-
|
|
2396
|
-
var subprocessVendorKeys = ["antigravity", "opencode", "kimi", "grok", "copilot", "qwen", "junie"];
|
|
2397
|
-
var acpProfiles = require("./yoke/acp-agent-profiles");
|
|
2398
|
-
for (var subprocessVendorIndex = 0; subprocessVendorIndex < subprocessVendorKeys.length; subprocessVendorIndex++) {
|
|
2399
|
-
var subprocessVendor = subprocessVendorKeys[subprocessVendorIndex];
|
|
2400
|
-
var subprocessInfo = yoke.getVendorInfo(subprocessVendor);
|
|
2401
|
-
if (!linuxUser || subprocessInfo.osUserIsolation) {
|
|
2402
|
-
if (subprocessVendor === "antigravity") {
|
|
2403
|
-
if (tryLookup(subprocessInfo.binaryName)) result.push(subprocessVendor);
|
|
2404
|
-
} else {
|
|
2405
|
-
var acpProfile = acpProfiles.getAcpAgentProfile(subprocessVendor);
|
|
2406
|
-
if (acpProfiles.findAcpAgentPath(acpProfile)) result.push(subprocessVendor);
|
|
2407
|
-
}
|
|
2408
|
-
} else {
|
|
2409
|
-
console.log("[sdk-bridge] " + subprocessInfo.displayName + " hidden for OS-isolated user " + linuxUser + ": per-user spawning is not implemented");
|
|
2410
|
-
}
|
|
2411
|
-
}
|
|
2412
|
-
|
|
2413
|
-
// Kiro has no per-user ACP spawn path yet. Do not advertise the daemon's
|
|
2414
|
-
// binary to an OS-isolated user, even if that user also has Kiro installed.
|
|
2415
|
-
var kiroInfo = yoke.getVendorInfo("kiro");
|
|
2416
|
-
if (!linuxUser || kiroInfo.osUserIsolation) {
|
|
2417
|
-
var kiroBin = null;
|
|
2418
|
-
try {
|
|
2419
|
-
kiroBin = require("./yoke/kiro-acp-server").findKiroPath();
|
|
2420
|
-
} catch (e) {}
|
|
2421
|
-
if ((kiroBin && fs.existsSync(kiroBin)) || tryLookup(kiroInfo.binaryName)) result.push("kiro");
|
|
2422
|
-
} else {
|
|
2423
|
-
console.log("[sdk-bridge] " + kiroInfo.displayName + " hidden for OS-isolated user " + linuxUser + ": per-user spawning is not implemented");
|
|
2424
|
-
}
|
|
2425
|
-
|
|
2426
|
-
return result;
|
|
2335
|
+
var installed = require("./yoke/discovery").installed({ user: linuxUser });
|
|
2336
|
+
return Object.keys(installed).filter(function(vendor) { return installed[vendor]; });
|
|
2427
2337
|
}
|
|
2428
2338
|
|
|
2429
2339
|
function getAvailableVendors(linuxUser) {
|
|
@@ -2495,7 +2405,20 @@ function createSDKBridge(opts) {
|
|
|
2495
2405
|
sendModelInfoForVendor(null, defaultVendor, defaultModelForVendor(defaultVendor));
|
|
2496
2406
|
}
|
|
2497
2407
|
|
|
2498
|
-
async function setModel(session, model) {
|
|
2408
|
+
async function setModel(session, model, options) {
|
|
2409
|
+
if (options && typeof options.revalidate === "function") {
|
|
2410
|
+
var vendor = session.vendor || sm.defaultVendor || "claude";
|
|
2411
|
+
return require("./session-config-transition").applySessionConfig({
|
|
2412
|
+
sm: sm, session: session, revalidate: options.revalidate, method: "setModel", nativeValue: model, changes: { model: model },
|
|
2413
|
+
publish: function () {
|
|
2414
|
+
sm.defaultModelByVendor = sm.defaultModelByVendor || {};
|
|
2415
|
+
sm.defaultModelByVendor[vendor] = model;
|
|
2416
|
+
sm.broadcastSessionList();
|
|
2417
|
+
sendModelInfoForVendor(session, vendor, model);
|
|
2418
|
+
sendToSession(session, { type: "config_state", sessionId: session.localId, vendor: vendor, model: model, effort: session.effort || sm.currentEffort || "medium", mode: session.permissionMode || sm.currentPermissionMode || "default", betas: sm.currentBetas || [] });
|
|
2419
|
+
},
|
|
2420
|
+
});
|
|
2421
|
+
}
|
|
2499
2422
|
// Normalize to string id in case a { value, displayName } object slips in
|
|
2500
2423
|
if (model && typeof model !== "string") {
|
|
2501
2424
|
model = modelEntryValue(model);
|
|
@@ -2531,7 +2454,22 @@ function createSDKBridge(opts) {
|
|
|
2531
2454
|
}
|
|
2532
2455
|
}
|
|
2533
2456
|
|
|
2534
|
-
async function setEffort(session, effort) {
|
|
2457
|
+
async function setEffort(session, effort, options) {
|
|
2458
|
+
if (options && typeof options.revalidate === "function") {
|
|
2459
|
+
var vendor = session.vendor || sm.defaultVendor || "claude";
|
|
2460
|
+
var value = yoke.clampEffort(vendor, effort);
|
|
2461
|
+
if (!value || value !== effort) throw new Error("Unsupported reasoning effort.");
|
|
2462
|
+
return require("./session-config-transition").applySessionConfig({
|
|
2463
|
+
sm: sm, session: session, revalidate: options.revalidate, method: "setEffort", nativeValue: value, changes: { effort: value },
|
|
2464
|
+
publish: function () {
|
|
2465
|
+
sm.currentEffortByVendor = sm.currentEffortByVendor || {};
|
|
2466
|
+
sm.currentEffortByVendor[vendor] = value;
|
|
2467
|
+
sm.currentEffort = value;
|
|
2468
|
+
sm.broadcastSessionList();
|
|
2469
|
+
sendToSession(session, { type: "config_state", sessionId: session.localId, vendor: vendor, effort: value, model: session.model || "", mode: session.permissionMode || sm.currentPermissionMode || "default", betas: sm.currentBetas || [] });
|
|
2470
|
+
},
|
|
2471
|
+
});
|
|
2472
|
+
}
|
|
2535
2473
|
// Remember the pick per vendor (clamped) so vendor switches don't leak
|
|
2536
2474
|
// unsupported levels across sessions; sm.currentEffort stays as the
|
|
2537
2475
|
// display value for the session the user just configured.
|
|
@@ -2652,16 +2590,7 @@ function createSDKBridge(opts) {
|
|
|
2652
2590
|
message: "Read-only access. You cannot make changes via @mention.",
|
|
2653
2591
|
});
|
|
2654
2592
|
},
|
|
2655
|
-
adapterOptions: {
|
|
2656
|
-
CLAUDE: {
|
|
2657
|
-
settingSources: ["user"],
|
|
2658
|
-
includePartialMessages: true,
|
|
2659
|
-
// Explicit, not omitted: SDK 0.3.286+ leaves an omitted
|
|
2660
|
-
// permissionMode to Claude Code's own default/auto behavior,
|
|
2661
|
-
// which could bypass the deny-by-default canUseTool above.
|
|
2662
|
-
permissionMode: "default",
|
|
2663
|
-
},
|
|
2664
|
-
},
|
|
2593
|
+
adapterOptions: require("./yoke/query-options").map(mentionAdapter, { instructionSources: ["user"], readOnly: true }),
|
|
2665
2594
|
});
|
|
2666
2595
|
} catch (e) {
|
|
2667
2596
|
opts.onError("Failed to create mention query: " + (e.message || e));
|
|
@@ -2800,12 +2729,14 @@ function createSDKBridge(opts) {
|
|
|
2800
2729
|
startQuery: startQuery,
|
|
2801
2730
|
startQueryWithAcceptance: startQueryWithAcceptance,
|
|
2802
2731
|
pushMessage: pushMessage,
|
|
2732
|
+
cancelResponseRecovery: responseRecovery.cancel,
|
|
2803
2733
|
refreshSessionRuntime: refreshSessionRuntime,
|
|
2804
2734
|
refreshEnvironmentRuntime: refreshEnvironmentRuntime,
|
|
2805
2735
|
setModel: setModel,
|
|
2806
2736
|
setEffort: setEffort,
|
|
2807
2737
|
setPermissionMode: setPermissionMode,
|
|
2808
|
-
|
|
2738
|
+
isProviderProcess: isProviderProcess,
|
|
2739
|
+
isClaudeProcess: isProviderProcess, // Compatibility export; production uses the provider contract.
|
|
2809
2740
|
permissionPushTitle: permissionPushTitle,
|
|
2810
2741
|
permissionPushBody: permissionPushBody,
|
|
2811
2742
|
warmup: warmup,
|