@byollm/protocol 0.1.0-alpha.84 → 0.1.0-alpha.86

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.84`) — under active development. Don't use this yet.**
2
+ > **Alpha (`0.1.0-alpha.86`) — under active development. Don't use this yet.**
3
3
  >
4
4
  > Install it deliberately: `npm install @byollm/protocol@alpha`.
5
5
  >
package/dist/index.d.ts CHANGED
@@ -845,6 +845,38 @@ declare function updateOfferFor(input: {
845
845
  }): {
846
846
  readonly updateTo?: string;
847
847
  };
848
+ /**
849
+ * Source with its comments removed, for the checks below.
850
+ *
851
+ * Comments go first because a rule about a field has to be explainable in
852
+ * prose beside the code it governs — and the first version of the relay's
853
+ * fence test flagged its own explanation, which was written to say there
854
+ * must be no such write anywhere.
855
+ */
856
+ declare function withoutComments(source: string): string;
857
+ /**
858
+ * Does this source mention a wire field at all?
859
+ *
860
+ * **A word, not a spelling.** The relay's fence test shipped scanning for
861
+ * `/\bupdateTo\s*:/`, which is one syntax of one way to set a property —
862
+ * `"updateTo": offer`, `res.updateTo = offer`, `res["updateTo"] = offer` and
863
+ * shorthand in a spread all walk straight past it. The check's claim was
864
+ * "there is nowhere else to call from"; what it enforced was a spelling.
865
+ * That is our own recurring law, and this is its sixth instance — the first
866
+ * inside a test written to be structural rather than remembered.
867
+ *
868
+ * So the question this asks is deliberately blunter than the rule it serves.
869
+ * A mention is not a write, and the false positives are the point: anything
870
+ * that names the field in code has to be looked at by a person, because the
871
+ * cost of the miss is every daemon that predates the field rejecting every
872
+ * heartbeat.
873
+ *
874
+ * Lives here, beside {@link updateOfferFor}, so the hub and the relay run one
875
+ * copy rather than two that drift — the field's hazard is fleet-wide and it
876
+ * crosses a repository boundary, which is exactly where a duplicated rule
877
+ * stops being the same rule.
878
+ */
879
+ declare function mentionsWireField(source: string, field: string): boolean;
848
880
 
849
881
  /** The full description — five sections, plus why it matters. */
850
882
  declare const ABOUT = "# About BYOLLM\n\n**What BYOLLM is**\n\nBYOLLM lets you use your own AI on websites. You install one small program on\nyour computer. Then, websites that support BYOLLM can use the AI you already\nhave \u2014 a free model running on your machine, or an AI service you already pay\nfor \u2014 instead of the website paying for AI and passing the cost to you.\n\n**Why it matters**\n\nFor you:\n\n- Your favorite model, everywhere you go.\n- New models the moment you get them \u2013 not when a site gets around to adding\n them.\n- Encrypted end-to-end. Your prompts go to your own device; byollm.cloud can't\n read them.\n- Sites never learn which model you use, and your subscriptions are never\n shared.\n- Pay less. Sites that don't pay for AI can charge you less \u2013 or nothing.\n\nFor sites and developers:\n\n- Zero AI bills. Your users bring their own compute.\n- No floating money \u2013 you don't pay LLM bills up front and hope to collect\n later, and you never ask people to prepay just to try you.\n- Free trials that cost you nothing to offer.\n- Ship the AI features you kept private for fear of the API bill.\n- One small integration. Your users choose the models.\n\n**Your device**\n\nThe `byollm` program runs on your computer. It knows which AI services you have\nset up: free open-source models on your machine, metered services you pay per\nuse, or your own subscriptions like Claude Pro/Max. When a website you have\nenabled sends work, your device runs it with the service you chose. Your\nprompts are encrypted end-to-end to your own device. byollm.cloud passes them\nalong and cannot read them.\n\n**Sites**\n\nA website that wants to use BYOLLM says what it needs \u2014 \"writing help,\" \"chat,\"\nand so on. When you connect the site, you pick which of your services answers\neach one. The site never learns which model you use. You can turn a site off at\nany time, and it stops getting your work.\n\n**Teams (optional)**\n\nA team lets you share what runs on your devices with people you name \u2014 the free\nopen-source models on your machine, or a metered service with a spending limit\nyou set. Your subscription accounts (like Claude Pro/Max) are never shared with\nanyone. That is a rule, not a setting.\n\n**byollm.cloud (or your own relay)**\n\nMany sites, many devices, many people. byollm.cloud keeps track of who has\nallowed what and sends each job to the right device. It never sees your\nprompts. If you would rather run this part yourself, the relay is open source \u2014\nyou can run your own instead of using byollm.cloud.";
@@ -1796,6 +1828,39 @@ declare const MAX_ENVELOPE_BYTES: number;
1796
1828
  * this function is that one number answers both questions.
1797
1829
  */
1798
1830
  declare function envelopeBytes(envelope: unknown): number;
1831
+ /**
1832
+ * A size somebody can act on, rounded **up** and only ever up.
1833
+ *
1834
+ * The rounding rule, in one place, because it has now been got wrong twice —
1835
+ * B072. `toFixed` rounds to nearest, so a message one byte over the line
1836
+ * printed "this message is 10.5 MB and the limit is 10.5 MB": a refusal that
1837
+ * reads as a contradiction, given to somebody who now has no idea what to
1838
+ * change.
1839
+ *
1840
+ * The relay found that, fixed it, and wrote the reasoning down beside the
1841
+ * fix. The SDK's refusal then rediscovered the identical bug, because it
1842
+ * copied the SENTENCE rather than the function — which is the same shape as
1843
+ * `MAX_BODY_BYTES` diverging from `MAX_ENVELOPE_BYTES`, and the same answer:
1844
+ * neither side holds the rule.
1845
+ *
1846
+ * Up is also the honest direction. Understating how far over a message is
1847
+ * sends somebody to trim a hundred bytes off something that needs to lose a
1848
+ * megabyte; overstating by a tenth costs them nothing.
1849
+ */
1850
+ declare function describeBytes(bytes: number): string;
1851
+ /**
1852
+ * What a message that is too big is told, minus anything about pricing.
1853
+ *
1854
+ * Shared because both planes refuse the same thing for the same reason. What
1855
+ * is NOT shared is the relay's "every plan has the same ceiling" — a hosted
1856
+ * sentence, and meaningless to somebody self-hosting the direct lane, where
1857
+ * there are no plans. One rule, two audiences: the rule travels, the words
1858
+ * about our billing do not.
1859
+ */
1860
+ declare function tooLargeMessage(input: {
1861
+ readonly bytes: number;
1862
+ readonly limit: number;
1863
+ }): string;
1799
1864
  /** Where the bucket boundaries sit, in characters of payload text. */
1800
1865
  declare const SIZE_CLASS_LIMITS: Readonly<{
1801
1866
  small: 4000;
@@ -2810,4 +2875,4 @@ declare const MUST_IDS: readonly ("PAIR_ONE_USER" | "PAIR_INTERACTIVE" | "PAIR_C
2810
2875
  /** Every MUST verified a particular way. */
2811
2876
  declare function mustsVerifiedBy(kind: MustVerification): MustId[];
2812
2877
 
2813
- 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, ENCRYPTION_KEY_CONTEXT, ENDPOINTS, ENVELOPE_MAX_AGE_MS, ERROR_STATUS, type Endpoint, type EnvelopeContext, EnvelopeDirection, type EnvelopeFailure, 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, type OpenResult, PAYLOAD_LIMITS, PROTOCOL_PREFIX, PROTOCOL_VERSION, PairPollRequest, PairPollResponse, PairRequest, PairStartRequest, PairStartResponse, type PayloadFor, PublicIdentity, Purpose, REFUSAL_MESSAGES, RESERVED_PURPOSE, RETIREMENT_WINDOW_MS, RefusalReason, ReleaseRequest, ReleaseResponse, RequestSignature, ResultDisposition, ResultProvenance, ResultRequest, ResultResponse, RunMetadata, SIZE_CLASSES, SIZE_CLASS_LIMITS, SUCCESSION_CONTEXT, SUPPORTED_PROTOCOL_VERSIONS, SealedEnvelope, SealedOutcome, type SignatureFailure, SignedGrant, SizeClass, type SpendConsent, 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, cryptoReady, declaredVersion, effectiveOfferScope, envelopeBytes, fingerprint, generateKeys, grantStatement, isBackendId, isCloudTaggedModel, isJobKind, isLocalHost, isTerminal, keyId, kindsOf, matchAudience, mayOfferUpdate, mustsVerifiedBy, open, payloadTextLength, provenanceFor, publicIdentityOf, resolveCost, seal, signGrant, signRequest, signSiteRequest, signSuccession, signWith, singlePurposeManifest, sizeClassCeiling, sizeClassOf, successionStatement, updateOfferFor, verifyGrant, verifyLink, verifyPublicIdentity, verifyRequest, verifySiteRequest, verifyWith, walkSuccession };
2878
+ 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, ENCRYPTION_KEY_CONTEXT, ENDPOINTS, ENVELOPE_MAX_AGE_MS, ERROR_STATUS, type Endpoint, type EnvelopeContext, EnvelopeDirection, type EnvelopeFailure, 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, type OpenResult, PAYLOAD_LIMITS, PROTOCOL_PREFIX, PROTOCOL_VERSION, PairPollRequest, PairPollResponse, PairRequest, PairStartRequest, PairStartResponse, type PayloadFor, PublicIdentity, Purpose, REFUSAL_MESSAGES, RESERVED_PURPOSE, RETIREMENT_WINDOW_MS, RefusalReason, ReleaseRequest, ReleaseResponse, RequestSignature, ResultDisposition, ResultProvenance, ResultRequest, ResultResponse, RunMetadata, SIZE_CLASSES, SIZE_CLASS_LIMITS, SUCCESSION_CONTEXT, SUPPORTED_PROTOCOL_VERSIONS, SealedEnvelope, SealedOutcome, type SignatureFailure, SignedGrant, SizeClass, type SpendConsent, 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, cryptoReady, declaredVersion, describeBytes, effectiveOfferScope, envelopeBytes, fingerprint, generateKeys, grantStatement, isBackendId, isCloudTaggedModel, isJobKind, isLocalHost, isTerminal, keyId, kindsOf, matchAudience, mayOfferUpdate, mentionsWireField, mustsVerifiedBy, open, payloadTextLength, provenanceFor, publicIdentityOf, resolveCost, seal, signGrant, signRequest, signSiteRequest, signSuccession, signWith, singlePurposeManifest, sizeClassCeiling, sizeClassOf, successionStatement, tooLargeMessage, updateOfferFor, verifyGrant, verifyLink, verifyPublicIdentity, verifyRequest, verifySiteRequest, verifyWith, walkSuccession, withoutComments };
package/dist/index.js CHANGED
@@ -905,6 +905,12 @@ function envelopeBytes(envelope) {
905
905
  const serialised = JSON.stringify(envelope);
906
906
  return serialised === void 0 ? 0 : serialised.length;
907
907
  }
908
+ function describeBytes(bytes) {
909
+ return `${(Math.ceil(bytes / (1024 * 1024) * 10) / 10).toFixed(1)} MB`;
910
+ }
911
+ function tooLargeMessage(input) {
912
+ return `this message is ${describeBytes(input.bytes)} and the limit is ${describeBytes(input.limit)} \u2014 it is a limit on one message rather than on how many you send. Split the work into smaller jobs and send them separately.`;
913
+ }
908
914
  var SIZE_CLASS_LIMITS = Object.freeze({
909
915
  small: 4e3,
910
916
  medium: 64e3,
@@ -1896,6 +1902,12 @@ function updateOfferFor(input) {
1896
1902
  function exactOffer(value) {
1897
1903
  return /^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$/.test(value) ? value : void 0;
1898
1904
  }
1905
+ function withoutComments(source) {
1906
+ return source.replace(/\/\*[\s\S]*?\*\//g, " ").replace(/^[ \t]*\/\/.*$/gm, " ");
1907
+ }
1908
+ function mentionsWireField(source, field) {
1909
+ return new RegExp(`\\b${field}\\b`).test(withoutComments(source));
1910
+ }
1899
1911
 
1900
1912
  // src/about.ts
1901
1913
  var ABOUT = `# About BYOLLM
