clay-server 3.8.1 → 4.0.0-beta.10

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 (222) hide show
  1. package/lib/ask-user-mcp-server.js +5 -119
  2. package/lib/background-task-timing.js +105 -0
  3. package/lib/builtin-mates.js +6 -6
  4. package/lib/claude-hook-installer.js +15 -0
  5. package/lib/clay-history-mcp-server.js +62 -297
  6. package/lib/daemon.js +23 -1
  7. package/lib/debate-model-selection.js +119 -0
  8. package/lib/home-debate-tool-policy.js +164 -0
  9. package/lib/home-response-notifications.js +26 -0
  10. package/lib/knowledge-import.js +436 -0
  11. package/lib/knowledge-record-store.js +188 -0
  12. package/lib/knowledge-search.js +224 -0
  13. package/lib/mate-creation-mcp-server.js +55 -0
  14. package/lib/mate-knowledge-mcp-server.js +151 -0
  15. package/lib/mate-knowledge-migration.js +283 -0
  16. package/lib/mate-knowledge-service.js +488 -0
  17. package/lib/mate-knowledge-sync.js +482 -0
  18. package/lib/mate-ready-creation.js +95 -0
  19. package/lib/mates.js +2 -0
  20. package/lib/notes.js +2 -1
  21. package/lib/project-ask-user.js +64 -0
  22. package/lib/project-capsule-catalog.js +101 -0
  23. package/lib/project-connection.js +17 -0
  24. package/lib/project-debate-proposal.js +133 -17
  25. package/lib/project-debate.js +214 -110
  26. package/lib/project-delegated-follow-up.js +84 -0
  27. package/lib/project-delegated-session.js +53 -0
  28. package/lib/project-filesystem.js +7 -9
  29. package/lib/project-knowledge.js +23 -0
  30. package/lib/project-logs-comments.js +137 -0
  31. package/lib/project-logs-mcp-server.js +290 -0
  32. package/lib/project-logs-query.js +167 -0
  33. package/lib/project-logs-root.js +67 -0
  34. package/lib/project-logs-schema.js +259 -0
  35. package/lib/project-logs-service.js +357 -0
  36. package/lib/project-logs-snapshot.js +202 -0
  37. package/lib/project-logs-store.js +498 -0
  38. package/lib/project-logs-versioning.js +128 -0
  39. package/lib/project-logs.js +347 -0
  40. package/lib/project-mate-creation-proposal.js +120 -0
  41. package/lib/project-mate-interaction.js +14 -0
  42. package/lib/project-mate-knowledge.js +110 -0
  43. package/lib/project-memory.js +36 -7
  44. package/lib/project-models.js +157 -32
  45. package/lib/project-session-notes.js +2 -2
  46. package/lib/project-session-pair.js +8 -8
  47. package/lib/project-sessions.js +66 -51
  48. package/lib/project-vendor-login.js +414 -0
  49. package/lib/project-worker-proposal.js +25 -25
  50. package/lib/project-workspace-query.js +144 -0
  51. package/lib/project.js +433 -102
  52. package/lib/public/app.js +109 -48
  53. package/lib/public/css/avatar-imprints.css +0 -1
  54. package/lib/public/css/base.css +4 -0
  55. package/lib/public/css/capsule-ui.css +389 -0
  56. package/lib/public/css/command-palette.css +306 -6
  57. package/lib/public/css/home-capsule-library.css +243 -0
  58. package/lib/public/css/home-capsule-source.css +192 -0
  59. package/lib/public/css/home-debate-live.css +146 -0
  60. package/lib/public/css/home-debate-planning.css +125 -0
  61. package/lib/public/css/home-debates-archive.css +281 -0
  62. package/lib/public/css/home-hub.css +896 -405
  63. package/lib/public/css/home-mate-creation.css +34 -0
  64. package/lib/public/css/home-mate-model-picker.css +131 -0
  65. package/lib/public/css/home-mate-settings.css +344 -0
  66. package/lib/public/css/home-session-actions.css +265 -0
  67. package/lib/public/css/home-sidebar.css +667 -0
  68. package/lib/public/css/icon-strip.css +1 -2
  69. package/lib/public/css/input.css +79 -25
  70. package/lib/public/css/mates.css +7 -34
  71. package/lib/public/css/pane.css +1 -1
  72. package/lib/public/css/playbook.css +0 -80
  73. package/lib/public/css/project-logs.css +234 -0
  74. package/lib/public/css/scheduler.css +1 -1
  75. package/lib/public/css/sticky-notes.css +53 -117
  76. package/lib/public/css/tui-attention.css +0 -44
  77. package/lib/public/css/workspace-assignment.css +45 -0
  78. package/lib/public/fonts/source-serif-4/LICENSE.md +93 -0
  79. package/lib/public/fonts/source-serif-4/SourceSerif4Caption-Semibold.ttf.woff2 +0 -0
  80. package/lib/public/index.html +96 -74
  81. package/lib/public/modules/app-connection.js +100 -5
  82. package/lib/public/modules/app-dm.js +43 -160
  83. package/lib/public/modules/app-home-hub.js +328 -585
  84. package/lib/public/modules/app-message-cards.js +242 -0
  85. package/lib/public/modules/app-message-router.js +84 -0
  86. package/lib/public/modules/app-messages.js +124 -38
  87. package/lib/public/modules/app-notifications.js +8 -119
  88. package/lib/public/modules/app-panels.js +43 -2
  89. package/lib/public/modules/app-projects.js +9 -1
  90. package/lib/public/modules/app-rendering.js +21 -602
  91. package/lib/public/modules/avatar-imprint.js +11 -8
  92. package/lib/public/modules/background-tasks-ui.js +139 -16
  93. package/lib/public/modules/chat-bubble-renderer.js +152 -0
  94. package/lib/public/modules/chat-render-runtime.js +198 -0
  95. package/lib/public/modules/command-palette.js +225 -436
  96. package/lib/public/modules/dm-render.js +82 -0
  97. package/lib/public/modules/home-capsule-creation-intent.js +31 -0
  98. package/lib/public/modules/home-capsule-library.js +146 -0
  99. package/lib/public/modules/home-capsule-source.js +383 -0
  100. package/lib/public/modules/home-chat-identity.js +35 -0
  101. package/lib/public/modules/home-chat-scroll.js +77 -0
  102. package/lib/public/modules/home-chat-stream-state.js +34 -0
  103. package/lib/public/modules/home-composer-focus.js +25 -0
  104. package/lib/public/modules/home-conversations-sheet.js +179 -0
  105. package/lib/public/modules/home-debate-activity.js +48 -0
  106. package/lib/public/modules/home-debate-controls.js +295 -0
  107. package/lib/public/modules/home-debate-launch.js +41 -0
  108. package/lib/public/modules/home-debate-live.js +284 -0
  109. package/lib/public/modules/home-debate-models.js +73 -0
  110. package/lib/public/modules/home-debate-planning.js +335 -0
  111. package/lib/public/modules/home-debates-archive.js +279 -0
  112. package/lib/public/modules/home-dock-resize.js +74 -0
  113. package/lib/public/modules/home-dock.js +446 -0
  114. package/lib/public/modules/home-mate-chat.js +496 -0
  115. package/lib/public/modules/home-mate-creation.js +109 -0
  116. package/lib/public/modules/home-mate-model-picker.js +281 -0
  117. package/lib/public/modules/home-mate-selection.js +38 -0
  118. package/lib/public/modules/home-mate-settings-menu.js +131 -0
  119. package/lib/public/modules/home-mate-settings.js +383 -0
  120. package/lib/public/modules/home-session-actions.js +373 -0
  121. package/lib/public/modules/home-session-model-confirmation.js +13 -0
  122. package/lib/public/modules/home-shell.js +9 -0
  123. package/lib/public/modules/home-sidebar-chat-list.js +63 -0
  124. package/lib/public/modules/home-sidebar.js +165 -0
  125. package/lib/public/modules/home-sub-surface.js +19 -0
  126. package/lib/public/modules/home-surface-boot.js +68 -0
  127. package/lib/public/modules/home-surface.js +141 -0
  128. package/lib/public/modules/home-tools.js +271 -0
  129. package/lib/public/modules/markdown.js +50 -2
  130. package/lib/public/modules/mate-management.js +86 -0
  131. package/lib/public/modules/mate-sidebar.js +0 -8
  132. package/lib/public/modules/mate-wizard.js +0 -7
  133. package/lib/public/modules/mention.js +17 -19
  134. package/lib/public/modules/pane-bridge.js +9 -0
  135. package/lib/public/modules/profile.js +4 -5
  136. package/lib/public/modules/project-logs-ambient.js +238 -0
  137. package/lib/public/modules/project-logs-render.js +359 -0
  138. package/lib/public/modules/project-logs.js +328 -0
  139. package/lib/public/modules/project-settings.js +34 -28
  140. package/lib/public/modules/project-switcher.js +11 -5
  141. package/lib/public/modules/project-workspace-assignment.js +23 -0
  142. package/lib/public/modules/scheduler.js +17 -3
  143. package/lib/public/modules/search-clay-chat.js +402 -0
  144. package/lib/public/modules/server-settings.js +14 -12
  145. package/lib/public/modules/session-actions.js +1 -1
  146. package/lib/public/modules/sidebar-mates.js +56 -552
  147. package/lib/public/modules/sidebar-mobile.js +12 -73
  148. package/lib/public/modules/sidebar-presence.js +37 -0
  149. package/lib/public/modules/sidebar-sessions.js +25 -1
  150. package/lib/public/modules/split-pair-ui.js +13 -13
  151. package/lib/public/modules/split-view.js +7 -0
  152. package/lib/public/modules/sticky-notes.js +106 -53
  153. package/lib/public/modules/tool-input-composition.js +48 -0
  154. package/lib/public/modules/tool-llm-status.js +167 -0
  155. package/lib/public/modules/tool-palette.js +76 -4
  156. package/lib/public/modules/tool-renderer-advanced.js +285 -0
  157. package/lib/public/modules/tool-renderer-chart.js +158 -0
  158. package/lib/public/modules/tool-renderer-semantics.js +35 -0
  159. package/lib/public/modules/tool-renderer.js +464 -0
  160. package/lib/public/modules/tool-runtime.js +309 -0
  161. package/lib/public/modules/tool-ui-evaluator.js +97 -0
  162. package/lib/public/modules/tool-ui-tree.js +21 -0
  163. package/lib/public/modules/tui-attention.js +10 -0
  164. package/lib/public/modules/user-settings.js +16 -23
  165. package/lib/public/modules/vendor-login.js +287 -0
  166. package/lib/public/modules/worker-proposal.js +2 -2
  167. package/lib/public/modules/workspace-assignment-card.js +149 -0
  168. package/lib/public/modules/workspace-assignment-routing.js +4 -0
  169. package/lib/public/style.css +14 -2
  170. package/lib/runtime-env.js +65 -0
  171. package/lib/sdk-bridge.js +120 -32
  172. package/lib/sdk-message-processor.js +34 -13
  173. package/lib/server-home-capsule-creation.js +36 -0
  174. package/lib/server-home-chat-events.js +287 -0
  175. package/lib/server-home-chat.js +518 -0
  176. package/lib/server-home-clay-entry.js +65 -0
  177. package/lib/server-home-clay-session-links.js +26 -0
  178. package/lib/server-home-debate-planning.js +214 -0
  179. package/lib/server-home-debates.js +137 -0
  180. package/lib/server-home-mate-creation.js +261 -0
  181. package/lib/server-home-models.js +241 -0
  182. package/lib/server-home-preferences.js +61 -0
  183. package/lib/server-mates.js +37 -0
  184. package/lib/server-tools.js +377 -0
  185. package/lib/server.js +166 -59
  186. package/lib/session-pair-mcp-server.js +6 -6
  187. package/lib/sessions.js +22 -4
  188. package/lib/tool-capsule-source.js +142 -0
  189. package/lib/tool-control-mcp-server.js +126 -0
  190. package/lib/tool-llm.js +48 -0
  191. package/lib/tool-storage.js +77 -0
  192. package/lib/tool-ui-spec-advanced.js +82 -0
  193. package/lib/tool-ui-spec.js +261 -0
  194. package/lib/tools-registry.js +286 -0
  195. package/lib/users-home-dock-preferences.js +77 -0
  196. package/lib/users-home-surface-preferences.js +99 -0
  197. package/lib/users-preferences.js +4 -7
  198. package/lib/users.js +12 -0
  199. package/lib/whats-new-content.js +3 -3
  200. package/lib/workspace-assignment-service.js +420 -0
  201. package/lib/workspace-query-mcp-server.js +154 -0
  202. package/lib/workspace-query-service.js +492 -0
  203. package/lib/ws-schema.js +107 -12
  204. package/lib/yoke/adapters/acp.js +2 -1
  205. package/lib/yoke/adapters/antigravity.js +2 -1
  206. package/lib/yoke/adapters/claude-worker.js +11 -4
  207. package/lib/yoke/adapters/claude.js +141 -46
  208. package/lib/yoke/adapters/codex.js +79 -11
  209. package/lib/yoke/adapters/kiro.js +2 -1
  210. package/lib/yoke/claude-user-input.js +38 -0
  211. package/lib/yoke/codex-app-server.js +20 -2
  212. package/lib/yoke/codex-background-tasks.js +8 -2
  213. package/lib/yoke/codex-user-input.js +64 -0
  214. package/lib/yoke/complete-once.js +164 -0
  215. package/lib/yoke/index.js +5 -1
  216. package/lib/yoke/interface.js +19 -0
  217. package/lib/yoke/mcp-bridge-server.js +2 -6
  218. package/lib/yoke/user-input.js +254 -0
  219. package/package.json +4 -3
  220. package/lib/public/css/home-chat.css +0 -380
  221. package/lib/public/modules/home-chat.js +0 -438
  222. package/lib/server-clay-home.js +0 -245
