@yanlinglabs/winter-agent-runtime 0.0.29 → 0.0.31

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 (42) 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-gmxxtqpe.js → index-0xqzk1hm.js} +30 -12
  5. package/dist/{index-8jgwp1px.js → index-9s68t0pd.js} +170 -11
  6. package/dist/index-bgeqc7ax.js +52 -0
  7. package/dist/{index-eyddzcgs.js → index-c7kq6erg.js} +2 -2
  8. package/dist/{index-jz5d90c6.js → index-d4pmmr27.js} +191 -128
  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 +9 -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/rpc/bridge.d.ts +5 -1
  38. package/dist/store/dialect.d.ts +1 -1
  39. package/dist/testing.js +5 -3
  40. package/dist/version.d.ts +1 -1
  41. package/dist/version.js +1 -1
  42. package/package.json +8 -4
@@ -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";
@@ -21,8 +31,9 @@ import {
21
31
  // src/rpc/bridge.ts
22
32
  import { randomUUID } from "node:crypto";
23
33
  import { WinterRpcError, WinterRpcTimeoutError } from "@yanlinglabs/winter-agent-sdk";
24
- function createRpcBridge2(output) {
34
+ function createRpcBridge2(output, bridgeOpts = {}) {
25
35
  const pending = new Map;
36
+ let held = bridgeOpts.holdUntilOpen === true ? new Map : undefined;
26
37
  let closed = false;
27
38
  return {
28
39
  request(subtype, payload, opts) {
@@ -39,6 +50,7 @@ function createRpcBridge2(output) {
39
50
  const timeoutMs = opts.timeoutMs;
40
51
  entry.timer = setTimeout(() => {
41
52
  pending.delete(requestId);
53
+ held?.delete(requestId);
42
54
  entry.detach?.();
43
55
  reject(new WinterRpcTimeoutError(subtype, timeoutMs));
44
56
  }, timeoutMs);
@@ -52,7 +64,8 @@ function createRpcBridge2(output) {
52
64
  pending.delete(requestId);
53
65
  if (entry.timer !== undefined)
54
66
  clearTimeout(entry.timer);
55
- output.write({ type: "control_cancel_request", requestId });
67
+ if (held?.delete(requestId) !== true)
68
+ output.write({ type: "control_cancel_request", requestId });
56
69
  reject(new WinterRpcError("cancelled", `the '${subtype}' request was cancelled: the runtime stopped waiting for it`));
57
70
  };
58
71
  signal.addEventListener("abort", onAbort, { once: true });
@@ -60,9 +73,20 @@ function createRpcBridge2(output) {
60
73
  }
61
74
  pending.set(requestId, entry);
62
75
  const frame = { type: "control_request", requestId, subtype, payload };
63
- output.write(frame);
76
+ if (held !== undefined)
77
+ held.set(requestId, frame);
78
+ else
79
+ output.write(frame);
64
80
  });
65
81
  },
82
+ open() {
83
+ if (held === undefined)
84
+ return;
85
+ const queued = [...held.values()];
86
+ held = undefined;
87
+ for (const frame of queued)
88
+ output.write(frame);
89
+ },
66
90
  ownsRequest(requestId) {
67
91
  return pending.has(requestId);
68
92
  },
@@ -92,6 +116,7 @@ function createRpcBridge2(output) {
92
116
  entry.reject(err);
93
117
  }
94
118
  pending.clear();
119
+ held?.clear();
95
120
  },
96
121
  cancel(requestId) {
97
122
  const entry = pending.get(requestId);
@@ -101,6 +126,7 @@ function createRpcBridge2(output) {
101
126
  clearTimeout(entry.timer);
102
127
  entry.detach?.();
103
128
  pending.delete(requestId);
129
+ held?.delete(requestId);
104
130
  }
105
131
  };
106
132
  }
@@ -4946,6 +4972,14 @@ function validateServerConfig(raw) {
4946
4972
  return { ok: false, reason: `MCP server config 'versionNegotiation' must be "legacy", "auto" or { "pin": "<YYYY-MM-DD revision>" }; got ${JSON.stringify(negotiation)}` };
4947
4973
  }
4948
4974
  }
