@byollm/protocol 0.1.0-alpha.101 → 0.1.0-alpha.103

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
@@ -1,5 +1,5 @@
1
1
  > [!WARNING]
2
- > **Alpha (`0.1.0-alpha.101`) — under active development. Don't use this yet.**
2
+ > **Alpha (`0.1.0-alpha.103`) — under active development. Don't use this yet.**
3
3
  >
4
4
  > Install it deliberately: `npm install @byollm/protocol@alpha`.
5
5
  >
@@ -183,7 +183,7 @@ to know which one happened.
183
183
 
184
184
  ## Normative spec
185
185
 
186
- [`docs/protocol.md`](../../docs/protocol.md). Every MUST there carries a
186
+ [`docs/protocol.md`](https://github.com/oftomorrowinc/byollm/blob/main/docs/protocol.md). Every MUST there carries a
187
187
  conformance id that appears in this package's `MUSTS` registry.
188
188
 
189
189
  MIT
@@ -200,4 +200,11 @@ Six packages, and they are only interesting together:
200
200
  - [`@byollm/control-plane`](https://www.npmjs.com/package/@byollm/control-plane) — who may ask whom, and the policy store behind it
201
201
  - [`@byollm/conformance`](https://www.npmjs.com/package/@byollm/conformance) — the kit that proves an implementation is one — including a posture audit that holds nothing but a URL
202
202
 
203
+ ### Where the rest lives
204
+
205
+ - [GitHub](https://github.com/oftomorrowinc/byollm) — the source, and where issues go
206
+ - [byo-llm.com](https://byo-llm.com) — what this is, and why
207
+ - [byollm.cloud](https://byollm.cloud) — the hosted relay — devices, consent and billing
208
+ - [docs.byollm.cloud](https://docs.byollm.cloud) — integrating a site, end to end
209
+
203
210
  <!-- family:end -->
package/dist/index.d.ts CHANGED
@@ -2,6 +2,84 @@ import { z } from 'zod';
2
2
  import { S as StoredKeys, P as PublicIdentity } from './portable-C6rfiCXi.js';
3
3
  export { C as CONSOLE_FRAME_VERSION, a as CONSOLE_MAX_DATA_BYTES, b as ConsoleBye, c as ConsoleFrame, d as ConsoleHello, e as ConsoleOrder, f as ConsoleOrderFault, g as ConsoleOrderResult, h as ConsoleResize, i as ConsoleStdin, j as ConsoleStdout, E as ENCRYPTION_KEY_CONTEXT, k as ENVELOPE_BODY_VERSION, l as ENVELOPE_MAX_AGE_MS, m as EnvelopeBodyContext, n as EnvelopeContext, o as EnvelopeDirection, p as EnvelopeFailure, q as EnvelopeInner, O as OpenResult, r as SealedEnvelope, s as consoleDataBytes, t as consoleEnvelope, u as consoleOrder, v as cryptoReady, w as decodeConsoleData, x as decodeEnvelopeInner, y as encodeConsoleData, z as encodeEnvelopeInner, A as envelopeSignedBody, B as fingerprint, D as fromBase64Url, F as generateKeys, G as keyId, H as open, I as publicIdentityOf, J as seal, K as signWith, L as toBase64Url, M as verifyPublicIdentity, N as verifyWith } from './portable-C6rfiCXi.js';
4
4
 
5
+ /**
6
+ * What byollm.cloud can see, in the words a person reads — B039.
7
+ *
8
+ * ## Why a sentence is in a published package
9
+ *
10
+ * This one sentence has to be true in three places at once: the privacy page
11
+ * that prints it, the check beside the hub's schema that proves every recorded
12
+ * column is claimed by one of its clauses, and the page test that pins the
13
+ * wording. Two of those live in `byollm-cloud`, one in `byollm-cloud-web`, and
14
+ * **a check cannot read another repository** — so until now the sentence was
15
+ * written out three times and nothing compared the copies.
16
+ *
17
+ * That is not a tidiness problem. An enumeration is a promise, and a promise
18
+ * kept in three places drifts in two of them: the copy beside the schema is
19
+ * the one that fails when a column is added, and if it is not the copy on the
20
+ * page, the page stays reassuring and stops being true. Which is worse than
21
+ * never having made the promise.
22
+ *
23
+ * `byollm_023` named the only two shapes — a copy per repository (they
24
+ * diverge, silently) or one source both import (it goes stale, loudly, as a
25
+ * version pin). **Todd ruled shape 2 on 2026-09-17**, and the reason is the
26
+ * one that decides it: staleness is a number somebody can compare, and B164's
27
+ * cross-repository pin comparison is the thing that compares it. Divergence
28
+ * has no such number.
29
+ *
30
+ * ## Why it lives in `@byollm/protocol` and not a package of its own
31
+ *
32
+ * It was one, for a day. **Todd ruled on 2026-09-19: no seventh package** —
33
+ * *"I do think just adding it to protocol and importing that makes a ton of
34
+ * sense"* — and `packages/agreements` was deleted rather than marked private.
35
+ * Both consuming repositories already pin `@byollm/protocol`, so the sentence
36
+ * arrives on a pin they carry instead of a name they would have to adopt, and
37
+ * the cut stops carrying a package that had never been published.
38
+ *
39
+ * ## Published, not internal
40
+ *
41
+ * It ships to npm rather than living in a private repository, and that is a
42
+ * property rather than an accident: a promise about what a hosted service can
43
+ * see is worth more when anybody can install the package, read the sentence,
44
+ * and check it against what the service does. Protocol is public, so that is
45
+ * unchanged by the move.
46
+ */
47
+ /**
48
+ * The hub fence, verbatim — ruled by Todd, 2026-09-04.
49
+ *
50
+ * Asserted whole rather than by keyword wherever it is checked: "close
51
+ * enough" is not a standard a page about what we can see gets to use, and a
52
+ * paraphrase that drifted would leave the other repository checking a sentence
53
+ * nobody ships.
54
+ */
55
+ declare const HUB_FENCE: string;
56
+ /**
57
+ * A clause of {@link HUB_FENCE}, and the columns it accounts for.
58
+ *
59
+ * The mapping is here rather than beside the schema for the same reason the
60
+ * sentence is: it is a fact about the sentence, and the sentence is shared.
61
+ * What stays beside the schema is the CHECK — every recorded column must be
62
+ * claimed by one of these before that suite passes — because the columns are
63
+ * the hub's and only the hub's repository can enumerate them.
64
+ *
65
+ * Coupling copy to columns by check rather than by convention is the whole of
66
+ * the ruling: the enumeration is only a promise if something fails when the
67
+ * code outgrows it.
68
+ */
69
+ interface FenceClause {
70
+ /** The clause as it appears in {@link HUB_FENCE}. */
71
+ readonly says: string;
72
+ /** The recorded columns this clause accounts for. */
73
+ readonly covers: readonly string[];
74
+ }
75
+ /**
76
+ * Every clause, with what it covers.
77
+ *
78
+ * A column not claimed by any clause is a fact about somebody that the page
79
+ * does not mention, which is what the check beside the schema refuses.
80
+ */
81
+ declare const HUB_FENCE_CLAUSES: readonly FenceClause[];
82
+
5
83
  /**
6
84
  * Protocol version carried on every request; servers refuse what they can't
7
85
  * speak.
@@ -1555,6 +1633,44 @@ declare const ClaimedJob: z.ZodObject<{
1555
1633
  }, z.core.$strict>;
1556
1634
  }, z.core.$strict>;
1557
1635
  type ClaimedJob = z.infer<typeof ClaimedJob>;
1636
+ /**
1637
+ * The closed set, as a schema — so the values exist once.
1638
+ *
1639
+ * A bare union would mean anything that has to VALIDATE a stop reason (the
1640
+ * ingress log, and the wire when step 4 lands) retyping the four strings
1641
+ * beside it. Instruction 9: one definition, both ends, and where a consumer
1642
+ * needs a runtime check the definition has to be one it can run.
1643
+ *
1644
+ * The type below is inferred from this rather than written twice, so the
1645
+ * compiler and the validator cannot disagree about what a stop reason is.
1646
+ */
1647
+ declare const StopReasonSchema: z.ZodEnum<{
1648
+ length: "length";
1649
+ unknown: "unknown";
1650
+ end: "end";
1651
+ "stop-sequence": "stop-sequence";
1652
+ }>;
1653
+ type StopReason =
1654
+ /** The model finished on its own. */
1655
+ "end"
1656
+ /** The model stopped at its own output ceiling. */
1657
+ | "length"
1658
+ /** A configured stop token ended it. */
1659
+ | "stop-sequence"
1660
+ /**
1661
+ * The adapter cannot tell, and says so.
1662
+ *
1663
+ * **The default, and never `"end"`.** An adapter nobody has updated — or
1664
+ * one somebody adds next year — must not be able to claim completion by
1665
+ * saying nothing. If absence meant "end", every un-updated adapter would go
1666
+ * on telling exactly the lie this exists to fix, and every new adapter
1667
+ * would inherit it in silence.
1668
+ *
1669
+ * It is the opposite-boolean rule this codebase keeps arriving at: when you
1670
+ * cannot tell, guess toward silence rather than toward a claim. "We do not
1671
+ * know" is a thing a site can act on; "it finished" when it did not is not.
1672
+ */
1673
+ | "unknown";
1558
1674
  /**
1559
1675
  * The provenance that travels with every result to the delivery seam.
1560
1676
  *
@@ -1575,6 +1691,13 @@ declare const ResultProvenance: z.ZodObject<{
1575
1691
  }>;
1576
1692
  model: z.ZodString;
1577
1693
  untrusted: z.ZodBoolean;
1694
+ stop: z.ZodOptional<z.ZodEnum<{
1695
+ length: "length";
1696
+ unknown: "unknown";
1697
+ end: "end";
1698
+ "stop-sequence": "stop-sequence";
1699
+ }>>;
1700
+ stopReported: z.ZodOptional<z.ZodBoolean>;
1578
1701
  }, z.core.$strict>;
1579
1702
  type ResultProvenance = z.infer<typeof ResultProvenance>;
1580
1703
  /**
@@ -1587,6 +1710,9 @@ declare function provenanceFor(input: {
1587
1710
  runnerOwner: string;
1588
1711
  backendClass: BackendClass;
1589
1712
  model: string;
1713
+ /** From the sealed `ran`; absent on a cancelled or errored job — B260. */
1714
+ stop?: StopReason | undefined;
1715
+ stopReported?: boolean | undefined;
1590
1716
  }): ResultProvenance;
1591
1717
  /**
1592
1718
  * What the daemon did, sealed with the answer — cloud_008 §2.5.
@@ -1603,44 +1729,6 @@ declare function provenanceFor(input: {
1603
1729
  * opens them, nothing in between sees them, and the disposition check that
1604
1730
  * already compares clear-text against ciphertext extends to cover them.
1605
1731
  */
1606
- /**
1607
- * The closed set, as a schema — so the values exist once.
1608
- *
1609
- * A bare union would mean anything that has to VALIDATE a stop reason (the
1610
- * ingress log, and the wire when step 4 lands) retyping the four strings
1611
- * beside it. Instruction 9: one definition, both ends, and where a consumer
1612
- * needs a runtime check the definition has to be one it can run.
1613
- *
1614
- * The type below is inferred from this rather than written twice, so the
1615
- * compiler and the validator cannot disagree about what a stop reason is.
1616
- */
1617
- declare const StopReasonSchema: z.ZodEnum<{
1618
- length: "length";
1619
- unknown: "unknown";
1620
- end: "end";
1621
- "stop-sequence": "stop-sequence";
1622
- }>;
1623
- type StopReason =
1624
- /** The model finished on its own. */
1625
- "end"
1626
- /** The model stopped at its own output ceiling. */
1627
- | "length"
1628
- /** A configured stop token ended it. */
1629
- | "stop-sequence"
1630
- /**
1631
- * The adapter cannot tell, and says so.
1632
- *
1633
- * **The default, and never `"end"`.** An adapter nobody has updated — or
1634
- * one somebody adds next year — must not be able to claim completion by
1635
- * saying nothing. If absence meant "end", every un-updated adapter would go
1636
- * on telling exactly the lie this exists to fix, and every new adapter
1637
- * would inherit it in silence.
1638
- *
1639
- * It is the opposite-boolean rule this codebase keeps arriving at: when you
1640
- * cannot tell, guess toward silence rather than toward a claim. "We do not
1641
- * know" is a thing a site can act on; "it finished" when it did not is not.
1642
- */
1643
- | "unknown";
1644
1732
  declare const RunMetadata: z.ZodObject<{
1645
1733
  model: z.ZodString;
1646
1734
  backendClass: z.ZodEnum<{
@@ -1840,6 +1928,13 @@ declare const DeliveredResult: z.ZodObject<{
1840
1928
  }>;
1841
1929
  model: z.ZodString;
1842
1930
  untrusted: z.ZodBoolean;
1931
+ stop: z.ZodOptional<z.ZodEnum<{
1932
+ length: "length";
1933
+ unknown: "unknown";
1934
+ end: "end";
1935
+ "stop-sequence": "stop-sequence";
1936
+ }>>;
1937
+ stopReported: z.ZodOptional<z.ZodBoolean>;
1843
1938
  }, z.core.$strict>>;
1844
1939
  fallback: z.ZodOptional<z.ZodLiteral<true>>;
1845
1940
  }, z.core.$strict>;
@@ -2281,6 +2376,74 @@ declare function verifyRequest(input: {
2281
2376
  * what a single-purpose site's one purpose actually is.
2282
2377
  */
2283
2378
  declare const RESERVED_PURPOSE = "default";
2379
+ /**
2380
+ * How a purpose's work is routed — RESERVED at 0.1.0, not yet acted on (B236).
2381
+ *
2382
+ * ## Why a key nothing reads is the highest-leverage thing in this file
2383
+ *
2384
+ * {@link Purpose} is `.strict()`, and that is right: a manifest is written by
2385
+ * hand by a third party, and a typo'd key that is silently ignored is a site
2386
+ * whose consent screen quietly says something other than what its author
2387
+ * wrote. The cost of `.strict()` is that **there is no such thing as an
2388
+ * additive change to it.** A key added in 1.4 is rejected by every validator
2389
+ * built before 1.4, and the OSS repository ships the server — so those
2390
+ * validators will exist, on machines nobody can upgrade, run by people who
2391
+ * have no idea a key was added.
2392
+ *
2393
+ * The window in which this field can be introduced is therefore now: before
2394
+ * the wire is declared stable, while every validator in existence is ours.
2395
+ * After that the shape is load-bearing whether or not anything reads it.
2396
+ *
2397
+ * So the values are the ruled ones (B232 house lanes, B233 fallback lanes),
2398
+ * the schema refuses anything else, and no code branches on it. A site may
2399
+ * write `routing` today and get exactly the behaviour it gets without it,
2400
+ * which is the point: the manifest it writes today is still valid the day
2401
+ * the lanes land.
2402
+ *
2403
+ * ## What each value will mean, when something reads it
2404
+ *
2405
+ * - `user-choice` — the person maps this purpose to one of their own
2406
+ * services, and that mapping is the consent. Today's only behaviour, and
2407
+ * the default for a manifest that says nothing.
2408
+ * - `site-fixed` — the site pays for and pins the service (B232's house
2409
+ * lane). The person is told, and consents to that rather than to a mapping.
2410
+ * - `user-first-with-fallback` — the person's service if they have one, the
2411
+ * site's if they do not (B233).
2412
+ *
2413
+ * **The commitment attached to those last two** is written in the lock
2414
+ * document rather than here, because it binds behaviour this file cannot
2415
+ * enforce: a hub that does not implement a lane treats the purpose as
2416
+ * `user-choice` and SAYS SO in a field rather than silently downgrading it,
2417
+ * and a lane added later carries its own disclosure and never weakens an
2418
+ * existing lane's promise.
2419
+ */
2420
+ declare const PurposeRouting: z.ZodEnum<{
2421
+ "user-choice": "user-choice";
2422
+ "site-fixed": "site-fixed";
2423
+ "user-first-with-fallback": "user-first-with-fallback";
2424
+ }>;
2425
+ type PurposeRouting = z.infer<typeof PurposeRouting>;
2426
+ /**
2427
+ * What a purpose that does not say means.
2428
+ *
2429
+ * Named once, here, rather than written into the schema as `.default()`. A
2430
+ * zod default is applied at PARSE, which would mean every manifest read by
2431
+ * anything becomes a manifest with a `routing` key — and a hub that parses a
2432
+ * site's manifest, stores it and serves it back would be handing a key to
2433
+ * readers that predate it. The thing this field exists to avoid, performed by
2434
+ * the field itself.
2435
+ *
2436
+ * Parsing stays shape-preserving; the default is applied where the value is
2437
+ * USED, by {@link routingOf}.
2438
+ */
2439
+ declare const DEFAULT_ROUTING: PurposeRouting;
2440
+ /**
2441
+ * The routing a purpose asks for, defaulted.
2442
+ *
2443
+ * The one place the absent case is answered, so that when B232 and B233 do
2444
+ * act on this there is no second opinion about what "not stated" meant.
2445
+ */
2446
+ declare function routingOf(purpose: Purpose): PurposeRouting;
2284
2447
  declare const Purpose: z.ZodObject<{
2285
2448
  label: z.ZodString;
2286
2449
  description: z.ZodOptional<z.ZodString>;
@@ -2288,6 +2451,11 @@ declare const Purpose: z.ZodObject<{
2288
2451
  "llm.generate": "llm.generate";
2289
2452
  "llm.chat": "llm.chat";
2290
2453
  }>>;
2454
+ routing: z.ZodOptional<z.ZodEnum<{
2455
+ "user-choice": "user-choice";
2456
+ "site-fixed": "site-fixed";
2457
+ "user-first-with-fallback": "user-first-with-fallback";
2458
+ }>>;
2291
2459
  }, z.core.$strict>;
2292
2460
  type Purpose = z.infer<typeof Purpose>;
2293
2461
  /**
@@ -2320,6 +2488,11 @@ declare const Manifest: z.ZodRecord<z.ZodString, z.ZodObject<{
2320
2488
  "llm.generate": "llm.generate";
2321
2489
  "llm.chat": "llm.chat";
2322
2490
  }>>;
2491
+ routing: z.ZodOptional<z.ZodEnum<{
2492
+ "user-choice": "user-choice";
2493
+ "site-fixed": "site-fixed";
2494
+ "user-first-with-fallback": "user-first-with-fallback";
2495
+ }>>;
2323
2496
  }, z.core.$strict>>;
2324
2497
  type Manifest = z.infer<typeof Manifest>;
2325
2498
  /**
@@ -2806,4 +2979,4 @@ declare const MUST_IDS: readonly ("PAIR_ONE_USER" | "PAIR_INTERACTIVE" | "PAIR_C
2806
2979
  /** Every MUST verified a particular way. */
2807
2980
  declare function mustsVerifiedBy(kind: MustVerification): MustId[];
2808
2981
 
2809
- export { ABOUT, ABOUT_SHORT, ABOUT_SHORT_LEDE, ABOUT_SHORT_TAIL, AUDIENCES, Audience, BACKENDS, BACKEND_CLASSES, BACKEND_IDS, BackendClass, BackendCost, type BackendDescriptor, type BackendId, BackendIdSchema, CLOCK_ATTRIBUTION_MS, CLOCK_SKEW_WARN_MS, Capability, CapabilityMatrix, ChatMessage, ChatPayload, ClaimRequest, ClaimResponse, ClaimedJob, ClaimedStub, DeliveredResult, ENDPOINTS, ERROR_STATUS, type Endpoint, FetchRequest, FetchResponse, type FloorRefusal, GRANT_CONTEXT, GRANT_MAX_AGE_MS, GRANT_SIGNED_FIELDS, GeneratePayload, type GrantClaims, GrantRef, type GrantRefusal, HeartbeatRequest, HeartbeatResponse, JOB_KINDS, JobKind, JobOutcome, JobPayload, JobRefused, JobResultCanceled, JobResultError, JobResultOk, JobState, JobStub, KindedPayload, Lease, MAX_CLOCK_SKEW_MS, MAX_ENVELOPE_BYTES, MAX_PURPOSES, MAX_SUCCESSION_CHAIN, MIN_PROTOCOL_VERSION, MUSTS, MUST_IDS, Manifest, type MatchDaemon, type MatchJob, MatchRefusal, type MatchResult, type Must, type MustEnforcer, type MustId, type MustVerification, type MustVerifiedBy, OFFER_SCOPES, OfferScope, PAYLOAD_LIMITS, PROTOCOL_PREFIX, PROTOCOL_VERSION, PairPollRequest, PairPollResponse, PairRequest, PairStartRequest, PairStartResponse, type PayloadFor, PublicIdentity, Purpose, REFUSAL_MESSAGES, REFUSAL_TEXT, RESERVED_PURPOSE, RETIREMENT_WINDOW_MS, RefusalReason, ReleaseRequest, ReleaseResponse, RequestSignature, ResultDisposition, ResultProvenance, ResultRequest, ResultResponse, RunMetadata, SIZE_CLASSES, SIZE_CLASS_LIMITS, SUCCESSION_CONTEXT, SUPPORTED_PROTOCOL_VERSIONS, SealedOutcome, type SignatureFailure, SignedGrant, SizeClass, type SpendConsent, type StopReason, StopReasonSchema, StoredKeys, Succession, type SuccessionFailure, type SuccessionWalk, TERMINAL_STATES, UPDATE_OFFER_SINCE, UPGRADE_COMMAND, type VersionRefusal, WireError, WireErrorCode, WithheldKind, backendDescriptor, backendName, canTransition, canonicalRequest, checkDaemonFloor, checkProtocolVersion, classifyCost, compareVersions, declaredVersion, describeBytes, effectiveOfferScope, envelopeBytes, grantStatement, isBackendId, isCloudTaggedModel, isJobKind, isLocalHost, isTerminal, kindsOf, matchAudience, mayOfferUpdate, mentionsWireField, mustsVerifiedBy, payloadTextLength, provenanceFor, resolveCost, signGrant, signRequest, signSiteRequest, signSuccession, singlePurposeManifest, sizeClassCeiling, sizeClassOf, successionStatement, tooLargeMessage, updateOfferFor, verifyGrant, verifyLink, verifyRequest, verifySiteRequest, walkSuccession, withoutComments };
2982
+ export { ABOUT, ABOUT_SHORT, ABOUT_SHORT_LEDE, ABOUT_SHORT_TAIL, AUDIENCES, Audience, BACKENDS, BACKEND_CLASSES, BACKEND_IDS, BackendClass, BackendCost, type BackendDescriptor, type BackendId, BackendIdSchema, CLOCK_ATTRIBUTION_MS, CLOCK_SKEW_WARN_MS, Capability, CapabilityMatrix, ChatMessage, ChatPayload, ClaimRequest, ClaimResponse, ClaimedJob, ClaimedStub, DEFAULT_ROUTING, DeliveredResult, ENDPOINTS, ERROR_STATUS, type Endpoint, type FenceClause, FetchRequest, FetchResponse, type FloorRefusal, GRANT_CONTEXT, GRANT_MAX_AGE_MS, GRANT_SIGNED_FIELDS, GeneratePayload, type GrantClaims, GrantRef, type GrantRefusal, HUB_FENCE, HUB_FENCE_CLAUSES, HeartbeatRequest, HeartbeatResponse, JOB_KINDS, JobKind, JobOutcome, JobPayload, JobRefused, JobResultCanceled, JobResultError, JobResultOk, JobState, JobStub, KindedPayload, Lease, MAX_CLOCK_SKEW_MS, MAX_ENVELOPE_BYTES, MAX_PURPOSES, MAX_SUCCESSION_CHAIN, MIN_PROTOCOL_VERSION, MUSTS, MUST_IDS, Manifest, type MatchDaemon, type MatchJob, MatchRefusal, type MatchResult, type Must, type MustEnforcer, type MustId, type MustVerification, type MustVerifiedBy, OFFER_SCOPES, OfferScope, PAYLOAD_LIMITS, PROTOCOL_PREFIX, PROTOCOL_VERSION, PairPollRequest, PairPollResponse, PairRequest, PairStartRequest, PairStartResponse, type PayloadFor, PublicIdentity, Purpose, PurposeRouting, REFUSAL_MESSAGES, REFUSAL_TEXT, RESERVED_PURPOSE, RETIREMENT_WINDOW_MS, RefusalReason, ReleaseRequest, ReleaseResponse, RequestSignature, ResultDisposition, ResultProvenance, ResultRequest, ResultResponse, RunMetadata, SIZE_CLASSES, SIZE_CLASS_LIMITS, SUCCESSION_CONTEXT, SUPPORTED_PROTOCOL_VERSIONS, SealedOutcome, type SignatureFailure, SignedGrant, SizeClass, type SpendConsent, type StopReason, StopReasonSchema, StoredKeys, Succession, type SuccessionFailure, type SuccessionWalk, TERMINAL_STATES, UPDATE_OFFER_SINCE, UPGRADE_COMMAND, type VersionRefusal, WireError, WireErrorCode, WithheldKind, backendDescriptor, backendName, canTransition, canonicalRequest, checkDaemonFloor, checkProtocolVersion, classifyCost, compareVersions, declaredVersion, describeBytes, effectiveOfferScope, envelopeBytes, grantStatement, isBackendId, isCloudTaggedModel, isJobKind, isLocalHost, isTerminal, kindsOf, matchAudience, mayOfferUpdate, mentionsWireField, mustsVerifiedBy, payloadTextLength, provenanceFor, resolveCost, routingOf, signGrant, signRequest, signSiteRequest, signSuccession, singlePurposeManifest, sizeClassCeiling, sizeClassOf, successionStatement, tooLargeMessage, updateOfferFor, verifyGrant, verifyLink, verifyRequest, verifySiteRequest, walkSuccession, withoutComments };
package/dist/index.js CHANGED
@@ -21,6 +21,34 @@ import {
21
21
  toBase64Url
22
22
  } from "./chunk-J3HTAGMX.js";
23
23
 
24
+ // src/hub-fence.ts
25
+ var HUB_FENCE = "byollm.cloud sees only what routing and metering need: which devices and sites are connected, each job's kind, size, and outcome, and timestamps. Never prompts, never answers, never credentials.";
26
+ var HUB_FENCE_CLAUSES = Object.freeze([
27
+ {
28
+ says: "which devices and sites are connected",
29
+ covers: Object.freeze(["owner_id", "site_id", "member_id"])
30
+ },
31
+ {
32
+ says: "each job's kind, size, and outcome",
33
+ covers: Object.freeze(["kind", "size_class", "disposition", "bytes"])
34
+ },
35
+ {
36
+ says: "and timestamps",
37
+ covers: Object.freeze(["at", "month", "updated_at"])
38
+ },
39
+ /*
40
+ * Ruled plumbing, not a new fact about a person — Todd, 2026-09-04.
41
+ *
42
+ * Named here rather than silently tolerated. A column exempted by a ruling
43
+ * and a column nobody noticed look identical to a check that just skips
44
+ * them, and only one of those is a decision.
45
+ */
46
+ {
47
+ says: "(metering plumbing, ruled unenumerated)",
48
+ covers: Object.freeze(["id", "job_id", "price_version"])
49
+ }
50
+ ]);
51
+
24
52
  // src/wire.ts
25
53
  import { z as z9 } from "zod";
26
54
 
@@ -888,6 +916,12 @@ var ClaimedJob = z7.object({
888
916
  deadlineAt: z7.number().int().positive().optional(),
889
917
  lease: Lease
890
918
  }).strict();
919
+ var StopReasonSchema = z7.enum([
920
+ "end",
921
+ "length",
922
+ "stop-sequence",
923
+ "unknown"
924
+ ]);
891
925
  var ResultProvenance = z7.object({
892
926
  /** The audience the job ran under. */
893
927
  audience: Audience,
@@ -900,10 +934,33 @@ var ResultProvenance = z7.object({
900
934
  /** The model the runner reports having used. */
901
935
  model: z7.string().min(1),
902
936
  /**
903
- * False only for `self` jobs. When true the app MUST treat `text` as
937
+ * False only for `private` jobs. When true the app MUST treat `text` as
904
938
  * untrusted third-party content.
905
939
  */
906
- untrusted: z7.boolean()
940
+ untrusted: z7.boolean(),
941
+ /**
942
+ * Why generation stopped, carried from the sealed `ran` — B260.
943
+ *
944
+ * It was sealed by the device, opened at the site, used to build this
945
+ * object, and **dropped**: `provenanceFor` took five fields and `stop` was
946
+ * not one of them, on either lane. So no site could ever see it, and a
947
+ * `length` result — an answer cut off mid-thought — arrived
948
+ * indistinguishable from a complete one. Kevin's team found it on a
949
+ * book-translation pipeline, which is the difference between shipping a
950
+ * chapter and shipping half of one without knowing.
951
+ *
952
+ * Optional here because it is optional there, and for the same reason:
953
+ * absent exactly where it would be a fact about nothing.
954
+ */
955
+ stop: StopReasonSchema.optional(),
956
+ /**
957
+ * Whether the adapter could report a stop signal at all — carried for the
958
+ * reason {@link RunMetadata.stopReported} gives: `unknown` is two facts,
959
+ * and a site told only `unknown` says "we do not know why this stopped"
960
+ * for a `claude-cli` job forever, which is true, and for a
961
+ * `content_filter` result, which is not the same thing at all.
962
+ */
963
+ stopReported: z7.boolean().optional()
907
964
  }).strict();
908
965
  function provenanceFor(input) {
909
966
  return {
@@ -912,15 +969,14 @@ function provenanceFor(input) {
912
969
  runnerOwner: input.runnerOwner,
913
970
  backendClass: input.backendClass,
914
971
  model: input.model,
915
- untrusted: input.audience !== "private"
972
+ untrusted: input.audience !== "private",
973
+ /* Spread rather than assigned: `exactOptionalPropertyTypes` makes an
974
+ explicit `undefined` a different thing from an absent key, and this
975
+ object is `.strict()`. */
976
+ ...input.stop === void 0 ? {} : { stop: input.stop },
977
+ ...input.stopReported === void 0 ? {} : { stopReported: input.stopReported }
916
978
  };
917
979
  }
918
- var StopReasonSchema = z7.enum([
919
- "end",
920
- "length",
921
- "stop-sequence",
922
- "unknown"
923
- ]);
924
980
  var RunMetadata = z7.object({
925
981
  /** Which model actually served it. */
926
982
  model: z7.string().min(1),
@@ -2181,6 +2237,15 @@ var PurposeKey = z11.string().regex(
2181
2237
  /^[a-z0-9][a-z0-9-]*$/,
2182
2238
  "a purpose key is a lowercase slug \u2014 letters, digits and hyphens"
2183
2239
  ).max(64);
2240
+ var PurposeRouting = z11.enum([
2241
+ "user-choice",
2242
+ "site-fixed",
2243
+ "user-first-with-fallback"
2244
+ ]);
2245
+ var DEFAULT_ROUTING = "user-choice";
2246
+ function routingOf(purpose) {
2247
+ return purpose.routing ?? DEFAULT_ROUTING;
2248
+ }
2184
2249
  var Purpose = z11.object({
2185
2250
  /**
2186
2251
  * What a person reads on the consent screen. The only rendered field.
@@ -2201,7 +2266,16 @@ var Purpose = z11.object({
2201
2266
  */
2202
2267
  kinds: z11.array(JobKind).min(1).max(JOB_KINDS.length).refine((kinds) => new Set(kinds).size === kinds.length, {
2203
2268
  message: "a purpose lists each kind once"
2204
- })
2269
+ }),
2270
+ /**
2271
+ * How this purpose is routed — RESERVED, validated, not acted on (B236).
2272
+ *
2273
+ * Optional rather than defaulted, so that a manifest without it parses to
2274
+ * a manifest without it. See {@link PurposeRouting} for why a key nothing
2275
+ * reads is worth adding before launch, and {@link routingOf} for the one
2276
+ * place "not stated" is answered.
2277
+ */
2278
+ routing: PurposeRouting.optional()
2205
2279
  }).strict();
2206
2280
  var MAX_PURPOSES = 32;
2207
2281
  var Manifest = z11.record(PurposeKey, Purpose).refine((manifest) => Object.keys(manifest).length > 0, {
@@ -2683,6 +2757,7 @@ export {
2683
2757
  ConsoleResize,
2684
2758
  ConsoleStdin,
2685
2759
  ConsoleStdout,
2760
+ DEFAULT_ROUTING,
2686
2761
  DeliveredResult,
2687
2762
  ENCRYPTION_KEY_CONTEXT,
2688
2763
  ENDPOINTS,
@@ -2697,6 +2772,8 @@ export {
2697
2772
  GRANT_SIGNED_FIELDS,
2698
2773
  GeneratePayload,
2699
2774
  GrantRef,
2775
+ HUB_FENCE,
2776
+ HUB_FENCE_CLAUSES,
2700
2777
  HeartbeatRequest,
2701
2778
  HeartbeatResponse,
2702
2779
  JOB_KINDS,
@@ -2732,6 +2809,7 @@ export {
2732
2809
  PairStartResponse,
2733
2810
  PublicIdentity,
2734
2811
  Purpose,
2812
+ PurposeRouting,
2735
2813
  REFUSAL_MESSAGES,
2736
2814
  REFUSAL_TEXT,
2737
2815
  RESERVED_PURPOSE,
@@ -2803,6 +2881,7 @@ export {
2803
2881
  provenanceFor,
2804
2882
  publicIdentityOf,
2805
2883
  resolveCost,
2884
+ routingOf,
2806
2885
  seal,
2807
2886
  signGrant,
2808
2887
  signRequest,