clay-server 3.8.1 → 4.0.0-beta.2

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 (189) 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-connection.js +4 -0
  16. package/lib/project-debate-proposal.js +133 -17
  17. package/lib/project-debate.js +214 -110
  18. package/lib/project-delegated-follow-up.js +84 -0
  19. package/lib/project-delegated-session.js +53 -0
  20. package/lib/project-filesystem.js +7 -9
  21. package/lib/project-knowledge.js +1 -0
  22. package/lib/project-mate-creation-proposal.js +120 -0
  23. package/lib/project-memory.js +14 -7
  24. package/lib/project-models.js +157 -32
  25. package/lib/project-session-notes.js +2 -2
  26. package/lib/project-session-pair.js +8 -8
  27. package/lib/project-sessions.js +66 -51
  28. package/lib/project-vendor-login.js +397 -0
  29. package/lib/project-worker-proposal.js +25 -25
  30. package/lib/project-workspace-query.js +144 -0
  31. package/lib/project.js +349 -102
  32. package/lib/public/app.js +85 -42
  33. package/lib/public/css/avatar-imprints.css +0 -1
  34. package/lib/public/css/base.css +4 -0
  35. package/lib/public/css/capsule-ui.css +389 -0
  36. package/lib/public/css/command-palette.css +306 -6
  37. package/lib/public/css/home-capsule-library.css +243 -0
  38. package/lib/public/css/home-capsule-source.css +192 -0
  39. package/lib/public/css/home-debate-live.css +146 -0
  40. package/lib/public/css/home-debate-planning.css +125 -0
  41. package/lib/public/css/home-debates-archive.css +281 -0
  42. package/lib/public/css/home-hub.css +896 -405
  43. package/lib/public/css/home-mate-creation.css +34 -0
  44. package/lib/public/css/home-mate-model-picker.css +131 -0
  45. package/lib/public/css/home-mate-settings.css +344 -0
  46. package/lib/public/css/home-session-actions.css +265 -0
  47. package/lib/public/css/home-sidebar.css +667 -0
  48. package/lib/public/css/icon-strip.css +1 -2
  49. package/lib/public/css/mates.css +7 -34
  50. package/lib/public/css/pane.css +1 -1
  51. package/lib/public/css/playbook.css +0 -80
  52. package/lib/public/css/sticky-notes.css +53 -117
  53. package/lib/public/css/tui-attention.css +0 -44
  54. package/lib/public/css/workspace-assignment.css +45 -0
  55. package/lib/public/fonts/source-serif-4/LICENSE.md +93 -0
  56. package/lib/public/fonts/source-serif-4/SourceSerif4Caption-Semibold.ttf.woff2 +0 -0
  57. package/lib/public/index.html +93 -74
  58. package/lib/public/modules/app-connection.js +10 -1
  59. package/lib/public/modules/app-dm.js +43 -160
  60. package/lib/public/modules/app-home-hub.js +328 -585
  61. package/lib/public/modules/app-message-cards.js +242 -0
  62. package/lib/public/modules/app-message-router.js +84 -0
  63. package/lib/public/modules/app-messages.js +102 -37
  64. package/lib/public/modules/app-notifications.js +8 -119
  65. package/lib/public/modules/app-projects.js +9 -1
  66. package/lib/public/modules/app-rendering.js +21 -602
  67. package/lib/public/modules/avatar-imprint.js +11 -8
  68. package/lib/public/modules/chat-bubble-renderer.js +152 -0
  69. package/lib/public/modules/chat-render-runtime.js +198 -0
  70. package/lib/public/modules/command-palette.js +225 -436
  71. package/lib/public/modules/dm-render.js +82 -0
  72. package/lib/public/modules/home-capsule-creation-intent.js +31 -0
  73. package/lib/public/modules/home-capsule-library.js +146 -0
  74. package/lib/public/modules/home-capsule-source.js +383 -0
  75. package/lib/public/modules/home-chat-identity.js +35 -0
  76. package/lib/public/modules/home-chat-scroll.js +77 -0
  77. package/lib/public/modules/home-chat-stream-state.js +34 -0
  78. package/lib/public/modules/home-composer-focus.js +25 -0
  79. package/lib/public/modules/home-conversations-sheet.js +179 -0
  80. package/lib/public/modules/home-debate-activity.js +48 -0
  81. package/lib/public/modules/home-debate-controls.js +295 -0
  82. package/lib/public/modules/home-debate-launch.js +41 -0
  83. package/lib/public/modules/home-debate-live.js +284 -0
  84. package/lib/public/modules/home-debate-models.js +73 -0
  85. package/lib/public/modules/home-debate-planning.js +335 -0
  86. package/lib/public/modules/home-debates-archive.js +279 -0
  87. package/lib/public/modules/home-dock-resize.js +74 -0
  88. package/lib/public/modules/home-dock.js +446 -0
  89. package/lib/public/modules/home-mate-chat.js +496 -0
  90. package/lib/public/modules/home-mate-creation.js +109 -0
  91. package/lib/public/modules/home-mate-model-picker.js +281 -0
  92. package/lib/public/modules/home-mate-selection.js +38 -0
  93. package/lib/public/modules/home-mate-settings-menu.js +131 -0
  94. package/lib/public/modules/home-mate-settings.js +383 -0
  95. package/lib/public/modules/home-session-actions.js +373 -0
  96. package/lib/public/modules/home-session-model-confirmation.js +13 -0
  97. package/lib/public/modules/home-shell.js +9 -0
  98. package/lib/public/modules/home-sidebar-chat-list.js +63 -0
  99. package/lib/public/modules/home-sidebar.js +158 -0
  100. package/lib/public/modules/home-sub-surface.js +19 -0
  101. package/lib/public/modules/home-surface-boot.js +68 -0
  102. package/lib/public/modules/home-surface.js +141 -0
  103. package/lib/public/modules/home-tools.js +271 -0
  104. package/lib/public/modules/markdown.js +50 -2
  105. package/lib/public/modules/mate-management.js +86 -0
  106. package/lib/public/modules/mate-wizard.js +0 -7
  107. package/lib/public/modules/mention.js +17 -19
  108. package/lib/public/modules/pane-bridge.js +9 -0
  109. package/lib/public/modules/profile.js +4 -5
  110. package/lib/public/modules/project-settings.js +34 -28
  111. package/lib/public/modules/project-switcher.js +11 -5
  112. package/lib/public/modules/project-workspace-assignment.js +23 -0
  113. package/lib/public/modules/search-clay-chat.js +402 -0
  114. package/lib/public/modules/server-settings.js +14 -12
  115. package/lib/public/modules/session-actions.js +1 -1
  116. package/lib/public/modules/sidebar-mates.js +56 -552
  117. package/lib/public/modules/sidebar-mobile.js +7 -64
  118. package/lib/public/modules/sidebar-presence.js +37 -0
  119. package/lib/public/modules/split-pair-ui.js +13 -13
  120. package/lib/public/modules/split-view.js +7 -0
  121. package/lib/public/modules/sticky-notes.js +7 -6
  122. package/lib/public/modules/tool-input-composition.js +48 -0
  123. package/lib/public/modules/tool-llm-status.js +167 -0
  124. package/lib/public/modules/tool-renderer-advanced.js +285 -0
  125. package/lib/public/modules/tool-renderer-chart.js +158 -0
  126. package/lib/public/modules/tool-renderer-semantics.js +35 -0
  127. package/lib/public/modules/tool-renderer.js +464 -0
  128. package/lib/public/modules/tool-runtime.js +309 -0
  129. package/lib/public/modules/tool-ui-evaluator.js +97 -0
  130. package/lib/public/modules/tool-ui-tree.js +21 -0
  131. package/lib/public/modules/tui-attention.js +10 -0
  132. package/lib/public/modules/user-settings.js +16 -23
  133. package/lib/public/modules/vendor-login.js +287 -0
  134. package/lib/public/modules/worker-proposal.js +2 -2
  135. package/lib/public/modules/workspace-assignment-card.js +149 -0
  136. package/lib/public/modules/workspace-assignment-routing.js +4 -0
  137. package/lib/public/style.css +13 -2
  138. package/lib/runtime-env.js +65 -0
  139. package/lib/sdk-bridge.js +120 -32
  140. package/lib/sdk-message-processor.js +10 -6
  141. package/lib/server-home-capsule-creation.js +36 -0
  142. package/lib/server-home-chat-events.js +287 -0
  143. package/lib/server-home-chat.js +518 -0
  144. package/lib/server-home-clay-entry.js +65 -0
  145. package/lib/server-home-clay-session-links.js +26 -0
  146. package/lib/server-home-debate-planning.js +214 -0
  147. package/lib/server-home-debates.js +137 -0
  148. package/lib/server-home-mate-creation.js +261 -0
  149. package/lib/server-home-models.js +241 -0
  150. package/lib/server-home-preferences.js +61 -0
  151. package/lib/server-mates.js +37 -0
  152. package/lib/server-tools.js +377 -0
  153. package/lib/server.js +111 -59
  154. package/lib/session-pair-mcp-server.js +6 -6
  155. package/lib/sessions.js +18 -3
  156. package/lib/tool-capsule-source.js +142 -0
  157. package/lib/tool-control-mcp-server.js +126 -0
  158. package/lib/tool-llm.js +48 -0
  159. package/lib/tool-storage.js +77 -0
  160. package/lib/tool-ui-spec-advanced.js +82 -0
  161. package/lib/tool-ui-spec.js +261 -0
  162. package/lib/tools-registry.js +286 -0
  163. package/lib/users-home-dock-preferences.js +77 -0
  164. package/lib/users-home-surface-preferences.js +99 -0
  165. package/lib/users-preferences.js +4 -7
  166. package/lib/users.js +12 -0
  167. package/lib/whats-new-content.js +3 -3
  168. package/lib/workspace-assignment-service.js +420 -0
  169. package/lib/workspace-query-mcp-server.js +154 -0
  170. package/lib/workspace-query-service.js +492 -0
  171. package/lib/ws-schema.js +93 -12
  172. package/lib/yoke/adapters/acp.js +2 -1
  173. package/lib/yoke/adapters/antigravity.js +2 -1
  174. package/lib/yoke/adapters/claude-worker.js +11 -4
  175. package/lib/yoke/adapters/claude.js +73 -43
  176. package/lib/yoke/adapters/codex.js +40 -10
  177. package/lib/yoke/adapters/kiro.js +2 -1
  178. package/lib/yoke/claude-user-input.js +38 -0
  179. package/lib/yoke/codex-app-server.js +20 -2
  180. package/lib/yoke/codex-user-input.js +64 -0
  181. package/lib/yoke/complete-once.js +164 -0
  182. package/lib/yoke/index.js +5 -1
  183. package/lib/yoke/interface.js +19 -0
  184. package/lib/yoke/mcp-bridge-server.js +2 -6
  185. package/lib/yoke/user-input.js +254 -0
  186. package/package.json +3 -2
  187. package/lib/public/css/home-chat.css +0 -380
  188. package/lib/public/modules/home-chat.js +0 -438
  189. package/lib/server-clay-home.js +0 -245
