clay-server 5.1.0-beta.1 → 6.0.0-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (222) 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/daemon-project-access.js +7 -1
  5. package/lib/daemon-settings.js +11 -0
  6. package/lib/daemon.js +10 -37
  7. package/lib/dicebear-avatar.js +62 -0
  8. package/lib/knowledge-db-service.js +175 -0
  9. package/lib/knowledge-db-validation.js +0 -0
  10. package/lib/knowledge-document-index.js +49 -0
  11. package/lib/knowledge-draft-store.js +56 -0
  12. package/lib/knowledge-file-store.js +199 -0
  13. package/lib/knowledge-markdown.js +128 -0
  14. package/lib/linear-store.js +35 -8
  15. package/lib/mate-knowledge-mcp-server.js +29 -6
  16. package/lib/mate-knowledge-service.js +23 -2
  17. package/lib/mate-knowledge-sync.js +36 -21
  18. package/lib/mate-workspace-color.js +4 -0
  19. package/lib/mates.js +18 -0
  20. package/lib/os-users.js +5 -16
  21. package/lib/project-connection.js +36 -9
  22. package/lib/project-debate-proposal.js +6 -0
  23. package/lib/project-debate-workspace.js +43 -0
  24. package/lib/project-debate.js +1 -0
  25. package/lib/project-filesystem.js +21 -39
  26. package/lib/project-knowledge.js +315 -136
  27. package/lib/project-mate-creation-proposal.js +1 -0
  28. package/lib/project-mate-interaction.js +2 -2
  29. package/lib/project-server-settings.js +31 -0
  30. package/lib/project-sessions.js +92 -182
  31. package/lib/project-user-knowledge.js +60 -0
  32. package/lib/project.js +59 -18
  33. package/lib/public/app.js +79 -71
  34. package/lib/public/clay-studio-wordmark-bold.svg +20 -0
  35. package/lib/public/css/clay-workspace.css +105 -0
  36. package/lib/public/css/debates-workbench.css +47 -0
  37. package/lib/public/css/home-debate-live.css +26 -0
  38. package/lib/public/css/home-hub.css +3 -1
  39. package/lib/public/css/home-mate-model-picker.css +15 -131
  40. package/lib/public/css/home-mate-settings.css +168 -16
  41. package/lib/public/css/icon-strip.css +249 -32
  42. package/lib/public/css/input.css +3 -1
  43. package/lib/public/css/mate-conversation.css +176 -0
  44. package/lib/public/css/mate-knowledge-workbench.css +118 -0
  45. package/lib/public/css/mate-workspace-color.css +16 -0
  46. package/lib/public/css/mates.css +28 -28
  47. package/lib/public/css/messages.css +1 -114
  48. package/lib/public/css/mobile-nav.css +16 -16
  49. package/lib/public/css/overlays.css +8 -0
  50. package/lib/public/css/project-logs.css +7 -0
  51. package/lib/public/css/project-mate-creation.css +105 -0
  52. package/lib/public/css/project-settings-modal.css +56 -0
  53. package/lib/public/css/right-workbench.css +3 -3
  54. package/lib/public/css/server-settings-system.css +125 -0
  55. package/lib/public/css/session-folders.css +80 -21
  56. package/lib/public/css/session-github.css +25 -16
  57. package/lib/public/css/settings-ui.css +247 -22
  58. package/lib/public/css/shared-browser.css +2 -0
  59. package/lib/public/css/sidebar.css +183 -107
  60. package/lib/public/css/title-bar.css +156 -1
  61. package/lib/public/css/worker-delegation.css +47 -0
  62. package/lib/public/css/worker-proposal.css +72 -150
  63. package/lib/public/css/you.css +74 -0
  64. package/lib/public/index.html +203 -547
  65. package/lib/public/modules/account-island.js +232 -0
  66. package/lib/public/modules/admin-settings-email.js +197 -0
  67. package/lib/public/modules/admin-settings-invitations.js +183 -0
  68. package/lib/public/modules/admin-settings-projects.js +198 -0
  69. package/lib/public/modules/admin-settings-ui.js +100 -0
  70. package/lib/public/modules/admin-settings-users.js +331 -0
  71. package/lib/public/modules/admin.js +19 -949
  72. package/lib/public/modules/app-cursors.js +19 -43
  73. package/lib/public/modules/app-debate-ui.js +4 -3
  74. package/lib/public/modules/app-dm.js +0 -2
  75. package/lib/public/modules/app-header.js +106 -0
  76. package/lib/public/modules/app-message-router.js +12 -3
  77. package/lib/public/modules/app-messages.js +47 -61
  78. package/lib/public/modules/app-notifications.js +4 -7
  79. package/lib/public/modules/app-projects.js +5 -38
  80. package/lib/public/modules/app-rate-limit.js +70 -12
  81. package/lib/public/modules/app-rendering.js +12 -13
  82. package/lib/public/modules/avatar.js +25 -9
  83. package/lib/public/modules/chat-render-runtime.js +16 -4
  84. package/lib/public/modules/command-palette.js +2 -15
  85. package/lib/public/modules/context-sources.js +27 -18
  86. package/lib/public/modules/cursor-sharing-preference.js +3 -3
  87. package/lib/public/modules/debate-controls.js +299 -0
  88. package/lib/public/modules/debate.js +2 -0
  89. package/lib/public/modules/debates-workbench.js +339 -0
  90. package/lib/public/modules/default-ai.js +88 -65
  91. package/lib/public/modules/header-tui-font.js +10 -20
  92. package/lib/public/modules/home-debate-controls.js +18 -289
  93. package/lib/public/modules/home-mate-avatar-editor.js +228 -0
  94. package/lib/public/modules/home-mate-color-picker.js +66 -0
  95. package/lib/public/modules/home-mate-creation.js +1 -1
  96. package/lib/public/modules/home-mate-instructions.js +90 -0
  97. package/lib/public/modules/home-mate-model-picker.js +80 -271
  98. package/lib/public/modules/home-mate-profile-editor.js +72 -0
  99. package/lib/public/modules/home-mate-settings.js +118 -125
  100. package/lib/public/modules/home-shell.js +3 -0
  101. package/lib/public/modules/home-surface-boot.js +1 -9
  102. package/lib/public/modules/icon-strip-tooltip.js +95 -0
  103. package/lib/public/modules/knowledge-db-edit-state.js +35 -0
  104. package/lib/public/modules/knowledge-db-workbench.js +234 -0
  105. package/lib/public/modules/knowledge-history-state.js +55 -0
  106. package/lib/public/modules/knowledge-markdown-editor.js +57 -0
  107. package/lib/public/modules/knowledge-preview-links.js +70 -0
  108. package/lib/public/modules/knowledge-workbench-shell.js +21 -0
  109. package/lib/public/modules/linear-settings.js +3 -2
  110. package/lib/public/modules/markdown.js +5 -6
  111. package/lib/public/modules/mate-knowledge-workbench-state.js +174 -0
  112. package/lib/public/modules/mate-knowledge-workbench.js +500 -0
  113. package/lib/public/modules/mate-knowledge.js +11 -3
  114. package/lib/public/modules/mate-management.js +54 -10
  115. package/lib/public/modules/mate-sidebar.js +11 -1
  116. package/lib/public/modules/mate-workspace-color.js +33 -0
  117. package/lib/public/modules/mention.js +5 -13
  118. package/lib/public/modules/notification-settings.js +81 -0
  119. package/lib/public/modules/notifications.js +44 -28
  120. package/lib/public/modules/personal-settings-dialog.js +51 -0
  121. package/lib/public/modules/profile.js +66 -24
  122. package/lib/public/modules/project-chat-identity.js +79 -0
  123. package/lib/public/modules/project-debate-planning-workspace.js +250 -0
  124. package/lib/public/modules/project-debate-workspace.js +185 -0
  125. package/lib/public/modules/project-header-identity.js +127 -0
  126. package/lib/public/modules/project-logs-render.js +9 -4
  127. package/lib/public/modules/project-mate-creation-workspace.js +414 -0
  128. package/lib/public/modules/project-mate-navigation.js +238 -0
  129. package/lib/public/modules/project-session-navigation.js +29 -0
  130. package/lib/public/modules/project-settings-environment.js +106 -0
  131. package/lib/public/modules/project-settings.js +72 -628
  132. package/lib/public/modules/project-switcher.js +6 -8
  133. package/lib/public/modules/projects-hub-view.js +5 -6
  134. package/lib/public/modules/projects-hub.js +2 -6
  135. package/lib/public/modules/qrcode.js +27 -8
  136. package/lib/public/modules/search-clay-chat.js +8 -11
  137. package/lib/public/modules/server-settings-controls.js +316 -0
  138. package/lib/public/modules/server-settings-environment.js +194 -0
  139. package/lib/public/modules/server-settings-navigation.js +39 -0
  140. package/lib/public/modules/server-settings-state.js +54 -0
  141. package/lib/public/modules/server-settings.js +116 -788
  142. package/lib/public/modules/server-storage-settings.js +84 -0
  143. package/lib/public/modules/session-activity-tooltip.js +12 -0
  144. package/lib/public/modules/session-create-form.js +86 -14
  145. package/lib/public/modules/session-create.js +50 -7
  146. package/lib/public/modules/session-github.js +2 -1
  147. package/lib/public/modules/session-list-visibility.js +41 -0
  148. package/lib/public/modules/session-presence-ui.js +3 -0
  149. package/lib/public/modules/session-title-marquee.js +51 -0
  150. package/lib/public/modules/settings-env-values.js +34 -0
  151. package/lib/public/modules/settings-select.js +121 -0
  152. package/lib/public/modules/shared-browser.js +10 -7
  153. package/lib/public/modules/sidebar-mates.js +3 -50
  154. package/lib/public/modules/sidebar-mobile.js +63 -60
  155. package/lib/public/modules/sidebar-projects.js +84 -97
  156. package/lib/public/modules/sidebar-session-folders-ui.js +2 -2
  157. package/lib/public/modules/sidebar-session-hierarchy.js +149 -70
  158. package/lib/public/modules/sidebar-sessions.js +49 -50
  159. package/lib/public/modules/sidebar.js +11 -13
  160. package/lib/public/modules/stt-picker.js +14 -5
  161. package/lib/public/modules/terminal-prefs.js +22 -0
  162. package/lib/public/modules/theme.js +88 -52
  163. package/lib/public/modules/thinking-lifecycle.js +7 -6
  164. package/lib/public/modules/tool-palette-order.js +4 -3
  165. package/lib/public/modules/tool-palette.js +13 -3
  166. package/lib/public/modules/tools.js +22 -22
  167. package/lib/public/modules/user-settings-account.js +318 -0
  168. package/lib/public/modules/user-settings-appearance.js +55 -0
  169. package/lib/public/modules/user-settings-editors.js +24 -0
  170. package/lib/public/modules/user-settings-integrations.js +104 -0
  171. package/lib/public/modules/user-settings-layout.js +80 -0
  172. package/lib/public/modules/user-settings-preferences.js +48 -0
  173. package/lib/public/modules/user-settings-profile.js +53 -0
  174. package/lib/public/modules/user-settings-themes.js +38 -0
  175. package/lib/public/modules/user-settings-voice.js +75 -0
  176. package/lib/public/modules/user-settings.js +53 -480
  177. package/lib/public/modules/worker-proposal.js +42 -8
  178. package/lib/public/modules/worker-runtime-picker.js +115 -0
  179. package/lib/public/modules/you-activity.js +121 -0
  180. package/lib/public/modules/you.js +251 -0
  181. package/lib/public/style.css +20 -8
  182. package/lib/public/vendor/codemirror/LICENSE +21 -0
  183. package/lib/public/vendor/codemirror/codemirror.css +344 -0
  184. package/lib/public/vendor/codemirror/codemirror.js +9884 -0
  185. package/lib/public/vendor/codemirror/continuelist.js +101 -0
  186. package/lib/public/vendor/codemirror/markdown.js +886 -0
  187. package/lib/public/vendor/codemirror/show-hint.css +37 -0
  188. package/lib/public/vendor/codemirror/show-hint.js +523 -0
  189. package/lib/sdk-bridge.js +15 -3
  190. package/lib/sdk-message-processor.js +16 -4
  191. package/lib/sdk-quota-snapshot.js +37 -0
  192. package/lib/server-admin.js +11 -0
  193. package/lib/server-appearance-settings.js +71 -0
  194. package/lib/server-home-chat-events.js +1 -1
  195. package/lib/server-home-chat.js +25 -3
  196. package/lib/server-home-debate-planning.js +4 -1
  197. package/lib/server-home-debates.js +2 -0
  198. package/lib/server-home-mate-creation.js +4 -1
  199. package/lib/server-linear.js +14 -8
  200. package/lib/server-mate-instructions.js +44 -0
  201. package/lib/server-mates.js +12 -3
  202. package/lib/server-notification-settings.js +67 -0
  203. package/lib/server-palette.js +1 -1
  204. package/lib/server-settings.js +69 -1
  205. package/lib/server-storage.js +56 -0
  206. package/lib/server-user-knowledge.js +119 -0
  207. package/lib/server.js +31 -3
  208. package/lib/session-visibility.js +7 -2
  209. package/lib/sessions.js +34 -9
  210. package/lib/startup-projects.js +40 -0
  211. package/lib/storage-usage.js +127 -0
  212. package/lib/user-knowledge-service.js +167 -0
  213. package/lib/users-permissions.js +7 -0
  214. package/lib/users-preferences.js +0 -74
  215. package/lib/users-project-mate-filter-preferences.js +39 -0
  216. package/lib/users.js +6 -4
  217. package/lib/workspace-query-access.js +4 -1
  218. package/lib/ws-schema.js +52 -4
  219. package/lib/yoke/adapters/codex.js +8 -45
  220. package/lib/yoke/codex-model-catalog.js +40 -0
  221. package/lib/yoke/codex-rate-limits.js +26 -0
  222. package/package.json +12 -3
