@yanlinglabs/winter-runtime-sdk 0.0.1 → 0.0.2

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.
@@ -2,7 +2,7 @@ export { createRuntimeDirectory } from "../directory/directory.js";
2
2
  export type { DirectorySnapshot, RuntimeDirectoryHandle, RuntimeDirectoryOptions } from "../directory/directory.js";
3
3
  export type { RuntimeDirectoryRecoveryHooks, RuntimeDirectoryRetention } from "../directory/recovery.js";
4
4
  export { entryToChildLike, entryToListedRuntimeObject, entryToListedRuntimeObjectList, isListableFrom, isLiveStatus, isResolvableFrom, mergeAdapterOwnedFields, owningSessionIdOf, parentAddressOf, sessionAddressOf } from "../directory/entries.js";
5
- export { createGlobalMessaging, callerAddressOf, deriveMessageId } from "./router.js";
5
+ export { createGlobalMessaging, deriveMessageId } from "./router.js";
6
6
  export type { GlobalMessagingContext, GlobalMessagingHandle, GlobalMessagingOptions, ReplyRequest } from "./router.js";
7
7
  export { createWinterMessagingAdapter } from "./winter-adapter.js";
8
8
  export type { WinterMessagingAdapter, WinterMessagingAdapterDeps } from "./winter-adapter.js";