@@ -0,0 +1,82 @@
1
+ // dm-render.js - Shared DOM builders for DM-style messages and typing state.
2
+
3
+ export function buildDmMessage(msg, options) {
4
+ options = options || {};
5
+ var container = options.container || null;
6
+ var timestamp = typeof msg.ts === "number" ? msg.ts : 0;
7
+ var date = new Date(timestamp);
8
+ var timeStr = date.getHours().toString().padStart(2, "0") + ":" + date.getMinutes().toString().padStart(2, "0");
9
+ var previous = container ? container.lastElementChild : null;
10
+ var compact = false;
11
+ if (previous && previous.dataset.from === String(msg.from || "")) {
12
+ var previousTimestamp = parseInt(previous.dataset.ts || "0", 10);
13
+ if (timestamp - previousTimestamp < 300000) compact = true;
14
+ }
15
+
16
+ var row = document.createElement("div");
17
+ row.className = "dm-msg" + (compact ? " dm-msg-compact" : "");
18
+ row.dataset.from = msg.from || "";
19
+ row.dataset.ts = timestamp;
20
+
21
+ if (compact) {
22
+ var hoverTime = document.createElement("span");
23
+ hoverTime.className = "dm-msg-hover-time";
24
+ hoverTime.textContent = timeStr;
25
+ row.appendChild(hoverTime);
26
+
27
+ var compactBody = document.createElement("div");
28
+ compactBody.className = "dm-msg-body";
29
+ compactBody.textContent = msg.text || "";
30
+ row.appendChild(compactBody);
31
+ return row;
32
+ }
33
+
34
+ var avatar = document.createElement("img");
35
+ avatar.className = "dm-msg-avatar";
36
+ avatar.src = options.avatarUrl || "";
37
+ avatar.alt = "";
38
+ row.appendChild(avatar);
39
+
40
+ var content = document.createElement("div");
41
+ content.className = "dm-msg-content";
42
+
43
+ var header = document.createElement("div");
44
+ header.className = "dm-msg-header";
45
+
46
+ var name = document.createElement("span");
47
+ name.className = "dm-msg-name";
48
+ name.textContent = options.displayName || (options.isMe ? "Me" : "User");
49
+ header.appendChild(name);
50
+
51
+ var time = document.createElement("span");
52
+ time.className = "dm-msg-time";
53
+ time.textContent = timeStr;
54
+ header.appendChild(time);
55
+ content.appendChild(header);
56
+
57
+ var body = document.createElement("div");
58
+ body.className = "dm-msg-body";
59
+ body.textContent = msg.text || "";
60
+ content.appendChild(body);
61
+ row.appendChild(content);
62
+ return row;
63
+ }
64
+
65
+ export function buildDmTypingIndicator(options) {
66
+ options = options || {};
67
+ var row = document.createElement("div");
68
+ if (options.id) row.id = options.id;
69
+ row.className = "dm-msg dm-typing-indicator";
70
+
71
+ var avatar = document.createElement("img");
72
+ avatar.className = "dm-msg-avatar";
73
+ avatar.src = options.avatarUrl || "";
74
+ avatar.alt = "";
75
+ row.appendChild(avatar);
76
+
77
+ var dots = document.createElement("div");
78
+ dots.className = "dm-typing-dots";
79
+ dots.innerHTML = "<span></span><span></span><span></span>";
80
+ row.appendChild(dots);
81
+ return row;
82
+ }
@@ -0,0 +1,31 @@
1
+ // Mate-neutral Capsule creation entry contract for Home surfaces.
2
+
3
+ import { store } from './store.js';
4
+ import { findHomeMate } from './home-mate-selection.js';
5
+
6
+ export var HOME_CAPSULE_CREATION_EVENT = "clay:home-capsule-create";
7
+
8
+ export function normalizeCapsuleDescription(value) {
9
+ if (typeof value !== "string") return "";
10
+ return value.replace(/[\u0000-\u0008\u000b\u000c\u000e-\u001f\u007f]/g, " ").trim().slice(0, 4000);
11
+ }
12
+
13
+ export function resolveCapsuleCreationMate(mates, currentMateId) {
14
+ var visible = (Array.isArray(mates) ? mates : []).filter(function (mate) {
15
+ return !!mate && !mate.archived;
16
+ });
17
+ return findHomeMate(visible, currentMateId);
18
+ }
19
+
20
+ export function requestHomeCapsuleCreation(value) {
21
+ var description = normalizeCapsuleDescription(value);
22
+ if (!description || !store.get('homeSurfaceLoaded')) return false;
23
+ var mate = resolveCapsuleCreationMate(
24
+ store.get('cachedMatesList') || [],
25
+ store.get('homeChatMateId')
26
+ );
27
+ if (!mate) return false;
28
+ var detail = { mateId: mate.id, description: description, accepted: false };
29
+ window.dispatchEvent(new CustomEvent(HOME_CAPSULE_CREATION_EVENT, { detail: detail }));
30
+ return detail.accepted === true;
31
+ }
@@ -0,0 +1,146 @@
1
+ // Native host surface for browsing the user's installed Capsules.
2
+
3
+ import { iconHtml } from './icons.js';
4
+ import { requestHomeCapsuleCreation } from './home-capsule-creation-intent.js';
5
+
6
+ function installedCapsules(definitions) {
7
+ return (definitions || []).filter(function (definition) {
8
+ return !!definition && !!definition.manifest && !!definition.manifest.id;
9
+ }).sort(function (a, b) {
10
+ return String(a.manifest.name || a.manifest.id).localeCompare(String(b.manifest.name || b.manifest.id));
11
+ });
12
+ }
13
+
14
+ function submitCreation(input, status) {
15
+ var description = input.value.trim();
16
+ if (!description) return;
17
+ status.textContent = "";
18
+ if (requestHomeCapsuleCreation(description)) return;
19
+ status.textContent = "Your Mate is not ready yet. Try again in a moment.";
20
+ }
21
+
22
+ function createComposer() {
23
+ var form = document.createElement("form");
24
+ form.className = "home-capsule-library-create";
25
+ var label = document.createElement("label");
26
+ label.className = "home-capsule-library-create-label";
27
+ label.htmlFor = "home-capsule-library-prompt";
28
+ label.textContent = "Create with Mate";
29
+ form.appendChild(label);
30
+ var guidance = document.createElement("p");
31
+ guidance.id = "home-capsule-library-create-guidance";
32
+ guidance.className = "home-capsule-library-create-guidance";
33
+ guidance.textContent = "Describe what should stay useful beyond this conversation. Your current Mate will open a new conversation and shape it with you.";
34
+ form.appendChild(guidance);
35
+ var frame = document.createElement("div");
36
+ frame.className = "home-capsule-library-create-frame";
37
+ var input = document.createElement("textarea");
38
+ input.id = "home-capsule-library-prompt";
39
+ input.className = "home-capsule-library-create-input";
40
+ input.rows = 2;
41
+ input.maxLength = 4000;
42
+ input.placeholder = "Describe the interface you need";
43
+ input.setAttribute("aria-describedby", "home-capsule-library-create-guidance home-capsule-library-create-status");
44
+ frame.appendChild(input);
45
+ var submit = document.createElement("button");
46
+ submit.type = "submit";
47
+ submit.className = "home-capsule-library-create-submit";
48
+ submit.setAttribute("aria-label", "Start a Capsule conversation with your Mate");
49
+ submit.innerHTML = iconHtml("arrow-up");
50
+ frame.appendChild(submit);
51
+ form.appendChild(frame);
52
+ var status = document.createElement("p");
53
+ status.id = "home-capsule-library-create-status";
54
+ status.className = "home-capsule-library-create-status";
55
+ status.setAttribute("role", "status");
56
+ form.appendChild(status);
57
+ form.addEventListener("submit", function (event) {
58
+ event.preventDefault();
59
+ submitCreation(input, status);
60
+ });
61
+ input.addEventListener("keydown", function (event) {
62
+ if (event.key !== "Enter" || event.shiftKey || event.isComposing) return;
63
+ event.preventDefault();
64
+ submitCreation(input, status);
65
+ });
66
+ return form;
67
+ }
68
+
69
+ export function renderHomeCapsuleLibrary(container, definitions, openCapsule) {
70
+ var root = document.createElement("section");
71
+ root.className = "home-capsule-library";
72
+ root.setAttribute("aria-labelledby", "home-capsule-library-title");
73
+
74
+ var intro = document.createElement("header");
75
+ intro.className = "home-capsule-library-intro";
76
+ var title = document.createElement("h2");
77
+ title.id = "home-capsule-library-title";
78
+ title.tabIndex = -1;
79
+ title.textContent = "Capsule Home";
80
+ intro.appendChild(title);
81
+ var description = document.createElement("p");
82
+ description.textContent = "Turn a recurring need into something you can open, use, and keep.";
83
+ intro.appendChild(description);
84
+ root.appendChild(intro);
85
+ var definition = document.createElement("section");
86
+ definition.className = "home-capsule-library-definition";
87
+ definition.setAttribute("aria-labelledby", "home-capsule-library-definition-title");
88
+ var definitionTitle = document.createElement("h3");
89
+ definitionTitle.id = "home-capsule-library-definition-title";
90
+ definitionTitle.textContent = "What is a Capsule?";
91
+ definition.appendChild(definitionTitle);
92
+ var definitionText = document.createElement("p");
93
+ definitionText.textContent = "A Capsule is a small, persistent app that a Mate creates with you. It keeps its interface and data beyond the conversation, and every Mate can help you use or improve it.";
94
+ definition.appendChild(definitionText);
95
+ root.appendChild(definition);
96
+ root.appendChild(createComposer());
97
+
98
+ var capsules = installedCapsules(definitions);
99
+ if (!capsules.length) { container.appendChild(root); return; }
100
+
101
+ var inventory = document.createElement("section");
102
+ inventory.className = "home-capsule-library-inventory";
103
+ inventory.setAttribute("aria-labelledby", "home-capsule-library-installed-title");
104
+ var inventoryTitle = document.createElement("h3");
105
+ inventoryTitle.id = "home-capsule-library-installed-title";
106
+ inventoryTitle.textContent = "Installed Capsules";
107
+ inventory.appendChild(inventoryTitle);
108
+ var list = document.createElement("ul");
109
+ list.className = "home-capsule-library-list";
110
+ for (var i = 0; i < capsules.length; i++) {
111
+ (function (definition) {
112
+ var manifest = definition.manifest;
113
+ var item = document.createElement("li");
114
+ item.className = "home-capsule-library-item";
115
+ var row = document.createElement("button");
116
+ row.type = "button";
117
+ row.className = "home-capsule-library-row";
118
+ row.setAttribute("aria-label", "Open " + (manifest.name || manifest.id) + " Capsule");
119
+ var icon = document.createElement("span");
120
+ icon.className = "home-capsule-library-icon";
121
+ icon.innerHTML = iconHtml(manifest.lucideIcon || "box");
122
+ row.appendChild(icon);
123
+ var copy = document.createElement("span");
124
+ copy.className = "home-capsule-library-copy";
125
+ var name = document.createElement("span");
126
+ name.className = "home-capsule-library-name";
127
+ name.textContent = manifest.name || manifest.id;
128
+ copy.appendChild(name);
129
+ var hint = document.createElement("span");
130
+ hint.className = "home-capsule-library-hint";
131
+ hint.textContent = "Open Capsule";
132
+ copy.appendChild(hint);
133
+ row.appendChild(copy);
134
+ var arrow = document.createElement("span");
135
+ arrow.className = "home-capsule-library-arrow";
136
+ arrow.innerHTML = iconHtml("arrow-up-right");
137
+ row.appendChild(arrow);
138
+ row.addEventListener("click", function () { openCapsule(manifest.id); });
139
+ item.appendChild(row);
140
+ list.appendChild(item);
141
+ })(capsules[i]);
142
+ }
143
+ inventory.appendChild(list);
144
+ root.appendChild(inventory);
145
+ container.appendChild(root);
146
+ }
@@ -0,0 +1,383 @@
1
+ import { getWs } from './ws-ref.js';
2
+ import { iconHtml, refreshIcons } from './icons.js';
3
+
4
+ var hosts = Object.create(null);
5
+ var sequence = 0;
6
+
7
+ function send(message) {
8
+ var ws = getWs();
9
+ if (!ws || ws.readyState !== 1) return false;
10
+ ws.send(JSON.stringify(message));
11
+ return true;
12
+ }
13
+
14
+ function nextRequestId(prefix) {
15
+ sequence += 1;
16
+ return prefix + "_" + Date.now() + "_" + sequence;
17
+ }
18
+
19
+ function setRuntimeVisible(host, visible) {
20
+ var surfaces = host.root.querySelectorAll("[data-capsule-runtime-surface]");
21
+ for (var i = 0; i < surfaces.length; i++) surfaces[i].hidden = !visible;
22
+ }
23
+
24
+ function setAccessStatus(host, text, error) {
25
+ if (!host.accessStatus) return;
26
+ host.accessStatus.textContent = text || "";
27
+ host.accessStatus.classList.toggle("is-error", error === true);
28
+ }
29
+
30
+ function syncAccess(host, metadata) {
31
+ if (!host.access) return;
32
+ var allowed = host.definition.manifest.runtime === "worker" && !!(metadata && metadata.mateEditingAllowed === true);
33
+ host.definition.metadata = Object.assign({}, host.definition.metadata || {}, metadata || {}, { mateEditingAllowed: allowed });
34
+ host.accessRequestId = null;
35
+ host.access.disabled = false;
36
+ host.access.setAttribute("aria-disabled", "false");
37
+ host.access.setAttribute("aria-checked", allowed ? "true" : "false");
38
+ host.access.classList.remove("is-saving");
39
+ setAccessStatus(host, allowed ? "Mates can inspect and propose source edits." : "Mates cannot inspect or propose source edits.", false);
40
+ }
41
+
42
+ function menuItems(host) {
43
+ if (!host.menu) return [];
44
+ var items = [];
45
+ for (var i = 0; i < host.menu.children.length; i++) {
46
+ var role = host.menu.children[i].getAttribute("role");
47
+ if ((role === "menuitem" || role === "menuitemcheckbox") && !host.menu.children[i].disabled) items.push(host.menu.children[i]);
48
+ }
49
+ return items;
50
+ }
51
+
52
+ function focusAccess(host) {
53
+ if (host.access && !host.menu.hidden && host.access.isConnected) host.access.focus({ preventScroll: true });
54
+ }
55
+
56
+ function removeMenuDocumentHandler(host) {
57
+ if (!host.outsideMenuHandler) return;
58
+ document.removeEventListener("pointerdown", host.outsideMenuHandler, true);
59
+ host.outsideMenuHandler = null;
60
+ }
61
+
62
+ function closeActionsMenu(host, returnFocus) {
63
+ if (!host || !host.menu) return;
64
+ host.menu.hidden = true;
65
+ host.actionsButton.setAttribute("aria-expanded", "false");
66
+ removeMenuDocumentHandler(host);
67
+ if (returnFocus && host.actionsButton.isConnected) host.actionsButton.focus({ preventScroll: true });
68
+ }
69
+
70
+ function openActionsMenu(host, focusFirst) {
71
+ if (!host || !host.menu) return;
72
+ var ids = Object.keys(hosts);
73
+ for (var i = 0; i < ids.length; i++) {
74
+ if (hosts[ids[i]] !== host) closeActionsMenu(hosts[ids[i]], false);
75
+ }
76
+ host.menu.hidden = false;
77
+ host.actionsButton.setAttribute("aria-expanded", "true");
78
+ removeMenuDocumentHandler(host);
79
+ host.outsideMenuHandler = function (event) {
80
+ if (host.toolbar.contains(event.target)) return;
81
+ closeActionsMenu(host, false);
82
+ };
83
+ document.addEventListener("pointerdown", host.outsideMenuHandler, true);
84
+ if (focusFirst) {
85
+ var items = menuItems(host);
86
+ if (items.length) items[0].focus({ preventScroll: true });
87
+ }
88
+ }
89
+
90
+ function toggleActionsMenu(host) {
91
+ if (host.menu.hidden) openActionsMenu(host, true);
92
+ else closeActionsMenu(host, true);
93
+ }
94
+
95
+ function closeInspector(host) {
96
+ if (!host || !host.inspector) return;
97
+ host.inspector.remove();
98
+ host.inspector = null;
99
+ host.sourceRequestId = null;
100
+ setRuntimeVisible(host, true);
101
+ if (host.actionsButton && host.actionsButton.isConnected) host.actionsButton.focus({ preventScroll: true });
102
+ }
103
+
104
+ function createTab(label, index, host) {
105
+ var tab = document.createElement("button");
106
+ tab.type = "button";
107
+ tab.className = "home-capsule-source-tab";
108
+ tab.id = "home-capsule-source-tab-" + host.toolId + "-" + index;
109
+ tab.setAttribute("role", "tab");
110
+ tab.dataset.sourceIndex = String(index);
111
+ tab.textContent = label;
112
+ return tab;
113
+ }
114
+
115
+ export function highlightCapsuleSource(code, language, text) {
116
+ code.className = "home-capsule-source-code language-" + language;
117
+ code.removeAttribute("data-highlighted");
118
+ code.textContent = text;
119
+ var highlighter = typeof window !== "undefined" ? window.hljs : null;
120
+ if (!highlighter || typeof highlighter.highlightElement !== "function") return false;
121
+ if (typeof highlighter.getLanguage === "function" && !highlighter.getLanguage(language)) return false;
122
+ try {
123
+ highlighter.highlightElement(code);
124
+ return true;
125
+ } catch (error) {
126
+ code.className = "home-capsule-source-code language-" + language;
127
+ code.removeAttribute("data-highlighted");
128
+ code.textContent = text;
129
+ return false;
130
+ }
131
+ }
132
+
133
+ function showSourcePanel(host, index) {
134
+ var tabs = host.inspector.querySelectorAll('[role="tab"]');
135
+ for (var i = 0; i < tabs.length; i++) {
136
+ var selected = Number(tabs[i].dataset.sourceIndex) === index;
137
+ tabs[i].setAttribute("aria-selected", selected ? "true" : "false");
138
+ tabs[i].tabIndex = selected ? 0 : -1;
139
+ }
140
+ var source = host.source;
141
+ var label = index === 0 ? "Manifest" : (index === 1 ? "UI" : "Logic");
142
+ var language = index === 2 ? "javascript" : "json";
143
+ var text;
144
+ host.code.setAttribute("aria-label", label + " source");
145
+ if (index === 0) text = JSON.stringify(source.manifest, null, 2);
146
+ else if (index === 1) text = JSON.stringify(source.uiTree, null, 2);
147
+ else if (source.logicAvailable) text = source.logicSource || "";
148
+ else text = "Logic is server-managed and is not available as authored Capsule source.";
149
+ highlightCapsuleSource(host.code, language, text);
150
+ }
151
+
152
+ function renderSource(host, source) {
153
+ host.source = source;
154
+ host.inspector.classList.remove("is-loading");
155
+ host.loading.remove();
156
+ var tablist = document.createElement("div");
157
+ tablist.className = "home-capsule-source-tabs";
158
+ tablist.setAttribute("role", "tablist");
159
+ tablist.setAttribute("aria-label", "Capsule source files");
160
+ var labels = ["Manifest", "UI", "Logic"];
161
+ for (var i = 0; i < labels.length; i++) {
162
+ var tab = createTab(labels[i], i, host);
163
+ tab.addEventListener("click", function (event) {
164
+ showSourcePanel(host, Number(event.currentTarget.dataset.sourceIndex));
165
+ });
166
+ tablist.appendChild(tab);
167
+ }
168
+ var code = document.createElement("pre");
169
+ code.className = "home-capsule-source-code";
170
+ code.setAttribute("role", "region");
171
+ code.tabIndex = 0;
172
+ host.code = code;
173
+ host.inspector.appendChild(tablist);
174
+ host.inspector.appendChild(code);
175
+ showSourcePanel(host, 0);
176
+ tablist.firstElementChild.focus({ preventScroll: true });
177
+ }
178
+
179
+ function openInspector(host) {
180
+ if (host.inspector) return;
181
+ closeActionsMenu(host, false);
182
+ var inspector = document.createElement("section");
183
+ inspector.className = "home-capsule-source-inspector is-loading";
184
+ inspector.setAttribute("aria-label", host.definition.manifest.name + " source inspector");
185
+ var header = document.createElement("header");
186
+ header.className = "home-capsule-source-header";
187
+ var back = document.createElement("button");
188
+ back.type = "button";
189
+ back.className = "home-capsule-source-back";
190
+ back.setAttribute("aria-label", "Return to Capsule");
191
+ back.innerHTML = iconHtml("arrow-left") + '<span>Capsule</span>';
192
+ back.addEventListener("click", function () { closeInspector(host); });
193
+ header.appendChild(back);
194
+ var title = document.createElement("div");
195
+ title.className = "home-capsule-source-title";
196
+ title.textContent = "Source · " + host.definition.manifest.name;
197
+ header.appendChild(title);
198
+ inspector.appendChild(header);
199
+ var loading = document.createElement("p");
200
+ loading.className = "home-capsule-source-loading";
201
+ loading.textContent = "Loading source…";
202
+ inspector.appendChild(loading);
203
+ inspector.addEventListener("keydown", function (event) {
204
+ if (event.key === "Escape") {
205
+ event.preventDefault();
206
+ event.stopPropagation();
207
+ closeInspector(host);
208
+ return;
209
+ }
210
+ if (!event.target || event.target.getAttribute("role") !== "tab") return;
211
+ var tabs = inspector.querySelectorAll('[role="tab"]');
212
+ var current = Number(event.target.dataset.sourceIndex);
213
+ var next = current;
214
+ if (event.key === "ArrowRight") next = (current + 1) % tabs.length;
215
+ else if (event.key === "ArrowLeft") next = (current + tabs.length - 1) % tabs.length;
216
+ else if (event.key === "Home") next = 0;
217
+ else if (event.key === "End") next = tabs.length - 1;
218
+ else return;
219
+ event.preventDefault();
220
+ showSourcePanel(host, next);
221
+ tabs[next].focus({ preventScroll: true });
222
+ });
223
+ host.inspector = inspector;
224
+ host.loading = loading;
225
+ host.root.appendChild(inspector);
226
+ setRuntimeVisible(host, false);
227
+ back.focus({ preventScroll: true });
228
+ var requestId = nextRequestId("tool_source");
229
+ host.sourceRequestId = requestId;
230
+ if (!send({ type: "tool_source_get", toolId: host.toolId, requestId: requestId })) {
231
+ loading.textContent = "Source is unavailable while Clay is disconnected.";
232
+ inspector.classList.remove("is-loading");
233
+ }
234
+ refreshIcons();
235
+ }
236
+
237
+ export function mountCapsuleHostControls(toolId, definition, chromeRoot, contentRoot) {
238
+ var toolbar = document.createElement("section");
239
+ toolbar.className = "home-capsule-chrome-controls";
240
+ toolbar.setAttribute("aria-label", definition.manifest.name + " Capsule controls");
241
+ var host = { toolId: toolId, definition: definition, root: contentRoot, toolbar: toolbar, actionsButton: null, menu: null, outsideMenuHandler: null, access: null, accessStatus: null, accessRequestId: null, inspector: null, sourceRequestId: null };
242
+ hosts[toolId] = host;
243
+ var actionsButton = document.createElement("button");
244
+ actionsButton.type = "button";
245
+ actionsButton.className = "home-capsule-actions-trigger";
246
+ actionsButton.setAttribute("aria-label", definition.manifest.name + " Capsule actions");
247
+ actionsButton.setAttribute("aria-haspopup", "menu");
248
+ actionsButton.setAttribute("aria-expanded", "false");
249
+ actionsButton.title = "Capsule actions";
250
+ actionsButton.innerHTML = iconHtml("ellipsis");
251
+ toolbar.appendChild(actionsButton);
252
+ var menu = document.createElement("div");
253
+ menu.className = "home-capsule-actions-menu";
254
+ menu.setAttribute("role", "menu");
255
+ menu.setAttribute("aria-label", definition.manifest.name + " Capsule actions");
256
+ menu.hidden = true;
257
+ var sourceItem = document.createElement("button");
258
+ sourceItem.type = "button";
259
+ sourceItem.className = "home-capsule-actions-item";
260
+ sourceItem.setAttribute("role", "menuitem");
261
+ sourceItem.tabIndex = -1;
262
+ sourceItem.innerHTML = iconHtml("file-code-2") + "<span>View source</span>";
263
+ sourceItem.addEventListener("click", function () { openInspector(host); });
264
+ menu.appendChild(sourceItem);
265
+ if (definition.manifest.runtime === "worker") {
266
+ var access = document.createElement("button");
267
+ access.type = "button";
268
+ access.className = "home-capsule-actions-item home-capsule-access-item";
269
+ access.setAttribute("role", "menuitemcheckbox");
270
+ access.setAttribute("aria-checked", "false");
271
+ access.setAttribute("aria-disabled", "false");
272
+ access.tabIndex = -1;
273
+ var check = document.createElement("span");
274
+ check.className = "home-capsule-access-check";
275
+ check.setAttribute("aria-hidden", "true");
276
+ check.textContent = "✓";
277
+ access.appendChild(check);
278
+ var copy = document.createElement("span");
279
+ copy.className = "home-capsule-access-copy";
280
+ var name = document.createElement("span");
281
+ name.className = "home-capsule-access-name";
282
+ name.textContent = "Allow Mate editing";
283
+ copy.appendChild(name);
284
+ var status = document.createElement("span");
285
+ status.className = "home-capsule-access-status";
286
+ status.setAttribute("aria-live", "polite");
287
+ copy.appendChild(status);
288
+ access.appendChild(copy);
289
+ menu.appendChild(access);
290
+ host.access = access;
291
+ host.accessStatus = status;
292
+ syncAccess(host, definition.metadata);
293
+ access.addEventListener("click", function () {
294
+ if (access.disabled) return;
295
+ var confirmed = !!(host.definition.metadata && host.definition.metadata.mateEditingAllowed);
296
+ var requested = !confirmed;
297
+ access.disabled = true;
298
+ access.setAttribute("aria-disabled", "true");
299
+ access.classList.add("is-saving");
300
+ setAccessStatus(host, "Saving permission…", false);
301
+ host.accessRequestId = nextRequestId("tool_access");
302
+ if (!send({ type: "tool_mate_access_set", toolId: toolId, allowed: requested, requestId: host.accessRequestId })) {
303
+ host.accessRequestId = null;
304
+ access.disabled = false;
305
+ access.setAttribute("aria-disabled", "false");
306
+ access.classList.remove("is-saving");
307
+ setAccessStatus(host, "Could not save while Clay is disconnected. Try again.", true);
308
+ focusAccess(host);
309
+ }
310
+ });
311
+ }
312
+ toolbar.appendChild(menu);
313
+ host.actionsButton = actionsButton;
314
+ host.menu = menu;
315
+ actionsButton.addEventListener("click", function () { toggleActionsMenu(host); });
316
+ actionsButton.addEventListener("keydown", function (event) {
317
+ if (event.key !== "ArrowDown") return;
318
+ event.preventDefault();
319
+ openActionsMenu(host, true);
320
+ });
321
+ menu.addEventListener("keydown", function (event) {
322
+ var items = menuItems(host);
323
+ if (!items.length) return;
324
+ var current = Array.prototype.indexOf.call(items, event.target);
325
+ var next = current;
326
+ if (event.key === "ArrowDown") next = (current + 1) % items.length;
327
+ else if (event.key === "ArrowUp") next = (current + items.length - 1) % items.length;
328
+ else if (event.key === "Home") next = 0;
329
+ else if (event.key === "End") next = items.length - 1;
330
+ else if (event.key === "Escape") {
331
+ event.preventDefault();
332
+ event.stopPropagation();
333
+ closeActionsMenu(host, true);
334
+ return;
335
+ } else return;
336
+ event.preventDefault();
337
+ items[next].focus({ preventScroll: true });
338
+ });
339
+ chromeRoot.appendChild(toolbar);
340
+ refreshIcons();
341
+ return toolbar;
342
+ }
343
+
344
+ export function disposeCapsuleHostControls(toolId) {
345
+ var host = hosts[toolId];
346
+ if (!host) return;
347
+ closeActionsMenu(host, false);
348
+ if (host.inspector) {
349
+ host.inspector.remove();
350
+ host.inspector = null;
351
+ setRuntimeVisible(host, true);
352
+ }
353
+ delete hosts[toolId];
354
+ }
355
+
356
+ export function handleToolSourceState(msg) {
357
+ var host = hosts[msg.toolId];
358
+ if (!host || !host.inspector || host.sourceRequestId !== msg.requestId) return;
359
+ if (!msg.ok) {
360
+ host.loading.textContent = msg.error || "Could not read Capsule source.";
361
+ host.inspector.classList.remove("is-loading");
362
+ return;
363
+ }
364
+ renderSource(host, msg);
365
+ }
366
+
367
+ export function handleToolMateAccessState(msg) {
368
+ var host = hosts[msg.toolId];
369
+ if (!host || !host.access) return;
370
+ if (!msg.ok) {
371
+ if (!host.accessRequestId || msg.requestId !== host.accessRequestId) return;
372
+ host.accessRequestId = null;
373
+ host.access.disabled = false;
374
+ host.access.setAttribute("aria-disabled", "false");
375
+ host.access.classList.remove("is-saving");
376
+ setAccessStatus(host, msg.error || "Could not update Mate editing permission.", true);
377
+ focusAccess(host);
378
+ return;
379
+ }
380
+ if (host.accessRequestId && msg.requestId && msg.requestId !== host.accessRequestId) return;
381
+ syncAccess(host, msg.metadata);
382
+ focusAccess(host);
383
+ }
@@ -0,0 +1,35 @@
1
+ // home-chat-identity.js - Stable speaker identity for ordinary Home conversations.
2
+ import { mateAvatarUrl } from './avatar.js';
3
+ import { createAssistantBubble, createUserBubble } from './chat-bubble-renderer.js';
4
+
5
+ export function createHomeOrdinaryBubble(message, mate, mateName, time) {
6
+ var isUser = message && message.role === "user";
7
+ var name = isUser ? "You" : (mateName || "Mate");
8
+ var row = isUser ? createUserBubble({
9
+ text: message.text || "",
10
+ }) : createAssistantBubble({
11
+ name: name,
12
+ avatarUrl: mateAvatarUrl(mate, 34),
13
+ time: time || "",
14
+ });
15
+ row.classList.add("home-chat-message", "home-chat-ordinary-message", isUser ? "home-chat-ordinary-user" : "home-chat-ordinary-mate");
16
+ row.setAttribute("role", "article");
17
+ row.setAttribute("aria-label", "Message from " + name);
18
+ return row;
19
+ }
20
+
21
+ export function createHomeOrdinaryTyping(mate, mateName) {
22
+ var row = createHomeOrdinaryBubble({ role: "assistant", text: "" }, mate, mateName, "");
23
+ row.classList.add("home-chat-ordinary-typing");
24
+ var content = row.querySelector(".md-content");
25
+ if (!content) return row;
26
+ content.className = "home-chat-typing";
27
+ content.setAttribute("role", "status");
28
+ content.setAttribute("aria-label", (mateName || "Mate") + " is responding");
29
+ for (var i = 0; i < 3; i++) {
30
+ var dot = document.createElement("span");
31
+ dot.setAttribute("aria-hidden", "true");
32
+ content.appendChild(dot);
33
+ }
34
+ return row;
35
+ }