clay-server 5.1.0-beta.1 → 6.0.0-beta.1
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/LICENSES/README.md +4 -0
- package/LICENSES/dicebear-avatars.md +16 -0
- package/bin/cli.js +9 -91
- package/lib/daemon-project-access.js +7 -1
- package/lib/daemon-settings.js +11 -0
- package/lib/daemon.js +10 -37
- package/lib/dicebear-avatar.js +62 -0
- package/lib/knowledge-db-service.js +175 -0
- package/lib/knowledge-db-validation.js +0 -0
- package/lib/knowledge-document-index.js +49 -0
- package/lib/knowledge-draft-store.js +56 -0
- package/lib/knowledge-file-store.js +199 -0
- package/lib/knowledge-markdown.js +128 -0
- package/lib/linear-store.js +35 -8
- package/lib/mate-knowledge-mcp-server.js +29 -6
- package/lib/mate-knowledge-service.js +23 -2
- package/lib/mate-knowledge-sync.js +36 -21
- package/lib/mate-workspace-color.js +4 -0
- package/lib/mates.js +18 -0
- package/lib/os-users.js +5 -16
- package/lib/project-connection.js +36 -9
- package/lib/project-debate-proposal.js +6 -0
- package/lib/project-debate-workspace.js +43 -0
- package/lib/project-debate.js +1 -0
- package/lib/project-filesystem.js +21 -39
- package/lib/project-knowledge.js +315 -136
- package/lib/project-mate-creation-proposal.js +1 -0
- package/lib/project-mate-interaction.js +2 -2
- package/lib/project-server-settings.js +31 -0
- package/lib/project-sessions.js +92 -182
- package/lib/project-user-knowledge.js +60 -0
- package/lib/project.js +59 -18
- package/lib/public/app.js +79 -71
- package/lib/public/clay-studio-wordmark-bold.svg +20 -0
- package/lib/public/css/clay-workspace.css +105 -0
- package/lib/public/css/debates-workbench.css +47 -0
- package/lib/public/css/home-debate-live.css +26 -0
- package/lib/public/css/home-hub.css +3 -1
- package/lib/public/css/home-mate-model-picker.css +15 -131
- package/lib/public/css/home-mate-settings.css +168 -16
- package/lib/public/css/icon-strip.css +249 -32
- package/lib/public/css/input.css +3 -1
- package/lib/public/css/mate-conversation.css +176 -0
- package/lib/public/css/mate-knowledge-workbench.css +118 -0
- package/lib/public/css/mate-workspace-color.css +16 -0
- package/lib/public/css/mates.css +28 -28
- package/lib/public/css/messages.css +1 -114
- package/lib/public/css/mobile-nav.css +16 -16
- package/lib/public/css/overlays.css +8 -0
- package/lib/public/css/project-logs.css +7 -0
- package/lib/public/css/project-mate-creation.css +105 -0
- package/lib/public/css/project-settings-modal.css +56 -0
- package/lib/public/css/right-workbench.css +3 -3
- package/lib/public/css/server-settings-system.css +125 -0
- package/lib/public/css/session-folders.css +80 -21
- package/lib/public/css/session-github.css +25 -16
- package/lib/public/css/settings-ui.css +247 -22
- package/lib/public/css/shared-browser.css +2 -0
- package/lib/public/css/sidebar.css +183 -107
- package/lib/public/css/title-bar.css +156 -1
- package/lib/public/css/worker-delegation.css +47 -0
- package/lib/public/css/worker-proposal.css +72 -150
- package/lib/public/css/you.css +74 -0
- package/lib/public/index.html +203 -547
- package/lib/public/modules/account-island.js +232 -0
- package/lib/public/modules/admin-settings-email.js +197 -0
- package/lib/public/modules/admin-settings-invitations.js +183 -0
- package/lib/public/modules/admin-settings-projects.js +198 -0
- package/lib/public/modules/admin-settings-ui.js +100 -0
- package/lib/public/modules/admin-settings-users.js +331 -0
- package/lib/public/modules/admin.js +19 -949
- package/lib/public/modules/app-cursors.js +19 -43
- package/lib/public/modules/app-debate-ui.js +4 -3
- package/lib/public/modules/app-dm.js +0 -2
- package/lib/public/modules/app-header.js +106 -0
- package/lib/public/modules/app-message-router.js +12 -3
- package/lib/public/modules/app-messages.js +47 -61
- package/lib/public/modules/app-notifications.js +4 -7
- package/lib/public/modules/app-projects.js +5 -38
- package/lib/public/modules/app-rate-limit.js +70 -12
- package/lib/public/modules/app-rendering.js +12 -13
- package/lib/public/modules/avatar.js +25 -9
- package/lib/public/modules/chat-render-runtime.js +16 -4
- package/lib/public/modules/command-palette.js +2 -15
- package/lib/public/modules/context-sources.js +27 -18
- package/lib/public/modules/cursor-sharing-preference.js +3 -3
- package/lib/public/modules/debate-controls.js +299 -0
- package/lib/public/modules/debate.js +2 -0
- package/lib/public/modules/debates-workbench.js +339 -0
- package/lib/public/modules/default-ai.js +88 -65
- package/lib/public/modules/header-tui-font.js +10 -20
- package/lib/public/modules/home-debate-controls.js +18 -289
- package/lib/public/modules/home-mate-avatar-editor.js +228 -0
- package/lib/public/modules/home-mate-color-picker.js +66 -0
- package/lib/public/modules/home-mate-creation.js +1 -1
- package/lib/public/modules/home-mate-instructions.js +90 -0
- package/lib/public/modules/home-mate-model-picker.js +80 -271
- package/lib/public/modules/home-mate-profile-editor.js +72 -0
- package/lib/public/modules/home-mate-settings.js +118 -125
- package/lib/public/modules/home-shell.js +3 -0
- package/lib/public/modules/home-surface-boot.js +1 -9
- package/lib/public/modules/icon-strip-tooltip.js +95 -0
- package/lib/public/modules/knowledge-db-edit-state.js +35 -0
- package/lib/public/modules/knowledge-db-workbench.js +234 -0
- package/lib/public/modules/knowledge-history-state.js +55 -0
- package/lib/public/modules/knowledge-markdown-editor.js +57 -0
- package/lib/public/modules/knowledge-preview-links.js +70 -0
- package/lib/public/modules/knowledge-workbench-shell.js +21 -0
- package/lib/public/modules/linear-settings.js +3 -2
- package/lib/public/modules/markdown.js +5 -6
- package/lib/public/modules/mate-knowledge-workbench-state.js +174 -0
- package/lib/public/modules/mate-knowledge-workbench.js +500 -0
- package/lib/public/modules/mate-knowledge.js +11 -3
- package/lib/public/modules/mate-management.js +54 -10
- package/lib/public/modules/mate-sidebar.js +11 -1
- package/lib/public/modules/mate-workspace-color.js +33 -0
- package/lib/public/modules/mention.js +5 -13
- package/lib/public/modules/notification-settings.js +81 -0
- package/lib/public/modules/notifications.js +44 -28
- package/lib/public/modules/personal-settings-dialog.js +51 -0
- package/lib/public/modules/profile.js +66 -24
- package/lib/public/modules/project-chat-identity.js +79 -0
- package/lib/public/modules/project-debate-planning-workspace.js +250 -0
- package/lib/public/modules/project-debate-workspace.js +185 -0
- package/lib/public/modules/project-header-identity.js +127 -0
- package/lib/public/modules/project-logs-render.js +9 -4
- package/lib/public/modules/project-mate-creation-workspace.js +414 -0
- package/lib/public/modules/project-mate-navigation.js +238 -0
- package/lib/public/modules/project-session-navigation.js +29 -0
- package/lib/public/modules/project-settings-environment.js +106 -0
- package/lib/public/modules/project-settings.js +72 -628
- package/lib/public/modules/project-switcher.js +6 -8
- package/lib/public/modules/projects-hub-view.js +5 -6
- package/lib/public/modules/projects-hub.js +2 -6
- package/lib/public/modules/qrcode.js +27 -8
- package/lib/public/modules/search-clay-chat.js +8 -11
- package/lib/public/modules/server-settings-controls.js +316 -0
- package/lib/public/modules/server-settings-environment.js +194 -0
- package/lib/public/modules/server-settings-navigation.js +39 -0
- package/lib/public/modules/server-settings-state.js +54 -0
- package/lib/public/modules/server-settings.js +116 -788
- package/lib/public/modules/server-storage-settings.js +84 -0
- package/lib/public/modules/session-activity-tooltip.js +12 -0
- package/lib/public/modules/session-create-form.js +86 -14
- package/lib/public/modules/session-create.js +50 -7
- package/lib/public/modules/session-github.js +2 -1
- package/lib/public/modules/session-list-visibility.js +41 -0
- package/lib/public/modules/session-presence-ui.js +3 -0
- package/lib/public/modules/session-title-marquee.js +51 -0
- package/lib/public/modules/settings-env-values.js +34 -0
- package/lib/public/modules/settings-select.js +121 -0
- package/lib/public/modules/shared-browser.js +10 -7
- package/lib/public/modules/sidebar-mates.js +3 -50
- package/lib/public/modules/sidebar-mobile.js +63 -60
- package/lib/public/modules/sidebar-projects.js +84 -97
- package/lib/public/modules/sidebar-session-folders-ui.js +2 -2
- package/lib/public/modules/sidebar-session-hierarchy.js +149 -70
- package/lib/public/modules/sidebar-sessions.js +49 -50
- package/lib/public/modules/sidebar.js +11 -13
- package/lib/public/modules/stt-picker.js +14 -5
- package/lib/public/modules/terminal-prefs.js +22 -0
- package/lib/public/modules/theme.js +88 -52
- package/lib/public/modules/thinking-lifecycle.js +7 -6
- package/lib/public/modules/tool-palette-order.js +4 -3
- package/lib/public/modules/tool-palette.js +13 -3
- package/lib/public/modules/tools.js +22 -22
- package/lib/public/modules/user-settings-account.js +318 -0
- package/lib/public/modules/user-settings-appearance.js +55 -0
- package/lib/public/modules/user-settings-editors.js +24 -0
- package/lib/public/modules/user-settings-integrations.js +104 -0
- package/lib/public/modules/user-settings-layout.js +80 -0
- package/lib/public/modules/user-settings-preferences.js +48 -0
- package/lib/public/modules/user-settings-profile.js +53 -0
- package/lib/public/modules/user-settings-themes.js +38 -0
- package/lib/public/modules/user-settings-voice.js +75 -0
- package/lib/public/modules/user-settings.js +53 -480
- package/lib/public/modules/worker-proposal.js +42 -8
- package/lib/public/modules/worker-runtime-picker.js +115 -0
- package/lib/public/modules/you-activity.js +121 -0
- package/lib/public/modules/you.js +251 -0
- package/lib/public/style.css +20 -8
- package/lib/public/vendor/codemirror/LICENSE +21 -0
- package/lib/public/vendor/codemirror/codemirror.css +344 -0
- package/lib/public/vendor/codemirror/codemirror.js +9884 -0
- package/lib/public/vendor/codemirror/continuelist.js +101 -0
- package/lib/public/vendor/codemirror/markdown.js +886 -0
- package/lib/public/vendor/codemirror/show-hint.css +37 -0
- package/lib/public/vendor/codemirror/show-hint.js +523 -0
- package/lib/sdk-bridge.js +15 -3
- package/lib/sdk-message-processor.js +16 -4
- package/lib/sdk-quota-snapshot.js +37 -0
- package/lib/server-admin.js +11 -0
- package/lib/server-appearance-settings.js +71 -0
- package/lib/server-home-chat-events.js +1 -1
- package/lib/server-home-chat.js +25 -3
- package/lib/server-home-debate-planning.js +4 -1
- package/lib/server-home-debates.js +2 -0
- package/lib/server-home-mate-creation.js +4 -1
- package/lib/server-linear.js +14 -8
- package/lib/server-mate-instructions.js +44 -0
- package/lib/server-mates.js +12 -3
- package/lib/server-notification-settings.js +67 -0
- package/lib/server-palette.js +1 -1
- package/lib/server-settings.js +69 -1
- package/lib/server-storage.js +56 -0
- package/lib/server-user-knowledge.js +119 -0
- package/lib/server.js +31 -3
- package/lib/session-visibility.js +7 -2
- package/lib/sessions.js +34 -9
- package/lib/startup-projects.js +40 -0
- package/lib/storage-usage.js +127 -0
- package/lib/user-knowledge-service.js +167 -0
- package/lib/users-permissions.js +7 -0
- package/lib/users-preferences.js +0 -74
- package/lib/users-project-mate-filter-preferences.js +39 -0
- package/lib/users.js +6 -4
- package/lib/workspace-query-access.js +4 -1
- package/lib/ws-schema.js +52 -4
- package/lib/yoke/adapters/codex.js +8 -45
- package/lib/yoke/codex-model-catalog.js +40 -0
- package/lib/yoke/codex-rate-limits.js +26 -0
- package/package.json +12 -3
|
@@ -23,7 +23,7 @@ var config = require("./config");
|
|
|
23
23
|
var knowledgeImport = require("./knowledge-import");
|
|
24
24
|
|
|
25
25
|
var SOURCE_NAMESPACE = "mate-knowledge";
|
|
26
|
-
var SOURCE_VERSION =
|
|
26
|
+
var SOURCE_VERSION = 3;
|
|
27
27
|
var MAX_SOURCE_BYTES = 64 * 1024 * 1024;
|
|
28
28
|
|
|
29
29
|
// Identity and template artifacts. Never mirrored, at any version.
|
|
@@ -84,6 +84,13 @@ function sourceKindFor(name) {
|
|
|
84
84
|
return null;
|
|
85
85
|
}
|
|
86
86
|
|
|
87
|
+
function safeSourceName(value) {
|
|
88
|
+
var name = String(value || "").replace(/\\/g, "/").replace(/^\.\//, ""); var parts = name.split("/");
|
|
89
|
+
if (!name || name[0] === "/" || name.length > 500 || parts.length > 20) return null;
|
|
90
|
+
if (parts.some(function (part) { return !part || part === "." || part === ".." || part.length > 120; })) return null;
|
|
91
|
+
return parts.join("/");
|
|
92
|
+
}
|
|
93
|
+
|
|
87
94
|
function isJournal(name) {
|
|
88
95
|
return !!JOURNAL_KINDS[name] || /\.jsonl$/.test(name);
|
|
89
96
|
}
|
|
@@ -133,9 +140,8 @@ function isMateSourceEntry(entry) {
|
|
|
133
140
|
if (typeof source.sourceVersion !== "number" || source.sourceVersion > SOURCE_VERSION) return false;
|
|
134
141
|
var name = source.fileName;
|
|
135
142
|
if (!name || typeof name !== "string") return false;
|
|
136
|
-
// The
|
|
137
|
-
|
|
138
|
-
if (path.basename(name) !== name) return false;
|
|
143
|
+
// The source name and relative path must agree, so neither field alone is trusted.
|
|
144
|
+
if (safeSourceName(name) !== name) return false;
|
|
139
145
|
return source.relPath === relativeSource(name);
|
|
140
146
|
}
|
|
141
147
|
|
|
@@ -180,6 +186,18 @@ function readStat(filePath) {
|
|
|
180
186
|
}
|
|
181
187
|
}
|
|
182
188
|
|
|
189
|
+
function safeKnowledgeFileStat(root, name) {
|
|
190
|
+
var parts = name.split("/"); var cursor = root;
|
|
191
|
+
for (var i = 0; i < parts.length; i++) {
|
|
192
|
+
cursor = path.join(cursor, parts[i]); var stat;
|
|
193
|
+
try { stat = fs.lstatSync(cursor); } catch (error) { if (error.code === "ENOENT") return null; throw error; }
|
|
194
|
+
if (stat.isSymbolicLink()) throw new Error("Knowledge source contains a symbolic link.");
|
|
195
|
+
if (i < parts.length - 1 && !stat.isDirectory()) throw new Error("Knowledge source path is not a directory.");
|
|
196
|
+
if (i === parts.length - 1 && !stat.isFile()) return null;
|
|
197
|
+
}
|
|
198
|
+
return fs.statSync(cursor);
|
|
199
|
+
}
|
|
200
|
+
|
|
183
201
|
// --- File-addressed sources -------------------------------------------
|
|
184
202
|
|
|
185
203
|
function syncFile(importer, scope, name, kind, filePath, stat, actor, summary) {
|
|
@@ -361,8 +379,8 @@ function syncMateSource(opts) {
|
|
|
361
379
|
var scope = options.scope || scopeForMateDir(options.mateDir);
|
|
362
380
|
if (!scope) return summary;
|
|
363
381
|
if (options.mateName) scope.mateName = options.mateName;
|
|
364
|
-
var
|
|
365
|
-
if (!name) return summary;
|
|
382
|
+
var rawName = String(options.fileName || ""); var name = safeSourceName(rawName);
|
|
383
|
+
if (!name) { if (rawName) summary.unsupported++; return summary; }
|
|
366
384
|
if (IDENTITY_FILES.indexOf(name) !== -1) { summary.identitySkipped++; return summary; }
|
|
367
385
|
if (EXCLUDED_FILES.indexOf(name) !== -1) { summary.excluded++; return summary; }
|
|
368
386
|
var kind = sourceKindFor(name);
|
|
@@ -370,8 +388,8 @@ function syncMateSource(opts) {
|
|
|
370
388
|
|
|
371
389
|
var actor = normalizeActor(options.actor);
|
|
372
390
|
var importer = options.importer || importerFor(scope, options.baseDir);
|
|
373
|
-
var
|
|
374
|
-
var stat =
|
|
391
|
+
var knowledgeRoot = path.join(scope.mateDir, "knowledge"); var filePath = path.join(knowledgeRoot, name);
|
|
392
|
+
var stat = safeKnowledgeFileStat(knowledgeRoot, name);
|
|
375
393
|
|
|
376
394
|
if (isJournal(name)) syncJournal(importer, scope, name, kind, filePath, stat, actor, summary);
|
|
377
395
|
else syncFile(importer, scope, name, kind, filePath, stat, actor, summary);
|
|
@@ -382,19 +400,11 @@ function syncMateSource(opts) {
|
|
|
382
400
|
return summary;
|
|
383
401
|
}
|
|
384
402
|
|
|
385
|
-
// Alias that reads better at a call site reacting to a removal or a rewrite.
|
|
386
403
|
function reconcileSource(opts) {
|
|
387
404
|
return syncMateSource(opts);
|
|
388
405
|
}
|
|
389
406
|
|
|
390
407
|
// Reconcile every durable source in one Mate directory, in both directions.
|
|
391
|
-
//
|
|
392
|
-
// Enumerating the directory alone is not enough. A knowledge file or a whole
|
|
393
|
-
// journal can be deleted while the daemon is stopped, or by direct filesystem
|
|
394
|
-
// tooling, and nothing would ever call syncMateSource for it again. So the
|
|
395
|
-
// scope's own live records are also consulted: any source this module
|
|
396
|
-
// previously imported that is no longer on disk is reconciled, which tombstones
|
|
397
|
-
// a missing file record or every line of a missing journal.
|
|
398
408
|
function reconcileMate(opts) {
|
|
399
409
|
var options = opts || {};
|
|
400
410
|
var summary = options.summary || newSummary();
|
|
@@ -404,11 +414,17 @@ function reconcileMate(opts) {
|
|
|
404
414
|
var knowledgeDir = path.join(scope.mateDir, "knowledge");
|
|
405
415
|
var present = {};
|
|
406
416
|
var entries = [];
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
417
|
+
function discover(relative) {
|
|
418
|
+
var folder = relative ? path.join(knowledgeDir, relative) : knowledgeDir; var children;
|
|
419
|
+
try { children = fs.readdirSync(folder, { withFileTypes: true }); } catch (error) { return; }
|
|
420
|
+
for (var d = 0; d < children.length; d++) {
|
|
421
|
+
var name = relative ? relative + "/" + children[d].name : children[d].name; var stat;
|
|
422
|
+
try { stat = fs.lstatSync(path.join(knowledgeDir, name)); } catch (error) { continue; }
|
|
423
|
+
if (stat.isSymbolicLink()) continue;
|
|
424
|
+
if (stat.isDirectory()) discover(name); else if (stat.isFile()) entries.push({ name: name });
|
|
425
|
+
}
|
|
411
426
|
}
|
|
427
|
+
discover("");
|
|
412
428
|
var importer = options.importer || importerFor(scope, options.baseDir);
|
|
413
429
|
|
|
414
430
|
function reconcileOne(fileName) {
|
|
@@ -423,7 +439,6 @@ function reconcileMate(opts) {
|
|
|
423
439
|
}
|
|
424
440
|
|
|
425
441
|
for (var i = 0; i < entries.length; i++) {
|
|
426
|
-
if (!entries[i].isFile()) continue;
|
|
427
442
|
// Recorded as present whatever its kind, so an excluded or unsupported file
|
|
428
443
|
// that still exists is never mistaken for a deleted source.
|
|
429
444
|
present[entries[i].name] = true;
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
// Only curated pigments may affect a Mate's workspace surface.
|
|
2
|
+
var COLORS = ['mint', 'rose', 'plum', 'coral', 'peach', 'gold', 'sky', 'indigo', 'stone'];
|
|
3
|
+
function valid(value) { return typeof value === 'string' && COLORS.indexOf(value) !== -1; }
|
|
4
|
+
module.exports = { colors: COLORS, valid: valid };
|
package/lib/mates.js
CHANGED
|
@@ -2,6 +2,7 @@ var fs = require("fs");
|
|
|
2
2
|
var path = require("path");
|
|
3
3
|
var crypto = require("crypto");
|
|
4
4
|
var config = require("./config");
|
|
5
|
+
var workspaceColor = require("./mate-workspace-color");
|
|
5
6
|
|
|
6
7
|
var crisisSafety = require("./crisis-safety");
|
|
7
8
|
var matesPrompts = require("./mates-prompts");
|
|
@@ -192,9 +193,17 @@ function getMate(ctx, id) {
|
|
|
192
193
|
}
|
|
193
194
|
|
|
194
195
|
function updateMate(ctx, id, updates) {
|
|
196
|
+
updates = Object.assign({}, updates);
|
|
197
|
+
delete updates.id;
|
|
198
|
+
delete updates.createdBy;
|
|
199
|
+
delete updates.builtinKey;
|
|
195
200
|
var data = loadMates(ctx);
|
|
196
201
|
for (var i = 0; i < data.mates.length; i++) {
|
|
197
202
|
if (data.mates[i].id === id) {
|
|
203
|
+
if (Object.prototype.hasOwnProperty.call(updates, "workspaceColor")) {
|
|
204
|
+
if (data.mates[i].primary || data.mates[i].builtinKey === "clay") delete updates.workspaceColor;
|
|
205
|
+
else if (!workspaceColor.valid(updates.workspaceColor)) return { error: "Choose a supported workspace color." };
|
|
206
|
+
}
|
|
198
207
|
// Primary mates: protect name and core identity fields
|
|
199
208
|
if (data.mates[i].primary) {
|
|
200
209
|
delete updates.name;
|
|
@@ -206,6 +215,15 @@ function updateMate(ctx, id, updates) {
|
|
|
206
215
|
delete updates.profile.displayName;
|
|
207
216
|
}
|
|
208
217
|
}
|
|
218
|
+
// Locked built-in artwork is server-managed. Preserve it even when a
|
|
219
|
+
// stale or crafted client submits a replacement profile object.
|
|
220
|
+
if (data.mates[i].profile && data.mates[i].profile.avatarLocked && updates.profile) {
|
|
221
|
+
updates.profile.avatarStyle = data.mates[i].profile.avatarStyle;
|
|
222
|
+
updates.profile.avatarSeed = data.mates[i].profile.avatarSeed;
|
|
223
|
+
updates.profile.avatarColor = data.mates[i].profile.avatarColor;
|
|
224
|
+
updates.profile.avatarCustom = data.mates[i].profile.avatarCustom;
|
|
225
|
+
updates.profile.avatarLocked = true;
|
|
226
|
+
}
|
|
209
227
|
var keys = Object.keys(updates);
|
|
210
228
|
for (var j = 0; j < keys.length; j++) {
|
|
211
229
|
data.mates[i][keys[j]] = updates[keys[j]];
|
package/lib/os-users.js
CHANGED
|
@@ -17,20 +17,11 @@ function isSafeLinuxUsername(username) {
|
|
|
17
17
|
// --- Supplementary group inheritance (issue #367) ---
|
|
18
18
|
// Node's child_process { uid, gid } options call setuid/setgid but never
|
|
19
19
|
// initgroups(), so spawned sessions drop all supplementary groups (docker,
|
|
20
|
-
// video, wheel, ...).
|
|
20
|
+
// video, wheel, ...). Route user-switched spawns through
|
|
21
21
|
// `setpriv --init-groups` so the child gets the same groups as a normal login.
|
|
22
|
-
var _inheritGroups = true; // default: behave like a normal login shell
|
|
23
22
|
var _setprivAvail = null;
|
|
24
23
|
var _warnedNoSetpriv = false;
|
|
25
24
|
|
|
26
|
-
function setInheritGroups(value) {
|
|
27
|
-
_inheritGroups = !!value;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
function getInheritGroups() {
|
|
31
|
-
return _inheritGroups;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
25
|
function setprivAvailable() {
|
|
35
26
|
if (_setprivAvail !== null) return _setprivAvail;
|
|
36
27
|
try {
|
|
@@ -50,7 +41,7 @@ function setprivAvailable() {
|
|
|
50
41
|
* spawn()/execFileSync()/pty.spawn().
|
|
51
42
|
*
|
|
52
43
|
* - Same-user spawns (no uid/gid in options) are returned unchanged.
|
|
53
|
-
* - When
|
|
44
|
+
* - When setpriv is unavailable, falls back to
|
|
54
45
|
* Node's uid/gid options (primary group only) so behavior degrades safely.
|
|
55
46
|
* Requires the parent process to be root, which is the case in os-users mode.
|
|
56
47
|
*/
|
|
@@ -62,7 +53,7 @@ function wrapSpawnAsUser(command, args, options, setprivCheck) {
|
|
|
62
53
|
if (uid == null || gid == null) return { command: command, args: args, options: options };
|
|
63
54
|
|
|
64
55
|
var hasSetpriv = typeof setprivCheck === "function" ? setprivCheck() : setprivAvailable();
|
|
65
|
-
if (
|
|
56
|
+
if (hasSetpriv) {
|
|
66
57
|
var wrappedArgs = ["--reuid", String(uid), "--regid", String(gid), "--init-groups", "--", command].concat(args);
|
|
67
58
|
var newOpts = Object.assign({}, options);
|
|
68
59
|
delete newOpts.uid;
|
|
@@ -70,9 +61,9 @@ function wrapSpawnAsUser(command, args, options, setprivCheck) {
|
|
|
70
61
|
return { command: "setpriv", args: wrappedArgs, options: newOpts };
|
|
71
62
|
}
|
|
72
63
|
|
|
73
|
-
if (
|
|
64
|
+
if (!_warnedNoSetpriv) {
|
|
74
65
|
_warnedNoSetpriv = true;
|
|
75
|
-
console.warn("[os-users]
|
|
66
|
+
console.warn("[os-users] `setpriv` not found; supplementary groups will be dropped. Install util-linux to restore them.");
|
|
76
67
|
}
|
|
77
68
|
return { command: command, args: args, options: options };
|
|
78
69
|
}
|
|
@@ -673,8 +664,6 @@ module.exports = {
|
|
|
673
664
|
grantAllUsersAccess: grantAllUsersAccess,
|
|
674
665
|
installClaudeCli: installClaudeCli,
|
|
675
666
|
claudeAvailableForUser: claudeAvailableForUser,
|
|
676
|
-
setInheritGroups: setInheritGroups,
|
|
677
|
-
getInheritGroups: getInheritGroups,
|
|
678
667
|
wrapSpawnAsUser: wrapSpawnAsUser,
|
|
679
668
|
deactivateLinuxUser: deactivateLinuxUser,
|
|
680
669
|
ensureProjectsDir: ensureProjectsDir,
|
|
@@ -6,6 +6,7 @@ var emailAccounts = require("./email-accounts");
|
|
|
6
6
|
var { getCodexConfig } = require("./codex-defaults");
|
|
7
7
|
var yoke = require("./yoke");
|
|
8
8
|
var permissionModes = require("./session-permission-mode");
|
|
9
|
+
var sessionProvenance = require("./session-provenance");
|
|
9
10
|
|
|
10
11
|
function dispatchMessageSafely(handleMessage, sendTo, slug, ws, msg) {
|
|
11
12
|
try {
|
|
@@ -45,6 +46,36 @@ function buildInitialModelInfo(sm, initialVendor, initialModel, initialModels, s
|
|
|
45
46
|
};
|
|
46
47
|
}
|
|
47
48
|
|
|
49
|
+
function buildRestoredSessionMessage(active, initialEffort, capabilities, permissionState) {
|
|
50
|
+
return {
|
|
51
|
+
type: "session_switched",
|
|
52
|
+
id: active.localId,
|
|
53
|
+
sessionOriginId: active.sessionOriginId || null,
|
|
54
|
+
sessionRole: sessionProvenance.isWorker(active) ? "worker" : "driver",
|
|
55
|
+
cliSessionId: active.cliSessionId || null,
|
|
56
|
+
loop: active.loop || null,
|
|
57
|
+
vendor: active.vendor || null,
|
|
58
|
+
model: active.model || null,
|
|
59
|
+
effort: active.effort || initialEffort || null,
|
|
60
|
+
hasHistory: !!(active.history && active.history.length > 0),
|
|
61
|
+
capabilities: capabilities,
|
|
62
|
+
isProcessing: !!active.isProcessing,
|
|
63
|
+
mateCreation: active.mateCreationMode === true,
|
|
64
|
+
mateCreationPhase: active.homeMateCreationPhase || null,
|
|
65
|
+
debatePlanning: active.debateSetupMode === true,
|
|
66
|
+
debatePhase: active.homeDebatePhase || null,
|
|
67
|
+
mode: active.mode || "gui",
|
|
68
|
+
terminalId: typeof active.terminalId === "number" ? active.terminalId : null,
|
|
69
|
+
runtimeMode: active.runtimeMode || null,
|
|
70
|
+
runtimeTerminalId: typeof active.runtimeTerminalId === "number" ? active.runtimeTerminalId : null,
|
|
71
|
+
tuiSuspended: !!active.tuiSuspended,
|
|
72
|
+
permissionMode: permissionState.requestedPermissionMode,
|
|
73
|
+
effectivePermissionMode: permissionState.effectivePermissionMode,
|
|
74
|
+
permissionCapabilities: permissionState.permissionCapabilities,
|
|
75
|
+
mcpPermissionModeOverrides: permissionState.mcpPermissionModeOverrides,
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
|
|
48
79
|
/**
|
|
49
80
|
* Attach connection/disconnection handlers to a project context.
|
|
50
81
|
*
|
|
@@ -63,6 +94,8 @@ function attachConnection(ctx) {
|
|
|
63
94
|
var cwd = ctx.cwd;
|
|
64
95
|
var slug = ctx.slug;
|
|
65
96
|
var isMate = ctx.isMate;
|
|
97
|
+
var mateId = ctx.mateId || null;
|
|
98
|
+
var mateDisplayName = ctx.mateDisplayName || "";
|
|
66
99
|
var osUsers = ctx.osUsers;
|
|
67
100
|
var debug = ctx.debug;
|
|
68
101
|
var dangerouslySkipPermissions = ctx.dangerouslySkipPermissions;
|
|
@@ -184,7 +217,7 @@ function attachConnection(ctx) {
|
|
|
184
217
|
sm.saveSessionFile(restoredActive);
|
|
185
218
|
}
|
|
186
219
|
var initialModels = (sm.modelsByVendor && sm.modelsByVendor[initialVendor]) || sm.availableModels || [];
|
|
187
|
-
sendTo(ws, { type: "info", cwd: cwd, slug: slug, project: title || project, version: currentVersion, debug: !!debug, dangerouslySkipPermissions: dangerouslySkipPermissions, osUsers: osUsers, lanHost: lanHost, projectCount: _filteredProjects.length, projects: _filteredProjects, projectOwnerId: projectOwnerId, ownerLocked: ownerLocked, vendors: yoke.VENDOR_REGISTRY });
|
|
220
|
+
sendTo(ws, { type: "info", cwd: cwd, slug: slug, project: title || project, version: currentVersion, debug: !!debug, dangerouslySkipPermissions: dangerouslySkipPermissions, osUsers: osUsers, lanHost: lanHost, projectCount: _filteredProjects.length, projects: _filteredProjects, projectOwnerId: projectOwnerId, ownerLocked: ownerLocked, vendors: yoke.VENDOR_REGISTRY, isMate: isMate, mateId: isMate ? mateId : null, mateDisplayName: isMate ? (mateDisplayName || title || project || "Mate") : "" });
|
|
188
221
|
if (typeof sendCursorSharingState === "function") sendCursorSharingState(ws);
|
|
189
222
|
// Update notifications are pushed on a scheduled interval (see
|
|
190
223
|
// scheduleUpdateBroadcast). We no longer push on connect to avoid
|
|
@@ -213,13 +246,6 @@ function attachConnection(ctx) {
|
|
|
213
246
|
sendTo(ws, { type: "email_accounts_list", accounts: emailAccountsList, providers: emailAccounts.PROVIDER_PRESETS });
|
|
214
247
|
sendTo(ws, { type: "notes_list", notes: nm.list() });
|
|
215
248
|
sendTo(ws, { type: "loop_registry_updated", records: getHubSchedules() });
|
|
216
|
-
// Initial per-user preference: how to render Claude sessions.
|
|
217
|
-
if (usersModule && typeof usersModule.getClaudeOpenMode === "function") {
|
|
218
|
-
var _comUid = (wsUser && wsUser.id) || null;
|
|
219
|
-
var _comVal = _comUid ? usersModule.getClaudeOpenMode(_comUid) : "gui";
|
|
220
|
-
sendTo(ws, { type: "claude_open_mode_changed", claudeOpenMode: _comVal || "gui" });
|
|
221
|
-
}
|
|
222
|
-
|
|
223
249
|
_loop.sendConnectionState(ws);
|
|
224
250
|
if (_mcp) _mcp.sendConnectionState(ws);
|
|
225
251
|
if (_notifications) _notifications.sendConnectionState(ws, sendTo);
|
|
@@ -262,7 +288,7 @@ function attachConnection(ctx) {
|
|
|
262
288
|
}
|
|
263
289
|
var _vendorCaps = (sm.capabilitiesByVendor && sm.capabilitiesByVendor[active.vendor || sm.defaultVendor || "claude"]) || {};
|
|
264
290
|
var activePermissionState = permissionModes.clientState(active, sm.currentPermissionMode, sm.defaultVendor);
|
|
265
|
-
sendTo(ws,
|
|
291
|
+
sendTo(ws, buildRestoredSessionMessage(active, initialEffort, _vendorCaps, activePermissionState));
|
|
266
292
|
if (ctx.autonomousRun) sendTo(ws, { type: "autonomous_run_state", sessionId: active.localId, run: ctx.autonomousRun.project(active.autonomousRun) });
|
|
267
293
|
// Send per-session context sources
|
|
268
294
|
var sessionSources = loadContextSources(slug, active.localId);
|
|
@@ -382,5 +408,6 @@ function attachConnection(ctx) {
|
|
|
382
408
|
module.exports = {
|
|
383
409
|
attachConnection: attachConnection,
|
|
384
410
|
buildInitialModelInfo: buildInitialModelInfo,
|
|
411
|
+
buildRestoredSessionMessage: buildRestoredSessionMessage,
|
|
385
412
|
dispatchMessageSafely: dispatchMessageSafely,
|
|
386
413
|
};
|
|
@@ -227,6 +227,12 @@ function attachDebateProposal(ctx) {
|
|
|
227
227
|
|
|
228
228
|
var found = findPendingProposal(ws, msg);
|
|
229
229
|
if (!found) return true;
|
|
230
|
+
if (msg.sessionId != null && (String(found.pending.session.localId) !== String(msg.sessionId)
|
|
231
|
+
|| String(ws._clayActiveSession) !== String(msg.sessionId)
|
|
232
|
+
|| (found.pending.session.ownerId && (!ws._clayUser || ws._clayUser.id !== found.pending.session.ownerId)))) {
|
|
233
|
+
ctx.sendTo(ws, { type: "debate_error", error: "This proposal belongs to a different session." });
|
|
234
|
+
return true;
|
|
235
|
+
}
|
|
230
236
|
startApprovedProposal(ws, msg, found).catch(function (error) {
|
|
231
237
|
delete pendingProposals[found.key];
|
|
232
238
|
rejectProposal(ws, found.pending, error && error.message ? error.message : "The selected debate models could not be verified.");
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
// Project-bound access to the same participation actions used by Home debates.
|
|
2
|
+
function handleProjectDebateControl(ws, msg, session, engine, sendTo) {
|
|
3
|
+
if (msg.type !== "project_debate_control" && msg.type !== "project_debate_state") return false;
|
|
4
|
+
var userId = ws && ws._clayUser && ws._clayUser.id;
|
|
5
|
+
var valid = session && msg.sessionId != null && String(session.localId) === String(msg.sessionId)
|
|
6
|
+
&& (!session.ownerId || session.ownerId === userId);
|
|
7
|
+
if (msg.type === "project_debate_state") {
|
|
8
|
+
sendTo(ws, { type: "project_debate_state", sessionId: msg.sessionId, header: valid ? debateHeader(session) : null });
|
|
9
|
+
return true;
|
|
10
|
+
}
|
|
11
|
+
var ok = false;
|
|
12
|
+
if (valid) {
|
|
13
|
+
if (session.homeDebatePlanning) ok = engine.handleHomeControl(ws, msg, session);
|
|
14
|
+
else if (msg.action === "hand_raise") { engine.handleDebateHandRaise(ws, session); ok = true; }
|
|
15
|
+
else if (msg.action === "stop") ok = engine.handleDebateStop(ws, session);
|
|
16
|
+
else if (msg.action === "cancel_stop") ok = engine.handleDebateCancelStop(ws, session);
|
|
17
|
+
else if (msg.action === "user_floor") { engine.handleDebateUserFloorResponse(ws, msg, session); ok = true; }
|
|
18
|
+
else if (msg.action === "conclude" || msg.action === "resume") {
|
|
19
|
+
engine.handleDebateConcludeResponse(ws, { action: msg.action === "resume" ? "continue" : msg.response, text: msg.text || "" }, session);
|
|
20
|
+
ok = true;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
sendTo(ws, { type: "project_debate_control_result", sessionId: msg.sessionId, ok: !!ok, error: ok ? null : "This debate action is no longer available. Reopen the debate and try again." });
|
|
24
|
+
return true;
|
|
25
|
+
}
|
|
26
|
+
function debateHeader(session) {
|
|
27
|
+
var header = null;
|
|
28
|
+
(session.history || []).forEach(function (event) {
|
|
29
|
+
if (event.type === "debate_started") header = Object.assign({}, event, { role: "debate_header", phase: "live", round: 1, interaction: null, handRaised: false });
|
|
30
|
+
if (!header) return;
|
|
31
|
+
if (event.round) header.round = event.round;
|
|
32
|
+
if (event.type === "debate_conclude_confirm") header.interaction = "conclude";
|
|
33
|
+
if (event.type === "debate_user_floor") header.interaction = "user_floor";
|
|
34
|
+
if (event.type === "debate_user_floor_done") { header.interaction = null; header.handRaised = false; }
|
|
35
|
+
if (event.type === "debate_hand_raised") header.handRaised = true;
|
|
36
|
+
if (event.type === "debate_stop_requested") header.stopping = true;
|
|
37
|
+
if (event.type === "debate_stop_cancelled") header.stopping = false;
|
|
38
|
+
if (event.type === "debate_turn" || event.type === "debate_resumed") { header.interaction = null; header.phase = "live"; }
|
|
39
|
+
if (event.type === "debate_ended") { header.phase = event.reason === "interrupted" ? "interrupted" : "ended"; header.reason = event.reason; header.interaction = null; header.stopping = false; }
|
|
40
|
+
});
|
|
41
|
+
return header;
|
|
42
|
+
}
|
|
43
|
+
module.exports = { handleProjectDebateControl: handleProjectDebateControl, debateHeader: debateHeader };
|
package/lib/project-debate.js
CHANGED
|
@@ -1859,6 +1859,7 @@ function attachDebate(ctx) {
|
|
|
1859
1859
|
handleDebateHandRaise: handleDebateHandRaise,
|
|
1860
1860
|
handleDebateComment: handleDebateComment,
|
|
1861
1861
|
handleDebateStop: handleDebateStop,
|
|
1862
|
+
handleDebateCancelStop: handleDebateCancelStop,
|
|
1862
1863
|
handleDebateConcludeResponse: handleDebateConcludeResponse,
|
|
1863
1864
|
handleDebateConfirmBrief: handleDebateConfirmBrief,
|
|
1864
1865
|
handleDebateUserFloorResponse: handleDebateUserFloorResponse,
|
|
@@ -204,26 +204,28 @@ function attachFilesystem(ctx) {
|
|
|
204
204
|
|
|
205
205
|
// --- Project settings permission gate ---
|
|
206
206
|
if (msg.type === "get_project_env" || msg.type === "set_project_env" ||
|
|
207
|
-
msg.type === "read_global_claude_md" || msg.type === "write_global_claude_md" ||
|
|
208
207
|
msg.type === "get_shared_env" || msg.type === "set_shared_env" ||
|
|
209
208
|
msg.type === "transfer_project_owner") {
|
|
210
|
-
var globalSetting = /^(
|
|
209
|
+
var globalSetting = /^(get_shared_env|set_shared_env)$/.test(msg.type);
|
|
211
210
|
var targetSlug = msg.slug || slug;
|
|
212
211
|
if (!access.hasPermission(ws, "projectSettings") ||
|
|
213
212
|
(globalSetting ? !access.isAdmin(ws) : !access.canAccessProject(ws, targetSlug))) {
|
|
214
|
-
sendTo(ws, { type:
|
|
213
|
+
sendTo(ws, { type: globalSetting ? (msg.type === 'get_shared_env' ? 'shared_env_result' : 'set_shared_env_result') : (msg.type === 'get_project_env' ? 'project_env_result' : msg.type === 'set_project_env' ? 'set_project_env_result' : 'error'), slug: targetSlug, requestId: msg.requestId, ok: false, error: 'Project settings access is not permitted', text: "Project settings access is not permitted" });
|
|
215
214
|
return true;
|
|
216
215
|
}
|
|
217
216
|
}
|
|
218
217
|
|
|
219
218
|
// --- Project environment variables ---
|
|
220
219
|
if (msg.type === "get_project_env") {
|
|
220
|
+
if (typeof opts.onGetProjectEnv !== "function") { sendTo(ws, { type: "project_env_result", slug: targetSlug, requestId: msg.requestId, error: "Project environment is unavailable in this runtime." }); return true; }
|
|
221
221
|
var envrc = "";
|
|
222
222
|
if (typeof opts.onGetProjectEnv === "function") {
|
|
223
|
-
var envResult
|
|
223
|
+
var envResult;
|
|
224
|
+
try { envResult = opts.onGetProjectEnv(targetSlug); }
|
|
225
|
+
catch (error) { sendTo(ws, { type: "project_env_result", slug: targetSlug, requestId: msg.requestId, error: error.message }); return true; }
|
|
224
226
|
envrc = envResult.envrc || "";
|
|
225
227
|
}
|
|
226
|
-
sendTo(ws, { type: "project_env_result", slug: msg.
|
|
228
|
+
sendTo(ws, { type: "project_env_result", slug: targetSlug, requestId: msg.requestId, envrc: envrc });
|
|
227
229
|
return true;
|
|
228
230
|
}
|
|
229
231
|
|
|
@@ -231,41 +233,16 @@ function attachFilesystem(ctx) {
|
|
|
231
233
|
if (typeof opts.onSetProjectEnv === "function") {
|
|
232
234
|
var envError = validateEnvString(msg.envrc || "");
|
|
233
235
|
if (envError) {
|
|
234
|
-
sendTo(ws, { type: "set_project_env_result", ok: false, slug: msg.
|
|
236
|
+
sendTo(ws, { type: "set_project_env_result", ok: false, slug: targetSlug, requestId: msg.requestId, error: envError });
|
|
235
237
|
return true;
|
|
236
238
|
}
|
|
237
|
-
var setResult
|
|
239
|
+
var setResult;
|
|
240
|
+
try { setResult = opts.onSetProjectEnv(targetSlug, msg.envrc || ""); }
|
|
241
|
+
catch (error) { sendTo(ws, { type: "set_project_env_result", slug: targetSlug, requestId: msg.requestId, ok: false, error: error.message }); return true; }
|
|
238
242
|
if (setResult.ok) onEnvironmentChanged();
|
|
239
|
-
sendTo(ws, { type: "set_project_env_result", ok: setResult.ok, slug: msg.
|
|
243
|
+
sendTo(ws, { type: "set_project_env_result", ok: setResult.ok, slug: targetSlug, requestId: msg.requestId, error: setResult.error, timing: setResult.ok ? "Applies to newly created coding-agent processes. Active processes keep their current environment." : undefined });
|
|
240
244
|
} else {
|
|
241
|
-
sendTo(ws, { type: "set_project_env_result", ok: false, error: "Not supported" });
|
|
242
|
-
}
|
|
243
|
-
return true;
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
// --- Global CLAUDE.md ---
|
|
247
|
-
if (msg.type === "read_global_claude_md") {
|
|
248
|
-
var globalMdPath = path.join(require("./config").REAL_HOME, ".claude", "CLAUDE.md");
|
|
249
|
-
try {
|
|
250
|
-
var globalMdContent = fs.readFileSync(globalMdPath, "utf8");
|
|
251
|
-
sendTo(ws, { type: "global_claude_md_result", content: globalMdContent });
|
|
252
|
-
} catch (e) {
|
|
253
|
-
sendTo(ws, { type: "global_claude_md_result", error: e.message });
|
|
254
|
-
}
|
|
255
|
-
return true;
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
if (msg.type === "write_global_claude_md") {
|
|
259
|
-
var globalMdDir = path.join(require("./config").REAL_HOME, ".claude");
|
|
260
|
-
var globalMdWritePath = path.join(globalMdDir, "CLAUDE.md");
|
|
261
|
-
try {
|
|
262
|
-
if (!fs.existsSync(globalMdDir)) {
|
|
263
|
-
fs.mkdirSync(globalMdDir, { recursive: true });
|
|
264
|
-
}
|
|
265
|
-
fs.writeFileSync(globalMdWritePath, msg.content || "", "utf8");
|
|
266
|
-
sendTo(ws, { type: "write_global_claude_md_result", ok: true });
|
|
267
|
-
} catch (e) {
|
|
268
|
-
sendTo(ws, { type: "write_global_claude_md_result", ok: false, error: e.message });
|
|
245
|
+
sendTo(ws, { type: "set_project_env_result", slug: targetSlug, requestId: msg.requestId, ok: false, error: "Not supported" });
|
|
269
246
|
}
|
|
270
247
|
return true;
|
|
271
248
|
}
|
|
@@ -274,9 +251,12 @@ function attachFilesystem(ctx) {
|
|
|
274
251
|
if (msg.type === "get_shared_env") {
|
|
275
252
|
var sharedEnvrc = "";
|
|
276
253
|
if (typeof opts.onGetSharedEnv === "function") {
|
|
277
|
-
|
|
278
|
-
|
|
254
|
+
try {
|
|
255
|
+
var sharedResult = opts.onGetSharedEnv();
|
|
256
|
+
sharedEnvrc = sharedResult.envrc || "";
|
|
257
|
+
} catch (error) { sendTo(ws, { type: "shared_env_result", error: error.message }); return true; }
|
|
279
258
|
}
|
|
259
|
+
if (typeof opts.onGetSharedEnv !== "function") { sendTo(ws, { type: "shared_env_result", error: "Shared environment is unavailable in this runtime." }); return true; }
|
|
280
260
|
sendTo(ws, { type: "shared_env_result", envrc: sharedEnvrc });
|
|
281
261
|
return true;
|
|
282
262
|
}
|
|
@@ -288,7 +268,9 @@ function attachFilesystem(ctx) {
|
|
|
288
268
|
sendTo(ws, { type: "set_shared_env_result", ok: false, error: sharedEnvError });
|
|
289
269
|
return true;
|
|
290
270
|
}
|
|
291
|
-
var sharedSetResult
|
|
271
|
+
var sharedSetResult;
|
|
272
|
+
try { sharedSetResult = opts.onSetSharedEnv(msg.envrc || ""); }
|
|
273
|
+
catch (error) { sendTo(ws, { type: "set_shared_env_result", ok: false, error: error.message }); return true; }
|
|
292
274
|
if (sharedSetResult.ok) onEnvironmentChanged();
|
|
293
275
|
sendTo(ws, { type: "set_shared_env_result", ok: sharedSetResult.ok, error: sharedSetResult.error, timing: sharedSetResult.ok ? "Applies to newly created coding-agent processes. Active processes keep their current environment." : undefined });
|
|
294
276
|
} else {
|