@yanlinglabs/winter-agent-runtime 0.0.29 → 0.0.30

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 (41) hide show
  1. package/dist/embedded-worker.js +6 -4
  2. package/dist/embedded.js +6 -4
  3. package/dist/engine.d.ts +16 -0
  4. package/dist/{index-jz5d90c6.js → index-51673qbw.js} +151 -120
  5. package/dist/{index-gmxxtqpe.js → index-9df1q7j4.js} +30 -12
  6. package/dist/{index-8jgwp1px.js → index-9s68t0pd.js} +170 -11
  7. package/dist/index-bgeqc7ax.js +52 -0
  8. package/dist/{index-eyddzcgs.js → index-wsfqsrh5.js} +2 -2
  9. package/dist/index-z6s3r4xm.js +903 -0
  10. package/dist/index.js +4 -2
  11. package/dist/mcp/client.d.ts +20 -1
  12. package/dist/mcp/lifecycle.d.ts +8 -0
  13. package/dist/mcp/transports/http.d.ts +10 -1
  14. package/dist/mcp/transports/sse.d.ts +4 -2
  15. package/dist/mcp-auth/account.d.ts +33 -0
  16. package/dist/mcp-auth/config.d.ts +6 -0
  17. package/dist/mcp-auth/constants.d.ts +30 -0
  18. package/dist/mcp-auth/discovery.d.ts +72 -0
  19. package/dist/mcp-auth/engine-wiring.d.ts +52 -0
  20. package/dist/mcp-auth/errors.d.ts +37 -0
  21. package/dist/mcp-auth/fetch-policy.d.ts +33 -0
  22. package/dist/mcp-auth/login.d.ts +59 -0
  23. package/dist/mcp-auth/records.d.ts +73 -0
  24. package/dist/mcp-auth/refresh.d.ts +34 -0
  25. package/dist/mcp-auth/revoke.d.ts +15 -0
  26. package/dist/mcp-auth/session-provider.d.ts +55 -0
  27. package/dist/mcp-auth/store.d.ts +85 -0
  28. package/dist/mcp-auth/test-fixture-as.d.ts +72 -0
  29. package/dist/mcp-auth.d.ts +14 -0
  30. package/dist/mcp-auth.js +493 -0
  31. package/dist/mcp-client.d.ts +12 -2
  32. package/dist/mcp-client.js +15 -2
  33. package/dist/production-wiring.d.ts +6 -0
  34. package/dist/provider/host-credentials.d.ts +48 -0
  35. package/dist/provider/keychain-store.d.ts +29 -1
  36. package/dist/provider/session-provider.d.ts +1 -1
  37. package/dist/store/dialect.d.ts +1 -1
  38. package/dist/testing.js +5 -3
  39. package/dist/version.d.ts +1 -1
  40. package/dist/version.js +1 -1
  41. package/package.json +8 -4
@@ -1,13 +1,15 @@
1
- import"./index-jz5d90c6.js";
1
+ import"./index-51673qbw.js";
2
2
  import {
3
3
  liveProcessGroups,
4
4
  onProcessGroupChange
5
- } from "./index-8jgwp1px.js";
5
+ } from "./index-9s68t0pd.js";
6
+ import"./index-z6s3r4xm.js";
7
+ import"./index-bgeqc7ax.js";
6
8
  import"./index-bef62z3r.js";
7
- import"./index-gmxxtqpe.js";
9
+ import"./index-9df1q7j4.js";
8
10
  import {
9
11
  runEmbeddedSession2
10
- } from "./index-eyddzcgs.js";
12
+ } from "./index-wsfqsrh5.js";
11
13
  import {
12
14
  Queue
13
15
  } from "./index-97t2rmtf.js";
package/dist/embedded.js CHANGED
@@ -1,10 +1,12 @@
1
1
  import {
2
2
  runEmbeddedSession2
3
- } from "./index-eyddzcgs.js";
4
- import"./index-gmxxtqpe.js";
5
- import"./index-jz5d90c6.js";
3
+ } from "./index-wsfqsrh5.js";
4
+ import"./index-9df1q7j4.js";
5
+ import"./index-51673qbw.js";
6
+ import"./index-bgeqc7ax.js";
6
7
  import"./index-bef62z3r.js";
7
- import"./index-8jgwp1px.js";
8
+ import"./index-9s68t0pd.js";
9
+ import"./index-z6s3r4xm.js";
8
10
  export {
9
11
  runEmbeddedSession2 as runEmbeddedSession
10
12
  };
package/dist/engine.d.ts CHANGED
@@ -12,6 +12,8 @@ import { type StructuredOutputSeam } from "./structured/seam.js";
12
12
  import { type FileCheckpointSink } from "./checkpoint/seam.js";
13
13
  import { type CommandResolver } from "./commands/seam.js";
14
14
  import { type McpServerSource } from "./mcp/lifecycle.js";
15
+ import type { McpOAuthStore } from "./mcp-auth/store.js";
16
+ import type { HostCredentialChannel } from "./provider/host-credentials.js";
15
17
  import { type ChildHandle } from "./subagents/child-handle.js";
16
18
  import { type SourcedRuleEntry } from "./permissions/ruleset.js";
17
19
  import { type AutoAuditRecorder, type ClassifierInterface } from "./permissions/auto/engine.js";