4975
+ const oauth = raw.oauth;
4976
+ if (oauth !== undefined) {
4977
+ if (type !== "http" && type !== "sse")
4978
+ return { ok: false, reason: "'oauth' applies only to a remote (http/sse) MCP server" };
4979
+ const reason = validateMcpOAuthConfig2(oauth, raw.url);
4980
+ if (reason !== undefined)
4981
+ return { ok: false, reason };
4982
+ }
4949
4983
  return { ok: true, config: raw };
4950
4984
  }
4951
4985
  function resolveMcpServerSources(sources, opts) {
@@ -5109,6 +5143,24 @@ function toolInfoToDefinition(tool) {
5109
5143
  ...tool._meta !== undefined ? { _meta: tool._meta } : {}
5110
5144
  };
5111
5145
  }
5146
+ function hasStaticAuthorization(config) {
5147
+ if (config.type !== "http" && config.type !== "sse")
5148
+ return false;
5149
+ return Object.keys(config.headers ?? {}).some((h) => h.toLowerCase() === "authorization");
5150
+ }
5151
+ function sessionAuthFor(name, config, oauth) {
5152
+ if (oauth === undefined || config.type !== "http" && config.type !== "sse" || hasStaticAuthorization(config))
5153
+ return;
5154
+ try {
5155
+ return createSessionAuthProvider({ serverName: name, serverUrl: config.url, oauth });
5156
+ } catch {
5157
+ return;
5158
+ }
5159
+ }
5160
+ function displayScope(scope) {
5161
+ const cleaned = (scope ?? "").replace(/[^A-Za-z0-9:._/ -]/g, "").trim().slice(0, 200);
5162
+ return cleaned === "" ? "unstated" : cleaned;
5163
+ }
5112
5164
  function createInMemoryDiscoveryCache() {
5113
5165
  const map = new Map;
5114
5166
  return {
@@ -5199,6 +5251,15 @@ function createMcpLifecycle(deps) {
5199
5251
  });
5200
5252
  return { output: capped.text, ...result.isError === true ? { isError: true } : {} };
5201
5253
  } catch (err) {
5254
+ const authFailure = classifyMcpAuthFailure(err);
5255
+ if (authFailure === "needs_auth")
5256
+ return { output: `Error: ${markNeedsAuth(slot, slot.client, err)}`, isError: true };
5257
+ if (authFailure === "insufficient_scope") {
5258
+ const challenge = insufficientScopeOf(err);
5259
+ slot.auth?.reportInsufficientScope(challenge?.requiredScope);
5260
+ const hint = deps.oauth?.signInHint(slot.name) ?? "";
5261
+ 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 };
5262
+ }
5202
5263
  const message = err instanceof Error ? err.message : String(err);
5203
5264
  return { output: `Error: mcp tool call "${toolName}" on server "${slot.name}" failed: ${message}`, isError: true };
5204
5265
  }