@@ -15,8 +15,6 @@ export type { InboundPolicy, InboundPolicyDeps, InboundPolicyHooks, InboundVerdi
15
15
  export { createAttachedSessionRegistry } from "./sessions.js";
16
16
  export type { AttachedOfficialSession, AttachedSession, AttachedSessionRegistry, AttachedWinterSession, LiveSessionStatus } from "./sessions.js";
17
17
  export { renderAttributedTurn, renderOwnerQualifiedTurn, UnattributableSenderError } from "./attribution.js";
18
- export { createMessagingToolHandlers, toolUseIdFromExtra, VENDOR_TOOL_USE_ID_META_KEY } from "./handlers.js";
19
- export type { MessagingToolCaller, MessagingToolHandler, MessagingToolHandlers, MessagingToolResult } from "./handlers.js";
20
18
  import { type RuntimeDirectoryHandle, type RuntimeDirectoryOptions } from "../directory/directory.js";
21
19
  import type { SeamContext } from "../seams/context.js";
22
20
  import { type GlobalMessagingHandle, type GlobalMessagingOptions } from "./router.js";
@@ -2,7 +2,7 @@ import type { NotificationRecord, SendMessageResult } from "@yanlinglabs/winter-
2
2
  import type { RuntimeDirectoryHandle } from "../directory/directory.js";
3
3
  import type { SeamContext } from "../seams/context.js";
4
4
  import type { GlobalMessaging, SendMessageRequest } from "../seams/global-messaging.js";
5
- import type { DeliveryOutcome, GlobalAgentMessage, RuntimeAddress, SerializedRuntimeAddress } from "../seams/messaging-contract.js";
5
+ import type { DeliveryOutcome, GlobalAgentMessage, SerializedRuntimeAddress } from "../seams/messaging-contract.js";
6
6
  import { type InboundPolicyHooks } from "./inbound.js";
7
7
  import { type OfficialMessagingAdapter, type OfficialMessagingAdapterDeps } from "./official-adapter.js";
8
8
  import { type AttachedOfficialSession, type AttachedWinterSession } from "./sessions.js";
@@ -88,14 +88,3 @@ export interface GlobalMessagingHandle extends GlobalMessaging {
88
88
  */
89
89
  export declare function deriveMessageId(senderSessionId: string, toolUseId: string): string;
90
90
  export declare function createGlobalMessaging(context: GlobalMessagingContext, options?: GlobalMessagingOptions): GlobalMessagingHandle;
91
- /**
92
- * The canonical address a `SendMessage`/`ListAgents` handler runs AS (see `handlers.ts`).
93
- *
94
- * The subpath's own `callerAddress` builds `agent:<parent>:<child>` for a child caller and
95
- * `session:<id>` for a top-level one — the same rule the resolution fence reads, so the handler and
96
- * the router can never disagree about who is asking.
97
- */
98
- export declare function callerAddressOf(caller: {
99
- sessionId: string;
100
- agentId?: string;
101
- }): RuntimeAddress;
@@ -6,14 +6,18 @@ export declare const ALIASED_BUILTINS: readonly [{
6
6
  }, {
7
7
  readonly builtin: "ListAgents";
8
8
  readonly tool: "list_agents";
9
+ }, {
10
+ readonly builtin: "ReadNotifications";
11
+ readonly tool: "read_notifications";
12
+ }, {
13
+ readonly builtin: "advisor";
14
+ readonly tool: "advisor";
9
15
  }];
10
16
  export type AliasedBuiltin = (typeof ALIASED_BUILTINS)[number]["builtin"];
11
17
  /** §2's `toolAliases` value: `{ SendMessage: "mcp__<server>__send_message", … }`. */
12
18
  export declare function officialToolAliases(brand: Pick<BrandProfile, "mcpServerName">): Record<string, string>;
13
19
  /** The canonical name one aliased built-in resolves to (single hop — never a chain). */
14
20
  export declare function aliasTargetFor(builtin: AliasedBuiltin, brand: Pick<BrandProfile, "mcpServerName">): string;
15
- export { LIST_AGENTS_FIELD_MAX, NATIVE_LIST_AGENTS_OUTPUT_SCHEMA, NATIVE_LIST_AGENTS_SCHEMA, NATIVE_SEND_MESSAGE_SCHEMA, SEND_MESSAGE_SUMMARY_MAX, SEND_MESSAGE_TO_MAX, acceptNativeListAgentsArgs, acceptNativeSendMessageArgs, } from "../native-args.js";
16
- export type { NativeArgsResult, NativeListAgentsArgs, NativeSendMessageArgs } from "../native-args.js";
17
21
  /**
18
22
  * How the canonical duplicates are exposed (§7's "SHOULD be deferred/hidden").
19
23
  *
@@ -17,8 +17,8 @@ export { EXECUTION_INDIRECTION_ENV_NAMES, EXECUTION_INDIRECTION_ENV_PREFIXES, is
17
17
  export type { EnvAllowlistSnapshot, OfficialEnvInput, OfficialEnvPolicy } from "./env-allowlist.js";
18
18
  export { OFFICIAL_ERROR_CODES, OfficialStdoutUnterminatedError, OfficialAgentResultError, OfficialApiError, OfficialBranchError, OfficialConfigurationError, OfficialConnectionError, OfficialExecutableNotFoundError, OfficialInterruptedError, OfficialInvalidResumeError, OfficialKilledError, OfficialMcpError, OfficialNonzeroExitError, OfficialPermissionDeniedError, OfficialProtocolError, OfficialSessionNotFoundError, OfficialSessionStoreError, OfficialToolError, isOfficialBranchError, } from "./errors.js";
19
19
  export type { OfficialCrashClass, OfficialErrorCode, WinterErrorClassName } from "./errors.js";
20
- export { OFFICIAL_MATERIALIZATION_DROPS, assertNoAdvisor, canonicalToolNames, materializeOfficialMcpServer, messagingToolDescriptors, officialMcpServers, winterMcpServerDescriptor } from "./mcp-descriptors.js";
21
- export type { InputShapeFactory, JsonSchemaObject, MessagingHandlers, OfficialMcpModule, WinterMcpHandler, WinterMcpServerDescriptor, WinterMcpToolDescriptor, WinterMcpToolResult } from "./mcp-descriptors.js";
20
+ export { OFFICIAL_MATERIALIZATION_DROPS, canonicalToolNames, materializeOfficialMcpServer, officialMcpServers, winterMcpServerDescriptor } from "./mcp-descriptors.js";
21
+ export type { InputShapeFactory, JsonSchemaObject, OfficialMcpModule, WinterMcpHandler, WinterMcpServerDescriptor, WinterMcpToolDescriptor, WinterMcpToolResult } from "./mcp-descriptors.js";
22
22
  export { AUTO_MEMORY_LOAD_CAP, DEFAULT_EXCLUDE_DYNAMIC_SECTIONS, PINNED_SYSTEM_PROMPT_PRESET, assertOptionsInvariants, brandedFlagSettings, buildOfficialOptions, captureOptions, mergeHooks } from "./options-template.js";
23
23
  export type { OptionsTemplatePolicy } from "./options-template.js";
24
24
  export { SPOOL_SEGMENTS, classifyLocalWriteRoot, officialSpoolRoot, validateObservedConfigDir, vendorTempRootReport } from "./spool.js";
@@ -1,10 +1,15 @@
1
- import { type BrandProfile } from "@yanlinglabs/winter-agent-sdk";
2
- /** A JSON-Schema object, in the subset the descriptors use. */
3
- export interface JsonSchemaObject {
4
- type: "object";
5
- properties: Readonly<Record<string, Readonly<Record<string, unknown>>>>;
6
- required?: readonly string[];
7
- }
1
+ import { type BrandProfile, type McpSdkServerConfigWithInstance } from "@yanlinglabs/winter-agent-sdk";
2
+ import { type AdvisorToolDeps, type JsonSchemaObject, type MessagingToolPort, type WinterToolCaller } from "@yanlinglabs/winter-agent-sdk/tools";
3
+ import { RuntimeLaunchInputError } from "../errors.js";
4
+ /**
5
+ * ONE `JsonSchemaObject`, AND IT IS THE SDK'S (interim review I-3).
6
+ *
7
+ * The router used to declare its own — `properties` required, no `additionalProperties` — and every
8
+ * composition of an SDK schema into a descriptor went through `as unknown as JsonSchemaObject`. Two
9
+ * types for one contract is how the two copies of the native schemas drifted in the first place
10
+ * (barrel-exports.test.ts's own header), so the type travels with the definitions it describes.
11
+ */
12
+ export type { JsonSchemaObject };
8
13
  /** What a handler answers with — the MCP content shape, identical on both branches. */
9
14
  export interface WinterMcpToolResult {
10
15
  content: Array<{
@@ -16,20 +21,20 @@ export interface WinterMcpToolResult {
16
21
  /**
17
22
  * A tool handler, and the vendor's own second argument (item 15).
18
23
  *
19
- * `extra` IS FORWARDED, NOT DROPPED but what it CARRIES is a measurement, not an assumption. The
20
- * carry that produced this change asked for it so the official branch could derive WS-10 §12's retry
21
- * key (the (session, tool-call id) pair a retry must allocate the SAME message id from), and the
22
- * whole-branch review was right to say that must be checked first: the in-process server's `extra` is
23
- * the MCP REQUEST CONTEXT — the JSON-RPC request id and `_meta` — which is not the model's
24
- * `tool_use_id`. `test/official/runtime-aliases.test.ts` records what the pinned runtime actually
25
- * puts there; see that test and this module's own note below for the answer.
26
- *
27
- * Forwarding it is worth doing either way: it is the only channel the vendor gives a tool for request
28
- * context, dropping it is unrecoverable at the handler, and a handler that does not want it simply
29
- * declares one parameter.
24
+ * `extra` IS FORWARDED, NOT DROPPED, and what it carries is measured rather than assumed: on 0.3.250
25
+ * `extra._meta["claudecode/toolUseId"]` is the id the model emitted, which is the half of WS-10 §12's
26
+ * retry key the official branch could not otherwise have. The SDK's `toolUseIdFromExtra` reads it (and
27
+ * falls back to the bound caller's id), so the router forwards the argument and decides nothing.
30
28
  */
31
29
  export type WinterMcpHandler = (args: unknown, extra?: unknown) => Promise<WinterMcpToolResult>;
32
- /** One tool on the standing server. Everything a branch needs to register it, and nothing branch-specific. */
30
+ /**
31
+ * One tool on the standing server — the ROUTER'S OWN COMPOSITION TYPE, and nothing more (R-8-1).
32
+ *
33
+ * It is a `WinterToolDefinition` (the SDK's: bare name, description, schemas, annotations, permission
34
+ * class) plus the two things only a HOST can supply: the handler that runs it and how this branch
35
+ * exposes it. The router declares no tool of its own — every field but those two is read off the
36
+ * definition the SDK owns.
37
+ */
33
38
  export interface WinterMcpToolDescriptor {
34
39
  /** The bare tool name (`send_message`); the canonical name is derived from it and the brand. */
35
40
  tool: string;
@@ -49,43 +54,51 @@ export interface WinterMcpServerDescriptor {
49
54
  version: string;
50
55
  tools: readonly WinterMcpToolDescriptor[];
51
56
  }
52
- /** The two messaging handlers §7's aliases resolve to. Their implementations are the router's. */
53
- export interface MessagingHandlers {
54
- sendMessage: WinterMcpHandler;
55
- listAgents: WinterMcpHandler;
56
- }
57
57
  /**
58
- * The messaging tools, with the NATIVE schemas mirrored (§7: "handlers MUST accept the native
59
- * argument schemas exactly").
58
+ * THE STANDING SERVER, COMPOSED FROM THE SDK'S DEFINITIONS AND THE SDK'S HANDLER FACTORIES (R-8-1).
60
59
  *
61
- * `deferred` on both: they are the canonical twins of aliased built-ins, and §7 wants the model to
62
- * see one `SendMessage`, not two.
63
- */
64
- export declare function messagingToolDescriptors(handlers: MessagingHandlers): readonly WinterMcpToolDescriptor[];
65
- /**
66
- * §11's refusal: no advisor on this server, on this branch.
60
+ * THE ROUTER OWNS NO TOOL. It used to declare `send_message` and `list_agents` here their
61
+ * descriptions, their native schemas, their acceptors — beside a second copy of the same declarations
62
+ * inside the Winter runtime, and the two had already drifted on what `to` may contain and on what an
63
+ * over-long `summary` does. `@yanlinglabs/winter-agent-sdk/tools` is now the single declaration, and
64
+ * this function is what BINDS it: `WINTER_DEFAULT_TOOL_DEFINITIONS` × the SDK's handler factories,
65
+ * under the brand's server name.
67
66
  *
68
- * Thrown rather than filtered. A silent filter would leave a host believing its advisor was
69
- * registered and wondering why the model never calls it; the whole point of D29's split is that each
70
- * branch's advisor has a DIFFERENT backing, and a host that tried to register one here has a
71
- * misunderstanding worth surfacing.
72
- */
73
- export declare function assertNoAdvisor(tools: readonly WinterMcpToolDescriptor[], branchLabel: string): void;
74
- /**
75
- * Builds the standing server descriptor for a session.
67
+ * THE PORT IS PASSED STRAIGHT THROUGH (ruling P-3). `GlobalMessagingHandle` satisfies
68
+ * `MessagingToolPort` structurally `sendDetailed`, `listReachable({from})`, `readNotifications(id)`
69
+ * so the router needs no adapter of its own and `messagingToolPortFromRuntimeDeps` (which adapts the
70
+ * RUNTIME's deps) is never used here.
76
71
  *
77
- * The capability plugins (browser, computer, office) are passed IN rather than declared here: WS-06
78
- * owns their exact names and schemas, and a copy of them in the router would be a second declaration
79
- * to drift. What the router owns is that they are registered under the same canonical names, with the
80
- * same identity, on both branches which is what `canonicalToolNames` below makes checkable.
72
+ * THE ADVISOR IS ALWAYS REGISTERED (interim review I-5). The Winter runtime always advertises
73
+ * `advisor`; gating the official branch's copy on a host option would make the two legs' advertised
74
+ * sets differ by a setting that changes nothing on the other leg. `advisor.resolveReviewer` supplies
75
+ * the reviewer its default answers `undefined`, which is WS-06 §4's ordinary tool error, not a throw.
81
76
  */
82
77
  export declare function winterMcpServerDescriptor(args: {
83
78
  brand: Pick<BrandProfile, "mcpServerName">;
84
- messaging: MessagingHandlers;
79
+ port: MessagingToolPort;
80
+ caller: WinterToolCaller | (() => WinterToolCaller);
81
+ advisor: Omit<AdvisorToolDeps, "resolveReviewer"> & Partial<Pick<AdvisorToolDeps, "resolveReviewer">>;
85
82
  capabilities?: readonly WinterMcpToolDescriptor[];
86
83
  version?: string;
87
- branchLabel: string;
88
84
  }): WinterMcpServerDescriptor;
85
+ /** One capability server, as a descriptor the official branch can register. */
86
+ export declare function capabilityServerDescriptor(server: McpSdkServerConfigWithInstance, version?: string): WinterMcpServerDescriptor;
87
+ /**
88
+ * The ONE refusal both legs raise when a caller's own `mcpServers` key names a forwarded capability.
89
+ *
90
+ * IT IS SHARED BECAUSE THE TWO LEGS MUST SAY THE SAME THING (review r1). The Winter leg refuses in
91
+ * `forwardableOptions`, the official leg before its launch; the same mistake producing a typed error
92
+ * on one leg and a silent per-key override on the other would leave the two branches advertising
93
+ * different tools under one name, with nothing to read about it. Two copies of this sentence is how
94
+ * that starts being true again, so there is one.
95
+ */
96
+ export declare function capabilityNameCollisionError(args: {
97
+ field: string;
98
+ name: string;
99
+ }): RuntimeLaunchInputError;
100
+ /** Every capability server, in the order the host declared them. */
101
+ export declare function capabilityServerDescriptors(servers: readonly McpSdkServerConfigWithInstance[]): readonly WinterMcpServerDescriptor[];
89
102
  /** The canonical names a descriptor registers — the identity both branches must agree on. */
90
103
  export declare function canonicalToolNames(descriptor: WinterMcpServerDescriptor, brand: Pick<BrandProfile, "mcpServerName">): readonly string[];
91
104
  /** The subset of the injected official module this needs. Duck-typed: the seam declares only `query`. */
@@ -119,7 +132,6 @@ export declare function materializeOfficialMcpServer(args: {
119
132
  toInputShape: InputShapeFactory;
120
133
  branchLabel: string;
121
134
  }): unknown;
122
- /** `Options.mcpServers` for this branch: one entry, keyed by the brand's own server name. */
123
135
  /** The descriptor fields the official branch's registration cannot carry (review r1, n3). */
124
136
  export declare const OFFICIAL_MATERIALIZATION_DROPS: readonly string[];
125
137
  export declare function officialMcpServers(args: {
package/dist/sdk.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { BrandProfile, Options, Query, SessionKey } from "@yanlinglabs/winter-agent-sdk";
1
+ import type { BrandProfile, McpSdkServerConfigWithInstance, Options, Query, SessionKey } from "@yanlinglabs/winter-agent-sdk";
2
2
  import { type RouterOfficialInput, type RouterOfficialPolicy, type RouterQuery } from "./door.js";
3
3
  import type { SeamContextWithDirectory } from "./seams/context.js";
4
4
  import type { OfficialSdkModule } from "./seams/official-sdk-shapes.js";
@@ -11,6 +11,8 @@ import type { RuntimeDirectory } from "./seams/directory.js";
11
11
  import type { RuntimeDirectoryStore } from "./seams/directory-store.js";
12
12
  import type { GlobalMessagingOptions, RuntimeDirectoryOptions } from "./messaging/index.js";
13
13
  import type { HandoffBarrierDeps } from "./store/index.js";
14
+ import type { ReviewerResolver } from "@yanlinglabs/winter-agent-sdk/tools";
15
+ import { type InputShapeFactory } from "./official/mcp-descriptors.js";
14
16
  import type { RuntimeKind, RuntimeSelection, SelectionInput } from "./selection/runtime-selection.js";
15
17
  import { type VersionMatrixReport } from "./version-matrix.js";
16
18
  /**
@@ -38,6 +40,21 @@ export interface RuntimeSdkPeers {
38
40
  }
39
41
  export interface RuntimeSdkOptions {
40
42
  peers: RuntimeSdkPeers;
43
+ /**
44
+ * Host-declared peer package versions (D19a/R2, WS-02 §7.1) — THE ONLY DOOR inside a compiled
45
+ * binary. The version matrix's probe 2 (`resolved-manifest`) resolves a peer's `package.json` via
46
+ * `createRequire(...).resolve()`, which cannot see outside a compiled binary's own bundle
47
+ * (`file:///$bunfs/...`); a host that self-spawns its own compiled artifact and whose injected peer
48
+ * exports no version identity of its own (probe 1) has NOTHING for the matrix to read unless it
49
+ * declares one here, from its own vendored `VERSIONS.json`. Declaring a version wins over both
50
+ * probes when it parses — it does not bypass the matrix's range/exact-pin checks, only replaces how
51
+ * the identity was DISCOVERED; an unparseable declared value falls through to probe 1 rather than
52
+ * refusing on its own. Absent, behaviour is unchanged: probes 1 and 2, in that order.
53
+ */
54
+ peerVersions?: {
55
+ winterAgentSdk?: string;
56
+ claudeAgentSdk?: string;
57
+ };
41
58
  /** R-7b-2's seam; default = in-memory (which is also what every hermetic test uses). */
42
59
  directoryStore?: RuntimeDirectoryStore;
43
60
  /** Host-provided credential reads (WS-14 §12) — never disk, never this package's own keychain. */
@@ -57,6 +74,61 @@ export interface RuntimeSdkOptions {
57
74
  * fill in when neither does.
58
75
  */
59
76
  brand?: Partial<BrandProfile>;
77
+ /**
78
+ * WS-09 §1.3's capability servers, as the Winter SDK's own in-process shape — FORWARDED ON BOTH
79
+ * LEGS, never rewritten into anything else (R-8, and the user's tool-ownership ruling R-8-1).
80
+ *
81
+ * THE ROUTER OWNS NO TOOL. The daemon owns the capability tools — computer, browser, office — and
82
+ * hands them over as MCP SERVERS; this is the door they come through, and the router's whole job is
83
+ * to put the same servers in front of both runtimes. The Winter leg receives each entry BY
84
+ * REFERENCE under its own `name`, merged into `Options.mcpServers`. The official leg cannot take the
85
+ * object itself (its runtime registers in-process servers through its own constructor, over its own
86
+ * validator's schema shape), so the same tools are REGISTERED there from the server's own
87
+ * declaration — see `capabilityServerDescriptor`. Identical names, identical schemas, identical
88
+ * handlers, two registrations.
89
+ *
90
+ * THE BRAND'S OWN SERVER NAME IS RESERVED. `brand.mcpServerName` is the standing server's key on the
91
+ * official branch (§7's aliases resolve to `mcp__<mcpServerName>__<tool>`), so a capability server
92
+ * that claimed it would shadow the messaging tools on one branch and not the other. That is a typed
93
+ * refusal at construction, not a silent overwrite.
94
+ *
95
+ * WITHOUT `toInputShape` THIS IS A WINTER-LEG-ONLY DOOR: the official leg refuses rather than open a
96
+ * session whose capability tools exist on one branch only.
97
+ *
98
+ * A CALLER'S OWN `Options.mcpServers` REACHES THE WINTER LEG ONLY, and always has: the official leg
99
+ * builds its servers from this constructor and `runtime.official.mcpServers`, and never reads the
100
+ * pinned `Options` field at all. A caller key that collides with a capability name is therefore
101
+ * refused for BOTH legs at the door (interim review I-6) rather than refused on one and silently
102
+ * dropped on the other.
103
+ */
104
+ capabilities?: readonly McpSdkServerConfigWithInstance[];
105
+ /**
106
+ * The host's JSON-Schema → validator-shape bridge, for the official branch's in-process servers.
107
+ *
108
+ * INJECTED BECAUSE THE ROUTER DEPENDS ON NO VALIDATOR (see `official/mcp-descriptors.ts`'s header):
109
+ * the official runtime's own server constructor takes schemas in its peer validator's shape, and a
110
+ * package whose entire design is "two injected peers and nothing else" will not grow a third
111
+ * dependency to produce them. A host that has the official SDK already has that validator, and
112
+ * writes this in one line.
113
+ */
114
+ toInputShape?: InputShapeFactory;
115
+ /**
116
+ * The REVIEWER behind the standing advisor (R-8-1(3), ruling P-6; interim review I-5).
117
+ *
118
+ * The advisor tool itself is not optional and is not configured here: the official leg registers it
119
+ * on the standing server unconditionally, because the Winter runtime always advertises `advisor` and
120
+ * two legs whose advertised sets differ by a host option is the divergence WS-14 §11 exists to
121
+ * prevent. This supplies WHO REVIEWS — a resolver the host owns, because reviewer RESOLUTION is the
122
+ * runtime's provider-layer concern (D30) and the router has no provider layer. With no resolver the
123
+ * tool answers WS-06 §4's ordinary error.
124
+ *
125
+ * The transcript is NOT a field here: the source is this session's own, built by the leg over the one
126
+ * shared store, keyed by the backend session id the runtime reports at `system/init`.
127
+ */
128
+ advisor?: {
129
+ resolveReviewer: ReviewerResolver;
130
+ maxChars?: number;
131
+ };
60
132
  /**
61
133
  * The handoff barrier's collaborators (whole-branch review, F-3).
62
134
  *
@@ -197,14 +269,27 @@ export declare function runtimeSdkInternals(sdk: RuntimeSdk): RuntimeSdkInternal
197
269
  /**
198
270
  * Builds the object forwarded to a peer's `query()`.
199
271
  *
272
+ * THE INVARIANT, IN ONE SENTENCE: **the forwarded options are the caller's options minus
273
+ * `ROUTER_ONLY_OPTION_KEYS`, plus the brand's capability-server entries under `mcpServers`, and
274
+ * nothing else is rewritten.**
275
+ *
200
276
  * THE COMMON CASE FORWARDS THE CALLER'S OWN OBJECT, by reference. "Passes through verbatim" is a
201
277
  * property a test can only really check by identity, and a router that copied unconditionally would
202
278
  * be quietly deciding which of `Options`' members it knows about — the exact drift D19b's "never a
203
- * translation layer" rules out. A copy is made ONLY when a router-owned key is present, because that
204
- * key must not reach an SDK that would not recognise it; every other member keeps its own value
205
- * identity through the copy.
279
+ * translation layer" rules out. A copy is made ONLY when there is something to remove or something to
280
+ * add, because that is the only time the caller's own object would be the wrong thing to hand over;
281
+ * every other member keeps its own value identity through the copy.
282
+ *
283
+ * SO R-8 DID NOT WEAKEN THE PROPERTY, IT RESTATED IT. A host that configures no capabilities is
284
+ * unaffected — the identity return below still fires, and the test that pins it is unchanged. A host
285
+ * that configures them gets a copy whose every member except `mcpServers` is `Object.is`-identical to
286
+ * its own, and whose `mcpServers` is its own entries plus the capability entries, by reference.
287
+ *
288
+ * A CAPABILITY KEY THE CALLER ALSO USES IS A TYPED REFUSAL, never a silent overwrite in either
289
+ * direction: one of the two servers would simply not be there, and the party who would find out is
290
+ * the model, at the one moment the tool matters.
206
291
  */
207
- export declare function forwardableOptions(options: RouterOptions, brand?: Partial<BrandProfile>): Options;
292
+ export declare function forwardableOptions(options: RouterOptions, brand?: Partial<BrandProfile>, capabilityServers?: Readonly<Record<string, unknown>>): Options;
208
293
  /**
209
294
  * Constructs the router. Throws `RuntimeSdkVersionError` on a version-matrix miss (D19a).
210
295
  *
@@ -8,7 +8,7 @@ import type { RuntimeSdkPeers } from "./sdk.js";
8
8
  * not a range that quietly widens.
9
9
  */
10
10
  export declare const SUPPORTED: {
11
- readonly winterAgentSdk: ">=0.0.2 <0.1.0";
11
+ readonly winterAgentSdk: ">=0.0.3 <0.1.0";
12
12
  readonly claudeAgentSdk: "0.3.250";
13
13
  };
14
14
  /**
@@ -18,7 +18,7 @@ export declare const SUPPORTED: {
18
18
  */
19
19
  export declare const SUPPORTED_PROTOCOL_VERSIONS: readonly ["1.0"];
20
20
  /** Where a peer's package version came from. See this module's header. */
21
- export type PeerVersionSource = "peer-export" | "resolved-manifest";
21
+ export type PeerVersionSource = "host-declared" | "peer-export" | "resolved-manifest";
22
22
  export interface PeerVersionIdentity {
23
23
  /** The package name the identity was read for. */
24
24
  packageName: string;
@@ -70,8 +70,23 @@ export declare function readExportedVersion(namespace: unknown): string | undefi
70
70
  * own manifest as a subpath. Every failure mode — no such package, a manifest that will not parse, a
71
71
  * manifest with no `version` — returns `undefined`, which the caller turns into the loud refusal.
72
72
  * Never throws: a missing OPTIONAL peer must not crash the matrix on its way to reporting itself.
73
+ *
74
+ * `resolveEntry` is an injectable seam (R2), defaulting to the real
75
+ * `createRequire(import.meta.url).resolve` — NOT deleted, still the only resolver this package ever
76
+ * uses on its own. It exists so a test can simulate the one condition this package cannot otherwise
77
+ * reproduce on demand: a compiled binary, where that resolve call cannot see outside the bundle
78
+ * (`file:///$bunfs/...`) and always throws. Production code never passes a second argument.
79
+ */
80
+ export declare function readResolvedManifestVersion(packageName: string, resolveEntry?: (name: string) => string): string | undefined;
81
+ /**
82
+ * The seams `resolveVersionMatrix` accepts. `resolveEntry`, when given, REPLACES probe 2's resolver
83
+ * end to end (through `identityFor` into `readResolvedManifestVersion`) — the only way a test can make
84
+ * that probe genuinely fail rather than merely be irrelevant because `declared` or peer-export already
85
+ * answered. Production code (`assertVersionMatrix`) never supplies one.
73
86
  */
74
- export declare function readResolvedManifestVersion(packageName: string): string | undefined;
87
+ export interface VersionMatrixSeams {
88
+ resolveEntry?: (name: string) => string;
89
+ }
75
90
  /**
76
91
  * Reads each injected peer's version identity and refuses loudly on a miss.
77
92
  *
@@ -79,6 +94,31 @@ export declare function readResolvedManifestVersion(packageName: string): string
79
94
  * Winter-only host injects `{ winter }` and never loads the official runtime (that is the whole
80
95
  * point of the optional peer), so "no claude peer" is a valid, fully-supported configuration and the
81
96
  * report simply omits the row.
97
+ *
98
+ * `declared` is `RuntimeSdkOptions.peerVersions` (R2), optional — the one-arg call
99
+ * (`assertVersionMatrix(peers)`) stays legal and behaves exactly as before: with nothing declared,
100
+ * every peer's identity comes from probe 1 or probe 2, unchanged.
101
+ *
102
+ * `seams` is test-only (R2 review r1): NOT exposed here — `resolveVersionMatrix` below is where a test
103
+ * reaches it. A public seam parameter on this function is a production call site that could pass a
104
+ * broken resolver by accident, for a capability real hosts never need.
105
+ */
106
+ export declare function assertVersionMatrix(peers: RuntimeSdkPeers, declared?: {
107
+ winterAgentSdk?: string;
108
+ claudeAgentSdk?: string;
109
+ }): VersionMatrixReport;
110
+ /**
111
+ * `assertVersionMatrix`'s full implementation, with an injectable `seams` (R2 review r1, `test/spine/
112
+ * version-matrix.test.ts`'s test (d)). NOT on the package barrel (`src/index.ts`) — deliberately: a
113
+ * real host has no reason to override how this package resolves ITS OWN peers' manifests, so this
114
+ * function exists only so a test can force probe 2 (`resolved-manifest`) to GENUINELY fail — the one
115
+ * condition a test cannot otherwise reproduce on demand (in an ordinary dev checkout the real peers
116
+ * really are resolvable, so a test that never overrides the resolver cannot tell "declared won" from
117
+ * "resolved-manifest would have answered the same value anyway"). `assertVersionMatrix` is the public,
118
+ * one-line delegation with the real (default) seams.
82
119
  */
83
- export declare function assertVersionMatrix(peers: RuntimeSdkPeers): VersionMatrixReport;
120
+ export declare function resolveVersionMatrix(peers: RuntimeSdkPeers, declared?: {
121
+ winterAgentSdk?: string;
122
+ claudeAgentSdk?: string;
123
+ }, seams?: VersionMatrixSeams): VersionMatrixReport;
84
124
  export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yanlinglabs/winter-runtime-sdk",
3
- "version": "0.0.1",
3
+ "version": "0.0.2",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "description": "One door over the Winter Agent SDK and the official Claude Agent SDK: runtime selection, the official-SDK adapter, the shared session store, the handoff barrier and the cross-runtime messaging router.",
@@ -39,7 +39,7 @@
39
39
  },
40
40
  "peerDependencies": {
41
41
  "@anthropic-ai/claude-agent-sdk": "0.3.250",
42
- "@yanlinglabs/winter-agent-sdk": ">=0.0.2 <0.1.0"
42
+ "@yanlinglabs/winter-agent-sdk": ">=0.0.3 <0.1.0"
43
43
  },
44
44
  "peerDependenciesMeta": {
45
45
  "@anthropic-ai/claude-agent-sdk": {
@@ -50,9 +50,9 @@
50
50
  "@anthropic-ai/claude-agent-sdk": "0.3.250",
51
51
  "@types/bun": "^1.3.0",
52
52
  "@types/node": "^26.4.0",
53
- "@yanlinglabs/winter-agent-sdk": "^0.0.2",
54
- "@yanlinglabs/winter-conformance": "^0.0.2",
55
- "@yanlinglabs/winter-provider-conformance": "^0.0.2",
53
+ "@yanlinglabs/winter-agent-sdk": "^0.0.3",
54
+ "@yanlinglabs/winter-conformance": "^0.0.3",
55
+ "@yanlinglabs/winter-provider-conformance": "^0.0.3",
56
56
  "typescript": "^5.9.0"
57
57
  },
58
58
  "scripts": {
@@ -1,56 +0,0 @@
1
- import { type GlobalMessagingHandle } from "./router.js";
2
- /** The MCP result shape both branches return — structurally the descriptor's own (WS-14 §11). */
3
- export interface MessagingToolResult {
4
- content: Array<{
5
- type: "text";
6
- text: string;
7
- }>;
8
- isError?: boolean;
9
- }
10
- export type MessagingToolHandler = (args: unknown, extra?: unknown) => Promise<MessagingToolResult>;
11
- /**
12
- * WS-10 §12's RETRY KEY, on the official branch — and it exists, which was not known until it was
13
- * measured (item 15; `test/official/runtime-aliases.test.ts` is the measurement).
14
- *
15
- * §12 wants a message id derived from (sender session, TOOL-CALL id) so "a retry allocates the SAME
16
- * id" and returns the stored outcome instead of starting a second turn. On the Winter branch the
17
- * caller binds `toolUseId` at registration. On the official branch the handler is inside the vendor's
18
- * in-process MCP server, where the only per-call channel is the second argument the vendor passes —
19
- * and the reasonable expectation was that it carries MCP request context (a JSON-RPC request id,
20
- * `_meta`) rather than an Anthropic-API `tool_use_id`, which is one layer up.
21
- *
22
- * THE PINNED RUNTIME BRIDGES THEM. Measured on 0.3.250: `extra._meta["claudecode/toolUseId"]` is the
23
- * exact id the model emitted. So the official branch gets a real §12 key rather than depending on the
24
- * rapid-repeat guard, and the vendor's own namespaced `_meta` name is read rather than guessed at.
25
- *
26
- * A VENDOR-NAMESPACED KEY IS NEVER REBRANDED (WS-01 §5): `claudecode/toolUseId` is the vendor's name
27
- * for the vendor's field, exactly like `CLAUDE_CONFIG_DIR`. It is read defensively — an absent or
28
- * non-string value simply falls back to the bound caller's id — because a future pin may move it, and
29
- * losing the key must degrade to today's behaviour rather than to a crash.
30
- */
31
- export declare const VENDOR_TOOL_USE_ID_META_KEY = "claudecode/toolUseId";
32
- export declare function toolUseIdFromExtra(extra: unknown): string | undefined;
33
- export interface MessagingToolHandlers {
34
- sendMessage: MessagingToolHandler;
35
- listAgents: MessagingToolHandler;
36
- }
37
- /**
38
- * WHO IS CALLING — bound at registration, never read out of the arguments.
39
- *
40
- * The standing MCP server is materialized per session (WS-14 §11), so the caller is known when the
41
- * handler is built. Taking it from the ARGUMENTS instead would make the sender's identity something a
42
- * model could write, and every fence in this package — the owning-parent rule, the self-target
43
- * refusal, WS-10 §13's sender class, WS-15 §6.2's dedupe key — is keyed on it.
44
- *
45
- * `toolUseId` is the second half of WS-10 §12's retry key. It is OPTIONAL because on the official
46
- * branch it is not available: the in-process MCP server hands a tool handler its arguments, and the
47
- * router's own materialization does not forward the vendor's `extra` (which is where a request id
48
- * would be). A caller that cannot supply one gets no dedupe — stated at the door rather than faked
49
- * with a stable-looking key that would make two different messages one.
50
- */
51
- export interface MessagingToolCaller {
52
- sessionId: string;
53
- agentId?: string;
54
- toolUseId?: string;
55
- }
56
- export declare function createMessagingToolHandlers(messaging: GlobalMessagingHandle, caller: MessagingToolCaller | (() => MessagingToolCaller)): MessagingToolHandlers;
@@ -1,76 +0,0 @@
1
- /** WS-10 §10.1's bounds. `to`'s own limit is the subpath's; this is the copy the SCHEMA advertises. */
2
- export declare const SEND_MESSAGE_TO_MAX = 300;
3
- export declare const SEND_MESSAGE_SUMMARY_MAX = 200;
4
- /** WS-10 §10.2's bound on both reserved `ListAgents` fields. */
5
- export declare const LIST_AGENTS_FIELD_MAX = 256;
6
- export declare const NATIVE_SEND_MESSAGE_SCHEMA: {
7
- readonly type: "object";
8
- readonly properties: {
9
- readonly to: {
10
- readonly type: "string";
11
- readonly maxLength: 300;
12
- readonly description: "no newline, no \"*\" broadcast";
13
- };
14
- readonly message: {
15
- readonly type: "string";
16
- readonly description: "required; defaults \"\" for pure idle subscription";
17
- };
18
- readonly summary: {
19
- readonly type: "string";
20
- readonly maxLength: 200;
21
- };
22
- readonly notify_when_idle: {
23
- readonly type: "boolean";
24
- readonly description: "one-shot; main conversation -> same-machine session only";
25
- };
26
- };
27
- readonly required: readonly ["to", "message"];
28
- };
29
- export declare const NATIVE_LIST_AGENTS_SCHEMA: {
30
- readonly type: "object";
31
- readonly properties: {
32
- readonly channel: {
33
- readonly type: "string";
34
- readonly maxLength: 256;
35
- readonly description: "reserved";
36
- };
37
- readonly q: {
38
- readonly type: "string";
39
- readonly maxLength: 256;
40
- readonly description: "reserved";
41
- };
42
- };
43
- };
44
- /** WS-10 §10.2: "`ListAgents` output is EXACTLY `{ listing: string }`." */
45
- export declare const NATIVE_LIST_AGENTS_OUTPUT_SCHEMA: {
46
- readonly type: "object";
47
- readonly properties: {
48
- readonly listing: {
49
- readonly type: "string";
50
- };
51
- };
52
- readonly required: readonly ["listing"];
53
- };
54
- /** The native `SendMessage` arguments (WS-10 §10.1), after validation. */
55
- export interface NativeSendMessageArgs {
56
- to: string;
57
- message: string;
58
- summary?: string;
59
- notify_when_idle?: boolean;
60
- }
61
- /** The native `ListAgents` arguments (WS-10 §10.2). Both fields are reserved in the pinned build. */
62
- export interface NativeListAgentsArgs {
63
- channel?: string;
64
- q?: string;
65
- }
66
- export type NativeArgsResult<T> = {
67
- ok: true;
68
- args: T;
69
- } | {
70
- ok: false;
71
- reason: string;
72
- };
73
- /** Accepts the native `SendMessage` arguments EXACTLY — no more, no less. */
74
- export declare function acceptNativeSendMessageArgs(input: unknown): NativeArgsResult<NativeSendMessageArgs>;
75
- /** The same treatment for `ListAgents`: two reserved optional fields, both capped, nothing else. */
76
- export declare function acceptNativeListAgentsArgs(input: unknown): NativeArgsResult<NativeListAgentsArgs>;