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/project-sessions.js
CHANGED
|
@@ -2,7 +2,7 @@ var serverSettingsMessages = require("./project-server-settings");
|
|
|
2
2
|
var fs = require("fs");
|
|
3
3
|
var path = require("path");
|
|
4
4
|
var { execFileSync } = require("child_process");
|
|
5
|
-
var
|
|
5
|
+
var providerSettings = require("./yoke/settings");
|
|
6
6
|
var updateSnooze = require("./update-snooze");
|
|
7
7
|
var yoke = require("./yoke");
|
|
8
8
|
var sessionVisibility = require("./session-visibility");
|
|
@@ -118,14 +118,20 @@ function attachSessions(ctx) {
|
|
|
118
118
|
var ensureProjectAccessForSession = ctx.ensureProjectAccessForSession;
|
|
119
119
|
var getOsUserInfoForWs = ctx.getOsUserInfoForWs;
|
|
120
120
|
var hydrateSkillCatalog = ctx.hydrateSkillCatalog || function () { return []; };
|
|
121
|
+
var sessionActions = require("./project-session-actions").attachSessionActions(ctx);
|
|
122
|
+
var sessionVendor = require("./project-session-vendor").attachSessionVendor(ctx);
|
|
123
|
+
var sessionRuntime = require("./project-session-runtime-actions").attachSessionRuntimeActions({ sm: sm, tm: tm, prepareGui: prepareTuiSessionForGuiView, isPendingQueuePaused: ctx.isPendingQueuePaused,
|
|
124
|
+
publishGui: function (session) {
|
|
125
|
+
clients.forEach(function (ws) {
|
|
126
|
+
if (ws.readyState !== 1 || ws._clayActiveSession !== session.localId) return;
|
|
127
|
+
if (usersModule.isMultiUser() && (!ws._clayUser || !usersModule.findUserById(ws._clayUser.id) || !usersModule.canAccessProject(ws._clayUser.id, getProjectAccess()) || !usersModule.canAccessSession(ws._clayUser.id, session, getProjectAccess()))) return;
|
|
128
|
+
sm.switchSession(session.localId, ws, ctx.hydrateImageRefs);
|
|
129
|
+
});
|
|
130
|
+
},
|
|
131
|
+
});
|
|
121
132
|
function stopSession(session, actor) {
|
|
122
133
|
if (!session) return false;
|
|
123
|
-
session
|
|
124
|
-
if (typeof ctx.onPendingQueuePause === "function") ctx.onPendingQueuePause(session, actor || null);
|
|
125
|
-
if (!session.isProcessing && !session._queryStarting) return true;
|
|
126
|
-
if (typeof ctx.onHumanPairStop === "function") ctx.onHumanPairStop(session);
|
|
127
|
-
session.taskStopRequested = true;
|
|
128
|
-
if (session.abortController) session.abortController.abort();
|
|
134
|
+
sessionActions.stop(session, actor);
|
|
129
135
|
return true;
|
|
130
136
|
}
|
|
131
137
|
var hydrateImageRefs = ctx.hydrateImageRefs;
|
|
@@ -149,9 +155,9 @@ function attachSessions(ctx) {
|
|
|
149
155
|
var answers = msg.answers || {};
|
|
150
156
|
var pending = session.pendingAskUser && session.pendingAskUser[toolId];
|
|
151
157
|
if (!pending) return false;
|
|
152
|
-
if (pending.request && pending.request.source
|
|
158
|
+
if (pending.request && ["mcp-import", "product-control"].indexOf(pending.request.source) >= 0 &&
|
|
153
159
|
(typeof pending.request.authorizeResponse !== "function" || !pending.request.authorizeResponse(ws))) return false;
|
|
154
|
-
if (pending.request && pending.request.source
|
|
160
|
+
if (pending.request && ["work-primary", "mate-schedule"].indexOf(pending.request.source) >= 0 && usersModule.isMultiUser() &&
|
|
155
161
|
(!ws || !ws._clayUser || ws._clayUser.id !== session.ownerId)) return false;
|
|
156
162
|
if (pending.mode === "yoke") {
|
|
157
163
|
try { yoke.userInput.normalizeAnswers(pending.request.questions, answers); }
|
|
@@ -213,6 +219,10 @@ function attachSessions(ctx) {
|
|
|
213
219
|
var setLatestVersion = ctx.setLatestVersion;
|
|
214
220
|
var loadContextSources = ctx.loadContextSources;
|
|
215
221
|
|
|
222
|
+
var sessionHistory = require("./project-session-history-actions").attachSessionHistoryActions(Object.assign({}, ctx, {
|
|
223
|
+
resolveSessionHome: resolveSessionHome, cancelPendingAskUser: cancelPendingAskUser,
|
|
224
|
+
}));
|
|
225
|
+
|
|
216
226
|
function canAccessProjectSlug(ws, targetSlug) {
|
|
217
227
|
if (!targetSlug) return false;
|
|
218
228
|
if (!usersModule || typeof usersModule.isMultiUser !== "function" || !usersModule.isMultiUser()) return true;
|
|
@@ -248,7 +258,7 @@ function attachSessions(ctx) {
|
|
|
248
258
|
if (!session || !session.cliSessionId) return;
|
|
249
259
|
if (session._titleWatcherStop) return; // already watching
|
|
250
260
|
var watcher;
|
|
251
|
-
try { watcher = require("./claude-jsonl-watcher"); } catch (e) { return; }
|
|
261
|
+
try { watcher = require("./yoke/claude-jsonl-watcher"); } catch (e) { return; }
|
|
252
262
|
var home = resolveSessionHome(session);
|
|
253
263
|
var jsonlPath = watcher.jsonlPathFor(home, cwd, session.cliSessionId);
|
|
254
264
|
if (!jsonlPath) return;
|
|
@@ -316,7 +326,7 @@ function attachSessions(ctx) {
|
|
|
316
326
|
// message right away. The transcript is small enough that a full
|
|
317
327
|
// re-send beats maintaining a delta protocol.
|
|
318
328
|
try {
|
|
319
|
-
var newIndex = require("./tui-transcript-index").readAssistantIndex(resolveSessionHome(s), cwd, s.cliSessionId);
|
|
329
|
+
var newIndex = require("./yoke/tui-transcript-index").readAssistantIndex(resolveSessionHome(s), cwd, s.cliSessionId);
|
|
320
330
|
send({
|
|
321
331
|
type: "tui_transcript_state",
|
|
322
332
|
id: s.localId,
|
|
@@ -440,10 +450,10 @@ function attachSessions(ctx) {
|
|
|
440
450
|
// Idempotent: if the PTY is already gone and history is populated, this
|
|
441
451
|
// is a no-op (avoids re-reading jsonl and re-writing the session file on
|
|
442
452
|
// every click while the user is reading the session in GUI mode).
|
|
443
|
-
function prepareTuiSessionForGuiView(session) {
|
|
453
|
+
function prepareTuiSessionForGuiView(session, options) {
|
|
444
454
|
if (!session || session.cliSessionId == null) return;
|
|
445
455
|
var cliSess;
|
|
446
|
-
try { cliSess = require("./cli-sessions"); } catch (e) { return; }
|
|
456
|
+
try { cliSess = require("./yoke/cli-sessions"); } catch (e) { return; }
|
|
447
457
|
// Re-read whenever the jsonl has changed since the last hydrate (a TUI
|
|
448
458
|
// turn appended messages), not just when history is empty. The earlier
|
|
449
459
|
// "already hydrated" short-circuit kept stale history after a
|
|
@@ -467,7 +477,7 @@ function attachSessions(ctx) {
|
|
|
467
477
|
try { tm.close(session.terminalId); } catch (e) {}
|
|
468
478
|
}
|
|
469
479
|
session.terminalId = null;
|
|
470
|
-
try { sm.saveSessionFile(session); } catch (e) {}
|
|
480
|
+
if (!options || !options.deferSave) { try { sm.saveSessionFile(session); } catch (e) {} }
|
|
471
481
|
}
|
|
472
482
|
|
|
473
483
|
// Resolve how a session should be presented to a viewer WITHOUT spawning a
|
|
@@ -593,7 +603,7 @@ function attachSessions(ctx) {
|
|
|
593
603
|
var from = sm.findTurnBoundary(session.history, Math.max(0, targetFrom));
|
|
594
604
|
var to = before;
|
|
595
605
|
var items = session.history.slice(from, to).map(function (item, index) {
|
|
596
|
-
return Object.assign({}, hydrateImageRefs(item), { _historyIndex: from + index });
|
|
606
|
+
return Object.assign({}, hydrateImageRefs(require("./yoke/usage").clientEvent(item, session.vendor || "claude")), { _historyIndex: from + index });
|
|
597
607
|
});
|
|
598
608
|
sendTo(ws, {
|
|
599
609
|
type: "history_prepend",
|
|
@@ -768,11 +778,10 @@ function attachSessions(ctx) {
|
|
|
768
778
|
sendTo(ws, { type: "error", text: "Cannot transfer ownership of home directory projects." });
|
|
769
779
|
return true;
|
|
770
780
|
}
|
|
771
|
-
var projectOwnerId =
|
|
772
|
-
var isAdmin = ws._clayUser && ws._clayUser.role === "admin";
|
|
781
|
+
var projectOwnerId = getProjectAccess().ownerId;
|
|
773
782
|
var isProjectOwner = ws._clayUser && projectOwnerId && ws._clayUser.id === projectOwnerId;
|
|
774
|
-
if (!ws._clayUser ||
|
|
775
|
-
sendTo(ws, { type: "error", text: "Only project owners
|
|
783
|
+
if (!ws._clayUser || !isProjectOwner) {
|
|
784
|
+
sendTo(ws, { type: "error", text: "Only project owners can transfer ownership." });
|
|
776
785
|
return true;
|
|
777
786
|
}
|
|
778
787
|
var targetUser = msg.userId ? usersModule.findUserById(msg.userId) : null;
|
|
@@ -780,11 +789,11 @@ function attachSessions(ctx) {
|
|
|
780
789
|
sendTo(ws, { type: "error", text: "User not found." });
|
|
781
790
|
return true;
|
|
782
791
|
}
|
|
792
|
+
// Persist before publishing runtime ownership.
|
|
793
|
+
if (!opts.onProjectOwnerChanged) return true;
|
|
794
|
+
var savedOwner = opts.onProjectOwnerChanged(slug, targetUser.id);
|
|
795
|
+
if (savedOwner && savedOwner.error) { sendTo(ws, { type: "error", text: savedOwner.error }); return true; }
|
|
783
796
|
setProjectOwnerId(targetUser.id);
|
|
784
|
-
// Persist via daemon callback
|
|
785
|
-
if (opts.onProjectOwnerChanged) {
|
|
786
|
-
opts.onProjectOwnerChanged(slug, targetUser.id);
|
|
787
|
-
}
|
|
788
797
|
send({ type: "project_owner_changed", ownerId: targetUser.id, ownerName: targetUser.displayName || targetUser.username });
|
|
789
798
|
return true;
|
|
790
799
|
}
|
|
@@ -801,6 +810,7 @@ function attachSessions(ctx) {
|
|
|
801
810
|
// broadcasts session_switched. A live runtime keeps its actual mode
|
|
802
811
|
// for every viewer; idle sessions open in chat. Nothing is spawned here.
|
|
803
812
|
var xmTarget = sm.sessions.get(msg.id);
|
|
813
|
+
if (usersModule.isMultiUser() && (!ws._clayUser || !usersModule.canAccessSession(ws._clayUser.id, xmTarget, getProjectAccess()))) return true;
|
|
804
814
|
if (xmTarget && (!xmTarget.vendor || vendorSupportsTui(xmTarget.vendor))) {
|
|
805
815
|
// Single source of truth: live runtime wins (tui stays tui, gui stays
|
|
806
816
|
// gui); idle legacy sessions hydrate their transcript into chat.
|
|
@@ -908,7 +918,7 @@ function attachSessions(ctx) {
|
|
|
908
918
|
return true;
|
|
909
919
|
}
|
|
910
920
|
try {
|
|
911
|
-
var tprIndex = require("./tui-transcript-index").readAssistantIndex(resolveSessionHome(tprSess), cwd, tprSess.cliSessionId);
|
|
921
|
+
var tprIndex = require("./yoke/tui-transcript-index").readAssistantIndex(resolveSessionHome(tprSess), cwd, tprSess.cliSessionId);
|
|
912
922
|
sendTo(ws, {
|
|
913
923
|
type: "tui_transcript_state",
|
|
914
924
|
id: tprId,
|
|
@@ -939,19 +949,7 @@ function attachSessions(ctx) {
|
|
|
939
949
|
var s = sm.sessions.get(msg.id);
|
|
940
950
|
if (usersModule.isMultiUser() && (!ws._clayUser ||
|
|
941
951
|
!usersModule.canAccessSession(ws._clayUser.id, s, getProjectAccess()))) return true;
|
|
942
|
-
s
|
|
943
|
-
s.titleProvisional = false;
|
|
944
|
-
s.titleAutoGenerated = false;
|
|
945
|
-
s.titleManuallySet = true;
|
|
946
|
-
sm.saveSessionFile(s);
|
|
947
|
-
sm.notifySessionRenamed(s.localId);
|
|
948
|
-
sm.broadcastSessionList();
|
|
949
|
-
// Sync title to SDK session
|
|
950
|
-
if (s.cliSessionId) {
|
|
951
|
-
adapter.renameSession(s.cliSessionId, s.title, { dir: cwd }).catch(function(e) {
|
|
952
|
-
console.error("[project] SDK renameSession failed:", e.message);
|
|
953
|
-
});
|
|
954
|
-
}
|
|
952
|
+
sessionActions.rename(s, String(msg.title).substring(0, 100));
|
|
955
953
|
}
|
|
956
954
|
return true;
|
|
957
955
|
}
|
|
@@ -1086,7 +1084,6 @@ function attachSessions(ctx) {
|
|
|
1086
1084
|
|
|
1087
1085
|
if (msg.type === "stop") {
|
|
1088
1086
|
var session = getSessionForWs(ws);
|
|
1089
|
-
if (autonomousRun && session && session.autonomousRun) autonomousRun.finish(session, "stopped", "Stopped by the user.");
|
|
1090
1087
|
stopSession(session, ws && ws._clayUser);
|
|
1091
1088
|
return true;
|
|
1092
1089
|
}
|
|
@@ -1102,14 +1099,14 @@ function attachSessions(ctx) {
|
|
|
1102
1099
|
var pid = msg.pid;
|
|
1103
1100
|
if (!pid || typeof pid !== "number") return true;
|
|
1104
1101
|
// Verify target is actually a claude process before killing
|
|
1105
|
-
if (!sdk.
|
|
1106
|
-
console.error("[project] Refused to kill PID " + pid + ": not a
|
|
1107
|
-
sendTo(ws, { type: "error", text: "Process " + pid + " is not a
|
|
1102
|
+
if (!sdk.isProviderProcess(pid, getSessionForWs(ws))) {
|
|
1103
|
+
console.error("[project] Refused to kill PID " + pid + ": not a verified provider process");
|
|
1104
|
+
sendTo(ws, { type: "error", text: "Process " + pid + " is not a verified provider process." });
|
|
1108
1105
|
return true;
|
|
1109
1106
|
}
|
|
1110
1107
|
try {
|
|
1111
1108
|
process.kill(pid, "SIGTERM");
|
|
1112
|
-
console.log("[project] Sent SIGTERM to conflicting
|
|
1109
|
+
console.log("[project] Sent SIGTERM to conflicting provider process PID " + pid);
|
|
1113
1110
|
sendTo(ws, { type: "process_killed", pid: pid });
|
|
1114
1111
|
} catch (e) {
|
|
1115
1112
|
console.error("[project] Failed to kill PID " + pid + ":", e.message);
|
|
@@ -1187,38 +1184,14 @@ function attachSessions(ctx) {
|
|
|
1187
1184
|
|
|
1188
1185
|
if (msg.type === "set_vendor" && msg.vendor) {
|
|
1189
1186
|
var vendorSession = getSessionForWs(ws);
|
|
1190
|
-
if (vendorSession)
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
if (alreadyBound) {
|
|
1197
|
-
console.warn("[project] set_vendor ignored: session " + vendorSession.localId +
|
|
1198
|
-
" is bound to '" + vendorSession.vendor + "', refused rebind to '" + msg.vendor + "'");
|
|
1199
|
-
} else {
|
|
1200
|
-
vendorSession.vendor = msg.vendor;
|
|
1201
|
-
if (vendorSession.permissionMode === "auto" && msg.vendor !== "claude") {
|
|
1202
|
-
vendorSession.permissionMode = "default";
|
|
1203
|
-
vendorSession.effectivePermissionMode = null;
|
|
1204
|
-
}
|
|
1205
|
-
if (vendorSession.vendor !== msg.vendor) vendorSession.model = "";
|
|
1206
|
-
sm.saveSessionFile(vendorSession);
|
|
1207
|
-
sm.broadcastSessionList();
|
|
1208
|
-
}
|
|
1209
|
-
}
|
|
1210
|
-
if (msg.vendor) {
|
|
1211
|
-
var vendorModels = (sm.modelsByVendor && sm.modelsByVendor[msg.vendor]) || [];
|
|
1212
|
-
sendTo(ws, {
|
|
1213
|
-
type: "model_info",
|
|
1214
|
-
model: "",
|
|
1215
|
-
models: vendorModels,
|
|
1216
|
-
vendor: msg.vendor, sessionId: vendorSession ? vendorSession.localId : null,
|
|
1217
|
-
availableVendors: sm.availableVendors || [],
|
|
1218
|
-
installedVendors: sm.installedVendors || [],
|
|
1219
|
-
});
|
|
1220
|
-
send({ type: "config_state", vendor: null, sessionId: null, mode: sm.currentPermissionMode || "default", effort: sm.currentEffort || "medium", betas: sm.currentBetas || [], thinking: sm.currentThinking || "adaptive", thinkingBudget: sm.currentThinkingBudget || 10000 });
|
|
1187
|
+
if (!vendorSession) return true;
|
|
1188
|
+
// Re-selecting the provider of an established thread does not rebind it.
|
|
1189
|
+
if (vendorSession.cliSessionId && vendorSession.vendor === msg.vendor) return true;
|
|
1190
|
+
try {
|
|
1191
|
+
var vendorResult = sessionVendor.setVendor(vendorSession, msg.vendor);
|
|
1192
|
+
if (vendorResult.status === "failed") sendTo(ws, { type: "error", text: vendorResult.error });
|
|
1221
1193
|
}
|
|
1194
|
+
catch (error) { sendTo(ws, { type: "error", text: error.message }); }
|
|
1222
1195
|
return true;
|
|
1223
1196
|
}
|
|
1224
1197
|
|
|
@@ -1286,15 +1259,7 @@ function attachSessions(ctx) {
|
|
|
1286
1259
|
if (!valid.ok) { permissionResult(false, valid.error, true); return true; }
|
|
1287
1260
|
(async function() {
|
|
1288
1261
|
try {
|
|
1289
|
-
|
|
1290
|
-
await autonomousRun.stopForPermissionChange(session);
|
|
1291
|
-
}
|
|
1292
|
-
await fullAccessService.setMode(session, valid.mode);
|
|
1293
|
-
var state = permissionModes.clientState(session, sm.currentPermissionMode, sm.defaultVendor);
|
|
1294
|
-
sm.sendToSession(session, { type: "config_state", sessionId: session.localId, vendor: session.vendor || sm.defaultVendor || "claude", mode: state.requestedPermissionMode,
|
|
1295
|
-
effectivePermissionMode: state.effectivePermissionMode, permissionRequestId: requestId,
|
|
1296
|
-
permissionCapabilities: state.permissionCapabilities, mcpPermissionModeOverrides: state.mcpPermissionModeOverrides,
|
|
1297
|
-
effort: session.effort || sm.currentEffort || "medium", betas: sm.currentBetas || [] });
|
|
1262
|
+
await sessionActions.permission(session, valid.mode, requestId);
|
|
1298
1263
|
permissionResult(true, null, true);
|
|
1299
1264
|
} catch (error) {
|
|
1300
1265
|
permissionResult(false, "Failed to change permission mode: " + (error.message || error), true);
|
|
@@ -1364,10 +1329,9 @@ function attachSessions(ctx) {
|
|
|
1364
1329
|
sendTo(ws, { type: "error", text: "This effort level is not supported by the selected vendor." });
|
|
1365
1330
|
return true;
|
|
1366
1331
|
}
|
|
1367
|
-
|
|
1332
|
+
sessionActions.effort(session, sessionEffort).catch(function (e) {
|
|
1368
1333
|
sendTo(ws, { type: "error", text: "Failed to change reasoning effort: " + (e.message || e) });
|
|
1369
1334
|
});
|
|
1370
|
-
sm.sendToSession(session, { type: "config_state", model: session.model || ((sm.defaultModelByVendor || {})[effortVendor]) || "", vendor: effortVendor, sessionId: session.localId, mode: session.permissionMode || sm.currentPermissionMode || "default", effort: sessionEffort, betas: sm.currentBetas || [], thinking: sm.currentThinking || "adaptive", thinkingBudget: sm.currentThinkingBudget || 10000 });
|
|
1371
1335
|
}
|
|
1372
1336
|
return true;
|
|
1373
1337
|
}
|
|
@@ -1403,20 +1367,12 @@ function attachSessions(ctx) {
|
|
|
1403
1367
|
return true;
|
|
1404
1368
|
}
|
|
1405
1369
|
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
if (msg.type === "set_codex_sandbox") {
|
|
1413
|
-
sm.codexSandbox = msg.sandbox || CODEX_DEFAULTS.sandbox;
|
|
1414
|
-
send(Object.assign({ type: "codex_config" }, getCodexConfig(sm)));
|
|
1415
|
-
return true;
|
|
1416
|
-
}
|
|
1417
|
-
if (msg.type === "set_codex_websearch") {
|
|
1418
|
-
sm.codexWebSearch = msg.webSearch || CODEX_DEFAULTS.webSearch;
|
|
1419
|
-
send(Object.assign({ type: "codex_config" }, getCodexConfig(sm)));
|
|
1370
|
+
var providerChange;
|
|
1371
|
+
try { providerChange = providerSettings.change(msg, sm); }
|
|
1372
|
+
catch (error) { sendTo(ws, { type: "error", text: error.message }); return true; }
|
|
1373
|
+
if (providerChange) {
|
|
1374
|
+
Object.keys(providerChange.patch).forEach(function(key) { sm[key] = providerChange.patch[key]; });
|
|
1375
|
+
providerChange.messages.forEach(function(message) { send(message); });
|
|
1420
1376
|
return true;
|
|
1421
1377
|
}
|
|
1422
1378
|
|
|
@@ -1444,92 +1400,9 @@ function attachSessions(ctx) {
|
|
|
1444
1400
|
sendTo(ws, { type: "rewind_error", text: "Rewind already in progress." });
|
|
1445
1401
|
return true;
|
|
1446
1402
|
}
|
|
1447
|
-
session.
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
(async function () {
|
|
1451
|
-
try {
|
|
1452
|
-
// File restoration (delegated to adapter via sdk-bridge)
|
|
1453
|
-
if (mode !== "chat") {
|
|
1454
|
-
await sdk.rewindExecuteFiles(session, msg.uuid);
|
|
1455
|
-
}
|
|
1456
|
-
|
|
1457
|
-
// Conversation rollback (skip for files-only mode)
|
|
1458
|
-
if (mode !== "files") {
|
|
1459
|
-
var targetIdx = -1;
|
|
1460
|
-
for (var i = 0; i < session.messageUUIDs.length; i++) {
|
|
1461
|
-
if (session.messageUUIDs[i].uuid === msg.uuid) {
|
|
1462
|
-
targetIdx = i;
|
|
1463
|
-
break;
|
|
1464
|
-
}
|
|
1465
|
-
}
|
|
1466
|
-
|
|
1467
|
-
// Codex's thread/revert cuts at a turn id, not a count. Read the
|
|
1468
|
-
// target entry's turnId BEFORE trimming local history below.
|
|
1469
|
-
var hasTurnsToRollBack = false;
|
|
1470
|
-
var targetTurnId = null;
|
|
1471
|
-
if (targetIdx >= 0) {
|
|
1472
|
-
targetTurnId = session.messageUUIDs[targetIdx].turnId || null;
|
|
1473
|
-
for (var ri = targetIdx; ri < session.messageUUIDs.length; ri++) {
|
|
1474
|
-
if (session.messageUUIDs[ri].type === "user") { hasTurnsToRollBack = true; break; }
|
|
1475
|
-
}
|
|
1476
|
-
}
|
|
1477
|
-
|
|
1478
|
-
// Notify adapter of conversation rollback (e.g. Codex thread/revert)
|
|
1479
|
-
// BEFORE trimming local history below. Deliberately not caught
|
|
1480
|
-
// here: a failed or impossible (missing turnId) provider-side
|
|
1481
|
-
// rollback must abort the whole rewind via the outer catch, never
|
|
1482
|
-
// leave local history trimmed ahead of the real provider thread.
|
|
1483
|
-
if (hasTurnsToRollBack) {
|
|
1484
|
-
await sdk.rollbackConversation(session, targetTurnId);
|
|
1485
|
-
}
|
|
1486
|
-
|
|
1487
|
-
if (targetIdx >= 0) {
|
|
1488
|
-
var trimTo = session.messageUUIDs[targetIdx].historyIndex;
|
|
1489
|
-
for (var k = trimTo - 1; k >= 0; k--) {
|
|
1490
|
-
if (session.history[k].type === "user_message") {
|
|
1491
|
-
trimTo = k;
|
|
1492
|
-
break;
|
|
1493
|
-
}
|
|
1494
|
-
}
|
|
1495
|
-
session.history = session.history.slice(0, trimTo);
|
|
1496
|
-
session.messageUUIDs = session.messageUUIDs.slice(0, targetIdx);
|
|
1497
|
-
// Reset digest checkpoint if it points past the trimmed history
|
|
1498
|
-
if (typeof session._dmLastDigestedIndex === "number" && session._dmLastDigestedIndex > trimTo) {
|
|
1499
|
-
session._dmLastDigestedIndex = trimTo;
|
|
1500
|
-
}
|
|
1501
|
-
}
|
|
1502
|
-
|
|
1503
|
-
var kept = session.messageUUIDs;
|
|
1504
|
-
session.lastRewindUuid = kept.length > 0 ? kept[kept.length - 1].uuid : null;
|
|
1505
|
-
}
|
|
1506
|
-
|
|
1507
|
-
if (session.abortController) {
|
|
1508
|
-
try { session.abortController.abort(); } catch (e) {}
|
|
1509
|
-
}
|
|
1510
|
-
if (session.messageQueue) {
|
|
1511
|
-
try { session.messageQueue.end(); } catch (e) {}
|
|
1512
|
-
}
|
|
1513
|
-
session.queryInstance = null;
|
|
1514
|
-
session.messageQueue = null;
|
|
1515
|
-
session.abortController = null;
|
|
1516
|
-
session.blocks = {};
|
|
1517
|
-
session.sentToolResults = {};
|
|
1518
|
-
session.pendingPermissions = {};
|
|
1519
|
-
cancelPendingAskUser(session, "Conversation rewound");
|
|
1520
|
-
session.isProcessing = false;
|
|
1521
|
-
onProcessingChanged();
|
|
1522
|
-
|
|
1523
|
-
sm.saveSessionFile(session);
|
|
1524
|
-
sm.switchSession(session.localId, ws, hydrateImageRefs);
|
|
1525
|
-
sm.sendAndRecord(session, { type: "rewind_complete", mode: mode });
|
|
1526
|
-
sm.broadcastSessionList();
|
|
1527
|
-
} catch (err) {
|
|
1528
|
-
sendTo(ws, { type: "rewind_error", text: "Rewind failed: " + err.message });
|
|
1529
|
-
} finally {
|
|
1530
|
-
session._rewindInProgress = false;
|
|
1531
|
-
}
|
|
1532
|
-
})();
|
|
1403
|
+
sessionHistory.rewind(session, msg.uuid, msg.mode || "both", ws, function () {}).catch(function (error) {
|
|
1404
|
+
sendTo(ws, { type: "rewind_error", text: "Rewind failed: " + error.message });
|
|
1405
|
+
});
|
|
1533
1406
|
return true;
|
|
1534
1407
|
}
|
|
1535
1408
|
|
|
@@ -1539,62 +1412,10 @@ function attachSessions(ctx) {
|
|
|
1539
1412
|
sendTo(ws, { type: "error", text: "Cannot fork: no CLI session" });
|
|
1540
1413
|
return true;
|
|
1541
1414
|
}
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
// Copy local history up to the target UUID
|
|
1547
|
-
var targetIdx = -1;
|
|
1548
|
-
for (var fi = 0; fi < session.messageUUIDs.length; fi++) {
|
|
1549
|
-
if (session.messageUUIDs[fi].uuid === msg.uuid) { targetIdx = fi; break; }
|
|
1550
|
-
}
|
|
1551
|
-
var forkHistory = [];
|
|
1552
|
-
if (targetIdx >= 0) {
|
|
1553
|
-
var trimTo = session.messageUUIDs[targetIdx].historyIndex;
|
|
1554
|
-
forkHistory = session.history.slice(0, trimTo);
|
|
1555
|
-
} else {
|
|
1556
|
-
forkHistory = session.history.slice();
|
|
1557
|
-
}
|
|
1558
|
-
var forked = sm.createSession({
|
|
1559
|
-
vendor: session.vendor,
|
|
1560
|
-
ownerId: session.ownerId || null,
|
|
1561
|
-
userKnowledgeOwnerId: session.userKnowledgeOwnerId || null,
|
|
1562
|
-
sessionVisibility: sessionVisibility.normalize(session.sessionVisibility),
|
|
1563
|
-
}, ws);
|
|
1564
|
-
forked.cliSessionId = result.sessionId;
|
|
1565
|
-
forked.title = forkTitle;
|
|
1566
|
-
forked.history = forkHistory;
|
|
1567
|
-
forked.messageUUIDs = [];
|
|
1568
|
-
for (var hi = 0; hi < forkHistory.length; hi++) {
|
|
1569
|
-
if (forkHistory[hi].type === "message_uuid") {
|
|
1570
|
-
forked.messageUUIDs.push({ uuid: forkHistory[hi].uuid, type: forkHistory[hi].messageType, historyIndex: hi, turnId: forkHistory[hi].turnId || null });
|
|
1571
|
-
}
|
|
1572
|
-
}
|
|
1573
|
-
sm.saveSessionFile(forked);
|
|
1574
|
-
sm.switchSession(forked.localId, ws, hydrateImageRefs);
|
|
1575
|
-
sendTo(ws, { type: "fork_complete", sessionId: forked.localId });
|
|
1576
|
-
sm.broadcastSessionList();
|
|
1577
|
-
} else {
|
|
1578
|
-
// Read history from CLI session files
|
|
1579
|
-
var cliSess = require("./cli-sessions");
|
|
1580
|
-
return cliSess.readCliSessionHistory(resolveSessionHome(session), cwd, result.sessionId).then(function(history) {
|
|
1581
|
-
var forked = sm.resumeSession(result.sessionId, {
|
|
1582
|
-
history: history,
|
|
1583
|
-
title: forkTitle,
|
|
1584
|
-
vendor: session.vendor,
|
|
1585
|
-
ownerId: session.ownerId || null,
|
|
1586
|
-
userKnowledgeOwnerId: session.userKnowledgeOwnerId || null,
|
|
1587
|
-
sessionVisibility: sessionVisibility.normalize(session.sessionVisibility),
|
|
1588
|
-
sessionVisibilityExplicit: session.sessionVisibilityExplicit === true,
|
|
1589
|
-
}, ws);
|
|
1590
|
-
if (forked) {
|
|
1591
|
-
ws._clayActiveSession = forked.localId;
|
|
1592
|
-
sendTo(ws, { type: "fork_complete", sessionId: forked.localId });
|
|
1593
|
-
}
|
|
1594
|
-
});
|
|
1595
|
-
}
|
|
1596
|
-
}).catch(function(e) {
|
|
1597
|
-
sendTo(ws, { type: "error", text: "Fork failed: " + (e.message || e) });
|
|
1415
|
+
sessionHistory.fork(session, msg.uuid, ws, function () {}).then(function (forked) {
|
|
1416
|
+
if (forked) sendTo(ws, { type: "fork_complete", sessionId: forked.localId });
|
|
1417
|
+
}).catch(function (error) {
|
|
1418
|
+
sendTo(ws, { type: "error", text: "Fork failed: " + error.message });
|
|
1598
1419
|
});
|
|
1599
1420
|
return true;
|
|
1600
1421
|
}
|
|
@@ -1809,14 +1630,6 @@ function attachSessions(ctx) {
|
|
|
1809
1630
|
if (msg.type === "browse_dir") {
|
|
1810
1631
|
var rawPath = (msg.path || "").replace(/^~/, require("./config").REAL_HOME);
|
|
1811
1632
|
var absTarget = path.resolve(rawPath);
|
|
1812
|
-
// Multi-user mode: non-admins can only browse their home directory
|
|
1813
|
-
if (osUsers && osUsers.length > 0 && ws._clayUser && ws._clayUser.role !== "admin") {
|
|
1814
|
-
var browseHome = ws._clayUser.linuxUser ? "/home/" + ws._clayUser.linuxUser : null;
|
|
1815
|
-
if (!browseHome || (absTarget !== browseHome && (absTarget + "/").indexOf(browseHome + "/") !== 0)) {
|
|
1816
|
-
sendTo(ws, { type: "browse_dir_result", path: msg.path, entries: [], error: "Access restricted to your home directory" });
|
|
1817
|
-
return true;
|
|
1818
|
-
}
|
|
1819
|
-
}
|
|
1820
1633
|
var parentDir, prefix;
|
|
1821
1634
|
try {
|
|
1822
1635
|
var stat = fs.statSync(absTarget);
|
|
@@ -1833,12 +1646,17 @@ function attachSessions(ctx) {
|
|
|
1833
1646
|
parentDir = path.dirname(absTarget);
|
|
1834
1647
|
prefix = path.basename(absTarget).toLowerCase();
|
|
1835
1648
|
}
|
|
1649
|
+
if (opts.canUsePath && !opts.canUsePath(ws._clayUser && ws._clayUser.id, parentDir)) {
|
|
1650
|
+
sendTo(ws, { type: "browse_dir_result", entries: [], error: "Directory access is not permitted" });
|
|
1651
|
+
return true;
|
|
1652
|
+
}
|
|
1836
1653
|
try {
|
|
1837
1654
|
var dirItems = fs.readdirSync(parentDir, { withFileTypes: true });
|
|
1838
1655
|
var dirEntries = [];
|
|
1839
1656
|
for (var di = 0; di < dirItems.length; di++) {
|
|
1840
1657
|
var d = dirItems[di];
|
|
1841
1658
|
if (!d.isDirectory()) continue;
|
|
1659
|
+
if (opts.canUsePath && !opts.canUsePath(ws._clayUser && ws._clayUser.id, path.join(parentDir, d.name))) continue;
|
|
1842
1660
|
if (d.name.charAt(0) === ".") continue;
|
|
1843
1661
|
if (IGNORED_DIRS.has(d.name)) continue;
|
|
1844
1662
|
if (prefix && !d.name.toLowerCase().startsWith(prefix)) continue;
|
|
@@ -1856,17 +1674,9 @@ function attachSessions(ctx) {
|
|
|
1856
1674
|
if (msg.type === "add_project") {
|
|
1857
1675
|
var addPath = (msg.path || "").replace(/^~/, require("./config").REAL_HOME);
|
|
1858
1676
|
var addAbs = path.resolve(addPath);
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
sendTo(ws, { type: "add_project_result", ok: false, error: "No Linux user assigned" });
|
|
1863
|
-
return true;
|
|
1864
|
-
}
|
|
1865
|
-
var userHome = "/home/" + ws._clayUser.linuxUser;
|
|
1866
|
-
if (addAbs !== userHome && (addAbs + "/").indexOf(userHome + "/") !== 0) {
|
|
1867
|
-
sendTo(ws, { type: "add_project_result", ok: false, error: "Path not allowed. You can only add directories under " + userHome });
|
|
1868
|
-
return true;
|
|
1869
|
-
}
|
|
1677
|
+
if (opts.canUsePath && !opts.canUsePath(ws._clayUser && ws._clayUser.id, addAbs)) {
|
|
1678
|
+
sendTo(ws, { type: "add_project_result", ok: false, error: "Directory access is not permitted" });
|
|
1679
|
+
return true;
|
|
1870
1680
|
}
|
|
1871
1681
|
try {
|
|
1872
1682
|
var addStat = fs.statSync(addAbs);
|
|
@@ -1880,7 +1690,7 @@ function attachSessions(ctx) {
|
|
|
1880
1690
|
}
|
|
1881
1691
|
if (typeof opts.onAddProject === "function") {
|
|
1882
1692
|
var result = opts.onAddProject(addAbs, ws._clayUser);
|
|
1883
|
-
sendTo(ws, {
|
|
1693
|
+
sendTo(ws, Object.assign({}, result, { type: "add_project_result" }));
|
|
1884
1694
|
} else {
|
|
1885
1695
|
sendTo(ws, { type: "add_project_result", ok: false, error: "Not supported" });
|
|
1886
1696
|
}
|
|
@@ -1905,7 +1715,7 @@ function attachSessions(ctx) {
|
|
|
1905
1715
|
}
|
|
1906
1716
|
if (typeof opts.onCreateProject === "function") {
|
|
1907
1717
|
var createResult = opts.onCreateProject(createName, ws._clayUser);
|
|
1908
|
-
sendTo(ws, {
|
|
1718
|
+
sendTo(ws, Object.assign({}, createResult, { type: "add_project_result" }));
|
|
1909
1719
|
} else {
|
|
1910
1720
|
sendTo(ws, { type: "add_project_result", ok: false, error: "Not supported" });
|
|
1911
1721
|
}
|
|
@@ -1995,10 +1805,10 @@ function attachSessions(ctx) {
|
|
|
1995
1805
|
moveAllSchedulesToProject(removeSlug, msg.moveTasksTo);
|
|
1996
1806
|
}
|
|
1997
1807
|
if (typeof opts.onRemoveProject === "function") {
|
|
1998
|
-
// Send result before removing so the WS is still open
|
|
1999
|
-
sendTo(ws, { type: "remove_project_result", ok: true, slug: removeSlug });
|
|
2000
1808
|
var removeUserId = ws._clayUser ? ws._clayUser.id : null;
|
|
2001
|
-
opts.onRemoveProject(removeSlug, removeUserId);
|
|
1809
|
+
Promise.resolve().then(function () { return opts.onRemoveProject(removeSlug, removeUserId); }).then(function (result) {
|
|
1810
|
+
sendTo(ws, { type: "remove_project_result", ok: !!result && result.ok === true, slug: removeSlug, error: result && result.error });
|
|
1811
|
+
}, function (error) { sendTo(ws, { type: "remove_project_result", ok: false, slug: removeSlug, error: error.message }); });
|
|
2002
1812
|
} else {
|
|
2003
1813
|
sendTo(ws, { type: "remove_project_result", ok: false, error: "Not supported" });
|
|
2004
1814
|
}
|
|
@@ -2044,7 +1854,7 @@ function attachSessions(ctx) {
|
|
|
2044
1854
|
}
|
|
2045
1855
|
if (typeof opts.onSetProjectTitle === "function") {
|
|
2046
1856
|
var titleResult = opts.onSetProjectTitle(msg.slug, msg.title || null);
|
|
2047
|
-
sendTo(ws, { type: "set_project_title_result",
|
|
1857
|
+
sendTo(ws, Object.assign({}, titleResult, { type: "set_project_title_result", slug: msg.slug }));
|
|
2048
1858
|
} else {
|
|
2049
1859
|
sendTo(ws, { type: "set_project_title_result", ok: false, error: "Not supported" });
|
|
2050
1860
|
}
|
|
@@ -2106,7 +1916,7 @@ function attachSessions(ctx) {
|
|
|
2106
1916
|
var galManaged = [];
|
|
2107
1917
|
var galUser = [];
|
|
2108
1918
|
try {
|
|
2109
|
-
var galInstaller = require("./claude-hook-installer");
|
|
1919
|
+
var galInstaller = require("./yoke/claude-hook-installer");
|
|
2110
1920
|
galManaged = galInstaller.CLAY_MANAGED_ALLOW || [];
|
|
2111
1921
|
} catch (e) {}
|
|
2112
1922
|
if (galUid) {
|
|
@@ -2132,7 +1942,7 @@ function attachSessions(ctx) {
|
|
|
2132
1942
|
// home (OS-mode: getent passwd; single-user: os.homedir()).
|
|
2133
1943
|
var salEffective = false;
|
|
2134
1944
|
try {
|
|
2135
|
-
var salInstaller = require("./claude-hook-installer");
|
|
1945
|
+
var salInstaller = require("./yoke/claude-hook-installer");
|
|
2136
1946
|
var salHome = null;
|
|
2137
1947
|
if (osUsers && ws._clayUser && ws._clayUser.linuxUser) {
|
|
2138
1948
|
try {
|
|
@@ -2198,6 +2008,11 @@ function attachSessions(ctx) {
|
|
|
2198
2008
|
return handleSessionsMessage(ws, { type: "permission_response", requestId: msg.requestId, decision: msg.decision });
|
|
2199
2009
|
},
|
|
2200
2010
|
stopHomeSession: stopSession,
|
|
2011
|
+
sessionActions: sessionActions,
|
|
2012
|
+
sessionHistoryActions: sessionHistory,
|
|
2013
|
+
sessionVendorActions: sessionVendor,
|
|
2014
|
+
sessionRuntimeActions: sessionRuntime,
|
|
2015
|
+
deleteProductControlPlan: function (plan) { sessionDelete.executeOwnedDeletion(plan); },
|
|
2201
2016
|
};
|
|
2202
2017
|
}
|
|
2203
2018
|
|