@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 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.28";
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.28",
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.28"
48
+ "@yanlinglabs/winter-provider-catalog": "0.0.30"
49
49
  },
50
50
  "optionalDependencies": {
51
- "@yanlinglabs/winter-agent-sdk-darwin-arm64": "0.0.28"
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.28",
56
- "@yanlinglabs/winter-conformance": "0.0.28"
55
+ "@yanlinglabs/winter-agent-runtime": "0.0.30",
56
+ "@yanlinglabs/winter-conformance": "0.0.30"
57
57
  },
58
58
  "scripts": {}
59
59
  }