@yanlinglabs/winter-provider-conformance 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.
@@ -0,0 +1,87 @@
1
+ import type { ProviderAdapter, ProviderContext } from "@yanlinglabs/winter-provider-runtime";
2
+ import type { WinterModelDescriptor } from "@yanlinglabs/winter-provider-catalog";
3
+ /**
4
+ * A case's OWN assertion failure — the one error whose `.message` the runner prints (review round 1,
5
+ * I1).
6
+ *
7
+ * The distinction is not stylistic. Every message constructed with this class is Winter-authored
8
+ * text in this file, built from measurements: a stop reason, a byte count, a normalized error code.
9
+ * Every OTHER error reaching the runner came from an adapter, and an adapter failure is a
10
+ * `ProviderRequestError` whose message embeds a 200-character snippet of the provider's response
11
+ * body (`provider-runtime/src/errors.ts`). That snippet is scrubbed of credential-shaped strings, but
12
+ * it is still response CONTENT, and the constraint on this gate's output is verbatim: identifiers and
13
+ * byte counts only.
14
+ *
15
+ * So the type IS the permission to print. A message that is safe to render is one this file wrote.
16
+ */
17
+ export declare class LiveCaseAssertionError extends Error {
18
+ constructor(message: string);
19
+ }
20
+ export type LiveCaseId = "discovery" | "text-turn" | "tool-round" | "thinking-summary" | "count-tokens" | "honest-identity-inference";
21
+ export interface LiveCaseSpec {
22
+ id: LiveCaseId;
23
+ question: string;
24
+ }
25
+ /** The legs the gate runs, in order: cheapest and least stateful first. */
26
+ export declare const LIVE_CASES: readonly LiveCaseSpec[];
27
+ /**
28
+ * The auth-shaped keys a refused inference request's body may be reported by NAME (WS-13b §4).
29
+ *
30
+ * AN ALLOWLIST, and it is the whole safety argument for reporting anything at all. A provider's error
31
+ * body is content and this gate never prints it (`live/index.ts`'s rendering rule) — but when a
32
+ * subscription bearer is refused, the ONE thing an operator needs is which auth dimension the vendor
33
+ * says was missing, and that is a handful of named scalar fields. Anything not named here, including
34
+ * every human-readable message, is dropped.
35
+ *
36
+ * `x_xai_token_auth` and `auth_kind` are the two xAI's own proxy reports (Lane O's capture); `scope`
37
+ * and `token_auth` are the neighbouring spellings the same family uses.
38
+ */
39
+ export declare const AUTH_DIMENSION_FIELDS: readonly string[];
40
+ /**
41
+ * Pulls ONLY the allowlisted auth dimensions out of an error message, as `field=value`.
42
+ *
43
+ * Exported for its own unit test, because "a marker elsewhere in the same body never survives" is the
44
+ * property that makes this safe and it must be falsifiable without a vendor. Values are bounded to a
45
+ * scalar shape (`[\w.:/-]+` — the `/` admits a scoped value like `grok-cli:access` written as a path
46
+ * and a bare URL host), so a field whose value is a sentence contributes only its first token.
47
+ */
48
+ export declare function authDimensionsOf(message: string): string[];
49
+ export interface LiveCaseContext {
50
+ /** The catalog PROVIDER id. Named in a report's remediation, so it is threaded rather than assumed. */
51
+ providerId: string;
52
+ adapter: ProviderAdapter;
53
+ ctx: ProviderContext;
54
+ /** The provider-local id that goes on the wire (never the catalog key). */
55
+ model: string;
56
+ /** Absent only for an `allowUnlisted` pass-through, in which case every capability-gated case skips. */
57
+ descriptor?: WinterModelDescriptor;
58
+ /**
59
+ * The PROVIDER row's pricing basis (WS-13b §1) — a provider fact, which is why it does not come off
60
+ * the model descriptor. The inference-path reversion case is about an entitlement, and
61
+ * `subscription` is what "an entitlement rather than a key" means in catalog terms.
62
+ */
63
+ pricingBasis?: "token" | "subscription" | "free";
64
+ /**
65
+ * WHICH DOCUMENTED THIRD-PARTY PATH this run's credential came down (WS-13b §1), supplied by the
66
+ * gate that planned the target.
67
+ *
68
+ * It is the discriminator the reversion case needs and the one `pricingBasis` cannot be:
69
+ * `subscription` catches four rows and two of them (`clinepass`, `kimi-coding`) are ordinary
70
+ * API-KEY products with a seat price. Reporting a bad key on one of those as "the vendor rejected
71
+ * Winter's identity" would be a false alarm about the one thing this phase is careful about.
72
+ *
73
+ * `ProviderContext.authRef` cannot answer it: its `kind` is a LOCATOR (`keychain`/`env`/…), never
74
+ * the material's — a host may legitimately keep an api key in the Keychain. Absent, the case
75
+ * declines rather than guessing.
76
+ */
77
+ targetKind?: "api-key" | "oauth" | "keyless";
78
+ signal?: AbortSignal;
79
+ }
80
+ export type LiveCaseResult = {
81
+ status: "ok";
82
+ detail: string;
83
+ } | {
84
+ status: "skipped";
85
+ detail: string;
86
+ };
87
+ export declare const LIVE_CASE_IMPLS: Record<LiveCaseId, (ctx: LiveCaseContext) => Promise<LiveCaseResult>>;
@@ -0,0 +1,101 @@
1
+ import type { ProviderAdapter, ProviderContext } from "@yanlinglabs/winter-provider-runtime";
2
+ import type { WinterModelDescriptor } from "@yanlinglabs/winter-provider-catalog";
3
+ import { LIVE_CASES, LiveCaseAssertionError, type LiveCaseContext, type LiveCaseId, type LiveCaseSpec } from "./cases.js";
4
+ export { LIVE_CASES, LiveCaseAssertionError };
5
+ export type { LiveCaseContext, LiveCaseId, LiveCaseSpec };
6
+ export interface LiveCaseOutcome {
7
+ id: LiveCaseId;
8
+ status: "ok" | "skipped" | "failed";
9
+ detail: string;
10
+ /** Wall-clock, because a live gate's most common real failure is "it answered, eventually". */
11
+ ms: number;
12
+ }
13
+ export interface LiveReport {
14
+ providerId: string;
15
+ adapterId: string;
16
+ adapterVersion: string;
17
+ /** The catalog key, so a reader can tell which row this run is evidence for. */
18
+ modelKey: string;
19
+ outcomes: LiveCaseOutcome[];
20
+ /** True when nothing FAILED. A skip is a recorded capability fact, not a failure. */
21
+ ok: boolean;
22
+ }
23
+ export interface RunLiveCasesOptions {
24
+ providerId: string;
25
+ modelKey: string;
26
+ adapter: ProviderAdapter;
27
+ ctx: ProviderContext;
28
+ /** The provider-local id that goes on the wire. */
29
+ model: string;
30
+ descriptor?: WinterModelDescriptor;
31
+ /** The PROVIDER row's pricing basis (WS-13b §1). Gates the inference-path reversion case. */
32
+ pricingBasis?: "token" | "subscription" | "free";
33
+ /** Which documented third-party path this run's credential came down. `runLiveTarget` supplies it from the gate's own plan. */
34
+ targetKind?: LiveTargetKindLabel;
35
+ signal?: AbortSignal;
36
+ /** Progress, one line per case as it finishes. Identifiers and counts only — the case details already obey that rule. */
37
+ onProgress?: (outcome: LiveCaseOutcome) => void;
38
+ }
39
+ export declare function runLiveCases(opts: RunLiveCasesOptions): Promise<LiveReport>;
40
+ /** How the target's credential was named — WS-13b §1's three documented third-party paths. Mirrors the gate's `LiveTargetKind`. */
41
+ export type LiveTargetKindLabel = "api-key" | "oauth" | "keyless";
42
+ export interface LiveRowSummary {
43
+ providerId: string;
44
+ /** The CATALOG KEY (`<providerId>/<model>`), never the provider-local id and never a display name. */
45
+ model: string;
46
+ kind: LiveTargetKindLabel;
47
+ /** True when no case FAILED. A skipped case is a recorded capability fact, not a failure. */
48
+ ok: boolean;
49
+ /** The summed wall-clock of every case. A live gate's most common real failure is "it answered, eventually". */
50
+ latencyMs: number;
51
+ /**
52
+ * True only when the tool round ran and PASSED.
53
+ *
54
+ * A skip therefore reads `false` — deliberately, because the question this column answers is "can
55
+ * this row be driven agentically?", and "the descriptor says it cannot" is a no. The per-case line
56
+ * above always says WHICH of the two it was, so the row is a summary and never the whole story.
57
+ */
58
+ toolCallOk: boolean;
59
+ /**
60
+ * The identity string THIS BUILD sends (`winterUserAgent()`), recorded beside the result so a live
61
+ * run's output carries the identity claim WS-13b §1 makes.
62
+ *
63
+ * It is not a wire observation — this runner never sees an outgoing header. That Winter's own
64
+ * user-agent is genuinely on every family's requests is pinned by the corpus (`corpus/*.test.ts`,
65
+ * "every request carries Winter's OWN user-agent"), against the live request a fake received.
66
+ */
67
+ identityHeader: string;
68
+ /**
69
+ * The row's ADMISSION EVIDENCE TIER (fix-wave R-FW-3, condition (b)).
70
+ *
71
+ * Printed because promotion is TWO-KEY: a live pass alone does not promote a row — it takes a live
72
+ * pass AND a fetched vendor document, with the citation upgraded in the same reviewed commit. A
73
+ * green row from a `pinned-upstream` provider says "the path works", not "the evidence is
74
+ * complete", and an operator reading a pasted row is exactly who would otherwise promote it by
75
+ * habit.
76
+ *
77
+ * A catalog fact, read off the resolved provider row — never re-derived here.
78
+ */
79
+ admissionTier: string;
80
+ }
81
+ export interface LiveRowSummaryOptions {
82
+ kind: LiveTargetKindLabel;
83
+ identityHeader: string;
84
+ admissionTier: string;
85
+ }
86
+ /** Folds a finished `LiveReport` into its one-line row. Pure: no adapter, no endpoint, no clock. */
87
+ export declare function liveRowSummary(report: LiveReport, opts: LiveRowSummaryOptions): LiveRowSummary;
88
+ /** `key=value` pairs, in a fixed order, so a run's rows grep and diff. */
89
+ export declare function formatLiveRow(row: LiveRowSummary): string;
90
+ /**
91
+ * The per-target case: run every live leg against one target, and fold the result into its row.
92
+ *
93
+ * One call so the gate cannot run the cases and then forget the row, or report a row built from
94
+ * something other than the run it names.
95
+ */
96
+ export declare function runLiveTarget(opts: RunLiveCasesOptions & LiveRowSummaryOptions): Promise<{
97
+ report: LiveReport;
98
+ row: LiveRowSummary;
99
+ }>;
100
+ /** One line per case. Identifiers, counts and durations only — never a byte of what a provider returned. */
101
+ export declare function formatLiveReport(report: LiveReport): string;
package/package.json ADDED
@@ -0,0 +1,50 @@
1
+ {
2
+ "name": "@yanlinglabs/winter-provider-conformance",
3
+ "version": "0.0.2",
4
+ "license": "MIT",
5
+ "type": "module",
6
+ "engines": {
7
+ "bun": ">=1.2"
8
+ },
9
+ "repository": {
10
+ "type": "git",
11
+ "url": "git+https://github.com/yanlingLabs/winter-agent-sdk.git",
12
+ "directory": "packages/provider-conformance"
13
+ },
14
+ "homepage": "https://github.com/yanlingLabs/winter-agent-sdk",
15
+ "bugs": {
16
+ "url": "https://github.com/yanlingLabs/winter-agent-sdk/issues"
17
+ },
18
+ "main": "./dist/index.js",
19
+ "types": "./dist/index.d.ts",
20
+ "exports": {
21
+ ".": {
22
+ "types": "./dist/index.d.ts",
23
+ "default": "./dist/index.js"
24
+ },
25
+ "./fakes": {
26
+ "types": "./dist/fakes/index.d.ts",
27
+ "default": "./dist/fakes/index.js"
28
+ }
29
+ },
30
+ "files": [
31
+ "dist",
32
+ "NOTICE",
33
+ "README.md",
34
+ "LICENSE"
35
+ ],
36
+ "publishConfig": {
37
+ "access": "restricted"
38
+ },
39
+ "winter": {
40
+ "publish": {
41
+ "npm": true,
42
+ "harness": true
43
+ }
44
+ },
45
+ "dependencies": {
46
+ "@yanlinglabs/winter-provider-catalog": "0.0.2",
47
+ "@yanlinglabs/winter-provider-runtime": "0.0.2"
48
+ },
49
+ "scripts": {}
50
+ }