@@ -1,184 +1,363 @@
1
- var fs = require("fs");
2
1
  var path = require("path");
2
+ var crypto = require("crypto");
3
3
  var mateSync = require("./mate-knowledge-sync");
4
+ var knowledgeFiles = require("./knowledge-file-store");
5
+ var markdown = require("./knowledge-markdown");
6
+ var draftStore = require("./knowledge-draft-store");
7
+ var documentIndexModule = require("./knowledge-document-index");
8
+ var knowledgeDbModule = require("./knowledge-db-service");
9
+ var knowledgeImport = require("./knowledge-import");
4
10
 
5
- /**
6
- * Attach knowledge file management to a project context.
7
- *
8
- * ctx fields:
9
- * cwd, isMate, sendTo, matesModule, projectOwnerId
10
- */
11
11
  function attachKnowledge(ctx) {
12
12
  var cwd = ctx.cwd;
13
13
  var isMate = ctx.isMate;
14
14
  var sendTo = ctx.sendTo;
15
15
  var matesModule = ctx.matesModule;
16
16
  var getProjectOwnerId = ctx.getProjectOwnerId;
17
+ var metadataKey = crypto.createHash("sha256").update(path.resolve(cwd)).digest("hex").slice(0, 24);
18
+ var metadataRoot = path.join(ctx.knowledgeBaseDir || path.join(cwd, ".clay"), "knowledge-documents", metadataKey);
19
+ var files = knowledgeFiles.createKnowledgeFileStore(cwd, { metaDir: metadataRoot });
20
+ var drafts = draftStore.createKnowledgeDraftStore(ctx.knowledgeBaseDir || path.join(cwd, ".clay"), path.resolve(cwd));
21
+ var indexer = documentIndexModule.createKnowledgeDocumentIndex({ list: listKnowledgeFiles, read: readEntry, maxDocumentBytes: knowledgeFiles.MAX_CONTENT_BYTES });
22
+ var dbScope = isMate ? mateSync.scopeForMateDir(cwd) : null;
23
+ var dbImporter = dbScope ? knowledgeImport.createKnowledgeImporter({ scopeId: dbScope.scopeId, baseDir: ctx.knowledgeBaseDir }) : null;
24
+ var databases = isMate ? knowledgeDbModule.createKnowledgeDbService({
25
+ projectDir: cwd,
26
+ authorizeTextReference: function (id) { return files.list().some(function (file) { return file.id === id && file.itemType === "text"; }); },
27
+ projectSummary: dbImporter ? function (summary) {
28
+ return dbImporter.importRecord({ importKey: "knowledge-database:" + summary.databaseId, sourceHash: String(summary.revision) + ":" + String(summary.archived),
29
+ kind: "knowledge-database-summary", name: summary.name, content: summary.content,
30
+ source: { namespace: "knowledge-database", databaseId: summary.databaseId, relPath: "knowledge/.clay-db/databases.db" }, actor: summary.actor });
31
+ } : null,
32
+ }) : null;
33
+ var databaseReady = databases && databases.hasStore() ? databases.initialize() : null;
34
+ if (databaseReady) databaseReady.catch(function (error) { console.error("[knowledge-db] Failed to initialize:", error.message); });
35
+
36
+ function canWrite(ws) {
37
+ if (typeof ctx.canManageKnowledge === "function") return ctx.canManageKnowledge(ws) === true;
38
+ var ownerId = getProjectOwnerId();
39
+ return !ownerId || !!(ws && ws._clayUser && ws._clayUser.id === ownerId);
40
+ }
41
+
42
+ function actor(ws) {
43
+ var user = (ws && ws._clayUser) || null;
44
+ return { type: user ? "user" : "system", userId: user ? user.id : null, displayName: user ? (user.displayName || user.username) : null };
45
+ }
46
+
47
+ function mirror(ws, name) {
48
+ if (!isMate) return { ok: true, names: [] };
49
+ var summary = mateSync.syncMateSource({ mateDir: cwd, fileName: name, baseDir: ctx.knowledgeBaseDir, actor: actor(ws) });
50
+ return { ok: !!summary && summary.failed === 0, names: [name], error: summary && summary.errors[0] ? summary.errors[0].message : "Knowledge index synchronization failed." };
51
+ }
52
+
53
+ function mirrorMany(ws, names) {
54
+ var failures = [];
55
+ for (var i = 0; i < names.length; i++) {
56
+ var mirrored = mirror(ws, names[i]);
57
+ if (!mirrored.ok) failures.push(mirrored.error);
58
+ }
59
+ return { ok: failures.length === 0, names: names.slice(), error: failures[0] || null };
60
+ }
61
+
62
+ function addSyncState(payload, sync) {
63
+ if (sync.ok) return payload;
64
+ return Object.assign(payload, { indexSyncPending: true, syncNames: sync.names, syncError: sync.error });
65
+ }
17
66
 
18
67
  function listKnowledgeFiles() {
19
- var knowledgeDir = path.join(cwd, "knowledge");
20
- var files = [];
21
- try {
22
- var entries = fs.readdirSync(knowledgeDir);
23
- for (var ki = 0; ki < entries.length; ki++) {
24
- if (entries[ki] === "session-digests.jsonl") continue;
25
- if (entries[ki] === "sticky-notes.md") continue;
26
- if (entries[ki] === "memory-summary.md") continue;
27
- if (entries[ki].endsWith(".md") || entries[ki].endsWith(".jsonl")) {
28
- var stat = fs.statSync(path.join(knowledgeDir, entries[ki]));
29
- files.push({ name: entries[ki], size: stat.size, mtime: stat.mtimeMs, common: false });
30
- }
31
- }
32
- } catch (e) { /* dir may not exist */ }
33
- files.sort(function (a, b) { return b.mtime - a.mtime; });
68
+ var listed = files.list();
69
+ for (var i = 0; i < listed.length; i++) listed[i].common = false;
70
+ if (!isMate) return listed;
71
+ var mateCtx = matesModule.buildMateCtx(getProjectOwnerId());
72
+ var thisMateId = path.basename(cwd);
73
+ for (var p = 0; p < listed.length; p++) listed[p].promoted = matesModule.isPromoted(mateCtx, thisMateId, listed[p].name);
74
+ var common = matesModule.getCommonKnowledgeForMate(mateCtx, thisMateId);
75
+ for (var c = 0; c < common.length; c++) {
76
+ if (common[c].ownMateId !== thisMateId) listed.push(Object.assign({}, common[c], { id: "shared:" + common[c].ownMateId + ":" + common[c].name,
77
+ itemType: /\.jsonl$/i.test(common[c].name) ? "db" : "text", writable: false, revision: null }));
78
+ }
79
+ return listed;
80
+ }
81
+
82
+ function listKnowledgeItems(ws) {
83
+ var textItems = listKnowledgeFiles();
84
+ if (!databases || !databases.hasStore()) return textItems;
85
+ function combine(result) { return textItems.concat(result.databases.map(function (item) { return Object.assign({}, item, { writable: canWrite(ws), common: false }); })); }
86
+ if (databases.isReady()) return combine(databases.listCached({}));
87
+ return databaseReady.then(function () { return combine(databases.listCached({})); });
88
+ }
34
89
 
35
- if (isMate) {
90
+ function response(ws, msg, payload) {
91
+ sendTo(ws, Object.assign({ requestId: msg.requestId || null }, payload));
92
+ }
93
+
94
+ function failure(ws, msg, error, extra) {
95
+ response(ws, msg, Object.assign({ type: "knowledge_error", operation: msg.type, name: msg.name || msg.from || null,
96
+ code: error.code || "FAILED", error: error.message || "Knowledge operation failed.", currentRevision: error.currentRevision || null }, extra || {}));
97
+ }
98
+
99
+ function requireWrite(ws, msg) {
100
+ if (canWrite(ws)) return true;
101
+ failure(ws, msg, Object.assign(new Error("Only the Mate owner can change Knowledge documents."), { code: "FORBIDDEN" }));
102
+ return false;
103
+ }
104
+
105
+ function readEntry(file) {
106
+ if (file.common && file.ownMateId && isMate) {
36
107
  var mateCtx = matesModule.buildMateCtx(getProjectOwnerId());
37
- var thisMateId = path.basename(cwd);
38
- for (var pi = 0; pi < files.length; pi++) {
39
- files[pi].promoted = matesModule.isPromoted(mateCtx, thisMateId, files[pi].name);
40
- }
41
- var commonFiles = matesModule.getCommonKnowledgeForMate(mateCtx, thisMateId);
42
- for (var ci = 0; ci < commonFiles.length; ci++) {
43
- if (commonFiles[ci].ownMateId !== thisMateId) {
44
- files.push(commonFiles[ci]);
45
- }
46
- }
108
+ return matesModule.readCommonKnowledgeFile(mateCtx, file.ownMateId, file.name);
47
109
  }
48
- return files;
110
+ return files.read(file.name).content;
49
111
  }
50
112
 
51
- function listKnowledgeFilesBasic() {
52
- var knowledgeDir = path.join(cwd, "knowledge");
53
- var files = [];
54
- try {
55
- var entries = fs.readdirSync(knowledgeDir);
56
- for (var ki = 0; ki < entries.length; ki++) {
57
- if (entries[ki].endsWith(".md") || entries[ki].endsWith(".jsonl")) {
58
- var stat = fs.statSync(path.join(knowledgeDir, entries[ki]));
59
- files.push({ name: entries[ki], size: stat.size, mtime: stat.mtimeMs });
113
+ function documentIndex() {
114
+ return indexer.reconcile();
115
+ }
116
+
117
+ function findDocument(index, name, id) {
118
+ var matches;
119
+ if (id) { matches = index.filter(function (item) { return item.id === id; }); return matches.length === 1 ? matches[0] : null; }
120
+ matches = index.filter(function (item) { return item.name === name; });
121
+ return matches.length === 1 ? matches[0] : null;
122
+ }
123
+
124
+ function linksFor(index, target) {
125
+ var outgoing = []; var backlinks = [];
126
+ for (var i = 0; i < target.parsed.links.length; i++) {
127
+ var link = target.parsed.links[i]; var resolved = markdown.resolveLink(target.name, link, index, target.id);
128
+ var headingValid = !link.heading || !!(resolved && resolved.parsed.headings.some(function (heading) { return heading.slug === markdown.slug(link.heading); }));
129
+ outgoing.push({ kind: link.kind, raw: link.raw, label: link.alias || link.target || link.heading, target: link.target, heading: link.heading,
130
+ headingValid: headingValid, documentId: resolved && resolved.id || null, documentKey: resolved && resolved.key || null,
131
+ name: resolved && resolved.name || null, external: link.external === true, self: !link.target && !!link.heading, unresolved: !resolved && !link.external });
132
+ }
133
+ for (var d = 0; d < index.length; d++) {
134
+ if (index[d] === target) continue;
135
+ for (var l = 0; l < index[d].parsed.links.length; l++) {
136
+ var resolvedTarget = markdown.resolveLink(index[d].name, index[d].parsed.links[l], index, index[d].id);
137
+ if (resolvedTarget && resolvedTarget.id === target.id) {
138
+ backlinks.push({ documentId: index[d].id, documentKey: index[d].key, name: index[d].name, heading: index[d].parsed.links[l].heading,
139
+ snippet: markdown.snippet(index[d].content, index[d].parsed.links[l].start) }); break;
60
140
  }
61
141
  }
62
- } catch (e) {}
63
- files.sort(function (a, b) { return b.mtime - a.mtime; });
64
- if (isMate) {
65
- var mateCtx = matesModule.buildMateCtx(getProjectOwnerId());
66
- var thisMateId = path.basename(cwd);
67
- for (var pi = 0; pi < files.length; pi++) {
68
- files[pi].common = false;
69
- files[pi].promoted = matesModule.isPromoted(mateCtx, thisMateId, files[pi].name);
70
- }
71
- var commonFiles = matesModule.getCommonKnowledgeForMate(mateCtx, thisMateId);
72
- for (var ci = 0; ci < commonFiles.length; ci++) {
73
- if (commonFiles[ci].ownMateId !== thisMateId) files.push(commonFiles[ci]);
142
+ }
143
+ return { outgoing: outgoing, backlinks: backlinks };
144
+ }
145
+
146
+ function relativeLink(from, to, withExtension) {
147
+ var value = path.posix.relative(path.posix.dirname(from), to) || path.posix.basename(to);
148
+ if (withExtension === false) value = value.replace(/\.md$/i, "");
149
+ return value;
150
+ }
151
+
152
+ function rewriteMoveLinks(index, target, nextName) {
153
+ var changes = [];
154
+ for (var i = 0; i < index.length; i++) {
155
+ var source = index[i];
156
+ if (source.common || source.itemType === "db" || !source.writable) continue;
157
+ var replacements = [];
158
+ for (var l = 0; l < source.parsed.links.length; l++) {
159
+ var link = source.parsed.links[l]; var resolved = markdown.resolveLink(source.name, link, index, source.id);
160
+ if (!resolved || source.id !== target.id && resolved.id !== target.id) continue;
161
+ var sourceAfter = source.id === target.id ? nextName : source.name;
162
+ var targetAfter = resolved.id === target.id ? nextName : resolved.name;
163
+ var nextTarget = relativeLink(sourceAfter, targetAfter, link.kind === "markdown" || link.kind === "image");
164
+ var suffix = link.heading ? "#" + link.heading : ""; var replacement;
165
+ if (link.kind === "wiki" || link.kind === "embed") replacement = (link.kind === "embed" ? "!" : "") + "[[" + nextTarget + suffix + (link.alias ? "|" + link.alias : "") + "]]";
166
+ else replacement = (link.kind === "image" ? "!" : "") + "[" + link.alias + "](" + nextTarget + suffix + ")";
167
+ replacements.push({ start: link.start, end: link.end, value: replacement });
74
168
  }
169
+ if (!replacements.length) continue;
170
+ var content = source.content;
171
+ for (var r = replacements.length - 1; r >= 0; r--) content = content.slice(0, replacements[r].start) + replacements[r].value + content.slice(replacements[r].end);
172
+ changes.push({ id: source.id, name: source.name, revision: source.revision, content: content, count: replacements.length });
75
173
  }
76
- return files;
174
+ return changes;
77
175
  }
78
176
 
79
- // Mirror a Mate-owned knowledge file into the Clay-wide Knowledge backend
80
- // after the legacy write has already succeeded. The legacy file stays
81
- // authoritative; this only keeps the new scope from going stale.
82
- function mirrorMateSource(ws, fileName) {
83
- if (!isMate) return;
84
- var user = (ws && ws._clayUser) || null;
85
- mateSync.syncQuietly({
86
- mateDir: cwd,
87
- fileName: fileName,
88
- actor: {
89
- type: user ? "user" : "system",
90
- userId: user ? user.id : null,
91
- displayName: user ? (user.displayName || user.username) : null,
92
- },
93
- });
177
+ function searchFiles(query, offset, limit) {
178
+ var start = Math.max(0, Number(offset) || 0); var count = Math.max(1, Math.min(100, Number(limit) || 50)); var direct = indexer.search(query, start, count); var needle = String(query || "").trim().toLowerCase();
179
+ if (!databases || !databases.hasStore()) return direct;
180
+ function combineSearch(result) {
181
+ var databaseMatches = result.databases.filter(function (item) { return needle && (item.name.toLowerCase().indexOf(needle) !== -1 || item.schema.some(function (field) { return field.name.toLowerCase().indexOf(needle) !== -1 || field.type.indexOf(needle) !== -1; })); }).map(function (item) { return Object.assign({}, item, { itemType: "db", writable: true, common: false, snippet: "Database · " + item.recordCount + " records" }); });
182
+ if (!databaseMatches.length) return direct;
183
+ var files = direct.files.slice(); var textTotal = direct.total; var databaseOffset = Math.max(0, start - textTotal); var room = count - files.length;
184
+ if (room > 0) files = files.concat(databaseMatches.slice(databaseOffset, databaseOffset + room));
185
+ return { files: files, total: textTotal + databaseMatches.length, offset: start, limit: count, complete: direct.complete, errors: direct.errors };
186
+ }
187
+ if (databases.isReady()) return combineSearch(databases.listCached({}));
188
+ return databaseReady.then(function () { return combineSearch(databases.listCached({})); });
94
189
  }
95
190
 
96
191
  function handleKnowledgeMessage(ws, msg) {
97
192
  if (msg.type === "knowledge_list") {
98
- sendTo(ws, { type: "knowledge_list", files: listKnowledgeFiles() });
193
+ try { var listed = listKnowledgeItems(ws); if (listed && typeof listed.then === "function") listed.then(function (items) { response(ws, msg, { type: "knowledge_list", files: items, drafts: drafts.list(ws && ws._clayUser && ws._clayUser.id || "system") }); }).catch(function (error) { failure(ws, msg, error); }); else response(ws, msg, { type: "knowledge_list", files: listed, drafts: drafts.list(ws && ws._clayUser && ws._clayUser.id || "system") }); } catch (error) { failure(ws, msg, error); }
194
+ return true;
195
+ }
196
+ if (msg.type === "knowledge_search") {
197
+ try { var searched = searchFiles(msg.query, msg.offset, msg.limit); if (searched && typeof searched.then === "function") searched.then(function (result) { response(ws, msg, Object.assign({ type: "knowledge_search_results", query: String(msg.query || "") }, result)); }).catch(function (error) { failure(ws, msg, error); }); else response(ws, msg, Object.assign({ type: "knowledge_search_results", query: String(msg.query || "") }, searched)); } catch (error) { failure(ws, msg, error); }
99
198
  return true;
100
199
  }
101
-
102
200
  if (msg.type === "knowledge_read") {
103
- if (!msg.name) return true;
104
- var safeName = path.basename(msg.name);
105
- if (msg.common && msg.ownMateId && isMate) {
106
- var mateCtx = matesModule.buildMateCtx(getProjectOwnerId());
107
- try {
108
- var content = matesModule.readCommonKnowledgeFile(mateCtx, msg.ownMateId, safeName);
109
- sendTo(ws, { type: "knowledge_content", name: safeName, content: content, common: true, ownMateId: msg.ownMateId });
110
- } catch (e) {
111
- sendTo(ws, { type: "knowledge_content", name: safeName, content: "", error: "File not found", common: true });
112
- }
113
- } else {
114
- var filePath = path.join(cwd, "knowledge", safeName);
115
- try {
116
- var content = fs.readFileSync(filePath, "utf8");
117
- sendTo(ws, { type: "knowledge_content", name: safeName, content: content });
118
- } catch (e) {
119
- sendTo(ws, { type: "knowledge_content", name: safeName, content: "", error: "File not found" });
201
+ try {
202
+ if (msg.common && msg.ownMateId && isMate) {
203
+ if (!knowledgeFiles.validName(msg.name)) throw new Error("Invalid knowledge file name.");
204
+ var mateCtx = matesModule.buildMateCtx(getProjectOwnerId());
205
+ var sharedContent = matesModule.readCommonKnowledgeFile(mateCtx, msg.ownMateId, msg.name);
206
+ var sharedParsed = markdown.parse(sharedContent); var sharedIndexState = documentIndex();
207
+ var sharedIndexed = findDocument(sharedIndexState.documents, msg.name, msg.documentId); var sharedRelations = sharedIndexed ? linksFor(sharedIndexState.documents, sharedIndexed) : { outgoing: [], backlinks: [] };
208
+ response(ws, msg, { type: "knowledge_content", name: msg.name, content: sharedContent, common: true, ownMateId: msg.ownMateId, writable: false, revision: null,
209
+ outline: sharedParsed.headings, outgoing: sharedRelations.outgoing, backlinks: sharedRelations.backlinks,
210
+ properties: sharedParsed.properties, tags: sharedParsed.tags, propertyErrors: sharedParsed.propertyErrors,
211
+ indexComplete: sharedIndexState.complete, indexErrors: sharedIndexState.errors });
212
+ } else {
213
+ var item = files.read(msg.name);
214
+ var indexState = documentIndex(); var indexed = findDocument(indexState.documents, item.name, msg.documentId || item.id); var relations = indexed ? linksFor(indexState.documents, indexed) : { outgoing: [], backlinks: [] };
215
+ var parsed = indexed ? indexed.parsed : markdown.parse(item.content);
216
+ var userId = ws && ws._clayUser && ws._clayUser.id || "system"; var recovered = drafts.read(userId, item.id);
217
+ response(ws, msg, { type: "knowledge_content", id: item.id, name: item.name, content: item.content, revision: item.revision, itemType: item.itemType,
218
+ writable: item.itemType === "text" && canWrite(ws), outline: parsed.headings, outgoing: relations.outgoing, backlinks: relations.backlinks,
219
+ properties: parsed.properties, tags: parsed.tags, propertyErrors: parsed.propertyErrors, recoveredDraft: recovered,
220
+ indexComplete: indexState.complete, indexErrors: indexState.errors });
120
221
  }
121
- }
222
+ } catch (error) { failure(ws, msg, error); }
122
223
  return true;
123
224
  }
124
-
125
225
  if (msg.type === "knowledge_save") {
126
- if (!msg.name || typeof msg.content !== "string") return true;
127
- var safeName = path.basename(msg.name);
128
- if (!safeName.endsWith(".md") && !safeName.endsWith(".jsonl")) safeName += ".md";
129
- var knowledgeDir = path.join(cwd, "knowledge");
130
- fs.mkdirSync(knowledgeDir, { recursive: true });
131
- fs.writeFileSync(path.join(knowledgeDir, safeName), msg.content);
132
- mirrorMateSource(ws, safeName);
133
- sendTo(ws, { type: "knowledge_saved", name: safeName });
134
- sendTo(ws, { type: "knowledge_list", files: listKnowledgeFilesBasic() });
226
+ if (!requireWrite(ws, msg)) return true;
227
+ try {
228
+ var saved = files.save(msg.name, msg.content, msg.expectedRevision || null);
229
+ var savedSync = mirror(ws, saved.name);
230
+ if (msg.documentId) drafts.remove(ws._clayUser && ws._clayUser.id || "system", msg.documentId, msg.draftVersion);
231
+ response(ws, msg, addSyncState({ type: "knowledge_saved", id: saved.id, name: saved.name, revision: saved.revision }, savedSync));
232
+ sendTo(ws, { type: "knowledge_list", files: listKnowledgeFiles() });
233
+ } catch (error) { failure(ws, msg, error); }
234
+ return true;
235
+ }
236
+ if (msg.type === "knowledge_rename") {
237
+ if (!requireWrite(ws, msg)) return true;
238
+ try {
239
+ var beforeMoveState = documentIndex(); var beforeMove = beforeMoveState.documents; var moveTarget = findDocument(beforeMove, msg.from, msg.documentId); var linkChanges = moveTarget ? rewriteMoveLinks(beforeMove, moveTarget, msg.name) : [];
240
+ var renamed = files.rename(msg.from, msg.name, msg.expectedRevision || null); var linkUpdateFailures = []; var updatedLinks = 0;
241
+ var updatedDocuments = []; var mirrorNames = [msg.from, renamed.name];
242
+ for (var u = 0; u < linkChanges.length; u++) {
243
+ var change = linkChanges[u]; var writeName = change.id === renamed.id ? renamed.name : change.name; var expected = change.id === renamed.id ? renamed.revision : change.revision;
244
+ try { var rewritten = files.save(writeName, change.content, expected); updatedLinks += change.count; updatedDocuments.push(rewritten); mirrorNames.push(rewritten.name); }
245
+ catch (linkError) { linkUpdateFailures.push({ name: writeName, error: linkError.message }); }
246
+ }
247
+ renamed = files.read(renamed.name);
248
+ var uniqueMirrorNames = mirrorNames.filter(function (name, at) { return mirrorNames.indexOf(name) === at; }); var renameSync = mirrorMany(ws, uniqueMirrorNames);
249
+ response(ws, msg, addSyncState({ type: "knowledge_renamed", id: renamed.id, from: msg.from, name: renamed.name, revision: renamed.revision,
250
+ content: renamed.content, updatedLinks: updatedLinks, linkUpdateFailures: linkUpdateFailures,
251
+ updatedDocuments: updatedDocuments.map(function (item) { return { id: item.id, name: item.name, content: item.content, revision: item.revision }; }) }, renameSync));
252
+ sendTo(ws, { type: "knowledge_list", files: listKnowledgeFiles() });
253
+ } catch (error) { failure(ws, msg, error); }
135
254
  return true;
136
255
  }
137
-
138
256
  if (msg.type === "knowledge_delete") {
139
- if (!msg.name) return true;
140
- var safeName = path.basename(msg.name);
141
- var filePath = path.join(cwd, "knowledge", safeName);
142
- try { fs.unlinkSync(filePath); } catch (e) {}
143
- // Reconciling after the unlink appends a tombstone, so a deleted file
144
- // does not stay live in the new backend.
145
- mirrorMateSource(ws, safeName);
146
- sendTo(ws, { type: "knowledge_deleted", name: safeName });
147
- sendTo(ws, { type: "knowledge_list", files: listKnowledgeFilesBasic() });
257
+ if (!requireWrite(ws, msg)) return true;
258
+ try {
259
+ var removed = files.remove(msg.name, msg.expectedRevision || null);
260
+ var deleteSync = mirror(ws, removed.name);
261
+ response(ws, msg, addSyncState({ type: "knowledge_deleted", name: removed.name }, deleteSync));
262
+ sendTo(ws, { type: "knowledge_list", files: listKnowledgeFiles() });
263
+ } catch (error) { failure(ws, msg, error); }
148
264
  return true;
149
265
  }
150
-
151
- if (msg.type === "knowledge_promote") {
152
- if (!isMate || !msg.name) return true;
153
- var safeName = path.basename(msg.name);
154
- var mateCtx = matesModule.buildMateCtx(getProjectOwnerId());
155
- var thisMateId = path.basename(cwd);
156
- var mate = matesModule.getMate(mateCtx, thisMateId);
157
- var mateName = (mate && mate.name) || null;
158
- matesModule.promoteKnowledge(mateCtx, thisMateId, mateName, safeName);
159
- sendTo(ws, { type: "knowledge_promoted", name: safeName });
160
- sendTo(ws, { type: "knowledge_list", files: listKnowledgeFiles() });
266
+ if (msg.type === "knowledge_sync") {
267
+ if (!requireWrite(ws, msg)) return true;
268
+ try {
269
+ var syncNames = Array.isArray(msg.names) ? msg.names.slice(0, 4) : [msg.name];
270
+ if (!syncNames.length) throw new Error("A Knowledge source is required.");
271
+ for (var n = 0; n < syncNames.length; n++) if (!knowledgeFiles.validName(syncNames[n])) throw new Error("Invalid knowledge file name.");
272
+ var retrySync = mirrorMany(ws, syncNames);
273
+ if (!retrySync.ok) throw Object.assign(new Error(retrySync.error), { code: "SYNC_FAILED" });
274
+ response(ws, msg, { type: "knowledge_synced", names: syncNames });
275
+ } catch (error) { failure(ws, msg, error, { diskCommitted: true, names: msg.names || [msg.name] }); }
161
276
  return true;
162
277
  }
163
-
164
- if (msg.type === "knowledge_depromote") {
165
- if (!isMate || !msg.name) return true;
166
- var safeName = path.basename(msg.name);
167
- var mateCtx = matesModule.buildMateCtx(getProjectOwnerId());
168
- var thisMateId = path.basename(cwd);
169
- matesModule.depromoteKnowledge(mateCtx, thisMateId, safeName);
170
- sendTo(ws, { type: "knowledge_depromoted", name: safeName });
171
- sendTo(ws, { type: "knowledge_list", files: listKnowledgeFiles() });
278
+ if (msg.type === "knowledge_draft_save") {
279
+ if (!requireWrite(ws, msg)) return true;
280
+ try {
281
+ var savedDraft = drafts.save(ws._clayUser && ws._clayUser.id || "system", msg.documentId, { name: msg.name, content: msg.content, baseRevision: msg.baseRevision,
282
+ cursor: msg.cursor, clientVersion: msg.clientVersion, newDocument: msg.newDocument });
283
+ response(ws, msg, { type: "knowledge_draft_saved", documentId: msg.documentId, savedAt: savedDraft.savedAt, clientVersion: savedDraft.clientVersion });
284
+ } catch (error) { failure(ws, msg, error); }
285
+ return true;
286
+ }
287
+ if (msg.type === "knowledge_draft_delete") {
288
+ if (!requireWrite(ws, msg)) return true;
289
+ try { var removedDraft = drafts.remove(ws._clayUser && ws._clayUser.id || "system", msg.documentId, msg.throughVersion); response(ws, msg, { type: "knowledge_draft_deleted", documentId: msg.documentId, removed: removedDraft }); }
290
+ catch (error) { failure(ws, msg, error); }
291
+ return true;
292
+ }
293
+ if (msg.type === "knowledge_discard_draft") {
294
+ if (!requireWrite(ws, msg)) return true;
295
+ try {
296
+ var disk = files.read(msg.name); if (msg.expectedRevision && disk.revision !== msg.expectedRevision) throw Object.assign(new Error("The disk version changed again."), { code: "STALE", currentRevision: disk.revision });
297
+ var removed = drafts.remove(ws._clayUser && ws._clayUser.id || "system", msg.documentId, msg.throughVersion);
298
+ response(ws, msg, { type: "knowledge_draft_discarded", documentId: msg.documentId, removed: removed, id: disk.id, name: disk.name, content: disk.content, revision: disk.revision });
299
+ } catch (error) { failure(ws, msg, error); }
300
+ return true;
301
+ }
302
+ if (msg.type === "knowledge_history") {
303
+ try { response(ws, msg, { type: "knowledge_history", documentId: msg.documentId, versions: files.history(msg.documentId) }); }
304
+ catch (error) { failure(ws, msg, error); }
305
+ return true;
306
+ }
307
+ if (msg.type === "knowledge_history_read") {
308
+ try { response(ws, msg, Object.assign({ type: "knowledge_history_version", documentId: msg.documentId }, files.readHistory(msg.documentId, msg.version))); }
309
+ catch (error) { failure(ws, msg, error); }
310
+ return true;
311
+ }
312
+ if (msg.type === "knowledge_deleted_list") {
313
+ if (!databases || !databases.hasStore()) { try { response(ws, msg, { type: "knowledge_deleted_list", files: files.deleted() }); } catch (error) { failure(ws, msg, error); } return true; }
314
+ databases.list({ archived: true }).then(function (result) { var archivedDatabases = result.databases.map(function (item) { return Object.assign({}, item, { itemType: "db", writable: canWrite(ws) }); }); response(ws, msg, { type: "knowledge_deleted_list", files: files.deleted().concat(archivedDatabases) }); }).catch(function (error) { failure(ws, msg, error); });
315
+ return true;
316
+ }
317
+ if (msg.type === "knowledge_restore") {
318
+ if (!requireWrite(ws, msg)) return true;
319
+ try {
320
+ var restored = files.restore(msg.documentId, msg.version, msg.name, msg.expectedRevision || null); var restoreSync = mirror(ws, restored.name);
321
+ response(ws, msg, addSyncState({ type: "knowledge_restored", id: restored.id, name: restored.name, revision: restored.revision }, restoreSync));
322
+ sendTo(ws, { type: "knowledge_list", files: listKnowledgeFiles() });
323
+ } catch (error) { failure(ws, msg, error); }
324
+ return true;
325
+ }
326
+ if (msg.type === "knowledge_promote" || msg.type === "knowledge_depromote") {
327
+ if (!isMate || !requireWrite(ws, msg)) return true;
328
+ try {
329
+ if (!knowledgeFiles.validName(msg.name)) throw new Error("Invalid knowledge file name.");
330
+ var promoteCtx = matesModule.buildMateCtx(getProjectOwnerId());
331
+ var mateId = path.basename(cwd);
332
+ if (msg.type === "knowledge_promote") {
333
+ var mate = matesModule.getMate(promoteCtx, mateId);
334
+ matesModule.promoteKnowledge(promoteCtx, mateId, mate && mate.name, msg.name);
335
+ } else matesModule.depromoteKnowledge(promoteCtx, mateId, msg.name);
336
+ response(ws, msg, { type: msg.type === "knowledge_promote" ? "knowledge_promoted" : "knowledge_depromoted", name: msg.name });
337
+ sendTo(ws, { type: "knowledge_list", files: listKnowledgeFiles() });
338
+ } catch (error) { failure(ws, msg, error); }
339
+ return true;
340
+ }
341
+ var dbOperations = {
342
+ knowledge_db_create: { method: "create", reply: "knowledge_db_created" }, knowledge_db_read: { method: "read", reply: "knowledge_db_content", read: true },
343
+ knowledge_db_rename: { method: "rename", reply: "knowledge_db_renamed" }, knowledge_db_archive: { method: "archive", reply: "knowledge_db_archived" },
344
+ knowledge_db_recover: { method: "recover", reply: "knowledge_db_recovered" }, knowledge_db_schema_save: { method: "updateSchema", reply: "knowledge_db_schema_saved" },
345
+ knowledge_db_view_save: { method: "saveView", reply: "knowledge_db_view_saved" }, knowledge_db_record_create: { method: "createRecord", reply: "knowledge_db_record_created" },
346
+ knowledge_db_record_read: { method: "readRecord", reply: "knowledge_db_record_content", read: true }, knowledge_db_record_update: { method: "updateRecord", reply: "knowledge_db_record_updated" },
347
+ knowledge_db_record_archive: { method: "archiveRecord", reply: "knowledge_db_record_archived" }, knowledge_db_record_recover: { method: "recoverRecord", reply: "knowledge_db_record_recovered" },
348
+ knowledge_db_query: { method: "query", reply: "knowledge_db_query_results", read: true }, knowledge_db_sync: { method: "retryProjection", reply: "knowledge_db_synced" },
349
+ };
350
+ if (dbOperations[msg.type]) {
351
+ if (!isMate) { failure(ws, msg, new Error("Knowledge databases are available only inside a Mate project.")); return true; }
352
+ var operation = dbOperations[msg.type]; if (!operation.read && !requireWrite(ws, msg)) return true;
353
+ var args = Object.assign({}, msg, { actor: actor(ws) }); delete args.type; delete args.requestId;
354
+ Promise.resolve(databases[operation.method](args)).then(function (result) { response(ws, msg, Object.assign({ type: operation.reply }, result)); }).catch(function (error) { failure(ws, msg, error, { databaseId: msg.databaseId || null, recordId: msg.recordId || null }); });
172
355
  return true;
173
356
  }
174
-
175
357
  return false;
176
358
  }
177
359
 
178
- return {
179
- handleKnowledgeMessage: handleKnowledgeMessage,
180
- listKnowledgeFiles: listKnowledgeFiles,
181
- };
360
+ return { handleKnowledgeMessage: handleKnowledgeMessage, listKnowledgeFiles: listKnowledgeFiles, databases: databases };
182
361
  }
183
362
 
184
363
  module.exports = { attachKnowledge: attachKnowledge };
@@ -98,6 +98,7 @@ function attachMateCreationProposal(ctx) {
98
98
  return ctx.createReadyMate(ws, session.ownerId || null, Object.assign({}, entry.definition, { vendor: vendor, model: selected.value }));
99
99
  }).then(function (mate) {
100
100
  session.homeMateCreationPhase = "created";
101
+ if (typeof ctx.persistSession === "function") ctx.persistSession(session);
101
102
  record(session, { type: "mate_creation_proposal_resolved", proposalId: msg.proposalId, action: "create", mateId: mate.id, mateName: (mate.profile && mate.profile.displayName) || mate.name || "Mate" });
102
103
  entry.resolve({ action: "create", mateId: mate.id });
103
104
  }).catch(function (error) {
@@ -758,8 +758,8 @@ function attachMateInteraction(ctx) {
758
758
  onDone: mentionCallbacks.onDone,
759
759
  onError: mentionCallbacks.onError,
760
760
  canUseTool: function (toolName, input, toolOpts) {
761
- // Full-auto mode: auto-approve everything except AskUserQuestion
762
- if (sm.currentPermissionMode === "bypassPermissions" && toolName !== "AskUserQuestion") {
761
+ // Read the caller's live mode so reused mentions follow session changes.
762
+ if ((session.permissionMode || sm.currentPermissionMode) === "bypassPermissions" && toolName !== "AskUserQuestion") {
763
763
  return Promise.resolve({ behavior: "allow", updatedInput: input });
764
764
  }
765
765
  // Use the shared whitelist from sdk-bridge (read-only tools + safe bash commands)
@@ -0,0 +1,31 @@
1
+ // Server administration messages. Every mutation returns an acknowledgement.
2
+ var SETTINGS = {
3
+ set_pin: { callback: 'onSetPin', field: 'pin', valid: function (value) { return value === null || typeof value === 'string' && /^\d{6}$/.test(value); }, timing: 'Applies to new sign-ins.' },
4
+ set_keep_awake: { callback: 'onSetKeepAwake', field: 'value', timing: 'Applies while the daemon is running.' },
5
+ set_inherit_groups: { callback: 'onSetInheritGroups', field: 'value', timing: 'Applies to newly spawned agent and terminal processes.' },
6
+ set_image_retention: { callback: 'onSetImageRetention', field: 'days', valid: function (value) { return Number.isInteger(value) && value >= 0 && value <= 36500; }, timing: 'The next cleanup applies this period to existing images.' }
7
+ };
8
+ function handleServerSettings(ctx, ws, msg) {
9
+ var entry = SETTINGS[msg.type];
10
+ if (!entry) return false;
11
+ function reply(data) { ctx.sendTo(ws, Object.assign({ type: msg.type + '_result' }, data)); }
12
+ if (ctx.usersModule.isMultiUser()) {
13
+ var id = ws._clayUser && ws._clayUser.id;
14
+ var user = id && ctx.usersModule.findUserById(id);
15
+ if (!user || user.role !== 'admin') { reply({ ok: false, error: 'Administrator access required.' }); return true; }
16
+ }
17
+ var value = msg[entry.field];
18
+ if (entry.valid ? !entry.valid(value) : typeof value !== 'boolean') { reply({ ok: false, error: 'Invalid setting value.' }); return true; }
19
+ var opts = ctx.opts;
20
+ try {
21
+ var config = opts.onGetDaemonConfig ? opts.onGetDaemonConfig() : {};
22
+ if (msg.type === 'set_keep_awake' && config.platform !== 'darwin') throw new Error('Keep awake requires macOS.');
23
+ if (msg.type === 'set_inherit_groups') throw new Error('Group inheritance is automatic in OS-user mode and is no longer configurable.');
24
+ if (typeof opts[entry.callback] !== 'function') throw new Error('This runtime does not support saving this setting.');
25
+ var result = opts[entry.callback](value);
26
+ reply(Object.assign({}, result, { timing: entry.timing }));
27
+ if (result.ok && msg.type === 'set_keep_awake') ctx.send({ type: 'keep_awake_changed', keepAwake: result.keepAwake });
28
+ } catch (error) { reply({ ok: false, error: error.message || 'Could not save this setting.' }); }
29
+ return true;
30
+ }
31
+ module.exports = { handleServerSettings: handleServerSettings };