clay-server 5.2.0-beta.4 → 5.2.0-beta.6

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 (301) hide show
  1. package/lib/account-auth-socket.js +1 -0
  2. package/lib/builtin-mates.js +17 -14
  3. package/lib/canonical-clay-record-authority.js +15 -0
  4. package/lib/canonical-clay-scheduling-authority.js +19 -0
  5. package/lib/claude-hook-installer.js +1 -239
  6. package/lib/claude-jsonl-watcher.js +1 -204
  7. package/lib/clay-mate-chat-receipts.js +26 -0
  8. package/lib/clay-mate-chat.js +87 -0
  9. package/lib/clay-mate-knowledge-input.js +26 -0
  10. package/lib/clay-mate-knowledge.js +95 -0
  11. package/lib/clay-mate-lifecycle.js +95 -0
  12. package/lib/clay-product-control-input.js +33 -0
  13. package/lib/clay-product-control-tools.js +51 -0
  14. package/lib/clay-product-control.js +237 -0
  15. package/lib/clay-project-lifecycle.js +154 -0
  16. package/lib/clay-project-record-input.js +44 -0
  17. package/lib/clay-project-records.js +83 -0
  18. package/lib/clay-scheduling-input.js +21 -0
  19. package/lib/clay-scheduling-receipts.js +26 -0
  20. package/lib/clay-scheduling.js +119 -0
  21. package/lib/clay-screen-controls.js +96 -0
  22. package/lib/clay-skill-install.js +1 -33
  23. package/lib/clay-workspace-controls.js +83 -0
  24. package/lib/clay-workspace-input.js +17 -0
  25. package/lib/clay-workspace-receipts.js +26 -0
  26. package/lib/cli-sessions.js +1 -300
  27. package/lib/codex-defaults.js +1 -28
  28. package/lib/context-usage.js +3 -2
  29. package/lib/daemon-project-access.js +11 -0
  30. package/lib/daemon-project-lifecycle-callbacks.js +15 -0
  31. package/lib/daemon-project-lifecycle.js +262 -0
  32. package/lib/daemon-projects.js +1 -1
  33. package/lib/daemon.js +78 -276
  34. package/lib/default-ai-runtime.js +4 -8
  35. package/lib/email-mcp-server.js +1 -1
  36. package/lib/issues-service.js +29 -15
  37. package/lib/kiro-defaults.js +1 -22
  38. package/lib/knowledge-db-import.js +5 -4
  39. package/lib/knowledge-db-service.js +9 -6
  40. package/lib/knowledge-document-index.js +2 -1
  41. package/lib/knowledge-product-operations.js +46 -0
  42. package/lib/mate-creation-mcp-server.js +2 -0
  43. package/lib/mate-knowledge-canonical-access.js +10 -0
  44. package/lib/mate-knowledge-service.js +7 -4
  45. package/lib/mate-lifecycle-service.js +94 -0
  46. package/lib/mate-message-delivery.js +43 -0
  47. package/lib/mate-ready-creation.js +9 -1
  48. package/lib/mate-schedule-input.js +29 -0
  49. package/lib/mate-schedule-lifecycle.js +151 -0
  50. package/lib/mate-scheduling.js +103 -0
  51. package/lib/mates.js +4 -3
  52. package/lib/mcp-proxy.js +2 -32
  53. package/lib/mcp-query-bridge.js +1 -1
  54. package/lib/mcp-source-adapters.js +1 -91
  55. package/lib/mcp-source-context.js +1 -42
  56. package/lib/mcp-source-discovery.js +1 -169
  57. package/lib/mcp-source-plugins.js +1 -68
  58. package/lib/mcp-source-restrictions.js +1 -47
  59. package/lib/mcp-tool-policy.js +1 -26
  60. package/lib/notes-checklist.js +50 -0
  61. package/lib/notes.js +49 -3
  62. package/lib/os-user-diagnostics.js +2 -4
  63. package/lib/os-users.js +24 -70
  64. package/lib/project-admin-overview.js +51 -0
  65. package/lib/project-ask-user.js +4 -2
  66. package/lib/project-connection.js +8 -7
  67. package/lib/project-delegated-follow-up.js +2 -1
  68. package/lib/project-directory-claim.js +65 -0
  69. package/lib/project-file-operations.js +61 -0
  70. package/lib/project-filesystem.js +13 -87
  71. package/lib/project-full-access.js +23 -2
  72. package/lib/project-http.js +4 -4
  73. package/lib/project-knowledge.js +32 -18
  74. package/lib/project-lifecycle-operations.js +98 -0
  75. package/lib/project-lifecycle-process-witness.js +32 -0
  76. package/lib/project-lifecycle-runtime.js +50 -0
  77. package/lib/project-linux-access.js +22 -0
  78. package/lib/project-logs-canonical-clay.js +27 -0
  79. package/lib/project-logs-clay-query.js +16 -0
  80. package/lib/project-logs-schema.js +4 -2
  81. package/lib/project-logs-service.js +8 -7
  82. package/lib/project-logs.js +1 -0
  83. package/lib/project-loop.js +1 -0
  84. package/lib/project-mate-chat-control.js +55 -0
  85. package/lib/project-mate-creation-proposal.js +18 -3
  86. package/lib/project-mcp.js +2 -2
  87. package/lib/project-models.js +14 -31
  88. package/lib/project-notifications.js +74 -16
  89. package/lib/project-ownership.js +77 -0
  90. package/lib/project-pair-result-delivery.js +14 -3
  91. package/lib/project-pair-usage.js +12 -13
  92. package/lib/project-pending-message-queue.js +4 -1
  93. package/lib/project-product-control.js +87 -0
  94. package/lib/project-product-session-queue.js +46 -0
  95. package/lib/project-push-access.js +28 -0
  96. package/lib/project-scheduled-message-queue.js +30 -0
  97. package/lib/project-scheduled-messages.js +63 -64
  98. package/lib/project-scheduled-task-control.js +83 -0
  99. package/lib/project-scheduled-tasks.js +13 -2
  100. package/lib/project-scheduling-access.js +10 -0
  101. package/lib/project-session-actions.js +65 -0
  102. package/lib/project-session-delete.js +8 -1
  103. package/lib/project-session-github.js +47 -12
  104. package/lib/project-session-history-actions.js +167 -0
  105. package/lib/project-session-pair.js +1 -1
  106. package/lib/project-session-runtime-actions.js +64 -0
  107. package/lib/project-session-spawn.js +2 -3
  108. package/lib/project-session-vendor.js +55 -0
  109. package/lib/project-sessions.js +82 -267
  110. package/lib/project-user-message.js +31 -1
  111. package/lib/project-vendor-login.js +6 -36
  112. package/lib/project-workspace-operations.js +126 -0
  113. package/lib/project-workspace-query.js +9 -2
  114. package/lib/project.js +95 -3
  115. package/lib/public/app.js +1 -0
  116. package/lib/public/css/clay-tool-activity.css +51 -0
  117. package/lib/public/css/home-mate-creation.css +5 -0
  118. package/lib/public/css/messages.css +6 -2
  119. package/lib/public/css/overlays.css +4 -2
  120. package/lib/public/css/project-admin-overview.css +13 -0
  121. package/lib/public/css/session-folders.css +11 -0
  122. package/lib/public/css/session-github.css +3 -0
  123. package/lib/public/css/sticky-notes.css +61 -1
  124. package/lib/public/index.html +1 -1
  125. package/lib/public/modules/admin-settings-projects.js +6 -2
  126. package/lib/public/modules/app-connection.js +8 -1
  127. package/lib/public/modules/app-favicon.js +11 -2
  128. package/lib/public/modules/app-header.js +4 -2
  129. package/lib/public/modules/app-loop-ui.js +2 -0
  130. package/lib/public/modules/app-message-router.js +1 -1
  131. package/lib/public/modules/app-messages.js +54 -8
  132. package/lib/public/modules/app-notifications.js +1 -2
  133. package/lib/public/modules/app-panels.js +61 -123
  134. package/lib/public/modules/app-projects.js +5 -0
  135. package/lib/public/modules/app-rate-limit.js +7 -27
  136. package/lib/public/modules/chat-render-runtime.js +3 -0
  137. package/lib/public/modules/clay-owner-navigation.js +27 -0
  138. package/lib/public/modules/clay-tool-activity.js +140 -0
  139. package/lib/public/modules/clay-tool-presentation.js +338 -0
  140. package/lib/public/modules/command-palette.js +1 -1
  141. package/lib/public/modules/default-ai.js +11 -3
  142. package/lib/public/modules/home-debate-planning.js +5 -5
  143. package/lib/public/modules/home-mate-chat.js +8 -6
  144. package/lib/public/modules/home-mate-creation.js +73 -1
  145. package/lib/public/modules/mcp-connection-view.js +1 -1
  146. package/lib/public/modules/permission-control.js +5 -4
  147. package/lib/public/modules/personal-settings-dialog.js +7 -1
  148. package/lib/public/modules/project-access-recovery.js +17 -0
  149. package/lib/public/modules/project-admin-overview.js +21 -0
  150. package/lib/public/modules/scheduler.js +2 -0
  151. package/lib/public/modules/session-create-form.js +10 -10
  152. package/lib/public/modules/session-folder-layout.js +1 -1
  153. package/lib/public/modules/session-folder-view-menu.js +9 -0
  154. package/lib/public/modules/session-github.js +65 -8
  155. package/lib/public/modules/settings-defaults.js +27 -28
  156. package/lib/public/modules/sidebar-session-folders-ui.js +1 -1
  157. package/lib/public/modules/sticky-note-markdown.js +72 -6
  158. package/lib/public/modules/sticky-notes-actions.js +30 -0
  159. package/lib/public/modules/sticky-notes-browser.js +9 -2
  160. package/lib/public/modules/sticky-notes-card.js +23 -7
  161. package/lib/public/modules/sticky-notes-checklist.js +71 -0
  162. package/lib/public/modules/sticky-notes-editor.js +108 -44
  163. package/lib/public/modules/sticky-notes-shared.js +50 -2
  164. package/lib/public/modules/sticky-notes-state.js +66 -0
  165. package/lib/public/modules/sticky-notes.js +34 -51
  166. package/lib/public/modules/thinking-lifecycle.js +33 -3
  167. package/lib/public/modules/thinking-view.js +23 -1
  168. package/lib/public/modules/tools.js +36 -7
  169. package/lib/public/modules/tui-grab.js +1 -2
  170. package/lib/public/modules/user-settings-appearance.js +10 -3
  171. package/lib/public/modules/user-settings-editors.js +1 -1
  172. package/lib/public/modules/user-settings-layout.js +11 -2
  173. package/lib/public/modules/user-settings-preferences.js +6 -2
  174. package/lib/public/modules/user-settings-profile.js +12 -2
  175. package/lib/public/modules/user-settings.js +3 -2
  176. package/lib/public/modules/vendor-login-browser.js +5 -3
  177. package/lib/public/modules/vendor-login.js +2 -2
  178. package/lib/public/modules/vendor-priority.js +15 -1
  179. package/lib/public/muse-avatar.svg +4 -0
  180. package/lib/public/style.css +2 -0
  181. package/lib/safe-bash-commands.js +3 -45
  182. package/lib/scheduled-task-records.js +3 -1
  183. package/lib/scheduler.js +2 -0
  184. package/lib/sdk-bridge.js +334 -403
  185. package/lib/sdk-foreground-admission.js +16 -0
  186. package/lib/sdk-mcp-descriptors.js +2 -37
  187. package/lib/sdk-message-processor.js +52 -17
  188. package/lib/sdk-quota-snapshot.js +1 -1
  189. package/lib/sdk-response-recovery.js +1 -0
  190. package/lib/sdk-worker.js +2 -677
  191. package/lib/server-admin.js +63 -16
  192. package/lib/server-dm.js +1 -1
  193. package/lib/server-global-ws.js +10 -20
  194. package/lib/server-home-chat-events.js +7 -3
  195. package/lib/server-home-chat.js +4 -16
  196. package/lib/server-home-mate-creation.js +1 -0
  197. package/lib/server-home-mcp-consent.js +3 -3
  198. package/lib/server-mates.js +25 -67
  199. package/lib/server-owner-ui.js +77 -0
  200. package/lib/server-path-access.js +38 -0
  201. package/lib/server-project-removal.js +22 -0
  202. package/lib/server.js +100 -30
  203. package/lib/session-config-transition.js +45 -0
  204. package/lib/session-folders.js +7 -2
  205. package/lib/session-fork-history.js +21 -0
  206. package/lib/session-github.js +9 -2
  207. package/lib/session-hygiene.js +3 -35
  208. package/lib/session-permission-mode.js +4 -3
  209. package/lib/session-skill-scope.js +11 -0
  210. package/lib/session-visibility.js +5 -2
  211. package/lib/sessions.js +27 -89
  212. package/lib/terminal-manager.js +52 -0
  213. package/lib/tool-llm.js +1 -1
  214. package/lib/tui-transcript-index.js +1 -132
  215. package/lib/users-permissions.js +2 -8
  216. package/lib/users.js +4 -0
  217. package/lib/vendor-login-browser.js +1 -11
  218. package/lib/workspace-query-access.js +4 -1
  219. package/lib/ws-schema.js +8 -2
  220. package/lib/yoke/acp-agent-profiles.js +5 -3
  221. package/lib/yoke/adapters/antigravity.js +3 -14
  222. package/lib/yoke/adapters/claude-query-contract.js +9 -0
  223. package/lib/yoke/adapters/claude.js +54 -50
  224. package/lib/yoke/adapters/codex-query-contract.js +24 -0
  225. package/lib/yoke/adapters/codex.js +53 -111
  226. package/lib/yoke/adapters/kiro.js +3 -1
  227. package/lib/yoke/adapters/muse-query-contract.js +7 -0
  228. package/lib/yoke/adapters/muse.js +122 -0
  229. package/lib/yoke/auth-events.js +17 -0
  230. package/lib/yoke/claude-hook-installer.js +267 -0
  231. package/lib/yoke/claude-jsonl-watcher.js +204 -0
  232. package/lib/yoke/claude-maintenance.js +55 -0
  233. package/lib/yoke/claude-permission-patterns.js +53 -0
  234. package/lib/yoke/claude-response-recovery.js +155 -0
  235. package/lib/yoke/claude-transcript-hygiene.js +34 -0
  236. package/lib/yoke/claude-worker-startup.js +28 -0
  237. package/lib/yoke/clay-skill-install.js +33 -0
  238. package/lib/yoke/cli-sessions.js +300 -0
  239. package/lib/yoke/codex-app-server.js +7 -12
  240. package/lib/yoke/codex-idle-recovery.js +81 -1
  241. package/lib/yoke/codex-recovery-admission.js +164 -0
  242. package/lib/yoke/configuration.js +13 -0
  243. package/lib/yoke/discovery.js +70 -0
  244. package/lib/yoke/errors.js +8 -0
  245. package/lib/yoke/host-environment.js +7 -0
  246. package/lib/yoke/index.js +9 -51
  247. package/lib/yoke/interface.js +116 -0
  248. package/lib/yoke/kiro-acp-server.js +5 -5
  249. package/lib/yoke/legacy-sessions.js +89 -0
  250. package/lib/yoke/login-protocol.js +25 -0
  251. package/lib/yoke/maintenance.js +12 -0
  252. package/lib/yoke/mcp-proxy.js +32 -0
  253. package/lib/yoke/mcp-source-adapters.js +91 -0
  254. package/lib/yoke/mcp-source-context.js +42 -0
  255. package/lib/yoke/mcp-source-discovery.js +169 -0
  256. package/lib/yoke/mcp-source-plugins.js +68 -0
  257. package/lib/yoke/mcp-source-restrictions.js +47 -0
  258. package/lib/yoke/mcp-tool-policy-normalize.js +28 -0
  259. package/lib/yoke/model-preferences.js +6 -0
  260. package/lib/yoke/muse-artifacts.js +36 -0
  261. package/lib/yoke/muse-controls.js +50 -0
  262. package/lib/yoke/muse-input.js +64 -0
  263. package/lib/yoke/muse-items.js +157 -0
  264. package/lib/yoke/muse-policy.js +52 -0
  265. package/lib/yoke/muse-query.js +428 -0
  266. package/lib/yoke/muse-runtime.js +211 -0
  267. package/lib/yoke/muse-services.js +79 -0
  268. package/lib/yoke/muse-skills.js +79 -0
  269. package/lib/yoke/muse-turns.js +71 -0
  270. package/lib/yoke/options/acp.js +2 -0
  271. package/lib/yoke/options/antigravity.js +2 -0
  272. package/lib/yoke/options/claude.js +21 -0
  273. package/lib/yoke/options/codex-defaults.js +29 -0
  274. package/lib/yoke/options/codex-settings.js +21 -0
  275. package/lib/yoke/options/codex.js +6 -0
  276. package/lib/yoke/options/kiro-defaults.js +22 -0
  277. package/lib/yoke/options/kiro.js +5 -0
  278. package/lib/yoke/options/muse.js +5 -0
  279. package/lib/yoke/provider-contract.js +5 -0
  280. package/lib/yoke/providers/antigravity-executable.js +8 -0
  281. package/lib/yoke/providers/claude-executable.js +40 -0
  282. package/lib/yoke/providers/claude-model-descriptions.js +14 -0
  283. package/lib/yoke/providers/claude-process.js +97 -0
  284. package/lib/yoke/providers/claude-provision.js +75 -0
  285. package/lib/yoke/providers/codex-errors.js +8 -0
  286. package/lib/yoke/providers/muse-executable.js +17 -0
  287. package/lib/yoke/provisioning.js +6 -0
  288. package/lib/yoke/query-options.js +10 -0
  289. package/lib/yoke/response-recovery.js +41 -0
  290. package/lib/yoke/sdk-mcp-descriptors.js +37 -0
  291. package/lib/yoke/sdk-server-factory.js +7 -0
  292. package/lib/yoke/session-history.js +8 -0
  293. package/lib/yoke/session-tool-transport.js +34 -13
  294. package/lib/yoke/settings.js +23 -0
  295. package/lib/yoke/source-locations.js +4 -0
  296. package/lib/yoke/title-query.js +18 -7
  297. package/lib/yoke/tui-transcript-index.js +132 -0
  298. package/lib/yoke/usage.js +48 -0
  299. package/lib/yoke/user-input.js +13 -0
  300. package/lib/yoke/vendor-registry.js +81 -1
  301. 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/
