@byollm/protocol 0.1.0-alpha.0

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/dist/index.js ADDED
@@ -0,0 +1,663 @@
1
+ // src/audience.ts
2
+ import { z as z2 } from "zod";
3
+
4
+ // src/backends.ts
5
+ import { z } from "zod";
6
+ var BackendClass = z.enum(["http", "process"]);
7
+ var BackendAccount = z.enum(["open", "subscription"]);
8
+ var backend = (b) => Object.freeze(b);
9
+ var BACKENDS = Object.freeze({
10
+ "openai-http": backend({
11
+ id: "openai-http",
12
+ label: "OpenAI-compatible HTTP server (Ollama, MLX, llama.cpp, vLLM)",
13
+ class: "http",
14
+ account: "open",
15
+ adversarialCorpus: "http"
16
+ }),
17
+ "claude-cli": backend({
18
+ id: "claude-cli",
19
+ label: "Claude CLI (your subscription)",
20
+ class: "process",
21
+ account: "subscription",
22
+ adversarialCorpus: "process"
23
+ })
24
+ });
25
+ var BACKEND_IDS = Object.freeze(Object.keys(BACKENDS));
26
+ var BackendIdSchema = z.enum(
27
+ BACKEND_IDS
28
+ );
29
+ function isBackendId(value) {
30
+ return Object.hasOwn(BACKENDS, value);
31
+ }
32
+ function backendDescriptor(id) {
33
+ return BACKENDS[id];
34
+ }
35
+
36
+ // src/audience.ts
37
+ var Audience = z2.enum(["self", "named", "public"]);
38
+ var OfferScope = z2.enum(["self", "named", "public"]);
39
+ var AUDIENCES = Object.freeze(Audience.options);
40
+ var OFFER_SCOPES = Object.freeze(OfferScope.options);
41
+ var MatchRefusal = z2.enum([
42
+ /** The daemon advertises no capability for this kind. */
43
+ "no-capability",
44
+ /** Job is `self` but this daemon belongs to a different user. */
45
+ "audience-self-other-owner",
46
+ /** Job is `named` but this daemon's local allowlist does not admit the owner. */
47
+ "not-locally-allowed",
48
+ /** Job is `named`/`public` but the server's own allowlist excludes this runner. */
49
+ "not-in-server-allowlist",
50
+ /** The backend offers only `self` and the job belongs to someone else. */
51
+ "offer-scope-too-narrow",
52
+ /** The matched backend is subscription-class, which is locked to `self`. */
53
+ "subscription-self-lock"
54
+ ]);
55
+ var ALLOWED = Object.freeze({ ok: true });
56
+ var refuse = (refusal) => Object.freeze({ ok: false, refusal });
57
+ function effectiveOfferScope(configured, account) {
58
+ return account === "subscription" ? "self" : configured;
59
+ }
60
+ function matchAudience(job, daemon) {
61
+ const sameOwner = job.owner === daemon.owner;
62
+ if (job.audience === "self" && !sameOwner) {
63
+ return refuse("audience-self-other-owner");
64
+ }
65
+ if (job.audience === "named" && !sameOwner && job.audienceAllow !== void 0 && !job.audienceAllow.includes(daemon.owner)) {
66
+ return refuse("not-in-server-allowlist");
67
+ }
68
+ const scope = effectiveOfferScope(daemon.offerScope, daemon.account);
69
+ if (sameOwner) {
70
+ return ALLOWED;
71
+ }
72
+ if (daemon.account === "subscription") {
73
+ return refuse("subscription-self-lock");
74
+ }
75
+ switch (scope) {
76
+ case "self":
77
+ return refuse("offer-scope-too-narrow");
78
+ case "named":
79
+ return daemon.locallyAllows(job.owner) ? ALLOWED : refuse("not-locally-allowed");
80
+ case "public":
81
+ return ALLOWED;
82
+ }
83
+ }
84
+ var REFUSAL_MESSAGES = Object.freeze({
85
+ "no-capability": "no backend on this machine is configured and healthy for that job kind",
86
+ "audience-self-other-owner": "the job is private to its owner and this machine is paired to someone else",
87
+ "not-locally-allowed": "the job's owner is not on this machine's allowlist (byollm allow <server> <user>)",
88
+ "not-in-server-allowlist": "the app restricted this job to named runners and this machine is not one of them",
89
+ "offer-scope-too-narrow": "this backend is offered to its owner only (byollm offer <backend> named|public to widen)",
90
+ "subscription-self-lock": "subscription-backed models run their owner's work only \u2014 this is a protocol rule, not a setting"
91
+ });
92
+
93
+ // src/kinds.ts
94
+ import { z as z3 } from "zod";
95
+ var PAYLOAD_LIMITS = Object.freeze({
96
+ /** Max characters in any single text field. */
97
+ maxTextChars: 1e6,
98
+ /** Max messages in an `llm.chat` conversation. */
99
+ maxMessages: 256,
100
+ /** Max characters across the whole payload. */
101
+ maxTotalChars: 4e6
102
+ });
103
+ var ChatMessage = z3.object({
104
+ role: z3.enum(["system", "user", "assistant"]),
105
+ content: z3.string().max(PAYLOAD_LIMITS.maxTextChars)
106
+ });
107
+ var GeneratePayload = z3.object({
108
+ prompt: z3.string().min(1).max(PAYLOAD_LIMITS.maxTextChars),
109
+ system: z3.string().max(PAYLOAD_LIMITS.maxTextChars).optional()
110
+ }).strict();
111
+ var ChatPayload = z3.object({
112
+ messages: z3.array(ChatMessage).min(1).max(PAYLOAD_LIMITS.maxMessages),
113
+ system: z3.string().max(PAYLOAD_LIMITS.maxTextChars).optional()
114
+ }).strict();
115
+ var JobKind = z3.enum(["llm.generate", "llm.chat"]);
116
+ var JOB_KINDS = Object.freeze(JobKind.options);
117
+ var KindedPayload = z3.discriminatedUnion("kind", [
118
+ z3.object({ kind: z3.literal("llm.generate"), payload: GeneratePayload }),
119
+ z3.object({ kind: z3.literal("llm.chat"), payload: ChatPayload })
120
+ ]);
121
+ function isJobKind(value) {
122
+ return JOB_KINDS.includes(value);
123
+ }
124
+ function payloadTextLength(kinded) {
125
+ if (kinded.kind === "llm.generate") {
126
+ return kinded.payload.prompt.length + (kinded.payload.system?.length ?? 0);
127
+ }
128
+ const messages = kinded.payload.messages.reduce(
129
+ (sum, m) => sum + m.content.length,
130
+ 0
131
+ );
132
+ return messages + (kinded.payload.system?.length ?? 0);
133
+ }
134
+
135
+ // src/job.ts
136
+ import { z as z4 } from "zod";
137
+ var JobState = z4.enum([
138
+ "queued",
139
+ "claimed",
140
+ "running",
141
+ "ok",
142
+ "error",
143
+ "canceled",
144
+ "expired"
145
+ ]);
146
+ var TERMINAL_STATES = Object.freeze([
147
+ "ok",
148
+ "error",
149
+ "canceled",
150
+ "expired"
151
+ ]);
152
+ function isTerminal(state) {
153
+ return TERMINAL_STATES.includes(state);
154
+ }
155
+ var TRANSITIONS = Object.freeze({
156
+ queued: ["claimed", "expired", "canceled"],
157
+ // A claimed job returns to `queued` when its lease expires un-renewed
158
+ // ({@link MUSTS.LEASE_RECLAIMABLE}).
159
+ claimed: ["running", "queued", "canceled", "error"],
160
+ running: ["ok", "error", "canceled", "queued"],
161
+ ok: [],
162
+ error: [],
163
+ canceled: [],
164
+ expired: []
165
+ });
166
+ function canTransition(from, to) {
167
+ return TRANSITIONS[from].includes(to);
168
+ }
169
+ var Lease = z4.object({
170
+ /** The runner holding the lease. */
171
+ runnerId: z4.string().min(1),
172
+ /** Epoch milliseconds after which the claim is void. */
173
+ expiresAt: z4.number().int().positive()
174
+ });
175
+ var JobPayload = z4.union([GeneratePayload, ChatPayload]);
176
+ var ClaimedJob = z4.object({
177
+ id: z4.string().min(1),
178
+ kind: JobKind,
179
+ payload: JobPayload,
180
+ audience: Audience,
181
+ /** The app's id for the user who enqueued it. */
182
+ owner: z4.string().min(1),
183
+ /** Runner owners the app restricted a `named` job to, if any. */
184
+ audienceAllow: z4.array(z4.string().min(1)).optional(),
185
+ lease: Lease
186
+ }).strict();
187
+ var ResultProvenance = z4.object({
188
+ /** The audience the job ran under. */
189
+ audience: Audience,
190
+ /** The runner that produced it. */
191
+ runnerId: z4.string().min(1),
192
+ /** The runner owner's id in this app's namespace. */
193
+ runnerOwner: z4.string().min(1),
194
+ /** Which backend class produced it — an HTTP call or a sandboxed spawn. */
195
+ backendClass: BackendClass,
196
+ /** The model the runner reports having used. */
197
+ model: z4.string().min(1),
198
+ /**
199
+ * False only for `self` jobs. When true the app MUST treat `text` as
200
+ * untrusted third-party content.
201
+ */
202
+ untrusted: z4.boolean()
203
+ }).strict();
204
+ function provenanceFor(input) {
205
+ return {
206
+ audience: input.audience,
207
+ runnerId: input.runnerId,
208
+ runnerOwner: input.runnerOwner,
209
+ backendClass: input.backendClass,
210
+ model: input.model,
211
+ untrusted: input.audience !== "self"
212
+ };
213
+ }
214
+ var JobResultOk = z4.object({
215
+ outcome: z4.literal("ok"),
216
+ text: z4.string(),
217
+ /** Optional reference to a stored artifact; never a local path. */
218
+ artifactUrl: z4.url().optional()
219
+ }).strict();
220
+ var JobResultError = z4.object({
221
+ outcome: z4.literal("error"),
222
+ code: z4.string().min(1),
223
+ message: z4.string().min(1),
224
+ /** Whether the app may reasonably re-enqueue. */
225
+ retryable: z4.boolean()
226
+ }).strict();
227
+ var JobResultCanceled = z4.object({
228
+ outcome: z4.literal("canceled")
229
+ }).strict();
230
+ var JobOutcome = z4.discriminatedUnion("outcome", [
231
+ JobResultOk,
232
+ JobResultError,
233
+ JobResultCanceled
234
+ ]);
235
+ var DeliveredResult = z4.object({
236
+ jobId: z4.string().min(1),
237
+ state: JobState,
238
+ outcome: JobOutcome.optional(),
239
+ provenance: ResultProvenance.optional()
240
+ }).strict();
241
+
242
+ // src/musts.ts
243
+ var must = (m) => Object.freeze(m);
244
+ var MUSTS = Object.freeze({
245
+ // ---- Pairing and identity -------------------------------------------
246
+ PAIR_ONE_USER: must({
247
+ id: "PAIR_ONE_USER",
248
+ statement: "A runner token MUST be bound to exactly one user; a daemon MUST refuse work not attributable to its paired user.",
249
+ enforcedBy: "both",
250
+ source: "byollm_001 \xA7MUSTs"
251
+ }),
252
+ PAIR_INTERACTIVE: must({
253
+ id: "PAIR_INTERACTIVE",
254
+ statement: "Pairing MUST be interactive (device-code approval in the app's own session); a long-lived pasted secret MUST NOT be accepted as pairing.",
255
+ enforcedBy: "server",
256
+ source: "byollm_001 \xA7Endpoints.1"
257
+ }),
258
+ PAIR_CODE_EXPIRES: must({
259
+ id: "PAIR_CODE_EXPIRES",
260
+ statement: "An unapproved device code MUST expire and MUST NOT be redeemable after expiry.",
261
+ enforcedBy: "server",
262
+ source: "byollm_001 \xA7Endpoints.1"
263
+ }),
264
+ // ---- Typed job kinds --------------------------------------------------
265
+ KIND_TYPED_ONLY: must({
266
+ id: "KIND_TYPED_ONLY",
267
+ statement: "Job kinds MUST resolve against handlers baked into the daemon. A daemon MUST refuse an unknown kind rather than guess.",
268
+ enforcedBy: "daemon",
269
+ source: "byollm_001 \xA7Jobs are typed data"
270
+ }),
271
+ KIND_NO_CODE: must({
272
+ id: "KIND_NO_CODE",
273
+ statement: "A server MUST NOT be able to convey code, a shell string, or a path to execute; payloads are data handed to a model only.",
274
+ enforcedBy: "daemon",
275
+ source: "byollm_001 \xA7Jobs are typed data; byollm_004 \xA71"
276
+ }),
277
+ // ---- Capability and claiming -----------------------------------------
278
+ CLAIM_REQUIRES_CAPABILITY: must({
279
+ id: "CLAIM_REQUIRES_CAPABILITY",
280
+ statement: "A daemon MUST NOT be given a job whose kind is absent from its advertised capability matrix.",
281
+ enforcedBy: "both",
282
+ source: "byollm_001 \xA7MUSTs"
283
+ }),
284
+ CAPABILITY_IS_DETECTED: must({
285
+ id: "CAPABILITY_IS_DETECTED",
286
+ statement: "An advertised capability matrix MUST be the intersection of owner config and detected, healthy reality \u2014 never config alone.",
287
+ enforcedBy: "daemon",
288
+ source: "byollm_002 \xA7Routing"
289
+ }),
290
+ CLAIM_ATOMIC: must({
291
+ id: "CLAIM_ATOMIC",
292
+ statement: "Claiming MUST be atomic: a job MUST NOT be handed to two runners concurrently.",
293
+ enforcedBy: "server",
294
+ source: "byollm_001 \xA7Endpoints.2"
295
+ }),
296
+ // ---- Leases -----------------------------------------------------------
297
+ LEASE_HONORED: must({
298
+ id: "LEASE_HONORED",
299
+ statement: "A daemon MUST stop work on a job whose lease it has failed to renew, and MUST NOT report a result for an expired lease it no longer holds.",
300
+ enforcedBy: "daemon",
301
+ source: "byollm_001 \xA7MUSTs"
302
+ }),
303
+ LEASE_RECLAIMABLE: must({
304
+ id: "LEASE_RECLAIMABLE",
305
+ statement: "A lease that expires un-renewed MUST make its job claimable again with no loss of the job.",
306
+ enforcedBy: "server",
307
+ source: "byollm_001 \xA7Endpoints.2"
308
+ }),
309
+ // ---- Audience and offer scope ----------------------------------------
310
+ AUDIENCE_BOTH_SIDES: must({
311
+ id: "AUDIENCE_BOTH_SIDES",
312
+ statement: "A job MUST run on a daemon only if the daemon's offer scope admits the job's owner AND the job's audience admits the daemon's owner.",
313
+ enforcedBy: "both",
314
+ source: "byollm_001 \xA7The audience model"
315
+ }),
316
+ SUBSCRIPTION_SELF_LOCK: must({
317
+ id: "SUBSCRIPTION_SELF_LOCK",
318
+ statement: "A subscription-class backend's offer scope MUST be 'self' and MUST NOT be widened by configuration.",
319
+ enforcedBy: "daemon",
320
+ source: "byollm_001 \xA7The audience model"
321
+ }),
322
+ NAMED_LOCAL_ALLOWLIST: must({
323
+ id: "NAMED_LOCAL_ALLOWLIST",
324
+ statement: "A 'named' job MUST be admitted only by the daemon's own local (server origin, user id) allowlist \u2014 never on the server's assertion alone.",
325
+ enforcedBy: "daemon",
326
+ source: "byollm_001 Rev 1 \xA7B"
327
+ }),
328
+ REFUSAL_NOT_REOFFERED: must({
329
+ id: "REFUSAL_NOT_REOFFERED",
330
+ statement: "A server MUST NOT re-offer a job to a runner that released it with reason 'refused'.",
331
+ enforcedBy: "server",
332
+ source: "byollm_001 Rev 1 \xA7B (loop resolved in build review)"
333
+ }),
334
+ // ---- Revocation and cancel -------------------------------------------
335
+ REVOCATION_HONORED: must({
336
+ id: "REVOCATION_HONORED",
337
+ statement: "A revoked daemon MUST stop claiming and MUST abandon in-flight work by the next heartbeat at the latest.",
338
+ enforcedBy: "daemon",
339
+ source: "byollm_001 \xA7MUSTs"
340
+ }),
341
+ CANCEL_HONORED: must({
342
+ id: "CANCEL_HONORED",
343
+ statement: "A job id in a heartbeat response's cancel list MUST abort that job's in-flight backend call and be reported as 'canceled'.",
344
+ enforcedBy: "daemon",
345
+ source: "byollm_001 Rev 1 \xA7C"
346
+ }),
347
+ // ---- Lifecycle, dependencies, delivery -------------------------------
348
+ DEPENDS_ON_GATING: must({
349
+ id: "DEPENDS_ON_GATING",
350
+ statement: "A job MUST NOT be claimable until every job in its dependsOn set has reached the 'ok' state.",
351
+ enforcedBy: "server",
352
+ source: "byollm_001 Rev 1 \xA7E"
353
+ }),
354
+ TTL_EXPIRY: must({
355
+ id: "TTL_EXPIRY",
356
+ statement: "An unclaimed job MUST become 'expired' once its TTL elapses, and the TTL clock MUST start when the job becomes claimable, not at enqueue.",
357
+ enforcedBy: "server",
358
+ source: "byollm_001 Rev 1 \xA7D (TTL clock resolved in build review)"
359
+ }),
360
+ NO_RUNNER_SIGNAL: must({
361
+ id: "NO_RUNNER_SIGNAL",
362
+ statement: "A server MUST surface noRunnerAvailable when no runner with matching capability has heartbeated within the liveness window, and MUST NOT raise it for a job still blocked on dependencies.",
363
+ enforcedBy: "server",
364
+ source: "byollm_001 Rev 1 \xA7D"
365
+ }),
366
+ RESULT_IDEMPOTENT: must({
367
+ id: "RESULT_IDEMPOTENT",
368
+ statement: "Result submission MUST be idempotent by job id; the first terminal outcome wins and later submissions MUST NOT change it.",
369
+ enforcedBy: "server",
370
+ source: "byollm_001 \xA7Endpoints.4"
371
+ }),
372
+ RESULT_PROVENANCE: must({
373
+ id: "RESULT_PROVENANCE",
374
+ statement: "A result from a non-'self' job MUST carry its provenance (audience and runner) to the delivery seam so an app never treats volunteer output as first-party.",
375
+ enforcedBy: "server",
376
+ source: "byollm_003 Rev 1 \xA7Return-trip"
377
+ }),
378
+ // ---- The trust surface -------------------------------------------------
379
+ INGRESS_LOGGED_BEFORE_EXECUTION: must({
380
+ id: "INGRESS_LOGGED_BEFORE_EXECUTION",
381
+ statement: "Every executed prompt MUST be appended to the local ingress log before execution begins.",
382
+ enforcedBy: "daemon",
383
+ source: "byollm_001 \xA7MUSTs"
384
+ }),
385
+ // ---- Execution isolation (byollm_004) ---------------------------------
386
+ NO_SHELL_INTERPOLATION: must({
387
+ id: "NO_SHELL_INTERPOLATION",
388
+ statement: "Process-class backends MUST be invoked with a fixed argv array and the payload delivered on stdin; payload text MUST NOT reach a command line.",
389
+ enforcedBy: "daemon",
390
+ source: "byollm_004 \xA72"
391
+ }),
392
+ NO_PAYLOAD_ROUTING: must({
393
+ id: "NO_PAYLOAD_ROUTING",
394
+ statement: "Model, backend, base URL, and flags MUST come from owner config only; a payload MUST NOT influence any of them.",
395
+ enforcedBy: "daemon",
396
+ source: "byollm_004 \xA72"
397
+ }),
398
+ STRIPPED_CHILD_ENV: must({
399
+ id: "STRIPPED_CHILD_ENV",
400
+ statement: "Process-class children MUST spawn with an allowlisted environment, a scratch cwd, no inherited descriptors beyond std streams, and hard timeout and output-size caps.",
401
+ enforcedBy: "daemon",
402
+ source: "byollm_004 \xA72"
403
+ }),
404
+ HTTP_BASE_URL_SAFE: must({
405
+ id: "HTTP_BASE_URL_SAFE",
406
+ statement: "HTTP-class backends MUST send requests only to the owner-configured base URL and MUST refuse base URLs resolving to cloud-metadata or link-local addresses.",
407
+ enforcedBy: "daemon",
408
+ source: "byollm_004 Rev 1 \xA7Backend taxonomy"
409
+ }),
410
+ OUTPUT_INERT: must({
411
+ id: "OUTPUT_INERT",
412
+ statement: "Returned text MUST be treated as inert bytes: never evaluated, never written to a payload-named path, never interpolated into a shell or into terminal control sequences when logged.",
413
+ enforcedBy: "daemon",
414
+ source: "byollm_004 \xA72"
415
+ }),
416
+ COMMUNITY_BUDGETS: must({
417
+ id: "COMMUNITY_BUDGETS",
418
+ statement: "Jobs whose owner is not the daemon's owner MUST be subject to the owner's rate limits, daily cap, and resource budget.",
419
+ enforcedBy: "daemon",
420
+ source: "byollm_004 \xA74"
421
+ })
422
+ });
423
+ var MUST_IDS = Object.freeze(Object.keys(MUSTS));
424
+
425
+ // src/wire.ts
426
+ import { z as z5 } from "zod";
427
+ var PROTOCOL_VERSION = "0";
428
+ var PROTOCOL_PREFIX = "/byollm";
429
+ var ENDPOINTS = Object.freeze([
430
+ "pair",
431
+ "claim",
432
+ "heartbeat",
433
+ "result",
434
+ "release"
435
+ ]);
436
+ var Capability = z5.object({
437
+ kind: JobKind,
438
+ backendId: BackendIdSchema,
439
+ backendClass: BackendClass,
440
+ model: z5.string().min(1),
441
+ offerScope: OfferScope
442
+ }).strict();
443
+ var CapabilityMatrix = z5.array(Capability);
444
+ var PairStartRequest = z5.object({
445
+ protocolVersion: z5.literal(PROTOCOL_VERSION),
446
+ action: z5.literal("start"),
447
+ daemon: z5.object({
448
+ version: z5.string().min(1),
449
+ /** Shown in the app's runner list so a user can tell their machines apart. */
450
+ label: z5.string().min(1).max(120),
451
+ platform: z5.enum(["darwin", "linux", "win32"])
452
+ }),
453
+ capabilities: CapabilityMatrix
454
+ }).strict();
455
+ var PairStartResponse = z5.object({
456
+ /** Secret the daemon polls with. Never shown to the user. */
457
+ deviceCode: z5.string().min(20),
458
+ /** Short code the user reads and confirms in the browser. */
459
+ userCode: z5.string().min(4).max(16),
460
+ /** Where the user approves. Must be on the server's own origin. */
461
+ verificationUrl: z5.url(),
462
+ /** Epoch ms after which the code is dead ({@link MUSTS.PAIR_CODE_EXPIRES}). */
463
+ expiresAt: z5.number().int().positive(),
464
+ /** How often the daemon may poll. */
465
+ pollIntervalMs: z5.number().int().min(500).max(6e4)
466
+ }).strict();
467
+ var PairPollRequest = z5.object({
468
+ protocolVersion: z5.literal(PROTOCOL_VERSION),
469
+ action: z5.literal("poll"),
470
+ deviceCode: z5.string().min(20)
471
+ }).strict();
472
+ var PairPollResponse = z5.discriminatedUnion("status", [
473
+ z5.object({ status: z5.literal("pending") }).strict(),
474
+ z5.object({ status: z5.literal("denied") }).strict(),
475
+ z5.object({ status: z5.literal("expired") }).strict(),
476
+ z5.object({
477
+ status: z5.literal("approved"),
478
+ /** Bearer token for every later call. Scoped to exactly one user. */
479
+ runnerToken: z5.string().min(20),
480
+ runnerId: z5.string().min(1),
481
+ /** The app's id for the approving user — this daemon's owner forever. */
482
+ owner: z5.string().min(1),
483
+ /** Display name for the trust UI, if the app offers one. */
484
+ ownerLabel: z5.string().optional()
485
+ }).strict()
486
+ ]);
487
+ var PairRequest = z5.discriminatedUnion("action", [
488
+ PairStartRequest,
489
+ PairPollRequest
490
+ ]);
491
+ var ClaimRequest = z5.object({
492
+ protocolVersion: z5.literal(PROTOCOL_VERSION),
493
+ runnerId: z5.string().min(1),
494
+ /** Re-sent on every claim so a server never matches against a stale matrix. */
495
+ capabilities: CapabilityMatrix,
496
+ /** Upper bound on jobs to return; the server may return fewer. */
497
+ max: z5.number().int().min(1).max(64)
498
+ }).strict();
499
+ var ClaimResponse = z5.object({
500
+ jobs: z5.array(ClaimedJob),
501
+ /** Lease duration granted, so the daemon knows its renewal deadline. */
502
+ leaseMs: z5.number().int().positive()
503
+ }).strict();
504
+ var HeartbeatRequest = z5.object({
505
+ protocolVersion: z5.literal(PROTOCOL_VERSION),
506
+ runnerId: z5.string().min(1),
507
+ daemonVersion: z5.string().min(1),
508
+ capabilities: CapabilityMatrix,
509
+ /** Jobs this daemon believes it holds; the server renews their leases. */
510
+ activeJobIds: z5.array(z5.string().min(1)),
511
+ /** True while the owner has the daemon paused; the server stops offering work. */
512
+ paused: z5.boolean()
513
+ }).strict();
514
+ var HeartbeatResponse = z5.object({
515
+ /** Once true, the daemon stops claiming and abandons in-flight work. */
516
+ revoked: z5.boolean(),
517
+ /**
518
+ * Per-job cancel (byollm_001 Rev 1 §C). The daemon aborts these jobs'
519
+ * in-flight backend calls and reports them `canceled`.
520
+ */
521
+ cancel: z5.array(z5.string().min(1)),
522
+ /** Jobs whose leases were renewed, with their new expiry. */
523
+ leases: z5.array(
524
+ z5.object({
525
+ jobId: z5.string().min(1),
526
+ expiresAt: z5.number().int().positive()
527
+ }).strict()
528
+ ),
529
+ /**
530
+ * Jobs the daemon thinks it holds but the server has reassigned or
531
+ * expired. The daemon must stop work on these and not report results.
532
+ */
533
+ lost: z5.array(z5.string().min(1)),
534
+ /** Server clock, so a daemon with a skewed clock still honors leases. */
535
+ serverTime: z5.number().int().positive()
536
+ }).strict();
537
+ var ResultRequest = z5.object({
538
+ protocolVersion: z5.literal(PROTOCOL_VERSION),
539
+ runnerId: z5.string().min(1),
540
+ jobId: z5.string().min(1),
541
+ outcome: JobOutcome,
542
+ /** Which model actually served it, for the result's provenance. */
543
+ model: z5.string().min(1),
544
+ backendClass: BackendClass,
545
+ /** Wall-clock milliseconds the backend call took. */
546
+ durationMs: z5.number().int().nonnegative()
547
+ }).strict();
548
+ var ResultResponse = z5.object({
549
+ /**
550
+ * False when the submission lost an idempotency race or the lease was
551
+ * already gone — the daemon should discard, not retry
552
+ * ({@link MUSTS.RESULT_IDEMPOTENT}).
553
+ */
554
+ accepted: z5.boolean(),
555
+ /** The job's state after this submission. */
556
+ state: z5.string().min(1)
557
+ }).strict();
558
+ var ReleaseRequest = z5.object({
559
+ protocolVersion: z5.literal(PROTOCOL_VERSION),
560
+ runnerId: z5.string().min(1),
561
+ jobIds: z5.array(z5.string().min(1)),
562
+ /**
563
+ * Why, so the app's runner list can say something true.
564
+ *
565
+ * `refused` is load-bearing, not cosmetic: the server cannot evaluate a
566
+ * daemon's *local* `named` allowlist (§4.2), so it may legitimately offer
567
+ * a job this daemon then declines. The server MUST record the refusal and
568
+ * stop offering that job to that runner, or the pair would spin between
569
+ * claim and release forever.
570
+ */
571
+ reason: z5.enum(["shutdown", "pause", "revoked", "backend-down", "refused"])
572
+ }).strict();
573
+ var ReleaseResponse = z5.object({
574
+ released: z5.array(z5.string().min(1))
575
+ }).strict();
576
+ var WireErrorCode = z5.enum([
577
+ "bad-request",
578
+ "unsupported-protocol-version",
579
+ "unauthorized",
580
+ "revoked",
581
+ "not-found",
582
+ "rate-limited",
583
+ "server-error"
584
+ ]);
585
+ var WireError = z5.object({
586
+ error: WireErrorCode,
587
+ message: z5.string().min(1),
588
+ /** Seconds; mirrors Retry-After for `rate-limited` and `server-error`. */
589
+ retryAfter: z5.number().int().nonnegative().optional()
590
+ }).strict();
591
+ var ERROR_STATUS = Object.freeze({
592
+ "bad-request": 400,
593
+ "unsupported-protocol-version": 400,
594
+ unauthorized: 401,
595
+ revoked: 403,
596
+ "not-found": 404,
597
+ "rate-limited": 429,
598
+ "server-error": 500
599
+ });
600
+ export {
601
+ AUDIENCES,
602
+ Audience,
603
+ BACKENDS,
604
+ BACKEND_IDS,
605
+ BackendAccount,
606
+ BackendClass,
607
+ BackendIdSchema,
608
+ Capability,
609
+ CapabilityMatrix,
610
+ ChatMessage,
611
+ ChatPayload,
612
+ ClaimRequest,
613
+ ClaimResponse,
614
+ ClaimedJob,
615
+ DeliveredResult,
616
+ ENDPOINTS,
617
+ ERROR_STATUS,
618
+ GeneratePayload,
619
+ HeartbeatRequest,
620
+ HeartbeatResponse,
621
+ JOB_KINDS,
622
+ JobKind,
623
+ JobOutcome,
624
+ JobPayload,
625
+ JobResultCanceled,
626
+ JobResultError,
627
+ JobResultOk,
628
+ JobState,
629
+ KindedPayload,
630
+ Lease,
631
+ MUSTS,
632
+ MUST_IDS,
633
+ MatchRefusal,
634
+ OFFER_SCOPES,
635
+ OfferScope,
636
+ PAYLOAD_LIMITS,
637
+ PROTOCOL_PREFIX,
638
+ PROTOCOL_VERSION,
639
+ PairPollRequest,
640
+ PairPollResponse,
641
+ PairRequest,
642
+ PairStartRequest,
643
+ PairStartResponse,
644
+ REFUSAL_MESSAGES,
645
+ ReleaseRequest,
646
+ ReleaseResponse,
647
+ ResultProvenance,
648
+ ResultRequest,
649
+ ResultResponse,
650
+ TERMINAL_STATES,
651
+ WireError,
652
+ WireErrorCode,
653
+ backendDescriptor,
654
+ canTransition,
655
+ effectiveOfferScope,
656
+ isBackendId,
657
+ isJobKind,
658
+ isTerminal,
659
+ matchAudience,
660
+ payloadTextLength,
661
+ provenanceFor
662
+ };
663
+ //# sourceMappingURL=index.js.map