clay-server 5.2.0-beta.4 → 5.2.0-beta.6

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 (301) hide show
  1. package/lib/account-auth-socket.js +1 -0
  2. package/lib/builtin-mates.js +17 -14
  3. package/lib/canonical-clay-record-authority.js +15 -0
  4. package/lib/canonical-clay-scheduling-authority.js +19 -0
  5. package/lib/claude-hook-installer.js +1 -239
  6. package/lib/claude-jsonl-watcher.js +1 -204
  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-skill-install.js +1 -33
  23. package/lib/clay-workspace-controls.js +83 -0
  24. package/lib/clay-workspace-input.js +17 -0
  25. package/lib/clay-workspace-receipts.js +26 -0
  26. package/lib/cli-sessions.js +1 -300
  27. package/lib/codex-defaults.js +1 -28
  28. package/lib/context-usage.js +3 -2
  29. package/lib/daemon-project-access.js +11 -0
  30. package/lib/daemon-project-lifecycle-callbacks.js +15 -0
  31. package/lib/daemon-project-lifecycle.js +262 -0
  32. package/lib/daemon-projects.js +1 -1
  33. package/lib/daemon.js +78 -276
  34. package/lib/default-ai-runtime.js +4 -8
  35. package/lib/email-mcp-server.js +1 -1
  36. package/lib/issues-service.js +29 -15
  37. package/lib/kiro-defaults.js +1 -22
  38. package/lib/knowledge-db-import.js +5 -4
  39. package/lib/knowledge-db-service.js +9 -6
  40. package/lib/knowledge-document-index.js +2 -1
  41. package/lib/knowledge-product-operations.js +46 -0
  42. package/lib/mate-creation-mcp-server.js +2 -0
  43. package/lib/mate-knowledge-canonical-access.js +10 -0
  44. package/lib/mate-knowledge-service.js +7 -4
  45. package/lib/mate-lifecycle-service.js +94 -0
  46. package/lib/mate-message-delivery.js +43 -0
  47. package/lib/mate-ready-creation.js +9 -1
  48. package/lib/mate-schedule-input.js +29 -0
  49. package/lib/mate-schedule-lifecycle.js +151 -0
  50. package/lib/mate-scheduling.js +103 -0
  51. package/lib/mates.js +4 -3
  52. package/lib/mcp-proxy.js +2 -32
  53. package/lib/mcp-query-bridge.js +1 -1
  54. package/lib/mcp-source-adapters.js +1 -91
  55. package/lib/mcp-source-context.js +1 -42
  56. package/lib/mcp-source-discovery.js +1 -169
  57. package/lib/mcp-source-plugins.js +1 -68
  58. package/lib/mcp-source-restrictions.js +1 -47
  59. package/lib/mcp-tool-policy.js +1 -26
  60. package/lib/notes-checklist.js +50 -0
  61. package/lib/notes.js +49 -3
  62. package/lib/os-user-diagnostics.js +2 -4
  63. package/lib/os-users.js +24 -70
  64. package/lib/project-admin-overview.js +51 -0
  65. package/lib/project-ask-user.js +4 -2
  66. package/lib/project-connection.js +8 -7
  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-http.js +4 -4
  73. package/lib/project-knowledge.js +32 -18
  74. package/lib/project-lifecycle-operations.js +98 -0
  75. package/lib/project-lifecycle-process-witness.js +32 -0
  76. package/lib/project-lifecycle-runtime.js +50 -0
  77. package/lib/project-linux-access.js +22 -0
  78. package/lib/project-logs-canonical-clay.js +27 -0
  79. package/lib/project-logs-clay-query.js +16 -0
  80. package/lib/project-logs-schema.js +4 -2
  81. package/lib/project-logs-service.js +8 -7
  82. package/lib/project-logs.js +1 -0
  83. package/lib/project-loop.js +1 -0
  84. package/lib/project-mate-chat-control.js +55 -0
  85. package/lib/project-mate-creation-proposal.js +18 -3
  86. package/lib/project-mcp.js +2 -2
  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-pair-usage.js +12 -13
  92. package/lib/project-pending-message-queue.js +4 -1
  93. package/lib/project-product-control.js +87 -0
  94. package/lib/project-product-session-queue.js +46 -0
  95. package/lib/project-push-access.js +28 -0
  96. package/lib/project-scheduled-message-queue.js +30 -0
  97. package/lib/project-scheduled-messages.js +63 -64
  98. package/lib/project-scheduled-task-control.js +83 -0
  99. package/lib/project-scheduled-tasks.js +13 -2
  100. package/lib/project-scheduling-access.js +10 -0
  101. package/lib/project-session-actions.js +65 -0
  102. package/lib/project-session-delete.js +8 -1
  103. package/lib/project-session-github.js +47 -12
  104. package/lib/project-session-history-actions.js +167 -0
  105. package/lib/project-session-pair.js +1 -1
  106. package/lib/project-session-runtime-actions.js +64 -0
  107. package/lib/project-session-spawn.js +2 -3
  108. package/lib/project-session-vendor.js +55 -0
  109. package/lib/project-sessions.js +82 -267
  110. package/lib/project-user-message.js +31 -1
  111. package/lib/project-vendor-login.js +6 -36
  112. package/lib/project-workspace-operations.js +126 -0
  113. package/lib/project-workspace-query.js +9 -2
  114. package/lib/project.js +95 -3
  115. package/lib/public/app.js +1 -0
  116. package/lib/public/css/clay-tool-activity.css +51 -0
  117. package/lib/public/css/home-mate-creation.css +5 -0
  118. package/lib/public/css/messages.css +6 -2
  119. package/lib/public/css/overlays.css +4 -2
  120. package/lib/public/css/project-admin-overview.css +13 -0
  121. package/lib/public/css/session-folders.css +11 -0
  122. package/lib/public/css/session-github.css +3 -0
  123. package/lib/public/css/sticky-notes.css +61 -1
  124. package/lib/public/index.html +1 -1
  125. package/lib/public/modules/admin-settings-projects.js +6 -2
  126. package/lib/public/modules/app-connection.js +8 -1
  127. package/lib/public/modules/app-favicon.js +11 -2
  128. package/lib/public/modules/app-header.js +4 -2
  129. package/lib/public/modules/app-loop-ui.js +2 -0
  130. package/lib/public/modules/app-message-router.js +1 -1
  131. package/lib/public/modules/app-messages.js +54 -8
  132. package/lib/public/modules/app-notifications.js +1 -2
  133. package/lib/public/modules/app-panels.js +61 -123
  134. package/lib/public/modules/app-projects.js +5 -0
  135. package/lib/public/modules/app-rate-limit.js +7 -27
  136. package/lib/public/modules/chat-render-runtime.js +3 -0
  137. package/lib/public/modules/clay-owner-navigation.js +27 -0
  138. package/lib/public/modules/clay-tool-activity.js +140 -0
  139. package/lib/public/modules/clay-tool-presentation.js +338 -0
  140. package/lib/public/modules/command-palette.js +1 -1
  141. package/lib/public/modules/default-ai.js +11 -3
  142. package/lib/public/modules/home-debate-planning.js +5 -5
  143. package/lib/public/modules/home-mate-chat.js +8 -6
  144. package/lib/public/modules/home-mate-creation.js +73 -1
  145. package/lib/public/modules/mcp-connection-view.js +1 -1
  146. package/lib/public/modules/permission-control.js +5 -4
  147. package/lib/public/modules/personal-settings-dialog.js +7 -1
  148. package/lib/public/modules/project-access-recovery.js +17 -0
  149. package/lib/public/modules/project-admin-overview.js +21 -0
  150. package/lib/public/modules/scheduler.js +2 -0
  151. package/lib/public/modules/session-create-form.js +10 -10
  152. package/lib/public/modules/session-folder-layout.js +1 -1
  153. package/lib/public/modules/session-folder-view-menu.js +9 -0
  154. package/lib/public/modules/session-github.js +65 -8
  155. package/lib/public/modules/settings-defaults.js +27 -28
  156. package/lib/public/modules/sidebar-session-folders-ui.js +1 -1
  157. package/lib/public/modules/sticky-note-markdown.js +72 -6
  158. package/lib/public/modules/sticky-notes-actions.js +30 -0
  159. package/lib/public/modules/sticky-notes-browser.js +9 -2
  160. package/lib/public/modules/sticky-notes-card.js +23 -7
  161. package/lib/public/modules/sticky-notes-checklist.js +71 -0
  162. package/lib/public/modules/sticky-notes-editor.js +108 -44
  163. package/lib/public/modules/sticky-notes-shared.js +50 -2
  164. package/lib/public/modules/sticky-notes-state.js +66 -0
  165. package/lib/public/modules/sticky-notes.js +34 -51
  166. package/lib/public/modules/thinking-lifecycle.js +33 -3
  167. package/lib/public/modules/thinking-view.js +23 -1
  168. package/lib/public/modules/tools.js +36 -7
  169. package/lib/public/modules/tui-grab.js +1 -2
  170. package/lib/public/modules/user-settings-appearance.js +10 -3
  171. package/lib/public/modules/user-settings-editors.js +1 -1
  172. package/lib/public/modules/user-settings-layout.js +11 -2
  173. package/lib/public/modules/user-settings-preferences.js +6 -2
  174. package/lib/public/modules/user-settings-profile.js +12 -2
  175. package/lib/public/modules/user-settings.js +3 -2
  176. package/lib/public/modules/vendor-login-browser.js +5 -3
  177. package/lib/public/modules/vendor-login.js +2 -2
  178. package/lib/public/modules/vendor-priority.js +15 -1
  179. package/lib/public/muse-avatar.svg +4 -0
  180. package/lib/public/style.css +2 -0
  181. package/lib/safe-bash-commands.js +3 -45
  182. package/lib/scheduled-task-records.js +3 -1
  183. package/lib/scheduler.js +2 -0
  184. package/lib/sdk-bridge.js +334 -403
  185. package/lib/sdk-foreground-admission.js +16 -0
  186. package/lib/sdk-mcp-descriptors.js +2 -37
  187. package/lib/sdk-message-processor.js +52 -17
  188. package/lib/sdk-quota-snapshot.js +1 -1
  189. package/lib/sdk-response-recovery.js +1 -0
  190. package/lib/sdk-worker.js +2 -677
  191. package/lib/server-admin.js +63 -16
  192. package/lib/server-dm.js +1 -1
  193. package/lib/server-global-ws.js +10 -20
  194. package/lib/server-home-chat-events.js +7 -3
  195. package/lib/server-home-chat.js +4 -16
  196. package/lib/server-home-mate-creation.js +1 -0
  197. package/lib/server-home-mcp-consent.js +3 -3
  198. package/lib/server-mates.js +25 -67
  199. package/lib/server-owner-ui.js +77 -0
  200. package/lib/server-path-access.js +38 -0
  201. package/lib/server-project-removal.js +22 -0
  202. package/lib/server.js +100 -30
  203. package/lib/session-config-transition.js +45 -0
  204. package/lib/session-folders.js +7 -2
  205. package/lib/session-fork-history.js +21 -0
  206. package/lib/session-github.js +9 -2
  207. package/lib/session-hygiene.js +3 -35
  208. package/lib/session-permission-mode.js +4 -3
  209. package/lib/session-skill-scope.js +11 -0
  210. package/lib/session-visibility.js +5 -2
  211. package/lib/sessions.js +27 -89
  212. package/lib/terminal-manager.js +52 -0
  213. package/lib/tool-llm.js +1 -1
  214. package/lib/tui-transcript-index.js +1 -132
  215. package/lib/users-permissions.js +2 -8
  216. package/lib/users.js +4 -0
  217. package/lib/vendor-login-browser.js +1 -11
  218. package/lib/workspace-query-access.js +4 -1
  219. package/lib/ws-schema.js +8 -2
  220. package/lib/yoke/acp-agent-profiles.js +5 -3
  221. package/lib/yoke/adapters/antigravity.js +3 -14
  222. package/lib/yoke/adapters/claude-query-contract.js +9 -0
  223. package/lib/yoke/adapters/claude.js +54 -50
  224. package/lib/yoke/adapters/codex-query-contract.js +24 -0
  225. package/lib/yoke/adapters/codex.js +53 -111
  226. package/lib/yoke/adapters/kiro.js +3 -1
  227. package/lib/yoke/adapters/muse-query-contract.js +7 -0
  228. package/lib/yoke/adapters/muse.js +122 -0
  229. package/lib/yoke/auth-events.js +17 -0
  230. package/lib/yoke/claude-hook-installer.js +267 -0
  231. package/lib/yoke/claude-jsonl-watcher.js +204 -0
  232. package/lib/yoke/claude-maintenance.js +55 -0
  233. package/lib/yoke/claude-permission-patterns.js +53 -0
  234. package/lib/yoke/claude-response-recovery.js +155 -0
  235. package/lib/yoke/claude-transcript-hygiene.js +34 -0
  236. package/lib/yoke/claude-worker-startup.js +28 -0
  237. package/lib/yoke/clay-skill-install.js +33 -0
  238. package/lib/yoke/cli-sessions.js +300 -0
  239. package/lib/yoke/codex-app-server.js +7 -12
  240. package/lib/yoke/codex-idle-recovery.js +81 -1
  241. package/lib/yoke/codex-recovery-admission.js +164 -0
  242. package/lib/yoke/configuration.js +13 -0
  243. package/lib/yoke/discovery.js +70 -0
  244. package/lib/yoke/errors.js +8 -0
  245. package/lib/yoke/host-environment.js +7 -0
  246. package/lib/yoke/index.js +9 -51
  247. package/lib/yoke/interface.js +116 -0
  248. package/lib/yoke/kiro-acp-server.js +5 -5
  249. package/lib/yoke/legacy-sessions.js +89 -0
  250. package/lib/yoke/login-protocol.js +25 -0
  251. package/lib/yoke/maintenance.js +12 -0
  252. package/lib/yoke/mcp-proxy.js +32 -0
  253. package/lib/yoke/mcp-source-adapters.js +91 -0
  254. package/lib/yoke/mcp-source-context.js +42 -0
  255. package/lib/yoke/mcp-source-discovery.js +169 -0
  256. package/lib/yoke/mcp-source-plugins.js +68 -0
  257. package/lib/yoke/mcp-source-restrictions.js +47 -0
  258. package/lib/yoke/mcp-tool-policy-normalize.js +28 -0
  259. package/lib/yoke/model-preferences.js +6 -0
  260. package/lib/yoke/muse-artifacts.js +36 -0
  261. package/lib/yoke/muse-controls.js +50 -0
  262. package/lib/yoke/muse-input.js +64 -0
  263. package/lib/yoke/muse-items.js +157 -0
  264. package/lib/yoke/muse-policy.js +52 -0
  265. package/lib/yoke/muse-query.js +428 -0
  266. package/lib/yoke/muse-runtime.js +211 -0
  267. package/lib/yoke/muse-services.js +79 -0
  268. package/lib/yoke/muse-skills.js +79 -0
  269. package/lib/yoke/muse-turns.js +71 -0
  270. package/lib/yoke/options/acp.js +2 -0
  271. package/lib/yoke/options/antigravity.js +2 -0
  272. package/lib/yoke/options/claude.js +21 -0
  273. package/lib/yoke/options/codex-defaults.js +29 -0
  274. package/lib/yoke/options/codex-settings.js +21 -0
  275. package/lib/yoke/options/codex.js +6 -0
  276. package/lib/yoke/options/kiro-defaults.js +22 -0
  277. package/lib/yoke/options/kiro.js +5 -0
  278. package/lib/yoke/options/muse.js +5 -0
  279. package/lib/yoke/provider-contract.js +5 -0
  280. package/lib/yoke/providers/antigravity-executable.js +8 -0
  281. package/lib/yoke/providers/claude-executable.js +40 -0
  282. package/lib/yoke/providers/claude-model-descriptions.js +14 -0
  283. package/lib/yoke/providers/claude-process.js +97 -0
  284. package/lib/yoke/providers/claude-provision.js +75 -0
  285. package/lib/yoke/providers/codex-errors.js +8 -0
  286. package/lib/yoke/providers/muse-executable.js +17 -0
  287. package/lib/yoke/provisioning.js +6 -0
  288. package/lib/yoke/query-options.js +10 -0
  289. package/lib/yoke/response-recovery.js +41 -0
  290. package/lib/yoke/sdk-mcp-descriptors.js +37 -0
  291. package/lib/yoke/sdk-server-factory.js +7 -0
  292. package/lib/yoke/session-history.js +8 -0
  293. package/lib/yoke/session-tool-transport.js +34 -13
  294. package/lib/yoke/settings.js +23 -0
  295. package/lib/yoke/source-locations.js +4 -0
  296. package/lib/yoke/title-query.js +18 -7
  297. package/lib/yoke/tui-transcript-index.js +132 -0
  298. package/lib/yoke/usage.js +48 -0
  299. package/lib/yoke/user-input.js +13 -0
  300. package/lib/yoke/vendor-registry.js +81 -1
  301. package/package.json +3 -2
