clay-server 3.8.1 → 4.0.0-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (182) hide show
  1. package/lib/ask-user-mcp-server.js +5 -119
  2. package/lib/builtin-mates.js +6 -6
  3. package/lib/claude-hook-installer.js +15 -0
  4. package/lib/clay-history-mcp-server.js +62 -297
  5. package/lib/daemon.js +1 -1
  6. package/lib/debate-model-selection.js +119 -0
  7. package/lib/home-debate-tool-policy.js +164 -0
  8. package/lib/home-response-notifications.js +26 -0
  9. package/lib/mate-creation-mcp-server.js +55 -0
  10. package/lib/mate-ready-creation.js +95 -0
  11. package/lib/mates.js +2 -0
  12. package/lib/notes.js +2 -1
  13. package/lib/project-ask-user.js +64 -0
  14. package/lib/project-capsule-catalog.js +101 -0
  15. package/lib/project-debate-proposal.js +133 -17
  16. package/lib/project-debate.js +214 -110
  17. package/lib/project-delegated-follow-up.js +84 -0
  18. package/lib/project-delegated-session.js +53 -0
  19. package/lib/project-filesystem.js +7 -9
  20. package/lib/project-knowledge.js +1 -0
  21. package/lib/project-mate-creation-proposal.js +120 -0
  22. package/lib/project-memory.js +14 -7
  23. package/lib/project-models.js +157 -32
  24. package/lib/project-session-notes.js +2 -2
  25. package/lib/project-session-pair.js +8 -8
  26. package/lib/project-sessions.js +66 -51
  27. package/lib/project-worker-proposal.js +25 -25
  28. package/lib/project-workspace-query.js +144 -0
  29. package/lib/project.js +329 -102
  30. package/lib/public/app.js +85 -42
  31. package/lib/public/css/avatar-imprints.css +0 -1
  32. package/lib/public/css/base.css +4 -0
  33. package/lib/public/css/capsule-ui.css +389 -0
  34. package/lib/public/css/command-palette.css +306 -6
  35. package/lib/public/css/home-capsule-library.css +243 -0
  36. package/lib/public/css/home-capsule-source.css +192 -0
  37. package/lib/public/css/home-debate-live.css +146 -0
  38. package/lib/public/css/home-debate-planning.css +125 -0
  39. package/lib/public/css/home-debates-archive.css +281 -0
  40. package/lib/public/css/home-hub.css +896 -405
  41. package/lib/public/css/home-mate-creation.css +34 -0
  42. package/lib/public/css/home-mate-model-picker.css +131 -0
  43. package/lib/public/css/home-mate-settings.css +344 -0
  44. package/lib/public/css/home-session-actions.css +265 -0
  45. package/lib/public/css/home-sidebar.css +667 -0
  46. package/lib/public/css/icon-strip.css +1 -2
  47. package/lib/public/css/mates.css +7 -34
  48. package/lib/public/css/pane.css +1 -1
  49. package/lib/public/css/playbook.css +0 -80
  50. package/lib/public/css/sticky-notes.css +53 -117
  51. package/lib/public/css/tui-attention.css +0 -44
  52. package/lib/public/css/workspace-assignment.css +45 -0
  53. package/lib/public/fonts/source-serif-4/LICENSE.md +93 -0
  54. package/lib/public/fonts/source-serif-4/SourceSerif4Caption-Semibold.ttf.woff2 +0 -0
  55. package/lib/public/index.html +93 -74
  56. package/lib/public/modules/app-connection.js +10 -1
  57. package/lib/public/modules/app-dm.js +43 -160
  58. package/lib/public/modules/app-home-hub.js +328 -585
  59. package/lib/public/modules/app-message-cards.js +242 -0
  60. package/lib/public/modules/app-message-router.js +84 -0
  61. package/lib/public/modules/app-messages.js +78 -20
  62. package/lib/public/modules/app-notifications.js +4 -2
  63. package/lib/public/modules/app-projects.js +9 -1
  64. package/lib/public/modules/app-rendering.js +21 -602
  65. package/lib/public/modules/avatar-imprint.js +11 -8
  66. package/lib/public/modules/chat-bubble-renderer.js +152 -0
  67. package/lib/public/modules/chat-render-runtime.js +198 -0
  68. package/lib/public/modules/command-palette.js +225 -436
  69. package/lib/public/modules/dm-render.js +82 -0
  70. package/lib/public/modules/home-capsule-creation-intent.js +31 -0
  71. package/lib/public/modules/home-capsule-library.js +146 -0
  72. package/lib/public/modules/home-capsule-source.js +383 -0
  73. package/lib/public/modules/home-chat-identity.js +35 -0
  74. package/lib/public/modules/home-chat-scroll.js +77 -0
  75. package/lib/public/modules/home-chat-stream-state.js +34 -0
  76. package/lib/public/modules/home-composer-focus.js +25 -0
  77. package/lib/public/modules/home-conversations-sheet.js +179 -0
  78. package/lib/public/modules/home-debate-activity.js +48 -0
  79. package/lib/public/modules/home-debate-controls.js +295 -0
  80. package/lib/public/modules/home-debate-launch.js +41 -0
  81. package/lib/public/modules/home-debate-live.js +284 -0
  82. package/lib/public/modules/home-debate-models.js +73 -0
  83. package/lib/public/modules/home-debate-planning.js +335 -0
  84. package/lib/public/modules/home-debates-archive.js +279 -0
  85. package/lib/public/modules/home-dock-resize.js +74 -0
  86. package/lib/public/modules/home-dock.js +446 -0
  87. package/lib/public/modules/home-mate-chat.js +496 -0
  88. package/lib/public/modules/home-mate-creation.js +109 -0
  89. package/lib/public/modules/home-mate-model-picker.js +281 -0
  90. package/lib/public/modules/home-mate-selection.js +38 -0
  91. package/lib/public/modules/home-mate-settings-menu.js +131 -0
  92. package/lib/public/modules/home-mate-settings.js +383 -0
  93. package/lib/public/modules/home-session-actions.js +373 -0
  94. package/lib/public/modules/home-session-model-confirmation.js +13 -0
  95. package/lib/public/modules/home-shell.js +9 -0
  96. package/lib/public/modules/home-sidebar-chat-list.js +63 -0
  97. package/lib/public/modules/home-sidebar.js +158 -0
  98. package/lib/public/modules/home-sub-surface.js +19 -0
  99. package/lib/public/modules/home-surface-boot.js +68 -0
  100. package/lib/public/modules/home-surface.js +141 -0
  101. package/lib/public/modules/home-tools.js +271 -0
  102. package/lib/public/modules/markdown.js +50 -2
  103. package/lib/public/modules/mate-management.js +86 -0
  104. package/lib/public/modules/mate-wizard.js +0 -7
  105. package/lib/public/modules/mention.js +17 -19
  106. package/lib/public/modules/profile.js +4 -5
  107. package/lib/public/modules/project-settings.js +34 -28
  108. package/lib/public/modules/project-switcher.js +11 -5
  109. package/lib/public/modules/project-workspace-assignment.js +23 -0
  110. package/lib/public/modules/search-clay-chat.js +402 -0
  111. package/lib/public/modules/server-settings.js +14 -12
  112. package/lib/public/modules/session-actions.js +1 -1
  113. package/lib/public/modules/sidebar-mates.js +56 -552
  114. package/lib/public/modules/sidebar-mobile.js +7 -64
  115. package/lib/public/modules/sidebar-presence.js +37 -0
  116. package/lib/public/modules/split-pair-ui.js +13 -13
  117. package/lib/public/modules/sticky-notes.js +7 -6
  118. package/lib/public/modules/tool-input-composition.js +48 -0
  119. package/lib/public/modules/tool-llm-status.js +167 -0
  120. package/lib/public/modules/tool-renderer-advanced.js +285 -0
  121. package/lib/public/modules/tool-renderer-chart.js +158 -0
  122. package/lib/public/modules/tool-renderer-semantics.js +35 -0
  123. package/lib/public/modules/tool-renderer.js +464 -0
  124. package/lib/public/modules/tool-runtime.js +309 -0
  125. package/lib/public/modules/tool-ui-evaluator.js +97 -0
  126. package/lib/public/modules/tool-ui-tree.js +21 -0
  127. package/lib/public/modules/user-settings.js +16 -23
  128. package/lib/public/modules/worker-proposal.js +2 -2
  129. package/lib/public/modules/workspace-assignment-card.js +149 -0
  130. package/lib/public/modules/workspace-assignment-routing.js +4 -0
  131. package/lib/public/style.css +13 -2
  132. package/lib/runtime-env.js +65 -0
  133. package/lib/sdk-bridge.js +120 -32
  134. package/lib/sdk-message-processor.js +10 -6
  135. package/lib/server-home-capsule-creation.js +36 -0
  136. package/lib/server-home-chat-events.js +287 -0
  137. package/lib/server-home-chat.js +518 -0
  138. package/lib/server-home-clay-entry.js +65 -0
  139. package/lib/server-home-clay-session-links.js +26 -0
  140. package/lib/server-home-debate-planning.js +214 -0
  141. package/lib/server-home-debates.js +137 -0
  142. package/lib/server-home-mate-creation.js +261 -0
  143. package/lib/server-home-models.js +241 -0
  144. package/lib/server-home-preferences.js +61 -0
  145. package/lib/server-mates.js +37 -0
  146. package/lib/server-tools.js +377 -0
  147. package/lib/server.js +111 -59
  148. package/lib/session-pair-mcp-server.js +6 -6
  149. package/lib/sessions.js +18 -3
  150. package/lib/tool-capsule-source.js +142 -0
  151. package/lib/tool-control-mcp-server.js +126 -0
  152. package/lib/tool-llm.js +48 -0
  153. package/lib/tool-storage.js +77 -0
  154. package/lib/tool-ui-spec-advanced.js +82 -0
  155. package/lib/tool-ui-spec.js +261 -0
  156. package/lib/tools-registry.js +276 -0
  157. package/lib/users-home-dock-preferences.js +77 -0
  158. package/lib/users-home-surface-preferences.js +99 -0
  159. package/lib/users-preferences.js +4 -7
  160. package/lib/users.js +12 -0
  161. package/lib/whats-new-content.js +3 -3
  162. package/lib/workspace-assignment-service.js +420 -0
  163. package/lib/workspace-query-mcp-server.js +154 -0
  164. package/lib/workspace-query-service.js +492 -0
  165. package/lib/ws-schema.js +86 -12
  166. package/lib/yoke/adapters/acp.js +2 -1
  167. package/lib/yoke/adapters/antigravity.js +2 -1
  168. package/lib/yoke/adapters/claude-worker.js +11 -4
  169. package/lib/yoke/adapters/claude.js +73 -43
  170. package/lib/yoke/adapters/codex.js +37 -6
  171. package/lib/yoke/adapters/kiro.js +2 -1
  172. package/lib/yoke/claude-user-input.js +38 -0
  173. package/lib/yoke/codex-user-input.js +64 -0
  174. package/lib/yoke/complete-once.js +164 -0
  175. package/lib/yoke/index.js +5 -1
  176. package/lib/yoke/interface.js +19 -0
  177. package/lib/yoke/mcp-bridge-server.js +2 -6
  178. package/lib/yoke/user-input.js +254 -0
  179. package/package.json +2 -1
  180. package/lib/public/css/home-chat.css +0 -380
  181. package/lib/public/modules/home-chat.js +0 -438
  182. package/lib/server-clay-home.js +0 -245