@@ -981,6 +983,20 @@ export interface EngineOptions {
981
983
  * clone connects freely.
982
984
  */
983
985
  extraMcpServerSources?: readonly McpServerSource[];
986
+ /**
987
+ * WS-25 (MCP OAuth): where this session READS its MCP sign-ins -- the session's own Keychain service,
988
+ * supplied by production wiring (`resolveSessionMcpOAuthStore`). Present, every remote (`http`/`sse`)
989
+ * server without a static `Authorization` header connects with a read-only bearer provider that asks
990
+ * the host (`mcp_oauth_refresh`) to refresh. ABSENT -- every direct `runEngine` caller, every unit test --
991
+ * no provider is attached anywhere, the Keychain is never touched, and a 401 is `needs-auth` as before.
992
+ */
993
+ mcpOAuthStore?: McpOAuthStore;
994
+ /**
995
+ * WS-25 §7: the host-credential channel production wiring built for a host-brokered session
996
+ * (`RuntimeConfig.hostCredentials`). Bound to this run's control bridge the moment it exists, so every
997
+ * `credential_resolve` rides the session's own stdio pipe. Absent: nothing to bind.
998
+ */
999
+ hostCredentialChannel?: HostCredentialChannel;
984
1000
  /**
985
1001
  * `system/init.slash_commands`. Produced by `slashCommandNames(resolver, cwd)`, which ALREADY
986
1002
  * includes the engine's own `/compact` -- the engine must not prepend it a second time.
@@ -1,9 +1,13 @@
1
1
  import {
2
2
  trackProcessGroup,
3
3
  createElicitationAsker2,
4
+ createSessionAuthProvider,
5
+ mcpSignInHint,
4
6
  McpConnectError2,
5
- connectMcpServer2
6
- } from "./index-8jgwp1px.js";
7
+ connectMcpServer2,
8
+ insufficientScopeOf,
9
+ classifyMcpAuthFailure
10
+ } from "./index-9s68t0pd.js";
7
11
  import {
8
12
  promptKey,
9
13
  RunJournal,
@@ -14,6 +18,12 @@ import {
14
18
  WORKFLOW_WORKER_ARGV_FLAG2,
15
19
  WORKFLOW_WORKER_BRIDGE_FLAG2
16
20
  } from "./index-bef62z3r.js";
21
+ import {
22
+ keychainAccountName
23
+ } from "./index-z6s3r4xm.js";
24
+ import {
25
+ validateMcpOAuthConfig2
26
+ } from "./index-bgeqc7ax.js";
17
27
  import {
18
28
  Queue
19
29
  } from "./index-97t2rmtf.js";
@@ -4946,6 +4956,14 @@ function validateServerConfig(raw) {
4946
4956
  return { ok: false, reason: `MCP server config 'versionNegotiation' must be "legacy", "auto" or { "pin": "<YYYY-MM-DD revision>" }; got ${JSON.stringify(negotiation)}` };
4947
4957
  }
4948
4958
  }
4959
+ const oauth = raw.oauth;
4960
+ if (oauth !== undefined) {
4961
+ if (type !== "http" && type !== "sse")
4962
+ return { ok: false, reason: "'oauth' applies only to a remote (http/sse) MCP server" };
4963
+ const reason = validateMcpOAuthConfig2(oauth, raw.url);
4964
+ if (reason !== undefined)
4965
+ return { ok: false, reason };
4966
+ }
4949
4967
  return { ok: true, config: raw };
4950
4968
  }
4951
4969
  function resolveMcpServerSources(sources, opts) {
@@ -5109,6 +5127,24 @@ function toolInfoToDefinition(tool) {
5109
5127
  ...tool._meta !== undefined ? { _meta: tool._meta } : {}
5110
5128
  };
5111
5129
  }
5130
+ function hasStaticAuthorization(config) {
5131
+ if (config.type !== "http" && config.type !== "sse")
5132
+ return false;
5133
+ return Object.keys(config.headers ?? {}).some((h) => h.toLowerCase() === "authorization");
5134
+ }
5135
+ function sessionAuthFor(name, config, oauth) {
5136
+ if (oauth === undefined || config.type !== "http" && config.type !== "sse" || hasStaticAuthorization(config))
5137
+ return;
5138
+ try {
5139
+ return createSessionAuthProvider({ serverName: name, serverUrl: config.url, oauth });
5140
+ } catch {
5141
+ return;
5142
+ }
5143
+ }
5144
+ function displayScope(scope) {
5145
+ const cleaned = (scope ?? "").replace(/[^A-Za-z0-9:._/ -]/g, "").trim().slice(0, 200);
5146
+ return cleaned === "" ? "unstated" : cleaned;
5147
+ }
5112
5148
  function createInMemoryDiscoveryCache() {
5113
5149
  const map = new Map;
5114
5150
  return {
@@ -5199,6 +5235,15 @@ function createMcpLifecycle(deps) {
5199
5235
  });
5200
5236
  return { output: capped.text, ...result.isError === true ? { isError: true } : {} };
5201
5237
  } catch (err) {
5238
+ const authFailure = classifyMcpAuthFailure(err);
5239
+ if (authFailure === "needs_auth")
5240
+ return { output: `Error: ${markNeedsAuth(slot, slot.client, err)}`, isError: true };
5241
+ if (authFailure === "insufficient_scope") {
5242
+ const challenge = insufficientScopeOf(err);
5243
+ slot.auth?.reportInsufficientScope(challenge?.requiredScope);
5244
+ const hint = deps.oauth?.signInHint(slot.name) ?? "";
5245
+ return { output: `Error: mcp tool call "${toolName}" on server "${slot.name}" needs additional permission the current sign-in does not grant (scope: ${displayScope(challenge?.requiredScope)}). ${hint}`.trim(), isError: true };
5246
+ }
5202
5247
  const message = err instanceof Error ? err.message : String(err);
5203
5248
  return { output: `Error: mcp tool call "${toolName}" on server "${slot.name}" failed: ${message}`, isError: true };
5204
5249
  }
@@ -5209,6 +5254,18 @@ function createMcpLifecycle(deps) {
5209
5254
  function isCurrentAttempt(slot, gen) {
5210
5255
  return slots.get(slot.name) === slot && slot.gen === gen;
5211
5256
  }
5257
+ function markNeedsAuth(slot, failed, err) {
5258
+ const hint = deps.oauth?.signInHint(slot.name) ?? "";
5259
+ const message = err instanceof McpConnectError2 || err instanceof Error && err.name === "McpNeedsAuthError" ? err.message : `MCP server "${slot.name}" needs sign-in (the server answered 401). ${hint}`.trim();
5260
+ if (slots.get(slot.name) !== slot || failed === undefined || slot.client !== failed)
5261
+ return message;
5262
+ slot.gen++;
5263
+ slot.client = undefined;
5264
+ failed.close().catch(() => {});
5265
+ unregisterMcpServerTools(slot.name);
5266
+ setSlotState(slot.name, "needsAuth", { errorCode: "needs_auth", error: message });
5267
+ return `${message} Its tools are unavailable until then.`;
5268
+ }
5212
5269
  function beginAttempt(slot) {
5213
5270
  return ++slot.gen;
5214
5271
  }
@@ -5241,6 +5298,7 @@ function createMcpLifecycle(deps) {
5241
5298
  async function connectSlotForReal(slot, gen) {
5242
5299
  let connection;
5243
5300
  slot.toolListStale = false;
5301
+ const auth = sessionAuthFor(slot.name, slot.config, deps.oauth);
5244
5302
  const client = await connectMcpServer2({
5245
5303
  name: slot.name,
5246
5304
  config: slot.config,
@@ -5248,7 +5306,8 @@ function createMcpLifecycle(deps) {
5248
5306
  elicitationAsk: deps.elicitationAsk,
5249
5307
  onToolListChanged: onToolListChangedFor(slot, () => connection),
5250
5308
  ...deps.inProcessServers?.[slot.name] !== undefined ? { inProcessServer: deps.inProcessServers[slot.name] } : {},
5251
- ...deps.cwd !== undefined ? { cwd: deps.cwd } : {}
5309
+ ...deps.cwd !== undefined ? { cwd: deps.cwd } : {},
5310
+ ...auth !== undefined ? { auth } : {}
5252
5311
  });
5253
5312
  connection = client;
5254
5313
  const tools = await client.listTools();
@@ -5257,6 +5316,7 @@ function createMcpLifecycle(deps) {
5257
5316
  return false;
5258
5317
  }
5259
5318
  slot.client = client;
5319
+ slot.auth = auth;
5260
5320
  slot.toolNames = tools.map((t) => t.name);
5261
5321
  slot.savedTools = tools;
5262
5322
  registerMcpServerTools(slot.name, tools.map(toolInfoToDefinition), {
@@ -5386,6 +5446,8 @@ function createMcpLifecycle(deps) {
5386
5446
  setSlotState(name, "connected", { toolNames: slot.toolNames });
5387
5447
  return { ok: true, toolNames: slot.toolNames };
5388
5448
  } catch (err) {
5449
+ if (classifyMcpAuthFailure(err) === "needs_auth")
5450
+ return { ok: false, reason: markNeedsAuth(slot, client, err) };
5389
5451
  const message = err instanceof Error ? err.message : String(err);
5390
5452
  return { ok: false, reason: `refreshing server "${name}" failed: ${message}` };
5391
5453
  }
@@ -5462,6 +5524,68 @@ function createMcpLifecycle(deps) {
5462
5524
  };
5463
5525
  }
5464
5526
 
5527
+ // src/mcp-auth/engine-wiring.ts
5528
+ import { MCP_OAUTH_REFRESH_SUBTYPE, WinterRpcError as WinterRpcError2 } from "@yanlinglabs/winter-agent-sdk";
5529
+ var MCP_OAUTH_HOST_REFRESH_TIMEOUT_MS = 60000;
5530
+ function isAnswer(value) {
5531
+ if (typeof value !== "object" || value === null)
5532
+ return false;
5533
+ const v = value;
5534
+ return v.ok === true || v.ok === false && (v.reason === "needs_auth" || v.reason === "transient");
5535
+ }
5536
+ function createSessionMcpOAuth(opts) {
5537
+ return {
5538
+ store: opts.store,
5539
+ ...opts.hostOwnsRefresh === true ? { hostOwnsRefresh: true } : {},
5540
+ signInHint: (serverName) => mcpSignInHint(opts.brand, serverName),
5541
+ askHost: async (request) => {
5542
+ try {
5543
+ const answer = await opts.sender.request(MCP_OAUTH_REFRESH_SUBTYPE, request, { timeoutMs: MCP_OAUTH_HOST_REFRESH_TIMEOUT_MS });
5544
+ return isAnswer(answer) ? answer : { ok: false, reason: "transient" };
5545
+ } catch (err) {
5546
+ if (err instanceof WinterRpcError2 && err.code === "unhandled_subtype")
5547
+ return "unhandled";
5548
+ return { ok: false, reason: "transient" };
5549
+ }
5550
+ }
5551
+ };
5552
+ }
5553
+ function needsAuthServers(states) {
5554
+ return (states ?? []).filter((s) => s.state === "needsAuth").map((s) => s.name).sort();
5555
+ }
5556
+ function needsAuthToolHint(states, toolName, brand) {
5557
+ const server = needsAuthServers(states).find((name) => toolName.startsWith(`mcp__${name}__`));
5558
+ if (server === undefined)
5559
+ return "";
5560
+ return `. The MCP server '${server}' needs sign-in, so its tools are unavailable. ${mcpSignInHint(brand, server)}`;
5561
+ }
5562
+ var SERVER_PLACEHOLDER = "<server>";
5563
+ function displayName(name) {
5564
+ return name.replace(/[^A-Za-z0-9_.:-]/g, "_").slice(0, 64);
5565
+ }
5566
+ registerAttachmentRenderer("mcp_needs_auth", (attachment) => {
5567
+ const servers = Array.isArray(attachment.servers) ? attachment.servers.filter((s) => typeof s === "string").map(displayName) : [];
5568
+ const door = typeof attachment.door === "string" ? attachment.door : "";
5569
+ if (servers.length === 0)
5570
+ return "The MCP servers that needed sign-in are signed in now; their tools are available again.";
5571
+ return `These MCP servers need the user to sign in, so their tools are not available: ${servers.join(", ")}. Do not try to authenticate on the user's behalf. If the user needs one of them, tell them how to sign in: ${door}`;
5572
+ });
5573
+ function lastAnnounced(messages) {
5574
+ let last = [];
5575
+ for (const a of attachmentsIn(messages)) {
5576
+ if (a.type === "mcp_needs_auth" && Array.isArray(a.servers))
5577
+ last = a.servers.filter((s) => typeof s === "string");
5578
+ }
5579
+ return last;
5580
+ }
5581
+ function mcpNeedsAuthAttachment(states, messages, brand) {
5582
+ const now = needsAuthServers(states);
5583
+ const before = lastAnnounced(messages);
5584
+ if (now.length === before.length && now.every((name, i) => name === before[i]))
5585
+ return;
5586
+ return { type: "mcp_needs_auth", servers: now, door: mcpSignInHint(brand, SERVER_PLACEHOLDER) };
5587
+ }
5588
+
5465
5589
  // src/subagents/child-handle.ts
5466
5590
  var childEngineFactory;
5467
5591
  function registerChildEngineFactory(factory) {
@@ -21788,6 +21912,8 @@ async function runEngineBody(opts, facetDisposers, registerBackgroundShellSweep)
21788
21912
  extraHookEntries,
21789
21913
  subagentHooks,
21790
21914
  extraMcpServerSources,
21915
+ mcpOAuthStore,
21916
+ hostCredentialChannel,
21791
21917
  initSlashCommands,
21792
21918
  initSkills,
21793
21919
  initPlugins,
@@ -21907,6 +22033,7 @@ ${escapeAttributionText(msg.body)}
21907
22033
  const policyStateStore = new PolicyStateStore({ mode: initialMode, rules: initialRules }, bypassGate);
21908
22034
  const permissionHome = homedir();
21909
22035
  const bridge = createRpcBridge2(output);
22036
+ hostCredentialChannel?.bind(bridge);
21910
22037
  const bridgePromptStage = createBridgePromptStage(bridge);
21911
22038
  const realPromptStage = {
21912
22039
  async prompt(call, evalCtx, meta) {
@@ -22763,7 +22890,8 @@ ${escapeAttributionText(msg.body)}
22763
22890
  envConfig: mcpEnvConfig,
22764
22891
  elicitationAsk: createElicitationAsker2(bridge),
22765
22892
  reservedServerName: sessionBrand.mcpServerName,
22766
- cwd: config.cwd
22893
+ cwd: config.cwd,
22894
+ ...mcpOAuthStore !== undefined ? { oauth: createSessionMcpOAuth({ store: mcpOAuthStore, sender: bridge, brand: sessionBrand, ...config.hostCredentials === true ? { hostOwnsRefresh: true } : {} }) } : {}
22767
22895
  });
22768
22896
  await mcpLifecycle.start();
22769
22897
  disposeSessionMcpLifecycle = registerSessionMcpLifecycle(sessionStateKey, mcpLifecycle);
@@ -24108,6 +24236,11 @@ ${escapeAttributionText(msg.body)}
24108
24236
  const plan = planModeAttachment();
24109
24237
  if (plan !== undefined)
24110
24238
  produced.push(plan);
24239
+ if (effectiveMcpStateSource !== undefined) {
24240
+ const needsAuth = mcpNeedsAuthAttachment(effectiveMcpStateSource.snapshot(), messages, sessionBrand);
24241
+ if (needsAuth !== undefined)
24242
+ produced.push(needsAuth);
24243
+ }
24111
24244
  }
24112
24245
  if (phase === "tool-round") {
24113
24246
  const attachment = taskNotificationAttachment(notifications.drainFor(config.agentId, { maxPriority: "next" }), {
@@ -24375,6 +24508,8 @@ ${escapeAttributionText(msg.body)}
24375
24508
  if (pending !== undefined)
24376
24509
  hint = `. The MCP server '${pending.name}' is still connecting. Call WaitForMcpServers to wait for it, then try again.`;
24377
24510
  }
24511
+ if (hint === "" && toolName.startsWith("mcp__"))
24512
+ hint = needsAuthToolHint(effectiveMcpStateSource?.snapshot(), toolName, sessionBrand);
24378
24513
  return `<tool_use_error>Error: No such tool available: ${toolName}${hint}</tool_use_error>`;
24379
24514
  };
24380
24515
  const assertMessagesWithinCap = (msgs) => {
@@ -26367,7 +26502,7 @@ class TranscriptWriter2 {
26367
26502
  return entries;
26368
26503
  }
26369
26504
  }
26370
- var RUNTIME_ENGINE_VERSION2 = "0.0.29";
26505
+ var RUNTIME_ENGINE_VERSION2 = "0.0.30";
26371
26506
  function withPermissionJournal(writer, location) {
26372
26507
  return {
26373
26508
  transcriptPath: join25(location.winterHome, "projects", location.projectKey, `${location.sessionId}.jsonl`),
@@ -26506,117 +26641,13 @@ function resolveProductionStoreHome(config, env) {
26506
26641
  // src/provider/credential-api.ts
26507
26642
  import {
26508
26643
  ANTHROPIC_CONSOLE_ACCOUNT_ID,
26509
- CredentialResolutionError as CredentialResolutionError2,
26644
+ CredentialResolutionError,
26510
26645
  WinterProviderResolutionError as WinterProviderResolutionError3,
26511
26646
  startAnthropicConsoleBrokerLogin,
26512
26647
  startCodexLogin,
26513
26648
  startXaiLogin
26514
26649
  } from "@yanlinglabs/winter-provider-runtime";
26515
26650
 
26516
- // src/provider/keychain-store.ts
26517
- import { CredentialResolutionError, redactRef } from "@yanlinglabs/winter-provider-runtime";
26518
- import { DEFAULT_KEYCHAIN_SERVICE } from "@yanlinglabs/winter-agent-sdk";
26519
- function defaultSecretsBackend() {
26520
- const secrets = globalThis.Bun?.secrets;
26521
- if (secrets === undefined) {
26522
- throw new CredentialResolutionError("unsupported", "the Keychain credential store requires Bun.secrets, which is unavailable in this runtime");
26523
- }
26524
- return secrets;
26525
- }
26526
- function createKeychainCredentialStore(service = DEFAULT_KEYCHAIN_SERVICE, opts = {}) {
26527
- const backend = () => opts.secrets ?? defaultSecretsBackend();
26528
- return {
26529
- async get(ref) {
26530
- if (ref.kind !== "keychain")
26531
- return null;
26532
- let raw;
26533
- try {
26534
- raw = await backend().get({ service: ref.service ?? service, name: ref.account });
26535
- } catch (err) {
26536
- if (err instanceof CredentialResolutionError)
26537
- throw err;
26538
- throw new CredentialResolutionError("io", `keychain lookup failed for ${redactRef(ref)}`);
26539
- }
26540
- if (raw === null)
26541
- return null;
26542
- let parsed;
26543
- try {
26544
- parsed = JSON.parse(raw);
26545
- } catch {
26546
- throw new CredentialResolutionError("malformed", `the keychain record for ${redactRef(ref)} is not valid JSON credential material`);
26547
- }
26548
- const material = coerceMaterial(parsed);
26549
- if (material === undefined)
26550
- throw new CredentialResolutionError("malformed", `the keychain record for ${redactRef(ref)} is not a recognized credential material shape`);
26551
- return material;
26552
- },
26553
- async set(ref, material) {
26554
- try {
26555
- await backend().set({ service: ref.service ?? service, name: ref.account, value: JSON.stringify(material) });
26556
- } catch (err) {
26557
- if (err instanceof CredentialResolutionError)
26558
- throw err;
26559
- throw new CredentialResolutionError("io", `keychain write failed for ${redactRef(ref)}`);
26560
- }
26561
- },
26562
- async delete(ref) {
26563
- try {
26564
- await backend().delete({ service: ref.service ?? service, name: ref.account });
26565
- } catch (err) {
26566
- if (err instanceof CredentialResolutionError)
26567
- throw err;
26568
- throw new CredentialResolutionError("io", `keychain delete failed for ${redactRef(ref)}`);
26569
- }
26570
- }
26571
- };
26572
- }
26573
- function createKeychainSecretReader(service = DEFAULT_KEYCHAIN_SERVICE, opts = {}) {
26574
- const backend = () => opts.secrets ?? defaultSecretsBackend();
26575
- return async (ref) => {
26576
- try {
26577
- return await backend().get({ service: ref.service ?? service, name: ref.account });
26578
- } catch (err) {
26579
- if (err instanceof CredentialResolutionError)
26580
- throw err;
26581
- throw new CredentialResolutionError("io", `keychain lookup failed for ${redactRef(ref)}`);
26582
- }
26583
- };
26584
- }
26585
- function keychainAccountName(providerId, accountId) {
26586
- return `${providerId}:${accountId}`;
26587
- }
26588
- var MATERIAL_KINDS = new Set(["api-key", "bearer", "oauth", "aws", "gcp-service-account", "gcp-access-token"]);
26589
- function coerceMaterial(value) {
26590
- if (typeof value !== "object" || value === null)
26591
- return;
26592
- const v = value;
26593
- if (typeof v.kind !== "string" || !MATERIAL_KINDS.has(v.kind))
26594
- return;
26595
- switch (v.kind) {
26596
- case "api-key":
26597
- return typeof v.key === "string" ? { kind: "api-key", key: v.key } : undefined;
26598
- case "bearer":
26599
- return typeof v.token === "string" ? { kind: "bearer", token: v.token, ...typeof v.expiresAt === "number" ? { expiresAt: v.expiresAt } : {} } : undefined;
26600
- case "oauth":
26601
- return typeof v.accessToken === "string" ? {
26602
- kind: "oauth",
26603
- accessToken: v.accessToken,
26604
- ...typeof v.refreshToken === "string" ? { refreshToken: v.refreshToken } : {},
26605
- ...typeof v.expiresAt === "number" ? { expiresAt: v.expiresAt } : {},
26606
- ...typeof v.accountId === "string" ? { accountId: v.accountId } : {},
26607
- ...typeof v.idToken === "string" ? { idToken: v.idToken } : {}
26608
- } : undefined;
26609
- case "aws":
26610
- return typeof v.accessKeyId === "string" && typeof v.secretAccessKey === "string" ? { kind: "aws", accessKeyId: v.accessKeyId, secretAccessKey: v.secretAccessKey, ...typeof v.sessionToken === "string" ? { sessionToken: v.sessionToken } : {} } : undefined;
26611
- case "gcp-service-account":
26612
- return typeof v.clientEmail === "string" && typeof v.privateKeyPem === "string" && typeof v.tokenUri === "string" ? { kind: "gcp-service-account", clientEmail: v.clientEmail, privateKeyPem: v.privateKeyPem, tokenUri: v.tokenUri } : undefined;
26613
- case "gcp-access-token":
26614
- return typeof v.token === "string" ? { kind: "gcp-access-token", token: v.token } : undefined;
26615
- default:
26616
- return;
26617
- }
26618
- }
26619
-
26620
26651
  // src/provider/selection.ts
26621
26652
  import { WinterProviderResolutionError as WinterProviderResolutionError2, createRegistry } from "@yanlinglabs/winter-provider-runtime";
26622
26653
  import { DEFAULT_PROVIDER_STALL_TIMEOUT_MS } from "@yanlinglabs/winter-agent-sdk";
@@ -26829,22 +26860,22 @@ var FORBIDDEN = /[\u0000-\u001f\u007f]/;
26829
26860
  function assertLocator(locator) {
26830
26861
  const { providerId, accountId, service } = locator;
26831
26862
  if (typeof providerId !== "string" || providerId.trim().length === 0)
26832
- throw new CredentialResolutionError2("malformed", "a provider credential needs a non-empty providerId");
26863
+ throw new CredentialResolutionError("malformed", "a provider credential needs a non-empty providerId");
26833
26864
  if (typeof accountId !== "string" || accountId.trim().length === 0)
26834
- throw new CredentialResolutionError2("malformed", `a provider credential for "${providerId}" needs a non-empty accountId`);
26865
+ throw new CredentialResolutionError("malformed", `a provider credential for "${providerId}" needs a non-empty accountId`);
26835
26866
  if (providerId.includes(":")) {
26836
- throw new CredentialResolutionError2("malformed", `providerId "${providerId}" contains ":", which is the separator in the "<providerId>:<accountId>" keychain account name (R6-10) and would make the record ambiguous`);
26867
+ throw new CredentialResolutionError("malformed", `providerId "${providerId}" contains ":", which is the separator in the "<providerId>:<accountId>" keychain account name (R6-10) and would make the record ambiguous`);
26837
26868
  }
26838
26869
  if (FORBIDDEN.test(providerId) || FORBIDDEN.test(accountId) || service !== undefined && FORBIDDEN.test(service)) {
26839
- throw new CredentialResolutionError2("malformed", `a provider credential locator may not contain control characters (provider "${providerId}")`);
26870
+ throw new CredentialResolutionError("malformed", `a provider credential locator may not contain control characters (provider "${providerId}")`);
26840
26871
  }
26841
26872
  if (service !== undefined && service.trim().length === 0)
26842
- throw new CredentialResolutionError2("malformed", `an explicit keychain service for provider "${providerId}" may not be blank; omit it to use the store's default`);
26873
+ throw new CredentialResolutionError("malformed", `an explicit keychain service for provider "${providerId}" may not be blank; omit it to use the store's default`);
26843
26874
  }
26844
26875
  function assertMaterial(material, ref) {
26845
26876
  const empty = (value) => value === undefined || value.trim().length === 0;
26846
26877
  const bad = (field) => {
26847
- throw new CredentialResolutionError2("malformed", `the ${material.kind} credential for ${redactCredentialRef(ref)} has an empty "${field}"; an empty secret is stored happily and then fails as an opaque provider rejection on every request`);
26878
+ throw new CredentialResolutionError("malformed", `the ${material.kind} credential for ${redactCredentialRef(ref)} has an empty "${field}"; an empty secret is stored happily and then fails as an opaque provider rejection on every request`);
26848
26879
  };
26849
26880
  switch (material.kind) {
26850
26881
  case "api-key":
@@ -26880,13 +26911,13 @@ function assertMaterial(material, ref) {
26880
26911
  }
26881
26912
  }
26882
26913
  function redactedFailure(operation, ref, err) {
26883
- const code = err instanceof CredentialResolutionError2 ? err.code : "io";
26914
+ const code = err instanceof CredentialResolutionError ? err.code : "io";
26884
26915
  const kind = err instanceof Error ? err.name : typeof err;
26885
- return new CredentialResolutionError2(code, `could not ${operation} the credential for ${redactCredentialRef(ref)}: the store failed with a ${kind} (its message is withheld because a store's error text may quote the record)`);
26916
+ return new CredentialResolutionError(code, `could not ${operation} the credential for ${redactCredentialRef(ref)}: the store failed with a ${kind} (its message is withheld because a store's error text may quote the record)`);
26886
26917
  }
26887
26918
  function statusFromThrow(err, ref, providerId) {
26888
26919
  const locator = redactCredentialRef(ref);
26889
- if (err instanceof CredentialResolutionError2) {
26920
+ if (err instanceof CredentialResolutionError) {
26890
26921
  if (err.code === "malformed")
26891
26922
  return { ok: false, code: "invalid", message: `the credential at ${locator} is not valid material for provider "${providerId}"` };
26892
26923
  if (err.code === "io")
@@ -27197,4 +27228,4 @@ function selectClassifierRoute2(config, descriptorOf) {
27197
27228
  return { kind: "worker-eligible" };
27198
27229
  }
27199
27230
 
27200
- export { ChildResumeModeIncomparableError, resolveChildResumeMode, ensureDefaultMessagingRuntimeRegistered, buildContinuationChain, AGENT_TOOL_CANONICAL_NAME, computeActiveSlotSet, resolveSlotToProvider, createRpcBridge2, shellWords, escapeRegExpLiteral, validatePermissionRuleString, registerTool2, getRegisteredTool, listRegisteredTools, rebrandStandingServerTools, mcpServerHasRegistrations, onCompaction2, STRUCTURED_OUTPUT_TOOL_NAME, looksLikeCommand, registerChildEngineFactory, matchFileRule, rawToRuleValue, sourceRule, resolveFileRuleAbsolutePath, resolveFileRuleAbsoluteGlobText, resolvesWithinPluginRoot, parseFrontmatter, parseAgentDefinitionFile, allowedAgentTypesFromTools, validateAgentDefinition, WinterPathsError2, sessionTempDir2, ensureTasksDir2, resolveProjectDirName2, buildForkDirectiveText, buildForkInitialMessages, capBytes, neutralizeReminderTags, readWhole, readCappedLinesAndBytes, registerAttachmentRenderer, attachmentsIn, gitInstructionsEnabled, isUserInvocable, buildSkillListing, registerPluginAgents, clearPluginAgents, registerSkillSessionRuntime, clearSkillSessionRuntime, buildHookEntriesFromSettings, defaultTrustSource, formatValidationErrors, createSchemaValidatorCache, killAllTaskProcessGroups, runGit, listWorktrees, canonicalAliases, WINTER_CANONICAL_ALIASES, DEFAULT_SKILL_BODY_BYTES, skillNameError, pluginNameError, capBytes2, parseSkillFile, validateSkillsOption, isSkillEnabled, autoSkillPermissionEntries, setHostWorkflowWorkerCommand, listWorkflowsForListing, buildWorkflowSkillPrompt, DEFAULT_CONTEXT_WINDOW_TOKENS2, ProviderTurnError, isProviderTurnError, createContextAccountant2, runEngine2, WINTER_SUMMARY_INSTRUCTION, WINTER_PREFIX_SUMMARY_INSTRUCTION, CARRIED_SUMMARY_NOTE, retainedExchangesNote, buildSummaryInstruction, redactForSummary, summarize, summarizeOverPrefix, recordedProviderSystems2, resetRecordedProviderSystems2, echoProvider2, scriptedProvider2, stubExecutor2, P5_FIXTURE_SKILL_NAME2, P5_WORKFLOW_SCRIPT2, isTestProviderName2, testProviderForNamespace, testProviderByName2, SUBAGENT_CHILD_PROBE_TEXT2, MCP_SDK_TEST_SERVER_NAME2, MCP_SDK_TEST_TOOL_NAME2, BGTASK_TEST_TOOL_NAME2, registerBgTaskTestTool2, ResumeTargetError2, ResumeTruncationError2, findContinueTarget2, findResumeTarget2, toDialectEntries2, truncateAt2, rebuildProviderMessages2, userEntry2, winterMessageIdFor2, assistantEntry2, CLAUDE_COMPACT_SUMMARY_PREAMBLE2, claudeCompactBoundaryEntry2, claudeCompactSummaryEntry2, TranscriptWriterError2, TranscriptWriter2, RUNTIME_ENGINE_VERSION2, childTranscriptSubpath, buildChildTranscriptWriter, listChildAgentIds, resolveEngineSession2, resolveProductionWinterHome2, resolveProductionStoreHome, createKeychainCredentialStore, createKeychainSecretReader, resolveSessionProvider, redactCredentialRef, createProviderContext, providerCredentialRef2, storeProviderCredential2, deleteProviderCredential2, validateProviderCredential2, CLASSIFIER_TOOL_NAME2, CLASSIFIER_VERDICT_SCHEMA2, CLASSIFIER_NO_VERDICT_REASONS2, DEFAULT_MAX_CONTEXT_CHARS2, CLASSIFIER_SYSTEM_PROMPT2, DEFAULT_CLASSIFIER_TIMEOUT_MS2, createModelClassifier2, selectClassifierRoute2 };
27231
+ export { ChildResumeModeIncomparableError, resolveChildResumeMode, ensureDefaultMessagingRuntimeRegistered, buildContinuationChain, AGENT_TOOL_CANONICAL_NAME, computeActiveSlotSet, resolveSlotToProvider, createRpcBridge2, shellWords, escapeRegExpLiteral, validatePermissionRuleString, registerTool2, getRegisteredTool, listRegisteredTools, rebrandStandingServerTools, mcpServerHasRegistrations, onCompaction2, STRUCTURED_OUTPUT_TOOL_NAME, looksLikeCommand, capBytes, neutralizeReminderTags, readWhole, readCappedLinesAndBytes, registerAttachmentRenderer, attachmentsIn, registerChildEngineFactory, matchFileRule, rawToRuleValue, sourceRule, resolveFileRuleAbsolutePath, resolveFileRuleAbsoluteGlobText, resolvesWithinPluginRoot, parseFrontmatter, parseAgentDefinitionFile, allowedAgentTypesFromTools, validateAgentDefinition, WinterPathsError2, sessionTempDir2, ensureTasksDir2, resolveProjectDirName2, buildForkDirectiveText, buildForkInitialMessages, gitInstructionsEnabled, isUserInvocable, buildSkillListing, registerPluginAgents, clearPluginAgents, registerSkillSessionRuntime, clearSkillSessionRuntime, buildHookEntriesFromSettings, defaultTrustSource, formatValidationErrors, createSchemaValidatorCache, killAllTaskProcessGroups, runGit, listWorktrees, canonicalAliases, WINTER_CANONICAL_ALIASES, DEFAULT_SKILL_BODY_BYTES, skillNameError, pluginNameError, capBytes2, parseSkillFile, validateSkillsOption, isSkillEnabled, autoSkillPermissionEntries, setHostWorkflowWorkerCommand, listWorkflowsForListing, buildWorkflowSkillPrompt, DEFAULT_CONTEXT_WINDOW_TOKENS2, ProviderTurnError, isProviderTurnError, createContextAccountant2, runEngine2, WINTER_SUMMARY_INSTRUCTION, WINTER_PREFIX_SUMMARY_INSTRUCTION, CARRIED_SUMMARY_NOTE, retainedExchangesNote, buildSummaryInstruction, redactForSummary, summarize, summarizeOverPrefix, recordedProviderSystems2, resetRecordedProviderSystems2, echoProvider2, scriptedProvider2, stubExecutor2, P5_FIXTURE_SKILL_NAME2, P5_WORKFLOW_SCRIPT2, isTestProviderName2, testProviderForNamespace, testProviderByName2, SUBAGENT_CHILD_PROBE_TEXT2, MCP_SDK_TEST_SERVER_NAME2, MCP_SDK_TEST_TOOL_NAME2, BGTASK_TEST_TOOL_NAME2, registerBgTaskTestTool2, ResumeTargetError2, ResumeTruncationError2, findContinueTarget2, findResumeTarget2, toDialectEntries2, truncateAt2, rebuildProviderMessages2, userEntry2, winterMessageIdFor2, assistantEntry2, CLAUDE_COMPACT_SUMMARY_PREAMBLE2, claudeCompactBoundaryEntry2, claudeCompactSummaryEntry2, TranscriptWriterError2, TranscriptWriter2, RUNTIME_ENGINE_VERSION2, childTranscriptSubpath, buildChildTranscriptWriter, listChildAgentIds, resolveEngineSession2, resolveProductionWinterHome2, resolveProductionStoreHome, resolveSessionProvider, redactCredentialRef, createProviderContext, providerCredentialRef2, storeProviderCredential2, deleteProviderCredential2, validateProviderCredential2, CLASSIFIER_TOOL_NAME2, CLASSIFIER_VERDICT_SCHEMA2, CLASSIFIER_NO_VERDICT_REASONS2, DEFAULT_MAX_CONTEXT_CHARS2, CLASSIFIER_SYSTEM_PROMPT2, DEFAULT_CLASSIFIER_TIMEOUT_MS2, createModelClassifier2, selectClassifierRoute2 };
@@ -15,6 +15,12 @@ import {
15
15
  mcpServerHasRegistrations,
16
16
  STRUCTURED_OUTPUT_TOOL_NAME,
17
17
  looksLikeCommand,
18
+ capBytes,
19
+ neutralizeReminderTags,
20
+ readWhole,
21
+ readCappedLinesAndBytes,
22
+ registerAttachmentRenderer,
23
+ attachmentsIn,
18
24
  registerChildEngineFactory,
19
25
  matchFileRule,
20
26
  rawToRuleValue,
@@ -29,12 +35,6 @@ import {
29
35
  resolveProjectDirName2,
30
36
  buildForkDirectiveText,
31
37
  buildForkInitialMessages,
32
- capBytes,
33
- neutralizeReminderTags,
34
- readWhole,
35
- readCappedLinesAndBytes,
36
- registerAttachmentRenderer,
37
- attachmentsIn,
38
38
  gitInstructionsEnabled,
39
39
  isUserInvocable,
40
40
  buildSkillListing,
@@ -79,15 +79,23 @@ import {
79
79
  childTranscriptSubpath,
80
80
  buildChildTranscriptWriter,
81
81
  listChildAgentIds,
82
- createKeychainCredentialStore,
83
- createKeychainSecretReader,
84
82
  resolveSessionProvider,
85
83
  redactCredentialRef,
86
84
  createProviderContext,
87
85
  providerCredentialRef2,
88
86
  createModelClassifier2,
89
87
  selectClassifierRoute2
90
- } from "./index-jz5d90c6.js";
88
+ } from "./index-51673qbw.js";
89
+ import {
90
+ createKeychainCredentialStore,
91
+ createKeychainSecretReader,
92
+ createHostCredentialChannel,
93
+ createHostBrokeredCredentialStore,
94
+ createHostBrokeredSecretReader,
95
+ mcpOAuthTestStoreEnvName,
96
+ resolveSessionMcpOAuthStore,
97
+ createHostBrokeredMcpOAuthStore
98
+ } from "./index-z6s3r4xm.js";
91
99
  import {
92
100
  createInMemoryChannel2
93
101
  } from "./index-97t2rmtf.js";
@@ -1700,8 +1708,8 @@ function apiProviderFor(providerId) {
1700
1708
  return providerId === undefined ? undefined : API_PROVIDER_BY_PROVIDER_ID[providerId];
1701
1709
  }
1702
1710
  var DEFAULT_PROVIDER_ACCOUNT_ID = "default";
1703
- function createProductionCredentialStore(config, env, home) {
1704
- const keychain = createKeychainCredentialStore(resolveSessionKeychainService(config));
1711
+ function createProductionCredentialStore(config, env, home, keychainMember) {
1712
+ const keychain = keychainMember ?? createKeychainCredentialStore(resolveSessionKeychainService(config));
1705
1713
  return createCompositeCredentialStore([
1706
1714
  {
1707
1715
  ...keychain,
@@ -2201,7 +2209,8 @@ var ALL_TIER_REFUSED_ENV = [
2201
2209
  storeHomeEnvName(WINTER_BRAND5),
2202
2210
  pluginCacheDirEnvName(WINTER_BRAND5),
2203
2211
  providerManagedByHostEnvName(WINTER_BRAND5),
2204
- disableCronEnvName(WINTER_BRAND5)
2212
+ disableCronEnvName(WINTER_BRAND5),
2213
+ mcpOAuthTestStoreEnvName(WINTER_BRAND5)
2205
2214
  ];
2206
2215
  function escapeRegExpLiteral2(literal) {
2207
2216
  return literal.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
@@ -5469,6 +5478,7 @@ function describeCatalogModel(catalog, model, providerId) {
5469
5478
  async function buildProductionWiring(opts) {
5470
5479
  const { config, env } = opts;
5471
5480
  const brand = config.brand ?? WINTER_BRAND15;
5481
+ const mcpOAuthTestStoreFile = env[mcpOAuthTestStoreEnvName(brand)];
5472
5482
  const winterHome = opts.winterHome ?? config.winterHome ?? resolveWinterHome2(env, brand);
5473
5483
  const storeHomeEnv = env[storeHomeEnvName2(brand)];
5474
5484
  const storeHome = config.storeHome ?? (isUnset(storeHomeEnv) ? undefined : storeHomeEnv);
@@ -5643,9 +5653,15 @@ async function buildProductionWiring(opts) {
5643
5653
  ...skillListingBudgetFraction !== undefined ? { budgetFraction: skillListingBudgetFraction } : {},
5644
5654
  ...config.contextWindowTokens !== undefined ? { contextWindowTokens: config.contextWindowTokens } : {}
5645
5655
  });
5656
+ const hostCredentialChannel = config.hostCredentials === true ? createHostCredentialChannel() : undefined;
5657
+ const hostCredentialOptions = hostCredentialChannel !== undefined ? {
5658
+ credentials: createProductionCredentialStore(config, env, opts.provider?.home ?? env["HOME"] ?? "", createHostBrokeredCredentialStore(hostCredentialChannel)),
5659
+ readKeychainSecret: createHostBrokeredSecretReader(hostCredentialChannel)
5660
+ } : {};
5646
5661
  const providerWiring = buildSessionProvider({
5647
5662
  config,
5648
5663
  env,
5664
+ ...hostCredentialOptions,
5649
5665
  providerSettings: () => providerSettingsFrom(settingsGetter()),
5650
5666
  resolveSlot: (requested, currentModelKey) => resolveSlot(requested, currentModelKey),
5651
5667
  advisorModelSetting: () => settingsGetter()?.advisor?.model,
@@ -5912,6 +5928,8 @@ async function buildProductionWiring(opts) {
5912
5928
  ...pluginWorkflows.length > 0 ? { pluginWorkflows } : {},
5913
5929
  ...settingSources !== undefined ? { settingSources } : {},
5914
5930
  extraMcpServerSources,
5931
+ mcpOAuthStore: hostCredentialChannel !== undefined ? createHostBrokeredMcpOAuthStore(hostCredentialChannel) : resolveSessionMcpOAuthStore({ keychainService: resolveSessionKeychainService(config), testStoreFile: mcpOAuthTestStoreFile }),
5932
+ ...hostCredentialChannel !== undefined ? { hostCredentialChannel } : {},
5915
5933
  initSlashCommands,
5916
5934
  initSkills,
5917
5935
  initPlugins,