clay-server 5.2.0-beta.3 → 5.2.0-beta.5

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 (339) hide show
  1. package/lib/account-auth-socket.js +1 -0
  2. package/lib/builtin-mates.js +19 -16
  3. package/lib/bundled-skills/clay-agent-browser/SKILL.md +6 -2
  4. package/lib/canonical-clay-record-authority.js +15 -0
  5. package/lib/canonical-clay-scheduling-authority.js +19 -0
  6. package/lib/claude-hook-installer.js +28 -0
  7. package/lib/clay-mate-chat-receipts.js +26 -0
  8. package/lib/clay-mate-chat.js +87 -0
  9. package/lib/clay-mate-knowledge-input.js +26 -0
  10. package/lib/clay-mate-knowledge.js +95 -0
  11. package/lib/clay-mate-lifecycle.js +95 -0
  12. package/lib/clay-product-control-input.js +33 -0
  13. package/lib/clay-product-control-tools.js +51 -0
  14. package/lib/clay-product-control.js +237 -0
  15. package/lib/clay-project-lifecycle.js +154 -0
  16. package/lib/clay-project-record-input.js +44 -0
  17. package/lib/clay-project-records.js +83 -0
  18. package/lib/clay-scheduling-input.js +21 -0
  19. package/lib/clay-scheduling-receipts.js +26 -0
  20. package/lib/clay-scheduling.js +119 -0
  21. package/lib/clay-screen-controls.js +96 -0
  22. package/lib/clay-workspace-controls.js +83 -0
  23. package/lib/clay-workspace-input.js +17 -0
  24. package/lib/clay-workspace-receipts.js +26 -0
  25. package/lib/daemon-project-access.js +11 -0
  26. package/lib/daemon-project-lifecycle-callbacks.js +15 -0
  27. package/lib/daemon-project-lifecycle.js +262 -0
  28. package/lib/daemon-projects.js +1 -1
  29. package/lib/daemon.js +73 -221
  30. package/lib/issues-service.js +29 -15
  31. package/lib/knowledge-db-import.js +5 -4
  32. package/lib/knowledge-db-service.js +9 -6
  33. package/lib/knowledge-document-index.js +2 -1
  34. package/lib/knowledge-file-store.js +7 -1
  35. package/lib/knowledge-product-operations.js +46 -0
  36. package/lib/mate-avatar-catalog.js +12 -0
  37. package/lib/mate-avatar-migration.js +41 -0
  38. package/lib/mate-creation-mcp-server.js +2 -0
  39. package/lib/mate-knowledge-canonical-access.js +10 -0
  40. package/lib/mate-knowledge-service.js +7 -4
  41. package/lib/mate-lifecycle-service.js +94 -0
  42. package/lib/mate-message-delivery.js +43 -0
  43. package/lib/mate-ready-creation.js +9 -1
  44. package/lib/mate-schedule-input.js +29 -0
  45. package/lib/mate-schedule-lifecycle.js +151 -0
  46. package/lib/mate-scheduling.js +103 -0
  47. package/lib/mates.js +18 -6
  48. package/lib/mcp-agent-tools.js +117 -0
  49. package/lib/mcp-connection-store.js +119 -15
  50. package/lib/mcp-import-credentials.js +31 -0
  51. package/lib/mcp-local-transport.js +13 -0
  52. package/lib/mcp-oauth.js +8 -6
  53. package/lib/mcp-query-bridge.js +30 -0
  54. package/lib/mcp-remote-connections.js +74 -20
  55. package/lib/mcp-source-adapters.js +91 -0
  56. package/lib/mcp-source-context.js +42 -0
  57. package/lib/mcp-source-discovery.js +169 -0
  58. package/lib/mcp-source-plugins.js +68 -0
  59. package/lib/mcp-source-restrictions.js +47 -0
  60. package/lib/mcp-tool-policy.js +34 -0
  61. package/lib/notes-checklist.js +50 -0
  62. package/lib/notes.js +49 -3
  63. package/lib/os-users.js +20 -5
  64. package/lib/project-admin-overview.js +51 -0
  65. package/lib/project-ask-user.js +8 -4
  66. package/lib/project-debate.js +1 -0
  67. package/lib/project-delegated-follow-up.js +2 -1
  68. package/lib/project-directory-claim.js +65 -0
  69. package/lib/project-file-operations.js +61 -0
  70. package/lib/project-filesystem.js +13 -87
  71. package/lib/project-full-access.js +23 -2
  72. package/lib/project-knowledge.js +32 -18
  73. package/lib/project-lifecycle-operations.js +98 -0
  74. package/lib/project-lifecycle-process-witness.js +32 -0
  75. package/lib/project-lifecycle-runtime.js +50 -0
  76. package/lib/project-linux-access.js +22 -0
  77. package/lib/project-logs-canonical-clay.js +27 -0
  78. package/lib/project-logs-clay-query.js +16 -0
  79. package/lib/project-logs-schema.js +4 -2
  80. package/lib/project-logs-service.js +8 -7
  81. package/lib/project-logs.js +1 -0
  82. package/lib/project-loop.js +1 -0
  83. package/lib/project-mate-chat-control.js +55 -0
  84. package/lib/project-mate-creation-proposal.js +18 -3
  85. package/lib/project-mate-interaction.js +8 -0
  86. package/lib/project-mcp.js +54 -16
  87. package/lib/project-models.js +14 -31
  88. package/lib/project-notifications.js +74 -16
  89. package/lib/project-ownership.js +77 -0
  90. package/lib/project-pair-result-delivery.js +14 -3
  91. package/lib/project-pending-message-queue.js +1 -0
  92. package/lib/project-product-control.js +87 -0
  93. package/lib/project-product-session-queue.js +46 -0
  94. package/lib/project-push-access.js +28 -0
  95. package/lib/project-scheduled-message-queue.js +30 -0
  96. package/lib/project-scheduled-messages.js +63 -64
  97. package/lib/project-scheduled-task-control.js +83 -0
  98. package/lib/project-scheduled-tasks.js +13 -2
  99. package/lib/project-scheduling-access.js +10 -0
  100. package/lib/project-session-actions.js +65 -0
  101. package/lib/project-session-delete.js +8 -1
  102. package/lib/project-session-document.js +8 -3
  103. package/lib/project-session-github.js +63 -19
  104. package/lib/project-session-history-actions.js +165 -0
  105. package/lib/project-session-linear.js +14 -6
  106. package/lib/project-session-notes.js +7 -3
  107. package/lib/project-session-runtime-actions.js +63 -0
  108. package/lib/project-session-vendor.js +54 -0
  109. package/lib/project-sessions.js +68 -241
  110. package/lib/project-shared-browser.js +56 -12
  111. package/lib/project-user-message.js +30 -1
  112. package/lib/project-workspace-operations.js +126 -0
  113. package/lib/project-workspace-query.js +9 -2
  114. package/lib/project.js +144 -37
  115. package/lib/public/app.js +5 -0
  116. package/lib/public/appearance-bootstrap.js +14 -0
  117. package/lib/public/css/clay-tool-activity.css +51 -0
  118. package/lib/public/css/clay-workspace.css +29 -0
  119. package/lib/public/css/home-debate-planning.css +2 -0
  120. package/lib/public/css/home-mate-creation.css +5 -0
  121. package/lib/public/css/mate-avatar-gallery.css +12 -0
  122. package/lib/public/css/mate-conversation.css +10 -5
  123. package/lib/public/css/overlays.css +4 -2
  124. package/lib/public/css/project-admin-overview.css +13 -0
  125. package/lib/public/css/session-folders.css +11 -0
  126. package/lib/public/css/session-github.css +46 -1
  127. package/lib/public/css/shared-browser.css +35 -1
  128. package/lib/public/css/sticky-notes.css +61 -1
  129. package/lib/public/index.html +11 -5
  130. package/lib/public/mate-avatars/B01.png +0 -0
  131. package/lib/public/mate-avatars/B02.png +0 -0
  132. package/lib/public/mate-avatars/B03.png +0 -0
  133. package/lib/public/mate-avatars/B04.png +0 -0
  134. package/lib/public/mate-avatars/B05.png +0 -0
  135. package/lib/public/mate-avatars/B06.png +0 -0
  136. package/lib/public/mate-avatars/B07.png +0 -0
  137. package/lib/public/mate-avatars/B08.png +0 -0
  138. package/lib/public/mate-avatars/B09.png +0 -0
  139. package/lib/public/mate-avatars/B10.png +0 -0
  140. package/lib/public/mate-avatars/B11.png +0 -0
  141. package/lib/public/mate-avatars/B12.png +0 -0
  142. package/lib/public/mate-avatars/B13.png +0 -0
  143. package/lib/public/mate-avatars/B14.png +0 -0
  144. package/lib/public/mate-avatars/B15.png +0 -0
  145. package/lib/public/mate-avatars/B16.png +0 -0
  146. package/lib/public/mate-avatars/B17.png +0 -0
  147. package/lib/public/mate-avatars/B18.png +0 -0
  148. package/lib/public/mate-avatars/B19.png +0 -0
  149. package/lib/public/mate-avatars/B20.png +0 -0
  150. package/lib/public/mate-avatars/B21.png +0 -0
  151. package/lib/public/mate-avatars/B22.png +0 -0
  152. package/lib/public/mate-avatars/B23.png +0 -0
  153. package/lib/public/mate-avatars/B24.png +0 -0
  154. package/lib/public/mate-avatars/B25.png +0 -0
  155. package/lib/public/mate-avatars/B26.png +0 -0
  156. package/lib/public/mate-avatars/B27.png +0 -0
  157. package/lib/public/mate-avatars/B28.png +0 -0
  158. package/lib/public/mate-avatars/B29.png +0 -0
  159. package/lib/public/mate-avatars/B30.png +0 -0
  160. package/lib/public/mate-avatars/B31.png +0 -0
  161. package/lib/public/mate-avatars/B32.png +0 -0
  162. package/lib/public/mate-avatars/B33.png +0 -0
  163. package/lib/public/mate-avatars/B34.png +0 -0
  164. package/lib/public/mate-avatars/B35.png +0 -0
  165. package/lib/public/mate-avatars/B36.png +0 -0
  166. package/lib/public/mate-avatars/B37.png +0 -0
  167. package/lib/public/mate-avatars/B38.png +0 -0
  168. package/lib/public/mate-avatars/B39.png +0 -0
  169. package/lib/public/mate-avatars/B40.png +0 -0
  170. package/lib/public/mate-avatars/B41.png +0 -0
  171. package/lib/public/mate-avatars/B42.png +0 -0
  172. package/lib/public/mate-avatars/B43.png +0 -0
  173. package/lib/public/mate-avatars/B44.png +0 -0
  174. package/lib/public/mate-avatars/B45.png +0 -0
  175. package/lib/public/mate-avatars/B46.png +0 -0
  176. package/lib/public/mate-avatars/B47.png +0 -0
  177. package/lib/public/mate-avatars/B48.png +0 -0
  178. package/lib/public/mate-avatars/C01.png +0 -0
  179. package/lib/public/mate-avatars/C02.png +0 -0
  180. package/lib/public/mate-avatars/C03.png +0 -0
  181. package/lib/public/mate-avatars/C04.png +0 -0
  182. package/lib/public/mate-avatars/C05.png +0 -0
  183. package/lib/public/mate-avatars/C06.png +0 -0
  184. package/lib/public/mate-avatars/C07.png +0 -0
  185. package/lib/public/mate-avatars/C08.png +0 -0
  186. package/lib/public/mate-avatars/C09.png +0 -0
  187. package/lib/public/mate-avatars/C10.png +0 -0
  188. package/lib/public/mate-avatars/C11.png +0 -0
  189. package/lib/public/mate-avatars/C12.png +0 -0
  190. package/lib/public/mate-avatars/C13.png +0 -0
  191. package/lib/public/mate-avatars/C14.png +0 -0
  192. package/lib/public/mate-avatars/C15.png +0 -0
  193. package/lib/public/mate-avatars/C16.png +0 -0
  194. package/lib/public/mate-avatars/C17.png +0 -0
  195. package/lib/public/mate-avatars/C18.png +0 -0
  196. package/lib/public/mate-avatars/C19.png +0 -0
  197. package/lib/public/mate-avatars/C20.png +0 -0
  198. package/lib/public/mate-avatars/C21.png +0 -0
  199. package/lib/public/mate-avatars/C22.png +0 -0
  200. package/lib/public/mate-avatars/C23.png +0 -0
  201. package/lib/public/mate-avatars/C24.png +0 -0
  202. package/lib/public/mate-avatars/C25.png +0 -0
  203. package/lib/public/mate-avatars/C26.png +0 -0
  204. package/lib/public/mate-avatars/C27.png +0 -0
  205. package/lib/public/mate-avatars/C28.png +0 -0
  206. package/lib/public/mate-avatars/C29.png +0 -0
  207. package/lib/public/mate-avatars/C30.png +0 -0
  208. package/lib/public/mate-avatars/C31.png +0 -0
  209. package/lib/public/mate-avatars/C32.png +0 -0
  210. package/lib/public/mate-avatars/C33.png +0 -0
  211. package/lib/public/mate-avatars/C34.png +0 -0
  212. package/lib/public/mate-avatars/C35.png +0 -0
  213. package/lib/public/mate-avatars/C36.png +0 -0
  214. package/lib/public/mate-avatars/C37.png +0 -0
  215. package/lib/public/mate-avatars/C38.png +0 -0
  216. package/lib/public/mate-avatars/C39.png +0 -0
  217. package/lib/public/mate-avatars/C40.png +0 -0
  218. package/lib/public/mate-avatars/C41.png +0 -0
  219. package/lib/public/mate-avatars/C42.png +0 -0
  220. package/lib/public/mate-avatars/C43.png +0 -0
  221. package/lib/public/mate-avatars/C44.png +0 -0
  222. package/lib/public/mate-avatars/C45.png +0 -0
  223. package/lib/public/mate-avatars/C46.png +0 -0
  224. package/lib/public/mate-avatars/C47.png +0 -0
  225. package/lib/public/mate-avatars/C48.png +0 -0
  226. package/lib/public/mate-avatars/catalog.json +482 -0
  227. package/lib/public/mate-avatars/lead-clay.png +0 -0
  228. package/lib/public/modules/admin-settings-projects.js +6 -2
  229. package/lib/public/modules/app-connection.js +8 -1
  230. package/lib/public/modules/app-message-router.js +4 -0
  231. package/lib/public/modules/app-messages.js +31 -5
  232. package/lib/public/modules/app-projects.js +5 -0
  233. package/lib/public/modules/app-rendering.js +1 -1
  234. package/lib/public/modules/avatar.js +15 -41
  235. package/lib/public/modules/chat-render-runtime.js +10 -1
  236. package/lib/public/modules/clay-browser-links.js +47 -0
  237. package/lib/public/modules/clay-log-links.js +1 -1
  238. package/lib/public/modules/clay-owner-navigation.js +27 -0
  239. package/lib/public/modules/clay-tool-activity.js +140 -0
  240. package/lib/public/modules/clay-tool-presentation.js +338 -0
  241. package/lib/public/modules/command-palette.js +2 -2
  242. package/lib/public/modules/debate.js +1 -0
  243. package/lib/public/modules/default-ai.js +11 -3
  244. package/lib/public/modules/history-replay-batch.js +5 -1
  245. package/lib/public/modules/home-debate-live.js +2 -0
  246. package/lib/public/modules/home-debate-planning.js +7 -7
  247. package/lib/public/modules/home-mate-avatar-editor.js +127 -208
  248. package/lib/public/modules/home-mate-chat.js +8 -6
  249. package/lib/public/modules/home-mate-creation.js +73 -1
  250. package/lib/public/modules/home-mate-settings.js +19 -12
  251. package/lib/public/modules/markdown.js +4 -0
  252. package/lib/public/modules/mate-avatar-catalog.js +97 -0
  253. package/lib/public/modules/mate-management.js +40 -7
  254. package/lib/public/modules/mcp-connection-view.js +31 -13
  255. package/lib/public/modules/mcp-discovery-view.js +31 -0
  256. package/lib/public/modules/mcp-ui.js +23 -4
  257. package/lib/public/modules/mention.js +3 -0
  258. package/lib/public/modules/pane-browser-bridge.js +20 -0
  259. package/lib/public/modules/personal-settings-dialog.js +7 -1
  260. package/lib/public/modules/profile.js +1 -238
  261. package/lib/public/modules/project-access-recovery.js +17 -0
  262. package/lib/public/modules/project-admin-overview.js +21 -0
  263. package/lib/public/modules/project-mate-navigation.js +99 -14
  264. package/lib/public/modules/search-clay-chat.js +16 -2
  265. package/lib/public/modules/session-folder-layout.js +1 -1
  266. package/lib/public/modules/session-folder-view-menu.js +9 -0
  267. package/lib/public/modules/session-github.js +85 -14
  268. package/lib/public/modules/session-work-primary.js +51 -0
  269. package/lib/public/modules/shared-browser-settings.js +112 -0
  270. package/lib/public/modules/shared-browser-tabs.js +13 -3
  271. package/lib/public/modules/shared-browser.js +27 -6
  272. package/lib/public/modules/sidebar-session-folders-ui.js +1 -1
  273. package/lib/public/modules/split-view.js +2 -0
  274. package/lib/public/modules/sticky-note-markdown.js +72 -6
  275. package/lib/public/modules/sticky-notes-actions.js +30 -0
  276. package/lib/public/modules/sticky-notes-browser.js +9 -2
  277. package/lib/public/modules/sticky-notes-card.js +23 -7
  278. package/lib/public/modules/sticky-notes-checklist.js +71 -0
  279. package/lib/public/modules/sticky-notes-editor.js +108 -44
  280. package/lib/public/modules/sticky-notes-shared.js +50 -2
  281. package/lib/public/modules/sticky-notes-state.js +66 -0
  282. package/lib/public/modules/sticky-notes.js +34 -51
  283. package/lib/public/modules/theme.js +8 -1
  284. package/lib/public/modules/thinking-lifecycle.js +33 -3
  285. package/lib/public/modules/thinking-view.js +23 -1
  286. package/lib/public/modules/tools.js +36 -7
  287. package/lib/public/modules/user-settings-appearance.js +10 -3
  288. package/lib/public/modules/user-settings-editors.js +1 -1
  289. package/lib/public/modules/user-settings-layout.js +11 -2
  290. package/lib/public/modules/user-settings-preferences.js +6 -2
  291. package/lib/public/modules/user-settings-profile.js +12 -2
  292. package/lib/public/modules/user-settings.js +3 -2
  293. package/lib/public/style.css +3 -0
  294. package/lib/public/sw.js +3 -1
  295. package/lib/scheduled-task-records.js +3 -1
  296. package/lib/scheduler.js +2 -0
  297. package/lib/sdk-bridge.js +228 -23
  298. package/lib/sdk-message-processor.js +2 -0
  299. package/lib/sdk-response-recovery.js +155 -0
  300. package/lib/server-admin.js +63 -16
  301. package/lib/server-appearance-settings.js +13 -1
  302. package/lib/server-dm.js +2 -1
  303. package/lib/server-global-ws.js +10 -20
  304. package/lib/server-home-chat-events.js +18 -4
  305. package/lib/server-home-chat.js +12 -16
  306. package/lib/server-home-mate-creation.js +1 -0
  307. package/lib/server-home-mcp-consent.js +14 -0
  308. package/lib/server-mates.js +25 -62
  309. package/lib/server-owner-ui.js +77 -0
  310. package/lib/server-path-access.js +38 -0
  311. package/lib/server-project-removal.js +22 -0
  312. package/lib/server-settings.js +4 -1
  313. package/lib/server.js +144 -41
  314. package/lib/session-config-transition.js +45 -0
  315. package/lib/session-folders.js +7 -2
  316. package/lib/session-github.js +9 -2
  317. package/lib/session-visibility.js +5 -2
  318. package/lib/session-work-primary.js +50 -0
  319. package/lib/sessions.js +13 -0
  320. package/lib/shared-browser-cookie-sync.js +32 -0
  321. package/lib/shared-browser-cookies.js +79 -0
  322. package/lib/shared-browser-runtime.js +25 -9
  323. package/lib/shared-browser-storage-sync.js +43 -0
  324. package/lib/shared-browser-storage.js +101 -0
  325. package/lib/shared-browser-tools.js +2 -1
  326. package/lib/shared-browser-worker.js +46 -10
  327. package/lib/terminal-manager.js +52 -0
  328. package/lib/users-permissions.js +2 -8
  329. package/lib/users.js +4 -0
  330. package/lib/workspace-query-access.js +4 -1
  331. package/lib/ws-schema.js +8 -2
  332. package/lib/yoke/acp-agent-profiles.js +2 -2
  333. package/lib/yoke/adapters/claude.js +47 -8
  334. package/lib/yoke/adapters/codex.js +52 -111
  335. package/lib/yoke/claude-worker-startup.js +28 -0
  336. package/lib/yoke/codex-idle-recovery.js +81 -1
  337. package/lib/yoke/codex-recovery-admission.js +164 -0
  338. package/lib/yoke/session-tool-transport.js +3 -0
  339. package/package.json +5 -1
