@byollm/server 0.1.0-alpha.8 → 0.1.0-alpha.80

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.
@@ -41,15 +41,17 @@ var PollingDelivery = class {
41
41
  options.signal?.throwIfAborted();
42
42
  const current = await this.#deps.read(jobId);
43
43
  if (current && isTerminalState(current.state)) return current;
44
- const availability = await this.#deps.availability(jobId);
45
- if (availability.available || availability.blocked) {
44
+ const availability = await this.#deps.availability?.(jobId);
45
+ if (availability === void 0 || availability.available || availability.blocked) {
46
46
  noRunnerSince = null;
47
47
  } else {
48
48
  noRunnerSince ??= now();
49
49
  if (now() - noRunnerSince >= graceMs) {
50
50
  const reason = availability.reason ?? "no-runner-online";
51
51
  const substitute = await options.onNoRunner?.(reason);
52
- if (substitute) return substitute;
52
+ if (substitute !== void 0) {
53
+ return labelFallback(jobId, substitute);
54
+ }
53
55
  throw new NoRunnerAvailableError(jobId, reason);
54
56
  }
55
57
  }
@@ -63,10 +65,22 @@ var PollingDelivery = class {
63
65
  function isTerminalState(state) {
64
66
  return state === "ok" || state === "error" || state === "canceled" || state === "expired";
65
67
  }
68
+ function labelFallback(jobId, substitute) {
69
+ if (typeof substitute === "string") {
70
+ return {
71
+ jobId,
72
+ state: "ok",
73
+ outcome: { outcome: "ok", text: substitute },
74
+ fallback: true
75
+ };
76
+ }
77
+ return { ...substitute, fallback: true };
78
+ }
66
79
 
67
80
  export {
68
81
  NoRunnerAvailableError,
69
82
  ResultTimeoutError,
70
- PollingDelivery
83
+ PollingDelivery,
84
+ labelFallback
71
85
  };
72
- //# sourceMappingURL=chunk-7RKXFPBZ.js.map
86
+ //# sourceMappingURL=chunk-I3ER27QG.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/delivery.ts"],"sourcesContent":["import type { DeliveredResult } from \"@byollm/protocol\";\n\n/** Why a wait ended without a result. */\nexport class NoRunnerAvailableError extends Error {\n override readonly name = \"NoRunnerAvailableError\";\n constructor(\n readonly jobId: string,\n readonly reason: string,\n ) {\n super(\n `no runner is available to execute job ${jobId} (${reason}). ` +\n `Fall back to a hosted model, or prompt the user to start their runner.`,\n );\n }\n}\n\n/** The wait exceeded its timeout while a runner was still plausibly working. */\nexport class ResultTimeoutError extends Error {\n override readonly name = \"ResultTimeoutError\";\n constructor(\n readonly jobId: string,\n readonly timeoutMs: number,\n ) {\n super(`job ${jobId} did not finish within ${String(timeoutMs)}ms`);\n }\n}\n\nexport interface WaitOptions {\n /** Give up after this long. Default 5 minutes. */\n readonly timeoutMs?: number;\n /**\n * Called instead of throwing when no runner can take the job. Return a\n * substitute and the wait resolves with it; return nothing and\n * {@link NoRunnerAvailableError} is thrown.\n *\n * **A string is enough.** It is the app's own fallback answer — a hosted\n * model's text, a cached reply — not wire data, and requiring a whole\n * `DeliveredResult` for it was ceremony that invited invented shapes. The\n * README's own example got it wrong, which is how this was found.\n *\n * **Whatever comes back is labelled `fallback: true` by the wait, not by\n * the caller** — {@link MUSTS.FALLBACK_LABELED}. Work that did not come\n * from the user's own compute must not be reportable as though it did, and\n * that stays true whether an app returns a bare string or a full record it\n * assembled itself. The stamp is applied after this function returns, so\n * there is no shape an app can hand back that hides what it is.\n */\n readonly onNoRunner?: (\n reason: string,\n ) =>\n | string\n | DeliveredResult\n | undefined\n | Promise<string | DeliveredResult | undefined>;\n /** Abort the wait. */\n readonly signal?: AbortSignal;\n}\n\n/**\n * How an app learns a job finished.\n *\n * byollm_003 Rev 1 is explicit that this is a *channel* — webhook, Realtime\n * subscription, or poll — and never an implied in-request `await`. The\n * polling implementation below is the portable default; the Supabase adapter\n * substitutes Realtime for the same interface.\n */\nexport interface ResultDelivery {\n waitFor(jobId: string, options?: WaitOptions): Promise<DeliveredResult>;\n}\n\nexport interface PollingDeliveryDeps {\n /** Current state of the job, or null if unknown. */\n readonly read: (jobId: string) => Promise<DeliveredResult | null>;\n /**\n * Whether a runner could still take this job — **when there is anybody to\n * ask.**\n *\n * Optional since alpha.66, and its absence is the answer rather than a\n * missing dependency. On the cloud lane nothing writes runners into this\n * site's store: devices pair with the relay, so the question has no local\n * answer and `runnerAvailability` refuses to invent one.\n *\n * The refusal was correct and it landed in a loop that asked every 500ms.\n * `job.result()` threw on its first poll for every cloud-lane site — a\n * refusal aimed at outsiders that our own delivery tripped over.\n *\n * Not fixed by catching the throw here. That is a swallowed error in\n * costume, and a catch wide enough to hold it would also eat a store that\n * had genuinely gone away. The instrument is simply not handed over on a\n * lane where it cannot see, and this loop does not ask a question nobody\n * can answer.\n */\n readonly availability?: (\n jobId: string,\n ) => Promise<{ available: boolean; reason?: string; blocked: boolean }>;\n readonly sleep?: (ms: number) => Promise<void>;\n /**\n * Injectable clock. It must advance in step with {@link sleep}: a test that\n * stubs one and not the other gets a loop whose grace window never elapses.\n */\n readonly now?: () => number;\n /**\n * How long a sustained no-runner signal must persist before it is believed.\n * Defaults to {@link NO_RUNNER_GRACE_MS}.\n */\n readonly graceMs?: number;\n}\n\nconst DEFAULT_TIMEOUT_MS = 5 * 60_000;\nconst POLL_INTERVAL_MS = 500;\n/**\n * How long to let a job sit with no available runner before giving up.\n *\n * Not zero: a daemon restarting, or one whose heartbeat is momentarily late,\n * would otherwise fail every job in flight. The signal has to be sustained\n * before it is believed.\n */\nconst NO_RUNNER_GRACE_MS = 10_000;\n\nconst defaultSleep = (ms: number): Promise<void> =>\n new Promise((resolve) => setTimeout(resolve, ms));\n\n/**\n * The portable delivery channel: poll the store until the job is terminal.\n *\n * Correct everywhere and adequate for most apps. An adapter with a push\n * channel should replace it — see the Supabase adapter's Realtime delivery.\n */\nexport class PollingDelivery implements ResultDelivery {\n readonly #deps: PollingDeliveryDeps;\n\n constructor(deps: PollingDeliveryDeps) {\n this.#deps = deps;\n }\n\n async waitFor(\n jobId: string,\n options: WaitOptions = {},\n ): Promise<DeliveredResult> {\n const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n const sleep = this.#deps.sleep ?? defaultSleep;\n const now = this.#deps.now ?? Date.now;\n const graceMs = this.#deps.graceMs ?? NO_RUNNER_GRACE_MS;\n const started = now();\n let noRunnerSince: number | null = null;\n\n for (;;) {\n options.signal?.throwIfAborted();\n\n const current = await this.#deps.read(jobId);\n if (current && isTerminalState(current.state)) return current;\n\n /**\n * The no-runner signal, when this deployment has one.\n *\n * With no instrument there is no sustained-absence signal and no\n * `NoRunnerAvailableError` — the wait ends when the job reaches a\n * terminal state or the timeout does. That is the honest behaviour on\n * the cloud lane, where an unsatisfiable slot is refused at enqueue and\n * a job with nowhere to run expires, both of which arrive through\n * `read` as states rather than as guesses made here.\n */\n const availability = await this.#deps.availability?.(jobId);\n if (\n availability === undefined ||\n availability.available ||\n availability.blocked\n ) {\n // `blocked` means the job is waiting on a dependency, which is not the\n // same event as \"nobody can run this\" ({@link MUSTS.NO_RUNNER_SIGNAL}).\n noRunnerSince = null;\n } else {\n noRunnerSince ??= now();\n if (now() - noRunnerSince >= graceMs) {\n const reason = availability.reason ?? \"no-runner-online\";\n const substitute = await options.onNoRunner?.(reason);\n if (substitute !== undefined) {\n return labelFallback(jobId, substitute);\n }\n throw new NoRunnerAvailableError(jobId, reason);\n }\n }\n\n if (now() - started >= timeoutMs) {\n throw new ResultTimeoutError(jobId, timeoutMs);\n }\n await sleep(POLL_INTERVAL_MS);\n }\n }\n}\n\nfunction isTerminalState(state: string): boolean {\n return (\n state === \"ok\" ||\n state === \"error\" ||\n state === \"canceled\" ||\n state === \"expired\"\n );\n}\n\n/**\n * Turn an app's fallback into a delivered result, marked as one.\n *\n * Exported because there are two delivery channels — polling here, Supabase\n * Realtime next door — and a label applied by one of them is a label an app\n * gets or does not get depending on which store it chose. That is exactly the\n * kind of divergence a \"delivery adapter must not change what a result means\"\n * rule exists to prevent.\n *\n * Two jobs, and the second is the one that matters. A string becomes the\n * obvious record — that is the sugar. Everything, string or record, gets\n * `fallback: true` — that is {@link MUSTS.FALLBACK_LABELED}, and it is\n * applied here rather than trusted from the caller because an app that\n * assembled its own record could otherwise return something indistinguishable\n * from a runner's answer. Spreading the caller's object first and the flag\n * second is deliberate: a supplied `fallback` cannot overwrite it.\n */\nexport function labelFallback(\n jobId: string,\n substitute: string | DeliveredResult,\n): DeliveredResult {\n if (typeof substitute === \"string\") {\n return {\n jobId,\n state: \"ok\",\n outcome: { outcome: \"ok\", text: substitute },\n fallback: true,\n };\n }\n return { ...substitute, fallback: true };\n}\n"],"mappings":";AAGO,IAAM,yBAAN,cAAqC,MAAM;AAAA,EAEhD,YACW,OACA,QACT;AACA;AAAA,MACE,yCAAyC,KAAK,KAAK,MAAM;AAAA,IAE3D;AANS;AACA;AAAA,EAMX;AAAA,EAPW;AAAA,EACA;AAAA,EAHO,OAAO;AAU3B;AAGO,IAAM,qBAAN,cAAiC,MAAM;AAAA,EAE5C,YACW,OACA,WACT;AACA,UAAM,OAAO,KAAK,0BAA0B,OAAO,SAAS,CAAC,IAAI;AAHxD;AACA;AAAA,EAGX;AAAA,EAJW;AAAA,EACA;AAAA,EAHO,OAAO;AAO3B;AAmFA,IAAM,qBAAqB,IAAI;AAC/B,IAAM,mBAAmB;AAQzB,IAAM,qBAAqB;AAE3B,IAAM,eAAe,CAAC,OACpB,IAAI,QAAQ,CAAC,YAAY,WAAW,SAAS,EAAE,CAAC;AAQ3C,IAAM,kBAAN,MAAgD;AAAA,EAC5C;AAAA,EAET,YAAY,MAA2B;AACrC,SAAK,QAAQ;AAAA,EACf;AAAA,EAEA,MAAM,QACJ,OACA,UAAuB,CAAC,GACE;AAC1B,UAAM,YAAY,QAAQ,aAAa;AACvC,UAAM,QAAQ,KAAK,MAAM,SAAS;AAClC,UAAM,MAAM,KAAK,MAAM,OAAO,KAAK;AACnC,UAAM,UAAU,KAAK,MAAM,WAAW;AACtC,UAAM,UAAU,IAAI;AACpB,QAAI,gBAA+B;AAEnC,eAAS;AACP,cAAQ,QAAQ,eAAe;AAE/B,YAAM,UAAU,MAAM,KAAK,MAAM,KAAK,KAAK;AAC3C,UAAI,WAAW,gBAAgB,QAAQ,KAAK,EAAG,QAAO;AAYtD,YAAM,eAAe,MAAM,KAAK,MAAM,eAAe,KAAK;AAC1D,UACE,iBAAiB,UACjB,aAAa,aACb,aAAa,SACb;AAGA,wBAAgB;AAAA,MAClB,OAAO;AACL,0BAAkB,IAAI;AACtB,YAAI,IAAI,IAAI,iBAAiB,SAAS;AACpC,gBAAM,SAAS,aAAa,UAAU;AACtC,gBAAM,aAAa,MAAM,QAAQ,aAAa,MAAM;AACpD,cAAI,eAAe,QAAW;AAC5B,mBAAO,cAAc,OAAO,UAAU;AAAA,UACxC;AACA,gBAAM,IAAI,uBAAuB,OAAO,MAAM;AAAA,QAChD;AAAA,MACF;AAEA,UAAI,IAAI,IAAI,WAAW,WAAW;AAChC,cAAM,IAAI,mBAAmB,OAAO,SAAS;AAAA,MAC/C;AACA,YAAM,MAAM,gBAAgB;AAAA,IAC9B;AAAA,EACF;AACF;AAEA,SAAS,gBAAgB,OAAwB;AAC/C,SACE,UAAU,QACV,UAAU,WACV,UAAU,cACV,UAAU;AAEd;AAmBO,SAAS,cACd,OACA,YACiB;AACjB,MAAI,OAAO,eAAe,UAAU;AAClC,WAAO;AAAA,MACL;AAAA,MACA,OAAO;AAAA,MACP,SAAS,EAAE,SAAS,MAAM,MAAM,WAAW;AAAA,MAC3C,UAAU;AAAA,IACZ;AAAA,EACF;AACA,SAAO,EAAE,GAAG,YAAY,UAAU,KAAK;AACzC;","names":[]}
@@ -19,10 +19,22 @@ interface WaitOptions {
19
19
  readonly timeoutMs?: number;
20
20
  /**
21
21
  * Called instead of throwing when no runner can take the job. Return a
22
- * substitute result (a hosted-model answer, say) and the wait resolves with
23
- * it; return nothing and {@link NoRunnerAvailableError} is thrown.
22
+ * substitute and the wait resolves with it; return nothing and
23
+ * {@link NoRunnerAvailableError} is thrown.
24
+ *
25
+ * **A string is enough.** It is the app's own fallback answer — a hosted
26
+ * model's text, a cached reply — not wire data, and requiring a whole
27
+ * `DeliveredResult` for it was ceremony that invited invented shapes. The
28
+ * README's own example got it wrong, which is how this was found.
29
+ *
30
+ * **Whatever comes back is labelled `fallback: true` by the wait, not by
31
+ * the caller** — {@link MUSTS.FALLBACK_LABELED}. Work that did not come
32
+ * from the user's own compute must not be reportable as though it did, and
33
+ * that stays true whether an app returns a bare string or a full record it
34
+ * assembled itself. The stamp is applied after this function returns, so
35
+ * there is no shape an app can hand back that hides what it is.
24
36
  */
25
- readonly onNoRunner?: (reason: string) => DeliveredResult | undefined | Promise<DeliveredResult | undefined>;
37
+ readonly onNoRunner?: (reason: string) => string | DeliveredResult | undefined | Promise<string | DeliveredResult | undefined>;
26
38
  /** Abort the wait. */
27
39
  readonly signal?: AbortSignal;
28
40
  }
@@ -40,8 +52,26 @@ interface ResultDelivery {
40
52
  interface PollingDeliveryDeps {
41
53
  /** Current state of the job, or null if unknown. */
42
54
  readonly read: (jobId: string) => Promise<DeliveredResult | null>;
43
- /** Whether a runner could still take this job. */
44
- readonly availability: (jobId: string) => Promise<{
55
+ /**
56
+ * Whether a runner could still take this job — **when there is anybody to
57
+ * ask.**
58
+ *
59
+ * Optional since alpha.66, and its absence is the answer rather than a
60
+ * missing dependency. On the cloud lane nothing writes runners into this
61
+ * site's store: devices pair with the relay, so the question has no local
62
+ * answer and `runnerAvailability` refuses to invent one.
63
+ *
64
+ * The refusal was correct and it landed in a loop that asked every 500ms.
65
+ * `job.result()` threw on its first poll for every cloud-lane site — a
66
+ * refusal aimed at outsiders that our own delivery tripped over.
67
+ *
68
+ * Not fixed by catching the throw here. That is a swallowed error in
69
+ * costume, and a catch wide enough to hold it would also eat a store that
70
+ * had genuinely gone away. The instrument is simply not handed over on a
71
+ * lane where it cannot see, and this loop does not ask a question nobody
72
+ * can answer.
73
+ */
74
+ readonly availability?: (jobId: string) => Promise<{
45
75
  available: boolean;
46
76
  reason?: string;
47
77
  blocked: boolean;
@@ -1,5 +1,5 @@
1
1
  import { StoredKeys, Endpoint } from '@byollm/protocol';
2
- import { B as ByollmStore } from './store-Cj5b6A9j.js';
2
+ import { B as ByollmStore } from './store-Cx2_bck1.js';
3
3
 
4
4
  /** Everything a mount needs to serve the protocol. */
5
5
  /**
@@ -70,6 +70,6 @@ declare class ByollmHandlers {
70
70
  handle(endpoint: Endpoint, body: unknown, auth: AuthContext): Promise<HandlerResult>;
71
71
  }
72
72
  /** The protocol version this build speaks. */
73
- declare const SERVED_PROTOCOL_VERSION: "0";
73
+ declare const SERVED_PROTOCOL_VERSION: "1";
74
74
 
75
75
  export { ByollmHandlers as B, type HandlerConfig as H, SERVED_PROTOCOL_VERSION as S, type HandlerResult as a };
package/dist/index.d.ts CHANGED
@@ -1,10 +1,10 @@
1
- import { StoredKeys, JobKind, DeliveredResult, Endpoint, Capability } from '@byollm/protocol';
2
- import { P as PollingDeliveryDeps, R as ResultDelivery, W as WaitOptions } from './delivery-36nIe-b3.js';
3
- export { N as NoRunnerAvailableError, a as PollingDelivery, b as ResultTimeoutError } from './delivery-36nIe-b3.js';
4
- import { B as ByollmStore, J as JobRecord, E as EnqueueInput, R as RunnerRecord, S as StoredJobInput, C as ClaimArgs, a as RenewArgs, b as RenewResult, A as AdoptArgs, c as CompleteArgs, d as CompleteResult, e as ReleaseArgs, P as PairingRecord, f as ApproveArgs, T as TouchArgs } from './store-Cj5b6A9j.js';
5
- export { g as CompleteHolder, h as JobStore, i as RunnerStore } from './store-Cj5b6A9j.js';
6
- import { H as HandlerConfig } from './handlers-DgW0QNTf.js';
7
- export { B as ByollmHandlers, a as HandlerResult, S as SERVED_PROTOCOL_VERSION } from './handlers-DgW0QNTf.js';
1
+ import { StoredKeys, JobKind, Audience, DeliveredResult, Endpoint, Capability } from '@byollm/protocol';
2
+ import { P as PollingDeliveryDeps, R as ResultDelivery, W as WaitOptions } from './delivery-CaGbp0Tc.js';
3
+ export { N as NoRunnerAvailableError, a as PollingDelivery, b as ResultTimeoutError } from './delivery-CaGbp0Tc.js';
4
+ import { B as ByollmStore, J as JobRecord, E as EnqueueInput, R as RunnerRecord, S as StoredJobInput, C as ClaimArgs, a as RenewArgs, b as RenewResult, A as AdoptArgs, c as CompleteArgs, d as CompleteResult, e as ReleaseArgs, P as PairingRecord, f as ApproveArgs, T as TouchArgs } from './store-Cx2_bck1.js';
5
+ export { g as CompleteHolder, h as JobStore, i as RunnerStore } from './store-Cx2_bck1.js';
6
+ import { H as HandlerConfig } from './handlers-CTV3Jc6Q.js';
7
+ export { B as ByollmHandlers, a as HandlerResult, S as SERVED_PROTOCOL_VERSION } from './handlers-CTV3Jc6Q.js';
8
8
 
9
9
  /**
10
10
  * The cloud lane — cloud_004 §9.4.
@@ -49,6 +49,44 @@ interface CloudLaneOptions {
49
49
  readonly fetch?: typeof fetch;
50
50
  }
51
51
  /** What one pump cycle did, for logging and for tests. */
52
+ /**
53
+ * A relay that could not answer this request — alpha.31.
54
+ *
55
+ * `retryable` is the whole point: a draining pod and a bad signature are both
56
+ * failures, and treating them alike is how a site either falls over on every
57
+ * deploy or stays silently disconnected for a week.
58
+ */
59
+ declare class RelayUnavailable extends Error {
60
+ readonly retryable: boolean;
61
+ /** The protocol's own code, when the relay sent one. */
62
+ readonly code: string;
63
+ constructor(message: string, retryable: boolean, code: string);
64
+ }
65
+ /**
66
+ * The job was not queued, and waiting will not change that.
67
+ *
68
+ * Distinct from {@link RelayUnavailable} because it is the opposite situation:
69
+ * the relay answered, promptly and correctly, and the answer is that this job
70
+ * has nowhere to go. Catching "the relay is down" to handle "nobody has chosen
71
+ * a model" would retry forever against a fact.
72
+ *
73
+ * Two codes, and they belong to two different people.
74
+ *
75
+ * `purpose-not-declared` is the site's own manifest. It names the purpose and
76
+ * the remedy, because a developer reading their own logs is entitled to both
77
+ * and neither says anything about a person.
78
+ *
79
+ * `slot-unsatisfiable` is the person's own dashboard, and says only that.
80
+ * Which service, whose device, whether one exists at all — none of it travels,
81
+ * and the sentence is the same for everybody. A site learns *that* a slot
82
+ * cannot be satisfied, which is exactly what the README has always promised
83
+ * and what this class finally delivers.
84
+ */
85
+ declare class EnqueueRefused extends Error {
86
+ /** `purpose-not-declared` or `slot-unsatisfiable`. */
87
+ readonly code: string;
88
+ constructor(message: string, code: string);
89
+ }
52
90
  interface PumpReport {
53
91
  /** Jobs sealed to a claiming device this cycle. */
54
92
  readonly sealed: string[];
@@ -62,6 +100,20 @@ interface PumpReport {
62
100
  * exactly the case `awaiting-payload` exists to bound.
63
101
  */
64
102
  readonly refused: string[];
103
+ /**
104
+ * Why this cycle stopped early, when it did — alpha.31.
105
+ *
106
+ * A relay can legitimately say "ask me later": a pod draining through its
107
+ * `preStop` window answers `503 not-ready` to every routed call, and that
108
+ * happens on **every deploy**. Before this existed the lane read the body
109
+ * of that answer, found no `jobs` in it, and threw `TypeError: finished.jobs
110
+ * is not iterable` — a site falling over because its relay was polite.
111
+ *
112
+ * Absent on an ordinary cycle. Present, with the reason, when the lane
113
+ * deferred: a site that quietly did nothing and a site that was told to wait
114
+ * must not look the same in a log.
115
+ */
116
+ readonly deferred?: string;
65
117
  }
66
118
  declare class CloudLane {
67
119
  #private;
@@ -79,6 +131,19 @@ declare class CloudLane {
79
131
  * field on `JobStub` to put one in.
80
132
  */
81
133
  publish(record: JobRecord): Promise<void>;
134
+ /**
135
+ * Withdraw a job at the relay — cloud_008 §2.2.
136
+ *
137
+ * `app.cancel()` marks the site's own row terminal, which stops the *next*
138
+ * seal. It cannot stop a device that is already running the work, because
139
+ * on this lane the site is not the upstream: only the relay talks to the
140
+ * daemon, and it answered `cancel: []` unconditionally.
141
+ *
142
+ * So the cancellation has to travel. The relay marks the job, stops
143
+ * offering it, and names it to the holding device at its next heartbeat —
144
+ * the same path the direct plane has always had, arriving one hop later.
145
+ */
146
+ cancel(jobId: string): Promise<void>;
82
147
  /**
83
148
  * One cycle: seal for anything claimed, collect anything finished.
84
149
  *
@@ -91,7 +156,18 @@ declare class CloudLane {
91
156
  }
92
157
 
93
158
  /** Why a job cannot presently run. */
94
- type NoRunnerReason = "no-runner-paired" | "no-runner-online" | "no-matching-capability" | "audience-admits-nobody";
159
+ type NoRunnerReason = "no-runner-paired" | "no-runner-online" | "no-matching-capability" | "audience-admits-nobody"
160
+ /**
161
+ * The owner's default for this kind can never serve *this* requester —
162
+ * byollm_016's defaults-meet-audiences corner.
163
+ *
164
+ * The specimen: a default of `claude-cli`, self-locked by
165
+ * `SUBSCRIPTION_SELF_LOCK`, and a team member's unselected job. It resolves
166
+ * to something that will never run it. Reported rather than left to time
167
+ * out, because a wait that can never end is indistinguishable from one that
168
+ * has not ended yet, and only one of them is worth waiting through.
169
+ */
170
+ | "default-unusable";
95
171
  /**
96
172
  * The no-runner signal (byollm_001 Rev 1 §D).
97
173
  *
@@ -110,7 +186,7 @@ interface RunnerAvailability {
110
186
  interface AvailabilityQuery {
111
187
  readonly kind: JobKind;
112
188
  readonly owner: string;
113
- readonly audience?: "self" | "named" | "public";
189
+ readonly audience?: Audience;
114
190
  readonly audienceAllow?: readonly string[];
115
191
  }
116
192
  interface ByollmAppOptions {
@@ -163,14 +239,6 @@ interface JobHandle {
163
239
  /** Ask the runner to stop. */
164
240
  cancel(): Promise<void>;
165
241
  }
166
- /**
167
- * The app-facing half of `@byollm/server`.
168
- *
169
- * The daemon talks to {@link ByollmHandlers}; the app talks to this. Keeping
170
- * them separate is what makes "one door per state write" hold — an app
171
- * enqueues and cancels through these methods and never writes job rows by
172
- * hand.
173
- */
174
242
  declare class ByollmApp {
175
243
  #private;
176
244
  /** Present only in the cloud lane; the site's side of the relay. */
@@ -183,7 +251,7 @@ declare class ByollmApp {
183
251
  * result comes back marked untrusted (see {@link ByollmApp.result}), and
184
252
  * the app is obliged to disclose that to whoever reads it.
185
253
  */
186
- enqueue(input: EnqueueInput): Promise<JobHandle>;
254
+ enqueue<K extends JobKind>(input: EnqueueInput<K>): Promise<JobHandle>;
187
255
  /** Read a job's current state. */
188
256
  job(jobId: string): Promise<JobRecord | null>;
189
257
  /**
@@ -192,7 +260,7 @@ declare class ByollmApp {
192
260
  * Check `provenance.untrusted` before rendering. It is true for every
193
261
  * `named`/`public` job, because that text came from someone else's machine
194
262
  * and the app must not present it as its own AI's answer
195
- * ({@link MUSTS.RESULT_PROVENANCE}).
263
+ * ({@link MUSTS.PROVENANCE_NAMES_DEVICE}).
196
264
  */
197
265
  result(jobId: string): Promise<DeliveredResult | null>;
198
266
  /** Ask a runner to stop. Queued jobs cancel at once; held jobs at the next heartbeat. */
@@ -315,8 +383,6 @@ declare function formatSiteKeys(keys: StoredKeys): string;
315
383
 
316
384
  /** A device code: the secret the daemon polls with. Never shown to a user. */
317
385
  declare function generateDeviceCode(): string;
318
- /** A runner bearer token. */
319
- declare function generateRunnerToken(): string;
320
386
  /** A runner id. */
321
387
  declare function generateRunnerId(): string;
322
388
  /** A job id. */
@@ -366,14 +432,16 @@ declare class MemoryStore implements ByollmStore {
366
432
  expireDue(now: number): Promise<JobRecord[]>;
367
433
  cancel(jobId: string, now: number): Promise<JobRecord | null>;
368
434
  listClaimedBy(runnerId: string): Promise<JobRecord[]>;
369
- listCancelRequests(runnerId: string): Promise<string[]>;
435
+ listCancelRequests(runnerId: string): Promise<{
436
+ jobId: string;
437
+ leaseId: string;
438
+ }[]>;
370
439
  createPairing(record: PairingRecord): Promise<void>;
371
440
  getPairingByDeviceCodeHash(hash: string): Promise<PairingRecord | null>;
372
441
  getPairingByUserCode(userCode: string): Promise<PairingRecord | null>;
373
442
  approvePairing(args: ApproveArgs): Promise<RunnerRecord>;
374
443
  denyPairing(userCode: string, _now: number): Promise<void>;
375
444
  consumePairingToken(deviceCodeHash: string): Promise<void>;
376
- getRunnerByTokenHash(hash: string): Promise<RunnerRecord | null>;
377
445
  getRunner(runnerId: string): Promise<RunnerRecord | null>;
378
446
  touchRunner(args: TouchArgs): Promise<RunnerRecord | null>;
379
447
  revokeRunner(runnerId: string, now: number): Promise<void>;
@@ -384,4 +452,4 @@ declare class MemoryStore implements ByollmStore {
384
452
  /** The capability that would serve a kind, if any. */
385
453
  declare function capabilityFor(capabilities: readonly Capability[], kind: string): Capability | undefined;
386
454
 
387
- export { AdoptArgs, ApproveArgs, type AvailabilityQuery, ByollmApp, type ByollmAppOptions, ByollmStore, ClaimArgs, CloudLane, type CloudLaneOptions, CompleteArgs, CompleteResult, EnqueueInput, HandlerConfig, type JobHandle, JobRecord, MemoryStore, type MemoryStoreOptions, type NoRunnerReason, PairingRecord, PollingDeliveryDeps, type PumpReport, ReleaseArgs, RenewArgs, RenewResult, ResultDelivery, type RunnerAvailability, RunnerRecord, TouchArgs, WaitOptions, capabilityFor, createFetchHandler, formatSiteKeys, generateDeviceCode, generateJobId, generateRunnerId, generateRunnerToken, generateSiteKeys, generateUserCode, hashSecret, normalizeUserCode, routeEndpoint, secretsMatch, signatureFrom, siteKeysFromEnv };
455
+ export { AdoptArgs, ApproveArgs, type AvailabilityQuery, ByollmApp, type ByollmAppOptions, ByollmStore, ClaimArgs, CloudLane, type CloudLaneOptions, CompleteArgs, CompleteResult, EnqueueInput, EnqueueRefused, HandlerConfig, type JobHandle, JobRecord, MemoryStore, type MemoryStoreOptions, type NoRunnerReason, PairingRecord, PollingDeliveryDeps, type PumpReport, RelayUnavailable, ReleaseArgs, RenewArgs, RenewResult, ResultDelivery, type RunnerAvailability, RunnerRecord, TouchArgs, WaitOptions, capabilityFor, createFetchHandler, formatSiteKeys, generateDeviceCode, generateJobId, generateRunnerId, generateSiteKeys, generateUserCode, hashSecret, normalizeUserCode, routeEndpoint, secretsMatch, signatureFrom, siteKeysFromEnv };