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
@@ -8,11 +8,24 @@ function attachSessionGithub(ctx) {
8
8
  var attempts = new Map();
9
9
  var statusCache = new Map();
10
10
  var statusQueue = Promise.resolve();
11
- function readStatus(session, link, stillAllowed) {
11
+ var viewerCache = new Map();
12
+ async function readViewerLogin(identity, viewerKey) {
13
+ var key = JSON.stringify([viewerKey || null, identity || null]);
14
+ var cached = viewerCache.get(key);
15
+ if (cached && cached.until > Date.now()) return cached.value;
16
+ var pending = (ctx.run || github.runGh)(ctx.cwd, ["api", "user"], identity).then(function (value) {
17
+ var login = String(value && value.login || "").trim().toLowerCase();
18
+ viewerCache.set(key, { value: login, until: Date.now() + 300000 });
19
+ return login;
20
+ });
21
+ viewerCache.set(key, { value: pending, until: Date.now() + 30000 });
22
+ try { return await pending; } catch (error) { viewerCache.delete(key); throw error; }
23
+ }
24
+ function readStatus(session, link, stillAllowed, viewerKey, allowPersonalReview) {
12
25
  var identity = authorize(session);
13
- var key = JSON.stringify([session.ownerId || null, identity && identity.uid, identity && identity.home, link.url.toLowerCase()]);
26
+ var key = JSON.stringify([session.ownerId || null, viewerKey || null, !!allowPersonalReview, identity && identity.uid, identity && identity.home, link.url.toLowerCase()]);
14
27
  var cached = statusCache.get(key);
15
- if (cached && (cached.pending || cached.until > Date.now())) return cached.pending || (cached.error ? Promise.reject(cached.error) : Promise.resolve(cached.value));
28
+ if (cached && (cached.pending || cached.until > Date.now())) return cached.pending || (cached.error ? Promise.reject(cached.error) : Promise.resolve({ value: cached.value, personalReviewState: cached.personalReviewState || "unknown" }));
16
29
  var entry = { failures: cached ? cached.failures : 0 };
17
30
  statusCache.set(key, entry);
18
31
  entry.pending = statusQueue.then(async function () {
@@ -20,9 +33,17 @@ function attachSessionGithub(ctx) {
20
33
  var currentIdentity = authorize(session);
21
34
  if (JSON.stringify(currentIdentity) !== JSON.stringify(identity) || (stillAllowed && !stillAllowed())) throw new Error("Session access changed.");
22
35
  entry.value = await github.readReference(ctx.cwd, link.url, identity, ctx.run, true);
36
+ entry.personalReviewState = "clear";
37
+ if (allowPersonalReview && entry.value.reviewRequests && entry.value.reviewRequests.length) {
38
+ try {
39
+ entry.personalReviewState = entry.value.reviewRequests.indexOf(await readViewerLogin(identity, viewerKey)) >= 0 ? "requested" : "clear";
40
+ } catch (viewerError) { entry.personalReviewState = "unknown"; }
41
+ }
42
+ if (!allowPersonalReview) entry.personalReviewState = "unknown";
43
+ delete entry.value.reviewRequests;
23
44
  entry.failures = 0;
24
45
  entry.until = Date.now() + (entry.value.state === "closed" || entry.value.state === "merged" ? 300000 : 45000);
25
- return entry.value;
46
+ return { value: entry.value, personalReviewState: entry.personalReviewState };
26
47
  } catch (error) {
27
48
  entry.error = error;
28
49
  entry.failures++;
@@ -51,21 +72,29 @@ function attachSessionGithub(ctx) {
51
72
  if (ctx.sm.saveSessionFile(session) === false) throw new Error("Could not save GitHub links.");
52
73
  ctx.sm.broadcastSessionList();
53
74
  }
54
- async function refresh(session, force, statusOnly, stillAllowed) {
75
+ async function refresh(session, force, statusOnly, stillAllowed, viewerKey, allowPersonalReview) {
55
76
  var identity = authorize(session);
56
- var id = session.localId + (statusOnly ? ":status" : ":full");
77
+ var id = session.localId + (statusOnly ? ":status:" + (viewerKey || "anonymous") : ":full");
57
78
  if (inflight.has(id)) return inflight.get(id);
58
- if (!force && Date.now() - (attempts.get(id) || 0) < (statusOnly ? 30000 : 60000)) return session.githubLinks || [];
79
+ if (!force && Date.now() - (attempts.get(id) || 0) < (statusOnly ? 30000 : 60000)) return { links: session.githubLinks || [], viewerReviewRequests: {} };
59
80
  attempts.set(id, Date.now());
60
81
  var pending = (async function () {
61
82
  var links = (session.githubLinks || []).slice();
62
83
  var updated = [];
84
+ var viewerReviewRequests = {};
63
85
  for (var link of links) {
64
86
  try {
65
87
  if (stillAllowed && !stillAllowed()) throw new Error("Session access changed.");
66
- updated.push(statusOnly ? Object.assign({}, link, await readStatus(session, link, stillAllowed), { stale: false }) : await github.readReference(ctx.cwd, link.url, identity, ctx.run));
88
+ if (statusOnly) {
89
+ var status = await readStatus(session, link, stillAllowed, viewerKey, allowPersonalReview);
90
+ updated.push(Object.assign({}, link, status.value, { stale: false }));
91
+ if (status.personalReviewState !== "unknown") viewerReviewRequests[link.url.toLowerCase()] = status.personalReviewState;
92
+ } else updated.push(await github.readReference(ctx.cwd, link.url, identity, ctx.run));
93
+ }
94
+ catch (error) {
95
+ updated.push(Object.assign({}, link, { stale: true }));
96
+ if (statusOnly) viewerReviewRequests[link.url.toLowerCase()] = "unknown";
67
97
  }
68
- catch (error) { updated.push(Object.assign({}, link, { stale: true })); }
69
98
  }
70
99
  authorize(session);
71
100
  if (stillAllowed && !stillAllowed()) throw new Error("Session access changed.");
@@ -81,7 +110,7 @@ function attachSessionGithub(ctx) {
81
110
  return next;
82
111
  });
83
112
  if (changed) persist(session);
84
- return session.githubLinks || [];
113
+ return { links: session.githubLinks || [], viewerReviewRequests: viewerReviewRequests };
85
114
  })();
86
115
  inflight.set(id, pending);
87
116
  try { return await pending; } finally { inflight.delete(id); }
@@ -89,7 +118,8 @@ function attachSessionGithub(ctx) {
89
118
  async function invoke(session, name, args) {
90
119
  var identity = authorize(session);
91
120
  if (name === "get_session_github") {
92
- var links = await refresh(session, false);
121
+ var refreshed = await refresh(session, false);
122
+ var links = refreshed.links;
93
123
  var matches = [];
94
124
  if (args.url) {
95
125
  var url = github.parseReference(args.url).url.toLowerCase();
@@ -140,7 +170,12 @@ function attachSessionGithub(ctx) {
140
170
  Array.from(new Set(ids)).forEach(function (id) {
141
171
  var session = ctx.sm.sessions.get(id);
142
172
  if (!session || !ctx.canRefresh(ws, session) || !(session.githubLinks || []).length) return;
143
- refresh(session, false, true, function () { return ctx.canRefresh(ws, session); }).catch(function () {});
173
+ var viewerId = ws._clayUser && ws._clayUser.id;
174
+ var allowPersonalReview = !session.ownerId || !viewerId || viewerId === session.ownerId;
175
+ refresh(session, false, true, function () { return ctx.canRefresh(ws, session); }, viewerId, allowPersonalReview).then(function (result) {
176
+ if (!allowPersonalReview || typeof ctx.sendTo !== "function" || !result.viewerReviewRequests || !ctx.canRefresh(ws, session)) return;
177
+ ctx.sendTo(ws, { type: "session_github_status", projectSlug: ctx.projectSlug || null, accountId: viewerId || null, requestId: msg.requestId || null, sessionId: session.localId, personalReview: result.viewerReviewRequests });
178
+ }).catch(function () {});
144
179
  });
145
180
  return true;
146
181
  },
@@ -0,0 +1,167 @@
1
+ var sessionVisibility = require("./session-visibility");
2
+ function attachSessionHistoryActions(ctx) {
3
+ var sm = ctx.sm, sdk = ctx.sdk, cwd = ctx.cwd, hydrateImageRefs = ctx.hydrateImageRefs;
4
+ var resolveSessionHome = ctx.resolveSessionHome, cancelPendingAskUser = ctx.cancelPendingAskUser;
5
+ var onProcessingChanged = ctx.onProcessingChanged;
6
+ async function rewind(session, uuid, mode, ws, revalidate) {
7
+ if (session._rewindInProgress) throw new Error("Rewind already in progress.");
8
+ session._rewindInProgress = true;
9
+ try {
10
+ // File restoration (delegated to adapter via sdk-bridge)
11
+ if (mode !== "chat") {
12
+ await sdk.rewindExecuteFiles(session, uuid);
13
+ revalidate();
14
+ }
15
+
16
+ // Conversation rollback (skip for files-only mode)
17
+ if (mode !== "files") {
18
+ var targetIdx = -1;
19
+ for (var i = 0; i < session.messageUUIDs.length; i++) {
20
+ if (session.messageUUIDs[i].uuid === uuid) {
21
+ targetIdx = i;
22
+ break;
23
+ }
24
+ }
25
+
26
+ // Codex's thread/revert cuts at a turn id, not a count. Read the
27
+ // target entry's turnId BEFORE trimming local history below.
28
+ var hasTurnsToRollBack = false;
29
+ var targetTurnId = null;
30
+ if (targetIdx >= 0) {
31
+ targetTurnId = session.messageUUIDs[targetIdx].turnId || null;
32
+ for (var ri = targetIdx; ri < session.messageUUIDs.length; ri++) {
33
+ if (session.messageUUIDs[ri].type === "user") { hasTurnsToRollBack = true; break; }
34
+ }
35
+ }
36
+
37
+ // Notify adapter of conversation rollback (e.g. Codex thread/revert)
38
+ // BEFORE trimming local history below. Deliberately not caught
39
+ // here: a failed or impossible (missing turnId) provider-side
40
+ // rollback must abort the whole rewind via the outer catch, never
41
+ // leave local history trimmed ahead of the real provider thread.
42
+ if (hasTurnsToRollBack) {
43
+ await sdk.rollbackConversation(session, targetTurnId);
44
+ revalidate();
45
+ }
46
+
47
+ if (targetIdx >= 0) {
48
+ var trimTo = session.messageUUIDs[targetIdx].historyIndex;
49
+ for (var k = trimTo - 1; k >= 0; k--) {
50
+ if (session.history[k].type === "user_message") {
51
+ trimTo = k;
52
+ break;
53
+ }
54
+ }
55
+ session.history = session.history.slice(0, trimTo);
56
+ session.messageUUIDs = session.messageUUIDs.slice(0, targetIdx);
57
+ // Reset digest checkpoint if it points past the trimmed history
58
+ if (typeof session._dmLastDigestedIndex === "number" && session._dmLastDigestedIndex > trimTo) {
59
+ session._dmLastDigestedIndex = trimTo;
60
+ }
61
+ }
62
+
63
+ var kept = session.messageUUIDs;
64
+ session.lastRewindUuid = kept.length > 0 ? kept[kept.length - 1].uuid : null;
65
+ }
66
+
67
+ if (session.abortController) {
68
+ try { session.abortController.abort(); } catch (e) {}
69
+ }
70
+ if (session.messageQueue) {
71
+ try { session.messageQueue.end(); } catch (e) {}
72
+ }
73
+ session.queryInstance = null;
74
+ session.messageQueue = null;
75
+ session.abortController = null;
76
+ session.blocks = {};
77
+ session.sentToolResults = {};
78
+ session.pendingPermissions = {};
79
+ cancelPendingAskUser(session, "Conversation rewound");
80
+ session.isProcessing = false;
81
+ onProcessingChanged();
82
+
83
+ if (sm.saveSessionFile(session) === false) throw new Error("The conversation was rewound but could not be saved.");
84
+ if (ws) sm.switchSession(session.localId, ws, hydrateImageRefs);
85
+ sm.sendAndRecord(session, { type: "rewind_complete", mode: mode });
86
+ sm.broadcastSessionList();
87
+ } finally { session._rewindInProgress = false; }
88
+ return { mode: mode };
89
+ }
90
+ function saveFork(forked, ws) {
91
+ if (sm.saveSessionFile(forked) !== false) return;
92
+ var error = new Error("The provider fork exists and its local session is retained in memory, but the fork could not be saved. Do not create another fork to retry this save.");
93
+ if (!ws) error.productControlResult = { status: "failed", outcome: "partial", persisted: false, runtimeEffect: "applied", retrySafe: false,
94
+ retainedSession: true, forkedSessionRef: require("./workspace-query-service").sessionRef(ctx.slug, forked), error: error.message };
95
+ throw error;
96
+ }
97
+ async function fork(session, uuid, ws, revalidate) {
98
+ var forkTitle = (session.title || "New Session") + " (fork)";
99
+ var result = await sdk.forkSession(session, uuid);
100
+ revalidate();
101
+ if (result.useLocalHistory) {
102
+ // Copy local history up to the target UUID
103
+ var targetIdx = -1;
104
+ for (var fi = 0; fi < session.messageUUIDs.length; fi++) {
105
+ if (session.messageUUIDs[fi].uuid === uuid) { targetIdx = fi; break; }
106
+ }
107
+ var forkHistory = [];
108
+ if (result.localHistoryEndIndex !== undefined) {
109
+ if (!Number.isInteger(result.localHistoryEndIndex) || result.localHistoryEndIndex < 0 || result.localHistoryEndIndex > session.history.length) throw new Error("The provider fork exists but its local history boundary is unavailable; inspect before retrying");
110
+ forkHistory = session.history.slice(0, result.localHistoryEndIndex);
111
+ } else if (targetIdx >= 0) {
112
+ var trimTo = session.messageUUIDs[targetIdx].historyIndex;
113
+ forkHistory = session.history.slice(0, trimTo);
114
+ } else {
115
+ forkHistory = session.history.slice();
116
+ }
117
+ var create = ws ? sm.createSession : sm.createSessionRaw;
118
+ var forked = create({
119
+ vendor: session.vendor,
120
+ ownerId: session.ownerId || null,
121
+ userKnowledgeOwnerId: session.userKnowledgeOwnerId || null,
122
+ sessionVisibility: sessionVisibility.normalize(session.sessionVisibility),
123
+ }, ws);
124
+ forked.cliSessionId = result.sessionId;
125
+ forked.title = forkTitle;
126
+ forked.history = forkHistory;
127
+ forked.messageUUIDs = [];
128
+ for (var hi = 0; hi < forkHistory.length; hi++) {
129
+ if (forkHistory[hi].type === "message_uuid") {
130
+ forked.messageUUIDs.push({ uuid: forkHistory[hi].uuid, type: forkHistory[hi].messageType, historyIndex: hi, turnId: forkHistory[hi].turnId || null });
131
+ }
132
+ }
133
+ saveFork(forked, ws);
134
+ if (ws) sm.switchSession(forked.localId, ws, hydrateImageRefs);
135
+ sm.broadcastSessionList();
136
+ return forked;
137
+ } else {
138
+ // Read history from CLI session files
139
+ var history = await require("./yoke/session-history").read(session.vendor || "claude", resolveSessionHome(session), cwd, result.sessionId);
140
+ revalidate();
141
+ var options = {
142
+ history: history,
143
+ title: forkTitle,
144
+ vendor: session.vendor,
145
+ ownerId: session.ownerId || null,
146
+ userKnowledgeOwnerId: session.userKnowledgeOwnerId || null,
147
+ sessionVisibility: sessionVisibility.normalize(session.sessionVisibility),
148
+ sessionVisibilityExplicit: session.sessionVisibilityExplicit === true,
149
+ };
150
+ var forked;
151
+ if (ws) forked = sm.resumeSession(result.sessionId, options, ws);
152
+ else {
153
+ forked = sm.createSessionRaw(Object.assign({}, options, { cliSessionId: result.sessionId }));
154
+ forked.title = forkTitle;
155
+ forked.history = history;
156
+ saveFork(forked, ws);
157
+ sm.broadcastSessionList();
158
+ }
159
+ if (forked) {
160
+ if (ws) ws._clayActiveSession = forked.localId;
161
+ }
162
+ return forked;
163
+ }
164
+ }
165
+ return { fork: fork, rewind: rewind };
166
+ }
167
+ module.exports = { attachSessionHistoryActions: attachSessionHistoryActions };
@@ -350,7 +350,7 @@ function attachSessionPair(ctx) {
350
350
  if (boundRoles && boundRoles.workers.indexOf(boundSession) !== -1) return taskControl.workerToolDefs(boundSession);
351
351
  var adHocSplit = !!(group && !group.pair);
352
352
  if (!driverEligibility.isEligibleDriverSession(boundSession, sm)) return [];
353
- var persistentCatalog = boundSession.vendor === "codex" && (!group || !!group.pair);
353
+ var persistentCatalog = require("./yoke/session-tool-transport").contract(boundSession.vendor || "claude").catalogLifetime === "conversation" && (!group || !!group.pair);
354
354
  if (!group && !persistentCatalog) {
355
355
  return workerProposal.getToolDefs(boundSession)
356
356
  .concat(workerPermission.getToolDefs(boundSession, { dormantDriver: true }));
@@ -0,0 +1,64 @@
1
+ // Legacy terminal recovery only. New terminal presentation has been retired.
2
+ function attachSessionRuntimeActions(ctx) {
3
+ function inspect(session) {
4
+ return JSON.stringify([session.terminalId, session.runtimeTerminalId, session.tuiSuspended, [session.terminalId, session.runtimeTerminalId].map(function (id) { return typeof id === "number" && ctx.tm ? ctx.tm.identity(id) : null; })]);
5
+ }
6
+ async function change(session, mode, revalidate) {
7
+ revalidate();
8
+ if (mode !== "gui") throw new Error("New terminal presentation has been removed. Only return to GUI chat is supported; provider threads cannot migrate across vendors.");
9
+ if (session.isProcessing || session._queryStarting || session.queryInstance || Object.keys(session.pendingPermissions || {}).length || Object.keys(session.pendingAskUser || {}).length) throw new Error("Runtime changes require an idle session without an active provider handle or pending approvals.");
10
+ if (session.taskStopRequested || session._pendingMessageDrainPaused || ctx.isPendingQueuePaused && ctx.isPendingQueuePaused(session)) throw new Error("A stopped session cannot be resumed by a runtime change.");
11
+ var vendor = session.vendor || ctx.sm.defaultVendor || "claude";
12
+ var ids = Array.from(new Set([session.terminalId, session.runtimeTerminalId].filter(function (id) { return typeof id === "number"; })));
13
+ if (!ids.length && (session.runtimeMode || session.mode || "gui") === "gui") return { status: "completed", runtimeMode: "gui", runtimeEffect: "none", threadPreserved: true };
14
+ var lifecycle = require("./yoke/provider-contract").resolve(vendor);
15
+ if (typeof lifecycle.supportsLegacyGuiResume !== "function" || !lifecycle.supportsLegacyGuiResume(session)) throw new Error("This provider cannot return a legacy terminal thread to GUI chat.");
16
+ var closed = 0, persisted = false;
17
+ var fields = ['terminalId', 'runtimeTerminalId'];
18
+ var originalFields = fields.map(function (field) { return session[field]; });
19
+ var targets = ids.map(function (id) { return { id: id, identity: ctx.tm && ctx.tm.identity(id), closed: false }; });
20
+ var suspended = session.tuiSuspended;
21
+ function exactTerminals() {
22
+ if (session.tuiSuspended !== suspended) throw new Error("Terminal presentation changed during shutdown.");
23
+ targets.forEach(function (target) {
24
+ var current = ctx.tm && ctx.tm.identity(target.id);
25
+ if (!target.identity || (target.closed ? current !== null : current !== target.identity)) throw new Error("A terminal runtime was replaced during shutdown.");
26
+ });
27
+ fields.forEach(function (field, index) {
28
+ var original = originalFields[index], current = session[field];
29
+ if (current === original || current == null && original == null) return;
30
+ var target = targets.find(function (item) { return item.id === original; });
31
+ if (current == null && target && target.closed) return;
32
+ throw new Error("A new or replacement terminal was attached during shutdown.");
33
+ });
34
+ }
35
+ try {
36
+ for (var target of targets) {
37
+ revalidate();
38
+ exactTerminals();
39
+ if (!ctx.tm || typeof ctx.tm.closeAndWait !== "function") throw new Error("Completion-aware terminal shutdown is unavailable.");
40
+ ctx.tm.markReclaimed(target.id);
41
+ await ctx.tm.closeAndWait(target.id); target.closed = true; closed++;
42
+ revalidate();
43
+ exactTerminals();
44
+ }
45
+ revalidate();
46
+ exactTerminals();
47
+ session.terminalId = null; session.runtimeTerminalId = null;
48
+ ctx.prepareGui(session, { deferSave: true });
49
+ exactTerminals();
50
+ session.runtimeMode = "gui"; session.tuiSuspended = false;
51
+ if (ctx.sm.saveSessionFile(session) !== true) throw new Error("GUI presentation could not be saved after terminal shutdown.");
52
+ persisted = true;
53
+ ctx.sm.broadcastSessionList();
54
+ ctx.publishGui(session);
55
+ return { status: "completed", runtimeMode: "gui", runtimeEffect: closed ? "terminal_closed" : "none", persisted: true, threadPreserved: true, executionStarted: false };
56
+ } catch (error) {
57
+ error.productControlResult = { status: "failed", outcome: "partial", persisted: persisted, runtimeEffect: closed ? "terminal_closed" : "closure_unconfirmed", retrySafe: false, executionStarted: false };
58
+ throw error;
59
+ }
60
+ }
61
+ function inspectLifecycle() { return ctx.tm.list().map(function (terminal) { return Object.assign({}, terminal, { identity: ctx.tm.identity(terminal.id) }); }); }
62
+ return { change: change, inspect: inspect, inspectLifecycle: inspectLifecycle };
63
+ }
64
+ module.exports = { attachSessionRuntimeActions: attachSessionRuntimeActions };
@@ -1,6 +1,5 @@
1
1
  var yoke = require("./yoke");
2
2
  var sessionSpawnMcp = require("./session-spawn-mcp-server");
3
- var cliSessions = require("./cli-sessions");
4
3
  var os = require("os");
5
4
  var osUsers = require("./os-users");
6
5
 
@@ -161,7 +160,7 @@ function attachSessionSpawn(ctx) {
161
160
  var sm = ctx.sm;
162
161
  var queue = createSpawnQueue(SPAWN_CONCURRENCY);
163
162
  var adapters = ctx.adapters || {};
164
- var readCliSessionHistory = ctx.readCliSessionHistory || cliSessions.readCliSessionHistory;
163
+ var readCliSessionHistory = ctx.readCliSessionHistory || function(home, project, sessionId, provider) { return require("./yoke/session-history").read(provider || "claude", home, project, sessionId); };
165
164
 
166
165
  function resolveSessionHome(session) {
167
166
  var linuxUser = ctx.getLinuxUserForSession(session);
@@ -283,7 +282,7 @@ function attachSessionSpawn(ctx) {
283
282
  forkHistory = parent.history.slice();
284
283
  } else {
285
284
  forkHistory = await readCliSessionHistory(
286
- resolveSessionHome(parent), ctx.cwd, forkResult.sessionId
285
+ resolveSessionHome(parent), ctx.cwd, forkResult.sessionId, parent.vendor
287
286
  );
288
287
  }
289
288
  } catch (e) {
@@ -0,0 +1,55 @@
1
+ var yoke = require('./yoke');
2
+ function attachSessionVendor(ctx) {
3
+ function verifyIdentity(session, vendor) {
4
+ if (!ctx.osUsers) return;
5
+ var info = yoke.getVendorInfo(vendor), linuxUser = ctx.getLinuxUserForSession(session);
6
+ if (!info || !info.osUserIsolation || !linuxUser) throw new Error('The provider requires an authorized mapped OS identity.');
7
+ var identity = require('./os-users').resolveOsUserInfo(linuxUser);
8
+ if (!Number.isInteger(identity.uid) || identity.uid < 0 || !Number.isInteger(identity.gid) || identity.gid < 0) throw new Error('The mapped OS identity is unavailable.');
9
+ }
10
+ function setVendor(session, vendor, revalidate) {
11
+ if (!session || ctx.sm.sessions.get(session.localId) !== session) throw new Error('The Driver was deleted or replaced.');
12
+ if (revalidate) revalidate();
13
+ if (!(ctx.adapters || {})[vendor] || (ctx.sm.availableVendors || []).indexOf(vendor) < 0) throw new Error('This provider is unavailable in the project runtime.');
14
+ verifyIdentity(session, vendor);
15
+ if (session.cliSessionId || session.queryInstance || session.isProcessing || session._queryStarting || (session.history || []).length || (session.runtimeMode || session.mode || 'gui') !== 'gui') throw new Error('Provider selection requires a fresh idle GUI Driver without a provider thread or transcript.');
16
+ if (session.taskStopRequested || session._pendingMessageDrainPaused || ctx.isPendingQueuePaused && ctx.isPendingQueuePaused(session)) throw new Error('A stopped Driver requires an explicit separate resume action.');
17
+ var previous = { vendor: session.vendor, model: session.model, effort: session.effort, permissionMode: session.permissionMode, effectivePermissionMode: session.effectivePermissionMode };
18
+ session.vendor = vendor;
19
+ session.model = null;
20
+ session.effort = yoke.clampEffort(vendor, session.effort) || null;
21
+ var modes = yoke.sessionTools.contract(ctx.adapters[vendor]).permissionModes;
22
+ if (session.permissionMode === 'auto' && (!Array.isArray(modes) || modes.indexOf('auto') < 0)) session.permissionMode = 'default';
23
+ session.effectivePermissionMode = null;
24
+ try { if (ctx.sm.saveSessionFile(session) !== true) throw new Error('Could not persist the provider selection.'); }
25
+ catch (error) { Object.assign(session, previous); throw error; }
26
+ try {
27
+ ctx.sm.broadcastSessionList();
28
+ ctx.sm.sendToSession(session, { type: 'model_info', vendor: vendor, sessionId: session.localId, model: '', models: (ctx.sm.modelsByVendor || {})[vendor] || [], availableVendors: ctx.sm.availableVendors || [], installedVendors: ctx.sm.installedVendors || [] });
29
+ ctx.sm.sendToSession(session, { type: 'config_state', vendor: vendor, sessionId: session.localId, model: '', effort: session.effort, mode: session.permissionMode || ctx.sm.currentPermissionMode || 'default' });
30
+ } catch (error) { return { status: 'failed', outcome: 'partial', persisted: true, runtimeEffect: 'none', vendor: vendor, error: 'Provider selection saved, but publication failed: ' + error.message }; }
31
+ return { status: 'completed', persisted: true, runtimeEffect: 'none', vendor: vendor };
32
+ }
33
+ function newSession(source, vendor, revalidate) {
34
+ revalidate();
35
+ if (!(ctx.adapters || {})[vendor] || (ctx.sm.availableVendors || []).indexOf(vendor) < 0) throw new Error('This provider is unavailable in the project runtime.');
36
+ if (source.isProcessing || source._queryStarting || source.taskStopRequested || source._pendingMessageDrainPaused || ctx.isPendingQueuePaused && ctx.isPendingQueuePaused(source)) throw new Error('Create a replacement provider session only from an idle, unstopped Driver.');
37
+ verifyIdentity(source, vendor);
38
+ revalidate();
39
+ var created = ctx.sm.createSessionRaw({ ownerId: source.ownerId || null, vendor: vendor, mode: 'gui', sessionVisibility: 'private' });
40
+ var ref = require('./workspace-query-service').sessionRef(ctx.slug, created), persisted = false;
41
+ try {
42
+ revalidate();
43
+ if (ctx.sm.saveSessionFile(created) !== true) throw new Error('Could not save the new provider session.');
44
+ persisted = true;
45
+ revalidate();
46
+ ctx.sm.broadcastSessionList();
47
+ return { status: 'completed', persisted: true, newSessionRef: ref, vendor: vendor, threadMigrated: false, originalRetained: true, executionStarted: false };
48
+ } catch (error) {
49
+ error.productControlResult = { status: 'failed', outcome: 'partial', persisted: persisted, retainedInMemory: true, newSessionRef: ref, threadMigrated: false, originalRetained: true, executionStarted: false, retrySafe: false };
50
+ throw error;
51
+ }
52
+ }
53
+ return { setVendor: setVendor, newSession: newSession };
54
+ }
55
+ module.exports = { attachSessionVendor: attachSessionVendor };