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
@@ -0,0 +1,164 @@
1
+ var idleRecovery = require('./codex-idle-recovery');
2
+
3
+ // Serialize decisions, not whole model turns. Waiting for a target must release
4
+ // the gate so an unrelated Driver can still answer its Worker's permissions.
5
+ function attachRecoveryAdmission(ctx) {
6
+ var handoff = null;
7
+ var runtimes = [];
8
+ var targets = new Map();
9
+ var cleanupTasks = new Map();
10
+ var cleanupFailures = new Map();
11
+ var cleanup = Promise.resolve();
12
+ var unconfirmedSharedProcess = null;
13
+
14
+ function checkSharedExit() {
15
+ if (!unconfirmedSharedProcess) return;
16
+ if (unconfirmedSharedProcess.exitCode === null && unconfirmedSharedProcess.signalCode === null) {
17
+ throw new Error('Codex shared runtime exit could not be confirmed; recovery remains reserved.');
18
+ }
19
+ unconfirmedSharedProcess = null;
20
+ }
21
+
22
+ function checker(queryOpts) {
23
+ var epoch = ctx.state().epoch;
24
+ return function() {
25
+ idleRecovery.checkAbort(queryOpts);
26
+ var state = ctx.state();
27
+ if ((state.shuttingDown && !state.retiring) || state.epoch !== epoch) throw ctx.shutdownError();
28
+ };
29
+ }
30
+
31
+ function cleanupRuntime(runtime) {
32
+ if (cleanupTasks.has(runtime)) return cleanupTasks.get(runtime);
33
+ var task = cleanup.catch(function() { return null; }).then(function() {
34
+ return runtime.shutdownForRecovery();
35
+ }).then(function() {
36
+ runtimes = runtimes.filter(function(current) { return current !== runtime; });
37
+ targets.forEach(function(current, threadId) { if (current === runtime) targets.delete(threadId); });
38
+ cleanupTasks.delete(runtime);
39
+ return true;
40
+ }, function(error) {
41
+ console.error('[yoke/codex] Isolated recovery runtime shutdown failed:', error.message || error);
42
+ // Retain ownership after an unconfirmed exit. In particular, never admit
43
+ // a second writer just because bounded process cleanup reached its limit.
44
+ cleanupFailures.set(runtime, error);
45
+ return false;
46
+ });
47
+ cleanupTasks.set(runtime, task);
48
+ cleanup = task;
49
+ return task;
50
+ }
51
+
52
+ async function withAdmission(checkCurrent, action) {
53
+ var release;
54
+ var prior = handoff;
55
+ var own = new Promise(function(resolve) { release = resolve; });
56
+ var tail = Promise.resolve(prior).then(function() { return own; });
57
+ handoff = tail;
58
+ tail.then(function() { if (handoff === tail) handoff = null; });
59
+ try {
60
+ await idleRecovery.wait(prior || Promise.resolve(), checkCurrent);
61
+ await idleRecovery.wait(cleanup, checkCurrent);
62
+ checkSharedExit();
63
+ return await action();
64
+ } finally {
65
+ // An aborted waiter still links to its predecessor. Releasing its own
66
+ // slot cannot let a later caller overtake a probe that is still running.
67
+ release();
68
+ }
69
+ }
70
+
71
+ async function releaseTarget(threadId, checkCurrent) {
72
+ var prior = targets.get(threadId);
73
+ if (!prior) return true;
74
+ if (cleanupFailures.has(prior)) throw cleanupFailures.get(prior);
75
+ await prior.retireIdleForRecovery(checkCurrent);
76
+ await cleanup;
77
+ checkCurrent();
78
+ if (cleanupFailures.has(prior)) throw cleanupFailures.get(prior);
79
+ return !targets.has(threadId);
80
+ }
81
+
82
+ async function prepareShared(threadId, checkCurrent) {
83
+ var state = ctx.state();
84
+ if (state.initializing) return false;
85
+ var retiredThreads = [];
86
+ if (state.refCount > 0) {
87
+ var snapshots = state.entries.map(function(entry) { return entry.handle && entry.handle.getIdleRecoverySnapshot(); });
88
+ if (await idleRecovery.retireIdleQueries(state.entries, state.server, checkCurrent)) {
89
+ retiredThreads = snapshots.filter(Boolean).map(function(snapshot) { return snapshot.threadId; });
90
+ }
91
+ }
92
+ state = ctx.state();
93
+ if (!state.server) return true;
94
+ if (!state.refCount && await idleRecovery.runtimeCanStop(state.server, retiredThreads, checkCurrent)) {
95
+ var proc = state.server.proc;
96
+ var stopped = await ctx.stopIdle();
97
+ if (stopped) { unconfirmedSharedProcess = proc || null; checkSharedExit(); }
98
+ return stopped;
99
+ }
100
+ return idleRecovery.releaseThread(state.entries, state.server, threadId, checkCurrent);
101
+ }
102
+
103
+ async function waitUntilAvailable(queryOpts, action) {
104
+ var checkCurrent = checker(queryOpts);
105
+ var waiting = false;
106
+ if (typeof queryOpts.onRecoveryAdmission === 'function') queryOpts.onRecoveryAdmission(true);
107
+ try {
108
+ while (true) {
109
+ checkCurrent();
110
+ // Cancellation returns to the caller immediately, but an in-flight
111
+ // unsubscribe or shutdown retains the gate until its bounded RPC settles.
112
+ var result = await idleRecovery.wait(withAdmission(checkCurrent, action.bind(null, checkCurrent)), checkCurrent);
113
+ if (result) {
114
+ if (waiting && typeof queryOpts.onRecoveryWaiting === 'function') queryOpts.onRecoveryWaiting(false);
115
+ return result;
116
+ }
117
+ if (!waiting && typeof queryOpts.onRecoveryWaiting === 'function') queryOpts.onRecoveryWaiting(true);
118
+ waiting = true;
119
+ await idleRecovery.wait(new Promise(function(resolve) { setTimeout(resolve, 50); }), checkCurrent);
120
+ }
121
+ } finally {
122
+ if (typeof queryOpts.onRecoveryAdmission === 'function') queryOpts.onRecoveryAdmission(false);
123
+ }
124
+ }
125
+
126
+ function createQuery(queryOpts) {
127
+ return waitUntilAvailable(queryOpts, async function(checkCurrent) {
128
+ if (!await releaseTarget(queryOpts.resumeSessionId, checkCurrent)) return false;
129
+ if (!await prepareShared(queryOpts.resumeSessionId, checkCurrent)) return false;
130
+ checkCurrent();
131
+ var runtime = ctx.createRuntime(queryOpts);
132
+ runtimes.push(runtime);
133
+ targets.set(queryOpts.resumeSessionId, runtime);
134
+ try {
135
+ return await runtime.createQuery(Object.assign({}, queryOpts, {
136
+ isolatedRecovery: true,
137
+ onRuntimeFinished: function() { return cleanupRuntime(runtime); },
138
+ }));
139
+ } catch (error) {
140
+ await cleanupRuntime(runtime);
141
+ throw error;
142
+ }
143
+ });
144
+ }
145
+
146
+ return {
147
+ createQuery: createQuery,
148
+ pending: function() { return handoff; },
149
+ checkSharedExit: checkSharedExit,
150
+ hasTarget: function(threadId) { return targets.has(threadId); },
151
+ waitForTarget: function(queryOpts) {
152
+ return waitUntilAvailable(queryOpts, function(checkCurrent) { return releaseTarget(queryOpts.resumeSessionId, checkCurrent); });
153
+ },
154
+ shutdown: function() {
155
+ return Promise.all(runtimes.slice().map(cleanupRuntime)).then(function(results) {
156
+ checkSharedExit();
157
+ if (results.indexOf(false) !== -1) throw new Error('Codex recovery runtime cleanup could not be confirmed.');
158
+ return cleanup;
159
+ });
160
+ },
161
+ };
162
+ }
163
+
164
+ module.exports = { attachRecoveryAdmission: attachRecoveryAdmission };
@@ -0,0 +1,13 @@
1
+ // Serializable descriptions of implemented provider controls. Legacy setting
2
+ // keys and message names describe persistence compatibility, never authority.
3
+ var claude = {
4
+ auto: true,
5
+ permissionModes: [{ value: 'default', label: 'Ask' }, { value: 'plan', label: 'Plan' }, { value: 'acceptEdits', label: 'Auto-accept edits' }],
6
+ thinking: { options: ['disabled', 'adaptive', 'budget'], budget: true },
7
+ };
8
+ var codex = {
9
+ approval: { options: [{ value: 'never', label: 'Auto' }, { value: 'untrusted', label: 'Untrusted' }, { value: 'on-request', label: 'Ask' }], state: 'codexApproval', message: 'set_codex_approval', key: 'approval' },
10
+ sandbox: { options: [{ value: 'read-only', label: 'Read Only' }, { value: 'workspace-write', label: 'Workspace' }, { value: 'danger-full-access', label: 'Full Access' }], state: 'codexSandbox', message: 'set_codex_sandbox', key: 'sandbox' },
11
+ webSearch: { options: [{ value: 'disabled', label: 'Off' }, { value: 'cached', label: 'Cached' }, { value: 'live', label: 'Live' }], state: 'codexWebSearch', message: 'set_codex_websearch', key: 'webSearch' },
12
+ };
13
+ module.exports = { claude: claude, codex: codex };
@@ -0,0 +1,70 @@
1
+ // Installation resolution shared by adapter creation, startup and product UI.
2
+ // This never starts a provider; callers may replace resolvers for isolated tests.
3
+ var fs = require('fs');
4
+ var cp = require('child_process');
5
+ function onPath(binary, user) {
6
+ try {
7
+ if (user && !/^[a-z_][a-z0-9_-]*\$?$/i.test(user)) return null;
8
+ var command = user ? 'su' : process.platform === 'win32' ? 'where' : 'which';
9
+ var args = user ? ['-', user, '-c', 'which ' + binary] : [binary];
10
+ if (!/^[a-zA-Z0-9_.-]+$/.test(binary)) return null;
11
+ var result = cp.execFileSync(command, args, { encoding: 'utf8', timeout: 3000, stdio: ['pipe', 'pipe', 'pipe'] });
12
+ return String(result || '').trim().split(/\r?\n/)[0] || null;
13
+ } catch (error) { return null; }
14
+ }
15
+ // Native commands require an executable regular file. SDK-owned JavaScript
16
+ // entries are readable files launched through Node, not native executables.
17
+ function validEntry(file, script) {
18
+ try {
19
+ if (typeof file !== 'string' || !file || !fs.statSync(file).isFile()) return false;
20
+ fs.accessSync(file, script && /\.(?:c|m)?js$/i.test(file) ? fs.constants.R_OK : fs.constants.X_OK);
21
+ return true;
22
+ } catch (error) { return false; }
23
+ }
24
+ function explicitEntry(env, name, script) {
25
+ if (!Object.prototype.hasOwnProperty.call(env, name)) return undefined;
26
+ if (!validEntry(env[name], script)) throw new Error('Invalid explicit provider executable: ' + name);
27
+ return env[name];
28
+ }
29
+ function pathResolver(binary, override) {
30
+ return function(options) {
31
+ options = options || {};
32
+ var env = options.env || process.env;
33
+ if (!options.user && override) {
34
+ var explicit = explicitEntry(env, override, false);
35
+ if (explicit !== undefined) return explicit;
36
+ }
37
+ var resolved = onPath(binary, options.user);
38
+ return options.user || validEntry(resolved, false) ? resolved : null;
39
+ };
40
+ }
41
+ function bundledResolver(find, binary) {
42
+ return function(options) {
43
+ options = options || {};
44
+ // The mapped account cannot inherit the daemon account's installation.
45
+ if (options.user) return onPath(binary, options.user);
46
+ // The runtime resolver owns all fallback policy. Adding a discovery-only
47
+ // PATH fallback would advertise a runtime that its adapter cannot start.
48
+ return find();
49
+ };
50
+ }
51
+ function acpResolver(vendor) {
52
+ return function(options) {
53
+ if (options && options.user) return null;
54
+ var profiles = require('./acp-agent-profiles');
55
+ return profiles.findAcpAgentPath(profiles.getAcpAgentProfile(vendor));
56
+ };
57
+ }
58
+ function installed(options) {
59
+ var registry = options && options.registry || require('./vendor-registry').VENDOR_REGISTRY;
60
+ var result = {};
61
+ Object.keys(registry).forEach(function(vendor) {
62
+ var info = registry[vendor];
63
+ result[vendor] = false;
64
+ if (options && options.user && info.osUserIsolation !== true) return;
65
+ if (typeof info.resolveExecutable !== 'function') return;
66
+ try { result[vendor] = !!info.resolveExecutable(options || {}); } catch (error) {}
67
+ });
68
+ return result;
69
+ }
70
+ module.exports = { installed: installed, pathResolver: pathResolver, bundledResolver: bundledResolver, acpResolver: acpResolver, validEntry: validEntry, explicitEntry: explicitEntry };
@@ -0,0 +1,8 @@
1
+ function classify(provider, text) {
2
+ var service = require('./provider-contract').resolve(provider).classifyError;
3
+ return typeof service === 'function' ? service(text) : null;
4
+ }
5
+ function contextOverflow(error) {
6
+ return /prompt is too long|context_length|maximum context length/i.test(String(error || ""));
7
+ }
8
+ module.exports = { contextOverflow: contextOverflow, classify: classify };
@@ -0,0 +1,7 @@
1
+ // Provider compatibility at the host boundary. No effects occur on import.
2
+ function prepare(env) { delete env.CLAUDECODE; }
3
+ function reaper(env) {
4
+ return { idleMs: Number(env.CLAY_ADAPTER_IDLE_MS) || Number(env.CLAY_CODEX_IDLE_MS) || 5 * 60 * 1000,
5
+ reapMs: Number(env.CLAY_ADAPTER_REAPER_MS) || Number(env.CLAY_CODEX_REAPER_MS) || 60 * 1000 };
6
+ }
7
+ module.exports = { prepare: prepare, reaper: reaper };
package/lib/yoke/index.js CHANGED
@@ -16,11 +16,12 @@ var createGrokAdapter = require("./adapters/grok").createGrokAdapter;
16
16
  var createCopilotAdapter = require("./adapters/copilot").createCopilotAdapter;
