clay-server 5.1.0-beta.1 → 5.1.0-beta.3
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/builtin-mates.js +7 -7
- 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-identity-file.js +39 -0
- package/lib/mate-knowledge-mcp-server.js +29 -6
- package/lib/mate-knowledge-service.js +23 -2
- package/lib/mate-knowledge-sync.js +37 -22
- package/lib/mate-ready-creation.js +2 -1
- package/lib/mate-workspace-color.js +4 -0
- package/lib/mates-identity.js +4 -4
- package/lib/mates-prompts.js +4 -3
- package/lib/mates.js +30 -11
- 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 +4 -2
- 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 +7 -6
- 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 +62 -21
- 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/menus.css +3 -0
- package/lib/public/css/messages.css +1 -114
- package/lib/public/css/mobile-nav.css +16 -16
- package/lib/public/css/overlays.css +15 -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 +7 -3
- 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 +206 -548
- 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 +30 -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 +22 -12
- 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 +14 -4
- 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 +57 -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 +35 -10
- package/lib/startup-projects.js +40 -0
- package/lib/storage-native-walk.js +85 -0
- package/lib/storage-usage.js +144 -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/acp-process-manager.js +2 -2
- package/lib/yoke/adapters/codex.js +12 -52
- package/lib/yoke/codex-app-server.js +2 -2
- package/lib/yoke/codex-model-catalog.js +52 -0
- package/lib/yoke/codex-rate-limits.js +26 -0
- package/package.json +12 -3
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
var fs = require('node:fs/promises');
|
|
2
|
+
var path = require('node:path');
|
|
3
|
+
var execFile = require('node:child_process').execFile;
|
|
4
|
+
var nativeWalk = require('./storage-native-walk');
|
|
5
|
+
|
|
6
|
+
function resolveIdentity(username) {
|
|
7
|
+
return new Promise(function (resolve) {
|
|
8
|
+
if (!/^[a-z_][a-z0-9_-]*[$]?$/.test(username || '')) return resolve(null);
|
|
9
|
+
execFile('getent', ['passwd', username], { timeout: 3000, maxBuffer: 8192 }, function (error, output) {
|
|
10
|
+
if (error) return resolve(null);
|
|
11
|
+
var parts = output.trim().split(':');
|
|
12
|
+
var uid = Number(parts[2]);
|
|
13
|
+
resolve(parts.length >= 7 && /^\d+$/.test(parts[2]) && Number.isInteger(uid) && uid >= 0 && path.isAbsolute(parts[5]) ? { uid: uid, home: parts[5] } : null);
|
|
14
|
+
});
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// Read metadata only. Shared paths and hard links count once across all roots.
|
|
19
|
+
async function scanStorage(options) {
|
|
20
|
+
var started = Date.now();
|
|
21
|
+
var deadline = options.maxMs ? started + options.maxMs : Infinity;
|
|
22
|
+
var maxEntries = options.maxEntries || Infinity;
|
|
23
|
+
var users = options.users || [];
|
|
24
|
+
var projectMode = options.attribution === "project";
|
|
25
|
+
var projects = options.projects || [];
|
|
26
|
+
var identities = await Promise.all(users.map(function (user) {
|
|
27
|
+
return !projectMode && options.platform === 'linux' && user.linuxUser ? (options.resolveIdentity || resolveIdentity)(user.linuxUser) : null;
|
|
28
|
+
}));
|
|
29
|
+
var rows = users.map(function (user, index) {
|
|
30
|
+
return { id: user.id, name: user.displayName || user.username, linuxUser: user.linuxUser || null, mapped: projectMode || !!identities[index], projects: 0, bytes: 0, files: 0 };
|
|
31
|
+
});
|
|
32
|
+
var owners = new Map();
|
|
33
|
+
identities.forEach(function (identity, index) {
|
|
34
|
+
if (!identity) return;
|
|
35
|
+
// A reused OS identity cannot be split accurately between Clay accounts.
|
|
36
|
+
owners.set(identity.uid, owners.has(identity.uid) ? null : rows[index]);
|
|
37
|
+
});
|
|
38
|
+
identities.forEach(function (identity, index) {
|
|
39
|
+
if (identity && !owners.get(identity.uid)) rows[index].mapped = false;
|
|
40
|
+
});
|
|
41
|
+
var projectRoots = new Map();
|
|
42
|
+
var projectErrors = 0;
|
|
43
|
+
for (var project of projects) {
|
|
44
|
+
try {
|
|
45
|
+
var projectPath = await fs.realpath(project.path);
|
|
46
|
+
var row = rows.find(function (item) { return item.id === project.projectOwnerId; }) || null;
|
|
47
|
+
if (row) row.projects++;
|
|
48
|
+
if (projectRoots.has(projectPath) && projectRoots.get(projectPath) !== row) row = null;
|
|
49
|
+
projectRoots.set(projectPath, row);
|
|
50
|
+
} catch (error) { projectErrors++; }
|
|
51
|
+
}
|
|
52
|
+
var orderedProjects = Array.from(projectRoots.keys()).sort(function (a, b) { return b.length - a.length; });
|
|
53
|
+
function projectOwner(filePath) {
|
|
54
|
+
var root = orderedProjects.find(function (candidate) { return filePath === candidate || filePath.indexOf(candidate + path.sep) === 0; });
|
|
55
|
+
return root ? projectRoots.get(root) : null;
|
|
56
|
+
}
|
|
57
|
+
var roots = orderedProjects.concat(options.projectPaths || []);
|
|
58
|
+
identities.forEach(function (identity) { if (identity) roots.push(identity.home); });
|
|
59
|
+
roots.push(options.configDir);
|
|
60
|
+
var report = { attribution: projectMode ? "project" : "filesystem", users: rows, totalBytes: 0, totalFiles: 0, unattributedBytes: 0, unattributedFiles: 0,
|
|
61
|
+
partial: false, errors: projectErrors + users.filter(function (user, index) { return !projectMode && options.platform === 'linux' && user.linuxUser && !identities[index]; }).length, skippedLinks: 0, skippedMounts: 0, scannedEntries: 0, roots: 0, platform: options.platform };
|
|
62
|
+
var lastProgress = 0;
|
|
63
|
+
function progress() {
|
|
64
|
+
if (!options.onProgress || Date.now() - lastProgress < 500) return;
|
|
65
|
+
lastProgress = Date.now();
|
|
66
|
+
options.onProgress(Object.assign({}, report, { scanning: true, checkedAt: new Date().toISOString(),
|
|
67
|
+
users: rows.map(function (row) { return Object.assign({}, row); }) }));
|
|
68
|
+
}
|
|
69
|
+
progress();
|
|
70
|
+
var seen = new Map();
|
|
71
|
+
var rootPaths = new Set();
|
|
72
|
+
var completedRoots = [];
|
|
73
|
+
var useNative = options.native !== false && await nativeWalk.available();
|
|
74
|
+
for (var root of roots) {
|
|
75
|
+
if (typeof root !== 'string' || !path.isAbsolute(root)) { report.errors++; continue; }
|
|
76
|
+
try {
|
|
77
|
+
root = await fs.realpath(root);
|
|
78
|
+
// Never turn a misconfigured home/project into a whole-host scan.
|
|
79
|
+
if (root === path.parse(root).root) { report.errors++; continue; }
|
|
80
|
+
if (rootPaths.has(root)) continue;
|
|
81
|
+
rootPaths.add(root);
|
|
82
|
+
var rootStat = await fs.lstat(root);
|
|
83
|
+
report.roots++;
|
|
84
|
+
if (useNative) {
|
|
85
|
+
if (seen.has(rootStat.dev + ':' + rootStat.ino)) continue;
|
|
86
|
+
var result = await nativeWalk.walk(root, completedRoots, function (filePath, stat) {
|
|
87
|
+
if (Date.now() >= deadline || report.scannedEntries >= maxEntries) { report.partial = true; return false; }
|
|
88
|
+
account(filePath, stat, rootStat.dev);
|
|
89
|
+
}, deadline - Date.now());
|
|
90
|
+
report.errors += result.errors;
|
|
91
|
+
if (result.stopped) report.partial = true;
|
|
92
|
+
completedRoots.push(root);
|
|
93
|
+
} else await walk(root, rootStat.dev);
|
|
94
|
+
} catch (error) { report.errors++; }
|
|
95
|
+
if (report.partial) break;
|
|
96
|
+
}
|
|
97
|
+
report.partial = report.partial || report.errors > 0 || report.skippedMounts > 0;
|
|
98
|
+
report.scanning = false;
|
|
99
|
+
report.checkedAt = new Date().toISOString();
|
|
100
|
+
return report;
|
|
101
|
+
|
|
102
|
+
async function walk(filePath, device) {
|
|
103
|
+
if (Date.now() >= deadline || report.scannedEntries >= maxEntries) { report.partial = true; return; }
|
|
104
|
+
var stat;
|
|
105
|
+
try { stat = await fs.lstat(filePath); } catch (error) { report.errors++; return; }
|
|
106
|
+
if (!account(filePath, stat, device)) return;
|
|
107
|
+
if (!stat.isDirectory()) return;
|
|
108
|
+
try {
|
|
109
|
+
var directory = await fs.opendir(filePath);
|
|
110
|
+
for await (var entry of directory) {
|
|
111
|
+
await walk(path.join(filePath, entry.name), device);
|
|
112
|
+
if (report.partial) break;
|
|
113
|
+
}
|
|
114
|
+
} catch (error) { report.errors++; }
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function account(filePath, stat, device) {
|
|
118
|
+
if (stat.isSymbolicLink()) { report.skippedLinks++; return; }
|
|
119
|
+
if (stat.dev !== device) { report.skippedMounts++; return; }
|
|
120
|
+
var key = stat.dev + ':' + stat.ino;
|
|
121
|
+
var owner = projectMode ? projectOwner(filePath) : owners.get(stat.uid);
|
|
122
|
+
if (seen.has(key)) {
|
|
123
|
+
var previous = seen.get(key);
|
|
124
|
+
if (projectMode && stat.isFile() && previous.owner && previous.owner !== owner) {
|
|
125
|
+
previous.owner.bytes -= previous.bytes; previous.owner.files--;
|
|
126
|
+
report.unattributedBytes += previous.bytes; report.unattributedFiles++;
|
|
127
|
+
previous.owner = null;
|
|
128
|
+
}
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
report.scannedEntries++;
|
|
132
|
+
if (!stat.isDirectory() && !stat.isFile()) return;
|
|
133
|
+
var bytes = typeof stat.blocks === 'number' ? stat.blocks * 512 : stat.size;
|
|
134
|
+
// Only directories and multiply-linked files need identity deduplication.
|
|
135
|
+
if (stat.isDirectory() || stat.nlink > 1) seen.set(key, { owner: owner, bytes: bytes });
|
|
136
|
+
report.totalBytes += bytes;
|
|
137
|
+
if (stat.isFile()) report.totalFiles++;
|
|
138
|
+
if (owner) { owner.bytes += bytes; if (stat.isFile()) owner.files++; }
|
|
139
|
+
else { report.unattributedBytes += bytes; if (stat.isFile()) report.unattributedFiles++; }
|
|
140
|
+
progress();
|
|
141
|
+
return true;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
module.exports = { scanStorage: scanStorage };
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
// Owner-private user knowledge. Only canonical Clay sessions curate records.
|
|
2
|
+
var crypto = require('crypto');
|
|
3
|
+
var canUsePersonalConnections = require('./mcp-session-access').canUsePersonalConnections;
|
|
4
|
+
var createRecordStore = require('./knowledge-record-store').createRecordStore;
|
|
5
|
+
|
|
6
|
+
function attachUserKnowledgeService(deps) {
|
|
7
|
+
var stores = new Map();
|
|
8
|
+
function owner(id) {
|
|
9
|
+
if (deps.isMultiUser() && (!id || !deps.findUser(id))) throw new Error('User knowledge requires an authenticated owner.');
|
|
10
|
+
return deps.isMultiUser() ? id : 'default';
|
|
11
|
+
}
|
|
12
|
+
function storage(id) {
|
|
13
|
+
var key = crypto.createHash('sha256').update(owner(id)).digest('hex');
|
|
14
|
+
if (!stores.has(key)) stores.set(key, createRecordStore({scopeId:'user/' + key + '/you',baseDir:deps.baseDir}));
|
|
15
|
+
return stores.get(key);
|
|
16
|
+
}
|
|
17
|
+
function snapshot(id) {
|
|
18
|
+
var result = {entries:{},reports:{},activity:[],seen:{},version:0};
|
|
19
|
+
storage(id).all().forEach(function (record) {
|
|
20
|
+
if (record.kind !== 'user-knowledge') return;
|
|
21
|
+
result.version++;
|
|
22
|
+
if (record.seen) result.seen[record.seen.ref] = Math.max(result.seen[record.seen.ref] || 0, record.seen.revision);
|
|
23
|
+
if (record.entry) result.entries[record.entry.ref] = record.entry;
|
|
24
|
+
if (record.report) result.reports[record.report.ref] = record.report;
|
|
25
|
+
if (record.activity) result.activity.push(record.activity);
|
|
26
|
+
});
|
|
27
|
+
return result;
|
|
28
|
+
}
|
|
29
|
+
function text(value, max, required) {
|
|
30
|
+
if (typeof value !== 'string' || value.length > max || (required && !value.trim())) throw new Error('Invalid or oversized knowledge text.');
|
|
31
|
+
return value.trim();
|
|
32
|
+
}
|
|
33
|
+
function page(items, args) {
|
|
34
|
+
args = args || {};
|
|
35
|
+
var offset = Math.max(0, Number(args.offset) || 0);
|
|
36
|
+
offset = Number.isFinite(offset) ? Math.floor(offset) : 0;
|
|
37
|
+
var limit = Math.min(50, Math.max(1, Number(args.limit) || 30));
|
|
38
|
+
return {items:items.slice(offset,offset+limit),total:items.length,nextOffset:offset+limit < items.length ? offset+limit:null};
|
|
39
|
+
}
|
|
40
|
+
function unread(entry,state) { return !entry.archived && entry.revisions > (state.seen[entry.ref] || 0); }
|
|
41
|
+
function attention(id) {
|
|
42
|
+
var state = snapshot(id);
|
|
43
|
+
return { unread: Object.values(state.entries).filter(function (entry) { return unread(entry,state); }).length, version: state.version };
|
|
44
|
+
}
|
|
45
|
+
function acknowledge(id,args) {
|
|
46
|
+
var state = snapshot(id);
|
|
47
|
+
var entry = state.entries[args.ref];
|
|
48
|
+
if (!entry || entry.archived) throw new Error('User knowledge not found.');
|
|
49
|
+
if (!Number.isInteger(args.revision) || args.revision < 1 || args.revision > entry.revisions) throw new Error('Invalid memory revision.');
|
|
50
|
+
if ((state.seen[args.ref] || 0) < args.revision) {
|
|
51
|
+
storage(id).append({kind:'user-knowledge',seen:{ref:args.ref,revision:args.revision,at:Date.now()}});
|
|
52
|
+
try { deps.onChange(owner(id),{seen:true}); } catch (error) { /* Acknowledgement survives notification failures. */ }
|
|
53
|
+
}
|
|
54
|
+
return attention(id);
|
|
55
|
+
}
|
|
56
|
+
function list(id,args) {
|
|
57
|
+
var state = snapshot(id);
|
|
58
|
+
args = args || {};
|
|
59
|
+
var query = String(args.query || '').toLowerCase().slice(0,300);
|
|
60
|
+
var entries = Object.values(state.entries).filter(function (entry) {
|
|
61
|
+
return !entry.archived && (!args.category || entry.category === args.category) &&
|
|
62
|
+
(!query || (entry.title+' '+entry.summary+' '+entry.body).toLowerCase().indexOf(query)!==-1);
|
|
63
|
+
}).map(function(entry){return Object.assign({},entry,{unread:unread(entry,state)});}).sort(function (a,b) {return Number(b.unread)-Number(a.unread) || b.updatedAt-a.updatedAt;});
|
|
64
|
+
return Object.assign(page(entries,args),{categories:Array.from(new Set(Object.values(state.entries).filter(function (e) {return !e.archived;}).map(function(e){return e.category;}))).sort(),pending:Object.values(state.reports).filter(function(r){return r.status==='pending';}).length});
|
|
65
|
+
}
|
|
66
|
+
function read(id,ref) {
|
|
67
|
+
var entry = snapshot(id).entries[ref];
|
|
68
|
+
if (!entry || entry.archived) throw new Error('User knowledge not found.');
|
|
69
|
+
var comments = Object.values(snapshot(id).reports).filter(function (item) { return item.commentEntryRef === ref; }).map(function (item) {
|
|
70
|
+
return { id: item.ref, body: item.observation, at: item.createdAt, author: { type: 'user', displayName: 'You' },
|
|
71
|
+
status: item.status === 'accepted' ? 'incorporated' : item.status,
|
|
72
|
+
review: item.reviewedAt ? { response: item.reason, at: item.reviewedAt, author: { displayName: 'Clay' } } : null };
|
|
73
|
+
});
|
|
74
|
+
return Object.assign({}, entry, { comments: comments, commentCount: comments.length });
|
|
75
|
+
}
|
|
76
|
+
function comment(id,args,source) {
|
|
77
|
+
var entry = read(id,args.ref);
|
|
78
|
+
return report(id,{observation:args.body,evidence:'Comment on '+entry.ref+': '+args.body,evidenceType:'explicit'},source,entry.ref);
|
|
79
|
+
}
|
|
80
|
+
function report(id,args,source,commentEntryRef) {
|
|
81
|
+
var state = snapshot(id);
|
|
82
|
+
var observation = text(args.observation,4000,true);
|
|
83
|
+
var evidence = text(args.evidence,4000,true);
|
|
84
|
+
var evidenceType = args.evidenceType || 'explicit';
|
|
85
|
+
if (['explicit','inferred'].indexOf(evidenceType)===-1) throw new Error('Invalid evidence type.');
|
|
86
|
+
var existing = Object.values(state.reports).find(function (r) {return r.observation===observation && r.evidence===evidence && r.source.session===source.session && r.source.project===source.project && r.status==='pending';});
|
|
87
|
+
if (existing) return existing;
|
|
88
|
+
if (Object.values(state.reports).filter(function (r) {return r.status==='pending';}).length>=200) throw new Error('Clay has 200 pending observations. Review those before reporting more.');
|
|
89
|
+
var record = {ref:'observation:'+crypto.randomBytes(12).toString('hex'),observation:observation,evidence:evidence,evidenceType:evidenceType,source:source,commentEntryRef:commentEntryRef || null,status:'pending',createdAt:Date.now()};
|
|
90
|
+
storage(id).append({kind:'user-knowledge',report:record});
|
|
91
|
+
try {deps.onReport(owner(id));} catch (error) { /* Durable pending report remains available for retry. */ }
|
|
92
|
+
return record;
|
|
93
|
+
}
|
|
94
|
+
function bind(binding) {
|
|
95
|
+
var originalOwner = binding.session && binding.session.ownerId;
|
|
96
|
+
function principal() {
|
|
97
|
+
var project = deps.getProject(binding.projectSlug);
|
|
98
|
+
var session = binding.session;
|
|
99
|
+
if (!project || !session || project.sm.sessions.get(session.localId)!==session || session.destroying) throw new Error('A live owner-bound session is required.');
|
|
100
|
+
if (session.ownerId !== originalOwner) throw new Error('Session ownership changed.');
|
|
101
|
+
if (deps.isMultiUser() && session.sessionVisibility === 'shared') throw new Error('Private User Knowledge is unavailable in shared conversations.');
|
|
102
|
+
if (deps.isMultiUser() && !canUsePersonalConnections(session, project.sm.sessions)) throw new Error('User Knowledge requires an owner-only conversation and Driver.');
|
|
103
|
+
var id = owner(session.ownerId);
|
|
104
|
+
var status = project.getStatus();
|
|
105
|
+
if (deps.isMultiUser() && !deps.canAccess(id,binding.projectSlug)) throw new Error('Project access was revoked.');
|
|
106
|
+
var mate = status.isMate && deps.resolveMate(id,status.mateId);
|
|
107
|
+
var clay = !!(mate && mate.builtinKey==='clay' && (!deps.isMultiUser() || mate.createdBy===id));
|
|
108
|
+
return {id:id,clay:clay,source:{project:binding.projectSlug,session:session.cliSessionId || 'local:'+session.localId,mateId:status.mateId || null}};
|
|
109
|
+
}
|
|
110
|
+
function protect(p) {
|
|
111
|
+
if (!deps.isMultiUser()) return;
|
|
112
|
+
var session = binding.session;
|
|
113
|
+
var manager = deps.getProject(binding.projectSlug).sm;
|
|
114
|
+
function mark(target) {
|
|
115
|
+
if (target.userKnowledgeOwnerId === p.id) return;
|
|
116
|
+
var previous = target.userKnowledgeOwnerId;
|
|
117
|
+
target.userKnowledgeOwnerId = p.id;
|
|
118
|
+
try { manager.saveSessionFile(target); } catch (error) { target.userKnowledgeOwnerId = previous; throw error; }
|
|
119
|
+
}
|
|
120
|
+
var provenance = session.sessionProvenance;
|
|
121
|
+
if (provenance && provenance.kind === 'worker') manager.sessions.forEach(function (parent) {
|
|
122
|
+
if (parent.sessionOriginId === provenance.parentSessionOriginId) mark(parent);
|
|
123
|
+
});
|
|
124
|
+
mark(session);
|
|
125
|
+
}
|
|
126
|
+
function curator() {var p=principal();if(!p.clay) throw new Error('Only your canonical Clay can curate User Knowledge.');return p;}
|
|
127
|
+
return {
|
|
128
|
+
isClay:function () {return principal().clay;},
|
|
129
|
+
list:function(args){var p=principal();var result=list(p.id,args);if(result.items.length)protect(p);return result;},
|
|
130
|
+
read:function(args){var p=principal();var result=read(p.id,args.ref);protect(p);return result;},
|
|
131
|
+
report:function(args){var p=principal();return report(p.id,args,p.source);},
|
|
132
|
+
pending:function(args){var p=curator();return page(Object.values(snapshot(p.id).reports).filter(function(r){return r.status==='pending';}),args);},
|
|
133
|
+
curate:function(args) {
|
|
134
|
+
var p=curator();
|
|
135
|
+
var state=snapshot(p.id);
|
|
136
|
+
var observation=state.reports[args.reportRef];
|
|
137
|
+
if (!observation) throw new Error('Observation not found.');
|
|
138
|
+
if (observation.status!=='pending') return {report:observation,entry:observation.entryRef ? state.entries[observation.entryRef]:null};
|
|
139
|
+
var action=args.action;
|
|
140
|
+
if (['save','decline','archive'].indexOf(action)===-1) throw new Error('Invalid curation action.');
|
|
141
|
+
var reason=text(args.reason,2000,true);
|
|
142
|
+
var previous=args.ref ? state.entries[args.ref]:null;
|
|
143
|
+
if (args.ref && !previous) throw new Error('User knowledge not found.');
|
|
144
|
+
if (previous && args.expectedRevision!==previous.revisions) throw new Error('Knowledge changed. Read it again before updating.');
|
|
145
|
+
var entry=null;
|
|
146
|
+
var now=Date.now();
|
|
147
|
+
if (action!=='decline') {
|
|
148
|
+
if(action==='archive' && !previous) throw new Error('Choose an existing entry to archive.');
|
|
149
|
+
var category=action==='archive' ? previous.category:text(args.category,32,true);
|
|
150
|
+
if(!/^[a-z][a-z0-9-]*$/.test(category)) throw new Error('Use a lowercase category.');
|
|
151
|
+
entry=Object.assign({},previous || {},{ref:previous ? previous.ref:'you:'+crypto.randomBytes(12).toString('hex'),category:category,kind:category,
|
|
152
|
+
title:action==='archive' ? previous.title:text(args.title,160,true),summary:action==='archive' ? previous.summary:text(args.summary,600,true),
|
|
153
|
+
body:action==='archive' ? previous.body:text(args.body,8000,true),archived:action==='archive',
|
|
154
|
+
createdAt:previous ? previous.createdAt:now,updatedAt:now,revisions:previous ? previous.revisions+1:1,
|
|
155
|
+
updatedBy:{type:'system',displayName:'Clay'},source:observation.source,evidence:observation.evidence,evidenceType:observation.evidenceType,reason:reason});
|
|
156
|
+
}
|
|
157
|
+
var reviewed=Object.assign({},observation,{status:action==='decline'?'declined':'accepted',reviewedAt:now,reason:reason,entryRef:entry && entry.ref});
|
|
158
|
+
var activity=entry ? {id:crypto.randomBytes(12).toString('hex'),ref:entry.ref,title:entry.title,summary:entry.summary,at:now,action:action==='archive'?'archive':(previous?'update':'create'),revision:entry.revisions}:null;
|
|
159
|
+
storage(p.id).append({kind:'user-knowledge',entry:entry,report:reviewed,activity:activity});
|
|
160
|
+
if(activity) {try {deps.onChange(p.id,activity);} catch(error) { /* Saved record survives notification failures. */ }}
|
|
161
|
+
return {entry:entry,report:reviewed};
|
|
162
|
+
}
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
return {bind:bind,list:list,read:read,report:report,comment:comment,attention:attention,acknowledge:acknowledge,activity:function(id,args){return page(snapshot(id).activity.slice().reverse(),args);},pendingCount:function(id){return Object.values(snapshot(id).reports).filter(function(r){return r.status==='pending';}).length;}};
|
|
166
|
+
}
|
|
167
|
+
module.exports={attachUserKnowledgeService:attachUserKnowledgeService};
|
package/lib/users-permissions.js
CHANGED
|
@@ -65,6 +65,9 @@ function attachPermissions(deps) {
|
|
|
65
65
|
|
|
66
66
|
function canAccessProject(userId, project) {
|
|
67
67
|
if (!project) return false;
|
|
68
|
+
if (project.isMate === true || (typeof project.slug === "string" && project.slug.indexOf("mate-") === 0)) {
|
|
69
|
+
return !!userId && !!project.ownerId && project.ownerId === userId;
|
|
70
|
+
}
|
|
68
71
|
// Public projects are accessible to all authenticated users
|
|
69
72
|
if (!project.visibility || project.visibility === "public") return true;
|
|
70
73
|
// Admin always has access
|
|
@@ -89,6 +92,10 @@ function attachPermissions(deps) {
|
|
|
89
92
|
function canAccessSession(userId, session, project) {
|
|
90
93
|
// Must have project access first
|
|
91
94
|
if (!canAccessProject(userId, project)) return false;
|
|
95
|
+
if (session.isMate === true || project.isMate === true || (typeof project.slug === "string" && project.slug.indexOf("mate-") === 0)) {
|
|
96
|
+
return !!userId && !!session.ownerId && session.ownerId === userId;
|
|
97
|
+
}
|
|
98
|
+
if (session.userKnowledgeOwnerId) return !!userId && session.userKnowledgeOwnerId === userId && session.ownerId === userId;
|
|
92
99
|
// Resolve the stored user record so client-supplied roles cannot elevate access.
|
|
93
100
|
var user = findUserById(userId);
|
|
94
101
|
if (user && user.role === "admin") return true;
|
package/lib/users-preferences.js
CHANGED
|
@@ -218,78 +218,6 @@ function attachPreferences(deps) {
|
|
|
218
218
|
return { error: "User not found" };
|
|
219
219
|
}
|
|
220
220
|
|
|
221
|
-
// --- Per-user Claude open mode ---
|
|
222
|
-
//
|
|
223
|
-
// Decides how Claude sessions are rendered when the user clicks into one:
|
|
224
|
-
// 'gui' - Clay's custom chat UI driven by the Claude Agent SDK.
|
|
225
|
-
// 'tui' - Embedded xterm running the real `claude` CLI. Keeps usage in
|
|
226
|
-
// the Interactive billing bucket post Agent SDK split.
|
|
227
|
-
//
|
|
228
|
-
// Cutover moment: midnight 2026-06-15 in UTC+14 (Line Islands - the
|
|
229
|
-
// earliest timezone on Earth in June, 14h ahead of UTC). On that instant
|
|
230
|
-
// two things happen at once:
|
|
231
|
-
// 1. The default flips from 'gui' to 'tui' (so usage lands in the
|
|
232
|
-
// Interactive billing bucket).
|
|
233
|
-
// 2. Any previously-stored per-user preference is treated as cleared
|
|
234
|
-
// so the new default applies to everyone. Toggles made on or after
|
|
235
|
-
// the cutover persist normally.
|
|
236
|
-
//
|
|
237
|
-
// The preference applies on the next session open. Currently displayed
|
|
238
|
-
// sessions are not re-rendered retroactively. The cross-mode click
|
|
239
|
-
// logic in project-sessions.js handles both directions in-place
|
|
240
|
-
// (prepareTuiSessionForGuiView for tui->gui rendering, claude --resume
|
|
241
|
-
// PTY respawn for gui->tui rendering on a born-tui session).
|
|
242
|
-
|
|
243
|
-
// Single cutover instant for both default flip and one-time reset:
|
|
244
|
-
// 2026-06-15 00:00 in UTC+14 = 2026-06-14 10:00 UTC.
|
|
245
|
-
var CLAUDE_OPEN_MODE_CUTOVER_MS = Date.UTC(2026, 5, 14, 10, 0, 0);
|
|
246
|
-
var CLAUDE_OPEN_MODE_RESET_MS = CLAUDE_OPEN_MODE_CUTOVER_MS;
|
|
247
|
-
|
|
248
|
-
function defaultClaudeOpenMode() {
|
|
249
|
-
// Reverted to GUI: the TUI default was a hedge against the (since-cancelled)
|
|
250
|
-
// Agent SDK separate-credit billing. Users who explicitly picked TUI after
|
|
251
|
-
// the cutover keep it (isStoredPreferenceLive); everyone else gets GUI.
|
|
252
|
-
return "gui";
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
// A stored preference counts only if it was set on or after the reset
|
|
256
|
-
// moment (or we haven't reached the reset moment yet). Older preferences
|
|
257
|
-
// are ignored so the new default applies once.
|
|
258
|
-
function isStoredPreferenceLive(user) {
|
|
259
|
-
if (Date.now() < CLAUDE_OPEN_MODE_RESET_MS) return true;
|
|
260
|
-
var setAt = user && user.claudeOpenModeSetAt;
|
|
261
|
-
return typeof setAt === "number" && setAt >= CLAUDE_OPEN_MODE_RESET_MS;
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
function getClaudeOpenMode(userId) {
|
|
265
|
-
var data = loadUsers();
|
|
266
|
-
var fallback = defaultClaudeOpenMode();
|
|
267
|
-
for (var i = 0; i < data.users.length; i++) {
|
|
268
|
-
if (data.users[i].id === userId) {
|
|
269
|
-
var m = data.users[i].claudeOpenMode;
|
|
270
|
-
if ((m === "gui" || m === "tui") && isStoredPreferenceLive(data.users[i])) return m;
|
|
271
|
-
return fallback;
|
|
272
|
-
}
|
|
273
|
-
}
|
|
274
|
-
return fallback;
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
function setClaudeOpenMode(userId, mode) {
|
|
278
|
-
// GUI is the default in every other resolution path, so an unrecognized
|
|
279
|
-
// value must land on GUI too. Only an explicit "tui" opts into terminal.
|
|
280
|
-
var normalized = (mode === "tui") ? "tui" : "gui";
|
|
281
|
-
var data = loadUsers();
|
|
282
|
-
for (var i = 0; i < data.users.length; i++) {
|
|
283
|
-
if (data.users[i].id === userId) {
|
|
284
|
-
data.users[i].claudeOpenMode = normalized;
|
|
285
|
-
data.users[i].claudeOpenModeSetAt = Date.now();
|
|
286
|
-
saveUsers(data);
|
|
287
|
-
return { ok: true, claudeOpenMode: normalized };
|
|
288
|
-
}
|
|
289
|
-
}
|
|
290
|
-
return { error: "User not found" };
|
|
291
|
-
}
|
|
292
|
-
|
|
293
221
|
// --- Per-user cursor sharing preference ---
|
|
294
222
|
|
|
295
223
|
function getCursorSharing(userId) {
|
|
@@ -512,8 +440,6 @@ function attachPreferences(deps) {
|
|
|
512
440
|
setChatLayout: setChatLayout,
|
|
513
441
|
getAutoContinue: getAutoContinue,
|
|
514
442
|
setAutoContinue: setAutoContinue,
|
|
515
|
-
getClaudeOpenMode: getClaudeOpenMode,
|
|
516
|
-
setClaudeOpenMode: setClaudeOpenMode,
|
|
517
443
|
getCursorSharing: getCursorSharing,
|
|
518
444
|
setCursorSharing: setCursorSharing,
|
|
519
445
|
getClaudeUserAllowList: getClaudeUserAllowList,
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
function attachProjectMateFilterPreferences(deps) {
|
|
2
|
+
var loadUsers = deps.loadUsers;
|
|
3
|
+
var saveUsers = deps.saveUsers;
|
|
4
|
+
var configModule = deps.config || require("./config");
|
|
5
|
+
|
|
6
|
+
function normalize(mode) {
|
|
7
|
+
return mode === "projects" || mode === "mates" ? mode : "all";
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function get(userId) {
|
|
11
|
+
var data = loadUsers();
|
|
12
|
+
for (var i = 0; i < data.users.length; i++) {
|
|
13
|
+
if (data.users[i].id === userId) return normalize(data.users[i].projectMateFilterMode);
|
|
14
|
+
}
|
|
15
|
+
var current = configModule.loadConfig() || {};
|
|
16
|
+
return normalize(current.projectMateFilterMode);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function set(userId, mode) {
|
|
20
|
+
if (mode !== "all" && mode !== "projects" && mode !== "mates") {
|
|
21
|
+
return { error: "Invalid project and Mate filter mode" };
|
|
22
|
+
}
|
|
23
|
+
var data = loadUsers();
|
|
24
|
+
for (var i = 0; i < data.users.length; i++) {
|
|
25
|
+
if (data.users[i].id === userId) {
|
|
26
|
+
data.users[i].projectMateFilterMode = mode;
|
|
27
|
+
saveUsers(data);
|
|
28
|
+
return { ok: true, projectMateFilterMode: mode };
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
var current = configModule.loadConfig() || {};
|
|
32
|
+
configModule.saveConfig(Object.assign({}, current, { projectMateFilterMode: mode }));
|
|
33
|
+
return { ok: true, projectMateFilterMode: mode };
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return { get: get, set: set };
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
module.exports = { attachProjectMateFilterPreferences: attachProjectMateFilterPreferences };
|
package/lib/users.js
CHANGED
|
@@ -6,6 +6,7 @@ var { CONFIG_DIR } = require("./config");
|
|
|
6
6
|
var { attachAuth } = require("./users-auth");
|
|
7
7
|
var { DEFAULT_PERMISSIONS, ALL_PERMISSIONS, attachPermissions } = require("./users-permissions");
|
|
8
8
|
var { attachPreferences } = require("./users-preferences");
|
|
9
|
+
var { attachProjectMateFilterPreferences } = require("./users-project-mate-filter-preferences");
|
|
9
10
|
var { attachHomeDockPreferences } = require("./users-home-dock-preferences");
|
|
10
11
|
var { attachHomeSurfacePreferences } = require("./users-home-surface-preferences");
|
|
11
12
|
var { attachSessionFolderPreferences } = require("./users-session-folder-preferences");
|
|
@@ -402,6 +403,7 @@ function removeExpiredInvites() {
|
|
|
402
403
|
var auth = attachAuth({ loadUsers: loadUsers, saveUsers: saveUsers, findAdmin: findAdmin });
|
|
403
404
|
var permissions = attachPermissions({ loadUsers: loadUsers, saveUsers: saveUsers, findUserById: findUserById });
|
|
404
405
|
var preferences = attachPreferences({ loadUsers: loadUsers, saveUsers: saveUsers });
|
|
406
|
+
var projectMateFilterPreferences = attachProjectMateFilterPreferences({ loadUsers: loadUsers, saveUsers: saveUsers });
|
|
405
407
|
var homeDockPreferences = attachHomeDockPreferences({ loadUsers: loadUsers, saveUsers: saveUsers });
|
|
406
408
|
var homeSurfacePreferences = attachHomeSurfacePreferences({ loadUsers: loadUsers, saveUsers: saveUsers });
|
|
407
409
|
var sessionFolderPreferences = attachSessionFolderPreferences({ loadUsers: loadUsers, saveUsers: saveUsers });
|
|
@@ -445,8 +447,8 @@ var getChatLayout = preferences.getChatLayout;
|
|
|
445
447
|
var setChatLayout = preferences.setChatLayout;
|
|
446
448
|
var getAutoContinue = preferences.getAutoContinue;
|
|
447
449
|
var setAutoContinue = preferences.setAutoContinue;
|
|
448
|
-
var
|
|
449
|
-
var
|
|
450
|
+
var getProjectMateFilterMode = projectMateFilterPreferences.get;
|
|
451
|
+
var setProjectMateFilterMode = projectMateFilterPreferences.set;
|
|
450
452
|
var getCursorSharing = preferences.getCursorSharing;
|
|
451
453
|
var setCursorSharing = preferences.setCursorSharing;
|
|
452
454
|
var getClaudeUserAllowList = preferences.getClaudeUserAllowList;
|
|
@@ -531,8 +533,8 @@ module.exports = {
|
|
|
531
533
|
setMateOnboarded: setMateOnboarded,
|
|
532
534
|
getAutoContinue: getAutoContinue,
|
|
533
535
|
setAutoContinue: setAutoContinue,
|
|
534
|
-
|
|
535
|
-
|
|
536
|
+
getProjectMateFilterMode: getProjectMateFilterMode,
|
|
537
|
+
setProjectMateFilterMode: setProjectMateFilterMode,
|
|
536
538
|
getCursorSharing: getCursorSharing,
|
|
537
539
|
setCursorSharing: setCursorSharing,
|
|
538
540
|
getClaudeUserAllowList: getClaudeUserAllowList,
|
|
@@ -87,7 +87,10 @@ function attachWorkspaceQueryAccess(deps) {
|
|
|
87
87
|
return { owned: owned, accessible: owned };
|
|
88
88
|
}
|
|
89
89
|
if (!principal.userId) return denied;
|
|
90
|
-
if (
|
|
90
|
+
if (status.isMate === true) {
|
|
91
|
+
var ownsMate = ownsMateProject(principal, status);
|
|
92
|
+
return { owned: ownsMate, accessible: ownsMate };
|
|
93
|
+
}
|
|
91
94
|
// status.projectOwnerId is never trusted on its own in multi-user mode: it
|
|
92
95
|
// is a snapshot that can outlive a transfer or a revocation, so treating it
|
|
93
96
|
// as ownership would let a stale status skip the authoritative record.
|
package/lib/ws-schema.js
CHANGED
|
@@ -346,6 +346,8 @@ var schema = {
|
|
|
346
346
|
// -----------------------------------------------------------------------
|
|
347
347
|
// Updates
|
|
348
348
|
// -----------------------------------------------------------------------
|
|
349
|
+
"update_check_failed": { direction: "s2c", handler: "lib/public/modules/app-messages.js", description: "Update check failure" },
|
|
350
|
+
"set_update_channel_result": { direction: "s2c", handler: "lib/public/modules/app-messages.js", description: "Release channel save acknowledgement" },
|
|
349
351
|
"set_update_channel": { direction: "c2s", handler: "lib/project-sessions.js", description: "Set the update channel (stable/beta)" },
|
|
350
352
|
"check_update": { direction: "c2s", handler: "lib/project-sessions.js", description: "Check if an update is available" },
|
|
351
353
|
"update_now": { direction: "c2s", handler: "lib/project-sessions.js", description: "Start the update process" },
|
|
@@ -454,10 +456,6 @@ var schema = {
|
|
|
454
456
|
"project_env_result": { direction: "s2c", handler: "lib/public/modules/app-messages.js", description: "Project .envrc content" },
|
|
455
457
|
"set_project_env": { direction: "c2s", handler: "lib/project-filesystem.js", description: "Write the project .envrc" },
|
|
456
458
|
"set_project_env_result": { direction: "s2c", handler: "lib/public/modules/app-messages.js", description: "Project .envrc write result" },
|
|
457
|
-
"read_global_claude_md": { direction: "c2s", handler: "lib/project-filesystem.js", description: "Read the global CLAUDE.md" },
|
|
458
|
-
"global_claude_md_result": { direction: "s2c", handler: "lib/public/modules/app-messages.js", description: "Global CLAUDE.md content" },
|
|
459
|
-
"write_global_claude_md": { direction: "c2s", handler: "lib/project-filesystem.js", description: "Write the global CLAUDE.md" },
|
|
460
|
-
"write_global_claude_md_result": { direction: "s2c", handler: "lib/public/modules/app-messages.js", description: "Global CLAUDE.md write result" },
|
|
461
459
|
"get_shared_env": { direction: "c2s", handler: "lib/project-filesystem.js", description: "Read the shared .envrc" },
|
|
462
460
|
"shared_env_result": { direction: "s2c", handler: "lib/public/modules/app-messages.js", description: "Shared .envrc content" },
|
|
463
461
|
"set_shared_env": { direction: "c2s", handler: "lib/project-filesystem.js", description: "Write the shared .envrc" },
|
|
@@ -572,6 +570,9 @@ var schema = {
|
|
|
572
570
|
"mate_delete": { direction: "c2s", handler: "lib/server-mates.js", description: "Delete a mate" },
|
|
573
571
|
"mate_deleted": { direction: "s2c", handler: "lib/public/modules/app-messages.js", description: "Mate was deleted" },
|
|
574
572
|
"mate_update": { direction: "c2s", handler: "lib/server-mates.js", description: "Update a mate's properties" },
|
|
573
|
+
"mate_instructions_get": { direction: "c2s", handler: "lib/server-mate-instructions.js", description: "Read the owner-scoped Mate identity prompt" },
|
|
574
|
+
"mate_instructions_set": { direction: "c2s", handler: "lib/server-mate-instructions.js", description: "Save a Mate identity prompt with revision checking" },
|
|
575
|
+
"mate_instructions_result": { direction: "s2c", handler: "lib/public/modules/home-mate-instructions.js", description: "Correlated prompt read or save result" },
|
|
575
576
|
"mate_updated": { direction: "s2c", handler: "lib/public/modules/app-messages.js", description: "Mate was updated" },
|
|
576
577
|
"mate_readd_builtin": { direction: "c2s", handler: "lib/server-mates.js", description: "Re-add a previously deleted built-in mate" },
|
|
577
578
|
"mate_list_available_builtins": { direction: "c2s", handler: "lib/server-mates.js", description: "List built-in mates available for re-adding" },
|
|
@@ -658,16 +659,63 @@ var schema = {
|
|
|
658
659
|
// Knowledge (mate knowledge files)
|
|
659
660
|
// -----------------------------------------------------------------------
|
|
660
661
|
"knowledge_list": { direction: "both", handler: "lib/project-knowledge.js", description: "Request (c2s) or receive (s2c) knowledge file list" },
|
|
662
|
+
"knowledge_search": { direction: "c2s", handler: "lib/project-knowledge.js", description: "Search bounded project Knowledge filenames and document content" },
|
|
663
|
+
"knowledge_search_results": { direction: "s2c", handler: "lib/public/modules/mate-knowledge-workbench.js", description: "Correlated bounded Knowledge document search results" },
|
|
661
664
|
"knowledge_read": { direction: "c2s", handler: "lib/project-knowledge.js", description: "Read a knowledge file" },
|
|
662
665
|
"knowledge_content": { direction: "s2c", handler: "lib/public/modules/app-messages.js", description: "Knowledge file content" },
|
|
663
666
|
"knowledge_save": { direction: "c2s", handler: "lib/project-knowledge.js", description: "Save a knowledge file" },
|
|
664
667
|
"knowledge_saved": { direction: "s2c", handler: "lib/public/modules/app-messages.js", description: "Knowledge file was saved" },
|
|
668
|
+
"knowledge_rename": { direction: "c2s", handler: "lib/project-knowledge.js", description: "Rename a knowledge Markdown document with a revision precondition" },
|
|
669
|
+
"knowledge_renamed": { direction: "s2c", handler: "lib/public/modules/mate-knowledge-workbench.js", description: "Knowledge document was renamed" },
|
|
670
|
+
"knowledge_error": { direction: "s2c", handler: "lib/public/modules/mate-knowledge-workbench.js", description: "Correlated Knowledge operation error or stale-write conflict" },
|
|
671
|
+
"knowledge_sync": { direction: "c2s", handler: "lib/project-knowledge.js", description: "Retry common Knowledge record synchronization after a canonical file mutation" },
|
|
672
|
+
"knowledge_synced": { direction: "s2c", handler: "lib/public/modules/mate-knowledge-workbench.js", description: "Knowledge record synchronization retry completed" },
|
|
665
673
|
"knowledge_delete": { direction: "c2s", handler: "lib/project-knowledge.js", description: "Delete a knowledge file" },
|
|
666
674
|
"knowledge_deleted": { direction: "s2c", handler: "lib/public/modules/app-messages.js", description: "Knowledge file was deleted" },
|
|
675
|
+
"knowledge_draft_save": { direction: "c2s", handler: "lib/project-knowledge.js", description: "Persist a versioned user- and workspace-bound Knowledge draft" },
|
|
676
|
+
"knowledge_draft_saved": { direction: "s2c", handler: "lib/public/modules/mate-knowledge-workbench.js", description: "Knowledge draft persistence acknowledgement" },
|
|
677
|
+
"knowledge_draft_delete": { direction: "c2s", handler: "lib/project-knowledge.js", description: "Remove a Knowledge draft through a submitted client version" },
|
|
678
|
+
"knowledge_draft_deleted": { direction: "s2c", handler: "lib/public/modules/mate-knowledge-workbench.js", description: "Knowledge draft removal acknowledgement" },
|
|
679
|
+
"knowledge_discard_draft": { direction: "c2s", handler: "lib/project-knowledge.js", description: "Discard a bounded draft version and read a guarded disk revision" },
|
|
680
|
+
"knowledge_draft_discarded":{ direction: "s2c", handler: "lib/public/modules/mate-knowledge-workbench.js", description: "Guarded disk content returned after draft discard" },
|
|
681
|
+
"knowledge_history": { direction: "both", handler: "lib/project-knowledge.js", description: "Request or receive bounded document history" },
|
|
682
|
+
"knowledge_history_read": { direction: "c2s", handler: "lib/project-knowledge.js", description: "Read one explicit bounded document history version" },
|
|
683
|
+
"knowledge_history_version":{ direction: "s2c", handler: "lib/public/modules/mate-knowledge-workbench.js", description: "Content for one explicit document history version" },
|
|
684
|
+
"knowledge_restore": { direction: "c2s", handler: "lib/project-knowledge.js", description: "Restore an explicit history version with a revision precondition" },
|
|
685
|
+
"knowledge_restored": { direction: "s2c", handler: "lib/public/modules/mate-knowledge-workbench.js", description: "Knowledge history version was restored" },
|
|
686
|
+
"knowledge_deleted_list": { direction: "both", handler: "lib/project-knowledge.js", description: "Request or receive recoverable deleted Knowledge documents" },
|
|
667
687
|
"knowledge_promote": { direction: "c2s", handler: "lib/project-knowledge.js", description: "Promote a knowledge file to always-active" },
|
|
668
688
|
"knowledge_promoted": { direction: "s2c", handler: "lib/public/modules/app-messages.js", description: "Knowledge file was promoted" },
|
|
669
689
|
"knowledge_depromote": { direction: "c2s", handler: "lib/project-knowledge.js", description: "Depromote a knowledge file from always-active" },
|
|
670
690
|
"knowledge_depromoted": { direction: "s2c", handler: "lib/public/modules/app-messages.js", description: "Knowledge file was depromoted" },
|
|
691
|
+
"knowledge_db_create": { direction: "c2s", handler: "lib/project-knowledge.js", description: "Create a first-class Mate Knowledge database" },
|
|
692
|
+
"knowledge_db_created": { direction: "s2c", handler: "lib/public/modules/mate-knowledge-workbench.js", description: "Knowledge database creation result" },
|
|
693
|
+
"knowledge_db_read": { direction: "c2s", handler: "lib/project-knowledge.js", description: "Read Knowledge database schema and views" },
|
|
694
|
+
"knowledge_db_content": { direction: "s2c", handler: "lib/public/modules/mate-knowledge-workbench.js", description: "Knowledge database schema and views" },
|
|
695
|
+
"knowledge_db_rename": { direction: "c2s", handler: "lib/project-knowledge.js", description: "Rename a Knowledge database with a revision precondition" },
|
|
696
|
+
"knowledge_db_renamed": { direction: "s2c", handler: "lib/public/modules/mate-knowledge-workbench.js", description: "Knowledge database rename result" },
|
|
697
|
+
"knowledge_db_archive": { direction: "c2s", handler: "lib/project-knowledge.js", description: "Recoverably archive a Knowledge database" },
|
|
698
|
+
"knowledge_db_archived": { direction: "s2c", handler: "lib/public/modules/mate-knowledge-workbench.js", description: "Knowledge database archive result" },
|
|
699
|
+
"knowledge_db_recover": { direction: "c2s", handler: "lib/project-knowledge.js", description: "Recover an archived Knowledge database" },
|
|
700
|
+
"knowledge_db_recovered": { direction: "s2c", handler: "lib/public/modules/mate-knowledge-workbench.js", description: "Knowledge database recovery result" },
|
|
701
|
+
"knowledge_db_schema_save": { direction: "c2s", handler: "lib/project-knowledge.js", description: "Save a validated Knowledge database schema" },
|
|
702
|
+
"knowledge_db_schema_saved":{ direction: "s2c", handler: "lib/public/modules/mate-knowledge-workbench.js", description: "Knowledge database schema result" },
|
|
703
|
+
"knowledge_db_view_save": { direction: "c2s", handler: "lib/project-knowledge.js", description: "Save a validated table view" },
|
|
704
|
+
"knowledge_db_view_saved": { direction: "s2c", handler: "lib/public/modules/mate-knowledge-workbench.js", description: "Knowledge database table view result" },
|
|
705
|
+
"knowledge_db_record_create": { direction: "c2s", handler: "lib/project-knowledge.js", description: "Create a typed Knowledge database record" },
|
|
706
|
+
"knowledge_db_record_created": { direction: "s2c", handler: "lib/public/modules/mate-knowledge-workbench.js", description: "Knowledge database record creation result" },
|
|
707
|
+
"knowledge_db_record_read": { direction: "c2s", handler: "lib/project-knowledge.js", description: "Read one Knowledge database record and history" },
|
|
708
|
+
"knowledge_db_record_content": { direction: "s2c", handler: "lib/public/modules/mate-knowledge-workbench.js", description: "Knowledge database record detail" },
|
|
709
|
+
"knowledge_db_record_update": { direction: "c2s", handler: "lib/project-knowledge.js", description: "Update a typed Knowledge database record" },
|
|
710
|
+
"knowledge_db_record_updated": { direction: "s2c", handler: "lib/public/modules/mate-knowledge-workbench.js", description: "Knowledge database record update result" },
|
|
711
|
+
"knowledge_db_record_archive": { direction: "c2s", handler: "lib/project-knowledge.js", description: "Recoverably archive a Knowledge database record" },
|
|
712
|
+
"knowledge_db_record_archived": { direction: "s2c", handler: "lib/public/modules/mate-knowledge-workbench.js", description: "Knowledge database record archive result" },
|
|
713
|
+
"knowledge_db_record_recover": { direction: "c2s", handler: "lib/project-knowledge.js", description: "Recover an archived Knowledge database record" },
|
|
714
|
+
"knowledge_db_record_recovered": { direction: "s2c", handler: "lib/public/modules/mate-knowledge-workbench.js", description: "Knowledge database record recovery result" },
|
|
715
|
+
"knowledge_db_query": { direction: "c2s", handler: "lib/project-knowledge.js", description: "Run a bounded validated deterministic Knowledge database query" },
|
|
716
|
+
"knowledge_db_query_results": { direction: "s2c", handler: "lib/public/modules/mate-knowledge-workbench.js", description: "Knowledge database query page and totals" },
|
|
717
|
+
"knowledge_db_sync": { direction: "c2s", handler: "lib/project-knowledge.js", description: "Retry Knowledge database summary projection" },
|
|
718
|
+
"knowledge_db_synced": { direction: "s2c", handler: "lib/public/modules/mate-knowledge-workbench.js", description: "Knowledge database summary projection retry result" },
|
|
671
719
|
|
|
672
720
|
// -----------------------------------------------------------------------
|
|
673
721
|
// Memory (mate memory)
|
|
@@ -46,7 +46,7 @@ AcpProcessManager.prototype.start = function() {
|
|
|
46
46
|
var args = self.opts.args ? self.opts.args.slice() : [];
|
|
47
47
|
var env = Object.assign({}, process.env, self.opts.env || {});
|
|
48
48
|
|
|
49
|
-
self._log("Spawning:", self.executablePath, args.
|
|
49
|
+
self._log("Spawning:", self.executablePath, "(" + args.length + " arguments; values omitted)");
|
|
50
50
|
self.proc = spawn(self.executablePath, args, {
|
|
51
51
|
stdio: ["pipe", "pipe", "pipe"],
|
|
52
52
|
env: env,
|
|
@@ -82,7 +82,7 @@ AcpProcessManager.prototype.start = function() {
|
|
|
82
82
|
try {
|
|
83
83
|
self._handleMessage(JSON.parse(line));
|
|
84
84
|
} catch (e) {
|
|
85
|
-
self._error("Failed to parse
|
|
85
|
+
self._error("Failed to parse protocol message (content omitted)");
|
|
86
86
|
}
|
|
87
87
|
});
|
|
88
88
|
self.rl.on("close", function() {
|