@@ -118,6 +118,12 @@ function attachUserMessage(ctx) {
118
118
  requestId: msg.requestId || null, ok: !!ok, error: error || null });
119
119
  }
120
120
 
121
+ function noteUpdateResult(ws, msg, ok, note, error) {
122
+ if (!msg.requestId) return;
123
+ sendTo(ws, { type: "note_update_result", projectSlug: slug, noteId: msg.id || null,
124
+ requestId: msg.requestId, ok: !!ok, note: note || null, error: error || null });
125
+ }
126
+
121
127
  // --------------- Main handler ---------------
122
128
 
123
129
  function handleUserMessage(ws, msg, forcedSession, internal) {
@@ -155,7 +161,10 @@ function attachUserMessage(ctx) {
155
161
  }
156
162
 
157
163
  if (msg.type === "note_update") {
158
- if (!msg.id) return true;
164
+ if (!msg.id) {
165
+ noteUpdateResult(ws, msg, false, null, "Note id is required");
166
+ return true;
167
+ }
159
168
  // A legacy `hidden` update is a lifecycle transition, so the actor is
160
169
  // derived from the bound socket rather than taken from the message.
161
170
  var noteChanges = msg;
@@ -166,10 +175,24 @@ function attachUserMessage(ctx) {
166
175
  if (updated) {
167
176
  send({ type: "note_updated", note: updated });
168
177
  if (msg.text !== undefined || msg.hidden !== undefined) syncNotesKnowledge();
178
+ noteUpdateResult(ws, msg, true, updated, null);
179
+ } else {
180
+ noteUpdateResult(ws, msg, false, null, "Note update could not be saved");
169
181
  }
170
182
  return true;
171
183
  }
172
184
 
185
+ if (msg.type === "note_close" && msg.humanClose === true) {
186
+ if (!canHumanManageNotes(ws)) { noteUpdateResult(ws, msg, false, null, "You are not authorized to close notes in this project."); return true; }
187
+ var result = nm.closeHuman(msg.id, msg, notesLifecycle.userActor(ws && ws._clayUser));
188
+ if (!result) { noteUpdateResult(ws, msg, false, null, "The note changed or could not be saved. Review it and retry."); return true; }
189
+ if (result.removed) send({ type: "note_deleted", id: result.removed.id });
190
+ else send({ type: "note_updated", note: result.note });
191
+ noteUpdateResult(ws, msg, true, result.note || null, null);
192
+ syncNotesKnowledge();
193
+ return true;
194
+ }
195
+
173
196
  // Completing a note closes it. `note_delete` is the retired spelling and is
174
197
  // deliberately routed to the same non-destructive close, so an older client
175
198
  // still on the wire cannot erase a record.
@@ -443,6 +466,10 @@ function attachUserMessage(ctx) {
443
466
  finishPendingEarly(false, "Pending messages cannot be delivered to a Driver-operated Split Worker");
444
467
  return true;
445
468
  }
469
+ if (!pendingInternal && ctx.resumeLifecycleProject) {
470
+ try { ctx.resumeLifecycleProject(ws); }
471
+ catch (error) { sendTo(ws, { type: "error", text: error.message }); return true; }
472
+ }
446
473
  if (!pendingInternal && session._driverContinuationLease && pendingMessageQueue) {
447
474
  var continuationQueued = pendingMessageQueue.admit(session, msg, ws && ws._clayUser);
448
475
  if (continuationQueued.ok) {
@@ -461,6 +488,7 @@ function attachUserMessage(ctx) {
461
488
  if (queuedResult.ok) {
462
489
  messageDelivery.acknowledge(ws, deliveryReceipt, null, session);
463
490
  sendTo(ws, { type: "message_queued", clientMessageId: msg.clientMessageId || null, sessionId: session.localId, projectSlug: slug, state: "pending", paused: pendingMessageQueue.isPaused(session), revision: pendingMessageQueue.getRevision() });
491
+ if (require('./sdk-foreground-admission').available(session)) setImmediate(function () { consumePendingMessage(session); });
464
492
  } else {
465
493
  sendTo(ws, { type: "error", text: queuedResult.error || "Unable to queue message" });
466
494
  }
@@ -719,6 +747,7 @@ function attachUserMessage(ctx) {
719
747
 
720
748
  function pendingBlockReason() {
721
749
  if (!pendingInternal) return null;
750
+ if (ctx.isLifecycleExecutionPaused && ctx.isLifecycleExecutionPaused()) return "Pending message dispatch paused";
722
751
  if (!pendingMessageQueue.isClaimed(session, pendingInternal.item.id)) return "Pending message was cancelled";
723
752
  if (pendingMessageQueue.isPaused(session) || session._pendingMessageDrainPaused || session.taskStopRequested) return "Pending message dispatch paused";
724
753
  if (typeof ctx.authorizePendingDispatch !== "function" || !ctx.authorizePendingDispatch(session, ws && ws._clayUser, pendingInternal.item)) return "Pending message authorization changed";
@@ -941,6 +970,7 @@ function attachUserMessage(ctx) {
941
970
  }
942
971
 
943
972
  function consumePendingMessage(session) {
973
+ if (ctx.isLifecycleExecutionPaused && ctx.isLifecycleExecutionPaused()) return false;
944
974
  if (!pendingMessageQueue) return false;
945
975
  var consumed = pendingMessageQueue.consumeOne(session, function (item, complete) {
946
976
  var actor = item.actorId && usersModule.findUserById(item.actorId);
@@ -25,37 +25,11 @@ var yoke = require("./yoke");
25
25
  // Login CLIs print a success line and then hand the shell back, so the PTY
26
26
  // itself does not exit. Watch the output stream instead. Deliberately narrow:
27
27
  // "Successfully logged out" and MCP-server logins must not match.
28
- var LOGIN_SUCCESS_PATTERNS = [
29
- /successfully logged in(?![ \t]+to[ \t]+mcp)/i,
30
- /logged in successfully/i,
31
- /login successful/i,
32
- /successfully authenticated/i,
33
- /authentication successful/i,
34
- /signed in successfully/i,
35
- /signed in with your [a-z ]+ account/i,
36
- /you(?:'re| are) (?:now )?(?:logged|signed) in/i,
37
- ];
38
-
39
- // Rolling window kept per flow so a success line split across PTY chunks is
40
- // still matched, without buffering the whole login transcript.
41
28
  var OUTPUT_TAIL_MAX = 4096;
42
-
43
- // Grace period between detecting success and killing the terminal, so the user
44
- // actually sees the confirmation line before the modal closes.
45
29
  var CLOSE_AFTER_SUCCESS_MS = 2000;
46
-
47
- var ANSI_PATTERN = /\x1b\[[0-9;?]*[ -\/]*[@-~]|\x1b\][^\x07\x1b]*(?:\x07|\x1b\\)|\r/g;
48
-
49
- function stripAnsi(text) {
50
- return String(text).replace(ANSI_PATTERN, "");
51
- }
52
-
53
- function looksLikeLoginSuccess(text) {
54
- for (var i = 0; i < LOGIN_SUCCESS_PATTERNS.length; i++) {
55
- if (LOGIN_SUCCESS_PATTERNS[i].test(text)) return true;
56
- }
57
- return false;
58
- }
30
+ var loginProtocol = require("./yoke/login-protocol");
31
+ var stripAnsi = loginProtocol.stripAnsi;
32
+ var looksLikeLoginSuccess = loginProtocol.successful;
59
33
 
60
34
  /**
61
35
  * ctx fields:
@@ -96,11 +70,7 @@ function attachVendorLogin(ctx) {
96
70
  cancel: function (vendor) { closeFlowTerminal(discardFlow(vendor)); broadcastState(); },
97
71
  });
98
72
 
99
- function loginCommandFor(vendor) {
100
- if (vendor === "claude") return "claude auth login";
101
- var info = vendorRegistry.getVendorInfo(vendor);
102
- return (info && info.loginCommand) || "claude login";
103
- }
73
+ function loginCommandFor(vendor) { return loginProtocol.command(vendor, false); }
104
74
 
105
75
  function vendorDisplayName(vendor) {
106
76
  var info = vendorRegistry.getVendorInfo(vendor);
@@ -284,8 +254,8 @@ function attachVendorLogin(ctx) {
284
254
  var key = flowKey(ws, vendor);
285
255
  var identity = resolveLoginIdentity(ws, msg);
286
256
  var command = loginCommandFor(vendor);
287
- var browserMode = msg.mode === "browser" && (vendor === "claude" || vendor === "codex");
288
- if (browserMode) command = vendor === "codex" ? "codex login" : "claude auth login";
257
+ var browserMode = msg.mode === "browser" && !!(vendorRegistry.getVendorInfo(vendor) || {}).browserLoginCommand;
258
+ if (browserMode) command = loginProtocol.command(vendor, true);
289
259
  var flow = {
290
260
  vendor: vendor,
291
261
  key: key,
@@ -0,0 +1,126 @@
1
+ var fs = require('fs'), path = require('path'), crypto = require('crypto');
2
+ var git = require('./git-cli');
3
+ function digest(value) { return crypto.createHash('sha256').update(typeof value === 'string' || Buffer.isBuffer(value) ? value : JSON.stringify(value)).digest('hex'); }
4
+ function inode(file) { var s = fs.statSync(file); return [s.dev, s.ino, s.mode, s.uid, s.gid]; }
5
+ function attachWorkspaceOperations(ctx) {
6
+ var busy = false;
7
+ function authorize(principal, domain) {
8
+ var record = ctx.getAccess(), user = principal.userId && ctx.users.findUserById(principal.userId);
9
+ if (ctx.isMate || !record || record.lifecycleExecutionPaused || ctx.users.isMultiUser() && (!user || record.ownerId !== user.id || !ctx.users.canAccessProject(user.id, record))) throw new Error('Live selected-project ownership or access is unavailable.');
10
+ if (!ctx.users.isMultiUser() && record.ownerId && record.ownerId !== principal.singleUserOwnerId) throw new Error('Selected-project ownership changed.');
11
+ if (user && ctx.users.getEffectivePermissions(user, ctx.osUsers)[domain === 'terminal' ? 'terminal' : 'fileBrowser'] !== true) throw new Error('The account does not have permission for this product operation.');
12
+ var identity = null;
13
+ if (ctx.osUsers) {
14
+ if (!user || !user.linuxUser) throw new Error('A live mapped OS identity is required.');
15
+ identity = require('./os-users').resolveOsUserInfo(user.linuxUser);
16
+ if (!identity || !Number.isInteger(identity.uid) || identity.uid < 0 || !Number.isInteger(identity.gid) || identity.gid < 0 || identity.user !== user.linuxUser) throw new Error('The mapped OS identity is unavailable.');
17
+ }
18
+ return { identity: identity, projectBound: true, ownerId: user ? user.id : null };
19
+ }
20
+ function rootIdentity(principal, domain) {
21
+ var scope = authorize(principal, domain), root = fs.realpathSync(ctx.cwd);
22
+ return digest([root, inode(root), scope.ownerId, scope.identity && [scope.identity.user, scope.identity.uid, scope.identity.gid, scope.identity.home]]);
23
+ }
24
+ function bounded(requested, missing) {
25
+ if (typeof requested !== 'string' || !requested || path.isAbsolute(requested) || /[\u0000-\u001f\u007f]/.test(requested) || requested.split(/[\\/]/).some(function (part) { return part === '..' || ['.git', '.clay', '.claude', '.codex', '.envrc'].indexOf(part) >= 0; })) throw new Error('Use an in-project relative path outside managed settings and Git metadata.');
26
+ var root = fs.realpathSync(ctx.cwd), resolved = root;
27
+ var parts = requested.split('/').filter(function (part) { return part && part !== '.'; });
28
+ for (var i = 0; i < parts.length; i++) {
29
+ resolved = path.join(resolved, parts[i]);
30
+ var stat;
31
+ try { stat = fs.lstatSync(resolved); } catch (error) { if (missing && error.code === 'ENOENT' && i === parts.length - 1) return resolved; throw error; }
32
+ if (stat.isSymbolicLink() || stat.isFile() && stat.nlink !== 1) throw new Error('Symlinks and multiply linked files are unavailable in canonical project controls.');
33
+ }
34
+ if (path.relative(root, resolved).split(path.sep).indexOf('..') >= 0) throw new Error('The canonical project boundary changed.');
35
+ return resolved;
36
+ }
37
+ function repository(scope) {
38
+ var marker = fs.lstatSync(path.join(ctx.cwd, '.git'));
39
+ if (!marker.isDirectory() || marker.isSymbolicLink()) throw new Error('Canonical Git controls require a direct local .git directory; linked metadata and submodules are unavailable.');
40
+ var status = git.getStatus(ctx.cwd, scope.identity);
41
+ if (!status.isRepository || fs.realpathSync(status.root) !== fs.realpathSync(ctx.cwd)) throw new Error('Select the exact registered repository root, rather than a repository subdirectory.');
42
+ if (fs.realpathSync(status.gitDir) !== fs.realpathSync(path.join(ctx.cwd, '.git'))) throw new Error('The Git metadata directory escaped the selected project.');
43
+ return status;
44
+ }
45
+ function gitProjection(status) { return { isRepository: true, branch: status.branch, oid: status.oid, upstream: status.upstream, ahead: status.ahead, behind: status.behind, detached: status.detached, files: status.files.slice(0, 200), truncated: status.files.length > 200, dirty: status.dirty }; }
46
+ function terminal(scope, id) {
47
+ var item = ctx.tm.inspect(id);
48
+ var identity = scope.identity ? JSON.stringify([scope.identity.user, scope.identity.uid, scope.identity.gid, scope.identity.home]) : null;
49
+ if (!item || item.kind !== 'shell' || item.ownerId !== scope.ownerId || item.osIdentity !== identity) throw new Error('Select an exact same-owner shell terminal. Coding-agent, login and foreign terminals are excluded.');
50
+ return item;
51
+ }
52
+ function read(principal, op) {
53
+ var scope = authorize(principal, op.domain), before = rootIdentity(principal, op.domain), result;
54
+ if (op.domain === 'files') {
55
+ bounded(op.path || '.');
56
+ if (op.action === 'list') { var entries = ctx.files().list(scope, op.path || '.'); result = { entries: entries.slice(0, 200), truncated: entries.length > 200 }; }
57
+ else { result = ctx.files().read(scope, op.path); if (result.content && result.content.length > 65536) result = { error: 'Canonical text reads are limited to 64 KB.', size: result.size }; }
58
+ } else if (op.domain === 'git') {
59
+ var status = repository(scope);
60
+ if (op.action === 'status') result = gitProjection(status);
61
+ else { var working = bounded(op.path, true); if (fs.existsSync(working)) ctx.files().read(scope, op.path); result = git.getFileDiff(ctx.cwd, op.path, scope.identity); if ((result.oldContent.length + result.newContent.length) > 65536) throw new Error('Canonical diff reads are limited to 64 KB.'); }
62
+ } else if (op.action === 'list') result = { terminals: ctx.tm.list().filter(function (item) { try { terminal(scope, item.id); return true; } catch (error) { return false; } }).map(function (item) { return ctx.tm.inspect(item.id); }) };
63
+ else { var item = terminal(scope, op.id); result = Object.assign({}, item, ctx.tm.getScrollback(item.id)); }
64
+ if (rootIdentity(principal, op.domain) !== before) throw new Error('Project or OS identity changed during the read.');
65
+ return result;
66
+ }
67
+ function snapshot(principal, op) {
68
+ var scope = authorize(principal, op.domain);
69
+ if (op.domain === 'files') { bounded(op.path); return ctx.files().snapshot(scope, op.path); }
70
+ if (op.domain === 'git') {
71
+ var status = repository(scope);
72
+ if (!op.paths.length || !op.paths.every(function (name) { return status.files.some(function (item) { return item.path === name; }); })) throw new Error('Select exact currently changed Git paths.');
73
+ var entries = op.paths.map(function (name) {
74
+ var working = bounded(name, true);
75
+ if (fs.existsSync(working) && !fs.lstatSync(working).isFile()) throw new Error('Canonical staging requires individual regular file paths; recursive directory staging is unavailable.');
76
+ if (fs.existsSync(working)) ctx.files().read(scope, name);
77
+ var item = status.files.find(function (file) { return file.path === name; });
78
+ if (item.originalPath) bounded(item.originalPath, true);
79
+ var diff = git.getFileDiff(ctx.cwd, name, scope.identity);
80
+ if (diff.binary) throw new Error('Canonical staging of binary files is unavailable.');
81
+ return digest(diff);
82
+ });
83
+ var index = path.join(status.gitDir, 'index');
84
+ return { status: gitProjection(status), gitDirectory: [fs.realpathSync(status.gitDir), inode(status.gitDir)], index: fs.existsSync(index) ? digest(fs.readFileSync(index)) : null, files: entries,
85
+ configuration: digest(git.runGitSync(ctx.cwd, ['config', '--null', '--list', '--show-origin'], null, scope.identity)),
86
+ attributes: digest(git.runGitSync(ctx.cwd, ['check-attr', '-z', '--all', '--'].concat(op.paths), null, scope.identity)) };
87
+ }
88
+ return op.action === 'create' ? ctx.tm.list().map(function (item) { return ctx.tm.inspect(item.id); }) : terminal(scope, op.id);
89
+ }
90
+ async function apply(principal, op, expected, revalidate) {
91
+ if (busy) throw new Error('Another project file, Git or terminal change is applying. Inspect again after it settles.');
92
+ busy = true;
93
+ var attempted = false, result;
94
+ try {
95
+ var scope = authorize(principal, op.domain);
96
+ revalidate();
97
+ if (digest(snapshot(principal, op)) !== digest(expected)) throw new Error('The exact product revision changed.');
98
+ if (op.domain === 'files') return ctx.files().write(scope, op.path, op.content, { expected: expected, revalidate: function () { revalidate(); bounded(op.path); } });
99
+ attempted = true;
100
+ if (op.domain === 'git') {
101
+ await git.runAction(ctx.cwd, { action: op.action, paths: op.paths }, scope.identity);
102
+ result = { status: 'completed', runtimeEffect: 'index_updated', persisted: true };
103
+ } else if (op.action === 'create') {
104
+ var created = ctx.tm.create(op.cols, op.rows, scope.identity, null, { kind: 'shell', ownerId: scope.ownerId });
105
+ if (!created) throw new Error('The terminal transport is unavailable or its limit was reached.');
106
+ result = { status: 'completed', runtimeEffect: 'shell_created', terminalId: created.id, terminalIdentity: created.identity, commandSent: false, persisted: false };
107
+ } else if (op.action === 'rename') {
108
+ ctx.tm.rename(op.id, op.title); result = { status: 'completed', runtimeEffect: 'renamed', terminalId: op.id, terminalIdentity: expected.identity, persisted: false };
109
+ } else if (op.action === 'resize') {
110
+ ctx.tm.resizeExact(op.id, op.cols, op.rows, expected.identity, revalidate);
111
+ result = { status: 'completed', runtimeEffect: 'resized', terminalId: op.id, terminalIdentity: expected.identity, persisted: false };
112
+ } else {
113
+ await ctx.tm.closeAndWait(op.id, { identity: expected.identity, revalidate: function () { revalidate(); var live = terminal(authorize(principal, 'terminal'), op.id); if (live.identity !== expected.identity) throw new Error('Terminal identity changed during closure.'); } });
114
+ result = { status: 'completed', runtimeEffect: 'closed', terminalId: op.id, terminalIdentity: expected.identity, exitAcknowledged: true, persisted: false };
115
+ }
116
+ revalidate();
117
+ if (op.domain === 'terminal') ctx.publishTerminals();
118
+ return result;
119
+ } catch (error) {
120
+ error.productControlResult = Object.assign({ persisted: attempted ? 'unknown' : false, runtimeEffect: attempted ? 'unknown' : 'none', retrySafe: false }, result, error.productControlResult || {}, { status: 'failed', error: error.message });
121
+ throw error;
122
+ } finally { busy = false; }
123
+ }
124
+ return { rootIdentity: rootIdentity, read: read, snapshot: snapshot, apply: apply };
125
+ }
126
+ module.exports = { attachWorkspaceOperations: attachWorkspaceOperations };
@@ -1,4 +1,5 @@
1
1
  var workspaceMcp = require("./workspace-query-mcp-server");
2
+ var productTools = require("./clay-product-control-tools");
2
3
  var clayHistoryMcp = require("./clay-history-mcp-server");
3
4
 
4
5
  function attachProjectWorkspaceQuery(ctx) {
@@ -25,7 +26,12 @@ function attachProjectWorkspaceQuery(ctx) {
25
26
  function getToolDefs(session) {
26
27
  var bound = session ? binding(session) : null;
27
28
  if (session && !bound) return [];
28
- return workspaceMcp.getToolDefs(bound, !!(bound && bound.isClay));
29
+ var tools = workspaceMcp.getToolDefs(bound, !!(bound && bound.isClay));
30
+ if (bound && bound.isClay && ctx.controlService) {
31
+ var controls = ctx.controlService.bind({ projectSlug: projectSlug, projectOwnerId: getProjectOwnerId(), isMate: true, mateId: mateId, session: session });
32
+ if (controls) tools = tools.concat(productTools.getToolDefs(controls));
33
+ }
34
+ return tools;
29
35
  }
30
36
 
31
37
  function createMcpServer(adapter, session) {
@@ -34,7 +40,8 @@ function attachProjectWorkspaceQuery(ctx) {
34
40
  if (session && !bound) return null;
35
41
  var projectBound = binding(null);
36
42
  if (!projectBound) return null;
37
- return workspaceMcp.createMcpServer(adapter, bound, projectBound.isClay);
43
+ if (!adapter || typeof adapter.createToolServer !== "function") return null;
44
+ return adapter.createToolServer({ name: "clay-workspace", version: "2.0.0", tools: session ? getToolDefs(session) : workspaceMcp.getToolDefs(null, projectBound.isClay) });
38
45
  }
39
46
 
40
47
  function createHistoryMcpServer(adapter, session) {
package/lib/project.js CHANGED
@@ -46,6 +46,8 @@ var { attachShellCommand } = require("./project-shell-command");
46
46
  var { attachVendorLogin } = require("./project-vendor-login");
47
47
  var { attachConnection } = require("./project-connection");
48
48
  var { attachProjectScheduledMessages } = require("./project-scheduled-messages");
49
+ var { attachMateScheduling } = require("./mate-scheduling");
50
+ var { attachMateScheduleLifecycle } = require("./mate-schedule-lifecycle");
49
51
  var { attachMcp } = require("./project-mcp");
50
52
  var { createSessionSkillCatalog } = require("./yoke/session-skill-catalog");
51
53
  var { attachEmail: attachEmailModule } = require("./project-email");
@@ -229,6 +231,8 @@ function createProjectContext(opts) {
229
231
  var getScheduleCount = opts.getScheduleCount || function () { return 0; };
230
232
  var notifyProcessingChanged = opts.onProcessingChanged || function () {};
231
233
  var _scheduledMessages = null;
234
+ var _mateScheduling = null;
235
+ var _mateLifecycle = null;
232
236
  var _scheduledTasks = null;
233
237
  var _scheduledTaskExecution = null;
234
238
  function onProcessingChanged() {
@@ -819,6 +823,7 @@ function createProjectContext(opts) {
819
823
  });
820
824
  var _workspaceQuery = attachProjectWorkspaceQuery({
821
825
  service: opts.workspaceQueryService || null,
826
+ controlService: opts.clayProductControl || null,
822
827
  sm: sm,
823
828
  projectSlug: slug,
824
829
  getProjectOwnerId: function () { return projectOwnerId; },
@@ -1047,6 +1052,7 @@ function createProjectContext(opts) {
1047
1052
  persistSession: function (session) { return sm.saveSessionFile(session); },
1048
1053
  getVendorModelCatalog: function (ws, vendor) { return _models.getVendorCatalog(ws, vendor); },
1049
1054
  createReadyMate: opts.createReadyMate,
1055
+ isLive: function (session) { return sm.sessions.get(session.localId) === session; },
1050
1056
  });
1051
1057
  var _askUser = attachAskUser({
1052
1058
  record: function (session, event) { sm.sendAndRecord(session, event); },
@@ -1405,6 +1411,7 @@ function createProjectContext(opts) {
1405
1411
 
1406
1412
  // --- SDK bridge ---
1407
1413
  sdk = createSDKBridge({
1414
+ isLifecycleExecutionPaused: function () { return !!(opts.getProjectAccess && opts.getProjectAccess().lifecycleExecutionPaused); },
1408
1415
  cwd: cwd,
1409
1416
  slug: slug,
1410
1417
  sessionManager: sm,
@@ -1480,6 +1487,7 @@ function createProjectContext(opts) {
1480
1487
  _sessionGithub.getSystemPrompt(session),
1481
1488
  _sessionLinear.getSystemPrompt(session),
1482
1489
  _scheduledTasks ? _scheduledTasks.getSystemPrompt(session) : "",
1490
+ _mateScheduling ? _mateScheduling.getSystemPrompt(session) : "",
1483
1491
  _mateKnowledge.getSystemPrompt(session),
1484
1492
  _userKnowledge.getSystemPrompt(session),
1485
1493
  _autonomousRun ? _autonomousRun.getSystemPrompt(session) : "",
@@ -1509,6 +1517,7 @@ function createProjectContext(opts) {
1509
1517
  .concat(opts.userKnowledgeService ? _userKnowledge.getDynamicToolDefs(session) : [])
1510
1518
  .concat(_loopInterview ? _loopInterview.getToolDefs(session) : [])
1511
1519
  .concat(_scheduledTasks ? _scheduledTasks.getToolDefs(session) : [])
1520
+ .concat(_mateScheduling ? _mateScheduling.getToolDefs(session) : [])
1512
1521
  .concat(_scheduledTaskExecution ? _scheduledTaskExecution.getToolDefs(session) : [])
1513
1522
  .concat(_autonomousRun ? _autonomousRun.getToolDefs(session) : []);
1514
1523
  },
@@ -1564,6 +1573,8 @@ function createProjectContext(opts) {
1564
1573
 
1565
1574
  _scheduledMessages = attachProjectScheduledMessages({
1566
1575
  scheduler: opts.durableScheduler,
1576
+ mateLifecycle: function () { return _mateLifecycle; },
1577
+ deferRehydrate: isMate,
1567
1578
  slug: slug,
1568
1579
  isMate: isMate,
1569
1580
  osUsers: osUsers,
@@ -1586,6 +1597,32 @@ function createProjectContext(opts) {
1586
1597
  consumePendingMessage: function (session) { return _userMessage && _userMessage.consumePendingMessage(session); },
1587
1598
  });
1588
1599
 
1600
+ var _schedulingAccess = require('./project-scheduling-access').attachSchedulingAccess({ osUsers: osUsers, getLinuxUser: getLinuxUserForSession, resolveLinuxUser: getOsUserInfoForLinuxUser });
1601
+ _mateLifecycle = attachMateScheduleLifecycle({
1602
+ sm: sm, scheduler: opts.durableScheduler, slug: slug,
1603
+ owner: function (session) { return _mateScheduling.owner(session); },
1604
+ authorize: function (session, control) { return control ? _mateScheduling.authorizeControl(session, control) : _mateScheduling.authorize(session); },
1605
+ authorizeRuntime: function (session) { return _mateScheduling.authorizeLifecycle(session); },
1606
+ schedule: function () { return _scheduledMessages.schedule.apply(null, arguments); },
1607
+ queueItem: function (session, job) { return _scheduledMessages.queueItem(session, job); },
1608
+ cancelJob: function (session, job) {
1609
+ return _scheduledMessages.cancel(session, { id: job.ownerId }, null, "cancelled_by_mate", { jobId: job.id, revision: job.payload.revision });
1610
+ },
1611
+ });
1612
+ _mateScheduling = attachMateScheduling({
1613
+ isMate: isMate, isHostAgent: isHostAgent, projectOwnerId: projectOwnerId, slug: slug, mateId: opts.mateId,
1614
+ sm: sm, users: usersModule, lifecycle: _mateLifecycle,
1615
+ authorizeAccess: function (session, ownerId) { _schedulingAccess.ownerIdentity(session); return _scheduledMessages.authorize(session, ownerId); },
1616
+ userInput: function (session) { return _askUser.createHandler(session); },
1617
+ });
1618
+ if (isMate) {
1619
+ sm.sessions.forEach(function (session) {
1620
+ try { _mateLifecycle.reconcile(session); }
1621
+ catch (error) { console.error("[mate-schedules] Recovery failed:", error.message); }
1622
+ });
1623
+ _scheduledMessages.rehydrate();
1624
+ }
1625
+
1589
1626
  // --- Loop engine (delegated to project-loop.js) ---
1590
1627
  // --- Notification center (global singleton from server.js) ---
1591
1628
  var _notifications = opts.notificationsModule || null;
@@ -1667,6 +1704,7 @@ function createProjectContext(opts) {
1667
1704
  cwd: cwd,
1668
1705
  projectSlug: slug,
1669
1706
  projectTitle: title || project,
1707
+ validateOwnerIdentity: _schedulingAccess.ownerIdentity,
1670
1708
  isMate: isMate,
1671
1709
  sm: sm,
1672
1710
  sdk: sdk,
@@ -1765,8 +1803,8 @@ function createProjectContext(opts) {
1765
1803
  }
1766
1804
 
1767
1805
  // --- Schedule / cancel a message (used by WS handler and auto-continue) ---
1768
- function scheduleMessage(session, text, resetsAt, actorUser, targetWs, attachments, requestId, transportAccountId) {
1769
- return _scheduledMessages ? _scheduledMessages.schedule(session, text, resetsAt, actorUser, targetWs, attachments, requestId, transportAccountId) : null;
1806
+ function scheduleMessage(session, text, resetsAt, actorUser, targetWs, attachments, requestId, transportAccountId, options) {
1807
+ return _scheduledMessages ? _scheduledMessages.schedule(session, text, resetsAt, actorUser, targetWs, attachments, requestId, transportAccountId, options) : null;
1770
1808
  }
1771
1809
 
1772
1810
  function cancelScheduledMessage(session, actorUser, targetWs, correlation) {
@@ -1800,7 +1838,7 @@ function createProjectContext(opts) {
1800
1838
  }
1801
1839
 
1802
1840
  // --- DM messages (delegated to server-level handler) ---
1803
- if (msg.type === "home_debate_question_response" || msg.type === "home_debate_control" || msg.type === "home_mate_creation_question_response" || msg.type === "default_ai_get" || msg.type === "default_ai_catalog_get" || msg.type === "default_ai_set" || msg.type === "default_vendor_get" || msg.type === "default_vendor_set" || msg.type === "context_view_get" || msg.type === "context_view_set" || msg.type === "cursor_sharing_get" || msg.type === "cursor_sharing_set") {
1841
+ if (msg.type === "home_mate_question_response" || msg.type === "clay_ui_ready" || msg.type === "clay_navigation_claim" || msg.type === "clay_navigation_ack" || msg.type === "home_debate_question_response" || msg.type === "home_debate_control" || msg.type === "home_mate_creation_question_response" || msg.type === "default_ai_get" || msg.type === "default_ai_catalog_get" || msg.type === "default_ai_set" || msg.type === "default_vendor_get" || msg.type === "default_vendor_set" || msg.type === "context_view_get" || msg.type === "context_view_set" || msg.type === "cursor_sharing_get" || msg.type === "cursor_sharing_set") {
1804
1842
  if (typeof opts.onDmMessage === "function") opts.onDmMessage(ws, msg, slug);
1805
1843
  return;
1806
1844
  }
@@ -2096,6 +2134,8 @@ function createProjectContext(opts) {
2096
2134
  });
2097
2135
  sm.setSessionHydrator(_sessionSkillCatalog.hydrate);
2098
2136
  var _sessions = attachSessions({
2137
+ adapters: adapters,
2138
+ isPendingQueuePaused: function (session) { return _pendingMessageQueue.isPaused(session); },
2099
2139
  cwd: cwd,
2100
2140
  slug: slug,
2101
2141
  isMate: isMate,
@@ -2169,6 +2209,26 @@ function createProjectContext(opts) {
2169
2209
  _notifications: _notifications,
2170
2210
  _projectLogs: _projectLogs,
2171
2211
  });
2212
+ var _productControl = require("./project-product-control").attachProjectProductControl({
2213
+ sm: sm, users: usersModule, clients: clients, slug: slug, isMate: isMate, osUsers: osUsers,
2214
+ getProjectAccess: opts.getProjectAccess || function () { return { visibility: "public", ownerId: projectOwnerId || null }; },
2215
+ userInput: function (session) { return _askUser.createHandler(session); },
2216
+ actions: _sessions.sessionActions, history: _sessions.sessionHistoryActions, selectModel: function (session, model, options) { return _models.selectSessionModel(session, model, null, options); },
2217
+ isDriverOperatedSession: function (session) { return _sessionPair.workerPermission.isDriverOperated(session); },
2218
+ deletePlan: _sessions.deleteProductControlPlan,
2219
+ queue: _pendingMessageQueue,
2220
+ requestQueueDrain: function (session) { if (_userMessage) _userMessage.consumePendingMessage(session); },
2221
+ vendor: _sessions.sessionVendorActions,
2222
+ runtime: _sessions.sessionRuntimeActions,
2223
+ });
2224
+ var _mateChatControl = require('./project-mate-chat-control').attachMateChatControl({
2225
+ sm: sm, sdk: sdk, isMate: isMate, osUsers: osUsers, getLinuxUser: getLinuxUserForSession,
2226
+ users: usersModule, getAccess: opts.getProjectAccess || function () { return { isMate: isMate, ownerId: projectOwnerId || null, visibility: 'private' }; },
2227
+ actions: _sessions.sessionActions, vendor: _sessions.sessionVendorActions,
2228
+ selectModel: function (session, model, options) { return _models.selectSessionModel(session, model, null, options); },
2229
+ });
2230
+
2231
+
2172
2232
 
2173
2233
  var _models = attachModels({
2174
2234
  cwd: cwd,
@@ -2215,6 +2275,7 @@ function createProjectContext(opts) {
2215
2275
  });
2216
2276
  var _delegatedFollowUp = attachDelegatedFollowUp({
2217
2277
  sm: sm,
2278
+ isPendingQueuePaused: function (session) { return _pendingMessageQueue.isPaused(session); },
2218
2279
  getSdk: function () { return sdk; },
2219
2280
  getLinuxUserForSession: getLinuxUserForSession,
2220
2281
  onProcessingChanged: onProcessingChanged,
@@ -2229,6 +2290,12 @@ function createProjectContext(opts) {
2229
2290
 
2230
2291
  // --- User message handler (delegated to project-user-message.js) ---
2231
2292
  var _userMessage = attachUserMessage({
2293
+ isLifecycleExecutionPaused: function () { return !!(opts.getProjectAccess && opts.getProjectAccess().lifecycleExecutionPaused); },
2294
+ resumeLifecycleProject: function (ws) {
2295
+ if (!opts.getProjectAccess || !opts.getProjectAccess().lifecycleExecutionPaused) return true;
2296
+ if (!clients.has(ws) || !ws._clayUser || !opts.resumeLifecycleProject) throw new Error('The current owner must explicitly resume recovered project execution.');
2297
+ return opts.resumeLifecycleProject(ws._clayUser.id);
2298
+ },
2232
2299
  // Exact live pair state decides whether a session is Driver-operated; the
2233
2300
  // handler never infers the role from the message.
2234
2301
  isDriverOperatedSession: function (session) {
@@ -2351,6 +2418,13 @@ function createProjectContext(opts) {
2351
2418
  FS_MAX_SIZE: FS_VIEWER_MAX_SIZE,
2352
2419
  });
2353
2420
 
2421
+ var _workspaceOperations = require('./project-workspace-operations').attachWorkspaceOperations({
2422
+ cwd: cwd, isMate: isMate, osUsers: osUsers, users: usersModule, tm: tm,
2423
+ getAccess: opts.getProjectAccess || function () { return { ownerId: projectOwnerId || null, visibility: 'private' }; },
2424
+ files: function () { return _filesystem.productOperations; },
2425
+ publishTerminals: function () { send({ type: 'term_list', terminals: tm.list() }); },
2426
+ });
2427
+
2354
2428
  // --- MCP bridge handler for Codex and session-bound Kiro tools ---
2355
2429
  // Provides list_tools and call_tool operations over HTTP for mcp-bridge-server.js.
2356
2430
  // The normal Codex bridge excludes local MCP servers it manages natively;
@@ -2780,6 +2854,7 @@ function createProjectContext(opts) {
2780
2854
  return false;
2781
2855
  }));
2782
2856
  } catch (err) {
2857
+ shutdowns.push(Promise.resolve(false));
2783
2858
  console.error("[project] " + vendor + " shutdown threw for " + slug + ":", err && err.message ? err.message : err);
2784
2859
  }
2785
2860
  });
@@ -2923,8 +2998,24 @@ function createProjectContext(opts) {
2923
2998
  },
2924
2999
  getYouLinuxUser: getLinuxUserForSession,
2925
3000
  getStatus: getStatus,
3001
+ bindMateSchedule: function (session, intent, id) {
3002
+ return _mateLifecycle.create(session, require("./mate-schedule-input").normalize(intent), id);
3003
+ },
3004
+ cancelMateSchedule: function (session, record) { return _mateLifecycle.cancel(session, record); },
2926
3005
  getSessionManager: function () { return sm; },
2927
3006
  createDelegatedSession: _delegatedSession.createDelegatedSession,
3007
+ refreshEnvironmentRuntime: function () { return sdk.refreshEnvironmentRuntime(); },
3008
+ getWorkspaceProductOperations: function () { return isMate ? null : _workspaceOperations; },
3009
+ getMateChatControl: function () { return isMate ? _mateChatControl : null; },
3010
+ getMateScheduleControl: function () { return isMate ? _mateLifecycle : null; },
3011
+ getScheduledTaskControl: function () { return _scheduledTasks.productControl; },
3012
+ notifyIssueUpdate: _issues.notify,
3013
+ notifyLogUpdate: _projectLogs.broadcastUpdate,
3014
+ requestProductControlReview: _productControl.requestProductControlReview,
3015
+ applyProductSessionAction: _productControl.applyProductSessionAction,
3016
+ inspectProductSessionQueue: _productControl.inspectProductSessionQueue,
3017
+ inspectProductSessionRuntime: _sessions.sessionRuntimeActions.inspect,
3018
+ inspectMateLifecycleRuntime: _sessions.sessionRuntimeActions.inspectLifecycle,
2928
3019
  inspectDelegatedFollowUp: _delegatedFollowUp.inspectDelegatedFollowUp,
2929
3020
  dispatchDelegatedFollowUp: _delegatedFollowUp.dispatchDelegatedFollowUp,
2930
3021
  deliverCapsuleTurn: _capsuleTurn.deliverCapsuleTurn,
@@ -2941,6 +3032,7 @@ function createProjectContext(opts) {
2941
3032
  getMemoryState: _memory.readMemoryState,
2942
3033
  listKnowledgeFiles: _knowledge.listKnowledgeFiles,
2943
3034
  getKnowledgeDbService: function () { return _knowledge.databases; },
3035
+ getKnowledgeProductOperations: function () { return _knowledge.productOperations; },
2944
3036
  refreshCapsuleCatalog: _capsuleCatalog.refreshSessions,
2945
3037
  getNotificationsModule: function () { return _notifications; },
2946
3038
  refreshVendorAuth: _vendorLogin.refreshVendorAuth,
package/lib/public/app.js CHANGED
@@ -583,6 +583,7 @@ import { initDebate, handleDebatePreparing, handleDebateStarted, handleDebateRes
583
583
  // rendering
584
584
  currentMsgTs: null,
585
585
  richContextUsage: null,
586
+ contextMeasurement: "unavailable",
586
587
  currentMsgEl: null,
587
588
  currentFullText: "",
588
589
  matePreThinkingEl: null,
@@ -0,0 +1,51 @@
1
+ /* Keep internal activity in the established compact tool disclosure rows. */
2
+ .clay-tool-activity .tool-name {
3
+ flex-shrink: 1;
4
+ min-width: 0;
5
+ overflow: hidden;
6
+ text-overflow: ellipsis;
7
+ white-space: nowrap;
8
+ }
9
+ .clay-tool-activity .tool-desc {
10
+ font-family: var(--font-ui);
11
+ flex: 0 1 auto;
12
+ }
13
+ .clay-tool-activity .tool-status-icon { margin-left: auto; }
14
+ .clay-tool-activity .tool-subtitle { display: none; }
15
+ .tool-header[role="button"]:focus-visible,
16
+ .tool-group-header[role="button"]:focus-visible {
17
+ outline: 2px solid var(--accent);
18
+ outline-offset: 2px;
19
+ }
20
+ .clay-tool-activity[data-activity-phase="running"] .tool-bullet {
21
+ animation: pulse 1.5s ease-in-out infinite;
22
+ }
23
+ .clay-tool-activity[data-activity-phase="pending"] .tool-bullet,
24
+ .tool-group[data-activity-phase="pending"] .tool-group-bullet {
25
+ background: var(--text-muted);
26
+ animation: none;
27
+ }
28
+ .clay-tool-activity[data-activity-phase="error"] .tool-name,
29
+ .clay-tool-activity[data-activity-phase="cancelled"] .tool-name,
30
+ .tool-group[data-activity-phase="error"] .tool-group-label { color: var(--error); }
31
+ .clay-tool-technical-name,
32
+ .clay-tool-evidence-label {
33
+ padding: 8px 14px 0;
34
+ color: var(--text-muted);
35
+ font-size: 12px;
36
+ overflow-wrap: anywhere;
37
+ }
38
+ .clay-tool-technical-name { font-family: "Roboto Mono", monospace; }
39
+ .clay-tool-evidence-label { font-weight: 600; }
40
+ @media (prefers-reduced-motion: reduce) {
41
+ .clay-tool-activity .tool-bullet { animation: none; }
42
+ }
43
+
44
+ @media (max-width: 600px) {
45
+ .clay-tool-activity .tool-name {
46
+ white-space: normal;
47
+ overflow-wrap: anywhere;
48
+ line-height: 1.4;
49
+ }
50
+ .clay-tool-activity .tool-desc { max-width: 35%; }
51
+ }
@@ -32,3 +32,8 @@
32
32
  .home-mate-proposal-facts dd + dt { margin-top: 7px; }
33
33
  .home-mate-proposal-actions button { flex: 1; }
34
34
  }
35
+
36
+ .home-mate-proposal-schedule-details { margin-top: 2px; color: var(--text-dimmer); }
37
+ .home-mate-proposal-schedule-details summary { width: fit-content; padding-block: 3px; cursor: pointer; font-weight: 400; }
38
+ .home-mate-proposal-schedule-details summary:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
39
+ .home-mate-proposal-schedule-details[open] > div { padding-top: 3px; color: var(--text-muted); }
@@ -675,7 +675,8 @@ pre.mermaid-error {
675
675
 
676
676
  .thinking-live .thinking-chevron { display: none; }
677
677
 
678
- .thinking-live .thinking-label {
678
+ .thinking-live .thinking-label,
679
+ .activity-inline .activity-text.activity-state-label {
679
680
  background: linear-gradient(100deg, var(--text-muted) 40%, var(--text-dimmer) 50%, var(--text-muted) 60%);
680
681
  background-size: 300% 100%;
681
682
  background-clip: text;
@@ -690,7 +691,10 @@ pre.mermaid-error {
690
691
  }
691
692
 
692
693
  @media (prefers-reduced-motion: reduce) {
693
- .thinking-live .thinking-label { animation: none; background: none; color: var(--text-muted); }
694
+ .thinking-live .thinking-label,
695
+ .activity-inline .activity-text.activity-state-label {
696
+ animation: none; background: none; color: var(--text-muted); -webkit-text-fill-color: currentColor;
697
+ }
694
698
  }
695
699
 
696
700
  .thinking-header[aria-disabled="true"] { cursor: default; }
@@ -1360,8 +1360,10 @@ button.top-bar-pill.pill-accent:hover { background: color-mix(in srgb, var(--acc
1360
1360
  .connect-wordmark {
1361
1361
  display: block;
1362
1362
  width: clamp(142px, 15vw, 184px);
1363
- height: auto;
1364
- filter: brightness(0);
1363
+ aspect-ratio: 4742 / 1156;
1364
+ background: var(--text-muted);
1365
+ -webkit-mask: url("../clay-studio-wordmark-bold.svg") center / contain no-repeat;
1366
+ mask: url("../clay-studio-wordmark-bold.svg") center / contain no-repeat;
1365
1367
  }
1366
1368
 
1367
1369
  #connect-overlay-msg {