@yanlinglabs/winter-agent-sdk 0.0.28 → 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.
- package/dist/index.d.ts +3 -1
- package/dist/index.js +83 -1
- package/dist/options.d.ts +40 -1
- package/dist/protocol/config.d.ts +121 -0
- package/dist/query.d.ts +11 -0
- package/package.json +5 -5
package/dist/index.d.ts
CHANGED
|
@@ -3,7 +3,7 @@ export type { Query, SdkMessage, SessionMessagingFacet } from "./query.js";
|
|
|
3
3
|
export type { QueryInternal, ControlRequestHandler, ControlRequestHandlerResult } from "./query.js";
|
|
4
4
|
export type { Options } from "./options.js";
|
|
5
5
|
export { SYSTEM_PROMPT_DYNAMIC_BOUNDARY, DEFAULT_CONTEXT_WINDOW_TOKENS, DEFAULT_COMPACTION_THRESHOLD, DEFAULT_PLANS_DIRECTORY, DEFAULT_OUTPUT_STYLE } from "./options.js";
|
|
6
|
-
export { DEFAULT_PROVIDER_STALL_TIMEOUT_MS, DEFAULT_KEYCHAIN_SERVICE } from "./options.js";
|
|
6
|
+
export { DEFAULT_PROVIDER_STALL_TIMEOUT_MS, DEFAULT_KEYCHAIN_SERVICE, MCP_OAUTH_REFRESH_SUBTYPE, CREDENTIAL_RESOLVE_SUBTYPE } from "./options.js";
|
|
7
7
|
export { WEB_TOOLS_DEFAULTS, resolveWebToolsConfig } from "./options.js";
|
|
8
8
|
export type { ResolvedWebToolsConfig } from "./options.js";
|
|
9
9
|
export type { WebToolsConfig, WebSearchConfig, WebFetchConfig, WebPrivateAddressPolicy, AutoMemoryConfig } from "./protocol/config.js";
|
|
@@ -21,6 +21,8 @@ export { resolveRuntimeExecutable, defaultSpawn } from "./transport.js";
|
|
|
21
21
|
export type { SpawnedRuntimeProcess, SpawnRuntimeOptions, SpawnClaudeCodeProcess } from "./transport.js";
|
|
22
22
|
export type { RuntimeConfig, RuntimeHooksConfig, RuntimeHookMatcherGroup, SandboxSettingsConfig } from "./protocol/config.js";
|
|
23
23
|
export type { McpServerConfigForProcessTransport, McpVersionNegotiation, AgentMcpServerSpec, RuntimeAgentDefinition } from "./protocol/config.js";
|
|
24
|
+
export type { McpOAuthConfig, McpOAuthSecretRef, McpOAuthRefreshRequest, McpOAuthRefreshAnswer } from "./protocol/config.js";
|
|
25
|
+
export type { CredentialResolveRequest, CredentialResolveAnswer } from "./protocol/config.js";
|
|
24
26
|
export { encodeFrame, decodeFrame, splitFrames, ProtocolError } from "./protocol/codec.js";
|
|
25
27
|
export { PROTOCOL_VERSION } from "./protocol/frames.js";
|
|
26
28
|
export { SDK_VERSION } from "./version.js";
|
package/dist/index.js
CHANGED
|
@@ -66,6 +66,8 @@ var DEFAULT_PLANS_DIRECTORY = `${WINTER_BRAND2.projectDirName}/plans`;
|
|
|
66
66
|
var DEFAULT_OUTPUT_STYLE = "default";
|
|
67
67
|
var DEFAULT_PROVIDER_STALL_TIMEOUT_MS = 120000;
|
|
68
68
|
var DEFAULT_KEYCHAIN_SERVICE = WINTER_BRAND2.keychainService;
|
|
69
|
+
var MCP_OAUTH_REFRESH_SUBTYPE = "mcp_oauth_refresh";
|
|
70
|
+
var CREDENTIAL_RESOLVE_SUBTYPE = "credential_resolve";
|
|
69
71
|
var WEB_TOOLS_DEFAULTS = {
|
|
70
72
|
searchEnabled: true,
|
|
71
73
|
maxSearchesPerCall: 8,
|
|
@@ -474,6 +476,74 @@ function makeElicitationHandler(onElicitation, abortController) {
|
|
|
474
476
|
return { ok: true, payload: result ?? { action: "decline" } };
|
|
475
477
|
};
|
|
476
478
|
}
|
|
479
|
+
function isRefreshRequest(payload) {
|
|
480
|
+
if (typeof payload !== "object" || payload === null)
|
|
481
|
+
return false;
|
|
482
|
+
const p = payload;
|
|
483
|
+
return typeof p.server === "string" && typeof p.account === "string" && typeof p.generation === "number" && Number.isInteger(p.generation) && (p.stepUpScope === undefined || typeof p.stepUpScope === "string");
|
|
484
|
+
}
|
|
485
|
+
function isRefreshAnswer(value) {
|
|
486
|
+
if (typeof value !== "object" || value === null)
|
|
487
|
+
return false;
|
|
488
|
+
const v = value;
|
|
489
|
+
return v.ok === true || v.ok === false && (v.reason === "needs_auth" || v.reason === "transient");
|
|
490
|
+
}
|
|
491
|
+
function makeMcpOAuthRefreshHandler(onMcpOAuthRefresh, abortController) {
|
|
492
|
+
return async (payload, handlerCtx) => {
|
|
493
|
+
if (!isRefreshRequest(payload))
|
|
494
|
+
return { ok: false, error: { code: "invalid_payload", message: "mcp_oauth_refresh expects { server, account, generation, stepUpScope? }" } };
|
|
495
|
+
const controller = new AbortController;
|
|
496
|
+
if (abortController?.signal.aborted === true || handlerCtx?.signal.aborted === true)
|
|
497
|
+
controller.abort();
|
|
498
|
+
abortController?.signal.addEventListener("abort", () => controller.abort(), { once: true });
|
|
499
|
+
handlerCtx?.signal.addEventListener("abort", () => controller.abort(), { once: true });
|
|
500
|
+
const request = { server: payload.server, account: payload.account, generation: payload.generation, ...payload.stepUpScope !== undefined ? { stepUpScope: payload.stepUpScope } : {} };
|
|
501
|
+
try {
|
|
502
|
+
const answer = await onMcpOAuthRefresh(request, { signal: controller.signal });
|
|
503
|
+
return { ok: true, payload: isRefreshAnswer(answer) ? answer : { ok: false, reason: "transient" } };
|
|
504
|
+
} catch (err) {
|
|
505
|
+
console.error(`winter: onMcpOAuthRefresh threw for account '${payload.account}' (${err instanceof Error ? err.name : "error"}) -- answering transient`);
|
|
506
|
+
return { ok: true, payload: { ok: false, reason: "transient" } };
|
|
507
|
+
}
|
|
508
|
+
};
|
|
509
|
+
}
|
|
510
|
+
function isCredentialResolveRequest(payload) {
|
|
511
|
+
if (typeof payload !== "object" || payload === null)
|
|
512
|
+
return false;
|
|
513
|
+
const p = payload;
|
|
514
|
+
const ref = p.ref;
|
|
515
|
+
return typeof ref === "object" && ref !== null && ref.kind === "keychain" && typeof ref.account === "string" && (ref.service === undefined || typeof ref.service === "string") && (p.minGeneration === undefined || typeof p.minGeneration === "number" && Number.isInteger(p.minGeneration));
|
|
516
|
+
}
|
|
517
|
+
function isCredentialResolveAnswer(value) {
|
|
518
|
+
if (typeof value !== "object" || value === null)
|
|
519
|
+
return false;
|
|
520
|
+
const v = value;
|
|
521
|
+
if (v.ok === true)
|
|
522
|
+
return typeof v.material === "string" && typeof v.generation === "number" && (v.expiresAt === undefined || typeof v.expiresAt === "number");
|
|
523
|
+
return v.ok === false && (v.reason === "not_found" || v.reason === "not_allowed" || v.reason === "stale" || v.reason === "unavailable");
|
|
524
|
+
}
|
|
525
|
+
function makeCredentialResolveHandler(onCredentialResolve, abortController) {
|
|
526
|
+
return async (payload, handlerCtx) => {
|
|
527
|
+
if (!isCredentialResolveRequest(payload))
|
|
528
|
+
return { ok: false, error: { code: "invalid_payload", message: 'credential_resolve expects { ref: { kind: "keychain", account, service? }, minGeneration? }' } };
|
|
529
|
+
const controller = new AbortController;
|
|
530
|
+
if (abortController?.signal.aborted === true || handlerCtx?.signal.aborted === true)
|
|
531
|
+
controller.abort();
|
|
532
|
+
abortController?.signal.addEventListener("abort", () => controller.abort(), { once: true });
|
|
533
|
+
handlerCtx?.signal.addEventListener("abort", () => controller.abort(), { once: true });
|
|
534
|
+
const request = {
|
|
535
|
+
ref: { kind: "keychain", account: payload.ref.account, ...payload.ref.service !== undefined ? { service: payload.ref.service } : {} },
|
|
536
|
+
...payload.minGeneration !== undefined ? { minGeneration: payload.minGeneration } : {}
|
|
537
|
+
};
|
|
538
|
+
try {
|
|
539
|
+
const answer = await onCredentialResolve(request, { signal: controller.signal });
|
|
540
|
+
return { ok: true, payload: isCredentialResolveAnswer(answer) ? answer : { ok: false, reason: "unavailable" } };
|
|
541
|
+
} catch (err) {
|
|
542
|
+
console.error(`winter: onCredentialResolve threw for account '${payload.ref.account}' (${err instanceof Error ? err.name : "error"}) -- answering unavailable`);
|
|
543
|
+
return { ok: true, payload: { ok: false, reason: "unavailable" } };
|
|
544
|
+
}
|
|
545
|
+
};
|
|
546
|
+
}
|
|
477
547
|
function buildRuntimeHooksConfig(hooks) {
|
|
478
548
|
if (!hooks)
|
|
479
549
|
return;
|
|
@@ -640,6 +710,7 @@ function query(args) {
|
|
|
640
710
|
...options.providerStallTimeoutMs !== undefined ? { providerStallTimeoutMs: options.providerStallTimeoutMs } : {},
|
|
641
711
|
...options.maxOutputTokens !== undefined ? { maxOutputTokens: options.maxOutputTokens } : {},
|
|
642
712
|
...brand.keychainService !== WINTER_BRAND2.keychainService || options.keychainService !== undefined ? { keychainService: brand.keychainService } : {},
|
|
713
|
+
...options.onCredentialResolve !== undefined ? { hostCredentials: true } : {},
|
|
643
714
|
...options.autoClassifier !== undefined ? { autoClassifier: options.autoClassifier } : {},
|
|
644
715
|
...options.advisor !== undefined ? { advisor: options.advisor } : {},
|
|
645
716
|
...options.web !== undefined ? { web: options.web } : {},
|
|
@@ -773,6 +844,12 @@ function query(args) {
|
|
|
773
844
|
if (options.onElicitation) {
|
|
774
845
|
controlRequestHandlers.set("mcp_elicitation", makeElicitationHandler(options.onElicitation, options.abortController));
|
|
775
846
|
}
|
|
847
|
+
if (options.onCredentialResolve) {
|
|
848
|
+
controlRequestHandlers.set(CREDENTIAL_RESOLVE_SUBTYPE, makeCredentialResolveHandler(options.onCredentialResolve, options.abortController));
|
|
849
|
+
}
|
|
850
|
+
if (options.onMcpOAuthRefresh) {
|
|
851
|
+
controlRequestHandlers.set(MCP_OAUTH_REFRESH_SUBTYPE, makeMcpOAuthRefreshHandler(options.onMcpOAuthRefresh, options.abortController));
|
|
852
|
+
}
|
|
776
853
|
if (proc.stderr) {
|
|
777
854
|
const stderrIterable = proc.stderr;
|
|
778
855
|
(async () => {
|
|
@@ -919,6 +996,9 @@ function query(args) {
|
|
|
919
996
|
const retained = typeof payload === "object" && payload !== null ? payload.retained_count : undefined;
|
|
920
997
|
return { retainedCount: typeof retained === "number" ? retained : 0 };
|
|
921
998
|
};
|
|
999
|
+
gen.reconnectMcpServer = async (serverName) => {
|
|
1000
|
+
await sendControlRequest("mcp_reconnect", { serverName });
|
|
1001
|
+
};
|
|
922
1002
|
gen.supportedModels = async () => {
|
|
923
1003
|
const payload = await sendControlRequest("list_models", undefined);
|
|
924
1004
|
return Array.isArray(payload) ? payload : [];
|
|
@@ -1038,7 +1118,7 @@ function query(args) {
|
|
|
1038
1118
|
return gen;
|
|
1039
1119
|
}
|
|
1040
1120
|
// src/version.ts
|
|
1041
|
-
var SDK_VERSION = "0.0.
|
|
1121
|
+
var SDK_VERSION = "0.0.30";
|
|
1042
1122
|
// src/paths/project-key.ts
|
|
1043
1123
|
var TRANSCRIPT_PROJECT_KEY_MAX_LENGTH = 64;
|
|
1044
1124
|
var VENDOR_PROJECT_KEY_PATTERN = /^[A-Za-z0-9_-]{1,64}$/;
|
|
@@ -2203,6 +2283,7 @@ export {
|
|
|
2203
2283
|
BRAND_TOKEN_RE2 as BRAND_TOKEN_RE,
|
|
2204
2284
|
CATALOG_TAG_RENAMES,
|
|
2205
2285
|
CLIConnectionError,
|
|
2286
|
+
CREDENTIAL_RESOLVE_SUBTYPE,
|
|
2206
2287
|
DEFAULT_COMPACTION_THRESHOLD,
|
|
2207
2288
|
DEFAULT_CONTEXT_WINDOW_TOKENS,
|
|
2208
2289
|
DEFAULT_KEYCHAIN_SERVICE,
|
|
@@ -2214,6 +2295,7 @@ export {
|
|
|
2214
2295
|
FIRST_PARTY_ORIGINATORS2 as FIRST_PARTY_ORIGINATORS,
|
|
2215
2296
|
HOOK_EVENTS,
|
|
2216
2297
|
InvalidBrandError,
|
|
2298
|
+
MCP_OAUTH_REFRESH_SUBTYPE,
|
|
2217
2299
|
MESSAGING_CONTROL_SUBTYPES,
|
|
2218
2300
|
MESSAGING_CONTROL_SUBTYPE_LIST,
|
|
2219
2301
|
MESSAGING_HOST_REQUEST_SUBTYPES,
|
package/dist/options.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { SpawnClaudeCodeProcess } from "./transport.js";
|
|
2
2
|
import type { PermissionMode, CanUseTool, HookEvent, HookCallbackMatcher } from "./permissions/types.js";
|
|
3
|
-
import type { SandboxSettingsConfig, McpServerToolPolicy, McpStdioServerConfig, McpHttpServerConfig, McpSSEServerConfig, McpSdkServerConfig, RuntimeAgentDefinition, SdkPluginConfig, SystemPromptOption, OutputFormat, SkillsOption, ProviderSelection, ThinkingConfig, EffortLevel, AutoClassifierConfig, AdvisorConfig, WebToolsConfig, WebPrivateAddressPolicy, AutoMemoryConfig, CredentialRef } from "./protocol/config.js";
|
|
3
|
+
import type { SandboxSettingsConfig, McpServerToolPolicy, McpStdioServerConfig, McpHttpServerConfig, McpSSEServerConfig, McpSdkServerConfig, RuntimeAgentDefinition, SdkPluginConfig, SystemPromptOption, OutputFormat, SkillsOption, ProviderSelection, ThinkingConfig, EffortLevel, AutoClassifierConfig, AdvisorConfig, WebToolsConfig, WebPrivateAddressPolicy, AutoMemoryConfig, CredentialRef, McpOAuthRefreshRequest, McpOAuthRefreshAnswer, CredentialResolveRequest, CredentialResolveAnswer } from "./protocol/config.js";
|
|
4
4
|
import type { SettingSource } from "./settings/types.js";
|
|
5
5
|
import type { SessionStore } from "./store/session-store.js";
|
|
6
6
|
import { type BrandProfile } from "./brand.js";
|
|
@@ -15,6 +15,14 @@ export declare const DEFAULT_PLANS_DIRECTORY: string;
|
|
|
15
15
|
export declare const DEFAULT_OUTPUT_STYLE = "default";
|
|
16
16
|
export declare const DEFAULT_PROVIDER_STALL_TIMEOUT_MS = 120000;
|
|
17
17
|
export declare const DEFAULT_KEYCHAIN_SERVICE: string;
|
|
18
|
+
/**
|
|
19
|
+
* WS-25 (MCP OAuth): the runtime -> host control subtype a session sends when a stored MCP sign-in needs
|
|
20
|
+
* refreshing (`McpOAuthRefreshRequest` -> `McpOAuthRefreshAnswer`, protocol/config.ts). Spelled once:
|
|
21
|
+
* `query.ts` registers the handler under it and the runtime sends it.
|
|
22
|
+
*/
|
|
23
|
+
export declare const MCP_OAUTH_REFRESH_SUBTYPE = "mcp_oauth_refresh";
|
|
24
|
+
/** WS-25 §7: the runtime -> host control subtype a host-brokered session resolves a Keychain credential with (`CredentialResolveRequest` -> `CredentialResolveAnswer`). */
|
|
25
|
+
export declare const CREDENTIAL_RESOLVE_SUBTYPE = "credential_resolve";
|
|
18
26
|
/**
|
|
19
27
|
* Every web-tool default, spelled ONCE. A reader function resolves an absent field against this --
|
|
20
28
|
* no consumer writes a literal of its own.
|
|
@@ -172,6 +180,37 @@ export interface Options {
|
|
|
172
180
|
action: "accept" | "decline" | "cancel";
|
|
173
181
|
content?: Record<string, unknown>;
|
|
174
182
|
} | null>;
|
|
183
|
+
/**
|
|
184
|
+
* WS-25 (MCP OAuth), WINTER-ONLY: the host refreshes this session's MCP sign-ins. When set, `query()`
|
|
185
|
+
* answers the runtime's `mcp_oauth_refresh` control request with it; the session then re-reads the
|
|
186
|
+
* token item from the Keychain and never posts a refresh grant itself. The host is the ONE refresher
|
|
187
|
+
* (single-flight per `account`, a `generation` re-read before posting), which is what keeps a
|
|
188
|
+
* ROTATING refresh token from being replayed by two processes.
|
|
189
|
+
*
|
|
190
|
+
* Absent: the runtime's request lands on the generic "no handler registered" answer and the session
|
|
191
|
+
* refreshes in-process -- correct for a standalone SDK user with one process. A host that runs
|
|
192
|
+
* several sessions (or refreshes elsewhere) MUST set this, and must set it here, before the session
|
|
193
|
+
* starts: a handler registered later leaves the first connects refreshing in-process.
|
|
194
|
+
*
|
|
195
|
+
* Never serialized (a JS function), like `onElicitation`. Carries no material in either direction.
|
|
196
|
+
*/
|
|
197
|
+
onMcpOAuthRefresh?: (request: McpOAuthRefreshRequest, options: {
|
|
198
|
+
signal: AbortSignal;
|
|
199
|
+
}) => Promise<McpOAuthRefreshAnswer>;
|
|
200
|
+
/**
|
|
201
|
+
* WS-25 §7 (prompt-free credentials), WINTER-ONLY: the host resolves this session's Keychain
|
|
202
|
+
* credentials. When set, `query()` puts `hostCredentials: true` on the wire and answers the runtime's
|
|
203
|
+
* `credential_resolve` requests with it; the session then never touches the Keychain (no macOS consent
|
|
204
|
+
* prompt for a child binary reading items the host created) and never persists a credential -- renewal
|
|
205
|
+
* is the host's, and a 401 asks again with `minGeneration`.
|
|
206
|
+
*
|
|
207
|
+
* The host answers ONLY for refs this session's `Options` named, and never with a refresh token (see
|
|
208
|
+
* `CredentialResolveAnswer`). Never serialized (a JS function); the material crosses only in the
|
|
209
|
+
* control_response frame.
|
|
210
|
+
*/
|
|
211
|
+
onCredentialResolve?: (request: CredentialResolveRequest, options: {
|
|
212
|
+
signal: AbortSignal;
|
|
213
|
+
}) => Promise<CredentialResolveAnswer>;
|
|
175
214
|
systemPrompt?: SystemPromptOption;
|
|
176
215
|
plugins?: SdkPluginConfig[];
|
|
177
216
|
skills?: SkillsOption;
|
|
@@ -120,6 +120,117 @@ export interface McpServerToolPolicy {
|
|
|
120
120
|
export type McpVersionNegotiation = "legacy" | "auto" | {
|
|
121
121
|
pin: string;
|
|
122
122
|
};
|
|
123
|
+
/**
|
|
124
|
+
* WS-25, WINTER-OWNED: "this pre-registered client has a secret, stored in the Keychain". It is a MARKER,
|
|
125
|
+
* not a locator: the Keychain account is DERIVED from the server's canonical URL
|
|
126
|
+
* (`mcp-oauth-client-secret:<id>`, `/mcp-auth`'s `mcpOAuthClientSecretAccount`), in the host's own service,
|
|
127
|
+
* and a config can name neither. A config-named account would let any config source -- a trusted
|
|
128
|
+
* repository's MCP list, copied verbatim into a session -- point the sign-in at ANY Keychain item (a
|
|
129
|
+
* provider API key) and send it to an authorization server of its choosing as `client_secret`.
|
|
130
|
+
* `validateServerConfig` refuses any other key (`account`, `service`, a value).
|
|
131
|
+
*/
|
|
132
|
+
export interface McpOAuthSecretRef {
|
|
133
|
+
kind: "keychain";
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* WS-25, WINTER-OWNED: how Winter signs in to ONE remote (http/sse) MCP server over OAuth. Every field is
|
|
137
|
+
* optional, and so is the block: a server whose authorization server supports Client ID Metadata
|
|
138
|
+
* Documents or Dynamic Client Registration needs none of it. Tokens NEVER live here -- they live in the
|
|
139
|
+
* host's Keychain, keyed by the server's canonical URL (`@yanlinglabs/winter-agent-runtime/mcp-auth`'s
|
|
140
|
+
* `mcpOAuthAccountId`).
|
|
141
|
+
*
|
|
142
|
+
* - `clientId` -- a PRE-REGISTERED client (preferred over CIMD and DCR when present).
|
|
143
|
+
* - `clientSecretRef` -- `{ kind: "keychain" }`: that client has a secret, stored at the DERIVED Keychain
|
|
144
|
+
* account (see `McpOAuthSecretRef`).
|
|
145
|
+
* - `callbackPort` -- the loopback port the sign-in listener binds (`http://127.0.0.1:<port>/callback`);
|
|
146
|
+
* a pre-registered client usually needs it fixed. Absent: an ephemeral port (DCR persists it).
|
|
147
|
+
* - `authServerMetadataUrl` -- the authorization server's RFC 8414 metadata document, for a server that
|
|
148
|
+
* publishes no RFC 9728 protected-resource metadata.
|
|
149
|
+
* - `scopes` -- the scopes to request (absent: the server's own `scopes_supported`).
|
|
150
|
+
*/
|
|
151
|
+
export interface McpOAuthConfig {
|
|
152
|
+
clientId?: string;
|
|
153
|
+
clientSecretRef?: McpOAuthSecretRef;
|
|
154
|
+
callbackPort?: number;
|
|
155
|
+
authServerMetadataUrl?: string;
|
|
156
|
+
scopes?: string[];
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* WS-25: the runtime -> host `mcp_oauth_refresh` control request (subtype `MCP_OAUTH_REFRESH_SUBTYPE`).
|
|
160
|
+
* A session never refreshes a token itself when its host answers this: the host (Winter's daemon) is the
|
|
161
|
+
* ONLY refresher, so a rotating refresh token is posted by one process, once (RFC 9700 §4.14's family
|
|
162
|
+
* revocation would otherwise punish N sessions refreshing the same token).
|
|
163
|
+
*
|
|
164
|
+
* Carries NO material -- names only:
|
|
165
|
+
* - `server` -- the server's config name in this session (diagnostics; the host may use it to find the
|
|
166
|
+
* config);
|
|
167
|
+
* - `account` -- the FULL Keychain account name of the token item, `mcp-oauth:<id>` (the host keys its
|
|
168
|
+
* single-flight on it and derives the client item by the prefix swap `mcp-oauth-client:<id>`);
|
|
169
|
+
* - `generation` -- the token record's `generation` the session last read. A host that already holds a
|
|
170
|
+
* newer generation answers `{ ok: true }` without posting (another session asked first).
|
|
171
|
+
* - `stepUpScope` -- WS-25, additive: present when the server answered `403 insufficient_scope`. A refresh
|
|
172
|
+
* cannot widen a grant (RFC 6749 §6), so the host records the scope on the client registration for the
|
|
173
|
+
* next sign-in and answers `needs_auth`.
|
|
174
|
+
*/
|
|
175
|
+
export interface McpOAuthRefreshRequest {
|
|
176
|
+
server: string;
|
|
177
|
+
account: string;
|
|
178
|
+
generation: number;
|
|
179
|
+
stepUpScope?: string;
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* The host's answer. `{ ok: true }` means "re-read the Keychain item"; `needs_auth` means the sign-in is
|
|
183
|
+
* gone (no refresh token, `invalid_grant`, a revoked client) and the server is marked `needs-auth`;
|
|
184
|
+
* `transient` means try again later (the network, the authorization server's 5xx).
|
|
185
|
+
*/
|
|
186
|
+
export type McpOAuthRefreshAnswer = {
|
|
187
|
+
ok: true;
|
|
188
|
+
} | {
|
|
189
|
+
ok: false;
|
|
190
|
+
reason: "needs_auth" | "transient";
|
|
191
|
+
};
|
|
192
|
+
/**
|
|
193
|
+
* WS-25 §7: the runtime -> host `credential_resolve` control request (subtype `CREDENTIAL_RESOLVE_SUBTYPE`),
|
|
194
|
+
* sent only when `RuntimeConfig.hostCredentials` is set. A session asks its host for ONE Keychain item
|
|
195
|
+
* instead of reading the Keychain itself (a child reading an item another binary created is what raises
|
|
196
|
+
* the macOS consent prompt).
|
|
197
|
+
*
|
|
198
|
+
* - `ref` -- the Keychain LOCATOR the session was configured with (a provider `authRef`, a tool key's
|
|
199
|
+
* ref) or, for an MCP sign-in, `{ kind: "keychain", account: "mcp-oauth:<id>" }`. The host answers ONLY
|
|
200
|
+
* for refs that session's `Options` named (plus what its own MCP config implies) -- the allowlist is
|
|
201
|
+
* the host's.
|
|
202
|
+
* - `minGeneration` -- after a 401 (or an expiry): "I hold generation N-1; answer only with N or newer".
|
|
203
|
+
* The host refreshes (single-flight, its own job) until it can, or answers `stale`.
|
|
204
|
+
*/
|
|
205
|
+
export interface CredentialResolveRequest {
|
|
206
|
+
ref: {
|
|
207
|
+
kind: "keychain";
|
|
208
|
+
account: string;
|
|
209
|
+
service?: string;
|
|
210
|
+
};
|
|
211
|
+
minGeneration?: number;
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* The host's answer. `material` is the item's value EXACTLY as the Keychain would hold it -- a JSON
|
|
215
|
+
* `CredentialMaterial` for a provider credential, the bare key for a tool secret, the token record JSON
|
|
216
|
+
* for an MCP sign-in -- with every REFRESH token removed (a session never holds one; MCP records carry
|
|
217
|
+
* `MCP_OAUTH_HOST_HELD_REFRESH_TOKEN` in its place so the session knows a refresh is possible).
|
|
218
|
+
* `generation` counts the host's writes of that item. It travels ONLY in this `control_response` frame
|
|
219
|
+
* over the session's own stdio pipe -- never argv, env, `Options`, a transcript, a log or an error.
|
|
220
|
+
*
|
|
221
|
+
* `reason`: `not_found` (no such item -- the session behaves as with an empty Keychain),
|
|
222
|
+
* `not_allowed` (the ref is outside this session's allowlist), `stale` (no generation >= `minGeneration`
|
|
223
|
+
* could be produced -- a refresh failed), `unavailable` (retry later).
|
|
224
|
+
*/
|
|
225
|
+
export type CredentialResolveAnswer = {
|
|
226
|
+
ok: true;
|
|
227
|
+
material: string;
|
|
228
|
+
expiresAt?: number;
|
|
229
|
+
generation: number;
|
|
230
|
+
} | {
|
|
231
|
+
ok: false;
|
|
232
|
+
reason: "not_found" | "not_allowed" | "stale" | "unavailable";
|
|
233
|
+
};
|
|
123
234
|
export interface McpStdioServerConfig {
|
|
124
235
|
type?: "stdio";
|
|
125
236
|
command: string;
|
|
@@ -137,6 +248,7 @@ export interface McpHttpServerConfig {
|
|
|
137
248
|
timeout?: number;
|
|
138
249
|
alwaysLoad?: boolean;
|
|
139
250
|
versionNegotiation?: McpVersionNegotiation;
|
|
251
|
+
oauth?: McpOAuthConfig;
|
|
140
252
|
}
|
|
141
253
|
export interface McpSSEServerConfig {
|
|
142
254
|
type: "sse";
|
|
@@ -146,6 +258,7 @@ export interface McpSSEServerConfig {
|
|
|
146
258
|
timeout?: number;
|
|
147
259
|
alwaysLoad?: boolean;
|
|
148
260
|
versionNegotiation?: McpVersionNegotiation;
|
|
261
|
+
oauth?: McpOAuthConfig;
|
|
149
262
|
}
|
|
150
263
|
export interface WireMcpToolDefinition {
|
|
151
264
|
name: string;
|
|
@@ -357,6 +470,14 @@ export interface RuntimeConfig {
|
|
|
357
470
|
/** WS-23: `Options.maxOutputTokens`, carried to every main-loop `TurnRequest`. See its own doc. */
|
|
358
471
|
maxOutputTokens?: number;
|
|
359
472
|
keychainService?: string;
|
|
473
|
+
/**
|
|
474
|
+
* WS-25 §7 (prompt-free credentials), WINTER-ONLY: `true` when the host answers `credential_resolve`
|
|
475
|
+
* (`Options.onCredentialResolve`). The runtime then NEVER reads or writes the Keychain for this
|
|
476
|
+
* session: every `{ kind: "keychain" }` credential, tool key and MCP sign-in is asked of the host over
|
|
477
|
+
* the control channel, and renewal is the host's (a 401 asks again with `minGeneration`). Absent: the
|
|
478
|
+
* runtime's own Keychain store, as before (a standalone SDK user). A flag, never material.
|
|
479
|
+
*/
|
|
480
|
+
hostCredentials?: boolean;
|
|
360
481
|
autoClassifier?: AutoClassifierConfig;
|
|
361
482
|
advisor?: AdvisorConfig;
|
|
362
483
|
/** The wire twin of `Options.web` -- see `WebToolsConfig`. Pure passthrough; absent means every default in `WEB_TOOLS_DEFAULTS`. */
|
package/dist/query.d.ts
CHANGED
|
@@ -148,6 +148,17 @@ export interface Query extends AsyncGenerator<SdkMessage> {
|
|
|
148
148
|
}): Promise<{
|
|
149
149
|
retainedCount: number;
|
|
150
150
|
}>;
|
|
151
|
+
/**
|
|
152
|
+
* WS-25 -- reconnect ONE MCP server now (the runtime's `mcp_reconnect` control subtype), and resolve
|
|
153
|
+
* once it is connected; rejects when the reconnect fails or leaves the server `failed`/`needs-auth`.
|
|
154
|
+
* The pinned shape (`sdk.d.ts:2668`, `reconnectMcpServer(serverName): Promise<void>`, "throws on
|
|
155
|
+
* failure"). A host calls it after a sign-in so a `needs-auth` server lists its tools in the live
|
|
156
|
+
* session -- a reconnect, never a restart of the session.
|
|
157
|
+
*
|
|
158
|
+
* OPTIONAL on the interface for the same reason as `compact` (a host's structural `Query` doubles keep
|
|
159
|
+
* type-checking); every `Query` this package returns has it.
|
|
160
|
+
*/
|
|
161
|
+
reconnectMcpServer?(serverName: string): Promise<void>;
|
|
151
162
|
/**
|
|
152
163
|
* Phase 6 Task 10 (derived-shapes-p6 item (d), `sdk.d.ts:2566`): the models this session may select.
|
|
153
164
|
*
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yanlinglabs/winter-agent-sdk",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.30",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"engines": {
|
|
@@ -45,15 +45,15 @@
|
|
|
45
45
|
}
|
|
46
46
|
},
|
|
47
47
|
"dependencies": {
|
|
48
|
-
"@yanlinglabs/winter-provider-catalog": "0.0.
|
|
48
|
+
"@yanlinglabs/winter-provider-catalog": "0.0.30"
|
|
49
49
|
},
|
|
50
50
|
"optionalDependencies": {
|
|
51
|
-
"@yanlinglabs/winter-agent-sdk-darwin-arm64": "0.0.
|
|
51
|
+
"@yanlinglabs/winter-agent-sdk-darwin-arm64": "0.0.30"
|
|
52
52
|
},
|
|
53
53
|
"devDependencies": {
|
|
54
54
|
"@types/node": "^26.4.0",
|
|
55
|
-
"@yanlinglabs/winter-agent-runtime": "0.0.
|
|
56
|
-
"@yanlinglabs/winter-conformance": "0.0.
|
|
55
|
+
"@yanlinglabs/winter-agent-runtime": "0.0.30",
|
|
56
|
+
"@yanlinglabs/winter-conformance": "0.0.30"
|
|
57
57
|
},
|
|
58
58
|
"scripts": {}
|
|
59
59
|
}
|