@@ -2621,6 +2633,7 @@ export {
2621
2633
  compareVersions,
2622
2634
  cryptoReady,
2623
2635
  declaredVersion,
2636
+ describeBytes,
2624
2637
  effectiveOfferScope,
2625
2638
  envelopeBytes,
2626
2639
  fingerprint,
@@ -2635,6 +2648,7 @@ export {
2635
2648
  kindsOf,
2636
2649
  matchAudience,
2637
2650
  mayOfferUpdate,
2651
+ mentionsWireField,
2638
2652
  mustsVerifiedBy,
2639
2653
  open,
2640
2654
  payloadTextLength,
@@ -2651,6 +2665,7 @@ export {
2651
2665
  sizeClassCeiling,
2652
2666
  sizeClassOf,
2653
2667
  successionStatement,
2668
+ tooLargeMessage,
2654
2669
  updateOfferFor,
2655
2670
  verifyGrant,
2656
2671
  verifyLink,
@@ -2658,6 +2673,7 @@ export {
2658
2673
  verifyRequest,
2659
2674
  verifySiteRequest,
2660
2675
  verifyWith,
2661
- walkSuccession
2676
+ walkSuccession,
2677
+ withoutComments
2662
2678
  };
2663
2679
  //# sourceMappingURL=index.js.map