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,68 @@
1
+ // One-shot boot routing after durable Home preferences become available.
2
+ import { store } from './store.js';
3
+ import { rememberHomePrimarySurface } from './home-surface.js';
4
+
5
+ var initialized = false;
6
+ var resolved = false;
7
+
8
+ function explicitProjectPath(pathname) {
9
+ return /^\/p\/[a-z0-9_-]+(?:\/|$)/.test(pathname || "");
10
+ }
11
+
12
+ export function resolveHomeBootDestination(options) {
13
+ if (!options || options.surfaceLoaded !== true) return "wait";
14
+ if (options.paneMode || explicitProjectPath(options.pathname)) return "project";
15
+ if (options.surface === "project" && options.currentSlug) return "project";
16
+ if (options.dockLoaded !== true) return "wait";
17
+ return "home";
18
+ }
19
+
20
+ function finishPendingShell() {
21
+ document.body.classList.remove("home-surface-boot-pending");
22
+ var overlay = document.getElementById("connect-overlay");
23
+ if (overlay && store.get('connected')) overlay.classList.add("hidden");
24
+ }
25
+
26
+ function projectRoute(slug) {
27
+ return slug ? "/p/" + slug + "/" : null;
28
+ }
29
+
30
+ function settleBoot(state) {
31
+ if (resolved) return;
32
+ var destination = resolveHomeBootDestination({
33
+ surfaceLoaded: state.homeSurfaceLoaded,
34
+ dockLoaded: state.homeDockPreferenceLoaded,
35
+ surface: state.homePrimarySurface,
36
+ currentSlug: state.currentSlug,
37
+ pathname: location.pathname,
38
+ paneMode: state.paneMode,
39
+ });
40
+ if (destination === "wait") return;
41
+ resolved = true;
42
+ if (destination === "project") {
43
+ rememberHomePrimarySurface("project");
44
+ if (!explicitProjectPath(location.pathname)) {
45
+ var route = projectRoute(state.currentSlug);
46
+ if (route) history.replaceState(null, "", route);
47
+ }
48
+ store.set({ homeSurfaceBootResolved: true });
49
+ finishPendingShell();
50
+ var input = document.getElementById("input");
51
+ if (input && !input.disabled) input.focus({ preventScroll: true });
52
+ return;
53
+ }
54
+ store.set({ homeSurfaceBootResolved: true, homeSurfaceRestoreRequested: true });
55
+ finishPendingShell();
56
+ }
57
+
58
+ export function initHomeSurfaceBoot() {
59
+ if (initialized) return;
60
+ initialized = true;
61
+ if (!explicitProjectPath(location.pathname) && !store.get('paneMode')) {
62
+ document.body.classList.add("home-surface-boot-pending");
63
+ var message = document.getElementById("connect-overlay-msg");
64
+ if (message) message.textContent = "Restoring your workspace…";
65
+ }
66
+ store.subscribe(function (state) { settleBoot(state); });
67
+ settleBoot(store.snap());
68
+ }
@@ -0,0 +1,141 @@
1
+ // Durable, server-backed Home conversation selection state.
2
+
3
+ import { store } from './store.js';
4
+ import { getWs } from './ws-ref.js';
5
+
6
+ function send(message) {
7
+ var ws = getWs();
8
+ if (!ws || ws.readyState !== 1) return false;
9
+ ws.send(JSON.stringify(message));
10
+ return true;
11
+ }
12
+
13
+ function normalizeSessions(value) {
14
+ var source = value && typeof value === "object" && !Array.isArray(value) ? value : {};
15
+ var result = {};
16
+ var keys = Object.keys(source);
17
+ for (var i = 0; i < keys.length; i++) {
18
+ if (typeof source[keys[i]] === "string" && source[keys[i]]) result[keys[i]] = source[keys[i]];
19
+ }
20
+ return result;
21
+ }
22
+
23
+ function normalizePrimarySurface(value) {
24
+ return value === "home" || value === "project" ? value : null;
25
+ }
26
+
27
+ function normalizeProjectSlug(value) {
28
+ return typeof value === "string" && /^[a-z0-9][a-z0-9_-]{0,127}$/.test(value) ? value : null;
29
+ }
30
+
31
+ function normalizeChatScope(value) {
32
+ return value === "current" ? "current" : "all";
33
+ }
34
+
35
+ function normalizeSubSurface(value) {
36
+ return value === "debates" ? "debates" : "chat";
37
+ }
38
+
39
+ export function requestHomeSurfacePreference() {
40
+ return send({ type: "home_surface_get" });
41
+ }
42
+
43
+ export function updateHomeSurfacePreference(patch) {
44
+ var state = store.snap();
45
+ var next = {
46
+ surface: normalizePrimarySurface(state.homePrimarySurface),
47
+ projectSlug: normalizeProjectSlug(state.homeSurfaceProjectSlug || state.currentSlug),
48
+ activeMateId: state.homeChatMateId || null,
49
+ activeSessionByMate: Object.assign({}, state.homeActiveSessionByMate || {}),
50
+ sidebarCollapsed: state.homeSidebarCollapsed === true,
51
+ chatScope: normalizeChatScope(state.homeChatScope),
52
+ subSurface: normalizeSubSurface(state.homeSubSurface),
53
+ };
54
+ if (patch && Object.prototype.hasOwnProperty.call(patch, "surface")) next.surface = normalizePrimarySurface(patch.surface);
55
+ if (patch && Object.prototype.hasOwnProperty.call(patch, "projectSlug")) next.projectSlug = normalizeProjectSlug(patch.projectSlug);
56
+ if (patch && Object.prototype.hasOwnProperty.call(patch, "activeMateId")) next.activeMateId = patch.activeMateId;
57
+ if (patch && patch.activeSessionByMate) next.activeSessionByMate = Object.assign({}, next.activeSessionByMate, patch.activeSessionByMate);
58
+ if (patch && Object.prototype.hasOwnProperty.call(patch, "sidebarCollapsed")) next.sidebarCollapsed = patch.sidebarCollapsed === true;
59
+ if (patch && Object.prototype.hasOwnProperty.call(patch, "chatScope")) next.chatScope = normalizeChatScope(patch.chatScope);
60
+ if (patch && Object.prototype.hasOwnProperty.call(patch, "subSurface")) next.subSurface = normalizeSubSurface(patch.subSurface);
61
+ store.set({
62
+ homePrimarySurface: next.surface,
63
+ homeSurfaceProjectSlug: next.projectSlug,
64
+ homeActiveSessionByMate: normalizeSessions(next.activeSessionByMate),
65
+ homeSidebarCollapsed: next.sidebarCollapsed,
66
+ homeChatScope: next.chatScope,
67
+ homeSubSurface: next.subSurface,
68
+ });
69
+ var outgoing = {};
70
+ if (patch && Object.prototype.hasOwnProperty.call(patch, "surface")) outgoing.surface = next.surface;
71
+ if (patch && Object.prototype.hasOwnProperty.call(patch, "projectSlug")) outgoing.projectSlug = next.projectSlug;
72
+ if (patch && Object.prototype.hasOwnProperty.call(patch, "activeMateId")) outgoing.activeMateId = next.activeMateId;
73
+ if (patch && patch.activeSessionByMate) outgoing.activeSessionByMate = patch.activeSessionByMate;
74
+ if (patch && Object.prototype.hasOwnProperty.call(patch, "sidebarCollapsed")) outgoing.sidebarCollapsed = next.sidebarCollapsed;
75
+ if (patch && Object.prototype.hasOwnProperty.call(patch, "chatScope")) outgoing.chatScope = next.chatScope;
76
+ if (patch && Object.prototype.hasOwnProperty.call(patch, "subSurface")) outgoing.subSurface = next.subSurface;
77
+ return send({ type: "home_surface_set", preference: outgoing });
78
+ }
79
+
80
+ export function rememberHomePrimarySurface(surface, routeProjectSlug) {
81
+ var normalized = normalizePrimarySurface(surface);
82
+ if (!normalized) return false;
83
+ var projectSlug = normalizeProjectSlug(routeProjectSlug || store.get('currentSlug'));
84
+ if (store.get('homePrimarySurface') === normalized && store.get('homeSurfaceProjectSlug') === projectSlug) return false;
85
+ store.set({ homeSurfaceIntent: normalized, homePrimarySurface: normalized, homeSurfaceProjectSlug: projectSlug });
86
+ return updateHomeSurfacePreference({ surface: normalized, projectSlug: projectSlug });
87
+ }
88
+
89
+ export function rememberHomeMate(mateId) {
90
+ if (!mateId) return;
91
+ if (store.get('homePreferredMateId') === mateId) return;
92
+ store.set({ homePreferredMateId: mateId });
93
+ updateHomeSurfacePreference({ activeMateId: mateId });
94
+ }
95
+
96
+ export function rememberHomeSession(mateId, sessionId) {
97
+ if (!mateId || !sessionId) return;
98
+ if ((store.get('homeActiveSessionByMate') || {})[mateId] === sessionId && store.get('homePreferredMateId') === mateId) return;
99
+ var patch = {};
100
+ patch[mateId] = sessionId;
101
+ updateHomeSurfacePreference({ activeMateId: mateId, activeSessionByMate: patch });
102
+ }
103
+
104
+ export function forgetHomeSession(mateId) {
105
+ if (!mateId) return;
106
+ var sessions = Object.assign({}, store.get('homeActiveSessionByMate') || {});
107
+ delete sessions[mateId];
108
+ store.set({ homeActiveSessionByMate: sessions, homeChatSessionId: null });
109
+ var patch = {};
110
+ patch[mateId] = "";
111
+ send({ type: "home_surface_set", preference: { activeSessionByMate: patch } });
112
+ }
113
+
114
+ export function handleHomeSurfaceState(msg) {
115
+ if (!msg || msg.error || !msg.preference) {
116
+ if (!store.get('homeSurfaceLoaded')) store.set({ homeSurfaceLoaded: true });
117
+ return;
118
+ }
119
+ var preference = msg.preference;
120
+ var wasLoaded = store.get('homeSurfaceLoaded');
121
+ var intent = normalizePrimarySurface(store.get('homeSurfaceIntent'));
122
+ var intendedProject = normalizeProjectSlug(store.get('homeSurfaceProjectSlug'));
123
+ var savedSurface = normalizePrimarySurface(preference.surface);
124
+ var savedProject = normalizeProjectSlug(preference.projectSlug);
125
+ var intentConfirmed = !!intent && intent === savedSurface && intendedProject === savedProject;
126
+ var update = {
127
+ homeSurfaceLoaded: true,
128
+ homePrimarySurface: intent || savedSurface,
129
+ homeSurfaceProjectSlug: intent ? intendedProject : savedProject,
130
+ homeActiveSessionByMate: normalizeSessions(preference.activeSessionByMate),
131
+ homeSidebarCollapsed: preference.sidebarCollapsed === true,
132
+ homeChatScope: normalizeChatScope(preference.chatScope),
133
+ homeSubSurface: normalizeSubSurface(preference.subSurface),
134
+ };
135
+ if (intentConfirmed) update.homeSurfaceIntent = null;
136
+ if (!wasLoaded) {
137
+ update.homePreferredMateId = store.get('homePreferredMateId') || preference.activeMateId || store.get('homeChatMateId') || null;
138
+ }
139
+ store.set(update);
140
+ if (intent && !intentConfirmed) send({ type: "home_surface_set", preference: { surface: intent, projectSlug: intendedProject } });
141
+ }
@@ -0,0 +1,271 @@
1
+ // home-tools.js - Installed tool registration, rendering, and runtime glue.
2
+
3
+ import { store } from './store.js';
4
+ import { getWs } from './ws-ref.js';
5
+ import { registerDockTool, unregisterDockTool, renderDock, pulseDockTool, markDockToolChanged, reconcileDockActiveTool } from './home-dock.js';
6
+ import { renderToolUi, disposeToolUi, getControlCatalog } from './tool-renderer.js';
7
+ import { createToolRuntime } from './tool-runtime.js';
8
+ import { showToast } from './utils.js';
9
+ import { createToolLlmStatus, initialToolLlmAlias } from './tool-llm-status.js';
10
+ import { isToolModelAlias, shouldInjectToolLlmStatus } from './tool-ui-tree.js';
11
+ import { mountCapsuleHostControls, disposeCapsuleHostControls, handleToolSourceState as applyToolSourceState, handleToolMateAccessState as applyToolMateAccessState } from './home-capsule-source.js';
12
+
13
+ var definitions = Object.create(null);
14
+ var runtimes = Object.create(null);
15
+ var registeredIds = Object.create(null);
16
+ var errorSurfaces = Object.create(null);
17
+ var displays = Object.create(null);
18
+ var llmStatuses = Object.create(null);
19
+
20
+ export function requestTools() {
21
+ var ws = getWs();
22
+ if (ws && ws.readyState === 1) {
23
+ store.set({ homeToolRegistryLoaded: false });
24
+ ws.send(JSON.stringify({ type: "tools_list" }));
25
+ }
26
+ }
27
+
28
+ function stopRuntime(toolId) {
29
+ if (runtimes[toolId]) runtimes[toolId].stop();
30
+ if (llmStatuses[toolId]) llmStatuses[toolId].dispose();
31
+ if (displays[toolId]) disposeToolUi(displays[toolId]);
32
+ delete runtimes[toolId];
33
+ delete errorSurfaces[toolId];
34
+ delete displays[toolId];
35
+ delete llmStatuses[toolId];
36
+ disposeCapsuleHostControls(toolId);
37
+ }
38
+
39
+ function showRuntimeError(toolId, message) {
40
+ var surface = errorSurfaces[toolId];
41
+ if (!surface) return;
42
+ surface.textContent = message;
43
+ surface.classList.remove("hidden");
44
+ }
45
+
46
+ function renderRuntimeState(toolId, state) {
47
+ var definition = definitions[toolId];
48
+ var display = displays[toolId];
49
+ if (!definition || !display || !runtimes[toolId]) return;
50
+ renderToolUi(toolId, definition.uiTree, state, function (actionName, args) {
51
+ runtimes[toolId].action(actionName, args, "user").catch(function () {});
52
+ }, display);
53
+ }
54
+
55
+ function ensureRuntime(toolId) {
56
+ if (runtimes[toolId]) return runtimes[toolId];
57
+ var definition = definitions[toolId];
58
+ if (!definition) throw new Error("Tool is not installed on this home screen.");
59
+ if (definition.manifest.runtime === "server") throw new Error("Server-runtime capsules do not run in a browser worker.");
60
+ displays[toolId] = document.createElement("div");
61
+ displays[toolId].className = "home-tool-display";
62
+ var runtime = createToolRuntime({
63
+ toolId: toolId,
64
+ logicSource: definition.logicSource,
65
+ initialAction: definition.manifest.initialAction || null,
66
+ allowLlm: (definition.manifest.permissions || []).indexOf("llm") !== -1,
67
+ onLlmRequest: function (alias) {
68
+ if (llmStatuses[toolId]) llmStatuses[toolId].setAlias(alias);
69
+ },
70
+ onState: function (state) {
71
+ var error = errorSurfaces[toolId];
72
+ if (error) error.classList.add("hidden");
73
+ renderRuntimeState(toolId, state);
74
+ },
75
+ onError: function (message) { showRuntimeError(toolId, message); },
76
+ });
77
+ runtimes[toolId] = runtime;
78
+ runtime.start();
79
+ return runtime;
80
+ }
81
+
82
+ function mountTool(toolId, contentEl, chromeEl) {
83
+ var definition = definitions[toolId];
84
+ if (!definition) return;
85
+ var root = document.createElement("div");
86
+ root.className = "home-tool-root";
87
+ mountCapsuleHostControls(toolId, definition, chromeEl, root);
88
+ var permissions = definition.manifest.permissions || [];
89
+ if (permissions.indexOf("llm") !== -1 && shouldInjectToolLlmStatus(definition)) {
90
+ if (llmStatuses[toolId]) llmStatuses[toolId].dispose();
91
+ llmStatuses[toolId] = createToolLlmStatus(initialToolLlmAlias(definition.manifest));
92
+ llmStatuses[toolId].element.dataset.capsuleRuntimeSurface = "true";
93
+ root.appendChild(llmStatuses[toolId].element);
94
+ }
95
+ var error = document.createElement("div");
96
+ error.className = "home-tool-error hidden";
97
+ error.dataset.capsuleRuntimeSurface = "true";
98
+ root.appendChild(error);
99
+ contentEl.appendChild(root);
100
+ errorSurfaces[toolId] = error;
101
+ if (definition.manifest.runtime === "server") {
102
+ var serverDisplay = document.createElement("div");
103
+ serverDisplay.className = "home-tool-display";
104
+ serverDisplay.dataset.capsuleRuntimeSurface = "true";
105
+ root.appendChild(serverDisplay);
106
+ renderToolUi(toolId, definition.uiTree, {}, function () {}, serverDisplay);
107
+ return;
108
+ }
109
+ var runtime = ensureRuntime(toolId);
110
+ displays[toolId].dataset.capsuleRuntimeSurface = "true";
111
+ root.appendChild(displays[toolId]);
112
+ if (runtime.getState() !== null) renderRuntimeState(toolId, runtime.getState());
113
+ }
114
+
115
+ function registerDefinition(definition) {
116
+ if (!definition || !definition.manifest || !definition.manifest.id) return;
117
+ var toolId = definition.manifest.id;
118
+ definitions[toolId] = definition;
119
+ registeredIds[toolId] = true;
120
+ var dockTool = {
121
+ id: toolId,
122
+ name: definition.manifest.name,
123
+ lucideIcon: definition.manifest.lucideIcon || "box",
124
+ render: function (contentEl, chromeEl) { mountTool(toolId, contentEl, chromeEl); },
125
+ onChromeHide: function () { disposeCapsuleHostControls(toolId); },
126
+ onHide: function () { stopRuntime(toolId); },
127
+ };
128
+ registerDockTool(dockTool);
129
+ }
130
+
131
+ function syncStore() {
132
+ var tools = [];
133
+ var ids = Object.keys(definitions);
134
+ for (var i = 0; i < ids.length; i++) tools.push(definitions[ids[i]]);
135
+ store.set({ installedTools: tools });
136
+ }
137
+
138
+ export function handleToolsState(msg) {
139
+ var completeSync = !msg.requestedToolId;
140
+ var incoming = msg.tools || [];
141
+ var validIncoming = [];
142
+ var scanErrors = [];
143
+ for (var ei = 0; ei < incoming.length; ei++) {
144
+ if (incoming[ei] && incoming[ei].error) scanErrors.push(incoming[ei]);
145
+ else if (incoming[ei] && incoming[ei].manifest) validIncoming.push(incoming[ei]);
146
+ }
147
+ incoming = validIncoming;
148
+ store.set({ toolScanErrors: scanErrors });
149
+ if (scanErrors.length > 0) showToast("One or more capsule folders could not be loaded.", "error");
150
+ if (completeSync) {
151
+ var previousIds = Object.keys(registeredIds);
152
+ var incomingIds = Object.create(null);
153
+ for (var ii = 0; ii < incoming.length; ii++) incomingIds[incoming[ii].manifest.id] = true;
154
+ for (var pi = 0; pi < previousIds.length; pi++) {
155
+ if (incomingIds[previousIds[pi]]) continue;
156
+ stopRuntime(previousIds[pi]);
157
+ delete definitions[previousIds[pi]];
158
+ delete registeredIds[previousIds[pi]];
159
+ unregisterDockTool(previousIds[pi]);
160
+ }
161
+ }
162
+ for (var i = 0; i < incoming.length; i++) registerDefinition(incoming[i]);
163
+ syncStore();
164
+ if (completeSync) store.set({ homeToolRegistryLoaded: true });
165
+ if (completeSync && reconcileDockActiveTool()) return;
166
+ renderDock();
167
+ }
168
+
169
+ export function handleToolInstalled(msg) {
170
+ if (msg.tool && msg.tool.manifest && definitions[msg.tool.manifest.id]) stopRuntime(msg.tool.manifest.id);
171
+ registerDefinition(msg.tool);
172
+ syncStore();
173
+ renderDock();
174
+ markDockToolChanged(msg.tool && msg.tool.manifest ? msg.tool.manifest.id : null);
175
+ }
176
+
177
+ export function handleToolSourceState(msg) {
178
+ applyToolSourceState(msg);
179
+ }
180
+
181
+ export function handleToolMateAccessState(msg) {
182
+ if (msg.ok && msg.metadata && definitions[msg.toolId]) {
183
+ definitions[msg.toolId].metadata = msg.metadata;
184
+ syncStore();
185
+ }
186
+ applyToolMateAccessState(msg);
187
+ }
188
+
189
+ export function handleToolRemoved(msg) {
190
+ stopRuntime(msg.toolId);
191
+ delete definitions[msg.toolId];
192
+ delete registeredIds[msg.toolId];
193
+ unregisterDockTool(msg.toolId);
194
+ syncStore();
195
+ renderDock();
196
+ }
197
+
198
+ export function handleToolStorageResult(msg) {
199
+ if (runtimes[msg.toolId]) runtimes[msg.toolId].handleStorageResult(msg);
200
+ }
201
+
202
+ export function handleToolLlmResult(msg) {
203
+ if (runtimes[msg.toolId]) runtimes[msg.toolId].handleLlmResult(msg);
204
+ }
205
+
206
+ export function handleToolsError(msg) {
207
+ if (msg.toolId && msg.requestId && runtimes[msg.toolId]) {
208
+ runtimes[msg.toolId].handleLlmResult({ requestId: msg.requestId, error: msg.message });
209
+ return;
210
+ }
211
+ if (msg.toolId && msg.seq !== undefined && runtimes[msg.toolId]) {
212
+ runtimes[msg.toolId].handleStorageResult({ seq: msg.seq, error: msg.message });
213
+ return;
214
+ }
215
+ if (msg.toolId) showRuntimeError(msg.toolId, msg.message || "Tool operation failed.");
216
+ else showToast(msg.message || "Tool operation failed.", "error");
217
+ }
218
+
219
+ export function getToolRuntimeState(toolId) {
220
+ return runtimes[toolId] ? runtimes[toolId].getState() : null;
221
+ }
222
+
223
+ function replyControl(requestId, data, error) {
224
+ var ws = getWs();
225
+ if (!ws || ws.readyState !== 1) return;
226
+ ws.send(JSON.stringify({
227
+ type: "tool_control_response",
228
+ requestId: requestId,
229
+ data: data || null,
230
+ error: error || null,
231
+ }));
232
+ }
233
+
234
+ export function handleToolControlRequest(msg) {
235
+ Promise.resolve().then(async function () {
236
+ var definition = definitions[msg.toolId];
237
+ if (!definition) throw new Error("Tool is not installed on this home screen.");
238
+ var runtime = ensureRuntime(msg.toolId);
239
+ await runtime.ready();
240
+ var payload = msg.payload || {};
241
+ var state;
242
+ if (msg.kind === "snapshot") {
243
+ state = runtime.getState();
244
+ } else if (msg.kind === "act") {
245
+ state = await runtime.action(payload.actionId, payload.args || {}, msg.callerId);
246
+ pulseDockTool(msg.toolId);
247
+ } else if (msg.kind === "set") {
248
+ var control = getControlCatalog(msg.toolId)[payload.controlId];
249
+ if (!control) throw new Error("Unknown control '" + payload.controlId + "'.");
250
+ if (!control.action) throw new Error("Control '" + payload.controlId + "' does not expose an action.");
251
+ if (control.type === "model-select" && !isToolModelAlias(payload.value)) {
252
+ throw new Error("Model controls accept only fast, standard, or deep.");
253
+ }
254
+ state = await runtime.action(control.action, {
255
+ value: payload.value,
256
+ controlId: payload.controlId,
257
+ bind: control.bind,
258
+ }, msg.callerId);
259
+ pulseDockTool(msg.toolId);
260
+ } else {
261
+ throw new Error("Unknown tool control request kind '" + msg.kind + "'.");
262
+ }
263
+ replyControl(msg.requestId, {
264
+ state: state,
265
+ controls: getControlCatalog(msg.toolId),
266
+ ui: definition.uiTree,
267
+ }, null);
268
+ }).catch(function (error) {
269
+ replyControl(msg.requestId, null, error && error.message ? error.message : "Tool control failed.");
270
+ });
271
+ }
@@ -35,14 +35,62 @@ export function updateMermaidTheme(vars) {
35
35
 
36
36
  var mermaidIdCounter = 0;
37
37
 
38
+ function normalizeAdjacentEmphasis(text) {
39
+ var parts = text.split(/(```[\s\S]*?```|~~~[\s\S]*?~~~|`[^`\n]*`)/g);
40
+ for (var i = 0; i < parts.length; i++) {
41
+ if (/^(?:```|~~~|`)/.test(parts[i])) continue;
42
+ parts[i] = parts[i].replace(/(\*{1,3})([^*\n]+?)\1(?=[\p{L}\p{N}])/gu, "$1$2$1<wbr>");
43
+ }
44
+ return parts.join("");
45
+ }
46
+
38
47
  export function renderMarkdown(text) {
39
48
  // Normalize smart quotes so bold/italic delimiters flanking them parse correctly
40
- var normalized = text
49
+ var normalized = normalizeAdjacentEmphasis(text
41
50
  .replace(/[\u201C\u201D]/g, '"')
42
- .replace(/[\u2018\u2019]/g, "'");
51
+ .replace(/[\u2018\u2019]/g, "'")
52
+ .replace(/\*\*[ \t]+([^*\n]+?)[ \t]+\*\*/g, "**$1**"));
43
53
  return DOMPurify.sanitize(marked.parse(normalized));
44
54
  }
45
55
 
56
+ export function enhanceClaySessionLinks(root) {
57
+ if (!root || typeof document === "undefined") return;
58
+ var walker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT);
59
+ var nodes = [];
60
+ var node;
61
+ while ((node = walker.nextNode())) {
62
+ if (!node.parentElement || node.parentElement.closest("code, pre, a, button")) continue;
63
+ if (/\[clayos\/session:[A-Za-z0-9_-]{24}(?:\s+[\u2014-]\s+[^\]\n]{1,80})?\]/.test(node.nodeValue || "")) nodes.push(node);
64
+ }
65
+ for (var i = 0; i < nodes.length; i++) {
66
+ var text = nodes[i].nodeValue || "";
67
+ var pattern = /\[clayos\/(session:[A-Za-z0-9_-]{24})(?:\s+[\u2014-]\s+([^\]\n]{1,80}))?\]/g;
68
+ var fragment = document.createDocumentFragment();
69
+ var offset = 0;
70
+ var match;
71
+ while ((match = pattern.exec(text))) {
72
+ fragment.appendChild(document.createTextNode(text.slice(offset, match.index)));
73
+ var button = document.createElement("button");
74
+ button.type = "button";
75
+ button.className = "clayos-session-link";
76
+ button.dataset.sessionRef = match[1];
77
+ button.setAttribute("aria-label", "Open session" + (match[2] ? " from " + match[2].trim() : ""));
78
+ var label = document.createElement("span");
79
+ label.textContent = "Session";
80
+ button.appendChild(label);
81
+ if (match[2]) {
82
+ var meta = document.createElement("small");
83
+ meta.textContent = match[2].trim();
84
+ button.appendChild(meta);
85
+ }
86
+ fragment.appendChild(button);
87
+ offset = pattern.lastIndex;
88
+ }
89
+ fragment.appendChild(document.createTextNode(text.slice(offset)));
90
+ nodes[i].parentNode.replaceChild(fragment, nodes[i]);
91
+ }
92
+ }
93
+
46
94
  /**
47
95
  * Parse Unicode emojis inside an element into Twemoji SVG images.
48
96
  * Used only for UI chrome (icon strip, emoji picker, title bar) where
@@ -0,0 +1,86 @@
1
+ // Shared Mate profile and removal flows used by project and Home surfaces.
2
+
3
+ import { getWs } from './ws-ref.js';
4
+ import { showMateProfilePopover } from './profile.js';
5
+ import { showConfirm } from './app-misc.js';
6
+ import { iconHtml, refreshIcons } from './icons.js';
7
+
8
+ var mateMenu = null;
9
+
10
+ function send(message) {
11
+ var ws = getWs();
12
+ if (ws && ws.readyState === 1) ws.send(JSON.stringify(message));
13
+ }
14
+
15
+ export function editMateProfile(anchorEl, mate) {
16
+ if (!anchorEl || !mate || mate.primary) return;
17
+ showMateProfilePopover(anchorEl, mate, function (updates) {
18
+ send({ type: "mate_update", mateId: mate.id, updates: updates });
19
+ });
20
+ }
21
+
22
+ export function confirmMateRemoval(anchorEl, mate, onRemoved) {
23
+ if (!anchorEl || !mate || mate.primary) return;
24
+ showConfirm(
25
+ mate.builtinKey ? "Remove this mate? You can restore it from the home switcher." : "Delete this mate permanently?",
26
+ function () {
27
+ if (onRemoved) onRemoved();
28
+ send({ type: "mate_delete", mateId: mate.id });
29
+ },
30
+ mate.builtinKey ? "Remove" : "Delete",
31
+ true
32
+ );
33
+ }
34
+
35
+ export function closeMateManagementMenu() {
36
+ if (mateMenu) mateMenu.remove();
37
+ mateMenu = null;
38
+ document.removeEventListener("click", handleMateMenuOutside, true);
39
+ }
40
+
41
+ function handleMateMenuOutside(event) {
42
+ if (mateMenu && !mateMenu.contains(event.target)) closeMateManagementMenu();
43
+ }
44
+
45
+ export function showMateManagementMenu(anchorEl, mate, options) {
46
+ if (!anchorEl || !mate || mate.primary) return;
47
+ options = options || {};
48
+ closeMateManagementMenu();
49
+ if (options.beforeOpen) options.beforeOpen();
50
+ var menu = document.createElement("div");
51
+ menu.className = "project-ctx-menu";
52
+ var edit = document.createElement("button");
53
+ edit.className = "project-ctx-item";
54
+ edit.innerHTML = iconHtml("edit-2") + " <span>Edit Profile</span>";
55
+ edit.addEventListener("click", function (event) {
56
+ event.stopPropagation();
57
+ closeMateManagementMenu();
58
+ editMateProfile(anchorEl, mate);
59
+ });
60
+ var remove = document.createElement("button");
61
+ remove.className = "project-ctx-item project-ctx-delete";
62
+ remove.innerHTML = iconHtml(mate.builtinKey ? "minus-circle" : "trash-2")
63
+ + " <span>" + (mate.builtinKey ? "Remove mate" : "Delete mate") + "</span>";
64
+ remove.addEventListener("click", function (event) {
65
+ event.stopPropagation();
66
+ closeMateManagementMenu();
67
+ confirmMateRemoval(anchorEl, mate, options.onRemoved);
68
+ });
69
+ menu.appendChild(edit);
70
+ menu.appendChild(remove);
71
+ document.body.appendChild(menu);
72
+ mateMenu = menu;
73
+ refreshIcons();
74
+ requestAnimationFrame(function () {
75
+ var rect = anchorEl.getBoundingClientRect();
76
+ menu.style.position = "fixed";
77
+ menu.style.left = (rect.right + 6) + "px";
78
+ menu.style.top = rect.top + "px";
79
+ var menuRect = menu.getBoundingClientRect();
80
+ if (menuRect.right > window.innerWidth - 8) menu.style.left = (rect.left - menuRect.width - 6) + "px";
81
+ if (menuRect.bottom > window.innerHeight - 8) menu.style.top = (window.innerHeight - menuRect.height - 8) + "px";
82
+ });
83
+ setTimeout(function () {
84
+ document.addEventListener("click", handleMateMenuOutside, true);
85
+ }, 0);
86
+ }
@@ -102,13 +102,6 @@ export function initMateWizard(sendWs, onMateCreated) {
102
102
  }
103
103
 
104
104
  export function openMateWizard() {
105
- // Defensive: if the user has turned off the Mates UI in User Settings,
106
- // do not open the wizard even if it gets fired programmatically (e.g.
107
- // via a stale event handler). The CSS gate hides the entry point but
108
- // we still short-circuit here so JS calls become no-ops.
109
- if (typeof document !== "undefined" && document.body && document.body.classList.contains("mates-disabled")) {
110
- return;
111
- }
112
105
  mateWizardStep = 0;
113
106
  mateWizardData = {
114
107
  relationship: null,