@@ -0,0 +1,84 @@
1
+ function hasEntries(value) {
2
+ return !!(value && Object.keys(value).length > 0);
3
+ }
4
+
5
+ function unsafeReason(session, isPaired) {
6
+ if (!session || session.hidden) return "Target session is unavailable.";
7
+ if (!session.cliSessionId) return "Target session must have a durable session reference.";
8
+ if (session.sessionVisibility !== "private") return "Target session must be private.";
9
+ if (session.isProcessing || session._queryStarting || session._awaitingTurnResult) return "Target session must be idle.";
10
+ if (hasEntries(session.pendingPermissions) || hasEntries(session.pendingAskUser) || hasEntries(session.pendingElicitations) || hasEntries(session.pendingUserDialogs)) return "Target session is waiting for user input.";
11
+ if ((session.mode && session.mode !== "gui") || (session.runtimeMode && session.runtimeMode !== "gui") || session.terminalId != null || session.runtimeTerminalId != null || session.tuiSuspended) return "Terminal sessions cannot receive project follow-ups.";
12
+ if (session.homeDebatePlanning || session.homeDebatePhase || session.debateSetupMode || session.debateState || session._debate) return "Debate sessions cannot receive project follow-ups.";
13
+ if (session.loop || session.spawn || session.handoff || session.assignment || session.singleTurn || session._pairDelegation || isPaired) return "Orchestrated sessions cannot receive project follow-ups.";
14
+ if (session.dangerouslySkipPermissions || session.permissionMode === "bypassPermissions") return "Full-access sessions cannot receive project follow-ups.";
15
+ if (session.destroying || session.scheduledMessage || session.rateLimitAutoContinuePending || session.taskStopRequested || session._runtimeRefreshRequested || session.pendingPush && session.pendingPush.length) return "Target session is not safely resumable right now.";
16
+ return "";
17
+ }
18
+
19
+ function attachDelegatedFollowUp(ctx) {
20
+ var sm = ctx.sm;
21
+
22
+ function inspect(principal, targetSessionRef) {
23
+ if (!principal || typeof targetSessionRef !== "string" || targetSessionRef.indexOf("session:") !== 0) {
24
+ throw new Error("A durable opaque targetSessionRef is required.");
25
+ }
26
+ var session = null;
27
+ sm.sessions.forEach(function (candidate) {
28
+ if (!session && ctx.sessionRef(candidate) === targetSessionRef) session = candidate;
29
+ });
30
+ if (!session) throw new Error("Target session was not found in this project.");
31
+ if (ctx.isMultiUser()) {
32
+ if (!principal.userId || session.ownerId !== principal.userId) throw new Error("Target session access denied.");
33
+ } else if (session.ownerId) {
34
+ throw new Error("Target session access denied.");
35
+ }
36
+ var paired = !!(ctx.isPairedSession && ctx.isPairedSession(session));
37
+ var reason = unsafeReason(session, paired);
38
+ if (reason) throw new Error(reason);
39
+ if (!ctx.canResumeVendor(session.vendor)) throw new Error("Target session provider cannot safely resume this conversation.");
40
+ return { session: session, sessionRef: targetSessionRef, title: session.title || "Existing session" };
41
+ }
42
+
43
+ async function dispatch(principal, targetSessionRef, task, metadata, onReady) {
44
+ var resolved = inspect(principal, targetSessionRef);
45
+ var session = resolved.session;
46
+ var sdk = ctx.getSdk();
47
+ if (!sdk || typeof sdk.pushMessage !== "function" || typeof sdk.startQuery !== "function") {
48
+ throw new Error("Target project runtime is unavailable.");
49
+ }
50
+ if (typeof onReady === "function") onReady(session);
51
+ sm.sendAndRecord(session, {
52
+ type: "delegated_follow_up",
53
+ text: task,
54
+ assignmentId: metadata.assignmentId,
55
+ sourceMateId: metadata.sourceMateId,
56
+ sourceProjectSlug: metadata.sourceProjectSlug,
57
+ sourceSessionRef: metadata.sourceSessionRef,
58
+ });
59
+ session.isProcessing = true;
60
+ session.lastActivity = Date.now();
61
+ session.sentToolResults = {};
62
+ if (ctx.onProcessingChanged) ctx.onProcessingChanged();
63
+ sm.broadcastSessionList();
64
+ try {
65
+ var delivered = sdk.pushMessage(session, task);
66
+ if (!delivered) await sdk.startQuery(session, task, undefined, ctx.getLinuxUserForSession(session));
67
+ } catch (error) {
68
+ session.isProcessing = false;
69
+ sm.sendAndRecord(session, {
70
+ type: "error",
71
+ text: "Delegated follow-up could not start: " + (error.message || String(error)),
72
+ assignmentId: metadata.assignmentId,
73
+ });
74
+ if (ctx.onProcessingChanged) ctx.onProcessingChanged();
75
+ sm.broadcastSessionList();
76
+ throw error;
77
+ }
78
+ return session;
79
+ }
80
+
81
+ return { inspectDelegatedFollowUp: inspect, dispatchDelegatedFollowUp: dispatch };
82
+ }
83
+
84
+ module.exports = { attachDelegatedFollowUp: attachDelegatedFollowUp, unsafeReason: unsafeReason };
@@ -0,0 +1,53 @@
1
+ function attachDelegatedSession(ctx) {
2
+ var sm = ctx.sm;
3
+
4
+ async function createDelegatedSession(principal, task, metadata, onCreated) {
5
+ if (!principal || !task) throw new Error("A bound owner and task are required.");
6
+ var selection = await ctx.resolveModel(principal);
7
+ if (!selection || selection.status !== "ready" || !selection.vendor || !selection.model) {
8
+ throw new Error(selection && selection.error ? selection.error : "No configured model is available in the target project.");
9
+ }
10
+ var sdk = ctx.getSdk();
11
+ if (!sdk || typeof sdk.startQuery !== "function") throw new Error("Target project runtime is unavailable.");
12
+ var create = typeof sm.createSessionRaw === "function" ? sm.createSessionRaw : sm.createSession;
13
+ var session = create.call(sm, {
14
+ ownerId: principal.userId || null,
15
+ sessionVisibility: "private",
16
+ vendor: selection.vendor,
17
+ model: selection.model,
18
+ });
19
+ session.title = metadata.title;
20
+ session.assignment = {
21
+ assignmentId: metadata.assignmentId,
22
+ sourceMateId: metadata.sourceMateId,
23
+ sourceProjectSlug: metadata.sourceProjectSlug,
24
+ sourceSessionRef: metadata.sourceSessionRef,
25
+ delegated: true,
26
+ };
27
+ session.singleTurn = true;
28
+ if (typeof onCreated === "function") onCreated(session);
29
+ sm.sendAndRecord(session, {
30
+ type: "delegated_work",
31
+ text: task,
32
+ assignmentId: metadata.assignmentId,
33
+ sourceMateId: metadata.sourceMateId,
34
+ sourceProjectSlug: metadata.sourceProjectSlug,
35
+ sourceSessionRef: metadata.sourceSessionRef,
36
+ });
37
+ session.isProcessing = true;
38
+ session.lastActivity = Date.now();
39
+ session.sentToolResults = {};
40
+ sm.broadcastSessionList();
41
+ try {
42
+ await Promise.resolve(sdk.startQuery(session, task, undefined, ctx.getLinuxUserForSession(session)));
43
+ } catch (error) {
44
+ session.isProcessing = false;
45
+ sm.sendAndRecord(session, { type: "error", text: error.message || String(error) });
46
+ }
47
+ return session;
48
+ }
49
+
50
+ return { createDelegatedSession: createDelegatedSession };
51
+ }
52
+
53
+ module.exports = { attachDelegatedSession: attachDelegatedSession };
@@ -13,7 +13,7 @@ var execFileSync = require("child_process").execFileSync;
13
13
  * getOsUserInfoForWs (function)