@@ -30,7 +30,7 @@ function canonical(value) {
30
30
  if (!value || typeof value !== "object") return value;
31
31
  var out = {}; Object.keys(value).sort().forEach(function (key) { out[key] = canonical(value[key]); }); return out;
32
32
  }
33
- function fingerprint(label, args) { var envelope = Object.assign({}, args); delete envelope.actor; delete envelope.operationId; return crypto.createHash("sha256").update(label + ":" + JSON.stringify(canonical(envelope))).digest("hex"); }
33
+ function fingerprint(label, args) { var envelope = Object.assign({}, args); delete envelope.actor; delete envelope.operationId; delete envelope.revalidate; return crypto.createHash("sha256").update(label + ":" + JSON.stringify(canonical(envelope))).digest("hex"); }
34
34
  function ensureRevision(value, current, label) {
35
35
  if (!Number.isInteger(value) || value < 1) throw error("A valid " + label + " revision is required.", "STALE", current);
36
36
  if (value !== current) throw error("This " + label + " changed since it was opened.", "STALE", current);
@@ -114,7 +114,8 @@ function createKnowledgeDbService(options) {
114
114
  function all() { return storage.list(); }
115
115
  function listCached(args) { var items = storage.getCached(); return { databases: items.filter(function (item) { return args && args.archived === true ? !!item.archivedAt : !item.archivedAt; }).sort(function (a, b) { return b.updatedAt - a.updatedAt || (a._id === b._id ? 0 : a._id < b._id ? -1 : 1); }).map(function (item) { return publicDatabase(item, false); }) }; }
116
116
  function assertAggregate(next) { if (Buffer.byteLength(JSON.stringify(next), "utf8") > aggregateLimit) throw error("Knowledge database exceeds the " + aggregateLimit + " byte aggregate storage limit (records, history, mappings and live receipts).", "LIMIT"); }
117
- function replace(current, next) { assertAggregate(next); return storage.replaceIfRevision(current._id, current.revision, next).then(function (replaced) { if (typeof replaced !== "boolean") throw storageModule.invalid("replaceIfRevision must resolve to a boolean."); if (!replaced) throw error("This database changed during the operation.", "STALE", current.revision); return next; }); }
117
+ function guard(args, current, phase) { if (args && typeof args.revalidate === 'function') args.revalidate(current ? publicDatabase(current, false) : null, phase); }
118
+ function replace(current, next, control) { assertAggregate(next); guard(control, current, 'saving'); return storage.replaceIfRevision(current._id, current.revision, next).then(function (replaced) { if (typeof replaced !== "boolean") throw storageModule.invalid("replaceIfRevision must resolve to a boolean."); if (!replaced) throw Object.assign(error("This database changed during the operation.", "STALE", current.revision), { persisted: false }); guard(control, null, "saved"); return next; }); }
118
119
  function projectionContent(value) { return JSON.stringify({ schemaVersion: 1, namespace: "knowledge-database-summary", databaseId: value._id, name: value.name, archived: !!value.archivedAt, revision: value.revision, fields: value.schema.filter(function (field) { return !field.archived; }).map(function (field) { return { id: field.id, name: field.name, type: field.type }; }), views: (value.views || []).filter(function (view) { return !view.archived; }).map(function (view) { return { id: view.id, name: view.name, type: "table" }; }), recordCount: value.records.filter(function (record) { return !record.archivedAt; }).length }); }
119
120
  function project(value, actorValue) {
120
121
  if (typeof settings.projectSummary !== "function") return Promise.resolve({ pending: false });
@@ -125,18 +126,20 @@ function createKnowledgeDbService(options) {
125
126
  function finish(value, result, actorValue) { return project(value, actorValue).then(function (projection) { return Object.assign({}, result, { projectionPending: projection.pending, projectionError: projection.error || null }); }); }
126
127
  function mutate(databaseId, args, label, fn) {
127
128
  var operationId = mutationId(args.operationId); var operationFingerprint = fingerprint(label, args); return serialize(databaseId, function () { return load(databaseId).then(function (current) {
129
+ guard(args, current);
128
130
  var duplicate = receipt(current, operationId, operationFingerprint); if (duplicate) return Object.assign({}, duplicate, { projectionPending: current.projectionPending === true, projectionError: current.projectionError || null });
129
131
  if (current.archivedAt && label !== "database") throw error("Knowledge database is archived.", "ARCHIVED", current.revision);
130
- var next = clone(current); var result = fn(next, current); next.projectionPending = true; next.projectionError = null; remember(next, operationId, operationFingerprint, result); return replace(current, next).then(function () { return finish(next, result, actor(args.actor)); });
132
+ var next = clone(current); var result = fn(next, current); next.projectionPending = true; next.projectionError = null; remember(next, operationId, operationFingerprint, result); return replace(current, next, args).then(function () { return finish(next, result, actor(args.actor)); });
131
133
  }); });
132
134
  }
133
135
  function create(args) {
134
136
  var input = args || {}; var operationId = mutationId(input.operationId); var operationFingerprint = fingerprint("create", input); return serialize("create:" + operationId, function () { return all().then(function (items) {
137
+ guard(input);
135
138
  var duplicate; var duplicateDatabase; items.some(function (item) { duplicate = receipt(item, operationId, operationFingerprint); if (duplicate) duplicateDatabase = item; return !!duplicate; }); if (duplicate) return Object.assign({}, duplicate, { projectionPending: duplicateDatabase.projectionPending === true, projectionError: duplicateDatabase.projectionError || null });
136
139
  var at = now(); var who = actor(input.actor); var titleId = newId("fld"); var schema = input.schema ? validation.normalizeSchema(input.schema) : validation.normalizeSchema([{ id: titleId, name: "Name", type: "title", required: true }]);
137
140
  var viewId = newId("view"); var item = { _id: newId("db"), name: validation.text(input.name, "Database name", 120, false), revision: 1, archivedAt: null, schema: schema,
138
141
  views: [{ id: viewId, name: "Table", type: "table", columns: schema.filter(function (field) { return !field.archived; }).map(function (field) { return field.id; }), filters: [], sorts: [], archived: false }], records: [], recordHistory: {}, databaseHistory: [], receipts: [], createdAt: at, updatedAt: at, createdBy: who, updatedBy: who, projectionPending: true, projectionError: null };
139
- var result = { database: publicDatabase(item, false) }; remember(item, operationId, operationFingerprint, result); assertAggregate(item); return storage.insert(item).then(function () { return finish(item, result, who); });
142
+ var result = { database: publicDatabase(item, false) }; remember(item, operationId, operationFingerprint, result); assertAggregate(item); guard(input, null, "saving"); return storage.insert(item).then(function () { guard(input, null, "saved"); return finish(item, result, who); });
140
143
  }); });
141
144
  }
142
145
  function list(args) { return all().then(function (items) { return { databases: items.filter(function (item) { return args && args.archived === true ? !!item.archivedAt : !item.archivedAt; }).sort(function (a, b) { return b.updatedAt - a.updatedAt || (a._id === b._id ? 0 : a._id < b._id ? -1 : 1); }).map(function (item) { return publicDatabase(item, false); }) }; }); }
@@ -155,8 +158,8 @@ function createKnowledgeDbService(options) {
155
158
  function archiveRecord(args) { return mutate(args.databaseId, args, "record", function (next) { var index = next.records.findIndex(function (record) { return record.id === validation.id(args.recordId, "record id"); }); if (index === -1) throw error("Knowledge database record not found.", "NOT_FOUND"); var before = next.records[index]; ensureRevision(args.expectedRevision, before.revision, "record"); next.recordHistory[before.id] = (next.recordHistory[before.id] || []).concat([{ revision: before.revision, at: now(), actor: actor(args.actor), action: args.recover === true ? "recover" : "archive", values: before.values, archivedAt: before.archivedAt }]).slice(-MAX_HISTORY); var record = clone(before); record.archivedAt = args.recover === true ? null : now(); record.revision++; record.updatedAt = now(); record.updatedBy = actor(args.actor); next.records[index] = record; next.revision++; next.updatedAt = record.updatedAt; next.updatedBy = record.updatedBy; return { databaseRevision: next.revision, record: publicRecord(record) }; }); }
156
159
  function readRecord(args) { return all().then(function (databases) { var item = databases.filter(function (entry) { return entry._id === validation.id(args.databaseId, "database id"); })[0]; if (!item) throw error("Knowledge database not found.", "NOT_FOUND"); var record = item.records.filter(function (entry) { return entry.id === validation.id(args.recordId, "record id"); })[0]; if (!record) throw error("Knowledge database record not found.", "NOT_FOUND"); return { database: publicDatabase(item, false), record: publicRecord(record, referenceStates(item, record, databases, settings)), title: titleValue(item, record), history: clone(item.recordHistory[record.id] || []) }; }); }
157
160
  function query(args) { return all().then(function (databases) { var item = databases.filter(function (entry) { return entry._id === validation.id(args.databaseId, "database id"); })[0]; if (!item) throw error("Knowledge database not found.", "NOT_FOUND"); if (item.archivedAt) throw error("Knowledge database is archived.", "ARCHIVED", item.revision); return queryRecords(item, args, databases, settings); }); }
158
- function retryProjection(args) { return serialize(args.databaseId, function () { return load(args.databaseId).then(function (item) { return finish(item, { database: publicDatabase(item, false) }, actor(args.actor)); }); }); }
159
- var importer = imports.attachKnowledgeDbImport({ clone: clone, error: error, actor: actor, newId: newId, mutationId: mutationId, fingerprint: fingerprint, ensureRevision: ensureRevision, receipt: receipt, remember: remember, serialize: serialize, load: load, all: all, referenceContext: referenceContext, replace: replace, finish: finish });
161
+ function retryProjection(args) { return serialize(args.databaseId, function () { return load(args.databaseId).then(function (item) { guard(args, item, "saving"); return finish(item, { database: publicDatabase(item, false) }, actor(args.actor)).then(function (result) { guard(args, null, "saved"); return result; }); }); }); }
162
+ var importer = imports.attachKnowledgeDbImport({ clone: clone, error: error, actor: actor, newId: newId, mutationId: mutationId, fingerprint: fingerprint, ensureRevision: ensureRevision, receipt: receipt, remember: remember, serialize: serialize, load: load, all: all, referenceContext: referenceContext, replace: replace, guard: guard, finish: finish });
160
163
  return { readStorageStatus: function () { return typeof storage.diagnostics === "function" ? storage.diagnostics() : Promise.resolve(null); }, importRecords: importer.importRecords, readImportProgress: importer.readImportProgress, hasStore: function () { return storage.hasStore(); }, isReady: function () { return storage.isReady(); }, initialize: function () { return Promise.resolve(storage.initialize()).then(function () {}); }, listCached: listCached, create: create, list: list, read: read, rename: rename, archive: archive, recover: function (args) { return archive(Object.assign({}, args, { recover: true })); }, updateSchema: updateSchema, saveView: saveView, createRecord: createRecord, updateRecord: updateRecord, archiveRecord: archiveRecord, recoverRecord: function (args) { return archiveRecord(Object.assign({}, args, { recover: true })); }, readRecord: readRecord, query: query, retryProjection: retryProjection };
161
164
  }
162
165
 
@@ -18,7 +18,8 @@ function createKnowledgeDocumentIndex(options) {
18
18
  if (file.size > options.maxDocumentBytes) { errors.push({ name: file.name, code: "TOO_LARGE", error: "Document exceeds the safe read limit." }); continue; }
19
19
  if (known && token && known.token === token) {
20
20
  if (bytes + known.bytes > MAX_INDEX_BYTES) { errors.push({ name: file.name, code: "INDEX_LIMIT", error: "Workspace Knowledge exceeds the safe index limit." }); continue; }
21
- next[key] = known; documents.push(known.document); bytes += known.bytes; continue;
21
+ var fresh = Object.assign({}, known, { document: Object.assign({}, known.document, file) });
22
+ next[key] = fresh; documents.push(fresh.document); bytes += known.bytes; continue;
22
23
  }
23
24
  try { content = options.read(file); } catch (error) { errors.push({ name: file.name, code: error.code || "READ_FAILED", error: error.message }); continue; }
24
25
  var size = Buffer.byteLength(content, "utf8");
@@ -136,7 +136,13 @@ function createKnowledgeFileStore(cwd, options) {
136
136
  var listed = list(); var identity = listed.filter(function (entry) { return entry.name === item.name; })[0]; if (!identity) return; ensureMetadata(true);
137
137
  var folder = path.join(historyRoot, identity.id); safeDirectory(folder, true);
138
138
  var target = path.join(folder, Date.now() + "-" + item.revision + ".md");
139
- fs.writeFileSync(target, item.content, { flag: "wx", mode: 0o600 });
139
+ try { fs.writeFileSync(target, item.content, { flag: "wx", mode: 0o600 }); }
140
+ catch (error) {
141
+ // A move and its link rewrite can snapshot the same revision in one millisecond.
142
+ if (error.code !== "EEXIST") throw error;
143
+ var stat = fs.lstatSync(target);
144
+ if (!stat.isFile() || stat.isSymbolicLink() || fs.readFileSync(target, "utf8") !== item.content) throw error;
145
+ }
140
146
  }
141
147
  function save(name, content, expectedRevision) {
142
148
  var clean = cleanName(name); if (!clean || itemType(clean) !== "text") throw new Error("Only valid Markdown documents can be edited.");
@@ -0,0 +1,46 @@
1
+ var validName = require('./knowledge-file-store').validName;
2
+ // Narrow operations over the same document store used by the Knowledge workbench.
3
+ function createKnowledgeProductOperations(ctx) {
4
+ function find(args, deleted) {
5
+ var item = args.documentId ? ctx.files.list().concat(deleted ? ctx.files.deleted() : []).find(function (entry) { return entry.id === args.documentId; }) : null;
6
+ if (args.documentId && !item) throw Object.assign(new Error('Knowledge document not found.'), { code: 'ENOENT' });
7
+ var name = args.name || item && item.name;
8
+ if (!name || item && args.name && args.name !== item.name) throw new Error('Choose one exact Knowledge document.');
9
+ if (item && item.deleted) return item;
10
+ var current = ctx.files.read(name);
11
+ if (current.itemType !== 'text') throw new Error('Legacy JSONL sources are read-only.');
12
+ return current;
13
+ }
14
+ function read(args) { var item = find(args); return { documentId: item.id, name: item.name, revision: item.revision, content: item.content }; }
15
+ function saved(item, who) { return Object.assign({ documentId: item.id, name: item.name, revision: item.revision, persisted: true }, ctx.mirror(who, [item.name])); }
16
+ function snapshot(op) {
17
+ if (op.action === 'retry_document_sync' && op.names.some(function (name) { return !validName(name); })) throw new Error('Invalid Knowledge source name.');
18
+ if (op.action === 'create_document') return ctx.files.list().filter(function (item) { return item.name === op.name; });
19
+ if (op.action === 'retry_document_sync') return ctx.files.list().filter(function (item) { return op.names.indexOf(item.name) >= 0; });
20
+ var item = find(op, op.action === 'restore_document');
21
+ var result = { documentId: item.id, name: item.name, revision: item.revision, deleted: !!item.deleted };
22
+ if (op.action === 'restore_document') result.version = ctx.files.readHistory(item.id, op.version);
23
+ if (op.action === 'rename_document') result.documents = ctx.files.list();
24
+ return result;
25
+ }
26
+ function mutate(op, who) {
27
+ if (op.action === 'create_document') return saved(ctx.files.save(op.name, op.content, null), who);
28
+ if (op.action === 'retry_document_sync') return Object.assign({ persisted: true }, ctx.mirror(who, op.names));
29
+ var item = find(op, op.action === 'restore_document');
30
+ if (op.action === 'update_document') return saved(ctx.files.save(item.name, op.content, op.expectedRevision), who);
31
+ if (op.action === 'rename_document') return ctx.rename({ from: item.name, name: op.newName, documentId: item.id, expectedRevision: op.expectedRevision }, who);
32
+ if (op.action === 'delete_document') { ctx.files.remove(item.name, op.expectedRevision); return Object.assign({ documentId: item.id, name: item.name, persisted: true, deleted: true }, ctx.mirror(who, [item.name])); }
33
+ if (op.action === 'restore_document') return saved(ctx.files.restore(item.id, op.version, item.name, item.deleted ? null : op.expectedRevision), who);
34
+ throw new Error('Unsupported Knowledge document operation.');
35
+ }
36
+ function query(op) {
37
+ if (op.action === 'list_documents') return { documents: op.deleted ? ctx.files.deleted() : ctx.files.list() };
38
+ if (op.action === 'read_document') return read(op);
39
+ var item = find(op, true);
40
+ if (op.action === 'document_history') return { documentId: item.id, name: item.name, versions: ctx.files.history(item.id) };
41
+ if (op.action === 'read_document_version') return Object.assign({ documentId: item.id, name: item.name }, ctx.files.readHistory(item.id, op.version));
42
+ throw new Error('Unsupported Knowledge document query.');
43
+ }
44
+ return { query: query, snapshot: snapshot, mutate: mutate };
45
+ }
46
+ module.exports = { createKnowledgeProductOperations: createKnowledgeProductOperations };
@@ -0,0 +1,12 @@
1
+ // Stable catalog identities are the only accepted library references.
2
+ var catalog = require('./public/mate-avatars/catalog.json');
3
+ var ids = new Set(catalog.map(function (entry) { return entry.id; }));
4
+ function valid(id) { return typeof id === 'string' && ids.has(id); }
5
+ function validate(profile, mate) {
6
+ if (profile !== undefined && (!profile || typeof profile !== 'object' || Array.isArray(profile))) return 'Choose a valid Mate profile.';
7
+ if (profile && Object.prototype.hasOwnProperty.call(profile, 'avatarCatalogId') && profile.avatarCatalogId !== '' && !valid(profile.avatarCatalogId) && !(profile.avatarCatalogId === 'lead-clay' && mate && mate.builtinKey === 'clay')) {
8
+ return 'Choose an avatar from the Clay catalog.';
9
+ }
10
+ return null;
11
+ }
12
+ module.exports = { valid: valid, validate: validate };
@@ -0,0 +1,41 @@
1
+ // Stable v1 migration, run only against the caller's normal Mate registry.
2
+ var catalog = require('./mate-avatar-catalog');
3
+ var managedArtwork = { clay: ['/clay-studio-symbol.png', '/mates/ally.png', '/mate-avatars/lead-clay.png'], ally: ['/mates/ally.png'] };
4
+ function mappedId(mate) {
5
+ var p = mate.profile || mate;
6
+ var key = JSON.stringify([mate.id || '', p.avatarStyle || 'imprint', p.avatarSeed || mate.id || p.displayName || mate.name || 'Mate']);
7
+ var hash = 2166136261;
8
+ for (var i = 0; i < key.length; i++) hash = Math.imul(hash ^ key.charCodeAt(i), 16777619) >>> 0;
9
+ var index = hash % 96;
10
+ return (index < 48 ? 'B' : 'C') + String(index % 48 + 1).padStart(2, '0');
11
+ }
12
+ function hasCustom(mate) {
13
+ var p = mate.profile || {};
14
+ var custom = p.avatarCustom || mate.avatarCustom;
15
+ if (!custom) return false;
16
+ var managed = managedArtwork[mate.builtinKey] || [];
17
+ return managed.indexOf(custom) === -1;
18
+ }
19
+ function normalizeMate(mate) {
20
+ if (!mate || hasCustom(mate)) return false;
21
+ var before = JSON.stringify(mate.profile || {});
22
+ var p = Object.assign({}, mate.profile || {});
23
+ p.avatarCatalogId = mate.builtinKey === 'clay' ? 'lead-clay' : catalog.valid(p.avatarCatalogId) ? p.avatarCatalogId : mappedId(mate);
24
+ p.avatarStyle = 'catalog';
25
+ delete p.avatarSeed;
26
+ if (p.avatarCustom) p.avatarCustom = '';
27
+ if (mate.builtinKey === 'clay') p.avatarLocked = true;
28
+ if (before === JSON.stringify(p) && !Object.prototype.hasOwnProperty.call(mate, 'avatarStyle') && !Object.prototype.hasOwnProperty.call(mate, 'avatarSeed') && !Object.prototype.hasOwnProperty.call(mate, 'avatarCustom')) return false;
29
+ mate.profile = p;
30
+ delete mate.avatarStyle;
31
+ delete mate.avatarSeed;
32
+ delete mate.avatarCustom;
33
+ return true;
34
+ }
35
+ function migrate(data) {
36
+ var changed = false;
37
+ data.mates.forEach(function (mate) { if (normalizeMate(mate)) changed = true; });
38
+ if (data.avatarCatalogMigrationVersion !== 1) { data.avatarCatalogMigrationVersion = 1; changed = true; }
39
+ return changed;
40
+ }
41
+ module.exports = { mappedId: mappedId, hasCustom: hasCustom, normalizeMate: normalizeMate, migrate: migrate };
@@ -13,6 +13,7 @@ function schema() {
13
13
  communicationStyle: z.string().describe("JSON array of communication preferences"),
14
14
  autonomy: z.string().describe("A concise description of autonomy, boundaries, and escalation behavior"),
15
15
  identityMarkdown: z.string().describe("The complete first-person Mate identity in Markdown"),
16
+ scheduleIntent: z.string().optional().describe("Optional JSON schedule intent to bind only after the Mate is created"),
16
17
  };
17
18
  }
18
19
 
@@ -39,6 +40,7 @@ function getToolDefs(onPropose) {
39
40
  communicationStyle: parseArray(args.communicationStyle, "communicationStyle"),
40
41
  autonomy: args.autonomy,
41
42
  identityMarkdown: args.identityMarkdown,
43
+ scheduleIntent: args.scheduleIntent || null,
42
44
  };
43
45
  } catch (error) {
44
46
  return Promise.resolve({ content: [{ type: "text", text: "Error: " + error.message }], isError: true });
@@ -0,0 +1,10 @@
1
+ var personal = require('./mcp-session-access');
2
+ var provenance = require('./session-provenance');
3
+ function assertCanonicalKnowledgeQuery(source, project, multiUser, generation, findUser) {
4
+ var session = source.session, manager = project && project.getSessionManager();
5
+ if (!session || !manager || manager.sessions.get(session.localId) !== session || Number(session._sdkQueryGeneration || 0) !== generation) throw new Error('This canonical Knowledge query is stale.');
6
+ if (session.destroying || session.hidden || session.taskStopRequested || session.abortController && session.abortController.signal.aborted || session.spawn || provenance.isWorker(session) || session.homeDebatePlanning || session.debateSetupMode || session._mentionInProgress || session.mateCreationMode || session.sessionVisibility === 'shared' || session._mcpForeignInput || session.delegated || session._delegatedBy || session._pairDelegation) throw new Error('Canonical Knowledge requires a live private owner-authored query.');
7
+ if (multiUser && (!source.projectOwnerId || session.ownerId !== source.projectOwnerId || !personal.canUsePersonalConnections(session, manager.sessions) || findUser && !findUser(source.projectOwnerId))) throw new Error('Canonical Knowledge owner access changed.');
8
+ if (!multiUser && (session.ownerId || (session.history || []).some(function (entry) { return entry.type === 'user_message' && entry.from && entry.from !== 'default'; }))) throw new Error('Foreign input cannot authorize canonical Knowledge.');
9
+ }
10
+ module.exports = { assertCanonicalKnowledgeQuery: assertCanonicalKnowledgeQuery };
@@ -387,7 +387,7 @@ function attachMateKnowledgeService(ctx) {
387
387
  }
388
388
  if (typeof resolveMate !== "function") return null;
389
389
  var mate = resolveMate(source.projectOwnerId || null, source.mateId);
390
- if (!mate || mate.id !== source.mateId) return null;
390
+ if (!mate || mate.lifecycleDeletion || mate.id !== source.mateId) return null;
391
391
  if (isMultiUser()) {
392
392
  if (!source.projectOwnerId) return null;
393
393
  if (mate.createdBy !== source.projectOwnerId) return null;
@@ -398,7 +398,7 @@ function attachMateKnowledgeService(ctx) {
398
398
 
399
399
  var def = mate.builtinKey ? builtinMates.getBuiltinByKey(mate.builtinKey) : null;
400
400
  var isClay = !!(mate.builtinKey === "clay" && def && def.hostAgent === true);
401
- return { own: own, isClay: isClay, ownerId: source.projectOwnerId || null,
401
+ return { project: project, own: own, isClay: isClay, ownerId: source.projectOwnerId || null,
402
402
  databaseService: typeof project.getKnowledgeDbService === "function" ? project.getKnowledgeDbService() : null,
403
403
  actor: { type: "user", userId: source.session && source.session.ownerId || source.projectOwnerId || null, displayName: null } };
404
404
  }
@@ -410,12 +410,14 @@ function attachMateKnowledgeService(ctx) {
410
410
  // to work through a handler that was created while it was still live.
411
411
  function bind(source) {
412
412
  var initial = resolve(source);
413
+ var generation = Number(source && source.session && source.session._sdkQueryGeneration || 0);
413
414
  if (!initial) return null;
414
415
 
415
416
  function revalidate() {
416
417
  var current = resolve(source);
418
+ if (initial.isClay) require("./mate-knowledge-canonical-access").assertCanonicalKnowledgeQuery(source, getProjects().get(source.projectSlug), isMultiUser(), generation, context.findUser);
417
419
  if (!current) throw new Error("This Knowledge binding is no longer valid for the current session.");
418
- if (current.isClay !== initial.isClay || current.own.scopeId !== initial.own.scopeId || current.databaseService !== initial.databaseService) {
420
+ if (current.project !== initial.project || current.isClay !== initial.isClay || current.own.scopeId !== initial.own.scopeId || current.databaseService !== initial.databaseService) {
419
421
  throw new Error("This Knowledge binding is no longer valid for the current session.");
420
422
  }
421
423
  return current;
@@ -446,7 +448,8 @@ function attachMateKnowledgeService(ctx) {
446
448
  for (var i = 0; i < listed.length; i++) {
447
449
  var entry = listed[i];
448
450
  if (!entry || !entry.id || !entry.dir) continue;
449
- if (isMultiUser() && ownerId && entry.createdBy && entry.createdBy !== ownerId) continue;
451
+ var registered = resolveMate(ownerId, entry.id);
452
+ if (!registered || registered.lifecycleDeletion || isMultiUser() && registered.createdBy !== ownerId) continue;
450
453
  var scope = scopeForMate(entry.dir, entry.id, entry.name || null);
451
454
  if (!scope) continue;
452
455
  out.push(scope);
@@ -0,0 +1,94 @@
1
+ var crypto = require('crypto');
2
+ function attachMateLifecycleService(ctx) {
3
+ var busy = new Set();
4
+ function owner(userId) {
5
+ if (ctx.users.isMultiUser() && (!userId || !ctx.users.findUserById(userId))) throw new Error('Mate owner is unavailable.');
6
+ return ctx.mates.buildMateCtx(ctx.users.isMultiUser() ? userId : null);
7
+ }
8
+ function read(userId, id) {
9
+ var mate = ctx.mates.getMate(owner(userId), id);
10
+ if (!mate || ctx.users.isMultiUser() && mate.createdBy !== userId) throw new Error('Mate is unavailable or belongs to another owner.');
11
+ return mate;
12
+ }
13
+ function validateReceipt(userId, id, allowMissing) {
14
+ var mate = ctx.mates.getMate(owner(userId), id);
15
+ if (!mate && allowMissing) return;
16
+ if (!mate || ctx.users.isMultiUser() && mate.createdBy !== userId) throw new Error('Mate receipt access changed.');
17
+ }
18
+ function list(userId) {
19
+ return ctx.mates.getAllMates(owner(userId)).filter(function (mate) { return !ctx.users.isMultiUser() || mate.createdBy === userId; });
20
+ }
21
+ function broadcast(userId, message, ws) {
22
+ var seen = new Set();
23
+ function send(socket) {
24
+ if (!socket || seen.has(socket) || socket.readyState !== 1) return;
25
+ if (ctx.users.isMultiUser() && (!socket._clayUser || socket._clayUser.id !== userId)) return;
26
+ seen.add(socket); socket.send(JSON.stringify(message));
27
+ }
28
+ send(ws);
29
+ ctx.projects.forEach(function (project) { project.forEachClient(send); });
30
+ }
31
+ function update(userId, id, updates, ws, requestId) {
32
+ var mate = read(userId, id);
33
+ if (Object.prototype.hasOwnProperty.call(updates, 'lifecycleDeletion')) throw new Error('Mate deletion state is server-managed.');
34
+ if (busy.has(id) || mate.lifecycleDeletion) throw new Error('Mate deletion is pending.');
35
+ var updated = ctx.mates.updateMate(owner(userId), id, updates);
36
+ if (!updated || updated.error) throw new Error(updated && updated.error || 'Mate not found');
37
+ try {
38
+ broadcast(userId, { type: 'mate_updated', mate: updated, requestId: requestId }, ws);
39
+ if (ctx.refresh) ctx.refresh(owner(userId));
40
+ } catch (error) { error.productControlResult = { persisted: true, retrySafe: false }; throw error; }
41
+ return updated;
42
+ }
43
+ function idle(project) {
44
+ if (!project) return;
45
+ if (project.inspectMateLifecycleRuntime && project.inspectMateLifecycleRuntime().some(function (t) { return !t.exited; })) throw new Error('Close the Mate terminals before deletion.');
46
+ project.getSessionManager().sessions.forEach(function (session) {
47
+ if (session.isProcessing || session._queryStarting || session.queryInstance || session.worker || session.terminalId || session.runtimeTerminalId || Object.keys(session._mentionSessions || {}).length) throw new Error('Wait for this Mate runtime to become idle before deletion.');
48
+ });
49
+ }
50
+ async function closeRuntime(slug, validate) {
51
+ var timer;
52
+ try {
53
+ return await Promise.race([Promise.resolve().then(function () { if (validate) validate(true); return ctx.removeProject(slug, true); }), new Promise(function (resolve, reject) {
54
+ timer = setTimeout(function () { reject(new Error('Mate runtime cleanup timed out; deletion remains pending.')); }, 10000);
55
+ })]);
56
+ } finally { clearTimeout(timer); }
57
+ }
58
+ async function remove(userId, id, validate, ws) {
59
+ var mate = read(userId, id), slug = 'mate-' + id, project = ctx.projects.get(slug);
60
+ if (mate.primary || mate.builtinKey === 'clay') throw new Error('Canonical Clay and primary Mates cannot be deleted.');
61
+ if (busy.has(id)) throw new Error('Another Mate lifecycle operation is applying.');
62
+ idle(project);
63
+ if (validate) validate();
64
+ busy.add(id);
65
+ var persisted = false, runtimeClosed = false;
66
+ try {
67
+ // Retain an owner-visible tombstone until every required cleanup is acknowledged.
68
+ if (!mate.lifecycleDeletion) mate = ctx.mates.updateMate(owner(userId), id, { lifecycleDeletion: { id: crypto.randomUUID(), runtimeRequired: !!project, runtimeClosed: !project } });
69
+ if (!mate || mate.error) throw new Error('Could not persist the pending Mate deletion.');
70
+ persisted = true;
71
+ if (mate.builtinKey) ctx.users.addDeletedBuiltinKey(userId, mate.builtinKey);
72
+ if (!project && mate.lifecycleDeletion.runtimeRequired && !mate.lifecycleDeletion.runtimeClosed) throw new Error('Runtime cleanup proof is unavailable; retain the pending deletion for operator recovery.');
73
+ if (project && await closeRuntime(slug, validate) !== true) throw new Error('Mate runtime cleanup was not acknowledged.');
74
+ runtimeClosed = true;
75
+ mate = ctx.mates.updateMate(owner(userId), id, { lifecycleDeletion: Object.assign({}, mate.lifecycleDeletion, { runtimeClosed: true }) });
76
+ if (!mate || mate.error) throw new Error('Runtime closure proof could not be saved.');
77
+ if (validate) validate(true);
78
+ var current = read(userId, id);
79
+ if (!current.lifecycleDeletion || current.lifecycleDeletion.id !== mate.lifecycleDeletion.id) throw new Error('Mate deletion identity changed.');
80
+ var result = ctx.mates.deleteMate(owner(userId), id, { strictCleanup: true });
81
+ if (!result || !result.ok) throw new Error(result && result.error || 'Mate deletion was not persisted.');
82
+ var definitions = require('./builtin-mates');
83
+ var available = ctx.mates.getMissingBuiltinKeys(owner(userId)).map(function (key) { var def = definitions.getBuiltinByKey(key); return def && { key: def.key, displayName: def.displayName, bio: def.bio, avatarCustom: def.avatarCustom || '', avatarStyle: def.avatarStyle || 'imprint', avatarColor: def.avatarColor || '' }; }).filter(Boolean);
84
+ broadcast(userId, { type: 'mate_deleted', mateId: id, availableBuiltins: available }, ws);
85
+ if (ctx.refresh) ctx.refresh(owner(userId));
86
+ return { persisted: true, runtimeClosed: true, directoryRemoved: true };
87
+ } catch (error) {
88
+ error.productControlResult = { persisted: persisted, runtimeClosed: runtimeClosed, deletionPending: !!ctx.mates.getMate(owner(userId), id), retrySafe: false };
89
+ throw error;
90
+ } finally { busy.delete(id); }
91
+ }
92
+ return { read: read, validateReceipt: validateReceipt, list: list, update: update, remove: remove };
93
+ }
94
+ module.exports = { attachMateLifecycleService: attachMateLifecycleService };
@@ -0,0 +1,43 @@
1
+ // Shared Home/Mate delivery. Canonical callers supply an exact live guard and
2
+ // persist delegated provenance before any provider input is admitted.
3
+ async function deliver(ctx, session, text, options) {
4
+ options = options || {};
5
+ var guarded = typeof options.revalidate === 'function';
6
+ function current() {
7
+ if (ctx.sm.sessions.get(session.localId) !== session) throw new Error('The conversation was deleted or replaced.');
8
+ if (guarded) {
9
+ options.revalidate();
10
+ if (session.taskStopRequested || session._pendingMessageDrainPaused || session._responseRecoveryStopPending || session.abortController && session.abortController.signal.aborted) throw new Error('The conversation is stopped; explicit owner resumption is required.');
11
+ }
12
+ return true;
13
+ }
14
+ if (guarded) current.exactProductDelivery = true;
15
+ current();
16
+ var event = options.event || { type: 'user_message', text: text };
17
+ if (guarded) {
18
+ if (ctx.sm.saveSessionFile(session) !== true) throw new Error('The conversation could not be saved; no provider delivery was attempted.');
19
+ if (ctx.sm.sendAndRecordDurably(session, event) !== true) throw new Error('The delegated message could not be saved; no provider delivery was attempted.');
20
+ session._mcpForeignInput = true;
21
+ } else ctx.sm.sendAndRecord(session, event);
22
+ var attempted = false;
23
+ try {
24
+ current();
25
+ var active = !!session.isProcessing;
26
+ session.isProcessing = true;
27
+ if (!active) session.sentToolResults = {};
28
+ if (options.onRecorded) options.onRecorded(session);
29
+ var queryText = options.queryText || text;
30
+ attempted = true;
31
+ if (session.queryInstance || session.worker && session.messageQueue === 'worker' || active) {
32
+ if (ctx.sdk.pushMessage(session, queryText, null) === true) return { status: 'queued', persisted: true, delivery: 'accepted', completion: 'pending' };
33
+ if (active) return { status: 'failed', persisted: true, delivery: 'rejected', retrySafe: false, error: 'The active provider did not accept the message. Inspect before another request.' };
34
+ }
35
+ var accepted = await ctx.sdk.startQuery(session, queryText, null, ctx.linuxUser || null, guarded ? current : undefined);
36
+ return { status: accepted === true ? 'started' : 'failed', persisted: true, delivery: accepted === true ? 'accepted' : 'not_confirmed', completion: 'pending', retrySafe: false };
37
+ } catch (error) {
38
+ if (!attempted) session.isProcessing = false;
39
+ error.productControlResult = { persisted: true, delivery: attempted ? 'unknown' : 'not_attempted', retrySafe: false };
40
+ throw error;
41
+ }
42
+ }
43
+ module.exports = { deliver: deliver };
@@ -4,6 +4,7 @@ var resolveMateIdentityFile = require('./mate-identity-file').resolveMateIdentit
4
4
 
5
5
  var fs = require("fs");
6
6
  var path = require("path");
7
+ var scheduleInput = require("./mate-schedule-input");
7
8
 
8
9
  function cleanLine(value, maxLength, field) {
9
10
  if (typeof value !== "string") throw new Error(field + " is required.");
@@ -27,6 +28,11 @@ function normalize(definition) {
27
28
  var identity = typeof definition.identityMarkdown === "string" ? definition.identityMarkdown.replace(/\u0000/g, "").trim() : "";
28
29
  if (identity.length < 200) throw new Error("The Mate identity is incomplete.");
29
30
  if (identity.length > 30000) throw new Error("The Mate identity is too long.");
31
+ var scheduleIntent = definition.scheduleIntent || null;
32
+ if (scheduleIntent) {
33
+ var scheduleChecked = scheduleInput.normalize(scheduleIntent);
34
+ scheduleIntent = { message: scheduleChecked.message, scheduleRule: scheduleChecked.rule };
35
+ }
30
36
  return {
31
37
  name: cleanLine(definition.name, 80, "Mate name"),
32
38
  bio: cleanLine(definition.bio, 280, "Mate bio"),
@@ -37,6 +43,7 @@ function normalize(definition) {
37
43
  identityMarkdown: identity,
38
44
  vendor: cleanLine(definition.vendor || "claude", 40, "Vendor"),
39
45
  model: cleanLine(definition.model, 240, "Mate model"),
46
+ scheduleIntent: scheduleIntent,
40
47
  };
41
48
  }
42
49
 
@@ -54,6 +61,7 @@ function createReadyMate(mates, mateCtx, definition) {
54
61
  vendor: ready.vendor,
55
62
  model: ready.model,
56
63
  autonomy: ready.autonomy,
64
+ scheduleIntent: ready.scheduleIntent,
57
65
  });
58
66
  var mateDir = mates.getMateDir(mateCtx, mate.id);
59
67
  var identityPath = resolveMateIdentityFile(mateDir);
@@ -80,7 +88,7 @@ function createReadyMate(mates, mateCtx, definition) {
80
88
  vendor: ready.vendor,
81
89
  model: ready.model,
82
90
  profile: Object.assign({}, mate.profile || {}, { displayName: ready.name, bio: ready.bio }),
83
- seedData: Object.assign({}, mate.seedData || {}, { relationship: ready.relationship, activity: ready.activities, communicationStyle: ready.communicationStyle, autonomy: ready.autonomy }),
91
+ seedData: Object.assign({}, mate.seedData || {}, { relationship: ready.relationship, activity: ready.activities, communicationStyle: ready.communicationStyle, autonomy: ready.autonomy, scheduleIntent: ready.scheduleIntent }),
84
92
  });
85
93
  mates.backupIdentity(mateDir, ready.identityMarkdown);
86
94
  mates.logIdentityChange(mateDir, "clay_interview", ready.identityMarkdown, "");
@@ -0,0 +1,29 @@
1
+ var rules = require('./schedule-rule');
2
+ function normalize(input, previous) {
3
+ input = input || {};
4
+ var message = input.message === undefined && previous ? previous.message : input.message;
5
+ if (typeof message !== 'string' || !message.trim() || message.length > 1200 || message.indexOf('\u0000') >= 0) throw new Error('A message of 1 to 1200 characters is required.');
6
+ var source = input.scheduleRule || (previous && previous.rule) || {
7
+ version: 1, timezone: 'server-local', anchorDate: input.date, time: input.time, recurrence: null,
8
+ };
9
+ source = Object.assign({}, source);
10
+ if (input.date !== undefined) source.anchorDate = input.date;
11
+ if (input.time !== undefined) source.time = input.time;
12
+ var checked = rules.validateScheduleRule(source);
13
+ if (!checked.ok) throw new Error(checked.error);
14
+ return { message: message.trim(), rule: checked.value };
15
+ }
16
+ function timezone() { return Intl.DateTimeFormat().resolvedOptions().timeZone || 'UTC'; }
17
+ function review(value) {
18
+ var rule = value.rule;
19
+ var repeat = rule.recurrence ? 'Every ' + rule.recurrence.every + ' ' + rule.recurrence.unit +
20
+ (rule.recurrence.weekdays ? ' on ' + rule.recurrence.weekdays.map(function (day) { return ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'][day]; }).join(', ') : '') : 'Once';
21
+ if (rule.recurrenceEnd) repeat += rule.recurrenceEnd.type === 'after' ? ', ' + rule.recurrenceEnd.count + ' deliveries' : ', through ' + rule.recurrenceEnd.date;
22
+ if (rule.interval) {
23
+ var ending = rule.interval.end;
24
+ repeat += '; every ' + rule.interval.every + ' ' + rule.interval.unit;
25
+ repeat += !ending ? ', until end of day' : ending.type === 'after' ? ', ' + ending.count + ' deliveries per day' : ', until ' + ending.time;
26
+ }
27
+ return value.message + '\n' + rule.anchorDate + ' at ' + rule.time + ' (' + timezone() + ')\n' + repeat;
28
+ }
29
+ module.exports = { normalize: normalize, timezone: timezone, review: review };