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
@@ -0,0 +1,155 @@
1
+ // Claude delivery watchdog. Only an unanswered foreground turn is replayable.
2
+ function attachResponseRecovery(ctx) {
3
+ var states = new Map();
4
+ var schedule = ctx.setTimeout || setTimeout;
5
+ var unschedule = ctx.clearTimeout || clearTimeout;
6
+ var waitMs = ctx.waitMs || 180000;
7
+ var stopMs = ctx.stopMs || 10000;
8
+
9
+ function live(session, state) {
10
+ return states.get(session) === state && !session.destroying && !session.taskStopRequested
11
+ && state.ownerId === session.ownerId && state.vendor === session.vendor
12
+ && (!ctx.sessions || ctx.sessions.get(session.localId) === session);
13
+ }
14
+ function clearTimer(state) {
15
+ if (state.timer) unschedule(state.timer);
16
+ state.timer = null;
17
+ }
18
+ function cancel(session, expected) {
19
+ var state = states.get(session);
20
+ if (expected && state !== expected) return;
21
+ if (state) clearTimer(state);
22
+ states.delete(session);
23
+ }
24
+ function protectedWait(session) {
25
+ return [session.pendingPermissions, session.pendingAskUser, session.pendingElicitations,
26
+ session.pendingUserDialogs, session.activeTaskToolIds].some(function(map) {
27
+ return map && Object.keys(map).length > 0;
28
+ }) || (session.activeBackgroundTasks && session.activeBackgroundTasks.length > 0);
29
+ }
30
+ function arm(session, state) {
31
+ clearTimer(state);
32
+ if (!state.turns.length || state.turns[0].progress || state.stopping) return;
33
+ state.timer = schedule(function() { expire(session, state); }, waitMs);
34
+ if (state.timer && state.timer.unref) state.timer.unref();
35
+ }
36
+ function snapshot(payload) {
37
+ return Object.assign({}, payload, { images: payload.images ? JSON.parse(JSON.stringify(payload.images)) : payload.images });
38
+ }
39
+ function begin(session, payload, retryState) {
40
+ if (retryState) return retryState;
41
+ cancel(session);
42
+ var state = { turns: [snapshot(payload)], timer: null, stopping: false, retried: false, ownerId: session.ownerId, vendor: session.vendor };
43
+ states.set(session, state);
44
+ arm(session, state);
45
+ return state;
46
+ }
47
+ function enqueue(session, payload) {
48
+ var state = states.get(session);
49
+ if (!state) return begin(session, payload);
50
+ state.turns.push(snapshot(payload));
51
+ return state;
52
+ }
53
+ function progress(session) {
54
+ var state = states.get(session);
55
+ if (!state || state.stopping || !state.turns.length) return;
56
+ state.turns[0].progress = true;
57
+ clearTimer(state);
58
+ }
59
+ function event(session, msg) {
60
+ var state = states.get(session);
61
+ if (!state || state.stopping || !msg) return;
62
+ var type = msg.yokeType;
63
+ if (type === 'error' || type === 'interrupted') { cancel(session); return; }
64
+ if (type === 'result') {
65
+ if (msg.subtype === 'success' && !msg.is_error && msg.numTurns === 0
66
+ && msg.origin && msg.origin.kind === 'task-notification'
67
+ && !msg.localCommand && msg.resultText === '') return;
68
+ var remaining = require('./sdk-turn-queue').pendingSends(session, msg);
69
+ state.turns = remaining ? state.turns.slice(-remaining) : [];
70
+ state.retried = false;
71
+ arm(session, state);
72
+ if (!state.turns.length) cancel(session);
73
+ } else if ((type === 'text_delta' || type === 'thinking_delta') && (msg.text || msg.delta)
74
+ || type === 'tool_start' || type === 'tool_input_delta' || type === 'tool_progress'
75
+ || type === 'task_started' || type === 'task_progress' || type === 'subagent_message'
76
+ || type === 'message' && msg.messageRole === 'assistant' && msg.content && msg.content.length) {
77
+ progress(session);
78
+ }
79
+ }
80
+ function finish(session, state, text) {
81
+ if (!live(session, state)) { cancel(session, state); return; }
82
+ cancel(session);
83
+ // Fence late output when a transport cannot confirm termination.
84
+ session._sdkQueryGeneration = Number(session._sdkQueryGeneration || 0) + 1;
85
+ session.isProcessing = false;
86
+ session._awaitingTurnResult = false;
87
+ session._queuedTurnCount = 0;
88
+ ctx.changed();
89
+ ctx.record(session, { type: 'thinking_stop' });
90
+ ctx.record(session, { type: 'error', text: text });
91
+ ctx.record(session, { type: 'done', code: 1 });
92
+ ctx.broadcast();
93
+ }
94
+ async function expire(session, state) {
95
+ clearTimer(state);
96
+ if (!live(session, state)) { cancel(session, state); return; }
97
+ // Never stop a known tool or a human decision because prose is absent.
98
+ if (protectedWait(session)) { progress(session); return; }
99
+ state.stopping = true;
100
+ ctx.record(session, { type: 'info', text: 'Claude has not responded for three minutes. Stopping this attempt safely before retrying.' });
101
+ var timeout;
102
+ var stopped = false;
103
+ try {
104
+ var stopAttempt = (async function() {
105
+ var handle = session.queryInstance || state.handle;
106
+ if (!handle && session.abortController) session.abortController.abort();
107
+ if (!handle && state.starting) await state.starting;
108
+ handle = handle || session.queryInstance || state.handle;
109
+ if (handle) {
110
+ if (typeof handle.stopAndWait !== 'function') return false;
111
+ await handle.stopAndWait();
112
+ if (session.streamPromise) await session.streamPromise;
113
+ }
114
+ return true;
115
+ })();
116
+ session._responseRecoveryStopPending = stopAttempt;
117
+ stopAttempt.then(function(confirmed) {
118
+ if (confirmed && session._responseRecoveryStopPending === stopAttempt) session._responseRecoveryStopPending = null;
119
+ }, function() {});
120
+ stopped = await Promise.race([
121
+ stopAttempt,
122
+ new Promise(function(resolve) { timeout = schedule(function() { resolve(false); }, stopMs); }),
123
+ ]);
124
+ } catch (e) { stopped = false; }
125
+ if (timeout) unschedule(timeout);
126
+ if (!live(session, state)) { cancel(session, state); return; }
127
+ if (!stopped) {
128
+ finish(session, state, 'Claude could not be confirmed stopped. Automatic retry was cancelled to avoid running your request twice. Check your Claude connection before trying again.');
129
+ return;
130
+ }
131
+ if (state.retried || state.turns.length !== 1) {
132
+ finish(session, state, state.retried
133
+ ? 'Claude still has not responded after one automatic retry. Please send your message again or check your Claude connection.'
134
+ : 'Claude did not respond. Several messages were queued, so automatic replay was cancelled to avoid duplicating work. Please send the unanswered messages again.');
135
+ return;
136
+ }
137
+ state.retried = true;
138
+ state.stopping = false;
139
+ session.isProcessing = true;
140
+ ctx.changed();
141
+ ctx.record(session, { type: 'info', text: 'The previous attempt has stopped. Retrying your original message once.' });
142
+ ctx.send(session, { type: 'status', status: 'processing' });
143
+ arm(session, state);
144
+ var payload = state.turns[0];
145
+ await ctx.restart(session, payload, state);
146
+ }
147
+ return {
148
+ begin: begin, enqueue: enqueue, event: event, progress: progress, cancel: cancel,
149
+ current: function(session) { return states.get(session); },
150
+ valid: live,
151
+ stopping: function(session) { var state = states.get(session); return !!(state && state.stopping); },
152
+ dispose: function() { states.forEach(function(state) { clearTimer(state); }); states.clear(); },
153
+ };
154
+ }
155
+ module.exports = { attachResponseRecovery: attachResponseRecovery };
@@ -18,6 +18,13 @@ function attachAdmin(ctx) {
18
18
  var onGetProjectAccess = ctx.onGetProjectAccess;
19
19
  var onProjectOwnerChanged = ctx.onProjectOwnerChanged;
20
20
 
21
+ function verifyCurrentOwner(req, route) {
22
+ if (!route || req.method === "GET" || !users.isMultiUser()) return;
23
+ var actor = getMultiUserFromReq(req);
24
+ var access = onGetProjectAccess && onGetProjectAccess(route[1]);
25
+ if (!actor || !access || access.ownerId !== actor.id || !users.findUserById(access.ownerId)) throw new Error("Project owner access required");
26
+ }
27
+
21
28
  function handleRequest(req, res, fullUrl) {
22
29
  var mateAccessRoute = /^\/api\/admin\/projects\/([^/]+)\/(visibility|owner|users|access)$/.exec(fullUrl);
23
30
  if (mateAccessRoute) {
@@ -31,6 +38,25 @@ function attachAdmin(ctx) {
31
38
  }
32
39
 
33
40
 
41
+ // Project content authority belongs to the valid owner, never the role.
42
+ if (mateAccessRoute && users.isMultiUser()) {
43
+ var actor = getMultiUserFromReq(req);
44
+ var governing = onGetProjectAccess && onGetProjectAccess(mateAccessRoute[1]);
45
+ var validOwner = governing && governing.ownerId && users.findUserById(governing.ownerId);
46
+ var owns = actor && validOwner && actor.id === validOwner.id;
47
+ if (req.method !== "GET" && !owns) {
48
+ res.writeHead(403, { "Content-Type": "application/json" });
49
+ res.end(JSON.stringify({ error: "Only the valid project owner can change access." }));
50
+ return true;
51
+ }
52
+ if (req.method === "GET" && !owns && actor && actor.role === "admin") {
53
+ var overview = require("./project-admin-overview").metadataFor(actor, target && target.getStatus(), governing, users);
54
+ res.writeHead(overview ? 200 : 404, { "Content-Type": "application/json", "Cache-Control": "no-store" });
55
+ res.end(JSON.stringify(overview || { error: "Project not found" }));
56
+ return true;
57
+ }
58
+ }
59
+
34
60
  // --- Admin API endpoints (multi-user mode only) ---
35
61
 
36
62
  // List all users (admin only)
@@ -83,6 +109,16 @@ function attachAdmin(ctx) {
83
109
  // Look up the user before deletion to get linuxUser for deactivation
84
110
  var targetUser = users.findUserById(targetUserId);
85
111
  var targetLinuxUser = targetUser ? targetUser.linuxUser : null;
112
+ var owned = false;
113
+ projects.forEach(function (project, slug) {
114
+ var access = onGetProjectAccess && onGetProjectAccess(slug);
115
+ if (access && !access.isMate && access.ownerId === targetUserId) owned = true;
116
+ });
117
+ if (owned) {
118
+ res.writeHead(409, { "Content-Type": "application/json" });
119
+ res.end(JSON.stringify({ error: "The owner must transfer ordinary projects before account deletion." }));
120
+ return true;
121
+ }
86
122
  var result = users.removeUser(targetUserId);
87
123
  if (result.error) {
88
124
  res.writeHead(404, { "Content-Type": "application/json" });
@@ -118,6 +154,7 @@ function attachAdmin(ctx) {
118
154
  req.on("end", function () {
119
155
  try {
120
156
  var data = JSON.parse(body);
157
+ verifyCurrentOwner(req, mateAccessRoute);
121
158
  if (!data.username || typeof data.username !== "string" || data.username.trim().length < 1) {
122
159
  res.writeHead(400, { "Content-Type": "application/json" });
123
160
  res.end('{"error":"Username is required"}');
@@ -182,6 +219,11 @@ function attachAdmin(ctx) {
182
219
  res.end('{"error":"User not found"}');
183
220
  return true;
184
221
  }
222
+ if (mu.id !== targetUserId) {
223
+ res.writeHead(403, { "Content-Type": "application/json" });
224
+ res.end(JSON.stringify({ error: "Use account-owner credential recovery. Administrators cannot impersonate another account." }));
225
+ return true;
226
+ }
185
227
  var newPin = users.generatePin();
186
228
  var pinResult = users.updateUserPin(targetUserId, newPin);
187
229
  if (pinResult.error) {
@@ -222,15 +264,10 @@ function attachAdmin(ctx) {
222
264
  req.on("data", function(chunk) { body += chunk; });
223
265
  req.on("end", function() {
224
266
  try {
225
- var parsed = JSON.parse(body);
226
- var result = users.updateLinuxUser(targetUserId, parsed.linuxUser || null);
227
- if (result.error) {
228
- res.writeHead(400, { "Content-Type": "application/json" });
229
- res.end(JSON.stringify({ error: result.error }));
230
- } else {
231
- res.writeHead(200, { "Content-Type": "application/json" });
232
- res.end('{"ok":true}');
233
- }
267
+ JSON.parse(body);
268
+ res.writeHead(403, { "Content-Type": "application/json" });
269
+ res.end(JSON.stringify({ error: "OS identity mapping requires server-side verified provisioning." }));
270
+ return;
234
271
  } catch (e) {
235
272
  res.writeHead(400, { "Content-Type": "application/json" });
236
273
  res.end('{"error":"Invalid request body"}');
@@ -363,6 +400,7 @@ function attachAdmin(ctx) {
363
400
  req.on("end", function () {
364
401
  try {
365
402
  var data = JSON.parse(body);
403
+ verifyCurrentOwner(req, mateAccessRoute);
366
404
  if (!data.email || !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(data.email)) {
367
405
  res.writeHead(400, { "Content-Type": "application/json" });
368
406
  res.end('{"error":"Valid email is required"}');
@@ -429,6 +467,7 @@ function attachAdmin(ctx) {
429
467
  req.on("end", function () {
430
468
  try {
431
469
  var data = JSON.parse(body);
470
+ verifyCurrentOwner(req, mateAccessRoute);
432
471
  // Allow clearing SMTP config by sending empty fields
433
472
  if (!data.host && !data.user && !data.pass && !data.from) {
434
473
  smtp.saveSmtpConfig(null);
@@ -484,6 +523,7 @@ function attachAdmin(ctx) {
484
523
  req.on("end", function () {
485
524
  try {
486
525
  var data = JSON.parse(body);
526
+ verifyCurrentOwner(req, mateAccessRoute);
487
527
  // Use provided config or fall back to saved
488
528
  var existingCfg = smtp.getSmtpConfig();
489
529
  var pass = data.pass;
@@ -548,6 +588,7 @@ function attachAdmin(ctx) {
548
588
  req.on("end", function () {
549
589
  try {
550
590
  var data = JSON.parse(body);
591
+ verifyCurrentOwner(req, mateAccessRoute);
551
592
  if (data.visibility !== "public" && data.visibility !== "private") {
552
593
  res.writeHead(400, { "Content-Type": "application/json" });
553
594
  res.end('{"error":"Visibility must be public or private"}');
@@ -582,9 +623,10 @@ function attachAdmin(ctx) {
582
623
  return true;
583
624
  }
584
625
  var mu = getMultiUserFromReq(req);
585
- if (!mu || mu.role !== "admin") {
626
+ var ownerAccess = onGetProjectAccess && onGetProjectAccess(fullUrl.split("/")[4]);
627
+ if (!mu || !ownerAccess || mu.id !== ownerAccess.ownerId) {
586
628
  res.writeHead(403, { "Content-Type": "application/json" });
587
- res.end('{"error":"Admin access required"}');
629
+ res.end('{"error":"Project owner access required"}');
588
630
  return true;
589
631
  }
590
632
  var projSlug = fullUrl.split("/")[4];
@@ -598,17 +640,21 @@ function attachAdmin(ctx) {
598
640
  req.on("end", function () {
599
641
  try {
600
642
  var data = JSON.parse(body);
643
+ verifyCurrentOwner(req, mateAccessRoute);
601
644
  var targetCtx = projects.get(projSlug);
602
645
  if (!targetCtx) {
603
646
  res.writeHead(404, { "Content-Type": "application/json" });
604
647
  res.end('{"error":"Project not found"}');
605
648
  return;
606
649
  }
607
- var ownerId = data.userId || null;
650
+ var ownerId = data.userId;
651
+ if (typeof ownerId !== "string" || !users.findUserById(ownerId)) throw new Error("A valid owner is required");
652
+ var currentAccess = onGetProjectAccess && onGetProjectAccess(projSlug);
653
+ if (!currentAccess || currentAccess.ownerId !== mu.id) throw new Error("Project owner access required");
654
+ if (!onProjectOwnerChanged) throw new Error("Ownership persistence is unavailable");
655
+ var saved = onProjectOwnerChanged(projSlug, ownerId);
656
+ if (saved && saved.error) throw new Error(saved.error);
608
657
  targetCtx.setProjectOwner(ownerId);
609
- if (onProjectOwnerChanged) {
610
- onProjectOwnerChanged(projSlug, ownerId);
611
- }
612
658
  // Broadcast to project clients
613
659
  var ownerName = null;
614
660
  if (ownerId) {
@@ -653,7 +699,8 @@ function attachAdmin(ctx) {
653
699
  req.on("end", function () {
654
700
  try {
655
701
  var data = JSON.parse(body);
656
- if (!Array.isArray(data.allowedUsers)) {
702
+ verifyCurrentOwner(req, mateAccessRoute);
703
+ if (!Array.isArray(data.allowedUsers) || data.allowedUsers.some(function (id) { return typeof id !== "string" || !users.findUserById(id); })) {
657
704
  res.writeHead(400, { "Content-Type": "application/json" });
658
705
  res.end('{"error":"allowedUsers must be an array"}');
659
706
  return;
@@ -34,6 +34,18 @@ function attachAppearanceSettings(ctx) {
34
34
  fs.renameSync(defaultPath + '.tmp', defaultPath);
35
35
  }
36
36
  }
37
+ function getInitialAppearance(req) {
38
+ var user = identity(req);
39
+ if (!user) return null;
40
+ var saved = read(user);
41
+ return {
42
+ configured: saved.configured,
43
+ theme: saved.theme,
44
+ mode: saved.mode,
45
+ lightTheme: saved.lightTheme || null,
46
+ darkTheme: saved.darkTheme || null
47
+ };
48
+ }
37
49
  function handleRequest(req, res, url) {
38
50
  if (url !== '/api/user/appearance') return false;
39
51
  var user = identity(req);
@@ -66,6 +78,6 @@ function attachAppearanceSettings(ctx) {
66
78
  });
67
79
  return true;
68
80
  }
69
- return { handleRequest: handleRequest };
81
+ return { handleRequest: handleRequest, getInitialAppearance: getInitialAppearance };
70
82
  }
71
83
  module.exports = { attachAppearanceSettings: attachAppearanceSettings };
package/lib/server-dm.js CHANGED
@@ -49,7 +49,7 @@ function attachDm(ctx) {
49
49
  var mateCtx2 = mates.buildMateCtx(userId);
50
50
  if (mates.isMate(mateCtx2, msg.targetUserId)) {
51
51
  var mate = mates.getMate(mateCtx2, msg.targetUserId);
52
- if (!mate) return true;
52
+ if (!mate || mate.lifecycleDeletion) return true;
53
53
  // Ensure mate project is registered (survives server restarts)
54
54
  var mateSlug2 = "mate-" + mate.id;
55
55
  if (!projects.has(mateSlug2)) {
@@ -69,6 +69,7 @@ function attachDm(ctx) {
69
69
  id: mate.id,
70
70
  displayName: mp.displayName || mate.name || "New Mate",
71
71
  username: mate.id,
72
+ avatarCatalogId: mp.avatarCatalogId || "",
72
73
  avatarStyle: mp.avatarStyle || "imprint",
73
74
  avatarSeed: mp.avatarSeed || mate.id,
74
75
  avatarColor: mp.avatarColor || "#5857fc",
@@ -63,14 +63,6 @@ function attachGlobalWs(opts) {
63
63
  if (msg.type === "browse_dir") {
64
64
  var rawPath = (msg.path || "").replace(/^~/, config.REAL_HOME);
65
65
  var absTarget = path.resolve(rawPath);
66
- // Multi-user mode: non-admins can only browse their home directory.
67
- if (osUsers && ws._clayUser && ws._clayUser.role !== "admin") {
68
- var browseHome = ws._clayUser.linuxUser ? "/home/" + ws._clayUser.linuxUser : null;
69
- if (!browseHome || (absTarget !== browseHome && (absTarget + "/").indexOf(browseHome + "/") !== 0)) {
70
- sendTo(ws, { type: "browse_dir_result", path: msg.path, entries: [], error: "Access restricted to your home directory" });
71
- return;
72
- }
73
- }
74
66
  var parentDir, prefix;
75
67
  try {
76
68
  var stat = fs.statSync(absTarget);
@@ -87,12 +79,17 @@ function attachGlobalWs(opts) {
87
79
  parentDir = path.dirname(absTarget);
88
80
  prefix = path.basename(absTarget).toLowerCase();
89
81
  }
82
+ if (opts.canUsePath && !opts.canUsePath(ws._clayUser && ws._clayUser.id, parentDir)) {
83
+ sendTo(ws, { type: "browse_dir_result", entries: [], error: "Directory access is not permitted" });
84
+ return;
85
+ }
90
86
  try {
91
87
  var dirItems = fs.readdirSync(parentDir, { withFileTypes: true });
92
88
  var dirEntries = [];
93
89
  for (var di = 0; di < dirItems.length; di++) {
94
90
  var d = dirItems[di];
95
91
  if (!d.isDirectory()) continue;
92
+ if (opts.canUsePath && !opts.canUsePath(ws._clayUser && ws._clayUser.id, path.join(parentDir, d.name))) continue;
96
93
  if (d.name.charAt(0) === ".") continue;
97
94
  if (IGNORED_DIRS.has(d.name)) continue;
98
95
  if (prefix && !d.name.toLowerCase().startsWith(prefix)) continue;
@@ -110,16 +107,9 @@ function attachGlobalWs(opts) {
110
107
  if (msg.type === "add_project") {
111
108
  var addPath = (msg.path || "").replace(/^~/, config.REAL_HOME);
112
109
  var addAbs = path.resolve(addPath);
113
- if (osUsers && ws._clayUser && ws._clayUser.role !== "admin") {
114
- if (!ws._clayUser.linuxUser) {
115
- sendTo(ws, { type: "add_project_result", ok: false, error: "No Linux user assigned" });
116
- return;
117
- }
118
- var userHome = "/home/" + ws._clayUser.linuxUser;
119
- if (addAbs !== userHome && (addAbs + "/").indexOf(userHome + "/") !== 0) {
120
- sendTo(ws, { type: "add_project_result", ok: false, error: "Path not allowed. You can only add directories under " + userHome });
121
- return;
122
- }
110
+ if (opts.canUsePath && !opts.canUsePath(ws._clayUser && ws._clayUser.id, addAbs)) {
111
+ sendTo(ws, { type: "add_project_result", ok: false, error: "Directory access is not permitted" });
112
+ return;
123
113
  }
124
114
  try {
125
115
  var addStat = fs.statSync(addAbs);
@@ -133,7 +123,7 @@ function attachGlobalWs(opts) {
133
123
  }
134
124
  if (typeof onAddProject === "function") {
135
125
  var result = onAddProject(addAbs, ws._clayUser);
136
- sendTo(ws, { type: "add_project_result", ok: result.ok, slug: result.slug, error: result.error, existing: result.existing });
126
+ sendTo(ws, Object.assign({}, result, { type: "add_project_result" }));
137
127
  } else {
138
128
  sendTo(ws, { type: "add_project_result", ok: false, error: "Not supported" });
139
129
  }
@@ -159,7 +149,7 @@ function attachGlobalWs(opts) {
159
149
  }
160
150
  if (typeof onCreateProject === "function") {
161
151
  var createResult = onCreateProject(createName, ws._clayUser);
162
- sendTo(ws, { type: "add_project_result", ok: createResult.ok, slug: createResult.slug, error: createResult.error });
152
+ sendTo(ws, Object.assign({}, createResult, { type: "add_project_result" }));
163
153
  } else {
164
154
  sendTo(ws, { type: "add_project_result", ok: false, error: "Not supported" });
165
155
  }
@@ -3,7 +3,7 @@
3
3
  function homeDebateQuestions(input) {
4
4
  var source = input && Array.isArray(input.questions) && input.questions[0] ? input.questions[0] : null;
5
5
  if (!source) return [];
6
- var question = { header: source.header || "", question: source.question || "", multiSelect: false, options: [] };
6
+ var question = { header: source.header || "", question: source.question || "", multiSelect: false, allowOther: source.allowOther !== false, options: [] };
7
7
  var options = Array.isArray(source.options) ? source.options.slice(0, 6) : [];
8
8
  for (var i = 0; i < options.length; i++) question.options.push({ label: options[i].label || "Option", description: options[i].description || "" });
9
9
  return [question];
@@ -36,6 +36,7 @@ function findDebateTurn(messages, event) {
36
36
  function debateIdentity(source, fallback) {
37
37
  var prior = fallback || {};
38
38
  return {
39
+ avatarCatalogId: source.avatarCatalogId || prior.avatarCatalogId || "",
39
40
  avatarStyle: source.avatarStyle || prior.avatarStyle || "imprint",
40
41
  avatarSeed: source.avatarSeed || prior.avatarSeed || source.mateId || "mate",
41
42
  avatarColor: source.avatarColor || prior.avatarColor || "",
@@ -112,7 +113,7 @@ function applyAssignmentHistoryEvent(messages, event) {
112
113
  messages.push({ role: "assignment", assignment: assignment });
113
114
  }
114
115
 
115
- function historyToHomeChat(history, debateSetupMode, mateCreationMode) {
116
+ function historyToHomeChat(history, debateSetupMode, mateCreationMode, pendingAskUser) {
116
117
  var messages = [];
117
118
  var pending = "";
118
119
  var interviewMode = debateSetupMode === true || mateCreationMode === true;
@@ -124,6 +125,12 @@ function historyToHomeChat(history, debateSetupMode, mateCreationMode) {
124
125
  for (var i = 0; i < history.length; i++) {
125
126
  var event = history[i];
126
127
  if (!event) continue;
128
+ if (event.type === 'tool_executing' && event.name === 'AskUserQuestion' && ['mcp-import', 'mate-schedule', 'mcp_fallback', 'product-control'].indexOf(event.source) >= 0) {
129
+ flushAssistant();
130
+ var activeConsent = pendingAskUser === undefined || !!(pendingAskUser && pendingAskUser[event.id]);
131
+ messages.push({ role: 'question', flow: event.source === 'mcp-import' ? 'mcp_import' : 'mate_question', toolId: event.id, questions: homeDebateQuestions(event.input), status: activeConsent ? 'pending' : 'expired', error: activeConsent ? '' : 'This question is no longer active. Ask for a new review.' });
132
+ continue;
133
+ }
127
134
  if (event.type === "project_assignment_proposal" || event.type === "project_assignment_status") {
128
135
  flushAssistant();
129
136
  applyAssignmentHistoryEvent(messages, event);
@@ -185,7 +192,7 @@ function historyToHomeChat(history, debateSetupMode, mateCreationMode) {
185
192
  } else if (interviewMode && event.type === "tool_executing" && event.name === "AskUserQuestion" && event.id && event.input) {
186
193
  flushAssistant();
187
194
  messages.push({ role: "question", flow: mateCreationMode === true ? "mate_creation" : "debate", toolId: event.id, questions: homeDebateQuestions(event.input), status: "pending", error: "" });
188
- } else if ((event.type === "ask_user_answered" || event.type === "home_debate_question_expired" || event.type === "home_mate_creation_question_expired") && event.toolId) {
195
+ } else if ((event.type === "ask_user_answered" || event.type === "home_debate_question_expired" || event.type === "home_mate_creation_question_expired" || event.type === "mcp_import_consent_expired" || event.type === "mate_schedule_question_expired") && event.toolId) {
189
196
  for (var k = messages.length - 1; k >= 0; k--) {
190
197
  if (messages[k].role === "question" && messages[k].toolId === event.toolId) {
191
198
  messages[k].status = event.type === "ask_user_answered" ? "answered" : "expired";
@@ -273,7 +280,7 @@ function transformEvent(event, mateId, session, requestId, stableSessionId) {
273
280
  }
274
281
  if (session && session.homeDebatePlanning === true && HOME_DEBATE_EVENT_TYPES[event.type]) {
275
282
  var live = { type: "home_debate_event", eventType: event.type };
276
- var fields = ["turnId", "decisionId", "decision", "toolName", "command", "commandTruncated", "topic", "format", "moderatorId", "moderatorName", "panelists", "mateName", "role", "round", "activity", "delta", "text", "reason", "action", "avatarStyle", "avatarSeed", "avatarColor", "avatarCustom"];
283
+ var fields = ["turnId", "decisionId", "decision", "toolName", "command", "commandTruncated", "topic", "format", "moderatorId", "moderatorName", "panelists", "mateName", "role", "round", "activity", "delta", "text", "reason", "action", "avatarCatalogId", "avatarStyle", "avatarSeed", "avatarColor", "avatarCustom"];
277
284
  for (var fi = 0; fi < fields.length; fi++) if (Object.prototype.hasOwnProperty.call(event, fields[fi])) live[fields[fi]] = event[fields[fi]];
278
285
  if (event.mateId) live.speakerMateId = event.mateId;
279
286
  return Object.assign(live, metadata);
@@ -298,6 +305,13 @@ function transformEvent(event, mateId, session, requestId, stableSessionId) {
298
305
  if (event.type === "debate_proposal_resolved" && event.proposalId) return Object.assign({ type: "home_debate_proposal_resolved", proposalId: event.proposalId, action: event.action || "cancel", error: event.error || "" }, metadata);
299
306
  if (session && session.mateCreationMode === true && event.type === "mate_creation_proposal" && event.proposal) return Object.assign({ type: "home_mate_creation_proposal", proposal: event.proposal }, metadata);
300
307
  if (session && session.mateCreationMode === true && event.type === "mate_creation_proposal_resolved" && event.proposalId) return Object.assign({ type: "home_mate_creation_proposal_resolved", proposalId: event.proposalId, action: event.action || "cancel", createdMateId: event.mateId || null, mateName: event.mateName || null, error: event.error || "" }, metadata);
308
+ if (!(session && (session.debateSetupMode === true || session.mateCreationMode === true)) && event.type === 'tool_executing' && event.name === 'AskUserQuestion' && ['mate-schedule', 'mcp_fallback', 'product-control'].indexOf(event.source) >= 0) return Object.assign({ type: 'home_mate_question', toolId: event.id, questions: homeDebateQuestions(event.input) }, metadata);
309
+ if (session && session.debateSetupMode !== true && session.mateCreationMode !== true && event.type === 'ask_user_answered' && event.toolId && (session.history || []).some(function (item) { return item.id === event.toolId && ['mate-schedule', 'mcp_fallback', 'product-control'].indexOf(item.source) >= 0; })) return Object.assign({ type: 'home_mate_question_resolved', toolId: event.toolId, status: 'answered', answers: event.answers }, metadata);
310
+ if (event.type === 'tool_executing' && event.name === 'AskUserQuestion' && event.source === 'mcp-import') return Object.assign({ type: 'home_mcp_import_question', toolId: event.id, questions: homeDebateQuestions(event.input) }, metadata);
311
+ if (event.type === 'product_control_question_expired') return Object.assign({ type: 'home_mate_question_resolved', toolId: event.toolId, status: 'expired', error: event.error }, metadata);
312
+ if (event.type === 'mate_schedule_question_expired') return Object.assign({ type: 'home_mate_question_resolved', toolId: event.toolId, status: 'expired', error: event.error }, metadata);
313
+ if (event.type === 'mcp_import_consent_expired') return Object.assign({ type: 'home_mcp_import_question_resolved', toolId: event.toolId, status: 'expired', error: event.error }, metadata);
314
+ if (session && event.type === 'ask_user_answered' && event.toolId && (session.history || []).some(function (item) { return item.id === event.toolId && item.source === 'mcp-import'; })) return Object.assign({ type: 'home_mcp_import_question_resolved', toolId: event.toolId, status: 'answered', answers: event.answers }, metadata);
301
315
  if (session && session.debateSetupMode === true && event.type === "tool_executing" && event.name === "AskUserQuestion" && event.id && event.input) {
302
316
  return Object.assign({ type: "home_debate_question", toolId: event.id, questions: homeDebateQuestions(event.input) }, metadata);
303
317
  }
@@ -31,7 +31,7 @@ function attachHomeChat(deps) {
31
31
  }
32
32
  if (!mateId && allMates[i].builtinKey === "clay") mate = allMates[i];
33
33
  }
34
- if (!mate) return null;
34
+ if (!mate || mate.lifecycleDeletion) return null;
35
35
  var slug = "mate-" + mate.id;
36
36
  if (!projects.has(slug)) {
37
37
  if (!ensureRegistered) return null;
@@ -184,6 +184,12 @@ function attachHomeChat(deps) {
184
184
  }
185
185
  });
186
186
  if (!unsubscribe) return;
187
+ var unregisterConsent = null;
188
+ if (openedSession && typeof found.ctx.registerMcpConsentViewer === 'function') {
189
+ try { unregisterConsent = found.ctx.registerMcpConsentViewer(ws, openedSession); } catch (error) {}
190
+ }
191
+ var unsubscribeEvents = unsubscribe;
192
+ unsubscribe = function () { if (unregisterConsent) unregisterConsent(); unsubscribeEvents(); };
187
193
  ws[tapKey] = {
188
194
  unsubscribe: unsubscribe,
189
195
  sessionId: sessionId,
@@ -238,7 +244,7 @@ function attachHomeChat(deps) {
238
244
  requestId: requestId || null,
239
245
  model: session.model || null,
240
246
  vendor: session.vendor || null,
241
- messages: historyToHomeChat(session.history || [], session.debateSetupMode === true, session.mateCreationMode === true),
247
+ messages: historyToHomeChat(session.history || [], session.debateSetupMode === true, session.mateCreationMode === true, session.pendingAskUser),
242
248
  isProcessing: session.isProcessing === true,
243
249
  };
244
250
  if (session.homeClayEntryMode === "search") {
@@ -293,6 +299,7 @@ function attachHomeChat(deps) {
293
299
  homeModels: homeModels, isActorLive: isActorLive, persistActiveSession: persistActiveSession,
294
300
  });
295
301
  var homeMateCreation = attachHomeMateCreation({ findMateProject: findMateProject, resolveHomeSession: resolveHomeSession, sessionReference: sessionReference, setupTap: setupTap, sendHistory: sendHistory, sendSessionList: sendSessionList, sendError: sendError, sendModelError: sendModelError, homeModels: homeModels, isActorLive: isActorLive, persistActiveSession: persistActiveSession });
302
+ var homeMcpConsent = require('./server-home-mcp-consent').attachHomeMcpConsent({ findMateProject: findMateProject, resolveHomeSession: resolveHomeSession, messageTap: messageTap, isActorLive: isActorLive, sendError: sendError });
296
303
  var homeClayEntry = attachHomeClayEntry({ findMateProject: findMateProject, ownsSession: ownsSession, sessionReference: sessionReference, setupSearchTap: setupSearchTap, sendHistory: sendHistory, sendSessionList: sendSessionList, sendError: sendError, sendModelError: sendModelError, homeModels: homeModels, isActorLive: isActorLive });
297
304
 
298
305
  async function openExactSession(ws, found, userId, selected, msg) {
@@ -397,20 +404,8 @@ function attachHomeChat(deps) {
397
404
  historyError.sessionId = sessionReference(activeSession);
398
405
  throw historyError;
399
406
  }
400
- sessionManager.sendAndRecord(activeSession, { type: "user_message", text: text });
401
407
  try {
402
- if (!activeSession.isProcessing) {
403
- activeSession.isProcessing = true;
404
- activeSession.sentToolResults = {};
405
- if (typeof options.onRecorded === "function") options.onRecorded(activeSession);
406
- if (!activeSession.queryInstance && (!activeSession.worker || activeSession.messageQueue !== "worker")) {
407
- sdk.startQuery(activeSession, options.queryText || text, null, null);
408
- } else {
409
- sdk.pushMessage(activeSession, options.queryText || text, null);
410
- }
411
- } else {
412
- sdk.pushMessage(activeSession, options.queryText || text, null);
413
- }
408
+ await require('./mate-message-delivery').deliver({ sm: sessionManager, sdk: sdk }, activeSession, text, options);
414
409
  } catch (error) {
415
410
  error.sessionId = sessionReference(activeSession);
416
411
  throw error;
@@ -449,7 +444,7 @@ function attachHomeChat(deps) {
449
444
  }
450
445
  return true;
451
446
  }
452
- if (msg.type !== "issue_reference_resolve" && msg.type !== "home_debates_list" && msg.type !== "home_clay_ask" && msg.type !== "home_clay_session_resolve" && msg.type !== "home_clay_log_resolve" && msg.type !== "home_mate_present" && msg.type !== "home_mate_open" && msg.type !== "home_mate_sessions_list" && msg.type !== "home_mate_session_open" && msg.type !== "home_mate_send" && msg.type !== "home_mate_new_session" && msg.type !== "home_mate_debate_plan" && msg.type !== "home_debate_proposal_response" && msg.type !== "home_debate_question_response" && msg.type !== "home_debate_control" && msg.type !== "home_mate_creation_plan" && msg.type !== "home_mate_creation_question_response" && msg.type !== "home_mate_creation_proposal_response" && msg.type !== "home_mate_close" && msg.type !== "home_mate_memory_list" && msg.type !== "home_mate_knowledge_list" && msg.type !== "home_mate_models_get" && msg.type !== "home_mate_model_set") {
447
+ if (msg.type !== "issue_reference_resolve" && msg.type !== "home_debates_list" && msg.type !== "home_clay_ask" && msg.type !== "home_clay_session_resolve" && msg.type !== "home_clay_log_resolve" && msg.type !== "home_mate_present" && msg.type !== "home_mate_open" && msg.type !== "home_mate_sessions_list" && msg.type !== "home_mate_session_open" && msg.type !== "home_mate_send" && msg.type !== "home_mate_new_session" && msg.type !== "home_mate_debate_plan" && msg.type !== "home_debate_proposal_response" && msg.type !== "home_mcp_import_response" && msg.type !== "home_mate_question_response" && msg.type !== "home_debate_question_response" && msg.type !== "home_debate_control" && msg.type !== "home_mate_creation_plan" && msg.type !== "home_mate_creation_question_response" && msg.type !== "home_mate_creation_proposal_response" && msg.type !== "home_mate_close" && msg.type !== "home_mate_memory_list" && msg.type !== "home_mate_knowledge_list" && msg.type !== "home_mate_models_get" && msg.type !== "home_mate_model_set") {
453
448
  return false;
454
449
  }
455
450
 
@@ -474,6 +469,7 @@ function attachHomeChat(deps) {
474
469
  return true;
475
470
  }
476
471
 
472
+ if (msg.type === "home_mcp_import_response" || msg.type === "home_mate_question_response") { homeMcpConsent.respond(ws, userId, msg); return true; }
477
473
  if (msg.type === "issue_reference_resolve") { resolveIssueLink(ws, msg); return true; }
478
474
  if (homeDebates.handle(ws, userId, msg)) return true;
479
475
  if (msg.type === "home_clay_ask") { homeClayEntry.start(ws, userId, msg); return true; }
@@ -18,6 +18,7 @@ var INITIATION_PROMPT = [
18
18
  "Before the user answers again, your first and only action must be the next most useful AskUserQuestion. Before presenting it, do not use Bash, Read, Glob, Grep, workspace history, general knowledge, or any other inspection tool, and do not add a preface.",
19
19
  "When that tool returns the user's answer, continue the interview normally. Inspect only bounded context that is directly relevant to what the user supplied, and do not bulk-read knowledge files.",
20
20
  "Then explore the working relationship, recurring activities and context, communication preferences, autonomy, boundaries, and escalation behavior. Use grounded choices only when helpful. Every three to five exchanges, briefly summarize what you understand inside the next question and let the user correct it.",
21
+ "If the user explicitly asks the future Mate to send a scheduled message, collect the message and exact local date/time, timezone, and repetition only through one focused structured question at a time. Treat it as optional schedule intent: never create or register a job during this interview; include it only in propose_mate after reviewing it with the user.",
21
22
  "Choose the Mate's name last. Do not write files and do not create or update a Mate directly.",
22
23
  "When the identity is complete, end only by calling propose_mate with a concise public bio, structured preferences, and a complete first-person identityMarkdown. Do not emit a normal assistant preface or conclusion.",
23
24
  ].join("\n");
@@ -0,0 +1,14 @@
1
+ // Home transcript responses retain the project's server-owned consent request.
2
+ function attachHomeMcpConsent(ctx) {
3
+ function respond(ws, userId, msg) {
4
+ var found = ctx.findMateProject(userId, msg.mateId || null, false);
5
+ var session = found && ctx.resolveHomeSession(found, userId, msg.sessionId);
6
+ var tap = ctx.messageTap(ws, msg), pending = session && session.pendingAskUser && session.pendingAskUser[msg.toolId];
7
+ if (!ctx.isActorLive(ws, userId) || !found || !session || !tap || tap.mateSlug !== found.slug || tap.sessionId !== session.localId || (tap.requestId || null) !== (msg.requestId || null) || !pending || !pending.request || ['mcp-import', 'mate-schedule', 'mcp_fallback', 'product-control'].indexOf(pending.request.source) < 0) {
8
+ ctx.sendError(ws, found && found.mate.id, 'This owner question is unavailable or expired.', msg.requestId || null, msg.sessionId || null, 'question_expired', { toolId: msg.toolId }); return;
9
+ }
10
+ if (!found.ctx.handleHomeAskUserResponse({ type: 'ask_user_response', toolId: msg.toolId, answers: msg.answers || {} }, session, ws)) ctx.sendError(ws, found.mate.id, 'This question could not be submitted.', msg.requestId || null, msg.sessionId, 'question_refused', { toolId: msg.toolId });
11
+ }
12
+ return { respond: respond };
13
+ }
14
+ module.exports = { attachHomeMcpConsent: attachHomeMcpConsent };