@@ -10,6 +10,24 @@ var INITIALIZE_TIMEOUT_MS = require("../interface").INITIALIZE_TIMEOUT_MS;
10
10
  var skillDiscovery = require("../skill-discovery");
11
11
  var { resolveOsUserInfo } = require("../../os-users");
12
12
  var backgroundTasks = require("../codex-background-tasks");
13
+ var codexUserInput = require("../codex-user-input");
14
+
15
+ // --- Reasoning defaults ---
16
+ // Reasoning summaries are the only readable thinking channel Codex offers:
17
+ // raw reasoning content is encrypted for ChatGPT-authenticated models, so the
18
+ // item/reasoning/* events this adapter already maps to thinking only carry
19
+ // text when the app-server is asked for summaries. The CLI default
20
+ // (`model_reasoning_summary = "auto"`) frequently produces none at all, and the
21
+ // app-server otherwise inherits whatever is in the user's ~/.codex/config.toml,
22
+ // so Clay asks for detailed summaries explicitly.
23
+ //
24
+ // `show_raw_agent_reasoning` is deliberately absent: it is encrypted on
25
+ // ChatGPT-auth models and noisy elsewhere, so it stays a user opt-in.
26
+ // Everything here is overridable via adapterOptions.CODEX.config.
27
+ var CODEX_REASONING_DEFAULTS = {
28
+ model_reasoning_summary: "detailed",
29
+ model_supports_reasoning_summaries: true,
30
+ };
13
31
 