14
14
  * startFileWatch, stopFileWatch, startDirWatch (from _fileWatch)
15
15
  * usersModule, fsAsUser
16
- * validateEnvString (function)
16
+ * validateEnvString, onEnvironmentChanged (functions)
17
17
  * opts (for onGetProjectEnv, onSetProjectEnv, onGetSharedEnv, onSetSharedEnv callbacks)
18
18
  * IGNORED_DIRS, BINARY_EXTS, IMAGE_EXTS, FS_MAX_SIZE (constants)
19
19
  */
@@ -33,6 +33,7 @@ function attachFilesystem(ctx) {
33
33
  var usersModule = ctx.usersModule;
34
34
  var fsAsUser = ctx.fsAsUser;
35
35
  var validateEnvString = ctx.validateEnvString;
36
+ var onEnvironmentChanged = ctx.onEnvironmentChanged || function () {};
36
37
  var opts = ctx.opts;
37
38
  var IGNORED_DIRS = ctx.IGNORED_DIRS;
38
39
  var BINARY_EXTS = ctx.BINARY_EXTS;
@@ -234,16 +235,11 @@ function attachFilesystem(ctx) {
234
235
  // --- Project environment variables ---
235
236
  if (msg.type === "get_project_env") {
236
237
  var envrc = "";
237
- var hasEnvrc = false;
238
238
  if (typeof opts.onGetProjectEnv === "function") {
239
239
  var envResult = opts.onGetProjectEnv(msg.slug);
240
240
  envrc = envResult.envrc || "";
241
241
  }
242
- try {
243
- var envrcPath = path.join(cwd, ".envrc");
244
- hasEnvrc = fs.existsSync(envrcPath);
245
- } catch (e) {}
246
- sendTo(ws, { type: "project_env_result", slug: msg.slug, envrc: envrc, hasEnvrc: hasEnvrc });
242
+ sendTo(ws, { type: "project_env_result", slug: msg.slug, envrc: envrc });
247
243
  return true;
248
244
  }
249
245
 
@@ -255,7 +251,8 @@ function attachFilesystem(ctx) {
255
251
  return true;
256
252
  }
257
253
  var setResult = opts.onSetProjectEnv(msg.slug, msg.envrc || "");
258
- sendTo(ws, { type: "set_project_env_result", ok: setResult.ok, slug: msg.slug, error: setResult.error });
254
+ if (setResult.ok) onEnvironmentChanged();
255
+ sendTo(ws, { type: "set_project_env_result", ok: setResult.ok, slug: msg.slug, error: setResult.error, timing: setResult.ok ? "Applies to newly created coding-agent processes. Active processes keep their current environment." : undefined });
259
256
  } else {
260
257
  sendTo(ws, { type: "set_project_env_result", ok: false, error: "Not supported" });
261
258
  }
@@ -308,7 +305,8 @@ function attachFilesystem(ctx) {
308
305
  return true;
309
306
  }
310
307
  var sharedSetResult = opts.onSetSharedEnv(msg.envrc || "");
311
- sendTo(ws, { type: "set_shared_env_result", ok: sharedSetResult.ok, error: sharedSetResult.error });
308
+ if (sharedSetResult.ok) onEnvironmentChanged();
309
+ sendTo(ws, { type: "set_shared_env_result", ok: sharedSetResult.ok, error: sharedSetResult.error, timing: sharedSetResult.ok ? "Applies to newly created coding-agent processes. Active processes keep their current environment." : undefined });
312
310
  } else {
313
311
  sendTo(ws, { type: "set_shared_env_result", ok: false, error: "Not supported" });
314
312
  }