@@ -227,6 +204,11 @@ function createSessionManager(opts) {
227
204
  if (session.homeDebatePlanning) metaObj.homeDebatePlanning = true;
228
205
  if (session.homeDebatePhase) metaObj.homeDebatePhase = session.homeDebatePhase;
229
206
  if (session.homeDebateInitialTopic) metaObj.homeDebateInitialTopic = session.homeDebateInitialTopic;
207
+ if (session.mateScheduleDraft) metaObj.mateScheduleDraft = session.mateScheduleDraft;
208
+ if (session.mateSchedules) metaObj.mateSchedules = session.mateSchedules;
209
+ if (session.clayWorkspaceReceipts) metaObj.clayWorkspaceReceipts = session.clayWorkspaceReceipts;
210
+ if (session.clayMateChatReceipts) metaObj.clayMateChatReceipts = session.clayMateChatReceipts;
211
+ if (session.claySchedulingReceipts) metaObj.claySchedulingReceipts = session.claySchedulingReceipts;
230
212
  if (session.mateCreationMode) metaObj.mateCreationMode = true;
231
213
  if (session.homeMateCreationPhase) metaObj.homeMateCreationPhase = session.homeMateCreationPhase;
232
214
  if (session.homeClayEntryRequestId) metaObj.homeClayEntryRequestId = session.homeClayEntryRequestId;
@@ -386,6 +368,11 @@ function createSessionManager(opts) {
386
368
  if (m.homeDebatePlanning) session.homeDebatePlanning = true;
387
369
  if (m.homeDebatePhase) session.homeDebatePhase = m.homeDebatePhase;
388
370
  if (m.homeDebateInitialTopic) session.homeDebateInitialTopic = m.homeDebateInitialTopic;
371
+ if (m.mateScheduleDraft) session.mateScheduleDraft = m.mateScheduleDraft;
372
+ if (m.mateSchedules) session.mateSchedules = m.mateSchedules;
373
+ if (Array.isArray(m.clayWorkspaceReceipts)) session.clayWorkspaceReceipts = m.clayWorkspaceReceipts;
374
+ if (Array.isArray(m.clayMateChatReceipts)) session.clayMateChatReceipts = m.clayMateChatReceipts;
375
+ if (Array.isArray(m.claySchedulingReceipts)) session.claySchedulingReceipts = m.claySchedulingReceipts;
389
376
  if (m.mateCreationMode) session.mateCreationMode = true;
390
377
  if (m.homeMateCreationPhase) session.homeMateCreationPhase = m.homeMateCreationPhase;
391
378
  if (m.homeClayEntryRequestId) session.homeClayEntryRequestId = m.homeClayEntryRequestId;
@@ -460,9 +447,6 @@ function createSessionManager(opts) {
460
447
  // - Warmup shape: 1 real "hi" prompt, optional automatic interruption,
461
448
  // and 0 assistant messages (covered by daemon cleanup but defensive)
462
449
  function adoptOrphanedCliSessions() {
463
- var files;
464
- try { files = fs.readdirSync(cliSessionsDir); } catch (e) { return; }
465
-
466
450
  // Build set of cliSessionIds Clay already tracks
467
451
  var knownCliIds = new Set();
468
452
  sessions.forEach(function (s) {
@@ -471,56 +455,10 @@ function createSessionManager(opts) {
471
455
 
472
456
  var adopted = 0;
473
457
 
474
- for (var i = 0; i < files.length; i++) {
475
- if (!files[i].endsWith(".jsonl")) continue;
476
- var cliSid = files[i].slice(0, -".jsonl".length);
477
- if (knownCliIds.has(cliSid)) continue;
478
- if (deletedCliSessionIds.has(cliSid)) continue;
479
-
480
- var fp = path.join(cliSessionsDir, files[i]);
481
- var events = readCliTranscriptHead(fp);
482
- if (!events) continue;
483
- var userCount = 0;
484
- var firstUserText = null;
485
- var createdAtIso = null;
486
- for (var li = 0; li < events.length; li++) {
487
- var ev = events[li];
488
- if (ev.type === "user" && ev.message && ev.message.role === "user") {
489
- userCount++;
490
- if (firstUserText == null) {
491
- var c = ev.message.content;
492
- if (typeof c === "string") {
493
- firstUserText = c;
494
- } else if (Array.isArray(c)) {
495
- var parts = [];
496
- for (var ci = 0; ci < c.length; ci++) {
497
- if (c[ci] && c[ci].type === "text" && typeof c[ci].text === "string") parts.push(c[ci].text);
498
- }
499
- firstUserText = parts.join("");
500
- }
501
- if (ev.timestamp && !createdAtIso) createdAtIso = ev.timestamp;
502
- }
503
- }
504
- }
505
-
506
- if (userCount === 0) continue;
507
- // Defensive warmup skip (daemon also cleans these)
508
- if (hygiene.isClaudeWarmupTranscript(events)) continue;
509
-
510
- // Title: first user message trimmed/truncated. Fall back to a
511
- // neutral label so the sidebar entry is identifiable.
512
- var title = (firstUserText || "").trim().replace(/\s+/g, " ");
513
- if (title.length > 60) title = title.slice(0, 57) + "...";
514
- if (!title) title = "Imported CLI session";
515
-
516
- var createdAt = Date.now();
517
- if (createdAtIso) {
518
- var t = Date.parse(createdAtIso);
519
- if (!isNaN(t)) createdAt = t;
520
- }
521
- var lastActivity = createdAt;
522
- try { lastActivity = fs.statSync(fp).mtimeMs; } catch (e) {}
523
-
458
+ var candidates = require("./yoke/legacy-sessions").candidates(cliSessionsDir, knownCliIds, deletedCliSessionIds);
459
+ for (var i = 0; i < candidates.length; i++) {
460
+ var candidate = candidates[i], cliSid = candidate.id;
461
+ var title = candidate.title, createdAt = candidate.createdAt, lastActivity = candidate.lastActivity;
524
462
  var localId = nextLocalId++;
525
463
  var session = {
526
464
  localId: localId,
@@ -538,7 +476,7 @@ function createSessionManager(opts) {
538
476
  history: [],
539
477
  messageUUIDs: [],
540
478
  lastRewindUuid: null,
541
- vendor: "claude",
479
+ vendor: candidate.vendor,
542
480
  mode: "tui",
543
481
  // Adopted from an external `claude` run in this cwd, not started by
544
482
  // Clay. Notification emitters must stay silent for these until the
@@ -570,10 +508,7 @@ function createSessionManager(opts) {
570
508
  var stale = [];
571
509
  sessions.forEach(function (session) {
572
510
  if (!session.adopted || !session.cliSessionId) return;
573
- var transcriptPath = path.join(cliSessionsDir, session.cliSessionId + ".jsonl");
574
- var events = readCliTranscriptHead(transcriptPath);
575
- var missingEmptySource = !fs.existsSync(transcriptPath) && (!session.history || session.history.length === 0);
576
- if (missingEmptySource || (events && hygiene.isClaudeWarmupTranscript(events))) stale.push(session.localId);
511
+ if (require("./yoke/legacy-sessions").obsolete(cliSessionsDir, session)) stale.push(session.localId);
577
512
  });
578
513
  for (var i = 0; i < stale.length; i++) deleteSessionQuiet(stale[i]);
579
514
  if (stale.length > 0) {
@@ -867,7 +802,8 @@ function createSessionManager(opts) {
867
802
  if (_item && (_item.type === "mention_user" || _item.type === "mention_response")) {
868
803
  console.log("[DEBUG replayHistory] sending mention at index=" + i + " from=" + fromIndex + " total=" + total + " type=" + _item.type + " mate=" + (_item.mateName || ""));
869
804
  }
870
- var replayItem = transform ? transform(_item) : _item;
805
+ var normalizedItem = require("./yoke/usage").clientEvent(_item, session.vendor || "claude");
806
+ var replayItem = transform ? transform(normalizedItem) : normalizedItem;
871
807
  if (replayItem && typeof replayItem === "object") replayItem = Object.assign({}, replayItem, { _historyIndex: i });
872
808
  _send(replayItem);
873
809
  }
@@ -877,9 +813,11 @@ function createSessionManager(opts) {
877
813
  var lastModelUsage = null;
878
814
  var lastCost = null;
879
815
  var lastStreamInputTokens = null;
816
+ var lastContext = null;
880
817
  for (var j = total - 1; j >= 0; j--) {
881
818
  if (session.history[j].type === "result") {
882
- var r = session.history[j];
819
+ var r = require("./yoke/usage").clientEvent(session.history[j], session.vendor || "claude");
820
+ lastContext = r.context || null;
883
821
  lastUsage = r.usage || null;
884
822
  lastModelUsage = r.modelUsage || null;
885
823
  lastCost = r.cost != null ? r.cost : null;
@@ -888,7 +826,7 @@ function createSessionManager(opts) {
888
826
  }
889
827
  }
890
828
 
891
- _send({ type: "history_done", lastUsage: lastUsage, lastModelUsage: lastModelUsage, lastCost: lastCost, lastStreamInputTokens: lastStreamInputTokens, contextUsage: session.lastContextUsage || null });
829
+ _send({ type: "history_done", lastUsage: lastUsage, lastModelUsage: lastModelUsage, lastCost: lastCost, lastStreamInputTokens: lastStreamInputTokens, context: lastContext, contextUsage: require("./yoke/usage").context(session.lastContextUsage, session.vendor || "claude") });
892
830
  for (var vi = 0; vi < sessionViewedListeners.length; vi++) {
893
831
  try { sessionViewedListeners[vi](session, targetWs, _send); } catch (e) {}
894
832
  }
@@ -1,4 +1,5 @@
1
1
  var { createTerminal } = require("./terminal");
2
+ var crypto = require("crypto");
2
3
 
3
4
  var MAX_TERMINALS = 10;
4
5
  var SCROLLBACK_MAX = 50 * 1024; // 50 KB per terminal
@@ -48,6 +49,7 @@ function createTerminalManager(opts) {
48
49
  var id = nextId++;
49
50
  var session = {
50
51
  id: id,
52
+ identity: crypto.randomUUID(),
51
53
  pty: pty,
52
54
  scrollback: [],
53
55
  scrollbackSize: 0,
@@ -59,8 +61,11 @@ function createTerminalManager(opts) {
59
61
  lastActivityAt: Date.now(),
60
62
  exited: false,
61
63
  exitCode: null,
64
+ exitWaiters: [],
62
65
  subscribers: new Set(),
63
66
  ownerWs: ownerWs || null,
67
+ ownerId: ownerWs && ownerWs._clayUser ? ownerWs._clayUser.id : opts && opts.ownerId || null,
68
+ osIdentity: osUserInfo ? JSON.stringify([osUserInfo.user, osUserInfo.uid, osUserInfo.gid, osUserInfo.home]) : null,
64
69
  onExitHook: (opts && typeof opts.onExit === "function") ? opts.onExit : null,
65
70
  onDataHook: (opts && typeof opts.onData === "function") ? opts.onData : null,
66
71
  };
@@ -95,6 +100,7 @@ function createTerminalManager(opts) {
95
100
  session.exited = true;
96
101
  session.exitCode = e && e.exitCode != null ? e.exitCode : null;
97
102
  session.pty = null;
103
+ session.exitWaiters.splice(0).forEach(function (resolve) { resolve(true); });
98
104
 
99
105
  var msg = JSON.stringify({ type: "term_exited", id: id });
100
106
  for (var ws of session.subscribers) {
@@ -204,6 +210,34 @@ function createTerminalManager(opts) {
204
210
  if (terminals.size === 0) nextId = 1;
205
211
  }
206
212
 
213
+ // Keep the terminal registered until its actual exit acknowledges shutdown.
214
+ async function closeAndWait(id, control) {
215
+ var session = terminals.get(id);
216
+ if (!session) throw new Error("Terminal closure cannot be confirmed: runtime is missing.");
217
+ if (control) { control.revalidate(); if (session.identity !== control.identity) throw new Error('The exact terminal identity changed.'); }
218
+ if (!session.exited) {
219
+ if (!session.pty) throw new Error("Terminal closure is unconfirmed.");
220
+ var timer, waiter;
221
+ var exited = new Promise(function (resolve, reject) {
222
+ waiter = resolve; session.exitWaiters.push(waiter);
223
+ timer = setTimeout(function () { reject(new Error("Terminal exit acknowledgement timed out.")); }, 5000);
224
+ });
225
+ try { session.pty.kill(); await exited; }
226
+ catch (error) { exited.catch(function () {}); throw error; }
227
+ finally { clearTimeout(timer); var index = session.exitWaiters.indexOf(waiter); if (index >= 0) session.exitWaiters.splice(index, 1); }
228
+ }
229
+ if (terminals.get(id) !== session) {
230
+ var replaced = new Error('Terminal was replaced during shutdown.');
231
+ if (control) replaced.productControlResult = { runtimeEffect: session.exited ? 'exited' : 'closure_unconfirmed', replacementRetained: true, retrySafe: false };
232
+ throw replaced;
233
+ }
234
+ if (control) {
235
+ try { control.revalidate(); }
236
+ catch (error) { error.productControlResult = { runtimeEffect: 'exited', retained: true, retrySafe: false }; throw error; }
237
+ }
238
+ close(id); return true;
239
+ }
240
+
207
241
  function rename(id, title) {
208
242
  var session = terminals.get(id);
209
243
  if (!session) return;
@@ -298,6 +332,24 @@ function createTerminalManager(opts) {
298
332
  write: write,
299
333
  resize: resize,
300
334
  close: close,
335
+ 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
+ },
340
+ resizeExact: function (id, cols, rows, identity, revalidate) {
341
+ revalidate();
342
+ var terminal = terminals.get(id);
343
+ if (!terminal || terminal.identity !== identity || !terminal.pty) throw new Error('The exact terminal is unavailable for resizing.');
344
+ terminal.pty.resize(cols, rows);
345
+ revalidate();
346
+ if (terminals.get(id) !== terminal) throw new Error('Terminal was replaced during resize.');
347
+ terminal.cols = cols; terminal.rows = rows;
348
+ var message = JSON.stringify({ type: 'term_resized', id: id, cols: cols, rows: rows });
349
+ terminal.subscribers.forEach(function (ws) { if (ws.readyState === 1) ws.send(message); });
350
+ return true;
351
+ },
352
+ identity: function (id) { var terminal = terminals.get(id); return terminal ? terminal.identity : null; },
301
353
  rename: rename,
302
354
  list: list,
303
355
  getScrollback: getScrollback,
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");
@@ -64,15 +64,12 @@ function attachPermissions(deps) {
64
64
  // --- Project access helpers ---
65
65
 
66
66
  function canAccessProject(userId, project) {
67
- if (!project) return false;
67
+ if (!project || !userId || !findUserById(userId) || project.ownershipRecovery === "required") return false;
68
68
  if (project.isMate === true || (typeof project.slug === "string" && project.slug.indexOf("mate-") === 0)) {
69
69
  return !!userId && !!project.ownerId && project.ownerId === userId;
70
70
  }
71
71
  // Public projects are accessible to all authenticated users
72
72
  if (!project.visibility || project.visibility === "public") return true;
73
- // Admin always has access
74
- var user = findUserById(userId);
75
- if (user && user.role === "admin") return true;
76
73
  // Owner always has access to their own project
77
74
  if (project.ownerId && project.ownerId === userId) return true;
78
75
  // Private project -- check allowedUsers
@@ -96,10 +93,7 @@ function attachPermissions(deps) {
96
93
  return !!userId && !!session.ownerId && session.ownerId === userId;
97
94
  }
98
95
  if (session.userKnowledgeOwnerId) return !!userId && session.userKnowledgeOwnerId === userId && session.ownerId === userId;
99
- // Resolve the stored user record so client-supplied roles cannot elevate access.
100
- var user = findUserById(userId);
101
- if (user && user.role === "admin") return true;
102
- // Sessions without ownerId are legacy -- only admin can see them
96
+ // Unowned historical sessions require explicit recovery; role is not ownership.
103
97
  if (!session.ownerId) return false;
104
98
  // Owner can always see their own sessions
105
99
  if (session.ownerId === userId) return true;
package/lib/users.js CHANGED
@@ -155,6 +155,10 @@ function getOtherUsers(excludeUserId) {
155
155
  }
156
156
 
157
157
  function removeUser(userId) {
158
+ var registered = require("./config").loadConfig();
159
+ if (registered && (registered.projects || []).some(function (project) {
160
+ return !require("./project-ownership").isPersonal(project) && project.ownerId === userId;
161
+ })) return { error: "Transfer ordinary owned projects before deleting this account." };
158
162
  var data = loadUsers();
159
163
  var before = data.users.length;
160
164
  data.users = data.users.filter(function (u) { return u.id !== userId; });
@@ -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;
@@ -96,7 +96,10 @@ function attachWorkspaceQueryAccess(deps) {
96
96
  // as ownership would let a stale status skip the authoritative record.
97
97
  var access = record(status);
98
98
  if (!access) return denied;
99
- if (access.ownerId && access.ownerId === principal.userId) return { owned: true, accessible: true };
99
+ if (access.ownerId && access.ownerId === principal.userId) {
100
+ var ownerPermitted = permitted(principal.userId, access);
101
+ return { owned: ownerPermitted, accessible: ownerPermitted };
102
+ }
100
103
  return { owned: false, accessible: permitted(principal.userId, access) };
101
104
  }
102
105
 
package/lib/ws-schema.js CHANGED
@@ -495,10 +495,11 @@ var schema = {
495
495
  "note_written": { direction: "s2c", handler: "lib/public/modules/app-messages.js", description: "Agent created or updated a sticky note" },
496
496
  "note_update": { direction: "c2s", handler: "lib/project-user-message.js", description: "Update a sticky note" },
497
497
  "note_updated": { direction: "s2c", handler: "lib/public/modules/app-messages.js", description: "Sticky note was updated" },
498
- "note_close": { direction: "c2s", handler: "lib/project-user-message.js", description: "Close a sticky note (reversible; never deletes)" },
498
+ "note_update_result": { direction: "s2c", handler: "lib/public/modules/app-messages.js", description: "Correlated sticky note update result" },
499
+ "note_close": { direction: "c2s", handler: "lib/project-user-message.js", description: "Close a sticky note; human draft-aware requests remove empty notes, tool and legacy closes stay reversible" },
499
500
  "note_reopen": { direction: "c2s", handler: "lib/project-user-message.js", description: "Reopen a closed sticky note" },
500
501
  "note_delete": { direction: "c2s", handler: "lib/project-user-message.js", description: "Retired: older clients still send this; handled as a non-destructive close" },
501
- "note_deleted": { direction: "s2c", handler: "lib/public/modules/app-messages.js", description: "Retired: never sent. Closing broadcasts note_updated instead" },
502
+ "note_deleted": { direction: "s2c", handler: "lib/public/modules/app-messages.js", description: "A sticky note was permanently removed after a human-authorized storage transaction" },
502
503
  "note_list_request": { direction: "c2s", handler: "lib/project-user-message.js", description: "Request the full notes list" },
503
504
  "note_bring_front": { direction: "c2s", handler: "lib/project-user-message.js", description: "Bring a note to front (update z-order)" },
504
505
  "notes_list": { direction: "s2c", handler: "lib/public/modules/app-messages.js", description: "Full list of sticky notes" },
@@ -809,6 +810,11 @@ var schema = {
809
810
  "default_vendor_get": { direction: "c2s", handler: "lib/server-default-vendor.js", description: "Read the authenticated user's New session vendor preference" },
810
811
  "default_vendor_set": { direction: "c2s", handler: "lib/server-default-vendor.js", description: "Validate and persist the authenticated user's New session vendor preference" },
811
812
  "default_vendor_state": { direction: "s2c", handler: "lib/public/modules/default-vendor.js", description: "Correlated or same-user-broadcast New session vendor preference" },
813
+ "clay_ui_ready": { direction: "c2s", handler: "lib/server-owner-ui.js", description: "Advertise exact owner UI navigation support on this socket" },
814
+ "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
+ "clay_navigation_claim": { direction: "c2s", handler: "lib/server-owner-ui.js", description: "Claim an offered navigation with live source/target revalidation" },
816
+ "clay_navigation_apply": { direction: "s2c", handler: "lib/public/modules/clay-owner-navigation.js", description: "Invoke the shared conversation navigator after live claim" },
817
+ "clay_navigation_ack": { direction: "c2s", handler: "lib/server-owner-ui.js", description: "Client invocation acknowledgement; never proof of visible rendering" },
812
818
  "context_view_get": { direction: "c2s", handler: "lib/server-context-view.js", description: "Read the authenticated user's context display preference" },
813
819
  "context_view_set": { direction: "c2s", handler: "lib/server-context-view.js", description: "Persist the authenticated user's context display preference" },
814
820
  "context_view_state": { direction: "s2c", handler: "lib/public/modules/context-view-preference.js", description: "Correlated or same-user-broadcast context display preference" },
@@ -9,8 +9,9 @@ var execFile = require("child_process").execFile;
9
9
  var execFileSync = require("child_process").execFileSync;
10
10
 
11
11
  function findOnPath(binaryName, overrideName) {
12
- var overridePath = overrideName && process.env[overrideName];
13
- if (overridePath && fs.existsSync(overridePath)) return overridePath;
12
+ var discovery = require("./discovery");
13
+ var overridePath = overrideName && discovery.explicitEntry(process.env, overrideName, false);
14
+ if (overridePath !== undefined && overrideName) return overridePath;
14
15
  try {
15
16
  var command = process.platform === "win32" ? "where" : "which";
16
17
  var out = execFileSync(command, [binaryName], {
@@ -18,7 +19,8 @@ function findOnPath(binaryName, overrideName) {
18
19
  encoding: "utf8",
19
20
  stdio: ["pipe", "pipe", "pipe"],
20
21
  });
21
- return out.trim().split(/\r?\n/)[0] || null;
22
+ var resolved = out.trim().split(/\r?\n/)[0] || null;
23
+ return discovery.validEntry(resolved, false) ? resolved : null;
22
24
  } catch (e) {
23
25
  return null;
24
26
  }
@@ -3,19 +3,7 @@ var childProcess = require("child_process");
3
3
  var readline = require("readline");
4
4
  var skillDiscovery = require("../skill-discovery");
5
5
 
6
- function findBinary() {
7
- if (process.env.ANTIGRAVITY_CLI_PATH) return process.env.ANTIGRAVITY_CLI_PATH;
8
- try {
9
- var command = process.platform === "win32" ? "where" : "which";
10
- return childProcess.execFileSync(command, ["agy"], {
11
- encoding: "utf8",
12
- timeout: 3000,
13
- stdio: ["pipe", "pipe", "pipe"],
14
- }).trim().split(/\r?\n/)[0] || null;
15
- } catch (e) {
16
- return null;
17
- }
18
- }
6
+ var findBinary = require("../providers/antigravity-executable").resolve;
19
7
 
20
8
  function modelValue(model) {
21
9
  if (typeof model === "string") return model;
@@ -427,7 +415,8 @@ function createAntigravityAdapter(opts) {
427
415
  var adapter = {
428
416
  vendor: "antigravity",
429
417
  init: async function(initOpts) {
430
- if (!binaryPath) binaryPath = findBinary();
418
+ if (opts.linuxUser || initOpts && initOpts.linuxUser) throw new Error("Antigravity does not support mapped OS-user execution.");
419
+ if (!binaryPath) binaryPath = findBinary({ env: initOpts && initOpts.env || opts.env || process.env });
431
420
  if (!binaryPath) throw new Error("Antigravity CLI binary not found: agy");
432
421
  var fetched = opts._fetchModels ? await opts._fetchModels(binaryPath, cwd) : await fetchModels(binaryPath, cwd);
433
422
  if (fetched && fetched.length) cachedModels = fetched;
@@ -0,0 +1,9 @@
1
+ // Init-free provider transport obligations, shared by the registry and adapter.
2
+ module.exports = {
3
+ questionInput: function(name, input) { return name === "AskUserQuestion" ? input : null; },
4
+ toolTransport: "sdk-mcp",
5
+ catalogLifetime: "query",
6
+ permissionModes: ["auto"],
7
+ mcpPermissionOverrides: true,
8
+ interruptionMessage: "Interrupted · What should Claude do instead?",
9
+ };