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
package/lib/sessions.js CHANGED
@@ -3,7 +3,7 @@ var path = require("path");
3
3
  var config = require("./config");
4
4
  var utils = require("./utils");
5
5
  var users = require("./users");
6
- var { CODEX_DEFAULTS } = require("./codex-defaults");
6
+ var providerDefaults = require("./yoke/settings").defaults();
7
7
  var hygiene = require("./session-hygiene");
8
8
  var backgroundTaskTiming = require("./background-task-timing");
9
9
  var sessionProvenance = require("./session-provenance");
@@ -39,16 +39,16 @@ function createSessionManager(opts) {
39
39
  var permissionRequestIndex = {}; // requestId -> sessionLocalId (O(1) lookup)
40
40
  var capabilitiesByVendor = null; // set by sdk-bridge after adapter init
41
41
  var defaultVendor = null; // set by sdk-bridge
42
- var codexApproval = CODEX_DEFAULTS.approval;
43
- var codexSandbox = CODEX_DEFAULTS.sandbox;
44
- var codexWebSearch = CODEX_DEFAULTS.webSearch;
42
+ var codexApproval = providerDefaults.codexApproval;
43
+ var codexSandbox = providerDefaults.codexSandbox;
44
+ var codexWebSearch = providerDefaults.codexWebSearch;
45
45
 
46
46
  // --- Session persistence (centralized in ~/.clay/sessions/{encoded-cwd}/) ---
47
47
  var sessionsBase = opts.sessionsBase || path.join(config.CONFIG_DIR, "sessions");
48
48
  var encodedCwd = utils.resolveEncodedDir(sessionsBase, cwd);
49
49
  var sessionsDir = path.join(sessionsBase, encodedCwd);
50
50
  fs.mkdirSync(sessionsDir, { recursive: true });
51
- var cliSessionsDir = opts.cliSessionsDir || path.join(config.REAL_HOME, ".claude", "projects", utils.encodeCwd(cwd));
51
+ var cliSessionsDir = opts.cliSessionsDir || require("./yoke/legacy-sessions").directory(config.REAL_HOME, cwd);
52
52
  var deletedCliSessionsPath = path.join(sessionsDir, "deleted-cli-sessions.json");
53
53
  var deletedCliSessionIds = new Set();
54
54
  try {
@@ -77,29 +77,6 @@ function createSessionManager(opts) {
77
77
  }
78
78
  }
79
79
 
80
- function readCliTranscriptHead(filePath) {
81
- var MAX_READ = 64 * 1024;
82
- try {
83
- var stat = fs.statSync(filePath);
84
- var fd = fs.openSync(filePath, "r");
85
- var bytesToRead = Math.min(stat.size, MAX_READ);
86
- var buf = Buffer.alloc(bytesToRead);
87
- fs.readSync(fd, buf, 0, bytesToRead, 0);
88
- fs.closeSync(fd);
89
- var lines = buf.toString("utf8").split("\n");
90
- var events = [];
91
- for (var i = 0; i < lines.length; i++) {
92
- if (!lines[i]) continue;
93
- try {
94
- var event = JSON.parse(lines[i]);
95
- if (event && typeof event === "object") events.push(event);
96
- } catch (e) {}
97
- }
98
- return events;
99
- } catch (e) {
100
- return null;
101
- }
102
- }
103
80
 
104
81
  // Auto-migrate sessions from legacy locations:
105
82
  // v1: {cwd}/.claude-relay/sessions/
@@ -234,8 +211,11 @@ function createSessionManager(opts) {
234
211
  if (session.claySchedulingReceipts) metaObj.claySchedulingReceipts = session.claySchedulingReceipts;
235
212
  if (session.mateCreationMode) metaObj.mateCreationMode = true;
236
213
  if (session.homeMateCreationPhase) metaObj.homeMateCreationPhase = session.homeMateCreationPhase;
214
+ if (session.contextualCreationMateStamp) metaObj.contextualCreationMateStamp = session.contextualCreationMateStamp;
215
+ if (session.contextualCreationRequestId) metaObj.contextualCreationRequestId = session.contextualCreationRequestId;
237
216
  if (session.homeClayEntryRequestId) metaObj.homeClayEntryRequestId = session.homeClayEntryRequestId;
238
217
  if (session.homeClayEntryMode) metaObj.homeClayEntryMode = session.homeClayEntryMode;
218
+ if (session.homeClayEntryText) metaObj.homeClayEntryText = session.homeClayEntryText;
239
219
  metaObj.sessionOriginId = provenanceMeta.sessionOriginId;
240
220
  if (provenanceMeta.sessionProvenance) metaObj.sessionProvenance = provenanceMeta.sessionProvenance;
241
221
  if (Array.isArray(session._workerGenerations)) metaObj.workerGenerations = session._workerGenerations;
@@ -398,8 +378,11 @@ function createSessionManager(opts) {
398
378
  if (Array.isArray(m.claySchedulingReceipts)) session.claySchedulingReceipts = m.claySchedulingReceipts;
399
379
  if (m.mateCreationMode) session.mateCreationMode = true;
400
380
  if (m.homeMateCreationPhase) session.homeMateCreationPhase = m.homeMateCreationPhase;
381
+ if (m.contextualCreationMateStamp) session.contextualCreationMateStamp = m.contextualCreationMateStamp;
382
+ if (m.contextualCreationRequestId) session.contextualCreationRequestId = m.contextualCreationRequestId;
401
383
  if (m.homeClayEntryRequestId) session.homeClayEntryRequestId = m.homeClayEntryRequestId;
402
384
  if (m.homeClayEntryMode) session.homeClayEntryMode = m.homeClayEntryMode;
385
+ if (m.homeClayEntryText) session.homeClayEntryText = m.homeClayEntryText;
403
386
  if (m.ownerId) session.ownerId = m.ownerId;
404
387
  session.sessionOriginId = m.sessionOriginId || null;
405
388
  session._provenanceMigrationNeeded = sessionProvenance.restore(m, session);
@@ -470,9 +453,6 @@ function createSessionManager(opts) {
470
453
  // - Warmup shape: 1 real "hi" prompt, optional automatic interruption,
471
454
  // and 0 assistant messages (covered by daemon cleanup but defensive)
472
455
  function adoptOrphanedCliSessions() {
473
- var files;
474
- try { files = fs.readdirSync(cliSessionsDir); } catch (e) { return; }
475
-
476
456
  // Build set of cliSessionIds Clay already tracks
477
457
  var knownCliIds = new Set();
478
458
  sessions.forEach(function (s) {
@@ -481,56 +461,10 @@ function createSessionManager(opts) {
481
461
 
482
462
  var adopted = 0;
483
463
 
484
- for (var i = 0; i < files.length; i++) {
485
- if (!files[i].endsWith(".jsonl")) continue;
486
- var cliSid = files[i].slice(0, -".jsonl".length);
487
- if (knownCliIds.has(cliSid)) continue;
488
- if (deletedCliSessionIds.has(cliSid)) continue;
489
-
490
- var fp = path.join(cliSessionsDir, files[i]);
491
- var events = readCliTranscriptHead(fp);
492
- if (!events) continue;
493
- var userCount = 0;
494
- var firstUserText = null;
495
- var createdAtIso = null;
496
- for (var li = 0; li < events.length; li++) {
497
- var ev = events[li];
498
- if (ev.type === "user" && ev.message && ev.message.role === "user") {
499
- userCount++;
500
- if (firstUserText == null) {
501
- var c = ev.message.content;
502
- if (typeof c === "string") {
503
- firstUserText = c;
504
- } else if (Array.isArray(c)) {
505
- var parts = [];
506
- for (var ci = 0; ci < c.length; ci++) {
507
- if (c[ci] && c[ci].type === "text" && typeof c[ci].text === "string") parts.push(c[ci].text);
508
- }
509
- firstUserText = parts.join("");
510
- }
511
- if (ev.timestamp && !createdAtIso) createdAtIso = ev.timestamp;
512
- }
513
- }
514
- }
515
-
516
- if (userCount === 0) continue;
517
- // Defensive warmup skip (daemon also cleans these)
518
- if (hygiene.isClaudeWarmupTranscript(events)) continue;
519
-
520
- // Title: first user message trimmed/truncated. Fall back to a
521
- // neutral label so the sidebar entry is identifiable.
522
- var title = (firstUserText || "").trim().replace(/\s+/g, " ");
523
- if (title.length > 60) title = title.slice(0, 57) + "...";
524
- if (!title) title = "Imported CLI session";
525
-
526
- var createdAt = Date.now();
527
- if (createdAtIso) {
528
- var t = Date.parse(createdAtIso);
529
- if (!isNaN(t)) createdAt = t;
530
- }
531
- var lastActivity = createdAt;
532
- try { lastActivity = fs.statSync(fp).mtimeMs; } catch (e) {}
533
-
464
+ var candidates = require("./yoke/legacy-sessions").candidates(cliSessionsDir, knownCliIds, deletedCliSessionIds);
465
+ for (var i = 0; i < candidates.length; i++) {
466
+ var candidate = candidates[i], cliSid = candidate.id;
467
+ var title = candidate.title, createdAt = candidate.createdAt, lastActivity = candidate.lastActivity;
534
468
  var localId = nextLocalId++;
535
469
  var session = {
536
470
  localId: localId,
@@ -548,7 +482,7 @@ function createSessionManager(opts) {
548
482
  history: [],
549
483
  messageUUIDs: [],
550
484
  lastRewindUuid: null,
551
- vendor: "claude",
485
+ vendor: candidate.vendor,
552
486
  mode: "tui",
553
487
  // Adopted from an external `claude` run in this cwd, not started by
554
488
  // Clay. Notification emitters must stay silent for these until the
@@ -580,10 +514,7 @@ function createSessionManager(opts) {
580
514
  var stale = [];
581
515
  sessions.forEach(function (session) {
582
516
  if (!session.adopted || !session.cliSessionId) return;
583
- var transcriptPath = path.join(cliSessionsDir, session.cliSessionId + ".jsonl");
584
- var events = readCliTranscriptHead(transcriptPath);
585
- var missingEmptySource = !fs.existsSync(transcriptPath) && (!session.history || session.history.length === 0);
586
- if (missingEmptySource || (events && hygiene.isClaudeWarmupTranscript(events))) stale.push(session.localId);
517
+ if (require("./yoke/legacy-sessions").obsolete(cliSessionsDir, session)) stale.push(session.localId);
587
518
  });
588
519
  for (var i = 0; i < stale.length; i++) deleteSessionQuiet(stale[i]);
589
520
  if (stale.length > 0) {
@@ -636,6 +567,7 @@ function createSessionManager(opts) {
636
567
  var unreadMap = wsUnread || singleUserUnread;
637
568
  return {
638
569
  id: s.localId,
570
+ sessionOriginId: s.sessionOriginId || null,
639
571
  cliSessionId: s.cliSessionId || null,
640
572
  createdAt: s.createdAt || 0,
641
573
  title: s.title || "New Session",
@@ -877,7 +809,8 @@ function createSessionManager(opts) {
877
809
  if (_item && (_item.type === "mention_user" || _item.type === "mention_response")) {
878
810
  console.log("[DEBUG replayHistory] sending mention at index=" + i + " from=" + fromIndex + " total=" + total + " type=" + _item.type + " mate=" + (_item.mateName || ""));
879
811
  }
880
- var replayItem = transform ? transform(_item) : _item;
812
+ var normalizedItem = require("./yoke/usage").clientEvent(_item, session.vendor || "claude");
813
+ var replayItem = transform ? transform(normalizedItem) : normalizedItem;
881
814
  if (replayItem && typeof replayItem === "object") replayItem = Object.assign({}, replayItem, { _historyIndex: i });
882
815
  _send(replayItem);
883
816
  }
@@ -887,9 +820,11 @@ function createSessionManager(opts) {
887
820
  var lastModelUsage = null;
888
821
  var lastCost = null;
889
822
  var lastStreamInputTokens = null;
823
+ var lastContext = null;
890
824
  for (var j = total - 1; j >= 0; j--) {
891
825
  if (session.history[j].type === "result") {
892
- var r = session.history[j];
826
+ var r = require("./yoke/usage").clientEvent(session.history[j], session.vendor || "claude");
827
+ lastContext = r.context || null;
893
828
  lastUsage = r.usage || null;
894
829
  lastModelUsage = r.modelUsage || null;
895
830
  lastCost = r.cost != null ? r.cost : null;
@@ -898,7 +833,7 @@ function createSessionManager(opts) {
898
833
  }
899
834
  }
900
835
 
901
- _send({ type: "history_done", lastUsage: lastUsage, lastModelUsage: lastModelUsage, lastCost: lastCost, lastStreamInputTokens: lastStreamInputTokens, contextUsage: session.lastContextUsage || null });
836
+ _send({ type: "history_done", controlHistoryRevision: require("./clay-control-target-projection").digest(session.history || []), sessionId: session.localId, sessionOriginId: session.sessionOriginId || null, lastUsage: lastUsage, lastModelUsage: lastModelUsage, lastCost: lastCost, lastStreamInputTokens: lastStreamInputTokens, context: lastContext, contextUsage: require("./yoke/usage").context(session.lastContextUsage, session.vendor || "claude") });
902
837
  for (var vi = 0; vi < sessionViewedListeners.length; vi++) {
903
838
  try { sessionViewedListeners[vi](session, targetWs, _send); } catch (e) {}
904
839
  }
@@ -83,7 +83,7 @@ function createTerminalManager(opts) {
83
83
  }
84
84
 
85
85
  // Broadcast to subscribers
86
- var msg = JSON.stringify({ type: "term_output", id: id, data: data });
86
+ var msg = JSON.stringify(Object.assign({ type: "term_output", id: id, data: data }, controlProjection(id)));
87
87
  for (var ws of session.subscribers) {
88
88
  if (ws.readyState === 1) ws.send(msg);
89
89
  }
@@ -108,7 +108,7 @@ function createTerminalManager(opts) {
108
108
  }
109
109
 
110
110
  // Broadcast updated list
111
- send({ type: "term_list", terminals: list() });
111
+ send({ type: "term_list", terminals: list(true) });
112
112
 
113
113
  // Caller-supplied hook: e.g. TUI session manager uses this to delete
114
114
  // its session record once `claude` exits, so stale entries don't pile
@@ -181,10 +181,11 @@ function createTerminalManager(opts) {
181
181
  session.cols = cols;
182
182
  session.rows = rows;
183
183
  // Notify other subscribers about the resize so their xterm stays in sync
184
- var msg = JSON.stringify({ type: "term_resized", id: id, cols: cols, rows: rows });
184
+ var msg = JSON.stringify(Object.assign({ type: "term_resized", id: id, cols: cols, rows: rows }, controlProjection(id)));
185
185
  for (var ws of session.subscribers) {
186
186
  if (ws.readyState === 1 && ws !== sourceWs) ws.send(msg);
187
187
  }
188
+ if (sourceWs && sourceWs.readyState === 1) sourceWs.send(JSON.stringify(Object.assign({ type: "term_control_projection", id: id }, controlProjection(id))));
188
189
  } catch (e) {}
189
190
  }
190
191
  }
@@ -244,15 +245,24 @@ function createTerminalManager(opts) {
244
245
  session.title = String(title).substring(0, 50);
245
246
  }
246
247
 
247
- function list() {
248
+ function inspect(id) {
249
+ var terminal = terminals.get(id);
250
+ return terminal ? { id: id, identity: terminal.identity, ownerId: terminal.ownerId, osIdentity: terminal.osIdentity, title: terminal.title, kind: terminal.kind, exited: terminal.exited, cols: terminal.cols, rows: terminal.rows, totalBytesWritten: terminal.totalBytesWritten } : null;
251
+ }
252
+
253
+ function controlProjection(id) { var item = inspect(id); return item ? { controlIdentity: item.identity, controlRevision: require('./clay-control-target-projection').digest(item) } : {}; }
254
+
255
+ function list(control) {
248
256
  var result = [];
249
257
  for (var session of terminals.values()) {
250
- result.push({
258
+ var entry = {
251
259
  id: session.id,
252
260
  title: session.title,
253
261
  kind: session.kind,
254
262
  exited: session.exited,
255
- });
263
+ };
264
+ if (control) { entry.controlIdentity = session.identity; entry.controlRevision = require('./clay-control-target-projection').digest(inspect(session.id)); }
265
+ result.push(entry);
256
266
  }
257
267
  return result;
258
268
  }
@@ -333,10 +343,7 @@ function createTerminalManager(opts) {
333
343
  resize: resize,
334
344
  close: close,
335
345
  closeAndWait: closeAndWait,
336
- inspect: function (id) {
337
- var terminal = terminals.get(id);
338
- return terminal ? { id: id, identity: terminal.identity, ownerId: terminal.ownerId, osIdentity: terminal.osIdentity, title: terminal.title, kind: terminal.kind, exited: terminal.exited, cols: terminal.cols, rows: terminal.rows, totalBytesWritten: terminal.totalBytesWritten } : null;
339
- },
346
+ inspect: inspect,
340
347
  resizeExact: function (id, cols, rows, identity, revalidate) {
341
348
  revalidate();
342
349
  var terminal = terminals.get(id);
@@ -345,7 +352,7 @@ function createTerminalManager(opts) {
345
352
  revalidate();
346
353
  if (terminals.get(id) !== terminal) throw new Error('Terminal was replaced during resize.');
347
354
  terminal.cols = cols; terminal.rows = rows;
348
- var message = JSON.stringify({ type: 'term_resized', id: id, cols: cols, rows: rows });
355
+ var message = JSON.stringify(Object.assign({ type: 'term_resized', id: id, cols: cols, rows: rows }, controlProjection(id)));
349
356
  terminal.subscribers.forEach(function (ws) { if (ws.readyState === 1) ws.send(message); });
350
357
  return true;
351
358
  },
package/lib/tool-llm.js CHANGED
@@ -36,7 +36,7 @@ async function complete(opts) {
36
36
  skipProjectInstructions: true,
37
37
  skipSkills: true,
38
38
  canUseTool: function () { return Promise.resolve({ behavior: "deny", message: "Capsule LLM calls cannot use tools." }); },
39
- adapterOptions: { CLAUDE: { linuxUser: opts.linuxUser || undefined, settingSources: ["user"] } },
39
+ adapterOptions: require("./yoke/query-options").map(adapter, { instructionSources: ["user"], readOnly: true, isolation: { user: opts.linuxUser || undefined } }),
40
40
  },
41
41
  });
42
42
  return result.text;
@@ -1,132 +1 @@
1
- // tui-transcript-index.js
2
- //
3
- // Build a per-session index of assistant text messages from a Claude Code
4
- // TUI transcript (~/.claude/projects/{encoded-cwd}/{cliSessionId}.jsonl).
5
- //
6
- // The TUI message-grab feature uses this index on the client to map a
7
- // hovered xterm region back to its original markdown source. We only
8
- // extract assistant text blocks. Tool calls and tool results render in
9
- // the terminal in a shape that's quite different from their JSONL form,
10
- // so matching them would be noisy and not particularly useful.
11
- //
12
- // Codex sessions don't write transcripts at all; callers should skip
13
- // indexing for non-claude vendors.
14
-
15
- var fs = require("fs");
16
- var path = require("path");
17
- var utils = require("./utils");
18
- var { REAL_HOME } = require("./config");
19
-
20
- var encodeCwd = utils.encodeCwd;
21
-
22
- // Min length of a normalized assistant message to bother indexing.
23
- // Anything shorter ("ok", "sure", "yes") would generate too many false
24
- // positives when matched against a hovered terminal block.
25
- var MIN_MATCH_LEN = 20;
26
-
27
- // Claude Code writes its JSONL under the *running user's* home, which
28
- // is not the daemon's home in OS-isolation mode (where each Clay user
29
- // gets a real Linux account like /home/clay-name). Callers from the
30
- // project context resolve the session owner's home and pass it in;
31
- // when no home is provided we fall back to REAL_HOME for single-user
32
- // installs.
33
- function transcriptFilePath(home, cwd, cliSessionId) {
34
- if (!cwd || !cliSessionId) return null;
35
- var base = home || REAL_HOME;
36
- return path.join(base, ".claude", "projects", encodeCwd(cwd), cliSessionId + ".jsonl");
37
- }
38
-
39
- // matchKey is just whitespace-normalized raw markdown. We intentionally
40
- // do NOT strip markdown decoration here.
41
- //
42
- // Reasoning: TUI rendering drops markdown markers (**bold** -> bold,
43
- // `code` -> code, [text](url) -> text, etc.) and the visible text is
44
- // therefore always a substring of the JSONL source's character stream.
45
- // If we strip server-side we risk getting the rules wrong on corner
46
- // cases (double-backtick code spans, list-bullet rendering quirks,
47
- // nested formatting) and the resulting matchKey diverges from both
48
- // sides at once. Leaving the source alone and letting the client do a
49
- // multi-offset substring probe against block text is more robust and
50
- // much less code.
51
- function normalizeForMatch(s) {
52
- if (!s) return "";
53
- return s.replace(/\s+/g, " ").trim();
54
- }
55
-
56
- // Stable per-message id. Claude's JSONL usually carries one of these but
57
- // fall back to the parsed line index so the client always has a handle.
58
- function messageId(obj, index) {
59
- if (obj && obj.uuid) return String(obj.uuid);
60
- if (obj && obj.message && obj.message.id) return String(obj.message.id);
61
- return "msg-" + index;
62
- }
63
-
64
- // Parse a single JSONL line into one assistant-text record, or null
65
- // when the line isn't one we index (user prompts, tool_use blocks,
66
- // tool_result records, etc.). Text-only assistant blocks get joined
67
- // in order so a multi-block response surfaces as a single grabbable
68
- // message.
69
- function parseAssistantTextLine(line, index) {
70
- var obj;
71
- try { obj = JSON.parse(line); } catch (e) { return null; }
72
- if (!obj || !obj.message) return null;
73
- if (obj.message.role !== "assistant") return null;
74
- if (!Array.isArray(obj.message.content)) return null;
75
-
76
- var text = "";
77
- for (var i = 0; i < obj.message.content.length; i++) {
78
- var block = obj.message.content[i];
79
- if (block && block.type === "text" && typeof block.text === "string") {
80
- text += block.text;
81
- }
82
- }
83
- text = text.trim();
84
- if (!text) return null;
85
-
86
- var normalized = normalizeForMatch(text);
87
- if (normalized.length < MIN_MATCH_LEN) return null;
88
-
89
- return {
90
- id: messageId(obj, index),
91
- text: text,
92
- matchKey: normalized,
93
- };
94
- }
95
-
96
- // Read the whole transcript and return the assistant text index. The
97
- // transcripts are small enough (KB to a few MB) that a sync read on
98
- // session open or on a watcher-driven update is fine. We also return
99
- // the mtime/size so callers can short-circuit re-reads when nothing
100
- // changed.
101
- function readAssistantIndex(home, cwd, cliSessionId) {
102
- var file = transcriptFilePath(home, cwd, cliSessionId);
103
- if (!file) return { messages: [], mtimeMs: 0, byteLength: 0 };
104
- var raw;
105
- var stat;
106
- try {
107
- raw = fs.readFileSync(file, "utf8");
108
- stat = fs.statSync(file);
109
- } catch (e) {
110
- return { messages: [], mtimeMs: 0, byteLength: 0 };
111
- }
112
- var lines = raw.split("\n");
113
- var messages = [];
114
- for (var i = 0; i < lines.length; i++) {
115
- if (!lines[i]) continue;
116
- var entry = parseAssistantTextLine(lines[i], i);
117
- if (entry) messages.push(entry);
118
- }
119
- return {
120
- messages: messages,
121
- mtimeMs: stat.mtimeMs || 0,
122
- byteLength: stat.size || raw.length,
123
- };
124
- }
125
-
126
- module.exports = {
127
- MIN_MATCH_LEN: MIN_MATCH_LEN,
128
- transcriptFilePath: transcriptFilePath,
129
- readAssistantIndex: readAssistantIndex,
130
- parseAssistantTextLine: parseAssistantTextLine,
131
- normalizeForMatch: normalizeForMatch,
132
- };
1
+ module.exports = require("./yoke/tui-transcript-index");
@@ -0,0 +1,31 @@
1
+ // Personal geometry is never a canonical product-control operation.
2
+ function validGeometry(value) {
3
+ if (!value || typeof value !== 'object' || Array.isArray(value)) return false;
4
+ var keys = Object.keys(value);
5
+ if (keys.length !== 4 || keys.some(function (key) { return ['left', 'top', 'width', 'height'].indexOf(key) < 0; })) return false;
6
+ return keys.every(function (key) { return typeof value[key] === 'number' && Number.isFinite(value[key]); }) &&
7
+ value.left >= 0 && value.left <= 16384 && value.top >= 0 && value.top <= 16384 &&
8
+ value.width >= 340 && value.width <= 2560 && value.height >= 360 && value.height <= 2160;
9
+ }
10
+ function attachContextualClayPreferences(deps) {
11
+ function get(userId) {
12
+ var user = deps.loadUsers().users.find(function (entry) { return entry.id === userId; });
13
+ var saved = user && user.contextualClayGeometry;
14
+ if (!user && userId === 'default') saved = require('./config').loadConfig().contextualClayGeometry;
15
+ return validGeometry(saved) ? Object.assign({}, saved) : null;
16
+ }
17
+ function set(userId, geometry) {
18
+ if (!validGeometry(geometry)) return { error: 'Invalid popover geometry.' };
19
+ var value = Object.assign({}, geometry);
20
+ var data = deps.loadUsers();
21
+ var user = data.users.find(function (entry) { return entry.id === userId; });
22
+ if (user) { user.contextualClayGeometry = value; deps.saveUsers(data); }
23
+ else if (userId === 'default') {
24
+ var config = require('./config');
25
+ config.saveConfig(Object.assign({}, config.loadConfig(), { contextualClayGeometry: value }));
26
+ } else return { error: 'Account no longer available.' };
27
+ return { geometry: value };
28
+ }
29
+ return { getContextualClayGeometry: get, setContextualClayGeometry: set };
30
+ }
31
+ module.exports = { attachContextualClayPreferences: attachContextualClayPreferences, validGeometry: validGeometry };
package/lib/users.js CHANGED
@@ -332,6 +332,7 @@ var creation = require("./users-create").attachCreation({ loadUsers: loadUsers,
332
332
  var createUser = creation.createUser;
333
333
  var createUserWithoutPin = creation.createUserWithoutPin;
334
334
  var permissions = attachPermissions({ loadUsers: loadUsers, saveUsers: saveUsers, findUserById: findUserById });
335
+ var contextualClayPreferences = require("./users-contextual-clay-preferences").attachContextualClayPreferences({ loadUsers: loadUsers, saveUsers: saveUsers });
335
336
  var preferences = attachPreferences({ loadUsers: loadUsers, saveUsers: saveUsers });
336
337
  var projectMateFilterPreferences = attachProjectMateFilterPreferences({ loadUsers: loadUsers, saveUsers: saveUsers });
337
338
  var homeDockPreferences = attachHomeDockPreferences({ loadUsers: loadUsers, saveUsers: saveUsers });
@@ -478,6 +479,8 @@ module.exports = {
478
479
  setToolPalette: setToolPalette,
479
480
  getTerminalFont: getTerminalFont,
480
481
  setTerminalFont: setTerminalFont,
482
+ getContextualClayGeometry: contextualClayPreferences.getContextualClayGeometry,
483
+ setContextualClayGeometry: contextualClayPreferences.setContextualClayGeometry,
481
484
  getHomeDockPreference: getHomeDockPreference,
482
485
  setHomeDockPreference: setHomeDockPreference,
483
486
  getHomeSurfacePreference: getHomeSurfacePreference,
@@ -7,17 +7,7 @@ var spawn = require("child_process").spawn;
7
7
  var buildUserEnv = require("./build-user-env").buildUserEnv;
8
8
  var wrapSpawnAsUser = require("./os-users").wrapSpawnAsUser;
9
9
 
10
- function loginUrl(value, vendor) {
11
- try {
12
- var url = new URL(value);
13
- var hosts = vendor === "codex" ? ["auth.openai.com"] : ["claude.ai", "claude.com", "console.anthropic.com", "platform.claude.com"];
14
- if (url.protocol !== "https:" || url.username || url.password || hosts.indexOf(url.hostname) < 0) return null;
15
- if (!url.searchParams.get("state") || !url.searchParams.get("code_challenge")) return null;
16
- var callback = new URL(url.searchParams.get("redirect_uri"));
17
- if (callback.protocol !== "http:" || ["localhost", "127.0.0.1", "[::1]"].indexOf(callback.hostname) < 0) return null;
18
- return url.href;
19
- } catch (e) { return null; }
20
- }
10
+ var loginUrl = require("./yoke/login-protocol").loginUrl;
21
11
 
22
12
  function createLoginBrowser(options) {
23
13
  var initial = null;
@@ -19,6 +19,8 @@ function attachWorkspaceAssignmentService(ctx) {
19
19
  var records = [];
20
20
  var unsubscribers = {};
21
21
  var pendingRestartReplay = {};
22
+ var presentations = new WeakMap();
23
+ var presentationTargets = new WeakMap();
22
24
 
23
25
  function recordFile(userId) {
24
26
  return storageDir ? path.join(storageDir, userStorageKey(userId)) : null;
@@ -130,7 +132,27 @@ function attachWorkspaceAssignmentService(ctx) {
130
132
  return { project: project, manager: manager, session: session };
131
133
  }
132
134
 
135
+ function present(record, principal) {
136
+ try {
137
+ var display = ctx.getPresentation && ctx.getPresentation(principal), project = exactTarget(record);
138
+ if (!display || !project) return;
139
+ var view = { project: project, session: null, origin: null }; presentationTargets.set(record, view);
140
+ if (record.delivery === 'follow_up') project.getSessionManager().sessions.forEach(function (session) { if (ctx.sessionRef(record.targetProjectSlug, session) === record.targetSessionRef) { view.session = session; view.origin = session.sessionOriginId; } });
141
+ var projection = require('./clay-control-target-projection');
142
+ var target = view.session ? { kind: 'session', projectSlug: record.targetProjectSlug, sessionId: view.session.localId, sessionOriginId: view.origin } : { kind: 'project', projectSlug: record.targetProjectSlug, runtimeId: projection.runtime(project.getSessionManager()) };
143
+ var observer = display.track('assignment', record.delivery === 'follow_up' ? 'follow_up' : 'assign', target, function () {
144
+ if (exactTarget(record) !== project || view.session && (project.getSessionManager().sessions.get(view.session.localId) !== view.session || view.session.sessionOriginId !== view.origin || (view.session.ownerId || null) !== (record.userId || null))) throw new Error('Assignment target changed.');
145
+ }, 'review');
146
+ if (observer) { presentations.set(record, observer); var expiry = setTimeout(function () { presentations.delete(record); }, 300000); expiry.unref(); }
147
+ } catch (error) { /* Presentation never changes the assignment decision. */ }
148
+ }
133
149
  function emit(record, eventType) {
150
+ var observer = presentations.get(record);
151
+ if (observer) {
152
+ if (record.status === 'starting') observer.phase('queued');
153
+ else if (record.status === 'running') observer.phase('running');
154
+ else if (record.status !== 'proposed') { observer.finish({ status: record.status === 'interrupted' ? 'failed' : record.status, outcome: record.status === 'interrupted' ? 'uncertain' : undefined }); presentations.delete(record); }
155
+ }
134
156
  var source = exactSource(record);
135
157
  if (!source) return;
136
158
  source.manager.sendAndRecord(source.session, {
@@ -193,6 +215,7 @@ function attachWorkspaceAssignmentService(ctx) {
193
215
  if (!exactSource(record)) throw new Error("Source conversation is no longer available.");
194
216
  records.push(record);
195
217
  saveUser(record.userId);
218
+ present(record, principal);
196
219
  emit(record, "project_assignment_proposal");
197
220
  return publicRecord(record);
198
221
  }
@@ -229,6 +252,7 @@ function attachWorkspaceAssignmentService(ctx) {
229
252
  if (!exactSource(record)) throw new Error("Source conversation is no longer available.");
230
253
  records.push(record);
231
254
  saveUser(record.userId);
255
+ present(record, principal);
232
256
  emit(record, "project_assignment_proposal");
233
257
  return publicRecord(record);
234
258
  }
@@ -267,6 +291,7 @@ function attachWorkspaceAssignmentService(ctx) {
267
291
  }
268
292
 
269
293
  function watch(record, manager, session) {
294
+ var view = presentationTargets.get(record); if (view) { view.session = session; view.origin = session.sessionOriginId; var observer = presentations.get(record); if (observer && observer.target) observer.target({ kind: 'session', projectSlug: record.targetProjectSlug, sessionId: session.localId, sessionOriginId: session.sessionOriginId }); }
270
295
  var unsubscribe = manager.subscribeSession(session.localId, function (event) {
271
296
  if (event.type === "session_id") {
272
297
  record.targetSessionRef = ctx.sessionRef(record.targetProjectSlug, session);
@@ -32,7 +32,7 @@ function handler(bound, method) {
32
32
  };
33
33
  }
34
34
 
35
- var SESSION_CITATION = " When citing a returned session in user-visible text, write [clayos/<sessionRef> — short label] so Clay Studio renders an owner-validated session control.";
35
+ var SESSION_CITATION = " Cite returned sessions as Markdown links: [short human label](session:OPAQUE_REFERENCE), replacing the entire target with the exact returned sessionRef. Do not prepend a project name, change the opaque reference, or wrap citations in backticks/code. For a specific returned entry, append #entry-<entryIndex> using its exact returned entry index. Legacy [clayos/<sessionRef> — short label] remains supported. Labels are presentation only; Clay Studio resolves the exact reference with the authenticated owner's permissions.";
36
36
 
37
37
  function commonTools(bound) {
38
38
  return [
package/lib/ws-request.js CHANGED
@@ -8,6 +8,7 @@ function parseWsRequestUrl(requestUrl) {
8
8
  path: parsed.pathname || "/",
9
9
  pane: parsed.query.pane === "1",
10
10
  paneSession: paneSession,
11
+ contextual: parsed.query.pane === "1" && parsed.query.contextual === "1",
11
12
  };
12
13
  }
13
14
 
package/lib/ws-schema.js CHANGED
@@ -11,6 +11,11 @@
11
11
  * Use it as a reference when adding new message types.
12
12
  */
13
13
 
14
+ // message.contextualSnapshot: version=1, viewId, nullable projectTitle,
15
+ // conversationTitle/projectSlug (160 characters), nullable positive sessionId,
16
+ // screen=Home|Project|Direct message. Private canonical owner delivery only.
17
+ // Persisted user_message metadata is echoed in message_ack only after recording;
18
+ // queued admission ACK has no snapshot. Never part of user text/pastes or authority.
14
19
  var schema = {
15
20
 
16
21
  // -----------------------------------------------------------------------
@@ -62,6 +67,7 @@ var schema = {
62
67
  "project_assignment_response": { direction: "c2s", handler: "lib/workspace-assignment-service.js", description: "Approve or cancel an exact-session cross-project assignment proposal" },
63
68
  "home_clay_session_resolve": { direction: "c2s", handler: "lib/server-home-chat.js", description: "Resolve an opaque Mate session reference through the exact bound source session" },
64
69
  "home_clay_session_target": { direction: "s2c", handler: "lib/public/modules/command-palette.js", description: "Return an owner-validated navigation target for a rendered Mate session reference" },
70
+ "home_clay_contextual_session": { direction: "s2c", handler: "lib/public/modules/contextual-search.js", description: "Return the exact fresh canonical session for an idempotent contextual Ask request; does not submit or execute a message" },
65
71
  "issues_list": { direction: "c2s", handler: "lib/project-issues.js", description: "Project Issues issues list" },
66
72
  "issue_read": { direction: "c2s", handler: "lib/project-issues.js", description: "Project Issues issue read" },
67
73
  "issue_create": { direction: "c2s", handler: "lib/project-issues.js", description: "Project Issues issue create" },
@@ -482,6 +488,7 @@ var schema = {
482
488
  "term_resized": { direction: "s2c", handler: "lib/public/modules/app-messages.js", description: "Terminal was resized" },
483
489
  "term_exited": { direction: "s2c", handler: "lib/public/modules/app-messages.js", description: "Terminal process exited" },
484
490
  "term_closed": { direction: "s2c", handler: "lib/public/modules/app-messages.js", description: "Terminal session was closed" },
491
+ "term_control_projection": { direction: "s2c", handler: "lib/public/modules/terminal.js", description: "Authoritative terminal identity/revision after owner resize without echoing resize input" },
485
492
  "term_list": { direction: "s2c", handler: "lib/public/modules/app-messages.js", description: "Full list of open terminals" },
486
493
  "term_error": { direction: "s2c", handler: "lib/public/modules/app-messages.js", description: "Terminal error (e.g. access denied)" },
487
494
  "shell_command": { direction: "c2s", handler: "lib/project-shell-command.js", description: "Run a one-shot shell command for agent context" },
@@ -785,6 +792,7 @@ var schema = {
785
792
  // -----------------------------------------------------------------------
786
793
  "home_mate_open": { direction: "c2s", handler: "lib/server-home-chat.js", description: "Open or restore the selected mate's embedded chat session" },
787
794
  "home_clay_ask": { direction: "c2s", handler: "lib/server-home-chat.js", description: "Start a fresh builtin Clay conversation from the global search drawer" },
795
+ "contextual_mate_sessions_result": { direction: "s2c", handler: "lib/public/modules/contextual-mate-sessions.js", description: "Exact owner-private saved Mate conversation list or selection receipt; no transcript tap or underlying navigation" },
788
796
  "home_mate_sessions_list": { direction: "c2s", handler: "lib/server-home-chat.js", description: "List the current user's conversations with the selected mate" },
789
797
  "home_mate_session_open": { direction: "c2s", handler: "lib/server-home-chat.js", description: "Open one explicit conversation with the selected mate" },
790
798
  "home_mate_send": { direction: "c2s", handler: "lib/server-home-chat.js", description: "Send a message to the selected mate from the home hub" },
@@ -814,6 +822,8 @@ var schema = {
814
822
  "clay_navigation_offer": { direction: "s2c", handler: "lib/public/modules/clay-owner-navigation.js", description: "Offer one exact reviewed navigation to a selected owner connection" },
815
823
  "clay_navigation_claim": { direction: "c2s", handler: "lib/server-owner-ui.js", description: "Claim an offered navigation with live source/target revalidation" },
816
824
  "clay_navigation_apply": { direction: "s2c", handler: "lib/public/modules/clay-owner-navigation.js", description: "Invoke the shared conversation navigator after live claim" },
825
+ "clay_studio_rendered": { direction: "c2s", handler: "lib/project-studio-presentation.js", description: "Exact source-viewer workbench render receipt; no product effects" },
826
+ "clay_navigation_rendered": { direction: "c2s", handler: "lib/server-owner-ui.js", description: "Bounded exact-viewer rendered conversation evidence; acceptance alone remains separate" },
817
827
  "clay_navigation_ack": { direction: "c2s", handler: "lib/server-owner-ui.js", description: "Client invocation acknowledgement; never proof of visible rendering" },
818
828
  "context_view_get": { direction: "c2s", handler: "lib/server-context-view.js", description: "Read the authenticated user's context display preference" },
819
829
  "context_view_set": { direction: "c2s", handler: "lib/server-context-view.js", description: "Persist the authenticated user's context display preference" },