@@ -5209,6 +5270,18 @@ function createMcpLifecycle(deps) {
5209
5270
  function isCurrentAttempt(slot, gen) {
5210
5271
  return slots.get(slot.name) === slot && slot.gen === gen;
5211
5272
  }
5273
+ function markNeedsAuth(slot, failed, err) {
5274
+ const hint = deps.oauth?.signInHint(slot.name) ?? "";
5275
+ 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();
5276
+ if (slots.get(slot.name) !== slot || failed === undefined || slot.client !== failed)
5277
+ return message;
5278
+ slot.gen++;
5279
+ slot.client = undefined;
5280
+ failed.close().catch(() => {});
5281
+ unregisterMcpServerTools(slot.name);
5282
+ setSlotState(slot.name, "needsAuth", { errorCode: "needs_auth", error: message });
5283
+ return `${message} Its tools are unavailable until then.`;
5284
+ }
5212
5285
  function beginAttempt(slot) {
5213
5286
  return ++slot.gen;
5214
5287
  }
@@ -5241,6 +5314,7 @@ function createMcpLifecycle(deps) {
5241
5314
  async function connectSlotForReal(slot, gen) {
5242
5315
  let connection;
5243
5316
  slot.toolListStale = false;
5317
+ const auth = sessionAuthFor(slot.name, slot.config, deps.oauth);
5244
5318
  const client = await connectMcpServer2({
5245
5319
  name: slot.name,
5246
5320
  config: slot.config,
@@ -5248,7 +5322,8 @@ function createMcpLifecycle(deps) {
5248
5322
  elicitationAsk: deps.elicitationAsk,
5249
5323
  onToolListChanged: onToolListChangedFor(slot, () => connection),
5250
5324
  ...deps.inProcessServers?.[slot.name] !== undefined ? { inProcessServer: deps.inProcessServers[slot.name] } : {},
5251
- ...deps.cwd !== undefined ? { cwd: deps.cwd } : {}
5325
+ ...deps.cwd !== undefined ? { cwd: deps.cwd } : {},
5326
+ ...auth !== undefined ? { auth } : {}
5252
5327
  });
5253
5328
  connection = client;
5254
5329
  const tools = await client.listTools();
@@ -5257,6 +5332,7 @@ function createMcpLifecycle(deps) {
5257
5332
  return false;
5258
5333
  }
5259
5334
  slot.client = client;
5335
+ slot.auth = auth;
5260
5336
  slot.toolNames = tools.map((t) => t.name);
5261
5337
  slot.savedTools = tools;
5262
5338
  registerMcpServerTools(slot.name, tools.map(toolInfoToDefinition), {
@@ -5305,6 +5381,9 @@ function createMcpLifecycle(deps) {
5305
5381
  setSlotState(slot.name, "connected", { toolNames });
5306
5382
  }
5307
5383
  async function start() {
5384
+ await launch();
5385
+ }
5386
+ function launch() {
5308
5387
  const alwaysLoadWaits = [];
5309
5388
  for (const slot of slots.values()) {
5310
5389
  if (slot.state === "disabled")
@@ -5319,10 +5398,18 @@ function createMcpLifecycle(deps) {
5319
5398
  }
5320
5399
  }
5321
5400
  if (deps.envConfig.connectionNonblocking === false) {
5322
- await stateSource.waitForPending(undefined, deps.envConfig.connectTimeoutMs);
5323
- } else if (alwaysLoadWaits.length > 0) {
5324
- await Promise.race([Promise.all(alwaysLoadWaits), new Promise((resolve) => setTimeout(resolve, deps.envConfig.connectTimeoutMs).unref?.())]);
5401
+ if (!stateSource.snapshot().some((server) => server.state === "pending"))
5402
+ return;
5403
+ return stateSource.waitForPending(undefined, deps.envConfig.connectTimeoutMs).then(() => {
5404
+ return;
5405
+ });
5406
+ }
5407
+ if (alwaysLoadWaits.length > 0) {
5408
+ return Promise.race([Promise.all(alwaysLoadWaits).then(() => {
5409
+ return;
5410
+ }), new Promise((resolve) => setTimeout(resolve, deps.envConfig.connectTimeoutMs).unref?.())]);
5325
5411
  }
5412
+ return;
5326
5413
  }
5327
5414
  async function dispose() {
5328
5415
  for (const slot of slots.values()) {
@@ -5386,6 +5473,8 @@ function createMcpLifecycle(deps) {
5386
5473
  setSlotState(name, "connected", { toolNames: slot.toolNames });
5387
5474
  return { ok: true, toolNames: slot.toolNames };
5388
5475
  } catch (err) {
5476
+ if (classifyMcpAuthFailure(err) === "needs_auth")
5477
+ return { ok: false, reason: markNeedsAuth(slot, client, err) };
5389
5478
  const message = err instanceof Error ? err.message : String(err);
5390
5479
  return { ok: false, reason: `refreshing server "${name}" failed: ${message}` };
5391
5480
  }
@@ -5455,6 +5544,7 @@ function createMcpLifecycle(deps) {
5455
5544
  stateSource,
5456
5545
  controlSeam: createMcpControlSeam(internals, { ...deps.reservedServerName !== undefined ? { reservedServerName: deps.reservedServerName } : {} }),
5457
5546
  start,
5547
+ launch,
5458
5548
  dispose,
5459
5549
  listConnectedServerNames,
5460
5550
  getConnectedClient,
@@ -5462,6 +5552,68 @@ function createMcpLifecycle(deps) {
5462
5552
  };
5463
5553
  }
5464
5554
 
5555
+ // src/mcp-auth/engine-wiring.ts
5556
+ import { MCP_OAUTH_REFRESH_SUBTYPE, WinterRpcError as WinterRpcError2 } from "@yanlinglabs/winter-agent-sdk";
5557
+ var MCP_OAUTH_HOST_REFRESH_TIMEOUT_MS = 60000;
5558
+ function isAnswer(value) {
5559
+ if (typeof value !== "object" || value === null)
5560
+ return false;
5561
+ const v = value;
5562
+ return v.ok === true || v.ok === false && (v.reason === "needs_auth" || v.reason === "transient");
5563
+ }
5564
+ function createSessionMcpOAuth(opts) {
5565
+ return {
5566
+ store: opts.store,
5567
+ ...opts.hostOwnsRefresh === true ? { hostOwnsRefresh: true } : {},
5568
+ signInHint: (serverName) => mcpSignInHint(opts.brand, serverName),
5569
+ askHost: async (request) => {
5570
+ try {
5571
+ const answer = await opts.sender.request(MCP_OAUTH_REFRESH_SUBTYPE, request, { timeoutMs: MCP_OAUTH_HOST_REFRESH_TIMEOUT_MS });
5572
+ return isAnswer(answer) ? answer : { ok: false, reason: "transient" };
5573
+ } catch (err) {
5574
+ if (err instanceof WinterRpcError2 && err.code === "unhandled_subtype")
5575
+ return "unhandled";
5576
+ return { ok: false, reason: "transient" };
5577
+ }
5578
+ }
5579
+ };
5580
+ }
5581
+ function needsAuthServers(states) {
5582
+ return (states ?? []).filter((s) => s.state === "needsAuth").map((s) => s.name).sort();
5583
+ }
5584
+ function needsAuthToolHint(states, toolName, brand) {
5585
+ const server = needsAuthServers(states).find((name) => toolName.startsWith(`mcp__${name}__`));
5586
+ if (server === undefined)
5587
+ return "";
5588
+ return `. The MCP server '${server}' needs sign-in, so its tools are unavailable. ${mcpSignInHint(brand, server)}`;
5589
+ }
5590
+ var SERVER_PLACEHOLDER = "<server>";
5591
+ function displayName(name) {
5592
+ return name.replace(/[^A-Za-z0-9_.:-]/g, "_").slice(0, 64);
5593
+ }
5594
+ registerAttachmentRenderer("mcp_needs_auth", (attachment) => {
5595
+ const servers = Array.isArray(attachment.servers) ? attachment.servers.filter((s) => typeof s === "string").map(displayName) : [];
5596
+ const door = typeof attachment.door === "string" ? attachment.door : "";
5597
+ if (servers.length === 0)
5598
+ return "The MCP servers that needed sign-in are signed in now; their tools are available again.";
5599
+ 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}`;
5600
+ });
5601
+ function lastAnnounced(messages) {
5602
+ let last = [];
5603
+ for (const a of attachmentsIn(messages)) {
5604
+ if (a.type === "mcp_needs_auth" && Array.isArray(a.servers))
5605
+ last = a.servers.filter((s) => typeof s === "string");
5606
+ }
5607
+ return last;
5608
+ }
5609
+ function mcpNeedsAuthAttachment(states, messages, brand) {
5610
+ const now = needsAuthServers(states);
5611
+ const before = lastAnnounced(messages);
5612
+ if (now.length === before.length && now.every((name, i) => name === before[i]))
5613
+ return;
5614
+ return { type: "mcp_needs_auth", servers: now, door: mcpSignInHint(brand, SERVER_PLACEHOLDER) };
5615
+ }
5616
+
5465
5617
  // src/subagents/child-handle.ts
5466
5618
  var childEngineFactory;
5467
5619
  function registerChildEngineFactory(factory) {
@@ -21788,6 +21940,8 @@ async function runEngineBody(opts, facetDisposers, registerBackgroundShellSweep)
21788
21940
  extraHookEntries,
21789
21941
  subagentHooks,
21790
21942
  extraMcpServerSources,
21943
+ mcpOAuthStore,
21944
+ hostCredentialChannel,
21791
21945
  initSlashCommands,
21792
21946
  initSkills,
21793
21947
  initPlugins,
@@ -21906,7 +22060,8 @@ ${escapeAttributionText(msg.body)}
21906
22060
  const bypassAvailableToThisSession = bypassGate.allowDangerouslySkipPermissions && !bypassGate.disableBypassPermissionsMode;
21907
22061
  const policyStateStore = new PolicyStateStore({ mode: initialMode, rules: initialRules }, bypassGate);
21908
22062
  const permissionHome = homedir();
21909
- const bridge = createRpcBridge2(output);
22063
+ const bridge = createRpcBridge2(output, { holdUntilOpen: true });
22064
+ hostCredentialChannel?.bind(bridge);
21910
22065
  const bridgePromptStage = createBridgePromptStage(bridge);
21911
22066
  const realPromptStage = {
21912
22067
  async prompt(call, evalCtx, meta) {
@@ -22750,6 +22905,7 @@ ${escapeAttributionText(msg.body)}
22750
22905
  }
22751
22906
  const sessionStateKey = config.agentId ?? config.sessionId;
22752
22907
  let mcpLifecycle;
22908
+ let mcpStartupWait;
22753
22909
  let disposeSessionMcpLifecycle;
22754
22910
  if (mcpServerStateSource === undefined && mcpControlSeam === undefined) {
22755
22911
  const sources = [{ origin: "explicit", servers: config.mcpServers ?? {} }, ...extraMcpServerSources ?? []];
@@ -22763,9 +22919,10 @@ ${escapeAttributionText(msg.body)}
22763
22919
  envConfig: mcpEnvConfig,
22764
22920
  elicitationAsk: createElicitationAsker2(bridge),
22765
22921
  reservedServerName: sessionBrand.mcpServerName,
22766
- cwd: config.cwd
22922
+ cwd: config.cwd,
22923
+ ...mcpOAuthStore !== undefined ? { oauth: createSessionMcpOAuth({ store: mcpOAuthStore, sender: bridge, brand: sessionBrand, ...config.hostCredentials === true ? { hostOwnsRefresh: true } : {} }) } : {}
22767
22924
  });
22768
- await mcpLifecycle.start();
22925
+ mcpStartupWait = mcpLifecycle.launch();
22769
22926
  disposeSessionMcpLifecycle = registerSessionMcpLifecycle(sessionStateKey, mcpLifecycle);
22770
22927
  }
22771
22928
  const effectiveMcpStateSource = mcpServerStateSource ?? mcpLifecycle?.stateSource;
@@ -23011,6 +23168,7 @@ ${escapeAttributionText(msg.body)}
23011
23168
  tools: advertisedToolNames,
23012
23169
  ...mcpServersWire !== undefined ? { mcp_servers: mcpServersWire } : {}
23013
23170
  });
23171
+ bridge.open();
23014
23172
  const initLoadedSurface = {
23015
23173
  slash_commands: initSlashCommands !== undefined ? [...initSlashCommands] : [],
23016
23174
  output_style: initOutputStyle ?? config.outputStyle ?? DEFAULT_OUTPUT_STYLE,
@@ -23553,6 +23711,8 @@ ${escapeAttributionText(msg.body)}
23553
23711
  envConfig: mcpEnvConfig
23554
23712
  });
23555
23713
  };
23714
+ if (mcpStartupWait !== undefined)
23715
+ await mcpStartupWait;
23556
23716
  {
23557
23717
  const deadlineMs = firstTurnMcpWaitMs();
23558
23718
  if (deadlineMs !== undefined && mcpLifecycle.stateSource.snapshot().some((server) => server.state === "pending")) {
@@ -24108,6 +24268,11 @@ ${escapeAttributionText(msg.body)}
24108
24268
  const plan = planModeAttachment();
24109
24269
  if (plan !== undefined)
24110
24270
  produced.push(plan);
24271
+ if (effectiveMcpStateSource !== undefined) {
24272
+ const needsAuth = mcpNeedsAuthAttachment(effectiveMcpStateSource.snapshot(), messages, sessionBrand);
24273
+ if (needsAuth !== undefined)
24274
+ produced.push(needsAuth);
24275
+ }
24111
24276
  }
24112
24277
  if (phase === "tool-round") {
24113
24278
  const attachment = taskNotificationAttachment(notifications.drainFor(config.agentId, { maxPriority: "next" }), {
@@ -24375,6 +24540,8 @@ ${escapeAttributionText(msg.body)}
24375
24540
  if (pending !== undefined)
24376
24541
  hint = `. The MCP server '${pending.name}' is still connecting. Call WaitForMcpServers to wait for it, then try again.`;
24377
24542
  }
24543
+ if (hint === "" && toolName.startsWith("mcp__"))
24544
+ hint = needsAuthToolHint(effectiveMcpStateSource?.snapshot(), toolName, sessionBrand);
24378
24545
  return `<tool_use_error>Error: No such tool available: ${toolName}${hint}</tool_use_error>`;
24379
24546
  };
24380
24547
  const assertMessagesWithinCap = (msgs) => {
@@ -26367,7 +26534,7 @@ class TranscriptWriter2 {
26367
26534
  return entries;
26368
26535
  }
26369
26536
  }
26370
- var RUNTIME_ENGINE_VERSION2 = "0.0.29";
26537
+ var RUNTIME_ENGINE_VERSION2 = "0.0.31";
26371
26538
  function withPermissionJournal(writer, location) {
26372
26539
  return {
26373
26540
  transcriptPath: join25(location.winterHome, "projects", location.projectKey, `${location.sessionId}.jsonl`),
@@ -26506,117 +26673,13 @@ function resolveProductionStoreHome(config, env) {
26506
26673
  // src/provider/credential-api.ts
26507
26674
  import {
26508
26675
  ANTHROPIC_CONSOLE_ACCOUNT_ID,
26509
- CredentialResolutionError as CredentialResolutionError2,
26676
+ CredentialResolutionError,
26510
26677
  WinterProviderResolutionError as WinterProviderResolutionError3,
26511
26678
  startAnthropicConsoleBrokerLogin,
26512
26679
  startCodexLogin,
26513
26680
  startXaiLogin
26514
26681
  } from "@yanlinglabs/winter-provider-runtime";
26515
26682
 
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
26683
  // src/provider/selection.ts
26621
26684
  import { WinterProviderResolutionError as WinterProviderResolutionError2, createRegistry } from "@yanlinglabs/winter-provider-runtime";
26622
26685
  import { DEFAULT_PROVIDER_STALL_TIMEOUT_MS } from "@yanlinglabs/winter-agent-sdk";
@@ -26829,22 +26892,22 @@ var FORBIDDEN = /[\u0000-\u001f\u007f]/;
26829
26892
  function assertLocator(locator) {
26830
26893
  const { providerId, accountId, service } = locator;
26831
26894
  if (typeof providerId !== "string" || providerId.trim().length === 0)
26832
- throw new CredentialResolutionError2("malformed", "a provider credential needs a non-empty providerId");
26895
+ throw new CredentialResolutionError("malformed", "a provider credential needs a non-empty providerId");
26833
26896
  if (typeof accountId !== "string" || accountId.trim().length === 0)
26834
- throw new CredentialResolutionError2("malformed", `a provider credential for "${providerId}" needs a non-empty accountId`);
26897
+ throw new CredentialResolutionError("malformed", `a provider credential for "${providerId}" needs a non-empty accountId`);
26835
26898
  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`);
26899
+ 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
26900
  }
26838
26901
  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}")`);
26902
+ throw new CredentialResolutionError("malformed", `a provider credential locator may not contain control characters (provider "${providerId}")`);
26840
26903
  }
26841
26904
  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`);
26905
+ throw new CredentialResolutionError("malformed", `an explicit keychain service for provider "${providerId}" may not be blank; omit it to use the store's default`);
26843
26906
  }
26844
26907
  function assertMaterial(material, ref) {
26845
26908
  const empty = (value) => value === undefined || value.trim().length === 0;
26846
26909
  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`);
26910
+ 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
26911
  };
26849
26912
  switch (material.kind) {
26850
26913
  case "api-key":
@@ -26880,13 +26943,13 @@ function assertMaterial(material, ref) {
26880
26943
  }
26881
26944
  }
26882
26945
  function redactedFailure(operation, ref, err) {
26883
- const code = err instanceof CredentialResolutionError2 ? err.code : "io";
26946
+ const code = err instanceof CredentialResolutionError ? err.code : "io";
26884
26947
  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)`);
26948
+ 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
26949
  }
26887
26950
  function statusFromThrow(err, ref, providerId) {
26888
26951
  const locator = redactCredentialRef(ref);
26889
- if (err instanceof CredentialResolutionError2) {
26952
+ if (err instanceof CredentialResolutionError) {
26890
26953
  if (err.code === "malformed")
26891
26954
  return { ok: false, code: "invalid", message: `the credential at ${locator} is not valid material for provider "${providerId}"` };
26892
26955
  if (err.code === "io")
@@ -27197,4 +27260,4 @@ function selectClassifierRoute2(config, descriptorOf) {
27197
27260
  return { kind: "worker-eligible" };
27198
27261
  }
27199
27262
 
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 };
27263
+ 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 };