@usabledev/usable-chat 1.210.0 → 1.212.0

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 (2) hide show
  1. package/cli.js +333 -129
  2. package/package.json +1 -1
package/cli.js CHANGED
@@ -45714,6 +45714,11 @@ var init_model_pricing = __esm({
45714
45714
  output: 1e-5,
45715
45715
  multiplier: 16
45716
45716
  },
45717
+ "anthropic/claude-sonnet-5.5": {
45718
+ input: 2e-6,
45719
+ output: 1e-5,
45720
+ multiplier: 16
45721
+ },
45717
45722
  // Deepseek Models
45718
45723
  "deepseek/deepseek-chat": {
45719
45724
  input: 32e-8,
@@ -47013,6 +47018,29 @@ var init_model_registry_generated = __esm({
47013
47018
  },
47014
47019
  createdAt: 1790094732
47015
47020
  },
47021
+ {
47022
+ id: "anthropic/claude-sonnet-5.5",
47023
+ displayName: "Claude Sonnet 5.5",
47024
+ vendor: "anthropic",
47025
+ category: "balanced",
47026
+ capabilities: {
47027
+ vision: true,
47028
+ reasoning: true,
47029
+ maxTokens: 128e3,
47030
+ contextLength: 1e6
47031
+ },
47032
+ pricing: {
47033
+ inputPer1M: 2,
47034
+ outputPer1M: 10,
47035
+ multiplier: 16
47036
+ },
47037
+ availability: {
47038
+ mainChat: false,
47039
+ experts: true,
47040
+ embeds: true
47041
+ },
47042
+ createdAt: 1790553600
47043
+ },
47016
47044
  {
47017
47045
  id: "openai/gpt-6-astra",
47018
47046
  displayName: "GPT-6 Astra",
@@ -47940,7 +47968,7 @@ var init_model_registry_generated = __esm({
47940
47968
  category: "balanced",
47941
47969
  capabilities: {
47942
47970
  vision: true,
47943
- reasoning: false,
47971
+ reasoning: true,
47944
47972
  maxTokens: 128e3,
47945
47973
  contextLength: 1e6
47946
47974
  },
@@ -52618,6 +52646,7 @@ var init_model_registry = __esm({
52618
52646
  },
52619
52647
  "claude-sonnet-4-6": { bedrockId: "anthropic.claude-sonnet-4-6", crossRegion: true },
52620
52648
  "claude-sonnet-5": { bedrockId: "global.anthropic.claude-sonnet-5", crossRegion: false },
52649
+ "claude-sonnet-5-5": { bedrockId: "global.anthropic.claude-sonnet-5-5", crossRegion: false },
52621
52650
  "claude-opus-5": { bedrockId: "global.anthropic.claude-opus-5", crossRegion: false },
52622
52651
  "claude-opus-5-5": { bedrockId: "global.anthropic.claude-opus-5-5", crossRegion: false },
52623
52652
  // Opus 4.7 uses the Global Cross-Region Inference profile (no regional Geo access required)
@@ -95134,6 +95163,12 @@ var init_beta_features = __esm({
95134
95163
  name: "Research mode",
95135
95164
  description: "Run structured, sourced research with specialist workers and an HTML report."
95136
95165
  },
95166
+ "auto-model": {
95167
+ key: "auto-model",
95168
+ name: "Auto model",
95169
+ description: "Use Jev to choose a curated model for each text request.",
95170
+ failureMessage: "Could not save Auto model. Try again."
95171
+ },
95137
95172
  "configurable-hooks": {
95138
95173
  key: "configurable-hooks",
95139
95174
  name: "Configurable hooks",
@@ -95160,7 +95195,8 @@ var init_beta_features = __esm({
95160
95195
  betaFeatureGrantSchema = external_exports.object({
95161
95196
  enabled: external_exports.boolean(),
95162
95197
  enabledAt: external_exports.string().datetime(),
95163
- enabledBy: external_exports.string()
95198
+ enabledBy: external_exports.string(),
95199
+ autoModelChoices: external_exports.object({ simple: external_exports.string(), advanced: external_exports.string() }).optional()
95164
95200
  });
95165
95201
  userBetaFeaturesSchema = external_exports.record(external_exports.string(), betaFeatureGrantSchema);
95166
95202
  }
@@ -106896,11 +106932,11 @@ var require_randomUUID = __commonJS({
106896
106932
  var require_dist_cjs31 = __commonJS({
106897
106933
  "node_modules/.pnpm/@smithy+uuid@1.1.2/node_modules/@smithy/uuid/dist-cjs/index.js"(exports2) {
106898
106934
  "use strict";
106899
- var randomUUID15 = require_randomUUID();
106935
+ var randomUUID16 = require_randomUUID();
106900
106936
  var decimalToHex = Array.from({ length: 256 }, (_16, i19) => i19.toString(16).padStart(2, "0"));
106901
106937
  var v43 = () => {
106902
- if (randomUUID15.randomUUID) {
106903
- return randomUUID15.randomUUID();
106938
+ if (randomUUID16.randomUUID) {
106939
+ return randomUUID16.randomUUID();
106904
106940
  }
106905
106941
  const rnds = new Uint8Array(16);
106906
106942
  crypto.getRandomValues(rnds);
@@ -138218,6 +138254,7 @@ async function callClaude(apiKey, request, options2) {
138218
138254
  function extractClaudeModel(modelId) {
138219
138255
  const modelMap = {
138220
138256
  // Claude Sonnet 5 models
138257
+ "anthropic/claude-sonnet-5.5": "claude-sonnet-5-5",
138221
138258
  "anthropic/claude-sonnet-5": "claude-sonnet-5",
138222
138259
  // Claude Sonnet 4.5 models
138223
138260
  "anthropic/claude-sonnet-4.5": "claude-sonnet-4-5-20250929",
@@ -144784,8 +144821,8 @@ var require_utils = __commonJS({
144784
144821
  var HOST_DELIMS = { "@": "%40", "/": "%2F", "?": "%3F", "#": "%23", ":": "%3A" };
144785
144822
  var HOST_DELIM_RE = /[@/?#:]/g;
144786
144823
  var HOST_DELIM_NO_COLON_RE = /[@/?#]/g;
144787
- function reescapeHostDelimiters(host, isIP3) {
144788
- const re10 = isIP3 ? HOST_DELIM_NO_COLON_RE : HOST_DELIM_RE;
144824
+ function reescapeHostDelimiters(host, isIP4) {
144825
+ const re10 = isIP4 ? HOST_DELIM_NO_COLON_RE : HOST_DELIM_RE;
144789
144826
  re10.lastIndex = 0;
144790
144827
  return host.replace(re10, (ch) => HOST_DELIMS[ch]);
144791
144828
  }
@@ -145267,7 +145304,7 @@ var require_fast_uri = __commonJS({
145267
145304
  fragment: void 0
145268
145305
  };
145269
145306
  let malformedAuthorityOrPort = false;
145270
- let isIP3 = false;
145307
+ let isIP4 = false;
145271
145308
  if (options2.reference === "suffix") {
145272
145309
  if (options2.scheme) {
145273
145310
  uri = options2.scheme + ":" + uri;
@@ -145297,9 +145334,9 @@ var require_fast_uri = __commonJS({
145297
145334
  if (ipv4result === false) {
145298
145335
  const ipv6result = normalizeIPv6(parsed.host);
145299
145336
  parsed.host = ipv6result.host.toLowerCase();
145300
- isIP3 = ipv6result.isIPV6;
145337
+ isIP4 = ipv6result.isIPV6;
145301
145338
  } else {
145302
- isIP3 = true;
145339
+ isIP4 = true;
145303
145340
  }
145304
145341
  }
145305
145342
  if (parsed.scheme === void 0 && parsed.userinfo === void 0 && parsed.host === void 0 && parsed.port === void 0 && parsed.query === void 0 && !parsed.path) {
@@ -145316,7 +145353,7 @@ var require_fast_uri = __commonJS({
145316
145353
  }
145317
145354
  const schemeHandler = getSchemeHandler(options2.scheme || parsed.scheme);
145318
145355
  if (!options2.unicodeSupport && (!schemeHandler || !schemeHandler.unicodeSupport)) {
145319
- if (parsed.host && (options2.domainHost || schemeHandler && schemeHandler.domainHost) && isIP3 === false && nonSimpleDomain(parsed.host)) {
145356
+ if (parsed.host && (options2.domainHost || schemeHandler && schemeHandler.domainHost) && isIP4 === false && nonSimpleDomain(parsed.host)) {
145320
145357
  try {
145321
145358
  parsed.host = URL.domainToASCII(parsed.host.toLowerCase());
145322
145359
  } catch (e15) {
@@ -145330,7 +145367,7 @@ var require_fast_uri = __commonJS({
145330
145367
  parsed.scheme = unescape(parsed.scheme);
145331
145368
  }
145332
145369
  if (parsed.host !== void 0) {
145333
- parsed.host = reescapeHostDelimiters(unescape(parsed.host), isIP3);
145370
+ parsed.host = reescapeHostDelimiters(unescape(parsed.host), isIP4);
145334
145371
  }
145335
145372
  }
145336
145373
  if (parsed.path) {
@@ -162764,6 +162801,12 @@ var init_schema_mcp = __esm({
162764
162801
  lastError: text2("last_error"),
162765
162802
  deletedAt: timestamp("deleted_at", { withTimezone: true }),
162766
162803
  sourceEventId: varchar("source_event_id", { length: 255 }),
162804
+ // User approval for credential-bearing profiles (see services/mcp-approval.ts).
162805
+ // Existing rows are grandfathered with approved_via='legacy' and no fingerprint.
162806
+ origin: varchar("origin", { length: 16 }).notNull().default("legacy").$type(),
162807
+ approvedFingerprint: varchar("approved_fingerprint", { length: 80 }),
162808
+ approvedAt: timestamp("approved_at", { withTimezone: true }),
162809
+ approvedVia: varchar("approved_via", { length: 32 }),
162767
162810
  createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
162768
162811
  updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow()
162769
162812
  },
@@ -163435,6 +163478,41 @@ var init_tool = __esm({
163435
163478
  }
163436
163479
  });
163437
163480
 
163481
+ // src/core/pause/envelope.ts
163482
+ function isMcpApprovalPause(output) {
163483
+ if (!output || typeof output !== "object") return false;
163484
+ const pause = output.__pause;
163485
+ return pause !== void 0 && pause.kind === MCP_APPROVAL_PAUSE_KIND && typeof pause.resumeToken === "string" && typeof pause.mcpApproval?.profileId === "string" && typeof pause.mcpApproval.fingerprint === "string";
163486
+ }
163487
+ function detectPause(toolResult, toolCallId) {
163488
+ if (isAskQuestionPause(toolResult)) {
163489
+ return {
163490
+ resumeToken: toolResult.__pause.resumeToken,
163491
+ questions: toolResult.__pause.questions,
163492
+ context: toolResult.__pause.context,
163493
+ toolCallId
163494
+ };
163495
+ }
163496
+ if (isMcpApprovalPause(toolResult)) {
163497
+ return {
163498
+ kind: MCP_APPROVAL_PAUSE_KIND,
163499
+ resumeToken: toolResult.__pause.resumeToken,
163500
+ mcpApproval: toolResult.__pause.mcpApproval,
163501
+ action: toolResult.__pause.action,
163502
+ toolCallId
163503
+ };
163504
+ }
163505
+ return null;
163506
+ }
163507
+ var MCP_APPROVAL_PAUSE_KIND;
163508
+ var init_envelope = __esm({
163509
+ "src/core/pause/envelope.ts"() {
163510
+ "use strict";
163511
+ init_tool();
163512
+ MCP_APPROVAL_PAUSE_KIND = "mcp-approval";
163513
+ }
163514
+ });
163515
+
163438
163516
  // src/core/ai/provider-attempt.ts
163439
163517
  function providerAttemptAbortError(attempt) {
163440
163518
  const reason = attempt.signal.reason;
@@ -164100,15 +164178,10 @@ async function adaptClaudeStream(stream, options2) {
164100
164178
  });
164101
164179
  multiplexer.send(cardEvent);
164102
164180
  }
164103
- if (isAskQuestionPause(toolResult)) {
164181
+ const pause = detectPause(toolResult, id);
164182
+ if (pause) {
164104
164183
  pauseDetected = true;
164105
- if (onPauseDetected) {
164106
- await onPauseDetected({
164107
- resumeToken: toolResult.__pause.resumeToken,
164108
- questions: toolResult.__pause.questions,
164109
- context: toolResult.__pause.context
164110
- });
164111
- }
164184
+ if (onPauseDetected) await onPauseDetected(pause);
164112
164185
  return { toolResult };
164113
164186
  }
164114
164187
  if (toolResult?.__parentToolCall === true) {
@@ -164381,15 +164454,10 @@ async function adaptClaudeStream(stream, options2) {
164381
164454
  });
164382
164455
  multiplexer.send(cardEvent);
164383
164456
  }
164384
- if (isAskQuestionPause(toolResult)) {
164457
+ const pause = detectPause(toolResult, id);
164458
+ if (pause) {
164385
164459
  pauseDetected = true;
164386
- if (onPauseDetected) {
164387
- await onPauseDetected({
164388
- resumeToken: toolResult.__pause.resumeToken,
164389
- questions: toolResult.__pause.questions,
164390
- context: toolResult.__pause.context
164391
- });
164392
- }
164460
+ if (onPauseDetected) await onPauseDetected(pause);
164393
164461
  return { toolResult };
164394
164462
  }
164395
164463
  if (toolResult?.__parentToolCall === true) {
@@ -164609,7 +164677,7 @@ var init_claude_stream_adapter = __esm({
164609
164677
  "use strict";
164610
164678
  init_logger();
164611
164679
  init_store();
164612
- init_tool();
164680
+ init_envelope();
164613
164681
  init_provider_attempt();
164614
164682
  }
164615
164683
  });
@@ -164725,15 +164793,10 @@ async function adaptOpenRouterStream(stream, options2) {
164725
164793
  });
164726
164794
  multiplexer.send(cardEvent);
164727
164795
  }
164728
- if (isAskQuestionPause(toolResult)) {
164796
+ const pause = detectPause(toolResult, id);
164797
+ if (pause) {
164729
164798
  pauseDetected = true;
164730
- if (onPauseDetected) {
164731
- await onPauseDetected({
164732
- resumeToken: toolResult.__pause.resumeToken,
164733
- questions: toolResult.__pause.questions,
164734
- context: toolResult.__pause.context
164735
- });
164736
- }
164799
+ if (onPauseDetected) await onPauseDetected(pause);
164737
164800
  return;
164738
164801
  }
164739
164802
  if (toolResult?.__parentToolCall === true) {
@@ -165310,7 +165373,7 @@ var init_openrouter_stream_adapter = __esm({
165310
165373
  "use strict";
165311
165374
  init_logger();
165312
165375
  init_store();
165313
- init_tool();
165376
+ init_envelope();
165314
165377
  init_provider_attempt();
165315
165378
  }
165316
165379
  });
@@ -165358,8 +165421,8 @@ var init_model_tiers = __esm({
165358
165421
  name: "Heavy Reasoning",
165359
165422
  models: [
165360
165423
  {
165361
- id: "anthropic/claude-sonnet-5",
165362
- displayName: "Claude Sonnet 5",
165424
+ id: "anthropic/claude-sonnet-5.5",
165425
+ displayName: "Claude Sonnet 5.5",
165363
165426
  description: "Best for professional tasks, coding, agents, and long conversations",
165364
165427
  provider: "Anthropic",
165365
165428
  costTier: "Expensive",
@@ -166139,7 +166202,7 @@ function mapModeToModel(modeId) {
166139
166202
  const modeMap = {
166140
166203
  standard: "anthropic/claude-haiku-4.5",
166141
166204
  "quick-thinking": "anthropic/claude-haiku-4.5",
166142
- "deep-thinking": "anthropic/claude-sonnet-5",
166205
+ "deep-thinking": "anthropic/claude-sonnet-5.5",
166143
166206
  "multi-lingual": "google/gemini-3.1-pro-preview",
166144
166207
  "standard-fallback": "openai/gpt-5-mini"
166145
166208
  };
@@ -166162,6 +166225,7 @@ var init_model_config = __esm({
166162
166225
  "anthropic/claude-sonnet-4.5": { vision: true, reasoning: true },
166163
166226
  "anthropic/claude-sonnet-4.6": { vision: true, reasoning: true },
166164
166227
  "anthropic/claude-sonnet-5": { vision: true, reasoning: true },
166228
+ "anthropic/claude-sonnet-5.5": { vision: true, reasoning: true },
166165
166229
  "anthropic/claude-haiku-4.5": { vision: true, reasoning: true },
166166
166230
  "anthropic/claude-4.5-haiku": { vision: true, reasoning: true },
166167
166231
  "anthropic/claude-4.5-sonnet": { vision: true, reasoning: true },
@@ -175114,7 +175178,7 @@ var require_gaxios = __commonJS({
175114
175178
  var retry_js_1 = require_retry();
175115
175179
  var stream_1 = __require("stream");
175116
175180
  var interceptor_js_1 = require_interceptor();
175117
- var randomUUID15 = async () => globalThis.crypto?.randomUUID() || (await import("crypto")).randomUUID();
175181
+ var randomUUID16 = async () => globalThis.crypto?.randomUUID() || (await import("crypto")).randomUUID();
175118
175182
  var HTTP_STATUS_NO_CONTENT = 204;
175119
175183
  var Gaxios = class {
175120
175184
  agentCache = /* @__PURE__ */ new Map();
@@ -175387,7 +175451,7 @@ var require_gaxios = __commonJS({
175387
175451
  */
175388
175452
  ["Blob", "File", "FormData"].includes(opts.data?.constructor?.name || "");
175389
175453
  if (opts.multipart?.length) {
175390
- const boundary = await randomUUID15();
175454
+ const boundary = await randomUUID16();
175391
175455
  preparedHeaders.set("content-type", `multipart/related; boundary=${boundary}`);
175392
175456
  opts.body = stream_1.Readable.from(this.getMultipartRequest(opts.multipart, boundary));
175393
175457
  } else if (shouldDirectlyPassData) {
@@ -205823,15 +205887,10 @@ async function callGeminiDirect({
205823
205887
  try {
205824
205888
  if (abortSignal?.aborted) break;
205825
205889
  let toolResult = await executeToolByName(toolName, toolArgs, { ...context3, toolCallId });
205826
- if (isAskQuestionPause(toolResult)) {
205890
+ const pause = detectPause(toolResult, toolCallId);
205891
+ if (pause) {
205827
205892
  pauseDetected = true;
205828
- if (onPauseDetected) {
205829
- await onPauseDetected({
205830
- resumeToken: toolResult.__pause.resumeToken,
205831
- questions: toolResult.__pause.questions,
205832
- context: toolResult.__pause.context
205833
- });
205834
- }
205893
+ if (onPauseDetected) await onPauseDetected(pause);
205835
205894
  completedToolCalls.push({ id: toolCallId, name: toolName, arguments: toolArgs });
205836
205895
  break;
205837
205896
  }
@@ -205996,15 +206055,10 @@ async function callGeminiDirect({
205996
206055
  try {
205997
206056
  if (abortSignal?.aborted) break;
205998
206057
  let toolResult = await executeToolByName(toolName, toolArgs, { ...context3, toolCallId });
205999
- if (isAskQuestionPause(toolResult)) {
206058
+ const pause = detectPause(toolResult, toolCallId);
206059
+ if (pause) {
206000
206060
  pauseDetected = true;
206001
- if (onPauseDetected) {
206002
- await onPauseDetected({
206003
- resumeToken: toolResult.__pause.resumeToken,
206004
- questions: toolResult.__pause.questions,
206005
- context: toolResult.__pause.context
206006
- });
206007
- }
206061
+ if (onPauseDetected) await onPauseDetected(pause);
206008
206062
  completedToolCalls.push({ id: toolCallId, name: toolName, arguments: toolArgs });
206009
206063
  break;
206010
206064
  }
@@ -206261,7 +206315,7 @@ var init_gemini_direct_stream_adapter = __esm({
206261
206315
  init_node2();
206262
206316
  init_esm2();
206263
206317
  init_store();
206264
- init_tool();
206318
+ init_envelope();
206265
206319
  init_provider_attempt();
206266
206320
  }
206267
206321
  });
@@ -207715,6 +207769,7 @@ var init_tool_categories = __esm({
207715
207769
  "update-project",
207716
207770
  "mcp_config_apply",
207717
207771
  "mcp_config_request_secret",
207772
+ "mcp_server_request_approval",
207718
207773
  "mcp_call_tool",
207719
207774
  "mcp_server_create",
207720
207775
  "mcp_server_update",
@@ -219362,7 +219417,7 @@ HOW TO USE THIS:
219362
219417
  - \`model\`: **default to \`quick-thinking\` (alias for \`anthropic/claude-haiku-4.5\`) unless you have a specific reason not to.** Subagents are billed in addition to your own turn \u2014 every Sonnet-class subagent doubles cost. Guidance:
219363
219418
  - **\`quick-thinking\` / \`anthropic/claude-haiku-4.5\`** (cheapest, fast, strong tool use) \u2014 use this for: web/Usable search, summarisation, classification, simple extraction, drafting, tabular work, anything where one-shot quality is "good enough". Should be your **default**.
219364
219419
  - **\`google/gemini-3.8-flash\`** (cheap, huge context, multimodal/multi-lingual) \u2014 use this when: you need to read a 100k+-token document, the task is multi-lingual, or there's image/audio input.
219365
- - **\`deep-thinking\` / \`anthropic/claude-sonnet-5\`** (5\u201310\xD7 the cost of Haiku) \u2014 only use when: the work requires multi-step reasoning over conflicting evidence, code generation against a specific architecture, or careful policy/risk judgement. If you find yourself reaching for this for "research", reach for Haiku first.
219420
+ - **\`deep-thinking\` / \`anthropic/claude-sonnet-5.5\`** (5\u201310\xD7 the cost of Haiku) \u2014 only use when: the work requires multi-step reasoning over conflicting evidence, code generation against a specific architecture, or careful policy/risk judgement. If you find yourself reaching for this for "research", reach for Haiku first.
219366
219421
  - **\`google/gemini-3.1-pro-preview\`** \u2014 only when both massive context AND deep reasoning are required.
219367
219422
  - \`allowedTools\`: tool names exactly as they appear in YOUR own tool list. Raw MCP names (no \`mcp__usable__\` prefix) for built-in / MCP tools; \`parent_*\` prefixed names for embed-host tools registered by the surrounding application (e.g. \`parent_search_vessel_registry\`). Subagents in embed contexts CAN use parent tools \u2014 list them here just like any other tool.
219368
219423
  - \`systemPrompt\` (optional): override the default worker-agent system prompt.
@@ -220244,6 +220299,7 @@ function buildMcpServerToolsPromptSection({
220244
220299
  "- Prefer user-wide profile tools: `mcp_server_list`, `mcp_server_create`, `mcp_server_update`, `mcp_server_delete`, and `mcp_server_test`.",
220245
220300
  "- Use legacy sandbox config tools `mcp_config_list`, `mcp_config_propose`, `mcp_config_test`, and `mcp_config_apply` only when the user explicitly asks for legacy per-sandbox MCP config.",
220246
220301
  "- Never ask the user to reveal secret values. Use `vault_secret_list` to find full secret IDs, or exact Vault secret names when the create/update tool allows them.",
220302
+ "- A server that would receive Vault secrets stays inactive until the user approves it in the app. You cannot approve it: when a tool result says `approvalRequired`, share the `approvalUrl` and wait. Changing its URL, command, or secret bindings requires approval again.",
220247
220303
  "- For npx/uvx API-key MCP servers, put command/header flags in `args` and bind secret values through `envSecretRefs`, not `headersSecretRefs`.",
220248
220304
  '- Groundcover API-key setup uses `runner: "npx"`, `package: "mcp-remote"`, args including `https://mcp.groundcover.com/api/mcp`, `--header`, `Authorization:${GROUNDCOVER_API_KEY}`, and `envSecretRefs: { "GROUNDCOVER_API_KEY": "<secret-id-or-name>" }`.',
220249
220305
  "- After command-based MCP config changes, use `sandbox_list` then `sandbox_restart` if a running execution environment needs a fresh start."
@@ -220522,6 +220578,43 @@ var init_store2 = __esm({
220522
220578
  }
220523
220579
  });
220524
220580
 
220581
+ // src/core/pause/mcp-approval.ts
220582
+ var mcp_approval_exports = {};
220583
+ __export2(mcp_approval_exports, {
220584
+ buildMcpApprovalPause: () => buildMcpApprovalPause,
220585
+ buildMcpApprovalPlaceholder: () => buildMcpApprovalPlaceholder,
220586
+ formatMcpApprovalDecision: () => formatMcpApprovalDecision
220587
+ });
220588
+ import { randomUUID as randomUUID5 } from "node:crypto";
220589
+ function buildMcpApprovalPause(approval, action5) {
220590
+ return {
220591
+ __pause: {
220592
+ kind: MCP_APPROVAL_PAUSE_KIND,
220593
+ resumeToken: randomUUID5(),
220594
+ mcpApproval: approval,
220595
+ action: action5
220596
+ }
220597
+ };
220598
+ }
220599
+ function buildMcpApprovalPlaceholder(approval) {
220600
+ return `[Awaiting user approval for MCP server "${approval.name}" \u2014 approval card is open]`;
220601
+ }
220602
+ function formatMcpApprovalDecision(params) {
220603
+ const { approval, action: action5, decision } = params;
220604
+ const subject = `MCP server "${approval.name}" (id ${approval.profileId}, ${approval.target})`;
220605
+ const done = action5 === "requested" ? "" : ` It was ${action5}.`;
220606
+ if (decision === "approved") {
220607
+ return `User approved ${subject} to use its Vault secrets.${done} It is now active: test it with mcp_server_test before using its tools.`;
220608
+ }
220609
+ return `User rejected ${subject}; it stays disabled and receives no secrets.${done} Do not retry or re-create it unless the user asks.`;
220610
+ }
220611
+ var init_mcp_approval = __esm({
220612
+ "src/core/pause/mcp-approval.ts"() {
220613
+ "use strict";
220614
+ init_envelope();
220615
+ }
220616
+ });
220617
+
220525
220618
  // src/lib/services/user.service.ts
220526
220619
  var user_service_exports = {};
220527
220620
  __export2(user_service_exports, {
@@ -223154,32 +223247,15 @@ var init_hook_definition = __esm({
223154
223247
  }
223155
223248
  });
223156
223249
 
223157
- // src/lib/hooks/hook-runtime.ts
223158
- import { lookup } from "node:dns/promises";
223159
- import { request as httpRequest } from "node:http";
223160
- import { request as httpsRequest } from "node:https";
223250
+ // src/lib/net/guarded-fetch.ts
223161
223251
  import { isIP as isIP2 } from "node:net";
223162
- function boundedJson(value, maxBytes, code = "HOOK_RUNTIME_LIMIT") {
223163
- const json3 = JSON.stringify(value ?? null);
223164
- if (Buffer.byteLength(json3, "utf8") > maxBytes) throw new Error(code);
223165
- return json3;
223166
- }
223167
- function usableOrigin() {
223168
- try {
223169
- return config2.mcp.url ? new URL(config2.mcp.url).origin : null;
223170
- } catch {
223171
- return null;
223172
- }
223173
- }
223174
- function sanitizedUrl(url3) {
223175
- return `${url3.origin}${url3.pathname}`;
223176
- }
223177
223252
  function normalizeIpLiteral(address) {
223178
223253
  return address.toLowerCase().replace(/^\[|\]$/g, "");
223179
223254
  }
223180
223255
  function embeddedIpv4Address(address) {
223181
- const suffix = address.startsWith("::ffff:") ? address.slice("::ffff:".length) : address.startsWith("::") ? address.slice(2) : "";
223182
- if (!suffix) return null;
223256
+ const prefix2 = ["::ffff:", "64:ff9b::", "::"].find((candidate) => address.startsWith(candidate));
223257
+ if (!prefix2) return null;
223258
+ const suffix = address.slice(prefix2.length);
223183
223259
  if (isIP2(suffix) === 4) return suffix;
223184
223260
  const match2 = /^([0-9a-f]{1,4}):([0-9a-f]{1,4})$/.exec(suffix);
223185
223261
  if (!match2) return null;
@@ -223196,7 +223272,40 @@ function isForbiddenAddress(address) {
223196
223272
  const [a22 = 0, b32 = 0] = normalized.split(".").map(Number);
223197
223273
  return a22 === 0 || a22 === 10 || a22 === 127 || a22 === 100 && b32 >= 64 && b32 <= 127 || a22 === 169 && b32 === 254 || a22 === 172 && b32 >= 16 && b32 <= 31 || a22 === 192 && b32 === 0 || a22 === 192 && b32 === 168 || a22 === 198 && (b32 === 18 || b32 === 19) || a22 === 198 && b32 === 51 || a22 === 203 && b32 === 0 || a22 >= 224;
223198
223274
  }
223199
- return normalized.startsWith("fc") || normalized.startsWith("fd") || normalized.startsWith("fe8") || normalized.startsWith("fe9") || normalized.startsWith("fea") || normalized.startsWith("feb") || normalized.startsWith("ff") || normalized.startsWith("2001:db8");
223275
+ return normalized.startsWith("fc") || normalized.startsWith("fd") || /^fe[89a-f]/.test(normalized) || normalized.startsWith("ff") || normalized.startsWith("2001:db8");
223276
+ }
223277
+ var MCP_FETCH_POLICY;
223278
+ var init_guarded_fetch = __esm({
223279
+ "src/lib/net/guarded-fetch.ts"() {
223280
+ "use strict";
223281
+ MCP_FETCH_POLICY = {
223282
+ allowHttp: true,
223283
+ maxResponseBytes: 5 * 1024 * 1024,
223284
+ timeoutMs: 3e4,
223285
+ maxRedirects: 5
223286
+ };
223287
+ }
223288
+ });
223289
+
223290
+ // src/lib/hooks/hook-runtime.ts
223291
+ import { lookup } from "node:dns/promises";
223292
+ import { request as httpRequest } from "node:http";
223293
+ import { request as httpsRequest } from "node:https";
223294
+ import { isIP as isIP3 } from "node:net";
223295
+ function boundedJson(value, maxBytes, code = "HOOK_RUNTIME_LIMIT") {
223296
+ const json3 = JSON.stringify(value ?? null);
223297
+ if (Buffer.byteLength(json3, "utf8") > maxBytes) throw new Error(code);
223298
+ return json3;
223299
+ }
223300
+ function usableOrigin() {
223301
+ try {
223302
+ return config2.mcp.url ? new URL(config2.mcp.url).origin : null;
223303
+ } catch {
223304
+ return null;
223305
+ }
223306
+ }
223307
+ function sanitizedUrl(url3) {
223308
+ return `${url3.origin}${url3.pathname}`;
223200
223309
  }
223201
223310
  async function validateDestination(url3) {
223202
223311
  if (Buffer.byteLength(url3.toString()) > LIMITS.urlBytes || url3.username || url3.password) {
@@ -223213,8 +223322,8 @@ async function validateDestination(url3) {
223213
223322
  if (url3.hostname === "localhost" || url3.hostname.endsWith(".local")) {
223214
223323
  throw new Error("HOOK_FETCH_DESTINATION_BLOCKED");
223215
223324
  }
223216
- const hostname2 = normalizeIpLiteral(url3.hostname);
223217
- const addresses = isIP2(hostname2) ? [{ address: hostname2 }] : await lookup(hostname2, { all: true, verbatim: true });
223325
+ const hostname2 = url3.hostname.toLowerCase().replace(/^\[|\]$/g, "");
223326
+ const addresses = isIP3(hostname2) ? [{ address: hostname2 }] : await lookup(hostname2, { all: true, verbatim: true });
223218
223327
  if (addresses.length === 0 || addresses.some(({ address }) => isForbiddenAddress(address))) {
223219
223328
  throw new Error("HOOK_FETCH_DESTINATION_BLOCKED");
223220
223329
  }
@@ -223612,6 +223721,7 @@ var init_hook_runtime = __esm({
223612
223721
  "use strict";
223613
223722
  init_config();
223614
223723
  init_dist15();
223724
+ init_guarded_fetch();
223615
223725
  LIMITS = {
223616
223726
  memoryBytes: 16 * 1024 * 1024,
223617
223727
  stackBytes: 256 * 1024,
@@ -225376,12 +225486,12 @@ var init_hook_runtime_token = __esm({
225376
225486
  });
225377
225487
 
225378
225488
  // src/lib/hooks/hook-test-confirmation.ts
225379
- import { randomUUID as randomUUID5 } from "node:crypto";
225489
+ import { randomUUID as randomUUID6 } from "node:crypto";
225380
225490
  async function issueHookTestConfirmation(userId, conversationId, turnId) {
225381
225491
  if (!await isRedisAvailable()) return null;
225382
225492
  const redis = getRedis();
225383
225493
  if (!redis) return null;
225384
- const token = randomUUID5();
225494
+ const token = randomUUID6();
225385
225495
  await redis.set(
225386
225496
  `hook-test-confirmation:${token}`,
225387
225497
  JSON.stringify({ userId, conversationId, turnId }),
@@ -230847,7 +230957,7 @@ var init_conversation_resource_authorization = __esm({
230847
230957
  });
230848
230958
 
230849
230959
  // src/lib/services/hook-definition.service.ts
230850
- import { createHash as createHash10, randomUUID as randomUUID6 } from "node:crypto";
230960
+ import { createHash as createHash10, randomUUID as randomUUID7 } from "node:crypto";
230851
230961
  function deterministicUuid(namespace, value) {
230852
230962
  const bytes = Buffer.from(
230853
230963
  createHash10("sha256").update(`${namespace}\0${value}`).digest().subarray(0, 16)
@@ -230858,8 +230968,8 @@ function deterministicUuid(namespace, value) {
230858
230968
  return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
230859
230969
  }
230860
230970
  async function createHookDefinition(pathway, userId, input, idempotencyKey) {
230861
- const hookId = idempotencyKey ? deterministicUuid("usable-chat:hook-definition", `${userId}:${idempotencyKey}`) : randomUUID6();
230862
- const revisionId = idempotencyKey ? deterministicUuid("usable-chat:hook-revision", hookId) : randomUUID6();
230971
+ const hookId = idempotencyKey ? deterministicUuid("usable-chat:hook-definition", `${userId}:${idempotencyKey}`) : randomUUID7();
230972
+ const revisionId = idempotencyKey ? deterministicUuid("usable-chat:hook-revision", hookId) : randomUUID7();
230863
230973
  const existingHook = await getHookDefinition(userId, hookId);
230864
230974
  if (existingHook) return existingHook;
230865
230975
  const timestamp2 = (/* @__PURE__ */ new Date()).toISOString();
@@ -230873,7 +230983,7 @@ async function createHookDefinition(pathway, userId, input, idempotencyKey) {
230873
230983
  const order = (existing.at(-1)?.order ?? -1) + 1;
230874
230984
  await pathway.write("hook-definition.0/hook-definition.created.0", {
230875
230985
  data: {
230876
- id: randomUUID6(),
230986
+ id: randomUUID7(),
230877
230987
  timestamp: timestamp2,
230878
230988
  hookId,
230879
230989
  revisionId,
@@ -230922,11 +231032,11 @@ async function reviseHookDefinition(pathway, userId, hookId, baseRevision, input
230922
231032
  ).orderBy(asc(hookDefinitions.order));
230923
231033
  order = (rows.at(-1)?.order ?? -1) + 1;
230924
231034
  }
230925
- const revisionId = randomUUID6();
231035
+ const revisionId = randomUUID7();
230926
231036
  try {
230927
231037
  await pathway.write("hook-definition.0/hook-definition.revised.0", {
230928
231038
  data: {
230929
- id: randomUUID6(),
231039
+ id: randomUUID7(),
230930
231040
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
230931
231041
  hookId,
230932
231042
  revisionId,
@@ -230954,7 +231064,7 @@ async function setHookActivation(pathway, userId, hookId, enabled2) {
230954
231064
  if (!await getHookDefinition(userId, hookId)) return null;
230955
231065
  await pathway.write("hook-definition.0/hook-definition.activation-changed.0", {
230956
231066
  data: {
230957
- id: randomUUID6(),
231067
+ id: randomUUID7(),
230958
231068
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
230959
231069
  hookId,
230960
231070
  userId,
@@ -230966,7 +231076,7 @@ async function setHookActivation(pathway, userId, hookId, enabled2) {
230966
231076
  async function reorderHookDefinitions(pathway, userId, input) {
230967
231077
  await pathway.write("hook-definition.0/hook-definition.reordered.0", {
230968
231078
  data: {
230969
- id: randomUUID6(),
231079
+ id: randomUUID7(),
230970
231080
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
230971
231081
  userId,
230972
231082
  ...input
@@ -301777,7 +301887,7 @@ var init_httpHeaders = __esm({
301777
301887
  });
301778
301888
 
301779
301889
  // node_modules/.pnpm/@typespec+ts-http-runtime@0.3.4/node_modules/@typespec/ts-http-runtime/dist/esm/util/uuidUtils.js
301780
- function randomUUID7() {
301890
+ function randomUUID8() {
301781
301891
  return crypto.randomUUID();
301782
301892
  }
301783
301893
  var init_uuidUtils = __esm({
@@ -301831,7 +301941,7 @@ var init_pipelineRequest = __esm({
301831
301941
  this.abortSignal = options2.abortSignal;
301832
301942
  this.onUploadProgress = options2.onUploadProgress;
301833
301943
  this.onDownloadProgress = options2.onDownloadProgress;
301834
- this.requestId = options2.requestId || randomUUID7();
301944
+ this.requestId = options2.requestId || randomUUID8();
301835
301945
  this.allowInsecureConnection = options2.allowInsecureConnection ?? false;
301836
301946
  this.enableBrowserStreams = options2.enableBrowserStreams ?? false;
301837
301947
  this.requestOverrides = options2.requestOverrides;
@@ -303535,7 +303645,7 @@ var init_concat = __esm({
303535
303645
 
303536
303646
  // node_modules/.pnpm/@typespec+ts-http-runtime@0.3.4/node_modules/@typespec/ts-http-runtime/dist/esm/policies/multipartPolicy.js
303537
303647
  function generateBoundary() {
303538
- return `----AzSDKFormBoundary${randomUUID7()}`;
303648
+ return `----AzSDKFormBoundary${randomUUID8()}`;
303539
303649
  }
303540
303650
  function encodeHeaders(headers) {
303541
303651
  let result = "";
@@ -304164,8 +304274,8 @@ var init_typeGuards2 = __esm({
304164
304274
  function isError2(e15) {
304165
304275
  return isError(e15);
304166
304276
  }
304167
- function randomUUID8() {
304168
- return randomUUID7();
304277
+ function randomUUID9() {
304278
+ return randomUUID8();
304169
304279
  }
304170
304280
  var isNodeLike2;
304171
304281
  var init_esm40 = __esm({
@@ -333527,7 +333637,7 @@ var init_BlobLeaseClient = __esm({
333527
333637
  this._containerOrBlobOperation = clientContext.blob;
333528
333638
  }
333529
333639
  if (!leaseId2) {
333530
- leaseId2 = randomUUID8();
333640
+ leaseId2 = randomUUID9();
333531
333641
  }
333532
333642
  this._leaseId = leaseId2;
333533
333643
  }
@@ -337934,7 +338044,7 @@ var init_Clients = __esm({
337934
338044
  throw new RangeError(`The buffer's size is too big or the BlockSize is too small;the number of blocks must be <= ${BLOCK_BLOB_MAX_BLOCKS}`);
337935
338045
  }
337936
338046
  const blockList = [];
337937
- const blockIDPrefix = randomUUID8();
338047
+ const blockIDPrefix = randomUUID9();
337938
338048
  let transferProgress = 0;
337939
338049
  const batch = new Batch(options2.concurrency);
337940
338050
  for (let i19 = 0; i19 < numBlocks; i19++) {
@@ -338015,7 +338125,7 @@ var init_Clients = __esm({
338015
338125
  }
338016
338126
  return tracingClient.withSpan("BlockBlobClient-uploadStream", options2, async (updatedOptions) => {
338017
338127
  let blockNum = 0;
338018
- const blockIDPrefix = randomUUID8();
338128
+ const blockIDPrefix = randomUUID9();
338019
338129
  let transferProgress = 0;
338020
338130
  const blockList = [];
338021
338131
  const scheduler = new BufferScheduler(
@@ -339140,7 +339250,7 @@ var init_BlobBatch = __esm({
339140
339250
  constructor() {
339141
339251
  this.operationCount = 0;
339142
339252
  this.body = "";
339143
- const tempGuid = randomUUID8();
339253
+ const tempGuid = randomUUID9();
339144
339254
  this.boundary = `batch_${tempGuid}`;
339145
339255
  this.subRequestPrefix = `--${this.boundary}${HTTP_LINE_ENDING}${HeaderConstants.CONTENT_TYPE}: application/http${HTTP_LINE_ENDING}${HeaderConstants.CONTENT_TRANSFER_ENCODING}: binary`;
339146
339256
  this.multipartContentType = `multipart/mixed; boundary=${this.boundary}`;
@@ -359212,7 +359322,7 @@ __export2(auth_refresh_coordination_exports, {
359212
359322
  compressAccessTokenRotation: () => compressAccessTokenRotation,
359213
359323
  coordinateAccessTokenRefresh: () => coordinateAccessTokenRefresh
359214
359324
  });
359215
- import { createHash as createHash12, randomUUID as randomUUID9 } from "node:crypto";
359325
+ import { createHash as createHash12, randomUUID as randomUUID10 } from "node:crypto";
359216
359326
  async function coordinateAccessTokenRefresh(token, refresh2) {
359217
359327
  const redis = getRedis();
359218
359328
  const secret = process.env.NEXTAUTH_SECRET;
@@ -359264,7 +359374,7 @@ async function coordinateAccessTokenRefresh(token, refresh2) {
359264
359374
  }
359265
359375
  throw new Error("refresh_rotation_chain_limit");
359266
359376
  }
359267
- const owner = randomUUID9();
359377
+ const owner = randomUUID10();
359268
359378
  if (!await redis.set(key2, owner, "EX", TTL, "NX")) {
359269
359379
  for (let attempt = 0; attempt < 20; attempt++) {
359270
359380
  await new Promise((resolve9) => setTimeout(resolve9, 250));
@@ -361655,7 +361765,7 @@ var init_host_jwt = __esm({
361655
361765
  });
361656
361766
 
361657
361767
  // src/lib/work-runtime/resume-proof.ts
361658
- import { createHash as createHash14, createHmac as createHmac4, randomUUID as randomUUID10, timingSafeEqual as timingSafeEqual2 } from "node:crypto";
361768
+ import { createHash as createHash14, createHmac as createHmac4, randomUUID as randomUUID11, timingSafeEqual as timingSafeEqual2 } from "node:crypto";
361659
361769
  function encoded(value) {
361660
361770
  return Buffer.from(value).toString("base64url");
361661
361771
  }
@@ -362500,7 +362610,7 @@ __export2(file_storage_service_exports, {
362500
362610
  FileStorageService: () => FileStorageService,
362501
362611
  fileStorageService: () => fileStorageService
362502
362612
  });
362503
- import { randomUUID as randomUUID11 } from "node:crypto";
362613
+ import { randomUUID as randomUUID12 } from "node:crypto";
362504
362614
  function estimateBase64Bytes2(base643) {
362505
362615
  const content = base643.includes(",") ? base643.split(",")[1] : base643;
362506
362616
  const len = content.length;
@@ -362583,7 +362693,7 @@ var init_file_storage_service = __esm({
362583
362693
  mimeType: stored2.mimeType ?? params.mimeType
362584
362694
  };
362585
362695
  }
362586
- const id = randomUUID11();
362696
+ const id = randomUUID12();
362587
362697
  const preview = params.previewBytes ?? params.bytes;
362588
362698
  const llm = params.llmBytes ?? preview;
362589
362699
  const estimatedTokens = Math.ceil(Math.ceil(llm.byteLength * 4 / 3) / 100);
@@ -362750,7 +362860,7 @@ var init_file_storage_service = __esm({
362750
362860
  * For documents: keeps extractedContent as text and optional originalContent as base64 bytes.
362751
362861
  */
362752
362862
  async storeFile(params) {
362753
- const fileId = randomUUID11();
362863
+ const fileId = randomUUID12();
362754
362864
  let extractedContent = params.extractedContent;
362755
362865
  let originalContent = params.originalContent ?? null;
362756
362866
  if (params.fileType === "image" && params.contentType === "base64") {
@@ -363039,7 +363149,7 @@ var init_file_storage_service = __esm({
363039
363149
  paths = await storage.uploadVariants({
363040
363150
  fileId,
363041
363151
  userId,
363042
- namespace: `migration-${randomUUID11()}`,
363152
+ namespace: `migration-${randomUUID12()}`,
363043
363153
  variants: {
363044
363154
  original: { bytes: original2, mimeType },
363045
363155
  preview: { bytes: preview, mimeType: "image/webp" },
@@ -363304,7 +363414,7 @@ var init_file_storage_service = __esm({
363304
363414
  });
363305
363415
 
363306
363416
  // src/core/orchestrator/index.ts
363307
- import { createHash as createHash17, randomUUID as randomUUID12 } from "node:crypto";
363417
+ import { createHash as createHash17, randomUUID as randomUUID13 } from "node:crypto";
363308
363418
  function shouldRecoverSilentStop({
363309
363419
  text: text5,
363310
363420
  lastStepHadToolErrors,
@@ -363859,21 +363969,24 @@ function createPauseCallback({
363859
363969
  pauseSignaled
363860
363970
  }) {
363861
363971
  return async (data2) => {
363862
- orchestrationLogger.info("Pause detected \u2014 askUserQuestion (deferred persist)", {
363863
- resumeToken: data2.resumeToken,
363864
- questionCount: data2.questions.length
363865
- });
363972
+ orchestrationLogger.info(
363973
+ data2.kind === "mcp-approval" ? "Pause detected \u2014 MCP approval (deferred persist)" : "Pause detected \u2014 askUserQuestion (deferred persist)",
363974
+ {
363975
+ resumeToken: data2.resumeToken,
363976
+ ...data2.kind === "mcp-approval" ? { profileId: data2.mcpApproval.profileId } : { questionCount: data2.questions.length }
363977
+ }
363978
+ );
363866
363979
  pendingPauseRef.current = { data: data2 };
363867
363980
  pauseSignaled.current = true;
363868
363981
  };
363869
363982
  }
363870
- function replacePauseToolResultPlaceholder(messages4, toolUseId) {
363983
+ function replacePauseToolResultPlaceholder(messages4, toolUseId, placeholder2 = ASK_QUESTION_PLACEHOLDER) {
363871
363984
  for (const msg of messages4) {
363872
363985
  if (msg.role === "user" && Array.isArray(msg.content)) {
363873
363986
  const parts = msg.content;
363874
363987
  for (const part of parts) {
363875
363988
  if (part.type === "tool_result" && part.tool_use_id === toolUseId) {
363876
- part.content = ASK_QUESTION_PLACEHOLDER;
363989
+ part.content = placeholder2;
363877
363990
  return true;
363878
363991
  }
363879
363992
  }
@@ -363881,7 +363994,7 @@ function replacePauseToolResultPlaceholder(messages4, toolUseId) {
363881
363994
  const oaiMsg = msg;
363882
363995
  if (oaiMsg.role === "tool" && oaiMsg.tool_call_id === toolUseId) {
363883
363996
  ;
363884
- msg.content = ASK_QUESTION_PLACEHOLDER;
363997
+ msg.content = placeholder2;
363885
363998
  return true;
363886
363999
  }
363887
364000
  }
@@ -363903,6 +364016,22 @@ async function drainPendingPause({
363903
364016
  resolveStreamComplete
363904
364017
  }) {
363905
364018
  const { data: data2 } = pending;
364019
+ if (data2.kind === "mcp-approval") {
364020
+ return drainMcpApprovalPause({
364021
+ data: data2,
364022
+ toolCalls,
364023
+ emitter,
364024
+ multiplexer,
364025
+ context: context3,
364026
+ messageId,
364027
+ correlationId,
364028
+ conversationMessages,
364029
+ persona,
364030
+ config: config3,
364031
+ embedSnapshot,
364032
+ resolveStreamComplete
364033
+ });
364034
+ }
363906
364035
  const askToolCall = toolCalls.find((tc) => tc.name === "ask-user-question");
363907
364036
  const pausedToolUseId = askToolCall?.id;
363908
364037
  if (pausedToolUseId) {
@@ -363943,7 +364072,7 @@ async function drainPendingPause({
363943
364072
  if (durable && conversationId && pausedToolUseId) {
363944
364073
  await context3.sessionPathway.write("chat-interaction.0/chat-interaction.updated.0", {
363945
364074
  data: {
363946
- id: randomUUID12(),
364075
+ id: randomUUID13(),
363947
364076
  interactionId: data2.resumeToken,
363948
364077
  conversationId,
363949
364078
  assistantMessageId: messageId,
@@ -364027,6 +364156,81 @@ async function drainPendingPause({
364027
364156
  multiplexer.close();
364028
364157
  resolveStreamComplete();
364029
364158
  }
364159
+ async function drainMcpApprovalPause({
364160
+ data: data2,
364161
+ toolCalls,
364162
+ emitter,
364163
+ multiplexer,
364164
+ context: context3,
364165
+ messageId,
364166
+ correlationId,
364167
+ conversationMessages,
364168
+ persona,
364169
+ config: config3,
364170
+ embedSnapshot,
364171
+ resolveStreamComplete
364172
+ }) {
364173
+ const { buildMcpApprovalPlaceholder: buildMcpApprovalPlaceholder2 } = await Promise.resolve().then(() => (init_mcp_approval(), mcp_approval_exports));
364174
+ const pausedToolUseId = toolCalls.find((tc) => tc.id === data2.toolCallId)?.id ?? [...toolCalls].reverse().find((tc) => tc.name.startsWith("mcp_server_"))?.id;
364175
+ if (pausedToolUseId) {
364176
+ replacePauseToolResultPlaceholder(
364177
+ conversationMessages,
364178
+ pausedToolUseId,
364179
+ buildMcpApprovalPlaceholder2(data2.mcpApproval)
364180
+ );
364181
+ } else {
364182
+ orchestrationLogger.warn("MCP approval pause signaled but its tool_call was not found");
364183
+ }
364184
+ const snapshot2 = ensureToolCallIntegrity([...conversationMessages]);
364185
+ const lastSequence = emitter.getSequence();
364186
+ const now2 = /* @__PURE__ */ new Date();
364187
+ try {
364188
+ const { storePauseState: storePauseState2 } = await Promise.resolve().then(() => (init_store2(), store_exports));
364189
+ await storePauseState2({
364190
+ kind: "mcp-approval",
364191
+ mcpApproval: data2.mcpApproval,
364192
+ mcpAction: data2.action,
364193
+ resumeToken: data2.resumeToken,
364194
+ userId: context3.session?.user?.id || "",
364195
+ conversationId: context3.metadata?.conversationId,
364196
+ messageId,
364197
+ correlationId,
364198
+ lastSequence,
364199
+ conversationMessages: snapshot2,
364200
+ pausedToolUseId,
364201
+ persona,
364202
+ config: config3,
364203
+ questions: [],
364204
+ temporary: context3.metadata?.temporary === true,
364205
+ embedSnapshot,
364206
+ pausedAt: now2.toISOString(),
364207
+ expiresAt: new Date(now2.getTime() + 60 * 60 * 1e3).toISOString()
364208
+ });
364209
+ } catch (error41) {
364210
+ orchestrationLogger.warn("MCP approval pause cache unavailable; approval still works", {
364211
+ resumeToken: data2.resumeToken,
364212
+ error: error41 instanceof Error ? error41.message : String(error41)
364213
+ });
364214
+ }
364215
+ const approvalEvent = emitter.emit(
364216
+ "mcp-approval-request",
364217
+ {
364218
+ interactionId: data2.resumeToken,
364219
+ resumeToken: data2.resumeToken,
364220
+ approval: data2.mcpApproval,
364221
+ action: data2.action
364222
+ }
364223
+ );
364224
+ multiplexer.send(approvalEvent);
364225
+ orchestrationLogger.info("MCP approval requested, closing stream", {
364226
+ resumeToken: data2.resumeToken,
364227
+ profileId: data2.mcpApproval.profileId,
364228
+ pausedToolUseId,
364229
+ lastSequence
364230
+ });
364231
+ multiplexer.close();
364232
+ resolveStreamComplete();
364233
+ }
364030
364234
  function safeProviderErrorCategory(value) {
364031
364235
  return typeof value === "string" && providerErrorCategories.has(value) ? value : void 0;
364032
364236
  }
@@ -364038,7 +364242,7 @@ function beginNumberedProviderAttempt(budget, route) {
364038
364242
  const attempt = budget.begin(route);
364039
364243
  const number4 = (providerAttemptNumbers.get(budget) ?? 0) + 1;
364040
364244
  providerAttemptNumbers.set(budget, number4);
364041
- return { attempt, id: randomUUID12(), number: number4, startedAt: Date.now() };
364245
+ return { attempt, id: randomUUID13(), number: number4, startedAt: Date.now() };
364042
364246
  }
364043
364247
  function hasProviderAttemptCapacity(budget) {
364044
364248
  return (providerAttemptNumbers.get(budget) ?? 0) < budget.maxAttempts;
@@ -364137,7 +364341,7 @@ async function callLLM({
364137
364341
  }
364138
364342
  const effectiveZdr2 = openRouterPolicy.effectiveZdr;
364139
364343
  const resolveDirectPolicy = (route) => resolveProviderPolicy({ modelId: selectedModelId, route, ...providerPrivacy });
364140
- const safeCorrelationId = safeProviderIdentifier(context3.correlationId) ?? safeProviderIdentifier(context3.messageId) ?? randomUUID12();
364344
+ const safeCorrelationId = safeProviderIdentifier(context3.correlationId) ?? safeProviderIdentifier(context3.messageId) ?? randomUUID13();
364141
364345
  const safeModel = getModelById(selectedModelId) || getModelEntry(selectedModelId) ? selectedModelId : void 0;
364142
364346
  const endpointIdentity = (endpoint) => createHash17("sha256").update(endpoint).digest("hex").slice(0, 16);
364143
364347
  const circuitPolicy = (endpoint) => `zdr:${effectiveZdr2 ? "required" : "disabled"}:endpoint:${endpointIdentity(endpoint)}`;
@@ -366302,7 +366506,7 @@ Folder behavior:
366302
366506
  }
366303
366507
  };
366304
366508
  }
366305
- const planMessage = config3.enableTools ? "\u{1F527} Engaging model with tools..." : "\u{1F4AC} Responding with selected model...";
366509
+ const planMessage = config3.autoModelNotice ?? (config3.enableTools ? "\u{1F527} Engaging model with tools..." : "\u{1F4AC} Responding with selected model...");
366306
366510
  const planEvent = emitter.emit("plan", {
366307
366511
  plan: planMessage,
366308
366512
  steps: config3.maxSteps
@@ -368468,7 +368672,7 @@ Continue once without hiding or replacing your prior response.`
368468
368672
  };
368469
368673
  }
368470
368674
  function createOrchestratorRequest(messages4, context3, config3, persona, apiKey, mcpTools, anthropicApiKey, providerBaseUrl, providerAuthToken, codexAuth) {
368471
- const messageId = context3.messageId || randomUUID12();
368675
+ const messageId = context3.messageId || randomUUID13();
368472
368676
  if (!context3.sessionPathway) {
368473
368677
  throw new Error(
368474
368678
  "sessionPathway is required but not provided. Ensure createSessionPathwaysForAPI() is called in the API route and sessionPathway is passed to createOrchestratorRequest."
@@ -370366,7 +370570,7 @@ var init_skills3 = __esm({
370366
370570
  // src/adapters/cli/subagent.ts
370367
370571
  import { spawn as spawn5 } from "node:child_process";
370368
370572
  import { basename as basename8 } from "node:path";
370369
- import { randomUUID as randomUUID13 } from "node:crypto";
370573
+ import { randomUUID as randomUUID14 } from "node:crypto";
370370
370574
  function currentSubagentDepth() {
370371
370575
  const d21 = Number(process.env.USABLE_SUBAGENT_DEPTH ?? 0);
370372
370576
  return Number.isFinite(d21) && d21 > 0 ? Math.floor(d21) : 0;
@@ -370465,7 +370669,7 @@ function publicTask(task) {
370465
370669
  };
370466
370670
  }
370467
370671
  function startCliSubagentTask(input) {
370468
- const taskId = input.taskId ?? randomUUID13();
370672
+ const taskId = input.taskId ?? randomUUID14();
370469
370673
  const abortController = new AbortController();
370470
370674
  const startedAt = (/* @__PURE__ */ new Date()).toISOString();
370471
370675
  const run2 = input._run ?? runCliSubagent;
@@ -385940,7 +386144,7 @@ var init_todo_checklist = __esm({
385940
386144
  // src/cli/warp-agent.ts
385941
386145
  import { openSync, writeSync } from "node:fs";
385942
386146
  import { basename as basename9 } from "node:path";
385943
- import { randomUUID as randomUUID14 } from "node:crypto";
386147
+ import { randomUUID as randomUUID15 } from "node:crypto";
385944
386148
  function isWarpAgentHost() {
385945
386149
  return !!process.env.WARP_CLI_AGENT_PROTOCOL_VERSION && process.platform !== "win32";
385946
386150
  }
@@ -385993,7 +386197,7 @@ var init_warp_agent = __esm({
385993
386197
  "use strict";
385994
386198
  WARP_AGENT_SLUG = "usable-chat";
385995
386199
  APP_NAME = "usable-chat";
385996
- sessionId = randomUUID14();
386200
+ sessionId = randomUUID15();
385997
386201
  }
385998
386202
  });
385999
386203
 
@@ -391935,7 +392139,7 @@ init_tui_select();
391935
392139
  init_model_registry();
391936
392140
 
391937
392141
  // package.json
391938
- var version2 = "1.210.0";
392142
+ var version2 = "1.212.0";
391939
392143
 
391940
392144
  // src/adapters/cli/model-catalog.ts
391941
392145
  init_codex_auth();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@usabledev/usable-chat",
3
- "version": "1.210.0",
3
+ "version": "1.212.0",
4
4
  "description": "usable-chat — terminal harness for usable-chat (headless + TUI)",
5
5
  "type": "module",
6
6
  "bin": {