clay-server 5.2.0-beta.5 → 5.2.0-beta.7

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 (246) hide show
  1. package/lib/claude-hook-installer.js +1 -267
  2. package/lib/claude-jsonl-watcher.js +1 -204
  3. package/lib/clay-control-presentation-binding.js +96 -0
  4. package/lib/clay-control-presentation.js +128 -0
  5. package/lib/clay-control-target-projection.js +26 -0
  6. package/lib/clay-mate-chat.js +1 -0
  7. package/lib/clay-mate-knowledge.js +1 -1
  8. package/lib/clay-product-control-tools.js +1 -1
  9. package/lib/clay-product-control.js +22 -12
  10. package/lib/clay-project-records.js +2 -1
  11. package/lib/clay-scheduling.js +1 -1
  12. package/lib/clay-screen-controls.js +13 -5
  13. package/lib/clay-skill-install.js +1 -33
  14. package/lib/clay-workspace-controls.js +2 -1
  15. package/lib/cli-sessions.js +1 -300
  16. package/lib/codex-defaults.js +1 -28
  17. package/lib/context-usage.js +3 -2
  18. package/lib/contextual-screen-snapshot.js +38 -0
  19. package/lib/daemon.js +5 -55
  20. package/lib/default-ai-runtime.js +4 -8
  21. package/lib/email-mcp-server.js +1 -1
  22. package/lib/kiro-defaults.js +1 -22
  23. package/lib/mate-context-service.js +138 -0
  24. package/lib/mate-context-tools.js +21 -0
  25. package/lib/mcp-proxy.js +2 -32
  26. package/lib/mcp-query-bridge.js +1 -1
  27. package/lib/mcp-source-adapters.js +1 -91
  28. package/lib/mcp-source-context.js +1 -42
  29. package/lib/mcp-source-discovery.js +1 -169
  30. package/lib/mcp-source-plugins.js +1 -68
  31. package/lib/mcp-source-restrictions.js +1 -47
  32. package/lib/mcp-tool-policy.js +1 -26
  33. package/lib/os-user-diagnostics.js +2 -4
  34. package/lib/os-users.js +4 -65
  35. package/lib/project-connection.js +14 -10
  36. package/lib/project-control-git-view.js +12 -0
  37. package/lib/project-filesystem.js +5 -1
  38. package/lib/project-http.js +7 -4
  39. package/lib/project-mcp.js +2 -2
  40. package/lib/project-notifications.js +5 -4
  41. package/lib/project-pair-result-delivery.js +2 -2
  42. package/lib/project-pair-usage.js +12 -13
  43. package/lib/project-pending-message-queue.js +3 -1
  44. package/lib/project-product-control.js +3 -1
  45. package/lib/project-scheduled-messages.js +4 -4
  46. package/lib/project-scheduled-task-control.js +1 -1
  47. package/lib/project-session-document.js +20 -5
  48. package/lib/project-session-history-actions.js +5 -3
  49. package/lib/project-session-pair.js +1 -1
  50. package/lib/project-session-runtime-actions.js +2 -1
  51. package/lib/project-session-spawn.js +2 -3
  52. package/lib/project-session-vendor.js +2 -1
  53. package/lib/project-sessions.js +18 -26
  54. package/lib/project-shared-browser.js +22 -5
  55. package/lib/project-studio-presentation.js +58 -0
  56. package/lib/project-user-message.js +37 -0
  57. package/lib/project-vendor-login.js +6 -36
  58. package/lib/project-workspace-query.js +11 -1
  59. package/lib/project.js +24 -7
  60. package/lib/public/app.js +25 -0
  61. package/lib/public/appearance-bootstrap.js +2 -0
  62. package/lib/public/css/contextual-clay.css +240 -0
  63. package/lib/public/css/messages.css +6 -2
  64. package/lib/public/modules/app-favicon.js +18 -9
  65. package/lib/public/modules/app-header.js +4 -2
  66. package/lib/public/modules/app-loop-ui.js +2 -0
  67. package/lib/public/modules/app-message-router.js +5 -1
  68. package/lib/public/modules/app-messages.js +50 -6
  69. package/lib/public/modules/app-notifications.js +11 -5
  70. package/lib/public/modules/app-panels.js +61 -123
  71. package/lib/public/modules/app-projects.js +3 -0
  72. package/lib/public/modules/app-rate-limit.js +7 -27
  73. package/lib/public/modules/chat-render-runtime.js +3 -0
  74. package/lib/public/modules/clay-control-activity.js +214 -0
  75. package/lib/public/modules/clay-control-copy.js +36 -0
  76. package/lib/public/modules/clay-control-detail-targets.js +44 -0
  77. package/lib/public/modules/clay-control-object-targets.js +52 -0
  78. package/lib/public/modules/clay-control-target-visibility.js +12 -0
  79. package/lib/public/modules/clay-control-targets.js +44 -0
  80. package/lib/public/modules/clay-navigation-render.js +52 -0
  81. package/lib/public/modules/clay-owner-navigation.js +11 -2
  82. package/lib/public/modules/clay-session-links.js +31 -0
  83. package/lib/public/modules/clay-studio-bridge.js +20 -0
  84. package/lib/public/modules/clay-studio-document.js +67 -0
  85. package/lib/public/modules/clay-studio-render.js +144 -0
  86. package/lib/public/modules/clay-tool-presentation.js +4 -0
  87. package/lib/public/modules/command-palette.js +26 -31
  88. package/lib/public/modules/contextual-ask-session.js +26 -0
  89. package/lib/public/modules/contextual-clay-geometry.js +155 -0
  90. package/lib/public/modules/contextual-clay-session.js +130 -0
  91. package/lib/public/modules/contextual-clay-workspace.js +157 -0
  92. package/lib/public/modules/contextual-clay.js +237 -0
  93. package/lib/public/modules/contextual-composer.js +91 -0
  94. package/lib/public/modules/contextual-mate-context.js +115 -0
  95. package/lib/public/modules/contextual-mate-hint.js +40 -0
  96. package/lib/public/modules/contextual-mate-identity.js +23 -0
  97. package/lib/public/modules/contextual-mate-sessions.js +134 -0
  98. package/lib/public/modules/contextual-search.js +137 -0
  99. package/lib/public/modules/contextual-session-citations.js +58 -0
  100. package/lib/public/modules/filebrowser.js +20 -1
  101. package/lib/public/modules/generation-stop.js +8 -5
  102. package/lib/public/modules/git-panel.js +3 -1
  103. package/lib/public/modules/icon-strip-tooltip.js +2 -1
  104. package/lib/public/modules/input.js +35 -1
  105. package/lib/public/modules/issues-render.js +3 -2
  106. package/lib/public/modules/knowledge-db-workbench.js +4 -4
  107. package/lib/public/modules/markdown.js +2 -34
  108. package/lib/public/modules/mate-knowledge-workbench.js +1 -1
  109. package/lib/public/modules/mcp-connection-view.js +1 -1
  110. package/lib/public/modules/message-delivery.js +10 -0
  111. package/lib/public/modules/pane-bridge.js +2 -2
  112. package/lib/public/modules/pending-message-queue.js +1 -0
  113. package/lib/public/modules/permission-control.js +5 -4
  114. package/lib/public/modules/product-review.js +26 -0
  115. package/lib/public/modules/project-chat-identity.js +13 -4
  116. package/lib/public/modules/project-logs-render.js +3 -0
  117. package/lib/public/modules/project-mate-navigation.js +58 -21
  118. package/lib/public/modules/scheduled-tasks.js +2 -0
  119. package/lib/public/modules/scheduler.js +2 -0
  120. package/lib/public/modules/search-clay-chat.js +5 -454
  121. package/lib/public/modules/session-create-form.js +10 -10
  122. package/lib/public/modules/settings-defaults.js +27 -28
  123. package/lib/public/modules/shared-browser.js +2 -0
  124. package/lib/public/modules/sidebar-mobile.js +4 -3
  125. package/lib/public/modules/sidebar-projects.js +1 -0
  126. package/lib/public/modules/sidebar-sessions.js +1 -0
  127. package/lib/public/modules/split-view.js +7 -16
  128. package/lib/public/modules/terminal.js +9 -0
  129. package/lib/public/modules/tools.js +6 -2
  130. package/lib/public/modules/tui-grab.js +1 -2
  131. package/lib/public/modules/vendor-login-browser.js +5 -3
  132. package/lib/public/modules/vendor-login.js +2 -2
  133. package/lib/public/modules/vendor-priority.js +15 -1
  134. package/lib/public/modules/wireframe-workbench.js +5 -2
  135. package/lib/public/muse-avatar.svg +4 -0
  136. package/lib/public/style.css +2 -0
  137. package/lib/safe-bash-commands.js +3 -45
  138. package/lib/sdk-bridge.js +131 -395
  139. package/lib/sdk-foreground-admission.js +16 -0
  140. package/lib/sdk-mcp-descriptors.js +2 -37
  141. package/lib/sdk-message-processor.js +50 -17
  142. package/lib/sdk-quota-snapshot.js +1 -1
  143. package/lib/sdk-response-recovery.js +1 -155
  144. package/lib/sdk-worker.js +2 -677
  145. package/lib/server-contextual-clay-settings.js +40 -0
  146. package/lib/server-contextual-mate-sessions.js +70 -0
  147. package/lib/server-home-chat.js +7 -1
  148. package/lib/server-home-clay-entry.js +23 -4
  149. package/lib/server-home-clay-session-links.js +12 -0
  150. package/lib/server-owner-ui.js +49 -8
  151. package/lib/server-settings.js +2 -0
  152. package/lib/server.js +18 -1
  153. package/lib/session-fork-history.js +21 -0
  154. package/lib/session-hygiene.js +3 -35
  155. package/lib/session-permission-mode.js +4 -3
  156. package/lib/session-skill-scope.js +11 -0
  157. package/lib/sessions.js +24 -89
  158. package/lib/terminal-manager.js +18 -11
  159. package/lib/tool-llm.js +1 -1
  160. package/lib/tui-transcript-index.js +1 -132
  161. package/lib/users-contextual-clay-preferences.js +31 -0
  162. package/lib/users.js +3 -0
  163. package/lib/vendor-login-browser.js +1 -11
  164. package/lib/workspace-assignment-service.js +25 -0
  165. package/lib/workspace-query-mcp-server.js +1 -1
  166. package/lib/ws-request.js +1 -0
  167. package/lib/ws-schema.js +10 -0
  168. package/lib/yoke/acp-agent-profiles.js +5 -3
  169. package/lib/yoke/adapters/antigravity.js +3 -14
  170. package/lib/yoke/adapters/claude-query-contract.js +9 -0
  171. package/lib/yoke/adapters/claude.js +7 -42
  172. package/lib/yoke/adapters/codex-query-contract.js +24 -0
  173. package/lib/yoke/adapters/codex.js +1 -0
  174. package/lib/yoke/adapters/kiro.js +3 -1
  175. package/lib/yoke/adapters/muse-query-contract.js +7 -0
  176. package/lib/yoke/adapters/muse.js +122 -0
  177. package/lib/yoke/auth-events.js +17 -0
  178. package/lib/yoke/claude-hook-installer.js +271 -0
  179. package/lib/yoke/claude-jsonl-watcher.js +204 -0
  180. package/lib/yoke/claude-maintenance.js +55 -0
  181. package/lib/yoke/claude-permission-patterns.js +53 -0
  182. package/lib/yoke/claude-response-recovery.js +155 -0
  183. package/lib/yoke/claude-transcript-hygiene.js +34 -0
  184. package/lib/yoke/clay-skill-install.js +33 -0
  185. package/lib/yoke/cli-sessions.js +300 -0
  186. package/lib/yoke/codex-app-server.js +7 -12
  187. package/lib/yoke/configuration.js +13 -0
  188. package/lib/yoke/discovery.js +70 -0
  189. package/lib/yoke/errors.js +8 -0
  190. package/lib/yoke/host-environment.js +7 -0
  191. package/lib/yoke/index.js +9 -51
  192. package/lib/yoke/interface.js +116 -0
  193. package/lib/yoke/kiro-acp-server.js +5 -5
  194. package/lib/yoke/legacy-sessions.js +89 -0
  195. package/lib/yoke/login-protocol.js +25 -0
  196. package/lib/yoke/maintenance.js +12 -0
  197. package/lib/yoke/mcp-proxy.js +32 -0
  198. package/lib/yoke/mcp-source-adapters.js +91 -0
  199. package/lib/yoke/mcp-source-context.js +42 -0
  200. package/lib/yoke/mcp-source-discovery.js +169 -0
  201. package/lib/yoke/mcp-source-plugins.js +68 -0
  202. package/lib/yoke/mcp-source-restrictions.js +47 -0
  203. package/lib/yoke/mcp-tool-policy-normalize.js +28 -0
  204. package/lib/yoke/model-preferences.js +6 -0
  205. package/lib/yoke/muse-artifacts.js +36 -0
  206. package/lib/yoke/muse-controls.js +50 -0
  207. package/lib/yoke/muse-input.js +64 -0
  208. package/lib/yoke/muse-items.js +157 -0
  209. package/lib/yoke/muse-policy.js +52 -0
  210. package/lib/yoke/muse-query.js +428 -0
  211. package/lib/yoke/muse-runtime.js +211 -0
  212. package/lib/yoke/muse-services.js +79 -0
  213. package/lib/yoke/muse-skills.js +79 -0
  214. package/lib/yoke/muse-turns.js +71 -0
  215. package/lib/yoke/options/acp.js +2 -0
  216. package/lib/yoke/options/antigravity.js +2 -0
  217. package/lib/yoke/options/claude.js +21 -0
  218. package/lib/yoke/options/codex-defaults.js +29 -0
  219. package/lib/yoke/options/codex-settings.js +21 -0
  220. package/lib/yoke/options/codex.js +6 -0
  221. package/lib/yoke/options/kiro-defaults.js +22 -0
  222. package/lib/yoke/options/kiro.js +5 -0
  223. package/lib/yoke/options/muse.js +5 -0
  224. package/lib/yoke/provider-contract.js +5 -0
  225. package/lib/yoke/providers/antigravity-executable.js +8 -0
  226. package/lib/yoke/providers/claude-executable.js +40 -0
  227. package/lib/yoke/providers/claude-model-descriptions.js +14 -0
  228. package/lib/yoke/providers/claude-process.js +97 -0
  229. package/lib/yoke/providers/claude-provision.js +75 -0
  230. package/lib/yoke/providers/codex-errors.js +8 -0
  231. package/lib/yoke/providers/muse-executable.js +17 -0
  232. package/lib/yoke/provisioning.js +6 -0
  233. package/lib/yoke/query-options.js +10 -0
  234. package/lib/yoke/response-recovery.js +41 -0
  235. package/lib/yoke/sdk-mcp-descriptors.js +37 -0
  236. package/lib/yoke/sdk-server-factory.js +7 -0
  237. package/lib/yoke/session-history.js +8 -0
  238. package/lib/yoke/session-tool-transport.js +31 -13
  239. package/lib/yoke/settings.js +23 -0
  240. package/lib/yoke/source-locations.js +4 -0
  241. package/lib/yoke/title-query.js +18 -7
  242. package/lib/yoke/tui-transcript-index.js +132 -0
  243. package/lib/yoke/usage.js +48 -0
  244. package/lib/yoke/user-input.js +13 -0
  245. package/lib/yoke/vendor-registry.js +81 -1
  246. package/package.json +3 -2
