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,128 @@
1
+ var path = require("path");
2
+
3
+ function slug(value) {
4
+ return String(value || "").trim().toLowerCase().replace(/<[^>]*>/g, "").replace(/[^\p{L}\p{N}\s-]/gu, "").replace(/\s+/g, "-").replace(/-+/g, "-");
5
+ }
6
+
7
+ function frontmatter(content) {
8
+ var text = String(content || "");
9
+ if (text.indexOf("---\n") !== 0) return { properties: {}, tags: [], end: 0, errors: [] };
10
+ var close = text.indexOf("\n---\n", 4);
11
+ if (close === -1 || close > 8192) return { properties: {}, tags: [], end: 0, errors: ["Frontmatter is missing its closing --- line."] };
12
+ var properties = {}; var errors = []; var lines = text.slice(4, close).split("\n");
13
+ for (var i = 0; i < lines.length; i++) {
14
+ if (!lines[i].trim() || /^\s*#/.test(lines[i])) continue;
15
+ var match = /^([A-Za-z][A-Za-z0-9_-]{0,63}):\s*(.*)$/.exec(lines[i]);
16
+ if (!match) { errors.push("Invalid property on line " + (i + 2) + "."); continue; }
17
+ var value = match[2].trim();
18
+ if (value.length > 500) errors.push("Property " + match[1] + " is too long.");
19
+ else properties[match[1]] = value;
20
+ }
21
+ var tags = [];
22
+ if (properties.tags) {
23
+ var raw = properties.tags.replace(/^\[/, "").replace(/\]$/, "").split(",");
24
+ for (var t = 0; t < raw.length; t++) {
25
+ var tag = raw[t].trim().replace(/^#/, "");
26
+ if (/^[\p{L}\p{N}_/-]{1,64}$/u.test(tag) && tags.indexOf(tag) === -1) tags.push(tag);
27
+ else if (tag) errors.push("Invalid tag: " + tag);
28
+ }
29
+ }
30
+ return { properties: properties, tags: tags, end: close + 5, errors: errors };
31
+ }
32
+
33
+ function ignoredRanges(text) {
34
+ var ranges = []; var lines = text.split("\n"); var offset = 0; var fence = null;
35
+ for (var i = 0; i < lines.length; i++) {
36
+ var line = lines[i]; var marker = /^( {0,3})(`{3,}|~{3,})(.*)$/.exec(line);
37
+ if (marker && (!fence || marker[2][0] === fence.marker && marker[2].length >= fence.length && /^\s*$/.test(marker[3]))) {
38
+ if (!fence) fence = { marker: marker[2][0], length: marker[2].length, start: offset };
39
+ else { ranges.push([fence.start, offset + line.length]); fence = null; }
40
+ }
41
+ if (!fence) {
42
+ var cursor = 0;
43
+ while (cursor < line.length) {
44
+ var opener = /`+/.exec(line.slice(cursor)); if (!opener) break;
45
+ var start = cursor + opener.index; var run = opener[0]; var close = line.indexOf(run, start + run.length);
46
+ if (close === -1) { cursor = start + run.length; continue; }
47
+ ranges.push([offset + start, offset + close + run.length]); cursor = close + run.length;
48
+ }
49
+ }
50
+ offset += line.length + 1;
51
+ }
52
+ if (fence) ranges.push([fence.start, text.length]);
53
+ return ranges;
54
+ }
55
+
56
+ function escaped(text, start) {
57
+ var slashes = 0;
58
+ for (var i = start - 1; i >= 0 && text[i] === "\\"; i--) slashes++;
59
+ return slashes % 2 === 1;
60
+ }
61
+
62
+ function inside(ranges, start) {
63
+ for (var i = 0; i < ranges.length; i++) if (start >= ranges[i][0] && start < ranges[i][1]) return true;
64
+ return false;
65
+ }
66
+
67
+ function parse(content) {
68
+ var text = String(content || ""); var ignored = ignoredRanges(text); var links = []; var headings = []; var match;
69
+ var headingPattern = /^(#{1,6})\s+(.+?)\s*#*\s*$/gm;
70
+ while ((match = headingPattern.exec(text))) {
71
+ if (!inside(ignored, match.index)) headings.push({ depth: match[1].length, text: match[2].trim(), slug: slug(match[2]), offset: match.index });
72
+ }
73
+ var wiki = /(!)?\[\[([^\]\n]+)\]\]/g;
74
+ while ((match = wiki.exec(text))) {
75
+ if (inside(ignored, match.index) || escaped(text, match.index)) continue;
76
+ var parts = match[2].split("|"); var target = parts[0].trim(); var alias = parts.length > 1 ? parts.slice(1).join("|").trim() : "";
77
+ var hash = target.indexOf("#");
78
+ var wikiTarget = hash === -1 ? target : target.slice(0, hash);
79
+ links.push({ kind: match[1] ? "embed" : "wiki", target: wikiTarget, heading: hash === -1 ? "" : target.slice(hash + 1), alias: alias,
80
+ start: match.index, end: match.index + match[0].length, raw: match[0], external: /^[a-z][a-z0-9+.-]*:/i.test(wikiTarget) || wikiTarget.indexOf("//") === 0 });
81
+ }
82
+ var markdown = /(!)?\[([^\]\n]*)\]\(([^)\n]+)\)/g;
83
+ while ((match = markdown.exec(text))) {
84
+ if (inside(ignored, match.index) || escaped(text, match.index)) continue;
85
+ var destination = match[3].trim().replace(/^<|>$/g, ""); var hashAt = destination.indexOf("#");
86
+ links.push({ kind: match[1] ? "image" : "markdown", target: hashAt === -1 ? destination : destination.slice(0, hashAt), heading: hashAt === -1 ? "" : destination.slice(hashAt + 1), alias: match[2],
87
+ start: match.index, end: match.index + match[0].length, raw: match[0], external: /^[a-z][a-z0-9+.-]*:/i.test(destination) || destination.indexOf("//") === 0 });
88
+ }
89
+ links.sort(function (a, b) { return a.start - b.start; });
90
+ var meta = frontmatter(text); var tags = meta.tags.slice(); var tagPattern = /(^|\s)#([\p{L}\p{N}_/-]{1,64})/gu;
91
+ while ((match = tagPattern.exec(text))) if (!inside(ignored, match.index) && tags.indexOf(match[2]) === -1) tags.push(match[2]);
92
+ return { links: links, headings: headings, properties: meta.properties, tags: tags, propertyErrors: meta.errors };
93
+ }
94
+
95
+ function normalizeTarget(sourceName, target) {
96
+ var raw = String(target || "").trim();
97
+ if (!raw || /^[a-z][a-z0-9+.-]*:/i.test(raw) || raw.indexOf("//") === 0) return null;
98
+ try { raw = decodeURIComponent(raw); } catch (error) {}
99
+ raw = raw.replace(/\\/g, "/");
100
+ if (!/\.md$/i.test(raw)) raw += ".md";
101
+ return path.posix.normalize(path.posix.join(path.posix.dirname(sourceName), raw)).replace(/^\.\//, "");
102
+ }
103
+
104
+ function resolveLink(sourceName, link, documents, sourceId) {
105
+ if (link.external) return null;
106
+ if (!link.target && link.heading) {
107
+ var self = documents.filter(function (item) { return sourceId ? item.id === sourceId : item.name === sourceName; });
108
+ return self.length === 1 ? self[0] : null;
109
+ }
110
+ var target = normalizeTarget(sourceName, link.target);
111
+ if (!target) return null;
112
+ var exact = documents.filter(function (item) { return item.name.toLowerCase() === target.toLowerCase(); });
113
+ if (exact.length === 1) return exact[0];
114
+ if (link.target.indexOf("/") === -1 && link.target.indexOf("\\") === -1) {
115
+ var base = path.posix.basename(target).toLowerCase();
116
+ var named = documents.filter(function (item) { return path.posix.basename(item.name).toLowerCase() === base; });
117
+ if (named.length === 1) return named[0];
118
+ }
119
+ return null;
120
+ }
121
+
122
+ function snippet(content, offset, needle) {
123
+ var raw = String(content || ""); var at = typeof offset === "number" ? offset : raw.toLowerCase().indexOf(String(needle || "").toLowerCase());
124
+ if (at < 0) at = 0; var start = Math.max(0, at - 70); var end = Math.min(raw.length, at + 150); var text = raw.slice(start, end).replace(/\s+/g, " ").trim();
125
+ return (start ? "…" : "") + text + (end < raw.length ? "…" : "");
126
+ }
127
+
128
+ module.exports = { parse: parse, slug: slug, normalizeTarget: normalizeTarget, resolveLink: resolveLink, snippet: snippet };
@@ -19,34 +19,61 @@ function attachLinearStore(root) {
19
19
  catch (error) { if (error.code !== 'EEXIST') throw error; }
20
20
  return fs.readFileSync(file);
21
21
  }
22
- function key(owner) {
22
+ function record(owner, id) {
23
23
  var data = read(owner);
24
+ return !id || id === "default" ? data : Object.hasOwn(data.connections || {}, id) ? data.connections[id] : {};
25
+ }
26
+ function key(owner, id) {
27
+ var data = record(owner, id);
24
28
  if (!data.secret) return null;
25
29
  var parts = data.secret.split('.').map(function (part) { return Buffer.from(part, 'base64'); });
26
30
  var cipher = crypto.createDecipheriv('aes-256-gcm', masterKey(), parts[0]);
27
- cipher.setAAD(Buffer.from(owner)); cipher.setAuthTag(parts[1]);
31
+ cipher.setAAD(Buffer.from(id && id !== 'default' ? owner + ':' + id : owner)); cipher.setAuthTag(parts[1]);
28
32
  return Buffer.concat([cipher.update(parts[2]), cipher.final()]).toString('utf8');
29
33
  }
30
- function view(owner) {
31
- var data = read(owner);
34
+ function view(owner, id) {
35
+ var data = record(owner, id);
32
36
  return { connected: !!data.secret, workspace: data.workspace || null, revision: data.revision || null };
33
37
  }
34
- function save(owner, secret, workspace) {
38
+ function save(owner, secret, workspace, id, name) {
39
+ if (id && (!/^[a-zA-Z0-9_-]{1,64}$/.test(id) || ['__proto__', 'constructor', 'prototype'].indexOf(id) >= 0)) throw new Error("Invalid connection.");
40
+ var previous = read(owner);
35
41
  var file = filename(owner), data = { revision: crypto.randomBytes(16).toString('hex') };
36
42
  if (secret !== null) {
37
43
  if (typeof secret !== 'string' || !secret.trim() || secret.length > 4096 || /[\r\n]/.test(secret)) throw new Error('Enter a valid Linear API key.');
38
44
  var iv = crypto.randomBytes(12), cipher = crypto.createCipheriv('aes-256-gcm', masterKey(), iv);
39
- cipher.setAAD(Buffer.from(owner));
45
+ cipher.setAAD(Buffer.from(id && id !== 'default' ? owner + ':' + id : owner));
40
46
  var bytes = Buffer.concat([cipher.update(secret.trim(), 'utf8'), cipher.final()]);
41
47
  data.secret = [iv, cipher.getAuthTag(), bytes].map(function (part) { return part.toString('base64'); }).join('.');
42
48
  data.workspace = workspace;
43
49
  }
50
+ if (name !== undefined) {
51
+ if (typeof name !== 'string' || !name.trim() || name.trim().length > 80) throw new Error('Enter a connection name of up to 80 characters.');
52
+ data.name = name.trim();
53
+ } else data.name = record(owner, id).name;
54
+ if (id && id !== 'default') {
55
+ previous.connections = previous.connections || {};
56
+ if (secret === null) delete previous.connections[id];
57
+ else {
58
+ if (!previous.connections[id] && Object.keys(previous.connections).length >= 30) throw new Error('Connection limit reached.');
59
+ previous.connections[id] = data;
60
+ }
61
+ data = previous;
62
+ } else data.connections = previous.connections;
44
63
  fs.mkdirSync(directory, { recursive: true, mode: 0o700 });
45
64
  var temp = file + '.' + crypto.randomBytes(8).toString('hex') + '.tmp';
46
65
  try { fs.writeFileSync(temp, JSON.stringify(data), { mode: 0o600 }); fs.renameSync(temp, file); }
47
66
  finally { try { fs.unlinkSync(temp); } catch (error) { if (error.code !== 'ENOENT') throw error; } }
48
- return view(owner);
67
+ return view(owner, id);
68
+ }
69
+ function list(owner) {
70
+ var data = read(owner), entries = [];
71
+ if (data.secret) entries.push(Object.assign({ id: 'default', name: data.name || 'Existing connection', legacy: true }, view(owner)));
72
+ Object.keys(data.connections || {}).forEach(function (id) {
73
+ entries.push(Object.assign({ id: id, name: data.connections[id].name || 'Linear', legacy: false }, view(owner, id)));
74
+ });
75
+ return entries;
49
76
  }
50
- return { key: key, view: view, save: save };
77
+ return { key: key, view: view, save: save, list: list };
51
78
  }
52
79
  module.exports = { attachLinearStore: attachLinearStore };
@@ -0,0 +1,39 @@
1
+ // Resolve the canonical Mate identity without overwriting either document.
2
+ var fs = require('fs');
3
+ var path = require('path');
4
+ var crypto = require('crypto');
5
+
6
+ function regularFile(file) {
7
+ try {
8
+ var stat = fs.lstatSync(file);
9
+ if (!stat.isFile()) throw new Error('Mate identity must be a regular file: ' + file);
10
+ return stat;
11
+ } catch (error) {
12
+ if (error.code === 'ENOENT') return null;
13
+ throw error;
14
+ }
15
+ }
16
+
17
+ function resolveMateIdentityFile(directory) {
18
+ var canonical = path.join(directory, 'AGENTS.md');
19
+ var legacy = path.join(directory, 'CLAUDE.md');
20
+ var current = regularFile(canonical);
21
+ if (!regularFile(legacy)) return canonical;
22
+ if (!current) {
23
+ // Exclusive linking preserves owner, mode and bytes and never replaces a
24
+ // concurrently created canonical file. Unlink only after publication.
25
+ try { fs.linkSync(legacy, canonical); }
26
+ catch (error) {
27
+ if (error.code !== 'EEXIST') throw error;
28
+ return resolveMateIdentityFile(directory);
29
+ }
30
+ } else if (fs.statSync(canonical).ino !== fs.statSync(legacy).ino) {
31
+ // Preserve a conflicting legacy document outside instruction discovery.
32
+ var backup = path.join(directory, '.identity-legacy-' + crypto.randomBytes(12).toString('hex') + '.md');
33
+ fs.linkSync(legacy, backup);
34
+ }
35
+ fs.unlinkSync(legacy);
36
+ return canonical;
37
+ }
38
+
39
+ module.exports = {resolveMateIdentityFile: resolveMateIdentityFile};
@@ -46,14 +46,37 @@ function handler(bound, method) {
46
46
  if (!bound || typeof bound[method] !== "function") {
47
47
  return errorResult(new Error("Knowledge tools require an exact session-bound Mate."));
48
48
  }
49
- try {
50
- return textResult(bound[method](args || {}));
51
- } catch (e) {
52
- return errorResult(e);
53
- }
49
+ try { return Promise.resolve(bound[method](args || {})).then(textResult, errorResult); } catch (e) { return errorResult(e); }
54
50
  };
55
51
  }
56
52
 
53
+ var ID = { type: "string", description: "Stable opaque id returned by a Knowledge database tool." };
54
+ var REVISION = { type: "number", description: "Revision returned by the most recent read or mutation; stale revisions are rejected." };
55
+ var OPERATION = { type: "string", description: "Stable retry id formatted label_<current epoch milliseconds>_<unique suffix>. Reuse the exact id and payload after an uncertain response; receipts are retained for the documented 24-hour retry window." };
56
+ var FIELD = { type: "object", description: "Field with stable id, name, type, required, and type-specific options or reference target." };
57
+ var FILTER = { type: "object", description: "Validated filter: fieldId, op, and value. Supported ops are eq, neq, contains, gt, gte, lt, lte, empty, not-empty." };
58
+ var SORT = { type: "object", description: "Validated sort with fieldId and asc or desc direction." };
59
+
60
+ function databaseTools(bound) {
61
+ return [
62
+ { name: "list_databases", description: "List canonical Knowledge databases, including archived databases when requested, so the Mate can discover and recover them without relying on summary projection.", inputSchema: buildShape({ archived: { type: "boolean" } }, []), handler: handler(bound, "listDatabases") },
63
+ { name: "create_database", description: "Create a first-class database in this Mate's Knowledge. Defaults to one required title field and a Table view.", inputSchema: buildShape({ name: { type: "string" }, schema: { type: "array", items: FIELD }, operationId: OPERATION }, ["name", "operationId"]), handler: handler(bound, "createDatabase") },
64
+ { name: "read_database", description: "Read one Knowledge database's current schema, views, revision, and record count.", inputSchema: buildShape({ databaseId: ID, includeArchived: { type: "boolean" } }, ["databaseId"]), handler: handler(bound, "readDatabase") },
65
+ { name: "rename_database", description: "Rename a Knowledge database with optimistic revision checking.", inputSchema: buildShape({ databaseId: ID, name: { type: "string" }, expectedRevision: REVISION, operationId: OPERATION }, ["databaseId", "name", "expectedRevision", "operationId"]), handler: handler(bound, "renameDatabase") },
66
+ { name: "archive_database", description: "Recoverably archive a Knowledge database.", inputSchema: buildShape({ databaseId: ID, expectedRevision: REVISION, operationId: OPERATION }, ["databaseId", "expectedRevision", "operationId"]), handler: handler(bound, "archiveDatabase") },
67
+ { name: "recover_database", description: "Recover an archived Knowledge database.", inputSchema: buildShape({ databaseId: ID, expectedRevision: REVISION, operationId: OPERATION }, ["databaseId", "expectedRevision", "operationId"]), handler: handler(bound, "recoverDatabase") },
68
+ { name: "update_database_schema", description: "Replace the schema using stable field ids. Exactly one active title field is required; populated fields cannot be removed or change type and should be archived instead.", inputSchema: buildShape({ databaseId: ID, schema: { type: "array", items: FIELD }, expectedRevision: REVISION, operationId: OPERATION }, ["databaseId", "schema", "expectedRevision", "operationId"]), handler: handler(bound, "updateDatabaseSchema") },
69
+ { name: "save_table_view", description: "Create or revise a persisted Table view with selected columns, validated filters, and deterministic sorts.", inputSchema: buildShape({ databaseId: ID, view: { type: "object", description: "View with stable id, name, columns, filters, and sorts." }, expectedRevision: REVISION, operationId: OPERATION }, ["databaseId", "view", "expectedRevision", "operationId"]), handler: handler(bound, "saveTableView") },
70
+ { name: "query_database", description: "Query a bounded deterministic page of records using a saved view or explicit validated filters and sorts. Returns count and numeric sums.", inputSchema: buildShape({ databaseId: ID, viewId: ID, filters: { type: "array", items: FILTER }, sorts: { type: "array", items: SORT }, cursor: { type: "string" }, limit: { type: "number" }, archived: { type: "boolean" } }, ["databaseId"]), handler: handler(bound, "queryDatabase") },
71
+ { name: "create_database_record", description: "Create a typed record. Values are keyed by stable field id; unknown fields and unsafe keys are rejected.", inputSchema: buildShape({ databaseId: ID, values: { type: "object" }, expectedDatabaseRevision: REVISION, operationId: OPERATION }, ["databaseId", "values", "expectedDatabaseRevision", "operationId"]), handler: handler(bound, "createDatabaseRecord") },
72
+ { name: "read_database_record", description: "Read one record with its values, revision, attribution, archive state, and bounded history.", inputSchema: buildShape({ databaseId: ID, recordId: ID }, ["databaseId", "recordId"]), handler: handler(bound, "readDatabaseRecord") },
73
+ { name: "update_database_record", description: "Update selected typed values using the record revision. Edits to other fields made since a prior save are not overwritten.", inputSchema: buildShape({ databaseId: ID, recordId: ID, values: { type: "object" }, expectedRevision: REVISION, operationId: OPERATION }, ["databaseId", "recordId", "values", "expectedRevision", "operationId"]), handler: handler(bound, "updateDatabaseRecord") },
74
+ { name: "archive_database_record", description: "Recoverably archive one record using its current revision.", inputSchema: buildShape({ databaseId: ID, recordId: ID, expectedRevision: REVISION, operationId: OPERATION }, ["databaseId", "recordId", "expectedRevision", "operationId"]), handler: handler(bound, "archiveDatabaseRecord") },
75
+ { name: "recover_database_record", description: "Recover one archived record using its current revision.", inputSchema: buildShape({ databaseId: ID, recordId: ID, expectedRevision: REVISION, operationId: OPERATION }, ["databaseId", "recordId", "expectedRevision", "operationId"]), handler: handler(bound, "recoverDatabaseRecord") },
76
+ { name: "retry_database_sync", description: "Retry the shared append-only Knowledge summary projection after a canonical database mutation reported projectionPending.", inputSchema: buildShape({ databaseId: ID }, ["databaseId"]), handler: handler(bound, "retryDatabaseSync") },
77
+ ].map(function (tool) { tool.description = MATE_CONTRACT + " " + tool.description; return tool; });
78
+ }
79
+
57
80
  function mateTools(bound) {
58
81
  return [
59
82
  {
@@ -87,7 +110,7 @@ function mateTools(bound) {
87
110
  }, ["ref"]),
88
111
  handler: handler(bound, "readKnowledge"),
89
112
  },
90
- ];
113
+ ].concat(databaseTools(bound));
91
114
  }
