@byok-sdk/server 0.12.0 → 0.13.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/ids.d.ts DELETED
@@ -1,3 +0,0 @@
1
- export declare function generatePairingCode(length?: number): string;
2
- export declare function generateDeviceId(): string;
3
- export declare function generateTaskId(): string;
package/dist/pairing.d.ts DELETED
@@ -1,87 +0,0 @@
1
- import type { TenantId } from './auth';
2
- /**
3
- * S1: the tenant identity a pairing code carries. Minted out-of-band by the
4
- * SaaS's own auth/device-flow UI — the only party that knows which tenant a
5
- * human is acting for — and returned by {@link PairingManager.redeemPairingCode}
6
- * so `POST /byok/pair` can write it onto the device row in the same
7
- * synchronous step that consumes the code.
8
- *
9
- * Deliberately NOT a wire field: `PairRequest` has no tenant of its own
10
- * (docs/protocol.md §6.1), so a device can never name the tenant it lands in.
11
- * These claims are the single source of truth for the row, and the row — not
12
- * a later token, and never client input — is what every authed surface
13
- * checks against.
14
- */
15
- export interface PairingCodeClaims {
16
- tenantId: TenantId;
17
- productId: string;
18
- }
19
- export interface PairingCodeInfo {
20
- code: string;
21
- expiresAt: string;
22
- }
23
- /** Immutable facts supplied by one first-pair HTTP request. */
24
- export interface PairingAttemptBinding {
25
- deviceName: string;
26
- devicePublicKey: string;
27
- }
28
- /** The recoverable enrollment fact created exactly once for one pairing code. */
29
- export interface PairingCompletion {
30
- deviceId: string;
31
- tenantId: TenantId;
32
- productId: string;
33
- deviceName: string;
34
- devicePublicKey: string;
35
- }
36
- /** Thrown when a pairing code is missing, expired, or already used. */
37
- export declare class PairingCodeInvalidError extends Error {
38
- constructor(reason: string);
39
- }
40
- /** A spent code may be replayed only with the immutable request that spent it. */
41
- export declare class PairingAttemptConflictError extends Error {
42
- constructor();
43
- }
44
- /**
45
- * In-memory pairing-code lifecycle: single-use, ~10min TTL codes minted
46
- * out-of-band (by the SaaS's own auth/device-flow UI) and redeemed exactly
47
- * once by `POST /byok/pair`.
48
- *
49
- * Device identity (deviceId/deviceName/devicePublicKey/revocation) and
50
- * token issuance moved to `auth.ts`'s `DeviceRegistry`/`TokenSigner` as of
51
- * Auth v2 (docs/protocol.md §6) — this class knows about devices only to the
52
- * extent of carrying the {@link PairingCodeClaims} that decide which tenant
53
- * and product the device being paired will belong to (S1).
54
- */
55
- export declare class PairingManager {
56
- private readonly codes;
57
- /**
58
- * Mint a single-use code bound to `claims`. Claims are REQUIRED — a
59
- * claimless mint is a compile error, and (for a JS caller, or a claims
60
- * object assembled from untyped config) a runtime {@link TypeError}. There
61
- * is no default tenant and no default product: a device with no tenant
62
- * must be inexpressible, so the failure happens here, at the mint, rather
63
- * than being filled in downstream.
64
- */
65
- createPairingCode(claims: PairingCodeClaims): PairingCodeInfo;
66
- /**
67
- * Validate and consume a pairing code, returning the {@link PairingCodeClaims}
68
- * it was minted with. Throws {@link PairingCodeInvalidError} if the code is
69
- * unknown, expired, or already used — callers (the HTTP handler) map that to
70
- * a 401. Single-use is what makes the caller's "redeem, then register the
71
- * device row with these claims" sequence safe: a second redeem of the same
72
- * code can never reach the registration step at all.
73
- */
74
- redeemPairingCode(code: string): PairingCodeClaims;
75
- /**
76
- * Bind a code to one exact pairing request, returning any completion that a
77
- * prior response/token failure left recoverable. This mutation is entirely
78
- * synchronous: the HTTP route cannot yield between binding, registration,
79
- * and completion recording, so concurrent requests have one winner.
80
- */
81
- beginPairingAttempt(code: string, binding: PairingAttemptBinding): {
82
- claims: PairingCodeClaims;
83
- completion?: PairingCompletion;
84
- };
85
- /** Record the one device identity a bound pairing attempt completed as. */
86
- completePairingAttempt(code: string, binding: PairingAttemptBinding, completion: PairingCompletion): PairingCompletion;
87
- }
@@ -1,94 +0,0 @@
1
- import type { DatabaseSync } from 'node:sqlite';
2
- import { type BlobStore, type CreateUploadInput, type ReadContentResult, type WriteContentResult } from './blob-store';
3
- import type { TenantId } from './auth';
4
- export interface SqliteBlobStoreOptions {
5
- /**
6
- * Database file path. Use `:memory:` to exercise the SQLite code path
7
- * without persistence — defeats this store's whole purpose (same caveat
8
- * as `SqliteTaskStore`'s `:memory:` option); real restart-safety requires
9
- * a real file path.
10
- */
11
- path: string;
12
- /** How long a presigned upload/download URL stays valid, ms. Default 15 minutes — same default as `LocalDiskBlobStore`. */
13
- urlTtlMs?: number;
14
- /**
15
- * HMAC signing key for presigned URLs. Defaults to a key generated once
16
- * and persisted in this same database (a `meta` table) — so, unlike
17
- * `LocalDiskBlobStore`'s fresh-per-instance `randomBytes(32)` (fine there
18
- * only because its metadata doesn't survive a restart either), the
19
- * default here is *already* stable across restarts: a URL signed by one
20
- * process instance still verifies against a later instance pointed at
21
- * the same database file. Pass this explicitly only if the key needs to
22
- * live outside the database (e.g. shared across multiple database files,
23
- * or rotated independently of the data).
24
- */
25
- signingKey?: Buffer;
26
- }
27
- /** Exported (only) so `sqlite-blob-store.test.ts` can apply the same schema to a raw `DatabaseSync` connection when testing {@link loadOrCreateSigningSecret}'s concurrency behavior directly. */
28
- export declare const SCHEMA = "\nCREATE TABLE IF NOT EXISTS meta (\n key TEXT PRIMARY KEY,\n value TEXT NOT NULL\n);\nCREATE TABLE IF NOT EXISTS blobs (\n blob_id TEXT PRIMARY KEY,\n tenant_id TEXT NOT NULL,\n size INTEGER NOT NULL,\n content_type TEXT NOT NULL,\n content_hash TEXT NOT NULL,\n uploaded INTEGER NOT NULL DEFAULT 0,\n data BLOB\n);\n";
29
- /**
30
- * Atomically load the persisted HMAC signing secret from `db`'s `meta`
31
- * table, generating and persisting one if none exists yet.
32
- *
33
- * Safe under two `DatabaseSync` connections racing on the same file — e.g.
34
- * two fresh `SqliteBlobStore` instances constructed against a brand-new
35
- * database at nearly the same moment. Both may see no existing row (via the
36
- * initial `SELECT`) and both generate a candidate secret, but `INSERT OR
37
- * IGNORE` guarantees at most one candidate is ever persisted, and —
38
- * critically — EVERY caller unconditionally re-reads the row afterward and
39
- * returns THAT value, never its own locally-generated candidate. Without
40
- * that re-read (the bug this fixes: the previous implementation used
41
- * `INSERT OR REPLACE` and returned its own candidate unconditionally), a
42
- * caller whose candidate lost the race would keep using its own discarded
43
- * value in memory — so a presigned URL it signs would fail to verify
44
- * against any other instance, which persisted (and uses) the winning value.
45
- *
46
- * `generateCandidate` defaults to `randomBytes(32)`; overridable so
47
- * `sqlite-blob-store.test.ts` can deterministically force the race window —
48
- * real callers never need to pass it.
49
- */
50
- export declare function loadOrCreateSigningSecret(db: DatabaseSync, generateCandidate?: () => Buffer): Buffer;
51
- /**
52
- * Persistent {@link BlobStore} backed by `node:sqlite` — no native
53
- * dependency, same rationale as `SqliteTaskStore` (`sqlite-support.ts`).
54
- * Metadata AND content bytes both live in the same database file (a `data
55
- * BLOB` column), so a fresh instance pointed at the same file recovers
56
- * everything: declared blobs, upload state, and the bytes themselves,
57
- * byte-for-byte.
58
- *
59
- * Presigned URLs use the same HMAC-signed-query-param scheme as
60
- * `LocalDiskBlobStore` (`/byok/blobs/:id/content?sig=...&exp=...`, verified
61
- * generically by `http.ts` via {@link verifySignedUrl} regardless of which
62
- * `BlobStore` is plugged in) — the only difference is where the signing
63
- * secret comes from; see {@link SqliteBlobStoreOptions.signingKey}.
64
- *
65
- * Requires Node.js 22.5+ (`node:sqlite`'s minimum); constructing this on an
66
- * unsupported runtime throws `SqliteUnavailableError` (`sqlite-support.ts`).
67
- */
68
- export declare class SqliteBlobStore implements BlobStore {
69
- private readonly db;
70
- private readonly urlTtlMs;
71
- private readonly secret;
72
- private readonly insertBlobStmt;
73
- private readonly selectBlobStmt;
74
- private readonly selectUploadedStmt;
75
- private readonly writeContentStmt;
76
- private closed;
77
- constructor(opts: SqliteBlobStoreOptions);
78
- createUpload(tenantId: TenantId, input: CreateUploadInput, requestedBlobId?: string): Promise<{
79
- blobId: string;
80
- uploadUrl: string;
81
- }>;
82
- getDownloadUrl(tenantId: TenantId, blobId: string): Promise<string | undefined>;
83
- exists(tenantId: TenantId, blobId: string): Promise<boolean>;
84
- getUploadReservation(blobId: string): Promise<{
85
- size: number;
86
- } | undefined>;
87
- verifySignedUrl(blobId: string, action: 'put' | 'get', sig: string, exp: number): boolean;
88
- writeContent(blobId: string, data: Buffer): Promise<WriteContentResult>;
89
- readContent(blobId: string): Promise<ReadContentResult | undefined>;
90
- /** Close the underlying database connection — see `SqliteTaskStore.close`'s doc comment; same rationale. */
91
- close(): void;
92
- private computeSig;
93
- private signUrl;
94
- }
@@ -1,124 +0,0 @@
1
- import { type TaskState } from '@byok-sdk/protocol';
2
- import type { DatabaseSync } from 'node:sqlite';
3
- import { type CreateTaskInput, type TaskRecord, type TaskStore } from './task-store';
4
- export interface SqliteTaskStoreOptions {
5
- /**
6
- * Database file path. Use `:memory:` to exercise the SQLite code path
7
- * without a temp file (e.g. schema/query correctness tests) — but note
8
- * that defeats the entire point of this store (restart-safety), since an
9
- * in-memory SQLite database vanishes with the process exactly like
10
- * `InMemoryTaskStore` does. Real persistence requires a real file path.
11
- */
12
- path: string;
13
- }
14
- /**
15
- * S5 hardening: two processes (or two `SqliteTaskStore` instances in this
16
- * one) can both construct against the same pre-existing file at close to the
17
- * same instant, both see a given column missing via the `PRAGMA table_info`
18
- * read below, and both attempt the same `ALTER TABLE ... ADD COLUMN` —
19
- * SQLite allows only one to actually add it; the loser's `db.exec` throws
20
- * `duplicate column name`. That failure means the OTHER writer already won —
21
- * the column now genuinely exists, which is exactly the end state this
22
- * function is trying to reach — so it's caught here, the column list is
23
- * re-inspected fresh, and this function proceeds normally (no throw) once
24
- * confirmed. Anything else (a real schema problem, a disk error, a
25
- * `duplicate column name` for a DIFFERENT column than expected) is rethrown
26
- * unchanged — this only swallows the exact race this is written for.
27
- *
28
- * Exported only for this package's own tests to exercise the race
29
- * deterministically (mirrors `sqlite-support.ts`'s
30
- * `isSqliteCapableNodeVersion` convention) — not re-exported from
31
- * `index.ts`, so not part of the public package API.
32
- */
33
- export declare function ensureAdditiveColumns(db: DatabaseSync): void;
34
- /**
35
- * Persistent {@link TaskStore} backed by the Node.js built-in `node:sqlite`
36
- * module — no native dependency (`sqlite-support.ts`'s doc comment explains
37
- * why that's a hard requirement here). A fresh instance pointed at the same
38
- * database file recovers every task's full RECORD (instruction, policy,
39
- * device/session refs, result) exactly as `InMemoryTaskStore` would have
40
- * held it in memory — this is the M3 "task records survive a process
41
- * restart" story.
42
- *
43
- * Scope of that claim, precisely: this is RECORD persistence, not live
44
- * active-task recovery. A fresh `ConnectionHub` (`hub.ts`) wired to a
45
- * reopened store starts with empty runtimes/result-promises/event-queues/
46
- * device-registry/outboxes — so a task that was `Running` at restart comes
47
- * back as a `Running` *record* you can read and further `transition()`, not
48
- * as a task with its device/runtime connection reattached that will go on
49
- * to actually produce more events. Recovering/resuming an in-flight task's
50
- * live connection is a larger feature and out of scope here.
51
- *
52
- * Every write goes through a compare-and-set `UPDATE ... WHERE task_id = ?
53
- * AND state = ?` (the state {@link transition} just validated against), not
54
- * an unconditional update: two connections racing on the same task (both
55
- * reading e.g. `Running`, both independently validating a different target
56
- * state) can't both commit, which would otherwise let the later write
57
- * silently perform an illegal transition — including terminal -> terminal —
58
- * that neither validation call would have allowed had it seen the other's
59
- * write first. A lost compare-and-set re-reads the row and either
60
- * re-validates the requested move against the state that actually won, or
61
- * throws {@link IllegalTaskTransitionError} against it.
62
- *
63
- * Requires Node.js 22.5+ (`node:sqlite`'s minimum); constructing this on an
64
- * older/unsupported runtime throws `SqliteUnavailableError`
65
- * (`sqlite-support.ts`) with a clear message rather than a cryptic "Cannot
66
- * find module" trace.
67
- *
68
- * Enforces the exact same `TASK_TRANSITIONS`/`canTransition` state machine
69
- * as `InMemoryTaskStore`, via the same {@link IllegalTaskTransitionError}.
70
- */
71
- export declare class SqliteTaskStore implements TaskStore {
72
- private readonly db;
73
- private readonly insertStmt;
74
- private readonly updateStmt;
75
- private readonly selectStmt;
76
- private readonly selectAllStmt;
77
- private readonly updatePendingApprovalIdStmt;
78
- private closed;
79
- constructor(opts: SqliteTaskStoreOptions);
80
- create(input: CreateTaskInput): TaskRecord;
81
- get(taskId: string): TaskRecord | undefined;
82
- list(): TaskRecord[];
83
- /**
84
- * Apply `taskId`'s state -> `to`, merging `patch` into the record. Throws
85
- * {@link IllegalTaskTransitionError} if the move isn't legal per
86
- * `TASK_TRANSITIONS`, and if the task doesn't exist at all — identical
87
- * contract and error shapes to `InMemoryTaskStore.transition`.
88
- *
89
- * Implemented as a compare-and-set retry loop rather than a single
90
- * read-validate-write, because two separate connections (two processes,
91
- * or two `SqliteTaskStore` instances in this one) can both read the same
92
- * current state and both validate a move against it before either writes.
93
- * An unconditional `UPDATE` would let whichever commits last silently win
94
- * — including an illegal terminal -> terminal transition neither
95
- * validation call would have allowed with up-to-date information. Each
96
- * iteration here reads the CURRENT state fresh, validates `to` against
97
- * it, then writes with `WHERE state = <the state just validated>`
98
- * (`updateStmt`). If zero rows changed, some other writer committed
99
- * between this read and this write, so the loop re-reads and either
100
- * re-validates `to` against whatever the state actually is now, or throws
101
- * {@link IllegalTaskTransitionError} against it — the same outcome a
102
- * caller would get if it happened to run a moment later.
103
- */
104
- transition(taskId: string, to: TaskState, patch?: Partial<Omit<TaskRecord, 'taskId' | 'state'>>): TaskRecord;
105
- /**
106
- * See {@link TaskStore.setPendingApprovalId}'s own doc comment for the
107
- * full rationale, and `updatePendingApprovalIdStmt`'s own doc comment
108
- * (constructor, above) for the S3 CAS-guard rationale. The guarded
109
- * statement affecting 0 rows means `taskId` is no longer `AwaitApproval`
110
- * (or vanished) as of the write — a legitimate no-op, not an error: this
111
- * method never throws for a state mismatch (best-effort bookkeeping, same
112
- * as its unconditional pre-S3 form). Returns a FRESH read in that case
113
- * rather than the caller's now-stale pre-write snapshot, so a caller sees
114
- * what's actually stored.
115
- */
116
- setPendingApprovalId(taskId: string, pendingApprovalId: string | undefined): TaskRecord | undefined;
117
- /**
118
- * Close the underlying database connection. Not part of the `TaskStore`
119
- * interface (an in-memory store has nothing to close) — call this
120
- * explicitly when a store instance is done, e.g. before opening a second
121
- * instance against the same file, or on process shutdown.
122
- */
123
- close(): void;
124
- }
@@ -1,127 +0,0 @@
1
- import { type AgentRef, type PermissionPolicy, type RuntimeId, type TaskState, type ToolsetId } from '@byok-sdk/protocol';
2
- import type { TaskSnapshot } from './types';
3
- /** Thrown by a {@link TaskStore}'s `transition` when `from -> to` is not in TASK_TRANSITIONS. Every implementation (in-memory, SQLite, or otherwise) must throw this rather than silently applying an invalid move. */
4
- export declare class IllegalTaskTransitionError extends Error {
5
- readonly taskId: string;
6
- readonly from: TaskState;
7
- readonly to: TaskState;
8
- constructor(taskId: string, from: TaskState, to: TaskState);
9
- }
10
- export interface CreateTaskInput {
11
- taskId: string;
12
- instruction: string;
13
- runtime?: RuntimeId;
14
- policy: PermissionPolicy;
15
- requiredToolsets?: ToolsetId[];
16
- deviceId?: string;
17
- sessionRef?: string;
18
- agentRef?: AgentRef;
19
- }
20
- /** A task's full persisted state, as tracked by any {@link TaskStore} implementation. Same shape as {@link TaskSnapshot} — kept as its own (structurally identical) type so this storage-layer contract can evolve independently of the SDK-facing `TaskSnapshot` if a future need arises. */
21
- export interface TaskRecord extends TaskSnapshot {
22
- }
23
- /**
24
- * Storage contract for task records — the M3 injection point, mirroring how
25
- * {@link BlobStore} (`blob-store.ts`) is injectable: `createByokServer`
26
- * (`index.ts`) accepts `opts.taskStore`, defaulting to {@link InMemoryTaskStore}
27
- * so nothing breaks for an embedder that doesn't override it. `ConnectionHub`
28
- * (`hub.ts`) is written against this interface only — it never references
29
- * {@link InMemoryTaskStore} (or any other concrete implementation) directly —
30
- * so a persistent implementation such as `sqlite-task-store.ts`'s
31
- * `SqliteTaskStore` (M3) drops in with zero changes anywhere else.
32
- *
33
- * Every implementation MUST enforce the protocol's `TASK_TRANSITIONS` state
34
- * machine (via `canTransition`) inside `transition`, throwing
35
- * {@link IllegalTaskTransitionError} rather than silently applying an
36
- * invalid move — this is part of the interface's contract, not just an
37
- * `InMemoryTaskStore` implementation detail. `ConnectionHub`'s `applyOrFail`
38
- * (`hub.ts`) relies on that exception type to decide "illegal transition ->
39
- * force `Failed` if possible, else drop".
40
- */
41
- export interface TaskStore {
42
- /** Create a new task record in the `Offered` state. */
43
- create(input: CreateTaskInput): TaskRecord;
44
- /** Look up a task by id, or `undefined` if unknown. */
45
- get(taskId: string): TaskRecord | undefined;
46
- /** All known tasks. */
47
- list(): TaskRecord[];
48
- /**
49
- * Apply `taskId`'s state -> `to`, merging `patch` into the record. Must
50
- * throw {@link IllegalTaskTransitionError} if the move isn't legal per
51
- * `TASK_TRANSITIONS`, and a plain `Error` (message: `` `unknown taskId:
52
- * ${taskId}` ``) if the task doesn't exist at all —
53
- * {@link InMemoryTaskStore.transition}'s existing message format, which
54
- * some tests match on.
55
- */
56
- transition(taskId: string, to: TaskState, patch?: Partial<Omit<TaskRecord, 'taskId' | 'state'>>): TaskRecord;
57
- /**
58
- * M5 (approval targeting): update `taskId`'s `pendingApprovalId` WITHOUT a
59
- * state transition. Needed because `AwaitApproval -> AwaitApproval` is
60
- * deliberately not a legal `TASK_TRANSITIONS` edge (`@byok-sdk/protocol`'s
61
- * `task-state.ts`) — self-transitions aren't part of the frozen wire state
62
- * machine, so `transition` above cannot be used to update this field while
63
- * the record STAYS in `AwaitApproval` — yet a re-sent/updated
64
- * `task.await_approval` carrying a NEWER id while the record is ALREADY
65
- * `AwaitApproval` must still be reflected (see `ConnectionHub.
66
- * onAwaitApproval`, `hub.ts`). Every state-CHANGING write still goes
67
- * through `transition`; this is the one narrow exception for a same-state
68
- * field update. Returns `undefined` for an unknown `taskId` rather than
69
- * throwing — this is a best-effort bookkeeping update, not a
70
- * state-machine-enforced operation like `transition`.
71
- *
72
- * OPTIONAL: a `TaskStore` implementation predating M5 (a custom embedder
73
- * store written against the pre-M5 interface) doesn't have this method at
74
- * all — every call site in `hub.ts` guards its absence with `?.()`. A
75
- * store that omits it simply never records a superseding
76
- * `pendingApprovalId` on the same-state redelivery path (the first entry
77
- * into `AwaitApproval` still records one via `transition`'s own patch,
78
- * same as ever); an operator-supplied `opts.approvalId` on
79
- * `approveTask`/`rejectTask` then has nothing recorded to compare against
80
- * and proceeds untargeted, exactly like a legacy daemon that never
81
- * reported an id at all — a graceful degrade, not a broken embedder.
82
- * Every CURRENT implementation in this package ({@link InMemoryTaskStore},
83
- * `sqlite-task-store.ts`'s `SqliteTaskStore`) still implements it as a
84
- * required, always-present method — optionality is a concession to
85
- * EXISTING third-party implementations of this interface, not a hint that
86
- * a new one should skip it.
87
- */
88
- setPendingApprovalId?(taskId: string, pendingApprovalId: string | undefined): TaskRecord | undefined;
89
- }
90
- /**
91
- * Plain, framework-agnostic in-memory {@link TaskStore}. Enforces the
92
- * protocol's `TASK_TRANSITIONS` state machine via `canTransition` — every
93
- * state change must go through {@link transition}, which throws
94
- * {@link IllegalTaskTransitionError} rather than silently applying an invalid
95
- * move. Callers (the connection hub) decide what to do with that error; see
96
- * `hub.ts`'s `applyOrFail` for the "illegal transition -> force Failed if
97
- * possible, else drop" policy.
98
- *
99
- * M0/M1/M2 reference default — loses all state on process restart. See
100
- * `sqlite-task-store.ts`'s `SqliteTaskStore` (M3) for a persistent
101
- * alternative implementing the same {@link TaskStore} contract.
102
- */
103
- export declare class InMemoryTaskStore implements TaskStore {
104
- private readonly tasks;
105
- create(input: CreateTaskInput): TaskRecord;
106
- get(taskId: string): TaskRecord | undefined;
107
- list(): TaskRecord[];
108
- /**
109
- * Apply `taskId`'s state -> `to`, merging `patch` into the record. Throws
110
- * {@link IllegalTaskTransitionError} if the move isn't legal per
111
- * `TASK_TRANSITIONS`, and if the task doesn't exist at all.
112
- */
113
- transition(taskId: string, to: TaskState, patch?: Partial<Omit<TaskRecord, 'taskId' | 'state'>>): TaskRecord;
114
- /**
115
- * See {@link TaskStore.setPendingApprovalId}'s own doc comment for the
116
- * full rationale. State-guarded (S3 hardening): a write only applies while
117
- * `taskId` is still `AwaitApproval` — mirrors `SqliteTaskStore`'s own `AND
118
- * state = 'AwaitApproval'` CAS predicate (`sqlite-task-store.ts`) for
119
- * symmetry between the two reference implementations, guarding against a
120
- * laggard caller resurrecting a pending id after the task already left
121
- * `AwaitApproval` (e.g. a queued/delayed `task.await_approval` processed
122
- * after a real `approveTask`/`rejectTask` already transitioned it
123
- * elsewhere). A non-matching call is a no-op: returns the record exactly
124
- * as it currently stands, not the caller's requested (rejected) value.
125
- */
126
- setPendingApprovalId(taskId: string, pendingApprovalId: string | undefined): TaskRecord | undefined;
127
- }
@@ -1,23 +0,0 @@
1
- import type { Server as HttpServer } from 'node:http';
2
- import { type AuthDeps } from './auth';
3
- import type { ConnectionHub } from './hub';
4
- interface AttachDeps extends AuthDeps {
5
- hub: ConnectionHub;
6
- productId: string;
7
- /** WS-native ping interval, ms. Defaults inside `heartbeat.ts` (30s) if omitted. */
8
- heartbeatIntervalMs?: number;
9
- helloTimeoutMs: number;
10
- maxPendingWebSockets: number;
11
- maxPayloadBytes: number;
12
- }
13
- /**
14
- * Wire up the `GET /byok/ws` upgrade on a raw Node HTTP server (the one
15
- * `@hono/node-server`'s `serve()` returns). Auth happens on the upgrade
16
- * request itself via `Authorization: Bearer <accessToken>` (a JWT minted by
17
- * `/byok/pair` or `/byok/token` — Auth v2, §6); an invalid, expired, or
18
- * revoked token gets a 401 and the socket is destroyed. Handshake
19
- * (`conn.hello` -> `conn.ack`) happens on the first WS message once
20
- * upgraded.
21
- */
22
- export declare function attachWebSocket(server: HttpServer, deps: AttachDeps): void;
23
- export {};