@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.
- package/dist/embedded-worker.js +6 -4
- package/dist/embedded.js +6 -4
- package/dist/engine.d.ts +16 -0
- package/dist/{index-gmxxtqpe.js → index-0xqzk1hm.js} +30 -12
- package/dist/{index-8jgwp1px.js → index-9s68t0pd.js} +170 -11
- package/dist/index-bgeqc7ax.js +52 -0
- package/dist/{index-eyddzcgs.js → index-c7kq6erg.js} +2 -2
- package/dist/{index-jz5d90c6.js → index-d4pmmr27.js} +191 -128
- package/dist/index-z6s3r4xm.js +903 -0
- package/dist/index.js +4 -2
- package/dist/mcp/client.d.ts +20 -1
- package/dist/mcp/lifecycle.d.ts +9 -0
- package/dist/mcp/transports/http.d.ts +10 -1
- package/dist/mcp/transports/sse.d.ts +4 -2
- package/dist/mcp-auth/account.d.ts +33 -0
- package/dist/mcp-auth/config.d.ts +6 -0
- package/dist/mcp-auth/constants.d.ts +30 -0
- package/dist/mcp-auth/discovery.d.ts +72 -0
- package/dist/mcp-auth/engine-wiring.d.ts +52 -0
- package/dist/mcp-auth/errors.d.ts +37 -0
- package/dist/mcp-auth/fetch-policy.d.ts +33 -0
- package/dist/mcp-auth/login.d.ts +59 -0
- package/dist/mcp-auth/records.d.ts +73 -0
- package/dist/mcp-auth/refresh.d.ts +34 -0
- package/dist/mcp-auth/revoke.d.ts +15 -0
- package/dist/mcp-auth/session-provider.d.ts +55 -0
- package/dist/mcp-auth/store.d.ts +85 -0
- package/dist/mcp-auth/test-fixture-as.d.ts +72 -0
- package/dist/mcp-auth.d.ts +14 -0
- package/dist/mcp-auth.js +493 -0
- package/dist/mcp-client.d.ts +12 -2
- package/dist/mcp-client.js +15 -2
- package/dist/production-wiring.d.ts +6 -0
- package/dist/provider/host-credentials.d.ts +48 -0
- package/dist/provider/keychain-store.d.ts +29 -1
- package/dist/provider/session-provider.d.ts +1 -1
- package/dist/rpc/bridge.d.ts +5 -1
- package/dist/store/dialect.d.ts +1 -1
- package/dist/testing.js +5 -3
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- 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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
5323
|
-
|
|
5324
|
-
|
|
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
|
-
|
|
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.
|
|
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
|
|
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
|
|
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
|
|
26897
|
+
throw new CredentialResolutionError("malformed", `a provider credential for "${providerId}" needs a non-empty accountId`);
|
|
26835
26898
|
if (providerId.includes(":")) {
|
|
26836
|
-
throw new
|
|
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
|
|
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
|
|
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
|
|
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
|
|
26946
|
+
const code = err instanceof CredentialResolutionError ? err.code : "io";
|
|
26884
26947
|
const kind = err instanceof Error ? err.name : typeof err;
|
|
26885
|
-
return new
|
|
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
|
|
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,
|
|
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 };
|