92
115
 
93
116
  function clayTools(bound) {
@@ -393,7 +393,9 @@ function attachMateKnowledgeService(ctx) {
393
393
 
394
394
  var def = mate.builtinKey ? builtinMates.getBuiltinByKey(mate.builtinKey) : null;
395
395
  var isClay = !!(mate.builtinKey === "clay" && def && def.hostAgent === true);
396
- return { own: own, isClay: isClay, ownerId: source.projectOwnerId || null };
396
+ return { own: own, isClay: isClay, ownerId: source.projectOwnerId || null,
397
+ databaseService: typeof project.getKnowledgeDbService === "function" ? project.getKnowledgeDbService() : null,
398
+ actor: { type: "user", userId: source.session && source.session.ownerId || source.projectOwnerId || null, displayName: null } };
397
399
  }
398
400
 
399
401
  // A tool handler captured earlier stays valid only while its exact source
@@ -408,7 +410,7 @@ function attachMateKnowledgeService(ctx) {
408
410
  function revalidate() {
409
411
  var current = resolve(source);
410
412
  if (!current) throw new Error("This Knowledge binding is no longer valid for the current session.");
411
- if (current.isClay !== initial.isClay || current.own.scopeId !== initial.own.scopeId) {
413
+ if (current.isClay !== initial.isClay || current.own.scopeId !== initial.own.scopeId || current.databaseService !== initial.databaseService) {
412
414
  throw new Error("This Knowledge binding is no longer valid for the current session.");
413
415
  }
414
416
  return current;
@@ -423,6 +425,10 @@ function attachMateKnowledgeService(ctx) {
423
425
  function ownScope() {
424
426
  return [revalidate().own];
425
427
  }
428
+ function db(method, args) {
429
+ var current = revalidate(); if (!current.databaseService || typeof current.databaseService[method] !== "function") throw new Error("Knowledge databases are unavailable for this Mate.");
430
+ return current.databaseService[method](Object.assign({}, args || {}, { actor: current.actor }));
431
+ }
426
432
  return {
427
433
  isClay: false,
428
434
  mateId: initial.own.mateId,
@@ -430,6 +436,21 @@ function attachMateKnowledgeService(ctx) {
430
436
  listKnowledge: function (args) { return listFor(ownScope(), args || {}, false); },
431
437
  searchKnowledge: function (args) { return searchFor(ownScope(), args || {}, false); },
432
438
  readKnowledge: function (args) { return readFor(ownScope(), args || {}, false); },
439
+ listDatabases: function (args) { return db("list", args); },
440
+ createDatabase: function (args) { return db("create", args); },
441
+ readDatabase: function (args) { return db("read", args); },
442
+ renameDatabase: function (args) { return db("rename", args); },
443
+ archiveDatabase: function (args) { return db("archive", args); },
444
+ recoverDatabase: function (args) { return db("recover", args); },
445
+ updateDatabaseSchema: function (args) { return db("updateSchema", args); },
446
+ saveTableView: function (args) { return db("saveView", args); },
447
+ queryDatabase: function (args) { return db("query", args); },
448
+ createDatabaseRecord: function (args) { return db("createRecord", args); },
449
+ readDatabaseRecord: function (args) { return db("readRecord", args); },
450
+ updateDatabaseRecord: function (args) { return db("updateRecord", args); },
451
+ archiveDatabaseRecord: function (args) { return db("archiveRecord", args); },
452
+ recoverDatabaseRecord: function (args) { return db("recoverRecord", args); },
453
+ retryDatabaseSync: function (args) { return db("retryProjection", args); },
433
454
  };
434
455
  }
435
456
 
@@ -12,7 +12,7 @@
12
12
  // call idempotent and makes deletions and rewrites work without the call site
13
13
  // having to describe what changed.
14
14
  //
15
- // Identity is never mirrored. CLAUDE.md, base-template.md, identity-backup.md,
15
+ // Identity is never mirrored. AGENTS.md, legacy CLAUDE.md, base-template.md, identity-backup.md,
16
16
  // and identity-history.jsonl define who a Mate is and remain canonical files.
17
17
  // Sticky notes are a separate attention layer and are excluded too.
18
18
 
@@ -23,7 +23,7 @@ var config = require("./config");
23
23
  var knowledgeImport = require("./knowledge-import");
24
24
 
25
25
  var SOURCE_NAMESPACE = "mate-knowledge";
26
- var SOURCE_VERSION = 2;
26
+ var SOURCE_VERSION = 3;
27
27
  var MAX_SOURCE_BYTES = 64 * 1024 * 1024;
28
28
 
29
29
  // Identity and template artifacts. Never mirrored, at any version.
@@ -84,6 +84,13 @@ function sourceKindFor(name) {
84
84
  return null;
85
85
  }
86
86
 
87
+ function safeSourceName(value) {
88
+ var name = String(value || "").replace(/\\/g, "/").replace(/^\.\//, ""); var parts = name.split("/");
89
+ if (!name || name[0] === "/" || name.length > 500 || parts.length > 20) return null;
90
+ if (parts.some(function (part) { return !part || part === "." || part === ".." || part.length > 120; })) return null;
91
+ return parts.join("/");
92
+ }
93
+
87
94
  function isJournal(name) {
88
95
  return !!JOURNAL_KINDS[name] || /\.jsonl$/.test(name);
89
96
  }
@@ -133,9 +140,8 @@ function isMateSourceEntry(entry) {
133
140
  if (typeof source.sourceVersion !== "number" || source.sourceVersion > SOURCE_VERSION) return false;
134
141
  var name = source.fileName;
135
142
  if (!name || typeof name !== "string") return false;
136
- // The file name and the relative path must agree, so neither field alone is
137
- // trusted, and the name must be a plain basename.
138
- if (path.basename(name) !== name) return false;
143
+ // The source name and relative path must agree, so neither field alone is trusted.
144
+ if (safeSourceName(name) !== name) return false;
139
145
  return source.relPath === relativeSource(name);
140
146
  }
141
147
 
@@ -180,6 +186,18 @@ function readStat(filePath) {
180
186
  }
181
187
  }
182
188
 
189
+ function safeKnowledgeFileStat(root, name) {
190
+ var parts = name.split("/"); var cursor = root;
191
+ for (var i = 0; i < parts.length; i++) {
192
+ cursor = path.join(cursor, parts[i]); var stat;
193
+ try { stat = fs.lstatSync(cursor); } catch (error) { if (error.code === "ENOENT") return null; throw error; }
194
+ if (stat.isSymbolicLink()) throw new Error("Knowledge source contains a symbolic link.");
195
+ if (i < parts.length - 1 && !stat.isDirectory()) throw new Error("Knowledge source path is not a directory.");
196
+ if (i === parts.length - 1 && !stat.isFile()) return null;
197
+ }
198
+ return fs.statSync(cursor);
199
+ }
200
+
183
201
  // --- File-addressed sources -------------------------------------------
184
202
 
185
203
  function syncFile(importer, scope, name, kind, filePath, stat, actor, summary) {
@@ -361,8 +379,8 @@ function syncMateSource(opts) {
361
379
  var scope = options.scope || scopeForMateDir(options.mateDir);
362
380
  if (!scope) return summary;
363
381
  if (options.mateName) scope.mateName = options.mateName;
364
- var name = path.basename(String(options.fileName || ""));
365
- if (!name) return summary;
382
+ var rawName = String(options.fileName || ""); var name = safeSourceName(rawName);
383
+ if (!name) { if (rawName) summary.unsupported++; return summary; }
366
384
  if (IDENTITY_FILES.indexOf(name) !== -1) { summary.identitySkipped++; return summary; }
367
385
  if (EXCLUDED_FILES.indexOf(name) !== -1) { summary.excluded++; return summary; }
368
386
  var kind = sourceKindFor(name);
@@ -370,8 +388,8 @@ function syncMateSource(opts) {
370
388
 
371
389
  var actor = normalizeActor(options.actor);
372
390
  var importer = options.importer || importerFor(scope, options.baseDir);
373
- var filePath = path.join(scope.mateDir, "knowledge", name);
374
- var stat = readStat(filePath);
391
+ var knowledgeRoot = path.join(scope.mateDir, "knowledge"); var filePath = path.join(knowledgeRoot, name);
392
+ var stat = safeKnowledgeFileStat(knowledgeRoot, name);
375
393
 
376
394
  if (isJournal(name)) syncJournal(importer, scope, name, kind, filePath, stat, actor, summary);
377
395
  else syncFile(importer, scope, name, kind, filePath, stat, actor, summary);
@@ -382,19 +400,11 @@ function syncMateSource(opts) {
382
400
  return summary;
383
401
  }
384
402
 
385
- // Alias that reads better at a call site reacting to a removal or a rewrite.
386
403
  function reconcileSource(opts) {
387
404
  return syncMateSource(opts);
388
405
  }
389
406
 
390
407
  // Reconcile every durable source in one Mate directory, in both directions.
391
- //
392
- // Enumerating the directory alone is not enough. A knowledge file or a whole
393
- // journal can be deleted while the daemon is stopped, or by direct filesystem
394
- // tooling, and nothing would ever call syncMateSource for it again. So the
395
- // scope's own live records are also consulted: any source this module
396
- // previously imported that is no longer on disk is reconciled, which tombstones
397
- // a missing file record or every line of a missing journal.
398
408
  function reconcileMate(opts) {
399
409
  var options = opts || {};
400
410
  var summary = options.summary || newSummary();
@@ -404,11 +414,17 @@ function reconcileMate(opts) {
404
414
  var knowledgeDir = path.join(scope.mateDir, "knowledge");
405
415
  var present = {};
406
416
  var entries = [];
407
- try {
408
- entries = fs.readdirSync(knowledgeDir, { withFileTypes: true });
409
- } catch (e) {
410
- entries = [];
417
+ function discover(relative) {
418
+ var folder = relative ? path.join(knowledgeDir, relative) : knowledgeDir; var children;
419
+ try { children = fs.readdirSync(folder, { withFileTypes: true }); } catch (error) { return; }
420
+ for (var d = 0; d < children.length; d++) {
421
+ var name = relative ? relative + "/" + children[d].name : children[d].name; var stat;
422
+ try { stat = fs.lstatSync(path.join(knowledgeDir, name)); } catch (error) { continue; }
423
+ if (stat.isSymbolicLink()) continue;
424
+ if (stat.isDirectory()) discover(name); else if (stat.isFile()) entries.push({ name: name });
425
+ }
411
426
  }
427
+ discover("");
412
428
  var importer = options.importer || importerFor(scope, options.baseDir);
413
429
 
414
430
  function reconcileOne(fileName) {
@@ -423,7 +439,6 @@ function reconcileMate(opts) {
423
439
  }
424
440
 
425
441
  for (var i = 0; i < entries.length; i++) {
426
- if (!entries[i].isFile()) continue;
427
442
  // Recorded as present whatever its kind, so an excluded or unsupported file
428
443
  // that still exists is never mistaken for a deleted source.
429
444
  present[entries[i].name] = true;
@@ -1,3 +1,4 @@
1
+ var resolveMateIdentityFile = require('./mate-identity-file').resolveMateIdentityFile;
1
2
  // Atomic finalization of an interviewed Mate. No placeholder is exposed unless
2
3
  // the complete identity has passed validation and is ready to register.
3
4
 
@@ -55,7 +56,7 @@ function createReadyMate(mates, mateCtx, definition) {
55
56
  autonomy: ready.autonomy,
56
57
  });
57
58
  var mateDir = mates.getMateDir(mateCtx, mate.id);
58
- var identityPath = path.join(mateDir, "CLAUDE.md");
59
+ var identityPath = resolveMateIdentityFile(mateDir);
59
60
  fs.writeFileSync(identityPath, ready.identityMarkdown + "\n");
60
61
  mates.enforceAllSections(identityPath, { ctx: mateCtx, mateId: mate.id });
61
62
  fs.mkdirSync(path.join(mateDir, "knowledge"), { recursive: true });
@@ -0,0 +1,4 @@
1
+ // Only curated pigments may affect a Mate's workspace surface.
2
+ var COLORS = ['mint', 'rose', 'plum', 'coral', 'peach', 'gold', 'sky', 'indigo', 'stone'];
3
+ function valid(value) { return typeof value === 'string' && COLORS.indexOf(value) !== -1; }
4
+ module.exports = { colors: COLORS, valid: valid };
@@ -2,7 +2,7 @@
2
2
  * Mates identity module -- Identity extraction, backup, and change tracking.
3
3
  *
4
4
  * Manages the boundary between a mate's user-authored identity and
5
- * system-managed sections in CLAUDE.md.
5
+ * system-managed sections in AGENTS.md.
6
6
  * Extracted from mates.js to keep module sizes manageable.
7
7
  */
8
8
 
@@ -18,7 +18,7 @@ var IDENTITY_MIN_LENGTH = 50;
18
18
  /**
19
19
  * Build the capabilities section for a primary mate.
20
20
  * Injected as a system section so it auto-updates with code changes
21
- * without touching the mate's identity in CLAUDE.md.
21
+ * without touching the mate's identity in AGENTS.md.
22
22
  */
23
23
  function buildPrimaryCapabilitiesSection(mate) {
24
24
  if (!mate || !mate.primary) return "";
@@ -52,9 +52,9 @@ function buildPrimaryCapabilitiesSection(mate) {
52
52
  }
53
53
 
54
54
  /**
55
- * Extract identity content from a CLAUDE.md string.
55
+ * Extract identity content from a AGENTS.md string.
56
56
  * Identity is everything before the first system marker.
57
- * @param {string} content - full CLAUDE.md content
57
+ * @param {string} content - full AGENTS.md content
58
58
  * @param {string[]} allMarkers - array of all system marker strings
59
59
  */
60
60
  function extractIdentity(content, allMarkers) {
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Mates prompts module -- System section enforcers for mate CLAUDE.md files.
2
+ * Mates prompts module -- System section enforcers for mate AGENTS.md files.
3
3
  *
4
4
  * Each enforcer manages a specific system-managed section (team awareness,
5
5
  * session memory, sticky notes, project registry, debate awareness).
@@ -20,7 +20,7 @@ var TEAM_SECTION =
20
20
  "**This section is managed by the system and cannot be removed.**\n\n" +
21
21
  "You are one of several AI Mates in this workspace. Your teammates and their profiles are listed in `../mates.json`. " +
22
22
  "Each teammate's identity and working style is described in their own directory:\n\n" +
23
- "- `../{mate_id}/CLAUDE.md` -- their identity, personality, and working style\n" +
23
+ "- `../{mate_id}/AGENTS.md` -- their identity, personality, and working style\n" +
24
24
  "- `../{mate_id}/mate.yaml` -- their metadata (name, role, status, activities)\n" +
25
25
  "- `../common-knowledge.json` -- shared knowledge registry; files listed here are readable by all mates\n\n" +
26
26
  "Check the team registry when it would be relevant to know who else is available or what they do. " +
@@ -31,6 +31,7 @@ var SESSION_MEMORY_MARKER = "<!-- SESSION_MEMORY_MANAGED_BY_SYSTEM -->";
31
31
  var SESSION_MEMORY_SECTION =
32
32
  "\n\n" + SESSION_MEMORY_MARKER + "\n" +
33
33
  "## Session Memory\n\n" +
34
+ "Your identity is stored in `AGENTS.md` in your Mate directory. Read and edit that file when reshaping your identity; do not create a separate `CLAUDE.md`.\n\n" +
34
35
  "**This section is managed by the system and cannot be removed.**\n\n" +
35
36
  "Your `knowledge/memory-summary.md` file contains your compressed long-term memory, " +
36
37
  "automatically maintained across sessions. Refer to it for context about past " +
@@ -115,7 +116,7 @@ function buildTeamSection(ctx, currentMateId, loadMates) {
115
116
 
116
117
  section += "\n" +
117
118
  "Each teammate's full identity is in their own directory:\n\n" +
118
- "- `../{mate_id}/CLAUDE.md` -- identity, personality, working style\n" +
119
+ "- `../{mate_id}/AGENTS.md` -- identity, personality, working style\n" +
119
120
  "- `../{mate_id}/mate.yaml` -- metadata (name, role, status, activities)\n" +
120
121
  "- `../common-knowledge.json` -- shared knowledge readable by all mates\n\n" +
121
122
  "Use the **ID** (not the name) when referencing teammates in structured data. " +