@yanlinglabs/winter-runtime-sdk 0.0.1 → 0.0.3

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/README.md CHANGED
@@ -14,19 +14,42 @@ Decision record: WS-00 D19 (2026-09-05). Boundaries that do not move:
14
14
  builtin-path containment, Winter MCP plugin registration), shared session-store wiring, the
15
15
  cross-runtime handoff barrier with the materialized-resume decoration doors, and the runtime
16
16
  directory plus cross-runtime messaging router.
17
+ - **It owns NO TOOL** (the user's tool-ownership ruling, R-8-1). The default tools — `SendMessage`,
18
+ `ListAgents`, `ReadNotifications`, `advisor` — are DECLARED once in
19
+ `@yanlinglabs/winter-agent-sdk/tools` and BOUND here, under the official runtime's own built-in
20
+ names; the capability tools (computer, browser, office) are the HOST's, handed over as MCP servers
21
+ and forwarded to both legs unchanged. This package re-exports no tool surface of its own.
17
22
  - The host vendors all three packages directly (`winter-runtime-sdk`, `winter-agent-sdk`,
18
23
  `claude-agent-sdk`); this package declares the two SDKs as peer dependencies and receives their
19
24
  module instances by injection, so a host that never creates a Claude session never loads the
20
25
  official runtime and no SDK is ever instantiated twice.
21
26
  - A `brand` profile flows through unchanged (Winter defaults); Claude Code's own literals stay fixed.
22
27
 
23
- Status: Phase 7b, all four lanes landed and the door routed. The spine (the package scaffold, the
24
- contract re-export, the `createRuntimeSdk` constructor with its version matrix, the seams, the test
25
- harness and CI) and the four lanes behind those seams — the official-SDK adapter, the runtime
26
- directory and messaging router, the store wiring and handoff barrier, and runtime selection — are on
27
- `main`, with WS-17's eighteen router-owned rows proven and cited in `docs/conformance-rows.md`. See
28
- `docs/architecture.md` for the ownership map, the pinned interfaces and how this package consumes the
29
- Winter SDK before its first publish.
28
+ Status: Phase 7b landed all four lanes and routed the door; `0.0.2` was the Phase-8b prerequisite
29
+ release. The spine (the package scaffold, the contract re-export, the `createRuntimeSdk` constructor
30
+ with its version matrix, the seams, the test harness and CI) and the four lanes behind those seams —
31
+ the official-SDK adapter, the runtime directory and messaging router, the store wiring and handoff
32
+ barrier, and runtime selection — are on `main`, with WS-17's eighteen router-owned rows proven and
33
+ cited in `docs/conformance-rows.md`. See `docs/architecture.md` for the ownership map, the pinned
34
+ interfaces and how this package consumes the Winter SDK.
35
+
36
+ **What `0.0.3` changes** (P8c-13 — the official-leg HOST SURFACE, so a host can bridge its own
37
+ approval broker and materialize MCP servers; no peer floor change):
38
+
39
+ | | |
40
+ |---|---|
41
+ | root exports | `createApprovalBridge`/`isOurApprovalBridge` (+ `ApprovalBroker`/`ApprovalRequest`/`ApprovalBridgeOptions`/`DecisionSource`/`OfficialPermissionMode`/`OfficialApprovalBridge`), `materializeOfficialMcpServer`/`officialMcpServers`/`winterMcpServerDescriptor`/`canonicalToolNames`/`OFFICIAL_MATERIALIZATION_DROPS` (+ their descriptor/schema types), `minimalOsEnvironmentFrom`/`buildOfficialChildEnv` (+ env-policy types), the containment/auth/options-template types, `containmentDispositions`/`officialDisallowedTools`, `officialBranchLabel`/`OFFICIAL_DISCLOSURES`, and `renderAttributedTurn` — all previously reachable only through `./official/index.ts`, a test-only import site. `buildOfficialOptions` uses `policy.canUseTool` verbatim and `assertOptionsInvariants` refuses anything not built by `createApprovalBridge`, so without this a host's every official-leg tool call was denied by the fail-closed default. The spawn-proxy/adapter internals (`createSupervisedSpawnProxy`, `createOfficialAdapter`, …) stay OUT — their declaration graph pulls Node-only types into a consumer that never asked for them; the door reaches the adapter through the seam, not through a root import. |
42
+ | `./testing` subpath | `createFakeKeychain`, `withHermeticHomes`, `withTempDir`, `createFakeClaudePeer`, `createFakeWinterPeer`, `HERMETIC_TRAFFIC_OPT_OUTS`, `officialCaptureEnv` — a NARROW barrel (`src/testing/host.ts`) that resolves neither `@yanlinglabs/winter-conformance` nor `@yanlinglabs/winter-provider-conformance`, so a host writing its own approval-bridge/MCP fixtures does not have to install either. The loopback fakes (`anthropicFake`, `openaiResponsesFake`, `requestsTo`, `withLoopbackFake`) and the golden-trace tooling stay on the internal, unpublished `./index.ts` barrel this repository's own tests use by relative path — a dynamic `import()` fixes their RUNTIME load without the peer, but not their TYPES, which still name it. |
43
+ | root exports (types only) | `HandoffParticipants`, `HandoffSourceOwner`, `HandoffDestinationRuntime`, `HandoffResumeTarget`, `HandoffStepReport`, `HandoffOwnerHealth`, `HandoffEligibilityLike`, `DetailedHandoffOutcome`, `HandoffBarrierDeps`, `HandoffSelection`, and `MaterializedResumeDecoratorHandle` (`HandoffBarrierDeps.decorator`'s type) — the data shapes a host actually renders a handoff plan/outcome from, not just `HandoffBarrier`/`HandoffOutcome`/`HandoffPlan` (already reachable via the seams). Pure interfaces: none pulls a `node:*` specifier into the declaration graph. |
44
+
45
+ **What `0.0.2` changed** (peer floor: `@yanlinglabs/winter-agent-sdk >=0.0.3 <0.1.0`):
46
+
47
+ | | |
48
+ |---|---|
49
+ | `peerVersions` | A host DECLARES its peers' versions — the only door inside a compiled binary, where `require.resolve` cannot see out of the bundle to read a manifest. |
50
+ | `capabilities` + `toInputShape` | The host's own MCP servers, forwarded to BOTH legs: by reference into the Winter leg's `Options.mcpServers`, and registered into the official runtime from the same declaration. One declaration, two registrations, identical canonical names. |
51
+ | `advisor` | The standing server carries Winter's four default tools, `advisor` among them, bound under the official runtime's built-in names (measured: the pin honours an alias key that is not one of its own built-ins — `docs/probes/advisor-alias.md`). `advisor` supplies the REVIEWER; the tool is always registered. |
52
+ | no tool ownership | `src/native-args.ts` and the router's messaging handlers are gone: the definitions, schemas, acceptors, handler factories and the advisor all come from `@yanlinglabs/winter-agent-sdk/tools`, and this package re-exports none of them. |
30
53
 
31
54
  ---
32
55
 
@@ -36,8 +59,22 @@ Winter SDK before its first publish.
36
59
  runtime's handle untouched.
37
60
 
38
61
  ```ts
62
+ // The host owns the capability tools and hands them over as MCP SERVERS; the router forwards the same
63
+ // servers to BOTH legs and rewrites nothing else (R-8-1). `toInputShape` is the one line of glue the
64
+ // official branch needs — its in-process server constructor takes schemas in its own validator's shape,
65
+ // and this package deliberately depends on no validator.
66
+ const sdk = createRuntimeSdk({
67
+ peers,
68
+ keychain,
69
+ vendoredOfficialRuntime,
70
+ capabilities: [computerServer, browserServer, officeServer], // your own `{ type: "sdk", name, tools, instance }`
71
+ toInputShape: (schema) => jsonSchemaToZodRawShape(schema), // one line, over the validator you already have
72
+ });
73
+
39
74
  // The Winter leg: exactly what it always was. No runtime input, so nothing is decided and nothing
40
- // is stripped — the caller's own `options` object is forwarded by reference.
75
+ // is stripped — the caller's own `options` object is forwarded by reference (with no `capabilities`
76
+ // configured, by IDENTITY; with them, a copy whose every other member is still your own object, plus
77
+ // your servers under `mcpServers`).
41
78
  for await (const message of sdk.query({ prompt: "hello" })) { /* SdkMessage */ }
42
79
 
43
80
  // The official leg: a `claude-agent` selection, plus what only a host can answer.
@@ -51,7 +88,13 @@ const query = sdk.query({
51
88
  official: {
52
89
  sessionId: "s-42", // its directory row is `session:s-42`
53
90
  base: minimalOsEnvironmentFrom(process.env),
54
- mcpServers: officialMcpServers({ /* */ }),
91
+ // OPTIONAL SINCE 0.0.2: the router materializes the standing server and your capability
92
+ // servers itself. This stays as the escape hatch, and its reach is exactly one key: an entry
93
+ // under the BRAND's own standing-server name replaces the router's (that key reaches no other
94
+ // leg, so overriding it diverges from nothing); an entry naming a forwarded CAPABILITY is a
95
+ // typed refusal on BOTH legs, because that name is on both and a silent override would leave
96
+ // the two branches running different tools under one canonical name.
97
+ // mcpServers: officialMcpServers({ /* … */ }),
55
98
  },
56
99
  },
57
100
  },
@@ -134,12 +177,19 @@ at all.
134
177
  **The official branch disables the runtime's remote feature configuration by default** (R-7b-11).
135
178
  Every official child gets `TRAFFIC_OPT_OUT_VARIABLES` — the four names are exported, so read them
136
179
  rather than trusting this sentence. Measured on the pin, same binary and same options: 25 advertised
137
- tools with the fetch, 21 without; `DesignSync`, `Monitor`, `PushNotification` and
138
- `advisor_20260301:advisor` appear only when a CDN answers. A tool surface that moves with no version
139
- moving is not a pinned artifact, so this is on unless you say otherwise: `remoteConfig: "allow"` (per
140
- query on `runtime.official`, or deployment-wide on `createRuntimeSdk({ official: { env: { } } })`)
141
- opts back in, and the choice is recorded on the session's directory row as
142
- `RuntimeDirectoryEntry.remoteConfig`.
180
+ tools with the fetch, 21 without; `DesignSync`, `Monitor`, `PushNotification` and Anthropic's own
181
+ API-side `advisor_20260301:advisor` appear only when a CDN answers that one is the vendor's server
182
+ tool, orthogonal to what follows. A tool surface that moves with no version moving is not a pinned
183
+ artifact, so this is on unless you say otherwise: `remoteConfig: "allow"` (per query on
184
+ `runtime.official`, or deployment-wide on `createRuntimeSdk({ official: { env: { } } })`) opts back
185
+ in, and the choice is recorded on the session's directory row as `RuntimeDirectoryEntry.remoteConfig`.
186
+
187
+ **Winter's own `advisor` is registered on the official branch too (R-8-1), backing Anthropic's rather
188
+ than being refused.** WS-14 §11's standing MCP server used to throw if a capability list named
189
+ `advisor` — each branch was meant to have its own, unrelated advisor. The user's tool-ownership ruling
190
+ reverses that: `mcp__<brand>__advisor` is reachable on the official branch exactly like
191
+ `send_message`/`list_agents`, independent of whether the CDN-gated API-side one above is present that
192
+ session. `docs/probes/d29-advisor.md` §6 has the full reversal and what it does and does not change.
143
193
 
144
194
  **The materialized-resume PREFERRED door is open for the pinned runtime, by measurement** (R-7b-12).
145
195
  WS-17 §8's four probes pass against 0.3.250 on darwin-arm64 and linux-x64, so a handle over that peer
@@ -242,6 +292,18 @@ was handed, the `claude-resume-<uuid>` staging root SURVIVES — the destination
242
292
  deleting a live child's `CLAUDE_CONFIG_DIR` is worse than leaving a directory behind. It is locatable
243
293
  at `outcome.target.stagingRoot` and belongs to your retention pass.
244
294
 
295
+ **Compiled hosts must declare their peers' versions.** The version matrix's second probe
296
+ (`resolved-manifest`) resolves a peer's `package.json` by walking up from `createRequire(...).resolve()`
297
+ — which cannot see outside a compiled binary's own bundle (`file:///$bunfs/...`). A host that
298
+ self-spawns its own compiled artifact and whose injected peer exports no version identity of its own
299
+ has nothing left for the matrix to read, and construction refuses. `createRuntimeSdk({ peerVersions:
300
+ { winterAgentSdk, claudeAgentSdk } })` is the door: supply both from your own vendored
301
+ `VERSIONS.json`, stamped at your own build time (WS-02 §7.1). A declared version is checked FIRST —
302
+ it wins even over a peer that exports its own identity — and still has to satisfy the same
303
+ range/exact-pin checks as either probe; it changes how the identity was discovered, not what counts
304
+ as supported. A host that runs uncompiled (plain `bun`/`node`, source or an ordinary install) never
305
+ needs this field.
306
+
245
307
  **"Exactly one runtime owns a session" is a convention here, not a mechanism.** The barrier moves
246
308
  ownership only after the destination confirms, and the transcript's producer record is authoritative —
247
309
  but the store's writer lease is re-entrant per pid and this router hosts both branches in one process,
package/dist/door.d.ts CHANGED
@@ -1,10 +1,12 @@
1
1
  import type { BrandProfile, CredentialRef, Options, ProviderSelection, Query } from "@yanlinglabs/winter-agent-sdk";
2
2
  import type { GlobalMessagingHandle } from "./messaging/router.js";
3
+ import { type ReviewerResolver } from "@yanlinglabs/winter-agent-sdk/tools";
3
4
  import type { ContainmentPolicy } from "./official/containment.js";
4
5
  import { type OfficialPermissionMode } from "./official/callbacks.js";
5
6
  import { type OfficialEnvPolicy } from "./official/env-allowlist.js";
6
7
  import { type AuthCredentialPlan } from "./official/auth.js";
7
8
  import { type OptionsTemplatePolicy } from "./official/options-template.js";
9
+ import { type InputShapeFactory, type OfficialMcpModule, type WinterMcpServerDescriptor } from "./official/mcp-descriptors.js";
8
10
  import type { RuntimeDirectory } from "./seams/directory.js";
9
11
  import type { OfficialAdapter, RemoteConfigPolicy } from "./seams/official-adapter.js";
10
12
  import type { OfficialQuery, OfficialUserMessage } from "./seams/official-sdk-shapes.js";
@@ -81,7 +83,24 @@ export interface RouterOfficialInput {
81
83
  projectKey?: string;
82
84
  /** §3's `CLAUDE_CODE_TMPDIR` — the shared per-user temp root the host derives from the brand. */
83
85
  sharedTempRoot?: string;
84
- /** §11's servers, already materialized by the host (`officialMcpServers`). */
86
+ /**
87
+ * §11's servers, already materialized by the host (`officialMcpServers`) — THE ESCAPE HATCH, AND
88
+ * EXACTLY HOW FAR IT REACHES (review r1).
89
+ *
90
+ * Since R-8 the router materializes the standing server and the constructor's capability servers
91
+ * itself (`RuntimeSdkOptions.capabilities` + `toInputShape`), so most hosts never fill this in.
92
+ *
93
+ * * THE STANDING-SERVER KEY (`brand.mcpServerName`): THE HOST WINS. A host that built its own
94
+ * standing server means that server, and the router replacing it would be the translation layer
95
+ * this package is not — the same precedence `remoteConfig` (`:148-149`) and `options` (`:153`)
96
+ * have. It is also a key the OTHER leg never receives from the router, so one branch overriding
97
+ * it diverges from nothing.
98
+ * * A FORWARDED CAPABILITY'S NAME: REFUSED, with the same `RuntimeLaunchInputError` the Winter leg
99
+ * raises for the identical collision, before any runtime is launched. `capabilities` are
100
+ * forwarded to BOTH legs, so a per-key override here would leave this branch running the host's
101
+ * server and the Winter branch running the daemon's under one name — two different tools, one
102
+ * canonical name, no error anywhere.
103
+ */
85
104
  mcpServers?: Readonly<Record<string, unknown>>;
86
105
  /** §1 profile 2's staging root. Defaults to the vendor's own `<tmpdir>/claude-resume-<resume id>`. */
87
106
  stagingRoot?: string;
@@ -160,6 +179,48 @@ export interface OfficialLegDeps {
160
179
  transcriptProjectKey: (cwd: string) => string;
161
180
  /** WS-14 §5.1's vendored runtime, from the constructor. A per-query `Options` value wins over it. */
162
181
  vendoredOfficialRuntime?: string;
182
+ /**
183
+ * The daemon's capability servers, as DESCRIPTORS (R-8 / R-8-1).
184
+ *
185
+ * DESCRIPTORS RATHER THAN THE WINTER INSTANCES, because this branch does not consume a server — it
186
+ * REGISTERS one, through its own runtime's in-process constructor. `createRuntimeSdk` reads the
187
+ * host's declaration once (`capabilityServerDescriptors`) and the leg materializes it per session,
188
+ * beside the standing server it builds from `messaging`.
189
+ *
190
+ * PER SERVER, UNDER ITS OWN NAME, so `mcp__<server>__<tool>` is the same canonical name on both legs
191
+ * — which is the whole of WS-14 §11's "registered identically into BOTH branches".
192
+ */
193
+ capabilities?: readonly WinterMcpServerDescriptor[];
194
+ /**
195
+ * The host's JSON-Schema → validator-shape bridge (`RuntimeSdkOptions.toInputShape`).
196
+ *
197
+ * IT IS WHAT SWITCHES THE ROUTER-BUILT SERVERS ON. With it, this leg registers the standing server
198
+ * (the messaging tools) and every capability server itself, and a host stops hand-materializing.
199
+ * Without it and WITH capabilities configured, the leg REFUSES: a session whose capability tools
200
+ * exist on the Winter branch and silently not on this one is the divergence §11 exists to prevent.
201
+ */
202
+ toInputShape?: InputShapeFactory;
203
+ /**
204
+ * The injected official peer, duck-typed to §11's surface (`createSdkMcpServer`/`tool`).
205
+ *
206
+ * THE SAME OBJECT `peers.claude` ALREADY IS — the seam declares only `query`, because that is all
207
+ * the launch path needs, and this is the one other member of it this package uses. A module without
208
+ * the constructor is `OfficialMcpError`, thrown where the server would have been built.
209
+ */
210
+ mcpModule?: OfficialMcpModule;
211
+ /**
212
+ * What the host supplies for the STANDING ADVISOR — the reviewer, never the tool (interim review I-5).
213
+ *
214
+ * The advisor is registered on this branch whether or not a host fills this in, because the Winter
215
+ * runtime always advertises `advisor` and two legs whose advertised sets differ by a host option is
216
+ * exactly the divergence WS-14 §11 forbids. What this supplies is the REVIEWER: absent, the default
217
+ * resolver answers `undefined`, which is WS-06 §4's ordinary tool error ("no reviewer configured"),
218
+ * not a throw and not a missing tool.
219
+ */
220
+ advisor?: {
221
+ resolveReviewer?: ReviewerResolver;
222
+ maxChars?: number;
223
+ };
163
224
  /** The adapter's own policy, so a host's `env`/`containment` choices reach the door's own builders. */
164
225
  policy?: RouterOfficialPolicy;
165
226
  /**
package/dist/errors.d.ts CHANGED
@@ -38,12 +38,20 @@ export declare class RuntimeHandoffRequiredError extends RuntimeSdkError {
38
38
  * cannot run. This one is about the DOOR's own inputs: the session id its directory row is addressed
39
39
  * by, the credential its auth family needs, the vendored runtime path §5.1 will not guess. A host
40
40
  * catching it knows to fix a call site, not a configuration.
41
+ *
42
+ * THE PREFIX IS LEG-NEUTRAL, AND IT HAD TO BECOME SO (interim review I-7). It read "the official leg
43
+ * needs `<field>`" for as long as the official leg was the only caller — and then R-8 gave the class
44
+ * three refusals that are not: `capabilities` at CONSTRUCTION (before any leg exists, and fatal to a
45
+ * Winter-only host), and `mcpServers` on the WINTER leg. A host with no official peer at all reading
46
+ * "the official leg needs `mcpServers`" is told to look at the one branch it does not use. `leg` names
47
+ * the branch when there IS one, so nothing is lost where the old sentence was right.
41
48
  */
42
49
  export declare class RuntimeLaunchInputError extends RuntimeSdkError {
43
50
  readonly field: string;
44
51
  constructor(args: {
45
52
  field: string;
46
53
  reason: string;
54
+ leg?: "official" | "winter";
47
55
  });
48
56
  }
49
57
  /**