14
32
  // --- Event flattening ---
15
33
  // Converts app-server JSON-RPC notifications into flat objects with a yokeType field.
@@ -726,6 +744,7 @@ function createCodexQueryHandle(appServer, queryOpts) {
726
744
  var canUseTool = queryOpts.canUseTool || null;
727
745
  var callDynamicTool = queryOpts.callDynamicTool || null;
728
746
  var onElicitation = queryOpts.onElicitation || null;
747
+ var onUserInputRequest = queryOpts.onUserInputRequest || null;
729
748
  var onFinished = queryOpts.onFinished || null;
730
749
 
731
750
  // Check if the query was cancelled (either via handle.abort() or direct signal abort)
@@ -925,12 +944,23 @@ function createCodexQueryHandle(appServer, queryOpts) {
925
944
  return;
926
945
  }
927
946
 
928
- // MCP tool approval / elicitation (app-server uses mcpServer/elicitation/request)
929
- if (method === "item/tool/requestUserInput" || method === "mcpServer/elicitation/request") {
947
+ if (method === "item/tool/requestUserInput") {
948
+ if (!onUserInputRequest) {
949
+ appServer.respondError(msg.id, -32002, "This query cannot request structured user input.");
950
+ return;
951
+ }
952
+ codexUserInput.handleNative(appServer, msg, onUserInputRequest, state.threadId, abortController && abortController.signal);
953
+ return;
954
+ }
955
+
956
+ // MCP elicitation shares the YOKE user-input lifecycle when available.
957
+ if (method === "mcpServer/elicitation/request") {
930
958
  var mcpParams = msg.params || {};
931
959
  var mcpMeta = mcpParams._meta || {};
932
960
  console.log("[yoke/codex] MCP approval request:", (mcpMeta.tool || "?"), "server=" + (mcpParams.serverName || "?"));
933
- if (onElicitation) {
961
+ if (onUserInputRequest) {
962
+ codexUserInput.handleElicitation(appServer, msg, onUserInputRequest, abortController && abortController.signal);
963
+ } else if (onElicitation) {
934
964
  var request = {
935
965
  serverName: mcpParams.serverName || (mcpMeta.tool || "Tool"),
936
966
  message: mcpParams.message || mcpParams.prompt || "",
@@ -1049,6 +1079,7 @@ function createCodexQueryHandle(appServer, queryOpts) {
1049
1079
  cwd: queryOpts.cwd,
1050
1080
  skipGitRepoCheck: true,
1051
1081
  };
1082
+ if (queryOpts.ephemeral === true) threadParams.ephemeral = true;
1052
1083
  if (queryOpts.modelReasoningEffort) {
1053
1084
  threadParams.modelReasoningEffort = queryOpts.modelReasoningEffort;
1054
1085
  }
@@ -1079,6 +1110,11 @@ function createCodexQueryHandle(appServer, queryOpts) {
1079
1110
  threadResult = await appServer.send("thread/start", threadParams, 60000);
1080
1111
  }
1081
1112
 
1113
+ if (queryOpts.ephemeral === true
1114
+ && (!threadResult || !threadResult.thread || threadResult.thread.ephemeral !== true)) {
1115
+ throw new Error("Codex did not confirm an ephemeral one-shot thread.");
1116
+ }
1117
+
1082
1118
  if (threadResult && threadResult.thread) {
1083
1119
  state.threadId = threadResult.thread.id;
1084
1120
  handlerEntry.threadId = state.threadId;
@@ -1110,7 +1146,7 @@ function createCodexQueryHandle(appServer, queryOpts) {
1110
1146
 
1111
1147
  // Explicit references use Codex's native skill input item regardless of
1112
1148
  // which vendor owns the SKILL.md file.
1113
- var availableSkills = skillDiscovery.discoverSkills(queryOpts.cwd);
1149
+ var availableSkills = queryOpts.skipSkills ? [] : skillDiscovery.discoverSkills(queryOpts.cwd);
1114
1150
  var skillItemsToInject = [];
1115
1151
  var injected = {};
1116
1152
  for (var ii = 0; ii < input.length; ii++) {
@@ -1293,15 +1329,14 @@ function createCodexAdapter(opts) {
1293
1329
  // model listing must not depend on a successful app-server init — otherwise a
1294
1330
  // slow/failed `initialize` leaves the picker empty and the chip shows the
1295
1331
  // previous vendor's model.
1332
+ // Kept in sync with what `model/list` reports for the bundled codex build
1333
+ // (0.152.1). Models the CLI no longer offers are rejected at turn/start, so
1334
+ // listing them would only surface a runtime failure in the model picker.
1296
1335
  var CODEX_MODELS = [
1297
1336
  "gpt-5.6-terra",
1298
1337
  "gpt-5.6-sol",
1299
1338
  "gpt-5.6-luna",
1300
1339
  "gpt-5.5",
1301
- "gpt-5.4",
1302
- "gpt-5.4-mini",
1303
- "gpt-5.3-codex",
1304
- "gpt-5.3-codex-spark",
1305
1340
  "gpt-5.2",
1306
1341
  ];
1307
1342
  var _cachedModels = CODEX_MODELS.slice();
@@ -1516,6 +1551,7 @@ function createCodexAdapter(opts) {
1516
1551
 
1517
1552
  var adapter = {
1518
1553
  vendor: "codex",
1554
+ userInputCapability: { mode: "native", native: true, transport: "item/tool/requestUserInput" },
1519
1555
 
1520
1556
  init: function(initOpts) {
1521
1557
  var requestedLinuxUser = initOpts && initOpts.linuxUser;
@@ -1543,17 +1579,21 @@ function createCodexAdapter(opts) {
1543
1579
  if (_initPromise) return _initPromise;
1544
1580
 
1545
1581
  _initPromise = (async function() {
1546
- var serverOpts = { cwd: _cwd };
1582
+ var serverOpts = { cwd: _cwd, env: effectiveInitOpts.env || null };
1547
1583
  if (_runtimeLinuxUser) {
1548
1584
  serverOpts.osUserInfo = _resolveOsUserInfo(_runtimeLinuxUser);
1549
1585
  }
1550
1586
 
1587
+ // Clay's config defaults go in first so any user-supplied
1588
+ // adapterOptions.CODEX.config key of the same name overrides them.
1589
+ serverOpts.config = Object.assign({}, CODEX_REASONING_DEFAULTS);
1590
+
1551
1591
  // Extract adapter options
1552
1592
  if (effectiveInitOpts && effectiveInitOpts.adapterOptions && effectiveInitOpts.adapterOptions.CODEX) {
1553
1593
  var co = effectiveInitOpts.adapterOptions.CODEX;
1554
1594
  if (co.apiKey) serverOpts.env = Object.assign({}, serverOpts.env || {}, { OPENAI_API_KEY: co.apiKey });
1555
1595
  if (co.baseUrl) serverOpts.env = Object.assign({}, serverOpts.env || {}, { OPENAI_BASE_URL: co.baseUrl });
1556
- if (co.config) serverOpts.config = co.config;
1596
+ if (co.config) serverOpts.config = Object.assign(serverOpts.config, co.config);
1557
1597
  }
1558
1598
 
1559
1599
  // Track 1: Read local MCP server definitions from ~/.clay/mcp.json
@@ -1749,9 +1789,11 @@ function createCodexAdapter(opts) {
1749
1789
  callDynamicTool: queryOpts.callDynamicTool || null,
1750
1790
  abortController: ac,
1751
1791
  canUseTool: queryOpts.canUseTool || null,
1752
- onElicitation: queryOpts.onElicitation || null,
1792
+ onElicitation: queryOpts.userInputMode === "fallback" ? null : (queryOpts.onElicitation || null),
1793
+ onUserInputRequest: queryOpts.userInputMode === "fallback" ? null : (queryOpts.onUserInputRequest || null),
1753
1794
  resumeSessionId: queryOpts.resumeSessionId || null,
1754
1795
  registerBackgroundTaskReset: registerBackgroundTaskReset,
1796
+ ephemeral: queryOpts.ephemeral === true,
1755
1797
  };
1756
1798
 
1757
1799
  // Reasoning effort
@@ -1806,6 +1848,14 @@ function createCodexAdapter(opts) {
1806
1848
  return handle;
1807
1849
  },
1808
1850
 
1851
+ createOneShotQuery: async function(queryOpts) {
1852
+ var handle = await adapter.createQuery(Object.assign({}, queryOpts || {}, {
1853
+ ephemeral: true,
1854
+ resumeSessionId: null,
1855
+ }));
1856
+ return { handle: handle, backendPersistence: "ephemeral" };
1857
+ },
1858
+
1809
1859
  // --- Title generation ---
1810
1860
  generateTitle: async function(messages, opts) {
1811
1861
  var requestedLinuxUser = opts && opts.linuxUser;
@@ -1823,6 +1873,7 @@ function createCodexAdapter(opts) {
1823
1873
  var ac = new AbortController();
1824
1874
  var handle = await adapter.createQuery({
1825
1875
  cwd: (opts && opts.cwd) || _cwd,
1876
+ env: opts && opts.env,
1826
1877
  systemPrompt: systemPrompt,
1827
1878
  model: "gpt-5.4-mini",
1828
1879
  abortController: ac,
@@ -1892,6 +1943,23 @@ function createCodexAdapter(opts) {
1892
1943
  ]).then(function() { return true; });
1893
1944
  },
1894
1945
 
1946
+ // A device login writes credentials for one OS identity. Refresh only
1947
+ // that identity in OS-isolated deployments; single-user projects restart
1948
+ // their sole app-server.
1949
+ refreshAuthIdentity: function(linuxUser) {
1950
+ if (!_runtimeLinuxUser && linuxUser) {
1951
+ var userRuntime = _userRuntimes[linuxUser];
1952
+ if (!userRuntime) return Promise.resolve(false);
1953
+ return userRuntime.shutdown().then(function() {
1954
+ delete _userRuntimes[linuxUser];
1955
+ return true;
1956
+ });
1957
+ }
1958
+ if (_runtimeLinuxUser && linuxUser !== _runtimeLinuxUser) return Promise.resolve(false);
1959
+ if (_requiresLinuxUser) return Promise.resolve(false);
1960
+ return adapter.shutdown();
1961
+ },
1962
+
1895
1963
  shutdownIfIdle: function(idleMs) {
1896
1964
  if (!_runtimeLinuxUser && Object.keys(_userRuntimes).length > 0) {
1897
1965
  return shutdownUserRuntimes("shutdownIfIdle", idleMs).then(function(results) {
@@ -1065,7 +1065,7 @@ function createKiroAdapter(opts) {
1065
1065
  var model = queryOpts.model || _defaultModel || "auto";
1066
1066
  var ac = queryOpts.abortController || new AbortController();
1067
1067
  var kiroOpts = (queryOpts.adapterOptions && queryOpts.adapterOptions.KIRO) || {};
1068
- var sharedSkills = skillDiscovery.discoverSkills(queryOpts.cwd || _cwd);
1068
+ var sharedSkills = queryOpts.skipSkills ? [] : skillDiscovery.discoverSkills(queryOpts.cwd || _cwd);
1069
1069
  var skillIndex = skillDiscovery.buildSkillIndex(sharedSkills);
1070
1070
 
1071
1071
  var activeEntry = { abort: function() { try { ac.abort(); } catch (e) {} } };
@@ -1123,6 +1123,7 @@ function createKiroAdapter(opts) {
1123
1123
  var ac = new AbortController();
1124
1124
  var handle = await adapter.createQuery({
1125
1125
  cwd: (opts && opts.cwd) || _cwd,
1126
+ env: opts && opts.env,
1126
1127
  systemPrompt: systemPrompt,
1127
1128
  model: "auto",
1128
1129
  abortController: ac,
@@ -0,0 +1,38 @@
1
+ // Claude SDK mappings for YOKE structured user input.
2
+ var userInput = require("./user-input");
3
+
4
+ function ask(handler, input, opts) {
5
+ opts = opts || {};
6
+ return userInput.dispatchUserInput(handler, input, {
7
+ requestId: opts.toolUseID || opts.requestId,
8
+ signal: opts.signal,
9
+ source: "claude_ask_user_question",
10
+ native: true,
11
+ provider: "claude",
12
+ }).then(function (result) { return userInput.claudePermissionResult(input, result); });
13
+ }
14
+
15
+ function elicitation(handler, request, opts) {
16
+ opts = opts || {};
17
+ return userInput.dispatchUserInput(handler, userInput.questionsFromElicitation(request), {
18
+ requestId: opts.requestId || request.elicitationId,
19
+ signal: opts.signal,
20
+ source: "claude_elicitation",
21
+ native: true,
22
+ provider: "claude",
23
+ presentation: "elicitation",
24
+ diagnostics: { elicitation: request },
25
+ }).then(userInput.elicitationResponse);
26
+ }
27
+
28
+ function canUseTool(queryOpts) {
29
+ var original = queryOpts.canUseTool;
30
+ return function (toolName, input, opts) {
31
+ if (toolName === "AskUserQuestion" && typeof queryOpts.onUserInputRequest === "function") return ask(queryOpts.onUserInputRequest, input, opts);
32
+ if (toolName === "AskUserQuestion") return Promise.resolve({ behavior: "deny", message: "Use the query's structured input fallback tool." });
33
+ if (original) return original(toolName, input, opts);
34
+ return Promise.resolve({ behavior: "deny", message: "No tool permission handler is available." });
35
+ };
36
+ }
37
+
38
+ module.exports = { ask: ask, elicitation: elicitation, canUseTool: canUseTool };
@@ -13,7 +13,13 @@ var { buildUserEnv } = require("../build-user-env");
13
13
  var { wrapSpawnAsUser } = require("../os-users");
14
14
 
15
15
  // --- Find the codex binary path ---
16
- // Mirrors the logic from @openai/codex-sdk findCodexPath()
16
+ // Mirrors the logic from @openai/codex-sdk findCodexPath().
17
+ //
18
+ // The bundled @openai/codex platform package is the default. Set the
19
+ // CLAY_CODEX_PATH env var to an existing executable to run a different codex
20
+ // build (local dev build, system install, pinned older release); the chosen
21
+ // binary is logged on every resolution. An unset or non-existent
22
+ // CLAY_CODEX_PATH silently falls back to the bundled resolution.
17
23
 
18
24
  var PLATFORM_PACKAGE_BY_TARGET = {
19
25
  "aarch64-apple-darwin": "@openai/codex-darwin-arm64",
@@ -39,6 +45,15 @@ function getTargetTriple() {
39
45
  }
40
46
 
41
47
  function findCodexPath() {
48
+ var override = process.env.CLAY_CODEX_PATH;
49
+ if (override) {
50
+ if (fs.existsSync(override)) {
51
+ console.log("[codex-app-server] Using CLAY_CODEX_PATH binary:", override);
52
+ return override;
53
+ }
54
+ console.warn("[codex-app-server] CLAY_CODEX_PATH does not exist, falling back to bundled codex:", override);
55
+ }
56
+
42
57
  var triple = getTargetTriple();
43
58
  if (!triple) throw new Error("Unsupported platform: " + process.platform + "/" + process.arch);
44
59
 
@@ -59,7 +74,10 @@ function findCodexPath() {
59
74
  path.join(vendorRoot, triple, "codex", binaryName),
60
75
  ];
61
76
  for (var i = 0; i < candidates.length; i++) {
62
- if (fs.existsSync(candidates[i])) return candidates[i];
77
+ if (fs.existsSync(candidates[i])) {
78
+ console.log("[codex-app-server] Using bundled codex binary:", candidates[i]);
79
+ return candidates[i];
80
+ }
63
81
  }
64
82
  throw new Error("codex binary not found in any known layout under " + path.join(vendorRoot, triple));
65
83
  } catch (e) {
@@ -19,11 +19,17 @@ function mapTerminals(result) {
19
19
  var terminal = terminals[i] || {};
20
20
  var taskId = terminal.id || terminal.terminalId || terminal.taskId || "";
21
21
  if (!taskId || !isLiveTerminal(terminal)) continue;
22
- tasks.push({
22
+ var task = {
23
23
  task_id: String(taskId),
24
24
  task_type: "shell",
25
25
  description: terminal.commandLine || terminal.command || terminal.name || "",
26
- });
26
+ };
27
+ // Forward the terminal's own start time when the app-server reports one,
28
+ // so elapsed time survives a Clay restart. Falls back to first-seen
29
+ // stamping in background-task-timing.js when absent.
30
+ var startedAt = terminal.startedAt || terminal.started_at || terminal.createdAt || terminal.created_at;
31
+ if (startedAt) task.started_at = startedAt;
32
+ tasks.push(task);
27
33
  }
28
34
  return tasks;
29
35
  }
@@ -0,0 +1,64 @@
1
+ // Codex app-server 0.147 structured user-input protocol mapping.
2
+ var userInput = require("./user-input");
3
+
4
+ function nativeInput(params) {
5
+ return {
6
+ questions: (params.questions || []).map(function (question) {
7
+ return {
8
+ id: question.id,
9
+ header: question.header || "",
10
+ question: question.question,
11
+ allowOther: question.isOther !== false,
12
+ secret: question.isSecret === true,
13
+ options: Array.isArray(question.options) ? question.options : [],
14
+ };
15
+ }),
16
+ };
17
+ }
18
+
19
+ function handleNative(appServer, msg, handler, expectedThreadId, signal) {
20
+ var params = msg.params || {};
21
+ if (!params.threadId || (expectedThreadId && params.threadId !== expectedThreadId)) {
22
+ appServer.respondError(msg.id, -32001, "Structured input request does not belong to this query thread.");
23
+ return;
24
+ }
25
+ userInput.dispatchUserInput(handler, nativeInput(params), {
26
+ requestId: params.itemId || String(msg.id),
27
+ signal: signal,
28
+ source: "codex_request_user_input",
29
+ native: true,
30
+ provider: "codex",
31
+ diagnostics: { threadId: params.threadId, turnId: params.turnId || null, itemId: params.itemId || null },
32
+ }).then(function (result) {
33
+ appServer.respond(msg.id, userInput.codexResponse(result));
34
+ }).catch(function (error) {
35
+ appServer.respondError(msg.id, -32602, error.message);
36
+ });
37
+ }
38
+
39
+ function handleElicitation(appServer, msg, handler, signal) {
40
+ var params = msg.params || {};
41
+ var request = {
42
+ serverName: params.serverName || (params._meta && params._meta.tool) || "Tool",
43
+ message: params.message || params.prompt || "",
44
+ url: params.url || null,
45
+ elicitationId: params.elicitationId || null,
46
+ requestedSchema: params.requestedSchema || null,
47
+ };
48
+ var input = Array.isArray(params.questions) ? nativeInput(params) : userInput.questionsFromElicitation(request);
49
+ userInput.dispatchUserInput(handler, input, {
50
+ requestId: request.elicitationId || String(msg.id),
51
+ signal: signal,
52
+ source: "codex_mcp_elicitation",
53
+ native: true,
54
+ provider: "codex",
55
+ presentation: "elicitation",
56
+ diagnostics: { threadId: params.threadId || null, elicitation: request },
57
+ }).then(function (result) {
58
+ appServer.respond(msg.id, userInput.elicitationResponse(result));
59
+ }).catch(function (error) {
60
+ appServer.respondError(msg.id, -32602, error.message);
61
+ });
62
+ }
63
+
64
+ module.exports = { nativeInput: nativeInput, handleNative: handleNative, handleElicitation: handleElicitation };
@@ -0,0 +1,164 @@
1
+ // Canonical single-turn completion lifecycle for YOKE adapters.
2
+
3
+ function assistantMessageText(event) {
4
+ if (!event || event.yokeType !== "message" || event.messageRole !== "assistant") return "";
5
+ if (!Array.isArray(event.content)) return "";
6
+ var text = "";
7
+ for (var i = 0; i < event.content.length; i++) {
8
+ if (event.content[i] && event.content[i].type === "text" && event.content[i].text) {
9
+ text += event.content[i].text;
10
+ }
11
+ }
12
+ return text;
13
+ }
14
+
15
+ function normalizedText(streamedText, finalText) {
16
+ if (!streamedText) return finalText;
17
+ if (!finalText) return streamedText;
18
+ if (finalText.indexOf(streamedText) === 0) return finalText;
19
+ if (streamedText.indexOf(finalText) === 0) return streamedText;
20
+ return streamedText;
21
+ }
22
+
23
+ function oneShotError(event) {
24
+ if (!event) return null;
25
+ if (event.yokeType === "error") return new Error(event.text || "One-shot completion failed.");
26
+ if (event.yokeType === "auth_required") {
27
+ return new Error("The " + (event.vendor || "selected") + " provider requires sign-in.");
28
+ }
29
+ return null;
30
+ }
31
+
32
+ async function createOneShot(adapter, queryOpts, allowUnknownPersistence) {
33
+ if (typeof adapter.createOneShotQuery === "function") {
34
+ var created = await adapter.createOneShotQuery(queryOpts);
35
+ if (!created || !created.handle) throw new Error("YOKE adapter returned an invalid one-shot query.");
36
+ if (created.backendPersistence !== "ephemeral") {
37
+ if (!allowUnknownPersistence) {
38
+ if (typeof created.handle.abort === "function") created.handle.abort();
39
+ if (typeof created.handle.close === "function") created.handle.close();
40
+ throw new Error("The " + adapter.vendor + " adapter did not confirm ephemeral one-shot execution.");
41
+ }
42
+ created.backendPersistence = "unknown";
43
+ }
44
+ return created;
45
+ }
46
+ if (!allowUnknownPersistence) {
47
+ throw new Error("The " + adapter.vendor + " adapter does not guarantee ephemeral one-shot execution.");
48
+ }
49
+ return {
50
+ handle: await adapter.createQuery(Object.assign({}, queryOpts, { persistSession: false })),
51
+ backendPersistence: "unknown",
52
+ };
53
+ }
54
+
55
+ async function completeOnce(adapter, opts) {
56
+ opts = opts || {};
57
+ if (!adapter || typeof adapter.createQuery !== "function") throw new Error("A YOKE adapter is required.");
58
+ if (typeof opts.prompt !== "string" || !opts.prompt.trim()) throw new Error("A one-shot prompt is required.");
59
+
60
+ var timeoutMs = opts.timeoutMs || 60000;
61
+ var controller = new AbortController();
62
+ var handle = null;
63
+ var timedOut = false;
64
+ var externallyAborted = false;
65
+ var timer = null;
66
+ var externalSignal = opts.signal || null;
67
+ var abortHandle = function() {
68
+ controller.abort();
69
+ if (handle && typeof handle.abort === "function") handle.abort();
70
+ };
71
+ var onExternalAbort = function() {
72
+ externallyAborted = true;
73
+ abortHandle();
74
+ };
75
+
76
+ if (externalSignal) {
77
+ if (externalSignal.aborted) onExternalAbort();
78
+ else externalSignal.addEventListener("abort", onExternalAbort, { once: true });
79
+ }
80
+ timer = setTimeout(function() {
81
+ timedOut = true;
82
+ abortHandle();
83
+ }, timeoutMs);
84
+
85
+ var streamedText = "";
86
+ var finalText = "";
87
+ var resultEvent = null;
88
+ var interactiveRequested = false;
89
+ var created;
90
+ try {
91
+ var queryOpts = Object.assign({}, opts.query || {}, {
92
+ cwd: opts.cwd,
93
+ model: opts.model,
94
+ systemPrompt: opts.systemPrompt,
95
+ abortController: controller,
96
+ onUserInputRequest: function (request, respond) {
97
+ interactiveRequested = true;
98
+ respond.cancel("One-shot completions cannot request interactive user input.");
99
+ },
100
+ });
101
+ var creationFinished = false;
102
+ var rejectCreation;
103
+ var creationAbort = new Promise(function(resolve, reject) { rejectCreation = reject; });
104
+ var onCreationAbort = function() { rejectCreation(new Error("One-shot completion aborted.")); };
105
+ controller.signal.addEventListener("abort", onCreationAbort, { once: true });
106
+ var creation = createOneShot(adapter, queryOpts, !!opts.allowUnknownBackendPersistence);
107
+ creation.then(function(lateCreated) {
108
+ if (!creationFinished && controller.signal.aborted && lateCreated && lateCreated.handle) {
109
+ if (typeof lateCreated.handle.abort === "function") lateCreated.handle.abort();
110
+ if (typeof lateCreated.handle.close === "function") lateCreated.handle.close();
111
+ }
112
+ }).catch(function() {});
113
+ try {
114
+ created = await Promise.race([creation, creationAbort]);
115
+ creationFinished = true;
116
+ } finally {
117
+ controller.signal.removeEventListener("abort", onCreationAbort);
118
+ }
119
+ handle = created.handle;
120
+ if (controller.signal.aborted) {
121
+ if (typeof handle.abort === "function") handle.abort();
122
+ throw new Error("One-shot completion aborted.");
123
+ }
124
+ if (handle.pushMessage(opts.prompt) !== true) {
125
+ throw new Error("YOKE one-shot query rejected its prompt.");
126
+ }
127
+ handle.endInput();
128
+
129
+ for await (var event of handle) {
130
+ var eventError = oneShotError(event);
131
+ if (eventError) throw eventError;
132
+ if (event.yokeType === "text_delta" && event.text) streamedText += event.text;
133
+ var messageText = assistantMessageText(event);
134
+ if (messageText) finalText = messageText;
135
+ if (event.yokeType === "result") {
136
+ resultEvent = event;
137
+ break;
138
+ }
139
+ }
140
+ if (timedOut) throw new Error("YOKE one-shot timed out after " + timeoutMs + " ms.");
141
+ if (externallyAborted) throw new Error("YOKE one-shot was aborted.");
142
+ if (interactiveRequested) throw new Error("YOKE one-shot completion requested interactive user input.");
143
+ return {
144
+ text: normalizedText(streamedText, finalText).trim(),
145
+ backendPersistence: created.backendPersistence || "unknown",
146
+ vendor: adapter.vendor,
147
+ model: opts.model || null,
148
+ result: resultEvent,
149
+ };
150
+ } catch (error) {
151
+ if (timedOut) throw new Error("YOKE one-shot timed out after " + timeoutMs + " ms.");
152
+ if (externallyAborted) throw new Error("YOKE one-shot was aborted.");
153
+ throw error;
154
+ } finally {
155
+ clearTimeout(timer);
156
+ if (externalSignal) externalSignal.removeEventListener("abort", onExternalAbort);
157
+ if (handle && typeof handle.close === "function") handle.close();
158
+ }
159
+ }
160
+
161
+ module.exports = {
162
+ completeOnce: completeOnce,
163
+ normalizedText: normalizedText,
164
+ };
package/lib/yoke/index.js CHANGED
@@ -4,6 +4,8 @@
4
4
  var iface = require("./interface");
5
5
  var instructions = require("./instructions");
6
6
  var vendorRegistry = require("./vendor-registry");
7
+ var completeOnce = require("./complete-once").completeOnce;
8
+ var userInput = require("./user-input");
7
9
  var createClaudeAdapter = require("./adapters/claude").createClaudeAdapter;
8
10
  var createCodexAdapter = require("./adapters/codex").createCodexAdapter;
9
11
  var createAntigravityAdapter = require("./adapters/antigravity").createAntigravityAdapter;
@@ -47,7 +49,7 @@ function wrapCreateQuery(adapter, defaultCwd) {
47
49
  adapter.createQuery = function(queryOpts) {
48
50
  queryOpts = queryOpts || {};
49
51
  var projectDir = (queryOpts && queryOpts.cwd) || defaultCwd;
50
- var merged = instructions.scanAndMerge(projectDir, adapter.vendor);
52
+ var merged = queryOpts.skipProjectInstructions ? "" : instructions.scanAndMerge(projectDir, adapter.vendor);
51
53
 
52
54
  if (merged) {
53
55
  var parts = [];
@@ -473,4 +475,6 @@ module.exports = {
473
475
  TOOL_POLICIES: iface.TOOL_POLICIES,
474
476
  validateAdapter: iface.validateAdapter,
475
477
  validateQueryHandle: iface.validateQueryHandle,
478
+ completeOnce: completeOnce,
479
+ userInput: userInput,
476
480
  };
@@ -40,6 +40,25 @@ var INITIALIZE_TIMEOUT_MS = 90000;
40
40
  * .createToolServer(def): ToolServer (opaque)
41
41
  * .createQuery(opts): QueryHandle
42
42
  *
43
+ * Optional one-shot capability:
44
+ * .createOneShotQuery(opts): Promise<{ handle, backendPersistence }>
45
+ * backendPersistence is "ephemeral" only when the adapter can verify the
46
+ * provider does not materialize the backend conversation. Adapters without
47
+ * this method may only use completeOnce's explicit unknown-persistence path.
48
+ *
49
+ * Optional structured user-input capability:
50
+ * .userInputCapability: { mode: "native"|"fallback", native: boolean, transport: string }
51
+ * createQuery({ userInputMode: "auto"|"native"|"fallback",
52
+ * onUserInputRequest(request, respond) })
53
+ * request.presentation is "questions" or "elicitation";
54
+ * request.questions contain 1-3 { id, header, question, multiSelect,
55
+ * allowOther, secret, options }; options contain zero or 2-6 safe
56
+ * { label, description } entries. respond(answerMap),
57
+ * respond.submitContent(typedMap), and respond.cancel(reason) settle
58
+ * exactly once. submitContent preserves typed MCP elicitation forms.
59
+ * Adapters with mode "native" translate their provider protocol directly.
60
+ * Other adapters use createFallbackServer() from yoke/user-input.
61
+ *
43
62
  * Lightweight utilities:
44
63
  * .generateTitle(messages, opts) : Promise<string> - generate a short session title
45
64
  * messages: string[] - user messages to derive the title from
@@ -102,12 +102,8 @@ function postJson(urlPath, body) {
102
102
  reject(err);
103
103
  });
104
104
 
105
- // 10 min safety ceiling. ask_user_questions is stateless (returns
106
- // immediately; the user's answer arrives on the next turn as a new
107
- // user message), so no tool call should legitimately block long.
108
- // The generous ceiling is just a belt-and-suspenders against slow
109
- // browser automation or external MCP servers, not the primary
110
- // mechanism for human-in-the-loop tools.
105
+ // 10 min safety ceiling. Structured user input may legitimately wait
106
+ // for a person, while other tools normally return much sooner.
111
107
  req.setTimeout(600000, function () {
112
108
  req.destroy(new Error("Request timed out"));
113
109
  });