17
17
  var createQwenAdapter = require("./adapters/qwen").createQwenAdapter;
18
18
  var createJunieAdapter = require("./adapters/junie").createJunieAdapter;
19
+ var createMuseAdapter = require("./adapters/muse").createMuseAdapter;
19
20
  var createKiroAdapter = require("./adapters/kiro").createKiroAdapter;
20
21
 
21
22
  // Keep the first session in a new project predictable. This order is also
22
23
  // used by the UI when a project has no remembered vendor yet.
23
- var DEFAULT_VENDOR_ORDER = ["claude", "codex", "grok", "kimi", "copilot", "qwen", "junie", "antigravity", "opencode", "kiro"];
24
+ var DEFAULT_VENDOR_ORDER = ["claude", "codex", "muse", "grok", "kimi", "copilot", "qwen", "junie", "antigravity", "opencode", "kiro"];
24
25
 
25
26
  function resolveDefaultVendor(availableVendors) {
26
27
  availableVendors = availableVendors || {};
@@ -97,6 +98,8 @@ function createAdapter(opts) {
97
98
  adapter = createJunieAdapter(opts);
98
99
  } else if (vendor === "kiro") {
99
100
  adapter = createKiroAdapter(opts);
101
+ } else if (vendor === "muse") {
102
+ adapter = createMuseAdapter(opts);
100
103
  } else {
101
104
  throw new Error("[YOKE] Unknown adapter vendor: " + vendor);
102
105
  }
@@ -262,6 +265,7 @@ function checkAuth() {
262
265
  qwen: !!lookupBinary("qwen"),
263
266
  junie: !!lookupBinary("junie"),
264
267
  kiro: checkKiro(),
268
+ muse: null,
265
269
  };
266
270
  logAuthCheck(_authCache);
267
271
  return _authCache;
@@ -280,54 +284,8 @@ function checkAuth() {
280
284
  var _installedCache = null;
281
285
 
282
286
  function checkInstalled() {
283
- if (_installedCache) return _installedCache;
284
-
285
- var fs = require("fs");
286
- var execFileSync = require("child_process").execFileSync;
287
- var result = { claude: false, codex: false, antigravity: false, opencode: false, kimi: false, grok: false, copilot: false, qwen: false, junie: false, kiro: false };
288
- try {
289
- if (process.platform === "win32") execFileSync("where", ["claude"], { timeout: 3000, stdio: ["pipe", "pipe", "pipe"] });
290
- else execFileSync("which", ["claude"], { timeout: 3000, stdio: ["pipe", "pipe", "pipe"] });
291
- result.claude = true;
292
- } catch (e) {}
293
- try {
294
- var findKiroPath = require("./kiro-acp-server").findKiroPath;
295
- var kiroBin = findKiroPath();
296
- if (kiroBin && fs.existsSync(kiroBin)) result.kiro = true;
297
- } catch (e) {}
298
- try {
299
- var codexBin = null;
300
- try {
301
- var findCodexPath = require("./codex-app-server").findCodexPath;
302
- codexBin = findCodexPath();
303
- if (codexBin && fs.existsSync(codexBin)) {
304
- result.codex = true;
305
- }
306
- } catch (e) {}
307
-
308
- if (!result.codex) {
309
- var whichOut = process.platform === "win32"
310
- ? execFileSync("where", ["codex"], { timeout: 3000, encoding: "utf8", stdio: ["pipe", "pipe", "pipe"] })
311
- : execFileSync("which", ["codex"], { timeout: 3000, encoding: "utf8", stdio: ["pipe", "pipe", "pipe"] });
312
- if (whichOut.trim()) result.codex = true;
313
- }
314
- } catch (e) {}
315
- try {
316
- var antigravityBinary = process.platform === "win32"
317
- ? execFileSync("where", ["agy"], { timeout: 3000, encoding: "utf8", stdio: ["pipe", "pipe", "pipe"] })
318
- : execFileSync("which", ["agy"], { timeout: 3000, encoding: "utf8", stdio: ["pipe", "pipe", "pipe"] });
319
- result.antigravity = !!antigravityBinary.trim();
320
- } catch (e) {}
321
- var acpVendorKeys = ["opencode", "kimi", "grok", "copilot", "qwen", "junie"];
322
- for (var acpIndex = 0; acpIndex < acpVendorKeys.length; acpIndex++) {
323
- try {
324
- var acpProfiles = require("./acp-agent-profiles");
325
- var acpVendor = acpVendorKeys[acpIndex];
326
- result[acpVendor] = !!acpProfiles.findAcpAgentPath(acpProfiles.getAcpAgentProfile(acpVendor));
327
- } catch (e) {}
328
- }
329
- _installedCache = result;
330
- return result;
287
+ if (!_installedCache) _installedCache = require("./discovery").installed();
288
+ return _installedCache;
331
289
  }
332
290
 
333
291
  function invalidateAuthCache() {
@@ -352,7 +310,7 @@ function createAdapters(opts) {
352
310
  // Tests can supply a fixed installation snapshot without probing or spawning
353
311
  // vendor binaries; production callers always use the cached host scan.
354
312
  var installed = opts._installed || checkInstalled();
355
- var auth = { claude: false, codex: false, antigravity: false, opencode: false, kimi: false, grok: false, copilot: false, qwen: false, junie: false, kiro: false };
313
+ var auth = { claude: false, codex: false, antigravity: false, opencode: false, kimi: false, grok: false, copilot: false, qwen: false, junie: false, kiro: false, muse: false };
356
314
  var adapters = {};
357
315
 
358
316
  function supportsConfiguredIsolation(vendor) {
@@ -390,7 +348,7 @@ function createAdapters(opts) {
390
348
  }
391
349
  }
392
350
 
393
- var subprocessVendors = ["antigravity", "opencode", "kimi", "grok", "copilot", "qwen", "junie"];
351
+ var subprocessVendors = ["antigravity", "opencode", "kimi", "grok", "copilot", "qwen", "junie", "muse"];
394
352
  for (var subprocessIndex = 0; subprocessIndex < subprocessVendors.length; subprocessIndex++) {
395
353
  var subprocessVendor = subprocessVendors[subprocessIndex];
396
354
  var subprocessInfo = vendorRegistry.getVendorInfo(subprocessVendor);
@@ -21,6 +21,25 @@ var INITIALIZE_TIMEOUT_MS = 90000;
21
21
 
22
22
  /*
23
23
  * Optional adapter event contract:
24
+ * { yokeType: "turn_activity", phase: "waiting" | "running" | "background", turnId?, disposition? }
25
+ * Waiting reports a pending submission/acknowledged queue, never model
26
+ * reasoning. Running requires an observed native start. Background means the
27
+ * foreground yielded while real terminals remain pending. These presentation
28
+ * facts never settle a turn or authorize tools; human decision surfaces take
29
+ * precedence. Common processing records them for live/replayed progress UI.
30
+ *
31
+ * { yokeType: "foreground_available" }
32
+ * Observed next-message admission while background work retains its real
33
+ * terminal. Offers one queue admission bound to this exact live query. Does
34
+ * not emit result/done, clear processing, settle reviews or complete tasks.
35
+ * Common processing persists an assistant_boundary presentation event before
36
+ * offering admission. Live/replayed clients flush the current assistant bubble
37
+ * at this boundary without treating it as a terminal or completing tools.
38
+ * Stop, paused delivery and query replacement invalidate the admission.
39
+ * A later successful result may carry backgroundOnly: true when its earlier
40
+ * foreground yielded to a newer accepted message. It accounts for that send
41
+ * without settling the newer turn's reviews or user-work completion callbacks.
42
+ *
24
43
  * { yokeType: "background_tasks_changed", tasks: [{ task_id, task_type, description }] }
25
44
  *
26
45
  * Each event replaces the complete live set. An adapter that emits this event
@@ -29,6 +48,25 @@ var INITIALIZE_TIMEOUT_MS = 90000;
29
48
  * and adapters that emit nothing simply have no background-task indicator.
30
49
  */
31
50
 
51
+ /*
52
+ * Optional image output event (independent of image input or invocation APIs):
53
+ * { yokeType: "generated_image", toolId, blockId, data, mediaType,
54
+ * prompt, savedPath, transparentBackground, isError, status }
55
+ * data is base64 image bytes (the common processor also accepts data URLs).
56
+ * toolId identifies this output; blockId associates any existing tool display.
57
+ * prompt is optional observed prompt text; savedPath is optional provenance,
58
+ * never authority to open a provider path. transparentBackground is true/false
59
+ * only when observed, otherwise null. Failed output sets isError and carries
60
+ * no successful image bytes. Common processing saves bytes, persists imageRefs
61
+ * and sends authenticated project URLs; failed saves become tool errors.
62
+ * The historical name covers provider-produced image artifacts, not proof of
63
+ * model generation. Emit only observed bytes, never an input echo, guessed
64
+ * filename or opaque artifact reference. Codex normalizes imageGeneration;
65
+ * Muse can read an explicitly typed raster outputRef through item/readOutput.
66
+ * This optional event does not imply pushMessage image-input support, an
67
+ * always-available model tool, or a dedicated adapter.generateImage() method.
68
+ */
69
+
32
70
  /**
33
71
  * Validate that an adapter object implements all required methods.
34
72
  * Throws if any are missing. Development-time safety net only.
@@ -40,6 +78,84 @@ var INITIALIZE_TIMEOUT_MS = 90000;
40
78
  * .createToolServer(def): ToolServer (opaque)
41
79
  * .createQuery(opts): QueryHandle
42
80
  *
81
+ * Optional query orchestration contract (.queryContract):
82
+ * toolTransport: "sdk-mcp" | "dynamic" | "stdio-mcp" (absent: unsupported)
83
+ * catalogLifetime: "query" | "conversation"
84
+ * externalToolTransport?: "dynamic" for remotely discovered definitions
85
+ * defaultUserInputMode?: "fallback" | "native" (auto otherwise)
86
+ * questionGuidance?(): string; provider-specific protocol guidance
87
+ * permissionModes?: string[]; implemented native modes, e.g. "auto"
88
+ * mcpPermissionOverrides?: true only for a working handle override setter
89
+ * interruptionMessage?: string; truthful feedback after Stop
90
+ * catalogVersion?(session, family): number
91
+ * recordCatalog?(session, versions): void
92
+ * staleCatalogReason?: string
93
+ *
94
+ * Conversation catalogs are immutable on resume. The host uses semantic tool
95
+ * families (schedules, questions, browser, issues, knowledge, linear) and their
96
+ * required revisions to request an exact-query recovery bridge. Adapter-owned
97
+ * mapping reads/writes persisted provider fields, including legacy names. A
98
+ * missing reader means unknown catalog; a missing writer never records success.
99
+ * Writers run only after a matching session_started event on the same live
100
+ * session/query generation. Creating a handle alone is not catalog confirmation.
101
+ * SDK transports must actually mount createToolServer output; dynamic transports
102
+ * must dispatch dynamicTools/callDynamicTool and deliver sessionMcpServer on
103
+ * resume or reject startup truthfully. These declarations never authorize tools:
104
+ * owner/foreign checks, target/review authority and live generation checks remain
105
+ * in common services. Stop cancels pending input/effects immediately; uncertain
106
+ * effects must not replay automatically. Provider protocols stay adapter-side.
107
+ * Init-free contracts live beside adapters and are also exposed by the registry
108
+ * for product controls before adapter initialization. Other providers retain
109
+ * their existing declared stdio transport or default to unsupported.
110
+ *
111
+ * Init-free provider services (adapter.providerContract or registry entry):
112
+ * mapQueryOptions(intent): object. Translates validated common intent to SDK
113
+ * namespaces; host never builds CLAUDE/CODEX/ACP/KIRO envelopes. Intent
114
+ * includes settings, thinking, nativePermissionMode/nativeBypass, readOnly,
115
+ * isolation, resumeAt, instructionSources, betas and start time. readOnly
116
+ * must dominate bypass, sandbox and search options. Unknown providers have
117
+ * no extra options; declared invalid/missing implementations must not imply
118
+ * success. Legacy persisted names remain accepted through provider codecs.
119
+ * configuration: serializable permission/thinking/approval/sandbox/search
120
+ * descriptions. These describe supported controls, never authorization.
121
+ * settings: defaults(), messages(settings), change(message, settings) returns
122
+ * a validated patch and response; host owns persistence and socket access.
123
+ * normalizeContext(value): { usedTokens, windowTokens, measurement, source }.
124
+ * measurement is observed, estimated or unavailable. Zero is a measurement;
125
+ * unknown is null. Billing totals are not context occupancy. YOKE usage
126
+ * projects result/account-quota history to the same client contract.
127
+ * authenticationReply(text), classifyError(text): protocol interpretation.
128
+ * Host handles authenticationRequired and classified errors, preserving
129
+ * identity, user-visible failure and explicit resumption requirements.
130
+ * resolveExecutable(options): path|null; discovery never runs the provider.
131
+ * Explicit supported overrides win; mapped users cannot inherit daemon
132
+ * credentials/installations. Errors or missing resolvers mean unavailable.
133
+ * login command/host metadata: only known validated token-only operations;
134
+ * OAuth URLs require exact hosts, PKCE, state and a loopback callback.
135
+ * Host still authorizes the exact owner/terminal and async flow revision.
136
+ * prepareIsolation, processControl: provider filesystem/PID protocol. Host
137
+ * authorizes identity and the exact process target before effects; absent
138
+ * verification refuses termination. No arbitrary shell/API dispatch.
139
+ * recoveryFactory(ctx): actual begin/enqueue/event/progress/cancel/current/
140
+ * valid/stopping/dispose implementation. Only the implemented confirmed
141
+ * termination protocol may retry; never replay uncertain delivery. Bindings
142
+ * retain the original service for immediate Stop and reject replacement
143
+ * providers. Unsupported providers receive no synthesized watchdog/retry.
144
+ * readSessionHistory(home, cwd, sessionId): actual provider fork-history
145
+ * reader. Local-history forks bypass file reading. Missing/nonfunction
146
+ * readers refuse; the host must not assume another provider's format.
147
+ * supportsLegacyGuiResume(session): implements the actual terminal format;
148
+ * the host retains idle/Stop/identity admission and persistence authority.
149
+ * notificationHooks, maintenance, provisionUser, credentialDirectories:
150
+ * bounded provider services; host owns provisioning and cleanup policy.
151
+ *
152
+ * publicInfo() sends only an explicit serializable field allowlist. Executable
153
+ * callbacks/services, OAuth internals and factories are never client metadata.
154
+ * Unknown/unsupported transports normalize to none. Declared SDK MCP transport
155
+ * with requested definitions must have a callable factory and non-null result;
156
+ * errors fail startup before admission. Capability data never grants canonical
157
+ * registry/query-generation authority, owner permissions or human review.
158
+ *
43
159
  * Optional one-shot capability:
44
160
  * .createOneShotQuery(opts): Promise<{ handle, backendPersistence }>
45
161
  * backendPersistence is "ephemeral" only when the adapter can verify the
@@ -8,9 +8,9 @@ var fs = require("fs");
8
8
  var { AcpProcessManager } = require("./acp-process-manager");
9
9
 
10
10
  function findKiroPath() {
11
- if (process.env.KIRO_CLI_PATH && fs.existsSync(process.env.KIRO_CLI_PATH)) {
12
- return process.env.KIRO_CLI_PATH;
13
- }
11
+ var discovery = require("./discovery");
12
+ var explicit = discovery.explicitEntry(process.env, "KIRO_CLI_PATH", false);
13
+ if (explicit !== undefined) return explicit;
14
14
 
15
15
  var binName = process.platform === "win32" ? "kiro-cli.exe" : "kiro-cli";
16
16
  var realHome;
@@ -23,7 +23,7 @@ function findKiroPath() {
23
23
  "/opt/homebrew/bin/" + binName,
24
24
  ];
25
25
  for (var i = 0; i < candidates.length; i++) {
26
- if (candidates[i] && fs.existsSync(candidates[i])) return candidates[i];
26
+ if (discovery.validEntry(candidates[i], false)) return candidates[i];
27
27
  }
28
28
 
29
29
  try {
@@ -32,7 +32,7 @@ function findKiroPath() {
32
32
  ? execFileSync("where", [binName], { timeout: 3000, encoding: "utf8", stdio: ["pipe", "pipe", "pipe"] })
33
33
  : execFileSync("which", ["kiro-cli"], { timeout: 3000, encoding: "utf8", stdio: ["pipe", "pipe", "pipe"] });
34
34
  var resolved = out.trim().split(/\r?\n/)[0];
35
- if (resolved) return resolved;
35
+ if (discovery.validEntry(resolved, false)) return resolved;
36
36
  } catch (e) {}
37
37
 
38
38
  throw new Error("Could not find kiro-cli binary (looked in ~/.local/bin, PATH, KIRO_CLI_PATH)");
@@ -0,0 +1,89 @@
1
+ // The existing imported terminal format. Product session creation/deletion
2
+ // remains in the host; these are provider file/grammar projections only.
3
+ var fs = require("fs"), path = require("path");
4
+ var hygiene = require("./claude-transcript-hygiene");
5
+ function directory(home, cwd) { return path.join(home, ".claude", "projects", require("../utils").encodeCwd(cwd)); }
6
+ function readHead(filePath) {
7
+ var MAX_READ = 64 * 1024;
8
+ try {
9
+ var stat = fs.statSync(filePath);
10
+ var fd = fs.openSync(filePath, "r");
11
+ var bytesToRead = Math.min(stat.size, MAX_READ);
12
+ var buf = Buffer.alloc(bytesToRead);
13
+ fs.readSync(fd, buf, 0, bytesToRead, 0);
14
+ fs.closeSync(fd);
15
+ var lines = buf.toString("utf8").split("\n");
16
+ var events = [];
17
+ for (var i = 0; i < lines.length; i++) {
18
+ if (!lines[i]) continue;
19
+ try {
20
+ var event = JSON.parse(lines[i]);
21
+ if (event && typeof event === "object") events.push(event);
22
+ } catch (e) {}
23
+ }
24
+ return events;
25
+ } catch (e) {
26
+ return null;
27
+ }
28
+ }
29
+
30
+ function metadata(events, fp) {
31
+ var userCount = 0;
32
+ var firstUserText = null;
33
+ var createdAtIso = null;
34
+ for (var li = 0; li < events.length; li++) {
35
+ var ev = events[li];
36
+ if (ev.type === "user" && ev.message && ev.message.role === "user") {
37
+ userCount++;
38
+ if (firstUserText == null) {
39
+ var c = ev.message.content;
40
+ if (typeof c === "string") {
41
+ firstUserText = c;
42
+ } else if (Array.isArray(c)) {
43
+ var parts = [];
44
+ for (var ci = 0; ci < c.length; ci++) {
45
+ if (c[ci] && c[ci].type === "text" && typeof c[ci].text === "string") parts.push(c[ci].text);
46
+ }
47
+ firstUserText = parts.join("");
48
+ }
49
+ if (ev.timestamp && !createdAtIso) createdAtIso = ev.timestamp;
50
+ }
51
+ }
52
+ }
53
+
54
+ if (userCount === 0) return null;
55
+ // Defensive warmup skip (daemon also cleans these)
56
+ if (hygiene.isClaudeWarmupTranscript(events)) return null;
57
+
58
+ // Title: first user message trimmed/truncated. Fall back to a
59
+ // neutral label so the sidebar entry is identifiable.
60
+ var title = (firstUserText || "").trim().replace(/\s+/g, " ");
61
+ if (title.length > 60) title = title.slice(0, 57) + "...";
62
+ if (!title) title = "Imported CLI session";
63
+
64
+ var createdAt = Date.now();
65
+ if (createdAtIso) {
66
+ var t = Date.parse(createdAtIso);
67
+ if (!isNaN(t)) createdAt = t;
68
+ }
69
+ var lastActivity = createdAt;
70
+ try { lastActivity = fs.statSync(fp).mtimeMs; } catch (e) {}
71
+
72
+ return { title: title, createdAt: createdAt, lastActivity: lastActivity };
73
+ }
74
+ function candidates(dir, known, deleted) {
75
+ var files;
76
+ try { files = fs.readdirSync(dir); } catch (error) { return []; }
77
+ return files.filter(function(file) { return file.endsWith(".jsonl"); }).flatMap(function(file) {
78
+ var id = file.slice(0, -6);
79
+ if (known.has(id) || deleted.has(id)) return [];
80
+ var filename = path.join(dir, file), events = readHead(filename);
81
+ var value = events && metadata(events, filename);
82
+ return value ? [Object.assign({ id: id, vendor: "claude" }, value)] : [];
83
+ });
84
+ }
85
+ function obsolete(dir, session) {
86
+ var filename = path.join(dir, session.cliSessionId + ".jsonl"), events = readHead(filename);
87
+ return !fs.existsSync(filename) && (!session.history || session.history.length === 0) || events && hygiene.isClaudeWarmupTranscript(events);
88
+ }
89
+ module.exports = { candidates: candidates, obsolete: obsolete, directory: directory, readHead: readHead, metadata: metadata, isWarmup: hygiene.isClaudeWarmupTranscript };
@@ -0,0 +1,25 @@
1
+ // Known provider login operations and output grammar; never accepts a command
2
+ // from model/client input. Identity and terminal permission stay in the host.
3
+ var PATTERNS = [/successfully logged in(?![ \t]+to[ \t]+mcp)/i, /logged in successfully/i, /login successful/i,
4
+ /successfully authenticated/i, /authentication successful/i, /signed in successfully/i,
5
+ /signed in with your [a-z ]+ account/i, /you(?:'re| are) (?:now )?(?:logged|signed) in/i];
6
+ function stripAnsi(text) { return String(text).replace(/\x1b\[[0-9;?]*[ -\/]*[@-~]|\x1b\][^\x07\x1b]*(?:\x07|\x1b\\)|\r/g, ''); }
7
+ function successful(text) { return PATTERNS.some(function(pattern) { return pattern.test(text); }); }
8
+ function command(vendor, browser) {
9
+ var info = require('./provider-contract').resolve(vendor);
10
+ var operation = browser ? info.browserLoginCommand : info.terminalLoginCommand || info.loginCommand;
11
+ if (typeof operation !== 'string' || !/^[a-zA-Z0-9_.-]+(?: [a-zA-Z0-9_.-]+)*$/.test(operation)) throw new Error('Provider sign-in is unavailable.');
12
+ return operation;
13
+ }
14
+ function loginUrl(value, provider) {
15
+ try {
16
+ var hosts = require('./provider-contract').resolve(provider).oauthHosts || [];
17
+ var url = new URL(value);
18
+ if (url.protocol !== 'https:' || url.username || url.password || hosts.indexOf(url.hostname) < 0) return null;
19
+ if (!url.searchParams.get('state') || !url.searchParams.get('code_challenge')) return null;
20
+ var callback = new URL(url.searchParams.get('redirect_uri'));
21
+ if (callback.protocol !== 'http:' || ['localhost', '127.0.0.1', '[::1]'].indexOf(callback.hostname) < 0) return null;
22
+ return url.href;
23
+ } catch (error) { return null; }
24
+ }
25
+ module.exports = { stripAnsi: stripAnsi, successful: successful, command: command, loginUrl: loginUrl };