clay-server 3.8.1 → 4.0.0-beta.2

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 (189) hide show
  1. package/lib/ask-user-mcp-server.js +5 -119
  2. package/lib/builtin-mates.js +6 -6
  3. package/lib/claude-hook-installer.js +15 -0
  4. package/lib/clay-history-mcp-server.js +62 -297
  5. package/lib/daemon.js +1 -1
  6. package/lib/debate-model-selection.js +119 -0
  7. package/lib/home-debate-tool-policy.js +164 -0
  8. package/lib/home-response-notifications.js +26 -0
  9. package/lib/mate-creation-mcp-server.js +55 -0
  10. package/lib/mate-ready-creation.js +95 -0
  11. package/lib/mates.js +2 -0
  12. package/lib/notes.js +2 -1
  13. package/lib/project-ask-user.js +64 -0
  14. package/lib/project-capsule-catalog.js +101 -0
  15. package/lib/project-connection.js +4 -0
  16. package/lib/project-debate-proposal.js +133 -17
  17. package/lib/project-debate.js +214 -110
  18. package/lib/project-delegated-follow-up.js +84 -0
  19. package/lib/project-delegated-session.js +53 -0
  20. package/lib/project-filesystem.js +7 -9
  21. package/lib/project-knowledge.js +1 -0
  22. package/lib/project-mate-creation-proposal.js +120 -0
  23. package/lib/project-memory.js +14 -7
  24. package/lib/project-models.js +157 -32
  25. package/lib/project-session-notes.js +2 -2
  26. package/lib/project-session-pair.js +8 -8
  27. package/lib/project-sessions.js +66 -51
  28. package/lib/project-vendor-login.js +397 -0
  29. package/lib/project-worker-proposal.js +25 -25
  30. package/lib/project-workspace-query.js +144 -0
  31. package/lib/project.js +349 -102
  32. package/lib/public/app.js +85 -42
  33. package/lib/public/css/avatar-imprints.css +0 -1
  34. package/lib/public/css/base.css +4 -0
  35. package/lib/public/css/capsule-ui.css +389 -0
  36. package/lib/public/css/command-palette.css +306 -6
  37. package/lib/public/css/home-capsule-library.css +243 -0
  38. package/lib/public/css/home-capsule-source.css +192 -0
  39. package/lib/public/css/home-debate-live.css +146 -0
  40. package/lib/public/css/home-debate-planning.css +125 -0
  41. package/lib/public/css/home-debates-archive.css +281 -0
  42. package/lib/public/css/home-hub.css +896 -405
  43. package/lib/public/css/home-mate-creation.css +34 -0
  44. package/lib/public/css/home-mate-model-picker.css +131 -0
  45. package/lib/public/css/home-mate-settings.css +344 -0
  46. package/lib/public/css/home-session-actions.css +265 -0
  47. package/lib/public/css/home-sidebar.css +667 -0
  48. package/lib/public/css/icon-strip.css +1 -2
  49. package/lib/public/css/mates.css +7 -34
  50. package/lib/public/css/pane.css +1 -1
  51. package/lib/public/css/playbook.css +0 -80
  52. package/lib/public/css/sticky-notes.css +53 -117
  53. package/lib/public/css/tui-attention.css +0 -44
  54. package/lib/public/css/workspace-assignment.css +45 -0
  55. package/lib/public/fonts/source-serif-4/LICENSE.md +93 -0
  56. package/lib/public/fonts/source-serif-4/SourceSerif4Caption-Semibold.ttf.woff2 +0 -0
  57. package/lib/public/index.html +93 -74
  58. package/lib/public/modules/app-connection.js +10 -1
  59. package/lib/public/modules/app-dm.js +43 -160
  60. package/lib/public/modules/app-home-hub.js +328 -585
  61. package/lib/public/modules/app-message-cards.js +242 -0
  62. package/lib/public/modules/app-message-router.js +84 -0
  63. package/lib/public/modules/app-messages.js +102 -37
  64. package/lib/public/modules/app-notifications.js +8 -119
  65. package/lib/public/modules/app-projects.js +9 -1
  66. package/lib/public/modules/app-rendering.js +21 -602
  67. package/lib/public/modules/avatar-imprint.js +11 -8
  68. package/lib/public/modules/chat-bubble-renderer.js +152 -0
  69. package/lib/public/modules/chat-render-runtime.js +198 -0
  70. package/lib/public/modules/command-palette.js +225 -436
  71. package/lib/public/modules/dm-render.js +82 -0
  72. package/lib/public/modules/home-capsule-creation-intent.js +31 -0
  73. package/lib/public/modules/home-capsule-library.js +146 -0
  74. package/lib/public/modules/home-capsule-source.js +383 -0
  75. package/lib/public/modules/home-chat-identity.js +35 -0
  76. package/lib/public/modules/home-chat-scroll.js +77 -0
  77. package/lib/public/modules/home-chat-stream-state.js +34 -0
  78. package/lib/public/modules/home-composer-focus.js +25 -0
  79. package/lib/public/modules/home-conversations-sheet.js +179 -0
  80. package/lib/public/modules/home-debate-activity.js +48 -0
  81. package/lib/public/modules/home-debate-controls.js +295 -0
  82. package/lib/public/modules/home-debate-launch.js +41 -0
  83. package/lib/public/modules/home-debate-live.js +284 -0
  84. package/lib/public/modules/home-debate-models.js +73 -0
  85. package/lib/public/modules/home-debate-planning.js +335 -0
  86. package/lib/public/modules/home-debates-archive.js +279 -0
  87. package/lib/public/modules/home-dock-resize.js +74 -0
  88. package/lib/public/modules/home-dock.js +446 -0
  89. package/lib/public/modules/home-mate-chat.js +496 -0
  90. package/lib/public/modules/home-mate-creation.js +109 -0
  91. package/lib/public/modules/home-mate-model-picker.js +281 -0
  92. package/lib/public/modules/home-mate-selection.js +38 -0
  93. package/lib/public/modules/home-mate-settings-menu.js +131 -0
  94. package/lib/public/modules/home-mate-settings.js +383 -0
  95. package/lib/public/modules/home-session-actions.js +373 -0
  96. package/lib/public/modules/home-session-model-confirmation.js +13 -0
  97. package/lib/public/modules/home-shell.js +9 -0
  98. package/lib/public/modules/home-sidebar-chat-list.js +63 -0
  99. package/lib/public/modules/home-sidebar.js +158 -0
  100. package/lib/public/modules/home-sub-surface.js +19 -0
  101. package/lib/public/modules/home-surface-boot.js +68 -0
  102. package/lib/public/modules/home-surface.js +141 -0
  103. package/lib/public/modules/home-tools.js +271 -0
  104. package/lib/public/modules/markdown.js +50 -2
  105. package/lib/public/modules/mate-management.js +86 -0
  106. package/lib/public/modules/mate-wizard.js +0 -7
  107. package/lib/public/modules/mention.js +17 -19
  108. package/lib/public/modules/pane-bridge.js +9 -0
  109. package/lib/public/modules/profile.js +4 -5
  110. package/lib/public/modules/project-settings.js +34 -28
  111. package/lib/public/modules/project-switcher.js +11 -5
  112. package/lib/public/modules/project-workspace-assignment.js +23 -0
  113. package/lib/public/modules/search-clay-chat.js +402 -0
  114. package/lib/public/modules/server-settings.js +14 -12
  115. package/lib/public/modules/session-actions.js +1 -1
  116. package/lib/public/modules/sidebar-mates.js +56 -552
  117. package/lib/public/modules/sidebar-mobile.js +7 -64
  118. package/lib/public/modules/sidebar-presence.js +37 -0
  119. package/lib/public/modules/split-pair-ui.js +13 -13
  120. package/lib/public/modules/split-view.js +7 -0
  121. package/lib/public/modules/sticky-notes.js +7 -6
  122. package/lib/public/modules/tool-input-composition.js +48 -0
  123. package/lib/public/modules/tool-llm-status.js +167 -0
  124. package/lib/public/modules/tool-renderer-advanced.js +285 -0
  125. package/lib/public/modules/tool-renderer-chart.js +158 -0
  126. package/lib/public/modules/tool-renderer-semantics.js +35 -0
  127. package/lib/public/modules/tool-renderer.js +464 -0
  128. package/lib/public/modules/tool-runtime.js +309 -0
  129. package/lib/public/modules/tool-ui-evaluator.js +97 -0
  130. package/lib/public/modules/tool-ui-tree.js +21 -0
  131. package/lib/public/modules/tui-attention.js +10 -0
  132. package/lib/public/modules/user-settings.js +16 -23
  133. package/lib/public/modules/vendor-login.js +287 -0
  134. package/lib/public/modules/worker-proposal.js +2 -2
  135. package/lib/public/modules/workspace-assignment-card.js +149 -0
  136. package/lib/public/modules/workspace-assignment-routing.js +4 -0
  137. package/lib/public/style.css +13 -2
  138. package/lib/runtime-env.js +65 -0
  139. package/lib/sdk-bridge.js +120 -32
  140. package/lib/sdk-message-processor.js +10 -6
  141. package/lib/server-home-capsule-creation.js +36 -0
  142. package/lib/server-home-chat-events.js +287 -0
  143. package/lib/server-home-chat.js +518 -0
  144. package/lib/server-home-clay-entry.js +65 -0
  145. package/lib/server-home-clay-session-links.js +26 -0
  146. package/lib/server-home-debate-planning.js +214 -0
  147. package/lib/server-home-debates.js +137 -0
  148. package/lib/server-home-mate-creation.js +261 -0
  149. package/lib/server-home-models.js +241 -0
  150. package/lib/server-home-preferences.js +61 -0
  151. package/lib/server-mates.js +37 -0
  152. package/lib/server-tools.js +377 -0
  153. package/lib/server.js +111 -59
  154. package/lib/session-pair-mcp-server.js +6 -6
  155. package/lib/sessions.js +18 -3
  156. package/lib/tool-capsule-source.js +142 -0
  157. package/lib/tool-control-mcp-server.js +126 -0
  158. package/lib/tool-llm.js +48 -0
  159. package/lib/tool-storage.js +77 -0
  160. package/lib/tool-ui-spec-advanced.js +82 -0
  161. package/lib/tool-ui-spec.js +261 -0
  162. package/lib/tools-registry.js +286 -0
  163. package/lib/users-home-dock-preferences.js +77 -0
  164. package/lib/users-home-surface-preferences.js +99 -0
  165. package/lib/users-preferences.js +4 -7
  166. package/lib/users.js +12 -0
  167. package/lib/whats-new-content.js +3 -3
  168. package/lib/workspace-assignment-service.js +420 -0
  169. package/lib/workspace-query-mcp-server.js +154 -0
  170. package/lib/workspace-query-service.js +492 -0
  171. package/lib/ws-schema.js +93 -12
  172. package/lib/yoke/adapters/acp.js +2 -1
  173. package/lib/yoke/adapters/antigravity.js +2 -1
  174. package/lib/yoke/adapters/claude-worker.js +11 -4
  175. package/lib/yoke/adapters/claude.js +73 -43
  176. package/lib/yoke/adapters/codex.js +40 -10
  177. package/lib/yoke/adapters/kiro.js +2 -1
  178. package/lib/yoke/claude-user-input.js +38 -0
  179. package/lib/yoke/codex-app-server.js +20 -2
  180. package/lib/yoke/codex-user-input.js +64 -0
  181. package/lib/yoke/complete-once.js +164 -0
  182. package/lib/yoke/index.js +5 -1
  183. package/lib/yoke/interface.js +19 -0
  184. package/lib/yoke/mcp-bridge-server.js +2 -6
  185. package/lib/yoke/user-input.js +254 -0
  186. package/package.json +3 -2
  187. package/lib/public/css/home-chat.css +0 -380
  188. package/lib/public/modules/home-chat.js +0 -438
  189. 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,