@@ -22,34 +22,15 @@ function getVendorUsageMeta(vendor) {
22
22
  var vendors = store.get('vendorInfo') || {};
23
23
  var info = vendors[vendor];
24
24
  if (info && info.usageDashboard) return info.usageDashboard;
25
- var fallbacks = {
26
- codex: {
27
- icon: "/codex-avatar.png",
28
- alt: "Codex",
29
- href: "https://chatgpt.com/codex/settings/usage",
30
- title: "Check usage on ChatGPT",
31
- },
32
- claude: {
33
- icon: "/claude-code-avatar.png",
34
- alt: "Claude Code",
35
- href: "https://claude.ai/settings/usage",
36
- title: "Check usage on claude.ai",
37
- },
38
- };
39
- return fallbacks[vendor] || null;
25
+ return null;
40
26
  }
41
27
 
42
28
  function vendorTracksRateLimits(vendor) {
43
- var vendors = store.get('vendorInfo') || {};
44
- var info = vendors[vendor];
45
- if (info) return info.rateLimitTracking !== false;
46
- var legacyTrackedVendors = ["claude", "codex"];
47
- return legacyTrackedVendors.indexOf(vendor) !== -1;
29
+ return ((store.get('vendorInfo') || {})[vendor] || {}).rateLimitTracking === true;
48
30
  }