@@ -155,6 +155,7 @@ function attachKnowledge(ctx) {
155
155
 
156
156
  return {
157
157
  handleKnowledgeMessage: handleKnowledgeMessage,
158
+ listKnowledgeFiles: listKnowledgeFiles,
158
159
  };
159
160
  }
160
161
 
@@ -0,0 +1,120 @@
1
+ var mateCreationMcp = require("./mate-creation-mcp-server");
2
+ var selectCatalogModel = require("./project-models").selectCatalogModel;
3
+
4
+ function attachMateCreationProposal(ctx) {
5
+ var pending = {};
6
+
7
+ function clean(value, maxLength) {
8
+ if (typeof value !== "string") return "";
9
+ return value.replace(/\u0000/g, "").trim().slice(0, maxLength);
10
+ }
11
+
12
+ function publicProposal(source) {
13
+ return {
14
+ proposalId: source.proposalId,
15
+ name: clean(source.name, 80),
16
+ bio: clean(source.bio, 280),
17
+ relationship: clean(source.relationship, 120),
18
+ activities: Array.isArray(source.activities) ? source.activities.slice(0, 12) : [],
19
+ communicationStyle: Array.isArray(source.communicationStyle) ? source.communicationStyle.slice(0, 12) : [],
20
+ autonomy: clean(source.autonomy, 500),
21
+ identityMarkdown: clean(source.identityMarkdown, 30000),
22
+ };
23
+ }
24
+
25
+ function record(session, event) {
26
+ if (typeof ctx.recordSessionEvent === "function") ctx.recordSessionEvent(session, event);
27
+ }
28
+
29
+ function validateBoundSession(session) {
30
+ var projectOwnerId = typeof ctx.getProjectOwnerId === "function" ? ctx.getProjectOwnerId() : null;
31
+ var ownerValid = typeof ctx.isMultiUser === "function" && ctx.isMultiUser() ? !!(session && session.ownerId && (!projectOwnerId || session.ownerId === projectOwnerId)) : !!(session && !session.ownerId);
32
+ return !!(session && ctx.isMate === true && ctx.isHostAgent === true && session.mateCreationMode === true
33
+ && ownerValid);
34
+ }
35
+
36
+ function hasInterviewAnswer(session) {
37
+ var history = session && Array.isArray(session.history) ? session.history : [];
38
+ for (var i = history.length - 1; i >= 0; i--) if (history[i] && history[i].type === "ask_user_answered") return true;
39
+ return false;
40
+ }
41
+
42
+ function getToolDefs(boundSession) {
43
+ if (!boundSession || boundSession.mateCreationMode !== true) return [];
44
+ return mateCreationMcp.getToolDefs(function (definition) {
45
+ if (!validateBoundSession(boundSession)) return Promise.resolve({ action: "error", error: "Mate proposals require an owned Clay creation interview." });
46
+ if (!hasInterviewAnswer(boundSession)) return Promise.resolve({ action: "error", error: "Interview the user before proposing a Mate." });
47
+ return new Promise(function (resolve) {
48
+ var proposalId = "mp_" + Date.now() + "_" + Math.random().toString(36).slice(2, 8);
49
+ definition.proposalId = proposalId;
50
+ pending[proposalId] = { resolve: resolve, definition: definition, session: boundSession };
51
+ record(boundSession, { type: "mate_creation_proposal", proposal: publicProposal(definition) });
52
+ });
53
+ });
54
+ }
55
+
56
+ function createMcpServer(adapter, boundSession) {
57
+ return adapter.createToolServer({ name: "clay-mate-creation", version: "1.0.0", tools: getToolDefs(boundSession) });
58
+ }
59
+
60
+ function getBridgeTools(boundSession, normalizeSchema) {
61
+ if (!boundSession) return [];
62
+ return getToolDefs(boundSession).map(function (tool) {
63
+ return { server: "clay-mate-creation", name: tool.name, description: tool.description || tool.name, inputSchema: normalizeSchema(tool.inputSchema) };
64
+ });
65
+ }
66
+
67
+ function callBridgeTool(boundSession, toolName, args) {
68
+ var tools = getToolDefs(boundSession);
69
+ for (var i = 0; i < tools.length; i++) if (tools[i].name === toolName) return Promise.resolve(tools[i].handler(args || {}));
70
+ return Promise.reject(new Error("Session tool not found: clay-mate-creation/" + toolName));
71
+ }
72
+
73
+ function handleHomeMessage(ws, msg, session) {
74
+ var entry = msg && msg.proposalId ? pending[msg.proposalId] : null;
75
+ if (!entry || entry.session !== session || !validateBoundSession(session)) return false;
76
+ delete pending[msg.proposalId];
77
+ if (msg.action !== "create") {
78
+ record(session, { type: "mate_creation_proposal_resolved", proposalId: msg.proposalId, action: "cancel" });
79
+ entry.resolve({ action: "cancel" });
80
+ return true;
81
+ }
82
+ if (typeof ctx.createReadyMate !== "function") {
83
+ var unavailable = "Mate creation is unavailable.";
84
+ record(session, { type: "mate_creation_proposal_resolved", proposalId: msg.proposalId, action: "error", error: unavailable });
85
+ entry.resolve({ action: "error", error: unavailable });
86
+ return true;
87
+ }
88
+ var vendor = session.vendor || "claude";
89
+ if (typeof ctx.getVendorModelCatalog !== "function") {
90
+ var catalogUnavailable = "The Mate model catalog is unavailable.";
91
+ record(session, { type: "mate_creation_proposal_resolved", proposalId: msg.proposalId, action: "error", error: catalogUnavailable });
92
+ entry.resolve({ action: "error", error: catalogUnavailable });
93
+ return true;
94
+ }
95
+ Promise.resolve(ctx.getVendorModelCatalog(ws, vendor)).then(function (catalog) {
96
+ var selected = catalog && catalog.status === "ready" ? selectCatalogModel(catalog.models || [], catalog.defaultModel, "deep") : null;
97
+ if (!selected) throw new Error((catalog && catalog.error) || "No high-capability Mate model is available for " + vendor + ".");
98
+ return ctx.createReadyMate(ws, session.ownerId || null, Object.assign({}, entry.definition, { vendor: vendor, model: selected.value }));
99
+ }).then(function (mate) {
100
+ session.homeMateCreationPhase = "created";
101
+ record(session, { type: "mate_creation_proposal_resolved", proposalId: msg.proposalId, action: "create", mateId: mate.id, mateName: (mate.profile && mate.profile.displayName) || mate.name || "Mate" });
102
+ entry.resolve({ action: "create", mateId: mate.id });
103
+ }).catch(function (error) {
104
+ var text = error && error.message ? error.message : "The Mate could not be created.";
105
+ record(session, { type: "mate_creation_proposal_resolved", proposalId: msg.proposalId, action: "error", error: text });
106
+ entry.resolve({ action: "error", error: text });
107
+ });
108
+ return true;
109
+ }
110
+
111
+ return {
112
+ createMcpServer: createMcpServer,
113
+ getToolDefs: getToolDefs,
114
+ getBridgeTools: getBridgeTools,
115
+ callBridgeTool: callBridgeTool,
116
+ handleHomeMessage: handleHomeMessage,
117
+ };
118
+ }
119
+
120
+ module.exports = { attachMateCreationProposal: attachMateCreationProposal };
@@ -8,7 +8,8 @@ function attachMemory(ctx) {
8
8
  var sendTo = ctx.sendTo;
9
9
  var matesModule = ctx.matesModule;
10
10
  var sessionSearch = ctx.sessionSearch;
11
- var getAllProjectSessions = ctx.getAllProjectSessions;
11
+ var getWorkspaceMemorySessions = ctx.getWorkspaceMemorySessions || function () { return []; };
12
+ var getOwnedCurrentSessions = ctx.getOwnedCurrentSessions || function () { return []; };
12
13
  var projectOwnerId = ctx.projectOwnerId;
13
14
  var handleMessage = ctx.handleMessage;
14
15
 
@@ -166,7 +167,7 @@ function attachMemory(ctx) {
166
167
  var timelineEntries = [];
167
168
 
168
169
  // Own sessions
169
- sm.sessions.forEach(function (s) {
170
+ getOwnedCurrentSessions().forEach(function (s) {
170
171
  if (s.hidden || !s.history || s.history.length === 0) return;
171
172
  timelineEntries.push({
172
173
  title: s.title || "New Session",
@@ -176,7 +177,7 @@ function attachMemory(ctx) {
176
177
  });
177
178
 
178
179
  // Cross-project sessions
179
- var crossForTimeline = getAllProjectSessions();
180
+ var crossForTimeline = getWorkspaceMemorySessions();
180
181
  for (var cti = 0; cti < crossForTimeline.length; cti++) {
181
182
  var cs = crossForTimeline[cti];
182
183
  timelineEntries.push({
@@ -209,7 +210,7 @@ function attachMemory(ctx) {
209
210
  try {
210
211
  // Collect mate's own sessions
211
212
  var mateSessions = [];
212
- sm.sessions.forEach(function (s) {
213
+ getOwnedCurrentSessions().forEach(function (s) {
213
214
  if (!s.hidden && s.history && s.history.length > 0) {
214
215
  mateSessions.push(s);
215
216
  }
@@ -218,7 +219,7 @@ function attachMemory(ctx) {
218
219
  // globalSearch: also collect sessions from all other projects + knowledge files
219
220
  var knowledgeFiles = [];
220
221
  if (hasGlobalSearch) {
221
- var crossSessions = getAllProjectSessions();
222
+ var crossSessions = getWorkspaceMemorySessions();
222
223
  for (var cs = 0; cs < crossSessions.length; cs++) {
223
224
  mateSessions.push(crossSessions[cs]);
224
225
  }
@@ -595,7 +596,7 @@ function attachMemory(ctx) {
595
596
 
596
597
  // --- Message handlers for memory management UI ---
597
598
 
598
- function handleMemoryList(ws) {
599
+ function readMemoryState() {
599
600
  var digestFile = path.join(cwd, "knowledge", "session-digests.jsonl");
600
601
  var summaryFile = path.join(cwd, "knowledge", "memory-summary.md");
601
602
  var entries = [];
@@ -620,7 +621,12 @@ function attachMemory(ctx) {
620
621
  } catch (e) {}
621
622
  // Return newest first
622
623
  entries.reverse();
623
- sendTo(ws, { type: "memory_list", entries: entries, summary: summary });
624
+ return { entries: entries, summary: summary };
625
+ }
626
+
627
+ function handleMemoryList(ws) {
628
+ var state = readMemoryState();
629
+ sendTo(ws, { type: "memory_list", entries: state.entries, summary: state.summary });
624
630
  }
625
631
 
626
632
  function handleMemorySearch(ws, msg) {
@@ -671,6 +677,7 @@ function attachMemory(ctx) {
671
677
  gateMemory: gateMemory,
672
678
  updateMemorySummary: updateMemorySummary,
673
679
  initMemorySummary: initMemorySummary,
680
+ readMemoryState: readMemoryState,
674
681
  handleMemoryList: handleMemoryList,
675
682
  handleMemorySearch: handleMemorySearch,
676
683
  handleMemoryDelete: handleMemoryDelete,
@@ -12,6 +12,46 @@ function modelEntryMatches(entry, value) {
12
12
  return entry.value === value || entry.id === value || entry.resolvedModel === value;
13
13
  }
14
14
 
15
+ function normalizeCatalogModels(models) {
16
+ var normalized = [];
17
+ models = models || [];
18
+ for (var i = 0; i < models.length; i++) {
19
+ var value = modelEntryValue(models[i]);
20
+ if (!value) continue;
21
+ var displayName = typeof models[i] === "string" ? models[i] : (models[i].displayName || models[i].name || value);
22
+ var searchParts = [value, displayName];
23
+ if (typeof models[i] === "object") {
24
+ searchParts.push(models[i].id || "");
25
+ searchParts.push(models[i].resolvedModel || "");
26
+ }
27
+ normalized.push({ value: value, displayName: displayName, search: searchParts.join(" ").toLowerCase(), source: models[i] });
28
+ }
29
+ return normalized;
30
+ }
31
+
32
+ function selectCatalogModel(models, defaultModel, alias) {
33
+ var entries = normalizeCatalogModels(models);
34
+ if (entries.length === 0) return null;
35
+ var configured = null;
36
+ for (var i = 0; i < entries.length; i++) {
37
+ if (modelEntryMatches(entries[i].source, defaultModel)) {
38
+ configured = entries[i];
39
+ break;
40
+ }
41
+ }
42
+ alias = alias === "fast" || alias === "deep" ? alias : "standard";
43
+ if (alias === "standard" && configured) return configured;
44
+ var matcher = alias === "fast"
45
+ ? /haiku|mini|flash|fast|lite|small|luna/
46
+ : alias === "deep"
47
+ ? /opus|\bpro\b|max|deep|large|\bsol\b|reason/
48
+ : /sonnet|standard|balanced|terra/;
49
+ for (var mi = 0; mi < entries.length; mi++) {
50
+ if (matcher.test(entries[mi].search)) return entries[mi];
51
+ }
52
+ return configured || entries[0];
53
+ }
54
+
15
55
  function attachModels(ctx) {
16
56
  var cwd = ctx.cwd;
17
57
  var slug = ctx.slug;
@@ -25,26 +65,36 @@ function attachModels(ctx) {
25
65
  var serverTls = ctx.serverTls;
26
66
  var serverAuthToken = ctx.serverAuthToken;
27
67
 
28
- async function loadVendorModels(ws, msg) {
29
- var vendor = msg.vendor || "";
30
- var requestId = msg.requestId || null;
31
- var loadError = null;
32
- var vendorAdapter = null;
33
- var session = getSessionForWs(ws);
68
+ function getVendorAvailability() {
69
+ var available = (sm.availableVendors || []).slice();
70
+ var installed = (sm.installedVendors || []).slice();
71
+ var ids = available.slice();
72
+ for (var i = 0; i < installed.length; i++) {
73
+ if (ids.indexOf(installed[i]) === -1) ids.push(installed[i]);
74
+ }
75
+ return ids.map(function (vendor) {
76
+ var info = yoke.getVendorInfo(vendor);
77
+ return {
78
+ id: vendor,
79
+ displayName: info && info.displayName ? info.displayName : vendor,
80
+ installed: installed.indexOf(vendor) !== -1,
81
+ };
82
+ });
83
+ }
34
84
 
35
- if (!vendor) {
36
- sendTo(ws, {
37
- type: "model_info",
38
- vendor: vendor,
39
- requestId: requestId,
40
- modelStatus: "error",
41
- error: "No vendor was selected.",
42
- model: "",
43
- models: [],
44
- sessionId: session ? session.localId : null,
45
- });
46
- return;
85
+ function modelDisplayName(models, value) {
86
+ for (var i = 0; i < models.length; i++) {
87
+ if (!modelEntryMatches(models[i], value)) continue;
88
+ return typeof models[i] === "string" ? models[i] : (models[i].displayName || modelEntryValue(models[i]));
47
89
  }
90
+ return value;
91
+ }
92
+
93
+ async function getVendorCatalog(ws, vendor) {
94
+ var loadError = null;
95
+ var loadedDefaultModel = "";
96
+ var vendorAdapter = null;
97
+ if (!vendor) return { vendor: "", models: [], capabilities: {}, status: "error", error: "No vendor was selected." };
48
98
 
49
99
  try {
50
100
  var modelLinuxUser = getLinuxUserForWs(ws);
@@ -75,6 +125,7 @@ function attachModels(ctx) {
75
125
  });
76
126
  sm.capabilitiesByVendor = sm.capabilitiesByVendor || {};
77
127
  sm.capabilitiesByVendor[vendor] = readyResult.capabilities || {};
128
+ loadedDefaultModel = readyResult.defaultModel || "";
78
129
  sm.modelsByVendor = sm.modelsByVendor || {};
79
130
  if (Array.isArray(readyResult.models) && readyResult.models.length > 0) {
80
131
  sm.modelsByVendor[vendor] = readyResult.models;
@@ -110,6 +161,84 @@ function attachModels(ctx) {
110
161
  }
111
162
 
112
163
  var vendorModels = (sm.modelsByVendor && sm.modelsByVendor[vendor]) || [];
164
+ var vendorInfo = yoke.getVendorInfo(vendor);
165
+ var displayName = vendorInfo ? vendorInfo.displayName : vendor;
166
+ var status = vendorModels.length > 0 ? "ready" : (loadError ? "error" : "empty");
167
+ var defaultModel = "";
168
+ var preferredModels = [
169
+ (sm.defaultModelByVendor && sm.defaultModelByVendor[vendor]) || "",
170
+ loadedDefaultModel,
171
+ ];
172
+ for (var preferredIndex = 0; preferredIndex < preferredModels.length && !defaultModel; preferredIndex++) {
173
+ for (var modelIndex = 0; modelIndex < vendorModels.length; modelIndex++) {
174
+ if (!modelEntryMatches(vendorModels[modelIndex], preferredModels[preferredIndex])) continue;
175
+ defaultModel = modelEntryValue(vendorModels[modelIndex]);
176
+ break;
177
+ }
178
+ }
179
+ var errorText = "";
180
+ if (status === "error") {
181
+ errorText = "Could not load " + displayName + " models: " + (loadError.message || loadError);
182
+ } else if (status === "empty") {
183
+ errorText = displayName + " returned no models. Check CLI authentication and retry.";
184
+ }
185
+ return {
186
+ vendor: vendor,
187
+ models: vendorModels,
188
+ defaultModel: defaultModel,
189
+ capabilities: (sm.capabilitiesByVendor && sm.capabilitiesByVendor[vendor]) || {},
190
+ availableVendors: sm.availableVendors || [],
191
+ installedVendors: sm.installedVendors || [],
192
+ status: status,
193
+ error: errorText,
194
+ };
195
+ }
196
+
197
+ async function resolveConfiguredModel(ws, alias) {
198
+ var availability = getVendorAvailability();
199
+ var candidates = [];
200
+ var preferredVendor = sm.defaultVendor || "";
201
+ if (preferredVendor) candidates.push(preferredVendor);
202
+ for (var i = 0; i < availability.length; i++) {
203
+ if (candidates.indexOf(availability[i].id) === -1) candidates.push(availability[i].id);
204
+ }
205
+ var errors = [];
206
+ for (var ci = 0; ci < candidates.length; ci++) {
207
+ var catalog = await getVendorCatalog(ws, candidates[ci]);
208
+ var models = catalog.models || [];
209
+ var selected = selectCatalogModel(models, catalog.defaultModel, alias);
210
+ if (catalog.status !== "ready" || !selected) {
211
+ if (catalog.error) errors.push(catalog.error);
212
+ continue;
213
+ }
214
+ var info = yoke.getVendorInfo(catalog.vendor);
215
+ return {
216
+ status: "ready",
217
+ vendor: catalog.vendor,
218
+ vendorName: info && info.displayName ? info.displayName : catalog.vendor,
219
+ alias: alias === "fast" || alias === "deep" ? alias : "standard",
220
+ model: selected.value,
221
+ modelName: selected.displayName || modelDisplayName(models, selected.value),
222
+ error: "",
223
+ };
224
+ }
225
+ return {
226
+ status: "error",
227
+ vendor: preferredVendor,
228
+ vendorName: preferredVendor ? ((yoke.getVendorInfo(preferredVendor) || {}).displayName || preferredVendor) : "",
229
+ model: "",
230
+ modelName: "",
231
+ alias: alias === "fast" || alias === "deep" ? alias : "standard",
232
+ error: errors[0] || "No configured model is available. Sign in to an installed model provider CLI, then retry.",
233
+ };
234
+ }
235
+
236
+ async function loadVendorModels(ws, msg) {
237
+ var vendor = msg.vendor || "";
238
+ var requestId = msg.requestId || null;
239
+ var session = getSessionForWs(ws);
240
+ var catalog = await getVendorCatalog(ws, vendor);
241
+ var vendorModels = catalog.models;
113
242
  var modelToSend = modelEntryValue(vendorModels[0]);
114
243
  var preferredModel = (session && session.vendor === vendor && session.model) || (sm.defaultModelByVendor && sm.defaultModelByVendor[vendor]) || "";
115
244
  if (preferredModel) {
@@ -120,27 +249,18 @@ function attachModels(ctx) {
120
249
  }
121
250
  }
122
251
  }
123
- var vendorInfo = yoke.getVendorInfo(vendor);
124
- var displayName = vendorInfo ? vendorInfo.displayName : vendor;
125
- var status = vendorModels.length > 0 ? "ready" : (loadError ? "error" : "empty");
126
- var errorText = "";
127
- if (status === "error") {
128
- errorText = "Could not load " + displayName + " models: " + (loadError.message || loadError);
129
- } else if (status === "empty") {
130
- errorText = displayName + " returned no models. Check CLI authentication and retry.";
131
- }
132
252
  sendTo(ws, {
133
253
  type: "model_info",
134
254
  model: modelToSend,
135
255
  models: vendorModels,
136
256
  vendor: vendor,
137
257
  sessionId: session ? session.localId : null,
138
- capabilities: (sm.capabilitiesByVendor && sm.capabilitiesByVendor[vendor]) || {},
139
- availableVendors: sm.availableVendors || [],
140
- installedVendors: sm.installedVendors || [],
258
+ capabilities: catalog.capabilities,
259
+ availableVendors: catalog.availableVendors,
260
+ installedVendors: catalog.installedVendors,
141
261
  requestId: requestId,
142
- modelStatus: status,
143
- error: errorText,
262
+ modelStatus: catalog.status,
263
+ error: catalog.error,
144
264
  });
145
265
  }
146
266
 
@@ -208,6 +328,9 @@ function attachModels(ctx) {
208
328
 
209
329
  return {
210
330
  handleMessage: handleMessage,
331
+ getVendorCatalog: getVendorCatalog,
332
+ getVendorAvailability: getVendorAvailability,
333
+ resolveConfiguredModel: resolveConfiguredModel,
211
334
  loadVendorModels: loadVendorModels,
212
335
  selectModel: selectModel,
213
336
  };
@@ -217,4 +340,6 @@ module.exports = {
217
340
  attachModels: attachModels,
218
341
  modelEntryMatches: modelEntryMatches,
219
342
  modelEntryValue: modelEntryValue,
343
+ normalizeCatalogModels: normalizeCatalogModels,
344
+ selectCatalogModel: selectCatalogModel,
220
345
  };
@@ -7,7 +7,7 @@ var MAX_NOTE_PROMPT_CHARS = 800;
7
7
  var NOTES_LABEL = "--- Project sticky notes (cross-session work memory; manage via clay-notes tools) ---";
8
8
  var NOTES_HINT = "- More notes are available; call list_notes for the rest.";
9
9
  var PROACTIVE_POLICY = sessionNotesMcp.MEMORY_CONTRACT + " If an injected preview is relevant or truncated, call list_notes before acting or writing so you do not create a duplicate.";
10
- var NOTE_COLORS = ["yellow", "blue", "green", "pink", "orange", "purple"];
10
+ var NOTE_COLORS = ["purple", "green", "yellow", "blue", "pink", "orange"];
11
11
 
12
12
  function toolResult(value) {
13
13
  return Promise.resolve({ content: [{ type: "text", text: JSON.stringify(value) }] });
@@ -150,7 +150,7 @@ function attachSessionNotes(ctx) {
150
150
  var placement = autoPlacement(notes);
151
151
  var created = nm.create({
152
152
  text: text,
153
- color: color || "yellow",
153
+ color: color || "purple",
154
154
  x: placement.x,
155
155
  y: placement.y,
156
156
  origin: { sessionId: caller.localId, vendor: caller.vendor || "claude" },