@@ -9,6 +9,7 @@ function attachMessageProcessor(ctx) {
9
9
  var mateDisplayName = ctx.mateDisplayName;
10
10
  var pushModule = ctx.pushModule;
11
11
  var getNotificationsModule = ctx.getNotificationsModule || function () { return null; };
12
+ var shouldSuppressResponseNotification = ctx.shouldSuppressResponseNotification || function () { return false; };
12
13
  var getSDK = ctx.getSDK;
13
14
  var adapter = ctx.adapter;
14
15
  var cwd = ctx.cwd;
@@ -484,10 +485,8 @@ function attachMessageProcessor(ctx) {
484
485
  session.pendingPermissions = {};
485
486
  session.pendingElicitations = {};
486
487
  // 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.
488
+ // Restored legacy MCP entries survive across turns. Canonical Yoke
489
+ // requests own a live provider callback and are cancelled on turn end.
491
490
  var leftoverAskIds = Object.keys(session.pendingAskUser);
492
491
  var keptAskUser = {};
493
492
  for (var lai = 0; lai < leftoverAskIds.length; lai++) {
@@ -497,6 +496,9 @@ function attachMessageProcessor(ctx) {
497
496
  keptAskUser[lid] = lentry;
498
497
  continue;
499
498
  }
499
+ if (lentry && lentry.mode === "yoke" && lentry.respond && typeof lentry.respond.cancel === "function") {
500
+ lentry.respond.cancel("Turn ended");
501
+ }
500
502
  sendAndRecord(session, { type: "ask_user_answered", toolId: lid });
501
503
  }
502
504
  session.pendingAskUser = keptAskUser;
@@ -582,8 +584,10 @@ function attachMessageProcessor(ctx) {
582
584
  var _donePreviewText = (session.responsePreview || "").replace(/\s+/g, " ").trim();
583
585
  if (_donePreviewText.length > 140) _donePreviewText = _donePreviewText.substring(0, 140) + "...";
584
586
  var _doneTitle = mateDisplayName ? (mateDisplayName + " responded") : (session.title || "Claude");
587
+ var _suppressResponseNotification = false;
588
+ try { _suppressResponseNotification = shouldSuppressResponseNotification(session) === true; } catch (e) {}
585
589
 
586
- if (pushModule && !session._delegatedBy) {
590
+ if (pushModule && !session._delegatedBy && !_suppressResponseNotification) {
587
591
  pushModule.sendPush({
588
592
  type: "done",
589
593
  slug: slug,
@@ -594,7 +598,7 @@ function attachMessageProcessor(ctx) {
594
598
  }
595
599
 
596
600
  var _nm = getNotificationsModule();
597
- if (_nm && !session.loop && !session._delegatedBy) {
601
+ if (_nm && !session.loop && !session._delegatedBy && !_suppressResponseNotification) {
598
602
  _nm.notify("response_done", {
599
603
  title: _doneTitle,
600
604
  preview: _donePreviewText,
@@ -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
+ };