49
31
 
50
32
  function vendorSupportsScheduledMessages(vendor) {
51
- var scheduledMessageVendors = ["claude"];
52
- return scheduledMessageVendors.indexOf(vendor) !== -1;
33
+ return ((store.get('vendorInfo') || {})[vendor] || {}).rateLimitScheduling === true;
53
34
  }
54
35
 
55
36
  function rateLimitEventAppliesToPane(msg, state) {
@@ -60,7 +41,7 @@ function rateLimitEventAppliesToPane(msg, state) {
60
41
  if (msg.vendor) return msg.vendor === activeVendor;
61
42
  // Legacy Claude records predate vendor/session stamps. Never project them
62
43
  // into a non-Claude pane, where they would look like Claude usage.
63
- return activeVendor === "claude";
44
+ return ((state.vendorInfo || {})[activeVendor] || {}).acceptsUnstampedQuota === true;
64
45
  }
65
46
 
66
47
  function rateLimitStateKey(sessionId, vendor) {
@@ -84,7 +65,6 @@ function setActiveRateLimitState(next) {
84
65
  }
85
66
 
86
67
  function extraUsageDescription(msg) {
87
- if (msg.vendor && msg.vendor !== "claude") return "";
88
68
  var reason = msg.overageDisabledReason;
89
69
  if (reason === "out_of_credits") return "Extra usage has no credits remaining";
90
70
  if (reason === "fetch_error") return "Extra usage availability could not be checked";
@@ -350,7 +330,7 @@ export function initRateLimit() {
350
330
  export function handleRateLimitEvent(msg) {
351
331
  if (!rateLimitEventAppliesToPane(msg, store.snap())) return;
352
332
  // Old Codex history contains quota snapshots misrecorded as rejections.
353
- if (msg.vendor === "codex" && !msg.source) return;
333
+ if (msg.ignored === true) return;
354
334
  if (msg.resetsAt && msg.resetsAt <= Date.now()) {
355
335
  var expiredState = Object.assign({}, getActiveRateLimitState());
356
336
  if (msg.rateLimitType) delete expiredState[msg.rateLimitType];
@@ -408,12 +388,12 @@ export function handleRateLimitEvent(msg) {
408
388
 
409
389
  export function updateRateLimitUsage(msg) {
410
390
  if (!rateLimitEventAppliesToPane(msg, store.snap())) return;
411
- if (msg.vendor === "codex" && msg.source === "account_snapshot" && Array.isArray(msg.quotaWindows)) {
391
+ if (msg.source === "account_snapshot" && Array.isArray(msg.quotaWindows)) {
412
392
  applyQuotaSnapshot(msg);
413
393
  renderRateLimitUsage();
414
394
  return;
415
395
  }
416
- if (msg.vendor === "codex" && !msg.source) return;
396
+ if (msg.ignored === true) return;
417
397
  var activeVendor = store.get('currentVendor') || "claude";
418
398
  if (!vendorTracksRateLimits(activeVendor)) {
419
399
  renderRateLimitUsage();
@@ -7,6 +7,7 @@ import { getProjectAssistantPlaceholder } from './project-chat-identity.js';
7
7
  export var VENDOR_AVATARS = {
8
8
  claude: "/claude-code-avatar.png",
9
9
  codex: "/codex-avatar.png",
10
+ muse: "/muse-avatar.svg",
10
11
  antigravity: "/antigravity-avatar.png",
11
12
  opencode: "/opencode-avatar.svg",
12
13
  kimi: "/kimi-avatar.svg",
@@ -20,6 +21,7 @@ export var VENDOR_AVATARS = {
20
21
  export var VENDOR_NAMES = {
21
22
  claude: "Claude Code",
22
23
  codex: "Codex",
24
+ muse: "Muse Code",
23
25
  antigravity: "Antigravity CLI",
24
26
  opencode: "OpenCode",
25
27
  kimi: "Kimi Code",
@@ -33,6 +35,7 @@ export var VENDOR_NAMES = {
33
35
  export var VENDOR_HOMEPAGES = {
34
36
  claude: "https://claude.com/product/claude-code",
35
37
  codex: "https://openai.com/codex/",
38
+ muse: "https://dev.meta.ai/docs/muse-code",
36
39
  antigravity: "https://antigravity.google/product/antigravity-cli",
37
40
  opencode: "https://opencode.ai/",
38
41
  kimi: "https://www.kimi.com/code/",
@@ -0,0 +1,214 @@
1
+ // Owner-authenticated presentation only; this module never invokes a product action.
2
+ import { contextualMateStamp } from './contextual-mate-identity.js';
3
+ import { store } from './store.js';
4
+ import { mateAvatarUrl } from './avatar.js';
5
+ import { activityCopy, validActivity } from './clay-control-copy.js';
6
+ import { controlTarget, activityPlacement, visibleTarget } from './clay-control-targets.js';
7
+
8
+ function forward(message) {
9
+ if (!store.get('paneMode') || window.parent === window) return false;
10
+ store.set({ clayControlForwarded: message });
11
+ window.parent.postMessage({ type: 'clay-control-presentation', message: message }, location.origin);
12
+ return true;
13
+ }
14
+ export function clearClayControlActivity() {
15
+ var activity = store.get('clayControlActivity');
16
+ if (activity) {
17
+ clearTimeout(activity.timer);
18
+ cancelAnimationFrame(activity.frame);
19
+ if (activity.animation) activity.animation.cancel();
20
+ if (activity.observer) activity.observer.disconnect();
21
+ if (activity.resize) activity.resize.disconnect();
22
+ }
23
+ store.set({ clayControlActivity: null });
24
+ var element = document.getElementById('clay-control-activity');
25
+ if (element) element.remove();
26
+ }
27
+ function layout() {
28
+ var activity = store.get('clayControlActivity'), element = document.getElementById('clay-control-activity');
29
+ if (!activity || !element) return;
30
+ var surface = document.getElementById('contextual-clay');
31
+ var narrow = window.matchMedia('(max-width: 768px)').matches;
32
+ var workbench = narrow && Array.from(document.querySelectorAll('#file-viewer, #mate-knowledge-workbench, #project-logs-panel, #issues-panel, #scheduled-tasks-panel')).find(function (item) { return visibleTarget(item); });
33
+ var host = workbench || surface;
34
+ var inline = narrow && host && !host.hidden && host.getClientRects().length;
35
+ element.classList.toggle('is-inline', !!inline);
36
+ if (inline) {
37
+ if (element.parentNode !== host) {
38
+ if (host === surface) host.insertBefore(element, surface.querySelector('.contextual-clay-body'));
39
+ else host.insertBefore(element, host.children[1] || null);
40
+ }
41
+ element.classList.remove('has-target', 'is-flipped', 'is-reversed');
42
+ element.dataset.targeted = 'false'; element.style.visibility = 'visible';
43
+ element.style.marginTop = '6px';
44
+ var inlineBox = element.getBoundingClientRect(), offset = 0;
45
+ Array.from(document.querySelectorAll('.notif-banner.show')).forEach(function (banner) { var rect = banner.getBoundingClientRect(); if (rect.left < inlineBox.right && rect.right > inlineBox.left && rect.bottom > inlineBox.top && rect.top < inlineBox.bottom) offset = Math.max(offset, rect.bottom - inlineBox.top + 8); });
46
+ if (offset) element.style.marginTop = (6 + offset) + 'px';
47
+ return;
48
+ }
49
+ if (element.parentNode !== document.body) document.body.appendChild(element);
50
+ element.style.removeProperty("margin-top");
51
+ var contextual = store.get('contextualClay') || {};
52
+ var background = activity.fromPane && (contextual.mode !== 'popover' || contextual.mateId !== activity.message.mateId);
53
+ var row = !background && !window.matchMedia('(pointer: coarse), (max-width: 768px)').matches && controlTarget(activity.message.target);
54
+ if (activity.targetNode !== row) {
55
+ if (activity.targetNode) activity.resize.unobserve(activity.targetNode);
56
+ activity.targetNode = row;
57
+ if (row) activity.resize.observe(row, { box: 'border-box' });
58
+ }
59
+ var obstacles = Array.from(document.querySelectorAll('#input-area, #contextual-clay, .ask-user-container:not(.answered), [role="dialog"], .notif-banner.show, #file-viewer-body, .wireframe-workbench-canvas')).filter(function (item) {
60
+ return (!row || !item.contains(row) || item.id === 'input-area') && !item.closest('details:not([open]), [hidden], [inert], [aria-hidden="true"]') && item.getClientRects().length && !item.hidden && getComputedStyle(item).visibility !== 'hidden' && Number(getComputedStyle(item).opacity) > 0;
61
+ }).map(function (item) { return item.getBoundingClientRect(); });
62
+ var box = element.getBoundingClientRect();
63
+ var place = activityPlacement(row, box.width, box.height, obstacles);
64
+ element.style.visibility = place ? 'visible' : 'hidden';
65
+ if (!place) { if (activity.animation) activity.animation.cancel(); element.dataset.targeted = 'false'; element.classList.remove('has-target', 'is-flipped', 'is-reversed'); element.style.removeProperty('--tip-x'); element.style.removeProperty('--tip-y'); return; }
66
+ var targeted = place.tipX !== undefined;
67
+ if (!targeted && activity.animation) { activity.animation.cancel(); activity.animation = null; }
68
+ element.classList.toggle('has-target', targeted);
69
+ element.classList.toggle('is-flipped', !!place.flip);
70
+ element.classList.toggle('is-reversed', !!place.reverse);
71
+ element.style.left = place.left + 'px'; element.style.top = place.top + 'px';
72
+ if (targeted) {
73
+ element.style.setProperty('--tip-x', (place.tipX - place.left) + 'px');
74
+ element.style.setProperty('--tip-y', (place.tipY - place.top) + 'px');
75
+ }
76
+ element.dataset.targeted = String(targeted);
77
+ if (document.getAnimations().some(function (animation) {
78
+ var node = animation.effect && animation.effect.target;
79
+ if (animation.playState !== 'running' || !(node instanceof Element) || element.contains(node) ||
80
+ !(row && (node === row || node.contains(row)) || node.matches('.notif-banner.show'))) return false;
81
+ if (!activity.motionSeen) activity.motionSeen = new WeakSet();
82
+ if (!activity.motionSeen.has(animation)) { activity.motionSeen.add(animation); activity.motionUntil = Date.now() + 5000; }
83
+ return Date.now() < activity.motionUntil;
84
+ })) scheduleLayout();
85
+ }
86
+ function scheduleLayout() {
87
+ var activity = store.get('clayControlActivity');
88
+ if (!activity || activity.frame) return;
89
+ activity.frame = requestAnimationFrame(function () { activity.frame = null; layout(); });
90
+ }
91
+ function render(message, mate, copy, fromPane) {
92
+ var existing = store.get('clayControlActivity');
93
+ if (existing && existing.message.id === message.id) {
94
+ existing.message = message;
95
+ existing.fromPane = !!fromPane;
96
+ clearTimeout(existing.timer);
97
+ existing.timer = setTimeout(clearClayControlActivity, message.terminal === false || ['review', 'running'].indexOf(message.phase) >= 0 ? 300000 : 6000);
98
+ document.querySelector('#clay-control-activity .clay-control-bubble').textContent = copy;
99
+ scheduleLayout(); return;
100
+ }
101
+ var previous = document.getElementById('clay-control-activity');
102
+ var previousRect = previous && previous.classList.contains('has-target') && previous.getBoundingClientRect();
103
+ clearClayControlActivity();
104
+ var element = document.createElement('div'); element.id = 'clay-control-activity';
105
+ element.setAttribute('role', 'status'); element.setAttribute('aria-live', 'polite'); element.setAttribute('aria-atomic', 'true');
106
+ var identity = document.createElement('span'); identity.className = 'clay-control-identity';
107
+ var avatar = document.createElement('img'); avatar.alt = ''; avatar.width = 20; avatar.height = 20;
108
+ avatar.addEventListener('error', function () { avatar.hidden = true; }, { once: true });
109
+ avatar.src = mateAvatarUrl(mate, 20);
110
+ var name = document.createElement('span'); name.className = 'clay-control-name';
111
+ name.textContent = (mate.profile || {}).displayName || mate.displayName || mate.name || (mate.builtinKey === 'clay' ? 'Clay' : 'Mate');
112
+ identity.append(avatar, name);
113
+ var bubble = document.createElement('span'); bubble.className = 'clay-control-bubble'; bubble.textContent = copy;
114
+ var pointer = document.createElement('span'); pointer.className = 'clay-control-pointer'; pointer.setAttribute('aria-hidden', 'true');
115
+ element.append(pointer, identity, bubble); document.body.appendChild(element);
116
+ var observer = new MutationObserver(function (changes) {
117
+ if (changes.some(function (change) { return !element.contains(change.target); })) scheduleLayout();
118
+ });
119
+ observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['class', 'hidden', 'inert', 'data-session-id', 'data-control-session-origin', 'data-control-revision', 'data-ref', 'data-task-id', 'data-open-issue', 'data-control-runtime', 'data-control-mate-stamp'] });
120
+ var resize = new ResizeObserver(scheduleLayout); resize.observe(document.body); resize.observe(element);
121
+ var activity = { message: message, fromPane: !!fromPane, observer: observer, resize: resize, frame: null,
122
+ timer: setTimeout(clearClayControlActivity, message.terminal === false || ['review', 'running'].indexOf(message.phase) >= 0 ? 300000 : 6000) };
123
+ store.set({ clayControlActivity: activity }); layout();
124
+ if (previousRect && element.classList.contains('has-target') && !window.matchMedia('(prefers-reduced-motion: reduce)').matches) {
125
+ var next = element.getBoundingClientRect(), dx = previousRect.left - next.left, dy = previousRect.top - next.top;
126
+ if (Math.hypot(dx, dy) <= 600 && (dx || dy)) activity.animation = element.animate([
127
+ { transform: 'translate(' + dx + 'px, ' + dy + 'px)' }, { transform: 'translate(0, 0)' }
128
+ ], { duration: 140, easing: 'ease-out' });
129
+ }
130
+ }
131
+ export function handleClayControlActivity(message) {
132
+ if (message && message.type === 'clay_control_viewer') { store.set({ clayControlViewerRef: message.viewerRef }); return true; }
133
+ if (!message || message.type !== 'clay_control_activity') return false;
134
+ if (!validActivity(message) || message.recipientViewerRef !== store.get('clayControlViewerRef')) return true;
135
+ if (store.get('paneMode') && (message.sourceSessionOriginId !== store.get('sessionOriginId') || message.sourceSessionId !== store.get('activeSessionId') || message.mateId !== store.get('activeProjectMateId'))) return true;
136
+ if (!sourceMate(message)) return true;
137
+ if (forward(message)) return true;
138
+ acceptActivity(message);
139
+ return true;
140
+ }
141
+ function sourceMate(message) {
142
+ return (store.get('cachedMatesList') || []).find(function (mate) {
143
+ return !mate.archived && !mate.lifecycleDeletion && mate.id === message.mateId && mate.builtinKey === 'clay' && contextualMateStamp(mate) === message.sourceMateStamp;
144
+ });
145
+ }
146
+ function acceptActivity(message, fromPane) {
147
+ if ((message.ownerId || null) !== (store.get('myUserId') || null) || !store.get('connected')) return;
148
+ if (!sourceMate(message)) return;
149
+ var active = store.get('clayControlActivity');
150
+ var receipts = store.get('clayControlReceipts') || [];
151
+ var old = receipts.find(function (entry) { return entry.id === message.id; });
152
+ var order = store.get('clayControlOrder') || 0;
153
+ if (message.order < order || old && message.sequence <= old.sequence) return;
154
+ if (old && old.stopped) return;
155
+ receipts = receipts.filter(function (entry) { return entry.id !== message.id && Date.now() - entry.time < 300000; }).slice(-99);
156
+ receipts.push({ id: message.id, sequence: message.sequence, time: Date.now(), stopped: message.phase === 'clear' });
157
+ store.set({ clayControlOrder: message.order, clayControlReceipts: receipts });
158
+ if (message.phase === 'clear') { if (active && active.message.id === message.id) clearClayControlActivity(); return; }
159
+ var mate = (store.get('cachedMatesList') || []).find(function (item) { return !item.archived && !item.lifecycleDeletion && item.id === message.mateId && item.builtinKey === 'clay'; });
160
+ if (!mate) return;
161
+ render(message, mate, activityCopy(message), fromPane);
162
+ }
163
+ // Ordinary Mates retain their existing restricted navigation invocation feedback.
164
+ export function showClayNavigationActivity(target, failed) {
165
+ if (!target || !target.contextualSourceMateId) return;
166
+ var mate = (store.get('cachedMatesList') || []).find(function (item) { return !item.archived && item.id === target.contextualSourceMateId; });
167
+ if (!mate) return;
168
+ render({ id: 'ordinary-navigation', target: Object.assign({ kind: 'session' }, target) }, mate,
169
+ failed ? 'I could not start that navigation.' : 'I’m opening the reviewed conversation.');
170
+ }
171
+ function stopPresentation() {
172
+ var active = store.get('clayControlActivity');
173
+ if (active) {
174
+ var receipts = (store.get('clayControlReceipts') || []).slice();
175
+ receipts.push({ id: active.message.id, sequence: Number.MAX_SAFE_INTEGER, stopped: true, time: Date.now() });
176
+ store.set({ clayControlReceipts: receipts.slice(-100) });
177
+ }
178
+ clearClayControlActivity();
179
+ }
180
+ export function initClayControlActivity() {
181
+ store.subscribe(function (value, previous) {
182
+ if (!value.connected && previous.connected) store.set({ clayControlOrder: 0, clayControlViewerRef: null });
183
+ if (!value.clayControlActivity) return;
184
+ if (!value.connected || value.myUserId !== previous.myUserId || value.currentSlug !== previous.currentSlug || value.activeSessionId !== previous.activeSessionId) { stopPresentation(); return; }
185
+ if (value.cachedMatesList !== previous.cachedMatesList) {
186
+ var actor = value.clayControlActivity.message.mateId;
187
+ if (actor && !sourceMate(value.clayControlActivity.message)) stopPresentation();
188
+ }
189
+ scheduleLayout();
190
+ });
191
+ ['resize', 'scroll'].forEach(function (name) { window.addEventListener(name, scheduleLayout, true); });
192
+ ['transitionrun', 'transitionend', 'transitioncancel', 'animationstart', 'animationend', 'animationcancel'].forEach(function (name) { document.addEventListener(name, scheduleLayout, true); });
193
+ document.addEventListener('visibilitychange', function () { if (document.hidden) stopPresentation(); });
194
+ document.addEventListener('click', function (event) {
195
+ if (event.target.closest('#generation-stop, #autonomous-run-stop, #send-btn.stop')) stopPresentation();
196
+ }, true);
197
+ window.addEventListener('message', function (event) {
198
+ if (event.origin !== location.origin || !event.data || event.data.type !== 'clay-control-presentation') return;
199
+ var host = document.getElementById('contextual-clay'), message = event.data.message;
200
+ if (!validActivity(message)) return;
201
+ var frame = host && Array.from(host.querySelectorAll('iframe[data-canonical="true"]')).find(function (item) { return item.contentWindow === event.source; });
202
+ var bridge = frame && frame.conversation;
203
+ var identity = bridge && bridge.document === frame.contentDocument && bridge.source === frame.contentWindow && bridge.identity();
204
+ if (!identity || message.phase !== 'clear' && (!identity.connected || !identity.available) || identity.ownerId !== store.get('myUserId') || identity.mateId !== message.mateId || identity.sessionId !== message.sourceSessionId || identity.sessionOriginId !== message.sourceSessionOriginId || identity.viewerRef !== message.recipientViewerRef || !sourceMate(message)) return;
205
+ if (new URL(frame.src, location.href).pathname !== '/p/' + encodeURIComponent(message.sourceSlug) + '/') return;
206
+ acceptActivity(message, true);
207
+ });
208
+ document.addEventListener('contextual-ask-stopped', stopPresentation, true);
209
+ }
210
+
211
+ export function withdrawPaneControlActivity() {
212
+ var message = store.get('clayControlForwarded');
213
+ if (message) forward(Object.assign({}, message, { phase: 'clear', sequence: message.sequence + 1 }));
214
+ }
@@ -0,0 +1,36 @@
1
+ var domains = { session: 'conversation', project: 'project', mate: 'Mate profile', knowledge: 'Mate Knowledge', records: 'project record', schedule: 'schedule', conversation: 'Mate conversation', workspace: 'workspace', screen: 'screen', assignment: 'Driver work', studio: 'Studio workbench' };
2
+ var actions = {
3
+ rename: 'rename this conversation so it is easier to find', stop: 'request Stop so this work can settle',
4
+ model: 'change the model for this conversation', effort: 'adjust reasoning effort for this conversation',
5
+ permission: 'change the reviewed permission mode', delete: 'remove the reviewed item',
6
+ fork: 'fork the conversation at the reviewed checkpoint', rewind: 'restore the reviewed conversation checkpoint',
7
+ vendor: 'select the provider for this conversation', vendor_new_session: 'prepare a new conversation with the reviewed provider',
8
+ runtime: 'return this conversation to GUI chat', queue_add: 'queue the reviewed message for the Driver',
9
+ queue_edit: 'update the reviewed queued message', queue_cancel: 'cancel the reviewed queued message',
10
+ queue_reorder: 'reorder the reviewed message queue', navigate: 'open the reviewed conversation so we can continue there',
11
+ dismiss_notifications: 'dismiss the reviewed notifications', profile: 'update the reviewed Mate profile',
12
+ send: 'deliver the reviewed message to this Mate', assign: 'prepare the requested Driver work for your review', follow_up: 'prepare a follow-up for the existing Driver', document: 'open the requested document for editing', sketch: 'present the requested sketch', browser: 'operate the requested shared browser tab'
13
+ };
14
+ export function activityCopy(msg) {
15
+ var purpose = (['session', 'conversation', 'screen', 'mate', 'assignment', 'studio'].indexOf(msg.domain) >= 0 && Object.hasOwn(actions, msg.action) && actions[msg.action]) || 'apply the reviewed change to this ' + (domains[msg.domain] || 'item');
16
+ var endings = { review: 'I’m waiting for your review before I ' + purpose + '.', running: 'I’m working to ' + purpose + '.',
17
+ proposed: 'I saved the task proposal. Use Create task in Scheduled Tasks to create it; execution has not started.',
18
+ persisted: 'I saved the reviewed change.', completed: 'The service finished the reviewed change.', queued: 'I queued the request. The work has not finished yet.',
19
+ delivered: 'I sent the navigation request. I cannot yet confirm what is visible.', acknowledged: 'I started opening the conversation. Its visible result is still unconfirmed.',
20
+ rendered: 'The reviewed conversation is visibly open in the selected window.',
21
+ cancelled: 'I left the change unapplied.', failed: 'I could not finish this change. Please check the result before trying again.',
22
+ partial: 'Part of this change took effect, but the operation failed. Please check the result.', unknown: 'I cannot confirm the outcome. Please check before trying again.' };
23
+ if (msg.domain === 'assignment') { var work = { review: 'I prepared the Driver work. Please review its proposal before it starts.', queued: 'I submitted the approved Driver work for dispatch.', running: 'The Driver is working on the approved request.', completed: 'The Driver finished this request.', cancelled: 'I left this Driver request unapplied.', unknown: 'I cannot confirm the Driver outcome. Please inspect the work before retrying.' }; return work[msg.phase] || endings[msg.phase] || null; }
24
+ if (msg.domain === 'studio' && msg.phase === 'rendered') return 'The requested workbench content is visibly rendered in this window.';
25
+ if (msg.domain === 'studio' && msg.phase === 'render_unknown') return 'The service returned, but I could not confirm that the workbench content is visible.';
26
+ if (msg.domain === 'studio' && msg.phase === 'delivered') return 'I sent the requested content to the workbench. Its visible result is unconfirmed.';
27
+ if (msg.domain === 'studio' && msg.phase === 'completed') return 'I finished the requested workbench operation.';
28
+ return endings[msg.phase] || null;
29
+ }
30
+ export function validActivity(msg) {
31
+ return msg && msg.version === 1 && typeof msg.id === 'string' && /^activity:[\w-]{36}$/.test(msg.id) &&
32
+ typeof msg.sourceSessionOriginId === 'string' && !!msg.sourceSessionOriginId && typeof msg.sourceMateStamp === 'string' &&
33
+ typeof msg.recipientViewerRef === 'string' && /^viewer:[\w-]{36}$/.test(msg.recipientViewerRef) && Number.isSafeInteger(msg.sourceQueryGeneration) &&
34
+ Number.isSafeInteger(msg.order) && msg.order > 0 && Number.isSafeInteger(msg.sequence) && msg.sequence > 0 &&
35
+ (msg.phase === 'clear' || Object.hasOwn(domains, msg.domain) && !!activityCopy(msg));
36
+ }
@@ -0,0 +1,44 @@
1
+ import { visibleTarget } from './clay-control-target-visibility.js';
2
+ import { store } from './store.js';
3
+
4
+ function node(selector, id, revision, field) {
5
+ return Array.from(document.querySelectorAll(selector)).find(function (item) {
6
+ return item.dataset[field] === id && item.dataset.controlRevision === String(revision) && item.dataset.controlRuntime === store.get('controlRuntimeId');
7
+ });
8
+ }
9
+ // Fixed object adapters verify authoritative client state before locating rendered identities.
10
+ export function detailedControlTarget(target) {
11
+ if (target.kind === 'notifications') {
12
+ var notifications = store.get('clayControlNotifications') || [];
13
+ var nodes = target.items.map(function (item) {
14
+ if (!notifications.some(function (saved) { return saved.id === item.id && saved.controlRevision === item.revision; })) return null;
15
+ return Array.from(document.querySelectorAll('.notif-banner[data-notif-id]')).find(function (el) { return el.dataset.notifId === item.id && el.dataset.controlRevision === item.revision; });
16
+ });
17
+ return nodes.every(function (item) { return visibleTarget(item); }) ? nodes.length === 1 ? nodes[0] : nodes[0].parentElement : null;
18
+ }
19
+ if (target.projectSlug !== store.get('currentSlug') || target.runtimeId !== store.get('controlRuntimeId')) return null;
20
+ if (target.kind === 'git-set') {
21
+ var git = store.get('clayControlGitView');
22
+ if (!git || git.repository !== target.repository || git.runtimeId !== target.runtimeId || git.projectSlug !== target.projectSlug) return null;
23
+ var rows = target.files.map(function (file) { return git.files.some(function (saved) { return saved.path === file.path && saved.revision === file.revision; }) && node('.git-file-row', file.path, file.revision, 'controlGitPath'); });
24
+ if (!rows.every(function (item) { return visibleTarget(item); })) return null;
25
+ return document.querySelector('#git-panel-body');
26
+ }
27
+ if (target.kind !== 'knowledge' || !target.objectId) return null;
28
+ var main = (store.get('mateKnowledgeWorkspaces') || {})[target.projectSlug];
29
+ if (!main) return null;
30
+ if (target.object === 'document') {
31
+ var file = main.files.concat(main.deletedFiles || []).find(function (item) { return !item.common && item.id === target.objectId && item.name === target.name && String(item.revision) === target.revision; });
32
+ return file && node('.knowledge-file-row', 'local:' + file.id, target.revision, 'openDocument');
33
+ }
34
+ if (target.object === 'database') {
35
+ var database = main.files.find(function (item) { return !item.common && item.id === target.databaseId && String(item.revision) === target.revision; });
36
+ return database && node('.knowledge-file-row', 'local:' + target.databaseId, target.revision, 'openDocument');
37
+ }
38
+ var db = ((store.get('mateKnowledgeDatabaseWorkspaces') || {})[target.projectSlug] || {}).databases;
39
+ var view = db && db[target.databaseId];
40
+ if (!view || !view.database || String(view.database.revision) !== target.databaseRevision) return null;
41
+ if (target.object === 'database') return node('.knowledge-file-row', 'local:' + target.databaseId, target.revision, 'openDocument');
42
+ var record = (view.records || []).concat(view.recordDetail && view.recordDetail.record || []).find(function (item) { return item.id === target.objectId && String(item.revision) === target.revision; });
43
+ return record && Array.from(document.querySelectorAll('[data-db-record][data-db-database]')).find(function (item) { return item.dataset.dbRecord === record.id && item.dataset.dbDatabase === target.databaseId && item.dataset.controlRevision === target.revision && item.dataset.controlRuntime === target.runtimeId; });
44
+ }
@@ -0,0 +1,52 @@
1
+ import { studioControlTarget } from './clay-studio-render.js';
2
+ import { store } from './store.js';
3
+ import { detailedControlTarget } from './clay-control-detail-targets.js';
4
+ import { contextualMateStamp } from './contextual-mate-identity.js';
5
+
6
+ // Server projections identify the object; fixed adapters only locate its rendered view.
7
+ export function objectControlTarget(target) {
8
+ if (target.kind === 'surface') return studioControlTarget(target);
9
+ var detailed = detailedControlTarget(target);
10
+ if (['knowledge', 'notifications', 'git-set'].indexOf(target.kind) >= 0) return detailed;
11
+ if (target.kind === 'mate') {
12
+ var mate = (store.get('cachedMatesList') || []).find(function (item) { return item.id === target.mateId && !item.archived && !item.lifecycleDeletion && contextualMateStamp(item) === target.mateStamp; });
13
+ if (!mate) return null;
14
+ return Array.from(document.querySelectorAll('.icon-strip-project-mate[data-mate-id], .icon-strip-brand')).find(function (item) {
15
+ return item.dataset.mateId === mate.id && item.dataset.controlMateStamp === target.mateStamp || mate.builtinKey === 'clay' && item.classList.contains('icon-strip-brand');
16
+ });
17
+ }
18
+ var project = (store.get('projectsHubList') || []).find(function (item) { return item.slug === target.projectSlug && item.controlRuntimeId === target.runtimeId && item.contentAccess !== false; });
19
+ if (target.kind === 'project') {
20
+ if (!project) return null;
21
+ return Array.from(document.querySelectorAll('#icon-strip-projects .icon-strip-item[href]')).find(function (item) { return item.dataset.controlRuntime === target.runtimeId && new URL(item.href, location.href).pathname === '/p/' + encodeURIComponent(target.projectSlug) + '/'; });
22
+ }
23
+ if (target.projectSlug !== store.get('currentSlug') || target.runtimeId !== store.get('controlRuntimeId')) return null;
24
+ if (target.kind === 'record') {
25
+ var selector = target.area === 'issues' ? '.issue-row[data-open-issue], .issue-detail[data-control-ref]' : '.project-log-row[data-ref]';
26
+ return Array.from(document.querySelectorAll(selector)).find(function (item) {
27
+ return (item.dataset.openIssue || item.dataset.controlRef || item.dataset.ref) === target.ref && item.dataset.controlRevision === target.revision && item.dataset.controlRuntime === target.runtimeId;
28
+ });
29
+ }
30
+ if (target.kind === 'file') {
31
+ var file = store.get('clayControlFileView');
32
+ if (!file || file.path !== target.path || file.revision !== target.revision || file.projectSlug !== target.projectSlug || file.runtimeId !== target.runtimeId) return null;
33
+ return document.querySelector('#file-viewer .file-viewer-header');
34
+ }
35
+ if (target.kind === 'git') {
36
+ var git = store.get('clayControlGitView');
37
+ if (!git || git.projectSlug !== target.projectSlug || git.runtimeId !== target.runtimeId || git.repository !== target.repository || !git.files.some(function (item) { return item.path === target.path && item.revision === target.revision; })) return null;
38
+ return Array.from(document.querySelectorAll('.git-file-row[data-control-git-path]')).find(function (item) { return item.dataset.controlGitPath === target.path && item.dataset.controlRevision === target.revision && item.dataset.controlRuntime === target.runtimeId; });
39
+ }
40
+ if (target.kind === 'terminal') {
41
+ var terminal = (store.get('clayControlTerminalViews') || []).find(function (item) { return item.id === target.id && item.identity === target.identity && item.revision === target.revision && item.runtimeId === target.runtimeId; });
42
+ if (!terminal) return null;
43
+ return Array.from(document.querySelectorAll('.terminal-tab[data-control-terminal-id]')).find(function (item) { return item.dataset.controlTerminalId === String(target.id) && item.dataset.controlRuntime === target.runtimeId; });
44
+ }
45
+ if (target.kind === 'schedule') {
46
+ if (target.sessionId !== store.get('activeSessionId') || target.sessionOriginId !== store.get('sessionOriginId')) return null;
47
+ var queue = store.get('pendingMessageQueue');
48
+ var logical = queue && (queue.items || []).some(function (item) { return item.schedule && item.schedule.logicalScheduleId === target.id && item.schedule.logicalRevision === target.revision; });
49
+ return Array.from(document.querySelectorAll('.scheduled-task-row[data-task-id], .pending-message-chip[data-control-schedule-id]')).find(function (item) { return (item.dataset.taskId === target.id || logical && item.dataset.controlScheduleId === target.id) && item.dataset.controlRevision === target.revision && item.dataset.controlRuntime === target.runtimeId; });
50
+ }
51
+ return null;
52
+ }
@@ -0,0 +1,12 @@
1
+ export function visibleTarget(element) {
2
+ if (!element || !element.isConnected || element.closest('[hidden], [inert], [aria-hidden="true"]')) return false;
3
+ var rect = element.getBoundingClientRect(), style = getComputedStyle(element);
4
+ if (style.visibility !== 'visible' || Number(style.opacity) === 0 || !rect.width || !rect.height || rect.left < 0 || rect.top < 0 || rect.right > innerWidth || rect.bottom > innerHeight) return false;
5
+ for (var parent = element.parentElement; parent; parent = parent.parentElement) {
6
+ if (Number(getComputedStyle(parent).opacity) === 0) return false;
7
+ }
8
+ return [rect.left + 2, rect.left + rect.width / 2, rect.right - 2].every(function (x) {
9
+ var hit = document.elementFromPoint(x, rect.top + rect.height / 2);
10
+ return hit && element.contains(hit);
11
+ });
12
+ }
@@ -0,0 +1,44 @@
1
+ import { visibleTarget } from './clay-control-target-visibility.js';
2
+ export { visibleTarget };
3
+ import { objectControlTarget } from './clay-control-object-targets.js';
4
+ import { store } from './store.js';
5
+
6
+ // Stable product adapters only. Activity data never supplies a CSS selector.
7
+ export function controlTarget(target) {
8
+ if (!target) return null;
9
+ if (target.kind !== 'session') { var object = objectControlTarget(target); return visibleTarget(object) ? object : null; }
10
+ if (target.projectSlug !== store.get('currentSlug')) return null;
11
+ if (target.kind === 'session' && Number.isSafeInteger(target.sessionId) && typeof target.sessionOriginId === 'string' && target.sessionOriginId) {
12
+ return Array.from(document.querySelectorAll('.session-item[data-session-id]')).find(function (row) {
13
+ return Number(row.dataset.sessionId) === target.sessionId && row.dataset.controlSessionOrigin === target.sessionOriginId && visibleTarget(row);
14
+ }) || null;
15
+ }
16
+ return null;
17
+ }
18
+
19
+ function overlaps(a, b) { return a.left < b.right && a.right > b.left && a.top < b.bottom && a.bottom > b.top; }
20
+ export function activityPlacement(row, width, height, obstacles) {
21
+ var margin = 12, rect = row && row.getBoundingClientRect();
22
+ var candidates = [];
23
+ if (rect) {
24
+ // Keep the cursor shaft seated in the identity pill in either vertical direction.
25
+ candidates.push({ left: rect.right + 8, top: rect.top + rect.height / 2 + 20, tipX: rect.right - 2, tipY: rect.top + rect.height / 2 });
26
+ candidates.push({ left: rect.right + 8, top: rect.top + rect.height / 2 - 34, tipX: rect.right - 2, tipY: rect.top + rect.height / 2, flip: true });
27
+ candidates.push({ left: rect.left - width - 8, top: rect.top + rect.height / 2 + 20, tipX: rect.left + 2, tipY: rect.top + rect.height / 2, reverse: true });
28
+ }
29
+ candidates.push({ left: innerWidth - width - margin, top: 64 });
30
+ candidates.push({ left: margin, top: 64 });
31
+ candidates.push({ left: innerWidth - width - margin, top: innerHeight - height - 80 });
32
+ candidates.push({ left: (innerWidth - width) / 2, top: 64 });
33
+ obstacles.slice(0, 12).forEach(function (obstacle) {
34
+ candidates.push({ left: innerWidth - width - margin, top: obstacle.bottom + 8 });
35
+ candidates.push({ left: margin, top: obstacle.bottom + 8 });
36
+ candidates.push({ left: (innerWidth - width) / 2, top: obstacle.bottom + 8 });
37
+ });
38
+ return candidates.find(function (candidate) {
39
+ var box = { left: candidate.left, right: candidate.left + width, top: candidate.top, bottom: candidate.top + height };
40
+ if (box.left < margin || box.right > innerWidth - margin || box.top < margin || box.bottom > innerHeight - margin) return false;
41
+ if (rect && overlaps(box, rect)) return false;
42
+ return !obstacles.some(function (obstacle) { return overlaps(box, obstacle); });
43
+ }) || null;
44
+ }
@@ -0,0 +1,52 @@
1
+ import { store } from './store.js';
2
+ import { getWs } from './ws-ref.js';
3
+
4
+ function visible(element) {
5
+ if (!element || !element.isConnected || element.closest('[hidden], [inert], [aria-hidden="true"]')) return false;
6
+ var box = element.getBoundingClientRect(), style = getComputedStyle(element);
7
+ if (style.visibility !== 'visible' || Number(style.opacity) === 0 || box.width <= 0 || box.height <= 0) return false;
8
+ for (var parent = element.parentElement; parent; parent = parent.parentElement) if (getComputedStyle(parent).visibility !== 'visible' || Number(getComputedStyle(parent).opacity) === 0) return false;
9
+ var left = Math.max(0, box.left), right = Math.min(innerWidth, box.right), top = Math.max(0, box.top), bottom = Math.min(innerHeight, box.bottom);
10
+ return right > left && bottom > top && [0.2, 0.5, 0.8].some(function (x) {
11
+ return [0.2, 0.5, 0.8].some(function (y) { var hit = document.elementFromPoint(left + (right - left) * x, top + (bottom - top) * y); return hit && element.contains(hit); });
12
+ });
13
+ }
14
+ // Bounded evidence from the exact authenticated document, never navigation receipt.
15
+ export function observeNavigationRendering(message, socket) {
16
+ var previous = store.get('clayNavigationRendering');
17
+ if (previous) previous.finish(false);
18
+ var target = message.target, owner = store.get('myUserId'), settled = false, stable = 0, frame, timer;
19
+ function finish(rendered) {
20
+ if (settled) return;
21
+ settled = true; cancelAnimationFrame(frame); clearTimeout(timer); unsubscribe();
22
+ document.removeEventListener('visibilitychange', hidden); document.removeEventListener('click', stop, true);
23
+ if (store.get('clayNavigationRendering') === attempt) store.set({ clayNavigationRendering: null });
24
+ if (getWs() === socket && socket.readyState === 1) socket.send(JSON.stringify({ type: 'clay_navigation_rendered', deliveryId: message.deliveryId,
25
+ renderToken: message.renderToken, visible: rendered, projectSlug: target.projectSlug, sessionId: target.sessionId,
26
+ sessionOriginId: target.sessionOriginId, surface: 'conversation', entryIndex: target.entryIndex }));
27
+ }
28
+ var attempt = { finish: finish };
29
+ store.set({ clayNavigationRendering: attempt });
30
+ function hidden() { if (document.hidden) finish(false); }
31
+ var unsubscribe = store.subscribe(function (value, old) {
32
+ if (!value.connected || value.myUserId !== owner || getWs() !== socket || value.currentSlug !== old.currentSlug && value.currentSlug !== target.projectSlug || value.activeSessionId !== old.activeSessionId && value.currentSlug === target.projectSlug && value.activeSessionId !== target.sessionId) finish(false);
33
+ });
34
+ function stop(event) { if (event.target.closest('#generation-stop, #autonomous-run-stop, #send-btn.stop')) finish(false); }
35
+ document.addEventListener('click', stop, true);
36
+ document.addEventListener('visibilitychange', hidden);
37
+ timer = setTimeout(function () { finish(false); }, Math.max(0, Math.min(5000, message.renderDeadline - Date.now())));
38
+ function inspect() {
39
+ if (settled) return;
40
+ if (document.hidden || getWs() !== socket || !store.get('connected') || store.get('myUserId') !== owner) { finish(false); return; }
41
+ var transcript = document.getElementById('messages');
42
+ var exact = store.get('currentSlug') === target.projectSlug && store.get('activeSessionId') === target.sessionId && store.get('sessionOriginId') === target.sessionOriginId &&
43
+ !store.get('paneMode') && !store.get('dmMode') && !store.get('homeShellVisible') && !store.get('replayingHistory') && transcript &&
44
+ transcript.dataset.controlSessionId === String(target.sessionId) && transcript.dataset.controlSessionOrigin === target.sessionOriginId && visible(transcript);
45
+ if (exact && target.entryIndex !== undefined) exact = transcript.dataset.controlHistoryRevision === target.historyRevision && Array.from(transcript.querySelectorAll('[data-history-index], [data-history-indexes]')).some(function (item) {
46
+ return (item.dataset.historyIndex === String(target.entryIndex) || (item.dataset.historyIndexes || '').split(',').indexOf(String(target.entryIndex)) >= 0) && visible(item);
47
+ });
48
+ stable = exact ? stable + 1 : 0;
49
+ if (stable >= 2) finish(true); else frame = requestAnimationFrame(inspect);
50
+ }
51
+ frame = requestAnimationFrame(inspect);
52
+ }
@@ -1,8 +1,11 @@
1
+ import { observeNavigationRendering } from './clay-navigation-render.js';
2
+ import { dismissContextualClay } from './contextual-clay.js';
3
+ import { showClayNavigationActivity, clearClayControlActivity } from './clay-control-activity.js';
1
4
  import { store } from './store.js';
2
5
  import { getWs } from './ws-ref.js';
3
6
  import { navigateToSession } from './command-palette.js';
4
7
  function context() {
5
- return JSON.stringify([store.get('currentSlug'), store.get('activeSessionId'), store.get('dmMode'), store.get('homeShellVisible'), store.get('homeChatMateId'), store.get('homeChatSessionId'), store.get('homeSubSurface')]);
8
+ return JSON.stringify([store.get('currentSlug'), store.get('activeSessionId'), store.get('dmMode'), store.get('homeShellVisible'), store.get('homeChatMateId'), store.get('homeChatSessionId'), store.get('homeSubSurface'), (store.get('contextualClay') || {}).mode]);
6
9
  }
7
10
  export function handleOwnerNavigation(msg) {
8
11
  if (msg.type !== 'clay_navigation_offer' && msg.type !== 'clay_navigation_apply') return false;
@@ -19,9 +22,15 @@ export function handleOwnerNavigation(msg) {
19
22
  var accepted = offer && offer.id === msg.deliveryId && offer.socket === ws && offer.context === context() && !document.hidden && !store.get('paneMode');
20
23
  var status = 'refused';
21
24
  if (accepted && msg.target && typeof msg.target.projectSlug === 'string' && Number.isSafeInteger(msg.target.sessionId)) {
22
- try { navigateToSession(msg.target); status = 'accepted'; } catch (error) { status = 'failed'; }
25
+ try {
26
+ var contextual = store.get('contextualClay') || {};
27
+ if (msg.target.contextualSourceMateId && contextual.mateId === msg.target.contextualSourceMateId && contextual.mode === 'popover') dismissContextualClay(false);
28
+ showClayNavigationActivity(msg.target, false); navigateToSession(msg.target, null, msg.target.entryIndex); status = 'accepted';
29
+ } catch (error) { showClayNavigationActivity(msg.target, true); status = 'failed'; }
23
30
  }
31
+ if (status === 'refused') clearClayControlActivity();
24
32
  // This acknowledges navigator invocation only. Rendering may finish after reconnect.
25
33
  if (ws.readyState === 1) ws.send(JSON.stringify({ type: 'clay_navigation_ack', deliveryId: msg.deliveryId, status: status }));
34
+ if (status === 'accepted') observeNavigationRendering(msg, ws);
26
35
  return true;
27
36
  }