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
package/lib/sdk-bridge.js CHANGED
@@ -10,6 +10,7 @@ var { splitShellSegments, attachSkillDiscovery } = require("./sdk-skill-discover
10
10
  var { isSafeBashSegment } = require("./safe-bash-commands");
11
11
  var { createMessageQueue } = require("./sdk-message-queue");
12
12
  var { attachMessageProcessor } = require("./sdk-message-processor");
13
+ var homeDebateToolPolicy = require("./home-debate-tool-policy");
13
14
  var yoke = require("./yoke");
14
15
 
15
16
  // Extract serializable tool descriptors from MCP server instances.
@@ -108,6 +109,7 @@ function createSDKBridge(opts) {
108
109
  var getNotificationsModule = opts.getNotificationsModule || function () { return null; };
109
110
  var adapter = opts.adapter;
110
111
  var adapters = opts.adapters || {};
112
+ var getRuntimeEnv = opts.getRuntimeEnv || function () { return process.env; };
111
113
  var mateDisplayName = opts.mateDisplayName || "";
112
114
  var isMate = opts.isMate || (slug.indexOf("mate-") === 0);
113
115
  var osUsers = !!opts.osUsers;
@@ -364,6 +366,7 @@ function createSDKBridge(opts) {
364
366
  sessionAdapter.generateTitle(userMessages, {
365
367
  cwd: cwd,
366
368
  linuxUser: getSessionLinuxUser(session) || undefined,
369
+ env: getRuntimeEnv(session),
367
370
  }).then(function(title) {
368
371
  if (!title || title.length < 2) return;
369
372
  title = title.substring(0, 100);
@@ -391,6 +394,7 @@ function createSDKBridge(opts) {
391
394
  mateDisplayName: mateDisplayName,
392
395
  pushModule: pushModule,
393
396
  getNotificationsModule: getNotificationsModule,
397
+ shouldSuppressResponseNotification: opts.shouldSuppressResponseNotification,
394
398
  adapter: adapter,
395
399
  saveImageFile: opts.saveImageFile,
396
400
  getSessionLinuxUser: getSessionLinuxUser,
@@ -573,6 +577,29 @@ function createSDKBridge(opts) {
573
577
  if (toolName.indexOf("mcp__clay-debate__") === 0) {
574
578
  return { behavior: "allow", updatedInput: input };
575
579
  }
580
+ if (toolName.indexOf("mcp__clay-mate-creation__") === 0) {
581
+ return { behavior: "allow", updatedInput: input };
582
+ }
583
+ // Structured user input only presents a question and waits for the user's
584
+ // answer. Yoke may expose the same query-bound capability as a native
585
+ // dynamic tool, its MCP fallback, or the legacy Clay MCP name.
586
+ if (toolName === "ask_user_questions"
587
+ || toolName === "mcp__yoke-user-input__ask_user_questions"
588
+ || toolName === "mcp__clay-ask-user__ask_user_questions") {
589
+ return { behavior: "allow", updatedInput: input };
590
+ }
591
+
592
+ var safeCapsuleTools = {
593
+ clay_tool_list: true,
594
+ clay_tool_snapshot: true,
595
+ clay_tool_act: true,
596
+ clay_tool_set: true,
597
+ clay_tool_source: true,
598
+ };
599
+ if (toolName.indexOf("mcp__clay-tools__") === 0
600
+ && safeCapsuleTools[toolName.substring(toolName.lastIndexOf("__") + 2)]) {
601
+ return { behavior: "allow", updatedInput: input };
602
+ }
576
603
 
577
604
  // Auto-approve split-pair partner tools. The pair itself is
578
605
  // user-constructed (the user created the split and its roles and
@@ -622,10 +649,20 @@ function createSDKBridge(opts) {
622
649
  }
623
650
  }
624
651
 
625
- // Auto-approve clay-history tools. Mounted only on Clay (host agent)
626
- // sessions and strictly read-only by design — search and read across
627
- // the user's own workspace data.
628
- if (toolName.indexOf("mcp__clay-history__") === 0) {
652
+ // Workspace inspection and proposal posting are session-bound. Keep this
653
+ // list exact so a future execution/mutation tool cannot inherit approval.
654
+ var safeWorkspaceTools = {
655
+ "mcp__clay-workspace__list_projects": true,
656
+ "mcp__clay-workspace__list_project_sessions": true,
657
+ "mcp__clay-workspace__search_project_history": true,
658
+ "mcp__clay-workspace__read_project_session": true,
659
+ "mcp__clay-workspace__search_workspace_history": true,
660
+ "mcp__clay-workspace__list_workspace_activity": true,
661
+ "mcp__clay-workspace__propose_project_assignment": true,
662
+ "mcp__clay-workspace__propose_project_follow_up": true,
663
+ "mcp__clay-workspace__get_assignment_status": true,
664
+ };
665
+ if (safeWorkspaceTools[toolName] || toolName.indexOf("mcp__clay-history__") === 0) {
629
666
  return { behavior: "allow", updatedInput: input };
630
667
  }
631
668
 
@@ -668,6 +705,18 @@ function createSDKBridge(opts) {
668
705
  }
669
706
 
670
707
  function handleCanUseTool(session, toolName, input, opts) {
708
+ var debateDecision = homeDebateToolPolicy.initialToolDecision(session, toolName, input);
709
+ if (debateDecision) return Promise.resolve(debateDecision);
710
+ // The dynamic proposal tool is bound to this exact query session. It only
711
+ // posts the inline proposal; starting the debate still requires the user.
712
+ if (session.homeDebatePlanning === true && toolName === "propose_debate") {
713
+ return Promise.resolve({ behavior: "allow", updatedInput: input });
714
+ }
715
+ // Mate creation proposals only present the exact-session approval card;
716
+ // creating the Mate still requires the user's explicit action there.
717
+ if (session.mateCreationMode === true && toolName === "propose_mate") {
718
+ return Promise.resolve({ behavior: "allow", updatedInput: input });
719
+ }
671
720
  // Full-auto mode: auto-approve everything except AskUserQuestion
672
721
  // (which still needs to go through the user interaction flow).
673
722
  var clayPermissionMode = session.permissionMode || sm.currentPermissionMode;
@@ -693,23 +742,6 @@ function createSDKBridge(opts) {
693
742
  return Promise.resolve(whitelisted);
694
743
  }
695
744
 
696
- // AskUserQuestion: wait for user answers via WebSocket
697
- if (toolName === "AskUserQuestion") {
698
- return new Promise(function(resolve) {
699
- session.pendingAskUser[opts.toolUseID] = {
700
- resolve: resolve,
701
- input: input,
702
- };
703
- if (opts.signal) {
704
- opts.signal.addEventListener("abort", function() {
705
- delete session.pendingAskUser[opts.toolUseID];
706
- sendAndRecord(session, { type: "ask_user_answered", toolId: opts.toolUseID });
707
- resolve({ behavior: "deny", message: "Cancelled" });
708
- });
709
- }
710
- });
711
- }
712
-
713
745
  // Auto-approve if tool was previously allowed for session
714
746
  if (session.allowedTools && session.allowedTools[toolName]) {
715
747
  return Promise.resolve({ behavior: "allow", updatedInput: input });
@@ -1032,19 +1064,15 @@ function createSDKBridge(opts) {
1032
1064
  if (ownsRuntime) {
1033
1065
  session.taskStopRequested = false;
1034
1066
  session.pendingPermissions = {};
1035
- // Preserve MCP-mode AskUserQuestion entries across turn boundaries.
1036
- // The MCP path is intentionally stateless: the tool returns immediately
1037
- // ("card posted, end your turn") and the user's answer is expected to
1038
- // arrive as a brand-new user_message on the *next* turn. That means the
1039
- // pending entry MUST survive this finally block. Without it, the
1040
- // ask_user_response handler can't find the toolId and silently drops
1041
- // the answer. canUseTool-mode entries (Claude's native path) still hold
1042
- // an open SDK permission callback that dies with the query, so those
1043
- // are correctly cleared here.
1067
+ // Preserve only restored legacy MCP entries. Canonical Yoke requests
1068
+ // hold a live provider callback and must be cancelled with their query.
1044
1069
  var keepAskUser = {};
1045
1070
  for (var _tid in session.pendingAskUser) {
1046
1071
  var _pending = session.pendingAskUser[_tid];
1047
1072
  if (_pending && _pending.mode === "mcp") keepAskUser[_tid] = _pending;
1073
+ else if (_pending && _pending.mode === "yoke" && _pending.respond && typeof _pending.respond.cancel === "function") {
1074
+ _pending.respond.cancel("Query closed");
1075
+ }
1048
1076
  }
1049
1077
  session.pendingAskUser = keepAskUser;
1050
1078
  session.pendingElicitations = {};
@@ -1093,6 +1121,7 @@ function createSDKBridge(opts) {
1093
1121
  handle = await adapter.createQuery({
1094
1122
  cwd: cwd,
1095
1123
  resumeSessionId: session.cliSessionId,
1124
+ env: getRuntimeEnv(session),
1096
1125
  adapterOptions: {
1097
1126
  CLAUDE: {
1098
1127
  settingSources: ["user", "project", "local"],
@@ -1209,6 +1238,7 @@ function createSDKBridge(opts) {
1209
1238
  }
1210
1239
 
1211
1240
  async function startQueryInner(session, text, images, linuxUser) {
1241
+ var sessionRuntimeEnv = getRuntimeEnv(session);
1212
1242
  async function ensureVendorReady(vendor) {
1213
1243
  if (!vendor) return null;
1214
1244
  if (linuxUser && !supportsOsUserIsolation(vendor)) return null;
@@ -1222,6 +1252,7 @@ function createSDKBridge(opts) {
1222
1252
  clayTls: clayTls,
1223
1253
  clayAuthToken: clayAuthToken,
1224
1254
  slug: slug,
1255
+ env: sessionRuntimeEnv,
1225
1256
  });
1226
1257
  }
1227
1258
  var needsReadyMetadata = !sm.capabilitiesByVendor || !sm.capabilitiesByVendor[vendor]
@@ -1235,6 +1266,7 @@ function createSDKBridge(opts) {
1235
1266
  clayTls: clayTls,
1236
1267
  clayAuthToken: clayAuthToken,
1237
1268
  slug: slug,
1269
+ env: sessionRuntimeEnv,
1238
1270
  });
1239
1271
  rememberAdapterReady(vendor, readyResult);
1240
1272
  }
@@ -1477,8 +1509,37 @@ function createSDKBridge(opts) {
1477
1509
 
1478
1510
  var codexConfig = getCodexConfig(sm);
1479
1511
  var kiroConfig = getKiroConfig(sm);
1480
- var mergedMcpServers = mergeMcpServers(getMcpServers(session), getRemoteMcpServers) || undefined;
1512
+ var requestedUserInputMode = session.debateSetupMode || session.mateCreationMode ? "fallback" : "auto";
1513
+ var selectedUserInputMode = yoke.userInput.selectMode(sessionAdapter, requestedUserInputMode);
1514
+ var sessionUserInputHandler = function(request, respond) {
1515
+ if (request.presentation === "elicitation" && request.diagnostics && request.diagnostics.elicitation) {
1516
+ var elicitationController = new AbortController();
1517
+ if (respond && typeof respond.onSettle === "function") {
1518
+ respond.onSettle(function (result) {
1519
+ if (result.status !== "submitted") elicitationController.abort();
1520
+ });
1521
+ }
1522
+ return handleElicitation(session, request.diagnostics.elicitation, { signal: elicitationController.signal }).then(function(result) {
1523
+ if (result && result.action === "accept") return respond.submitContent(result.content || {});
1524
+ return respond.cancel("Elicitation rejected");
1525
+ });
1526
+ }
1527
+ if (typeof opts.onUserInputRequest !== "function") return respond.cancel("Structured user input is unavailable.");
1528
+ return opts.onUserInputRequest(session, request, respond);
1529
+ };
1530
+ var localMcpServers = getMcpServers(session);
1531
+ if (selectedUserInputMode === "fallback") {
1532
+ var userInputServer = yoke.userInput.createFallbackServer(sessionAdapter, sessionUserInputHandler, { force: true });
1533
+ if (userInputServer) {
1534
+ localMcpServers = Object.assign({}, localMcpServers || {});
1535
+ localMcpServers[userInputServer.name || "yoke-user-input"] = userInputServer;
1536
+ }
1537
+ }
1538
+ var mergedMcpServers = mergeMcpServers(localMcpServers, getRemoteMcpServers) || undefined;
1481
1539
  var sessionToolDefs = getSessionToolDefs(session) || [];
1540
+ if (selectedUserInputMode === "fallback") {
1541
+ sessionToolDefs = sessionToolDefs.concat(yoke.userInput.fallbackToolDefs(sessionUserInputHandler));
1542
+ }
1482
1543
 
1483
1544
  // Derive an explicit session title for fresh queries so the SDK records
1484
1545
  // it at session creation and skips its own auto-generation. This also
@@ -1503,6 +1564,7 @@ function createSDKBridge(opts) {
1503
1564
  var queryOpts = {
1504
1565
  cwd: cwd,
1505
1566
  linuxUser: linuxUser || undefined,
1567
+ env: sessionRuntimeEnv,
1506
1568
  // appendSystemPrompt, NOT systemPrompt: a plain-string systemPrompt
1507
1569
  // REPLACES the entire Claude Code system prompt (sdk.d.ts:2096), which
1508
1570
  // would strip the driver session down to the two-sentence pair preset.
@@ -1535,8 +1597,19 @@ function createSDKBridge(opts) {
1535
1597
  resumeSessionId: session.cliSessionId || undefined,
1536
1598
  abortController: linuxUser ? undefined : session.abortController,
1537
1599
  canUseTool: function(toolName, input, toolOpts) {
1538
- return handleCanUseTool(session, toolName, input, toolOpts);
1600
+ var permissionName = toolName;
1601
+ for (var pti = 0; pti < sessionToolDefs.length; pti++) {
1602
+ if (sessionToolDefs[pti].name === toolName && sessionToolDefs[pti].permissionName) {
1603
+ permissionName = sessionToolDefs[pti].permissionName;
1604
+ break;
1605
+ }
1606
+ }
1607
+ return handleCanUseTool(session, permissionName, input, toolOpts);
1539
1608
  },
1609
+ onUserInputRequest: function(request, respond) {
1610
+ return sessionUserInputHandler(request, respond);
1611
+ },
1612
+ userInputMode: selectedUserInputMode,
1540
1613
  onElicitation: function(request, elicitOpts) {
1541
1614
  return handleElicitation(session, request, elicitOpts);
1542
1615
  },
@@ -1757,6 +1830,19 @@ function createSDKBridge(opts) {
1757
1830
  return true;
1758
1831
  }
1759
1832
 
1833
+ function refreshEnvironmentRuntime() {
1834
+ if (sm && sm.sessions) {
1835
+ sm.sessions.forEach(function (session) { refreshSessionRuntime(session); });
1836
+ }
1837
+ var vendorNames = Object.keys(adapters);
1838
+ for (var i = 0; i < vendorNames.length; i++) {
1839
+ var runtime = adapters[vendorNames[i]];
1840
+ if (!runtime || typeof runtime.shutdownIfIdle !== "function") continue;
1841
+ Promise.resolve(runtime.shutdownIfIdle(0)).catch(function () {});
1842
+ }
1843
+ return true;
1844
+ }
1845
+
1760
1846
  function permissionPushTitle(toolName, input) {
1761
1847
  if (!input) return "Claude wants to use " + toolName;
1762
1848
  var file = input.file_path ? input.file_path.split(/[/\\]/).pop() : "";
@@ -2078,6 +2164,7 @@ function createSDKBridge(opts) {
2078
2164
  handle = await mentionAdapter.createQuery({
2079
2165
  cwd: cwd,
2080
2166
  linuxUser: opts.linuxUser || undefined,
2167
+ env: getRuntimeEnv(opts.session || { linuxUser: opts.linuxUser || null }),
2081
2168
  systemPrompt: opts.claudeMd,
2082
2169
  model: opts.model || undefined,
2083
2170
  toolServers: opts.includeMcpServers ? (mergeMcpServers(getMcpServers(), getRemoteMcpServers) || undefined) : undefined,
@@ -2233,6 +2320,7 @@ function createSDKBridge(opts) {
2233
2320
  startQuery: startQuery,
2234
2321
  pushMessage: pushMessage,
2235
2322
  refreshSessionRuntime: refreshSessionRuntime,
2323
+ refreshEnvironmentRuntime: refreshEnvironmentRuntime,
2236
2324
  setModel: setModel,
2237
2325
  setEffort: setEffort,
2238
2326
  setPermissionMode: setPermissionMode,
@@ -1,5 +1,6 @@
1
1
  var usersModule = require("./users");
2
2
  var yoke = require("./yoke");
3
+ var backgroundTaskTiming = require("./background-task-timing");
3
4
 
4
5
  function attachMessageProcessor(ctx) {
5
6
  var sm = ctx.sm;
@@ -9,6 +10,7 @@ function attachMessageProcessor(ctx) {
9
10
  var mateDisplayName = ctx.mateDisplayName;
10
11
  var pushModule = ctx.pushModule;
11
12
  var getNotificationsModule = ctx.getNotificationsModule || function () { return null; };
13
+ var shouldSuppressResponseNotification = ctx.shouldSuppressResponseNotification || function () { return false; };
12
14
  var getSDK = ctx.getSDK;
13
15
  var adapter = ctx.adapter;
14
16
  var cwd = ctx.cwd;
@@ -59,6 +61,8 @@ function attachMessageProcessor(ctx) {
59
61
  if (session._lastAuthEmit && (now - session._lastAuthEmit) < 5000) return;
60
62
  session._lastAuthEmit = now;
61
63
 
64
+ var vendor = session.vendor || "claude";
65
+
62
66
  var authUser = session.ownerId ? usersModule.findUserById(session.ownerId) : null;
63
67
  var authLinuxUser = authUser && authUser.linuxUser ? authUser.linuxUser : null;
64
68
  var canAutoLogin = !usersModule.isMultiUser()
@@ -68,10 +72,13 @@ function attachMessageProcessor(ctx) {
68
72
  var authTitle = ((vendorInfo && vendorInfo.displayName) || "Claude Code") + " is not logged in.";
69
73
  var loginCommand = (vendorInfo && vendorInfo.loginCommand) || "claude login";
70
74
  var _nmLogin = getNotificationsModule();
71
- sendAndRecord(session, {
75
+ // Authentication recovery is transient UI state, not transcript content.
76
+ // Recording it makes history replay look like a fresh auth failure and
77
+ // re-opens the login terminal after a successful sign-in.
78
+ sendToSession(session, {
72
79
  type: "auth_required",
73
80
  text: authTitle,
74
- vendor: session.vendor || "claude",
81
+ vendor: vendor,
75
82
  loginCommand: loginCommand,
76
83
  linuxUser: authLinuxUser,
77
84
  canAutoLogin: canAutoLogin,
@@ -83,7 +90,7 @@ function attachMessageProcessor(ctx) {
83
90
  slug: slug,
84
91
  sessionId: session.localId,
85
92
  ownerId: session.ownerId || null,
86
- vendor: session.vendor || "claude",
93
+ vendor: vendor,
87
94
  loginCommand: loginCommand,
88
95
  linuxUser: authLinuxUser,
89
96
  canAutoLogin: canAutoLogin,
@@ -191,7 +198,9 @@ function attachMessageProcessor(ctx) {
191
198
 
192
199
  // Cache slash_commands and model from CLI init message
193
200
  if (parsed.yokeType === "init") {
194
- var previousBackgroundTaskCount = (session.activeBackgroundTasks || []).length;
201
+ // Ambient housekeeping never drove the sidebar badge, so a reset that
202
+ // only clears ambient watchers must not rebroadcast the session list.
203
+ var previousBackgroundTaskCount = backgroundTaskTiming.countUserTasks(session.activeBackgroundTasks);
195
204
  session.activeBackgroundTasks = [];
196
205
  sendToSession(session, { type: "active_background_tasks", tasks: [] });
197
206
  if (previousBackgroundTaskCount !== 0) sm.broadcastSessionList();
@@ -484,10 +493,8 @@ function attachMessageProcessor(ctx) {
484
493
  session.pendingPermissions = {};
485
494
  session.pendingElicitations = {};
486
495
  // Record ask_user_answered for any leftover pending questions so replay pairs correctly.
487
- // EXCEPTION: "mcp" mode entries are stateless — the tool returned immediately and the
488
- // turn is expected to end while the card is still awaiting the user's answer. Those
489
- // entries must survive across turns so the eventual ask_user_response can inject the
490
- // answer as the next user message. Only blocking modes (Claude canUseTool) get closed.
496
+ // Restored legacy MCP entries survive across turns. Canonical Yoke
497
+ // requests own a live provider callback and are cancelled on turn end.
491
498
  var leftoverAskIds = Object.keys(session.pendingAskUser);
492
499
  var keptAskUser = {};
493
500
  for (var lai = 0; lai < leftoverAskIds.length; lai++) {
@@ -497,6 +504,9 @@ function attachMessageProcessor(ctx) {
497
504
  keptAskUser[lid] = lentry;
498
505
  continue;
499
506
  }
507
+ if (lentry && lentry.mode === "yoke" && lentry.respond && typeof lentry.respond.cancel === "function") {
508
+ lentry.respond.cancel("Turn ended");
509
+ }
500
510
  sendAndRecord(session, { type: "ask_user_answered", toolId: lid });
501
511
  }
502
512
  session.pendingAskUser = keptAskUser;
@@ -582,8 +592,10 @@ function attachMessageProcessor(ctx) {
582
592
  var _donePreviewText = (session.responsePreview || "").replace(/\s+/g, " ").trim();
583
593
  if (_donePreviewText.length > 140) _donePreviewText = _donePreviewText.substring(0, 140) + "...";
584
594
  var _doneTitle = mateDisplayName ? (mateDisplayName + " responded") : (session.title || "Claude");
595
+ var _suppressResponseNotification = false;
596
+ try { _suppressResponseNotification = shouldSuppressResponseNotification(session) === true; } catch (e) {}
585
597
 
586
- if (pushModule && !session._delegatedBy) {
598
+ if (pushModule && !session._delegatedBy && !_suppressResponseNotification) {
587
599
  pushModule.sendPush({
588
600
  type: "done",
589
601
  slug: slug,
@@ -594,7 +606,7 @@ function attachMessageProcessor(ctx) {
594
606
  }
595
607
 
596
608
  var _nm = getNotificationsModule();
597
- if (_nm && !session.loop && !session._delegatedBy) {
609
+ if (_nm && !session.loop && !session._delegatedBy && !_suppressResponseNotification) {
598
610
  _nm.notify("response_done", {
599
611
  title: _doneTitle,
600
612
  preview: _donePreviewText,
@@ -682,11 +694,20 @@ function attachMessageProcessor(ctx) {
682
694
  }
683
695
 
684
696
  } else if (parsed.yokeType === "background_tasks_changed") {
685
- var previousBackgroundTaskCount = (session.activeBackgroundTasks || []).length;
686
- var activeBackgroundTasks = Array.isArray(parsed.tasks) ? parsed.tasks : [];
697
+ var previousBackgroundTasks = session.activeBackgroundTasks || [];
698
+ // Count user work only: an ambient watcher starting or stopping must not
699
+ // rebroadcast the session list and flicker every client's sidebar badge.
700
+ var previousBackgroundTaskCount = backgroundTaskTiming.countUserTasks(previousBackgroundTasks);
701
+ // Vendors re-send the whole list on every change, so carry each task's
702
+ // first-seen time forward; otherwise the composer's elapsed timer would
703
+ // restart whenever any other task starts or finishes.
704
+ var activeBackgroundTasks = backgroundTaskTiming.mergeStartTimes(
705
+ previousBackgroundTasks,
706
+ Array.isArray(parsed.tasks) ? parsed.tasks : []
707
+ );
687
708
  session.activeBackgroundTasks = activeBackgroundTasks;
688
709
  sendToSession(session, { type: "active_background_tasks", tasks: activeBackgroundTasks });
689
- if (previousBackgroundTaskCount !== activeBackgroundTasks.length) {
710
+ if (previousBackgroundTaskCount !== backgroundTaskTiming.countUserTasks(activeBackgroundTasks)) {
690
711
  sm.broadcastSessionList();
691
712
  }
692
713
 
@@ -0,0 +1,36 @@
1
+ // Validation and bounded Mate context for a Capsule creation conversation.
2
+
3
+ function normalizeCapsuleDescription(value) {
4
+ if (typeof value !== "string") return "";
5
+ return value.replace(/[\u0000-\u0008\u000b\u000c\u000e-\u001f\u007f]/g, " ").trim().slice(0, 4000);
6
+ }
7
+
8
+ function readCapsuleCreationIntent(value) {
9
+ if (!value || value.type !== "capsule_creation") return null;
10
+ var description = normalizeCapsuleDescription(value.description);
11
+ if (!description) {
12
+ var error = new Error("Describe the interface you need.");
13
+ error.code = "capsule_description_required";
14
+ throw error;
15
+ }
16
+ return { type: "capsule_creation", description: description };
17
+ }
18
+
19
+ function buildCapsuleCreationPrompt(description) {
20
+ return [
21
+ "Help the user turn the following request into a durable Capsule interface.",
22
+ "First inspect the need conversationally and help refine the behavior, state, and safe declarative UI.",
23
+ "Do not install or update a Capsule until the user has inspected the proposal and explicitly approved it through the existing permission flow.",
24
+ "The Capsule remains user-owned and available to all of the user's Mates.",
25
+ "",
26
+ "<capsule_request>",
27
+ description,
28
+ "</capsule_request>",
29
+ ].join("\n");
30
+ }
31
+
32
+ module.exports = {
33
+ normalizeCapsuleDescription: normalizeCapsuleDescription,
34
+ readCapsuleCreationIntent: readCapsuleCreationIntent,
35
+ buildCapsuleCreationPrompt: buildCapsuleCreationPrompt,
36
+ };