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.
Files changed (231) hide show
  1. package/LICENSES/README.md +4 -0
  2. package/LICENSES/dicebear-avatars.md +16 -0
  3. package/bin/cli.js +9 -91
  4. package/lib/builtin-mates.js +7 -7
  5. package/lib/daemon-project-access.js +7 -1
  6. package/lib/daemon-settings.js +11 -0
  7. package/lib/daemon.js +10 -37
  8. package/lib/dicebear-avatar.js +62 -0
  9. package/lib/knowledge-db-service.js +175 -0
  10. package/lib/knowledge-db-validation.js +0 -0
  11. package/lib/knowledge-document-index.js +49 -0
  12. package/lib/knowledge-draft-store.js +56 -0
  13. package/lib/knowledge-file-store.js +199 -0
  14. package/lib/knowledge-markdown.js +128 -0
  15. package/lib/linear-store.js +35 -8
  16. package/lib/mate-identity-file.js +39 -0
  17. package/lib/mate-knowledge-mcp-server.js +29 -6
  18. package/lib/mate-knowledge-service.js +23 -2
  19. package/lib/mate-knowledge-sync.js +37 -22
  20. package/lib/mate-ready-creation.js +2 -1
  21. package/lib/mate-workspace-color.js +4 -0
  22. package/lib/mates-identity.js +4 -4
  23. package/lib/mates-prompts.js +4 -3
  24. package/lib/mates.js +30 -11
  25. package/lib/os-users.js +5 -16
  26. package/lib/project-connection.js +36 -9
  27. package/lib/project-debate-proposal.js +6 -0
  28. package/lib/project-debate-workspace.js +43 -0
  29. package/lib/project-debate.js +4 -2
  30. package/lib/project-filesystem.js +21 -39
  31. package/lib/project-knowledge.js +315 -136
  32. package/lib/project-mate-creation-proposal.js +1 -0
  33. package/lib/project-mate-interaction.js +7 -6
  34. package/lib/project-server-settings.js +31 -0
  35. package/lib/project-sessions.js +92 -182
  36. package/lib/project-user-knowledge.js +60 -0
  37. package/lib/project.js +62 -21
  38. package/lib/public/app.js +79 -71
  39. package/lib/public/clay-studio-wordmark-bold.svg +20 -0
  40. package/lib/public/css/clay-workspace.css +105 -0
  41. package/lib/public/css/debates-workbench.css +47 -0
  42. package/lib/public/css/home-debate-live.css +26 -0
  43. package/lib/public/css/home-hub.css +3 -1
  44. package/lib/public/css/home-mate-model-picker.css +15 -131
  45. package/lib/public/css/home-mate-settings.css +168 -16
  46. package/lib/public/css/icon-strip.css +249 -32
  47. package/lib/public/css/input.css +3 -1
  48. package/lib/public/css/mate-conversation.css +176 -0
  49. package/lib/public/css/mate-knowledge-workbench.css +118 -0
  50. package/lib/public/css/mate-workspace-color.css +16 -0
  51. package/lib/public/css/mates.css +28 -28
  52. package/lib/public/css/menus.css +3 -0
  53. package/lib/public/css/messages.css +1 -114
  54. package/lib/public/css/mobile-nav.css +16 -16
  55. package/lib/public/css/overlays.css +15 -0
  56. package/lib/public/css/project-logs.css +7 -0
  57. package/lib/public/css/project-mate-creation.css +105 -0
  58. package/lib/public/css/project-settings-modal.css +56 -0
  59. package/lib/public/css/right-workbench.css +3 -3
  60. package/lib/public/css/server-settings-system.css +125 -0
  61. package/lib/public/css/session-folders.css +80 -21
  62. package/lib/public/css/session-github.css +25 -16
  63. package/lib/public/css/settings-ui.css +247 -22
  64. package/lib/public/css/shared-browser.css +7 -3
  65. package/lib/public/css/sidebar.css +183 -107
  66. package/lib/public/css/title-bar.css +156 -1
  67. package/lib/public/css/worker-delegation.css +47 -0
  68. package/lib/public/css/worker-proposal.css +72 -150
  69. package/lib/public/css/you.css +74 -0
  70. package/lib/public/index.html +206 -548
  71. package/lib/public/modules/account-island.js +232 -0
  72. package/lib/public/modules/admin-settings-email.js +197 -0
  73. package/lib/public/modules/admin-settings-invitations.js +183 -0
  74. package/lib/public/modules/admin-settings-projects.js +198 -0
  75. package/lib/public/modules/admin-settings-ui.js +100 -0
  76. package/lib/public/modules/admin-settings-users.js +331 -0
  77. package/lib/public/modules/admin.js +19 -949
  78. package/lib/public/modules/app-cursors.js +19 -43
  79. package/lib/public/modules/app-debate-ui.js +4 -3
  80. package/lib/public/modules/app-dm.js +0 -2
  81. package/lib/public/modules/app-header.js +106 -0
  82. package/lib/public/modules/app-message-router.js +12 -3
  83. package/lib/public/modules/app-messages.js +47 -61
  84. package/lib/public/modules/app-notifications.js +4 -7
  85. package/lib/public/modules/app-projects.js +5 -38
  86. package/lib/public/modules/app-rate-limit.js +70 -12
  87. package/lib/public/modules/app-rendering.js +12 -13
  88. package/lib/public/modules/avatar.js +30 -9
  89. package/lib/public/modules/chat-render-runtime.js +16 -4
  90. package/lib/public/modules/command-palette.js +2 -15
  91. package/lib/public/modules/context-sources.js +27 -18
  92. package/lib/public/modules/cursor-sharing-preference.js +3 -3
  93. package/lib/public/modules/debate-controls.js +299 -0
  94. package/lib/public/modules/debate.js +2 -0
  95. package/lib/public/modules/debates-workbench.js +339 -0
  96. package/lib/public/modules/default-ai.js +88 -65
  97. package/lib/public/modules/header-tui-font.js +10 -20
  98. package/lib/public/modules/home-debate-controls.js +18 -289
  99. package/lib/public/modules/home-mate-avatar-editor.js +228 -0
  100. package/lib/public/modules/home-mate-color-picker.js +66 -0
  101. package/lib/public/modules/home-mate-creation.js +1 -1
  102. package/lib/public/modules/home-mate-instructions.js +90 -0
  103. package/lib/public/modules/home-mate-model-picker.js +80 -271
  104. package/lib/public/modules/home-mate-profile-editor.js +72 -0
  105. package/lib/public/modules/home-mate-settings.js +118 -125
  106. package/lib/public/modules/home-shell.js +3 -0
  107. package/lib/public/modules/home-surface-boot.js +1 -9
  108. package/lib/public/modules/icon-strip-tooltip.js +95 -0
  109. package/lib/public/modules/knowledge-db-edit-state.js +35 -0
  110. package/lib/public/modules/knowledge-db-workbench.js +234 -0
  111. package/lib/public/modules/knowledge-history-state.js +55 -0
  112. package/lib/public/modules/knowledge-markdown-editor.js +57 -0
  113. package/lib/public/modules/knowledge-preview-links.js +70 -0
  114. package/lib/public/modules/knowledge-workbench-shell.js +21 -0
  115. package/lib/public/modules/linear-settings.js +3 -2
  116. package/lib/public/modules/markdown.js +5 -6
  117. package/lib/public/modules/mate-knowledge-workbench-state.js +174 -0
  118. package/lib/public/modules/mate-knowledge-workbench.js +500 -0
  119. package/lib/public/modules/mate-knowledge.js +11 -3
  120. package/lib/public/modules/mate-management.js +54 -10
  121. package/lib/public/modules/mate-sidebar.js +11 -1
  122. package/lib/public/modules/mate-workspace-color.js +33 -0
  123. package/lib/public/modules/mention.js +5 -13
  124. package/lib/public/modules/notification-settings.js +81 -0
  125. package/lib/public/modules/notifications.js +44 -28
  126. package/lib/public/modules/personal-settings-dialog.js +51 -0
  127. package/lib/public/modules/profile.js +66 -24
  128. package/lib/public/modules/project-chat-identity.js +79 -0
  129. package/lib/public/modules/project-debate-planning-workspace.js +250 -0
  130. package/lib/public/modules/project-debate-workspace.js +185 -0
  131. package/lib/public/modules/project-header-identity.js +127 -0
  132. package/lib/public/modules/project-logs-render.js +9 -4
  133. package/lib/public/modules/project-mate-creation-workspace.js +414 -0
  134. package/lib/public/modules/project-mate-navigation.js +238 -0
  135. package/lib/public/modules/project-session-navigation.js +29 -0
  136. package/lib/public/modules/project-settings-environment.js +106 -0
  137. package/lib/public/modules/project-settings.js +72 -628
  138. package/lib/public/modules/project-switcher.js +6 -8
  139. package/lib/public/modules/projects-hub-view.js +5 -6
  140. package/lib/public/modules/projects-hub.js +2 -6
  141. package/lib/public/modules/qrcode.js +27 -8
  142. package/lib/public/modules/search-clay-chat.js +8 -11
  143. package/lib/public/modules/server-settings-controls.js +316 -0
  144. package/lib/public/modules/server-settings-environment.js +194 -0
  145. package/lib/public/modules/server-settings-navigation.js +39 -0
  146. package/lib/public/modules/server-settings-state.js +54 -0
  147. package/lib/public/modules/server-settings.js +116 -788
  148. package/lib/public/modules/server-storage-settings.js +84 -0
  149. package/lib/public/modules/session-activity-tooltip.js +12 -0
  150. package/lib/public/modules/session-create-form.js +86 -14
  151. package/lib/public/modules/session-create.js +50 -7
  152. package/lib/public/modules/session-github.js +2 -1
  153. package/lib/public/modules/session-list-visibility.js +41 -0
  154. package/lib/public/modules/session-presence-ui.js +3 -0
  155. package/lib/public/modules/session-title-marquee.js +51 -0
  156. package/lib/public/modules/settings-env-values.js +34 -0
  157. package/lib/public/modules/settings-select.js +121 -0
  158. package/lib/public/modules/shared-browser.js +22 -12
  159. package/lib/public/modules/sidebar-mates.js +3 -50
  160. package/lib/public/modules/sidebar-mobile.js +63 -60
  161. package/lib/public/modules/sidebar-projects.js +84 -97
  162. package/lib/public/modules/sidebar-session-folders-ui.js +2 -2
  163. package/lib/public/modules/sidebar-session-hierarchy.js +149 -70
  164. package/lib/public/modules/sidebar-sessions.js +49 -50
  165. package/lib/public/modules/sidebar.js +11 -13
  166. package/lib/public/modules/stt-picker.js +14 -5
  167. package/lib/public/modules/terminal-prefs.js +22 -0
  168. package/lib/public/modules/theme.js +88 -52
  169. package/lib/public/modules/thinking-lifecycle.js +7 -6
  170. package/lib/public/modules/tool-palette-order.js +4 -3
  171. package/lib/public/modules/tool-palette.js +13 -3
  172. package/lib/public/modules/tools.js +22 -22
  173. package/lib/public/modules/user-settings-account.js +318 -0
  174. package/lib/public/modules/user-settings-appearance.js +55 -0
  175. package/lib/public/modules/user-settings-editors.js +24 -0
  176. package/lib/public/modules/user-settings-integrations.js +104 -0
  177. package/lib/public/modules/user-settings-layout.js +80 -0
  178. package/lib/public/modules/user-settings-preferences.js +48 -0
  179. package/lib/public/modules/user-settings-profile.js +53 -0
  180. package/lib/public/modules/user-settings-themes.js +38 -0
  181. package/lib/public/modules/user-settings-voice.js +75 -0
  182. package/lib/public/modules/user-settings.js +53 -480
  183. package/lib/public/modules/worker-proposal.js +42 -8
  184. package/lib/public/modules/worker-runtime-picker.js +115 -0
  185. package/lib/public/modules/you-activity.js +121 -0
  186. package/lib/public/modules/you.js +251 -0
  187. package/lib/public/style.css +20 -8
  188. package/lib/public/vendor/codemirror/LICENSE +21 -0
  189. package/lib/public/vendor/codemirror/codemirror.css +344 -0
  190. package/lib/public/vendor/codemirror/codemirror.js +9884 -0
  191. package/lib/public/vendor/codemirror/continuelist.js +101 -0
  192. package/lib/public/vendor/codemirror/markdown.js +886 -0
  193. package/lib/public/vendor/codemirror/show-hint.css +37 -0
  194. package/lib/public/vendor/codemirror/show-hint.js +523 -0
  195. package/lib/sdk-bridge.js +15 -3
  196. package/lib/sdk-message-processor.js +16 -4
  197. package/lib/sdk-quota-snapshot.js +37 -0
  198. package/lib/server-admin.js +11 -0
  199. package/lib/server-appearance-settings.js +71 -0
  200. package/lib/server-home-chat-events.js +1 -1
  201. package/lib/server-home-chat.js +25 -3
  202. package/lib/server-home-debate-planning.js +4 -1
  203. package/lib/server-home-debates.js +2 -0
  204. package/lib/server-home-mate-creation.js +4 -1
  205. package/lib/server-linear.js +14 -8
  206. package/lib/server-mate-instructions.js +44 -0
  207. package/lib/server-mates.js +14 -4
  208. package/lib/server-notification-settings.js +67 -0
  209. package/lib/server-palette.js +1 -1
  210. package/lib/server-settings.js +69 -1
  211. package/lib/server-storage.js +57 -0
  212. package/lib/server-user-knowledge.js +119 -0
  213. package/lib/server.js +31 -3
  214. package/lib/session-visibility.js +7 -2
  215. package/lib/sessions.js +35 -10
  216. package/lib/startup-projects.js +40 -0
  217. package/lib/storage-native-walk.js +85 -0
  218. package/lib/storage-usage.js +144 -0
  219. package/lib/user-knowledge-service.js +167 -0
  220. package/lib/users-permissions.js +7 -0
  221. package/lib/users-preferences.js +0 -74
  222. package/lib/users-project-mate-filter-preferences.js +39 -0
  223. package/lib/users.js +6 -4
  224. package/lib/workspace-query-access.js +4 -1
  225. package/lib/ws-schema.js +52 -4
  226. package/lib/yoke/acp-process-manager.js +2 -2
  227. package/lib/yoke/adapters/codex.js +12 -52
  228. package/lib/yoke/codex-app-server.js +2 -2
  229. package/lib/yoke/codex-model-catalog.js +52 -0
  230. package/lib/yoke/codex-rate-limits.js +26 -0
  231. 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};
@@ -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;
@@ -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 getClaudeOpenMode = preferences.getClaudeOpenMode;
449
- var setClaudeOpenMode = preferences.setClaudeOpenMode;
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
- getClaudeOpenMode: getClaudeOpenMode,
535
- setClaudeOpenMode: setClaudeOpenMode,
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 (ownsMateProject(principal, status)) return { owned: true, accessible: true };
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.join(" "));
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 line:", line.substring(0, 200));
85
+ self._error("Failed to parse protocol message (content omitted)");
86
86
  }
87
87
  });
88
88
  self.rl.on("close", function() {