@byok-sdk/cloud 0.5.0 → 0.6.1
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/cloud.d.ts +53 -8
- package/dist/errors.d.ts +12 -0
- package/dist/handlers/messages.d.ts +11 -0
- package/dist/inbound.d.ts +4 -3
- package/dist/index.d.ts +5 -5
- package/dist/index.js +779 -89
- package/dist/index.js.map +1 -1
- package/dist/stores/in-memory/agent-egress.d.ts +12 -0
- package/dist/stores/in-memory/blobs.d.ts +8 -2
- package/dist/stores/in-memory/device-directory.d.ts +6 -1
- package/dist/stores/in-memory/index.d.ts +3 -1
- package/dist/stores/in-memory/task-attempts.d.ts +27 -6
- package/dist/stores/in-memory/task-cancellations.d.ts +9 -0
- package/dist/stores/ports.d.ts +116 -5
- package/dist/tenant-stores.d.ts +31 -2
- package/dist/terminal-result.d.ts +11 -1
- package/package.json +3 -3
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { type Clock, type TenantId } from '@byok-sdk/core';
|
|
2
|
+
import type { AgentEgressRecord, AgentEgressStore } from '../ports';
|
|
3
|
+
/** Reference egress fact store. Duplicate event ids return, never overwrite, the first receipt. */
|
|
4
|
+
export declare class InMemoryAgentEgressStore implements AgentEgressStore {
|
|
5
|
+
#private;
|
|
6
|
+
constructor(clock: Clock);
|
|
7
|
+
record(tenant: TenantId, input: Omit<AgentEgressRecord, 'tenantId' | 'recordedAt'>): Promise<{
|
|
8
|
+
readonly record: AgentEgressRecord;
|
|
9
|
+
readonly created: boolean;
|
|
10
|
+
}>;
|
|
11
|
+
get(tenant: TenantId, deviceId: string, eventId: string): Promise<AgentEgressRecord | undefined>;
|
|
12
|
+
}
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
*/
|
|
29
29
|
import { type Clock, type ContentHash, type ObjectStore, type StorageReservation, type TenantId } from '@byok-sdk/core';
|
|
30
30
|
import type { CloudCrypto } from '../../crypto/port';
|
|
31
|
-
import type {
|
|
31
|
+
import type { BlobContentProxy, BlobObservation, BlobReadResult, BlobWriteResult, CloudBlobStore } from '../ports';
|
|
32
32
|
/** How long a presigned upload/download URL stays valid. */
|
|
33
33
|
export declare const BLOB_URL_TTL_MS: number;
|
|
34
34
|
interface BlobRecord {
|
|
@@ -78,7 +78,13 @@ export declare class InMemoryBlobContentProxy implements BlobContentProxy {
|
|
|
78
78
|
constructor(registry: InMemoryBlobRegistry);
|
|
79
79
|
verifySignedUrl(blobId: string, action: 'put' | 'get', sig: string, exp: number): Promise<boolean>;
|
|
80
80
|
writeContent(blobId: string, data: Uint8Array): Promise<BlobWriteResult>;
|
|
81
|
-
|
|
81
|
+
/**
|
|
82
|
+
* Never returns `{ok:false}`: this composition holds the bytes in the same
|
|
83
|
+
* process, so there is no upstream to be unreachable and no stream to be
|
|
84
|
+
* interrupted. Both `BlobReadErrorCode`s are structurally unreachable here
|
|
85
|
+
* — a proxy that fetches from object storage is where they become live.
|
|
86
|
+
*/
|
|
87
|
+
readContent(blobId: string): Promise<BlobReadResult | undefined>;
|
|
82
88
|
}
|
|
83
89
|
/** Both halves over one registry. The only way to obtain either. */
|
|
84
90
|
export interface InMemoryBlobs {
|
|
@@ -7,13 +7,18 @@
|
|
|
7
7
|
* so a revocation applied through the composite key is immediately visible to
|
|
8
8
|
* `/byok/challenge` and `/byok/token` with no second copy to keep in sync.
|
|
9
9
|
*/
|
|
10
|
-
import { type TenantId } from '@byok-sdk/core';
|
|
10
|
+
import { type PresenceStore, type TenantId, type TenantReadiness } from '@byok-sdk/core';
|
|
11
11
|
import type { DeviceDirectory, DeviceRecord, DeviceRegistration } from '../ports';
|
|
12
12
|
export declare class InMemoryDeviceDirectory implements DeviceDirectory {
|
|
13
13
|
#private;
|
|
14
14
|
register(tenant: TenantId, input: DeviceRegistration): Promise<DeviceRecord>;
|
|
15
15
|
get(tenant: TenantId, deviceId: string): Promise<DeviceRecord | undefined>;
|
|
16
16
|
revoke(tenant: TenantId, deviceId: string): Promise<void>;
|
|
17
|
+
recordCapabilities(tenant: TenantId, input: {
|
|
18
|
+
readonly deviceId: string;
|
|
19
|
+
readonly capabilities: readonly string[];
|
|
20
|
+
}): Promise<DeviceRecord | undefined>;
|
|
17
21
|
list(tenant: TenantId): Promise<readonly DeviceRecord[]>;
|
|
22
|
+
readiness(tenant: TenantId, presence: PresenceStore): Promise<TenantReadiness>;
|
|
18
23
|
resolveByDeviceId(deviceId: string): Promise<DeviceRecord | undefined>;
|
|
19
24
|
}
|
|
@@ -19,8 +19,10 @@ export { InMemoryPairingCodeStore } from './pairing-codes';
|
|
|
19
19
|
export { InMemoryRequestReceiptStore } from './receipts';
|
|
20
20
|
export { InMemoryProofRequestReceiptStore } from './proof-receipts';
|
|
21
21
|
export { InMemoryTaskAttemptStore } from './task-attempts';
|
|
22
|
+
export { InMemoryTaskCancellationStore } from './task-cancellations';
|
|
22
23
|
export { InMemoryActivityStore } from './activity';
|
|
23
24
|
export { InMemoryApprovalTimelineStore } from './approval-timeline';
|
|
25
|
+
export { InMemoryAgentEgressStore } from './agent-egress';
|
|
24
26
|
/**
|
|
25
27
|
* The port bundle plus the byte proxy, in the shape `createInMemoryCoreStores`
|
|
26
28
|
* already uses: the composition is an object with a `stores` field, not the
|
|
@@ -34,4 +36,4 @@ export interface InMemoryCloudComposition {
|
|
|
34
36
|
readonly stores: CloudStores;
|
|
35
37
|
readonly blobContentProxy: BlobContentProxy;
|
|
36
38
|
}
|
|
37
|
-
export declare function createInMemoryCloudStores(clock: Clock, crypto: CloudCrypto, objects: ObjectStore): InMemoryCloudComposition;
|
|
39
|
+
export declare function createInMemoryCloudStores(clock: Clock, crypto: CloudCrypto, objects: ObjectStore, mailbox: import('@byok-sdk/core').MailboxStore): InMemoryCloudComposition;
|
|
@@ -16,21 +16,42 @@
|
|
|
16
16
|
* unfalsifiable.
|
|
17
17
|
*/
|
|
18
18
|
import { type Clock, type TenantId } from '@byok-sdk/core';
|
|
19
|
-
import type { TaskAttempt, TaskAttemptStatus, TaskAttemptStore } from '../ports';
|
|
19
|
+
import type { AgentRef, TaskAttempt, TaskAttemptStatus, TaskAttemptStore } from '../ports';
|
|
20
20
|
export declare class InMemoryTaskAttemptStore implements TaskAttemptStore {
|
|
21
21
|
#private;
|
|
22
|
-
constructor(clock: Clock);
|
|
22
|
+
constructor(clock: Clock, state?: InMemoryTaskAttemptState);
|
|
23
23
|
open(tenant: TenantId, input: {
|
|
24
|
-
taskId: string;
|
|
25
|
-
deviceId: string;
|
|
24
|
+
readonly taskId: string;
|
|
25
|
+
readonly deviceId: string;
|
|
26
|
+
readonly agentRef?: AgentRef;
|
|
26
27
|
}): Promise<TaskAttempt>;
|
|
28
|
+
reserveAgentOffer(tenant: TenantId, input: {
|
|
29
|
+
readonly taskId: string;
|
|
30
|
+
readonly deviceId: string;
|
|
31
|
+
readonly agentRef: AgentRef;
|
|
32
|
+
}): Promise<{
|
|
33
|
+
readonly attempt: TaskAttempt;
|
|
34
|
+
readonly created: boolean;
|
|
35
|
+
}>;
|
|
27
36
|
get(tenant: TenantId, taskId: string): Promise<TaskAttempt | undefined>;
|
|
37
|
+
getMany(tenant: TenantId, taskIds: readonly string[]): Promise<readonly TaskAttempt[]>;
|
|
28
38
|
claim(tenant: TenantId, input: {
|
|
29
39
|
taskId: string;
|
|
30
40
|
deviceId: string;
|
|
31
41
|
}): Promise<TaskAttempt | undefined>;
|
|
32
42
|
recordStatus(tenant: TenantId, input: {
|
|
33
|
-
taskId: string;
|
|
34
|
-
status: TaskAttemptStatus;
|
|
43
|
+
readonly taskId: string;
|
|
44
|
+
readonly status: TaskAttemptStatus;
|
|
45
|
+
readonly agentRef?: AgentRef;
|
|
46
|
+
readonly terminalCause?: string;
|
|
35
47
|
}): Promise<TaskAttempt | undefined>;
|
|
36
48
|
}
|
|
49
|
+
/** Shared mutable state for the task and cancellation reference ports. */
|
|
50
|
+
export declare class InMemoryTaskAttemptState {
|
|
51
|
+
#private;
|
|
52
|
+
readonly attempts: Map<string, TaskAttempt>;
|
|
53
|
+
constructor(clock: Clock);
|
|
54
|
+
now(): string;
|
|
55
|
+
/** Serialize every state-changing operation for one tenant/task key. */
|
|
56
|
+
mutate<T>(key: string, operation: () => T | Promise<T>): Promise<T>;
|
|
57
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { type MailboxStore, type TenantId } from '@byok-sdk/core';
|
|
2
|
+
import type { TaskCancellationMutation, TaskCancellationRequest, TaskCancellationStore } from '../ports';
|
|
3
|
+
import { InMemoryTaskAttemptState } from './task-attempts';
|
|
4
|
+
/** Failure-free reference composition of the atomic cancellation port. */
|
|
5
|
+
export declare class InMemoryTaskCancellationStore implements TaskCancellationStore {
|
|
6
|
+
#private;
|
|
7
|
+
constructor(state: InMemoryTaskAttemptState, mailbox: MailboxStore);
|
|
8
|
+
request(tenant: TenantId, input: TaskCancellationRequest): Promise<TaskCancellationMutation | undefined>;
|
|
9
|
+
}
|
package/dist/stores/ports.d.ts
CHANGED
|
@@ -34,7 +34,9 @@
|
|
|
34
34
|
* because a composition backed by object storage physically cannot proxy
|
|
35
35
|
* bytes (see the blobs section below).
|
|
36
36
|
*/
|
|
37
|
-
import type { StorageReservation, TenantId } from '@byok-sdk/core';
|
|
37
|
+
import type { MailboxBody, MailboxMessage, PresenceStore, StorageReservation, TenantId, TenantReadiness } from '@byok-sdk/core';
|
|
38
|
+
import type { AgentEgressReliablePayload, AgentRef } from '@byok-sdk/protocol';
|
|
39
|
+
export type { AgentEgressReliablePayload, AgentRef } from '@byok-sdk/protocol';
|
|
38
40
|
import type { ActivityStore } from '../activity';
|
|
39
41
|
import type { ApprovalTimelineStore } from '../approval-timeline';
|
|
40
42
|
export interface DeviceRecord {
|
|
@@ -49,6 +51,12 @@ export interface DeviceRecord {
|
|
|
49
51
|
/** Current proof signing-key rotation generation. */
|
|
50
52
|
readonly proofKeyEpoch: number;
|
|
51
53
|
readonly revoked: boolean;
|
|
54
|
+
/**
|
|
55
|
+
* The latest capability snapshot written by an authenticated device
|
|
56
|
+
* handshake. This is deliberately separate from core presence: presence is
|
|
57
|
+
* lossy/TTL-bounded and cannot authorize Agent dispatch.
|
|
58
|
+
*/
|
|
59
|
+
readonly capabilities?: readonly string[];
|
|
52
60
|
}
|
|
53
61
|
/** Everything `POST /byok/pair` knows at registration time. `tenantId` is the store's first parameter; `revoked` is the store's own to set. */
|
|
54
62
|
export interface DeviceRegistration {
|
|
@@ -65,6 +73,17 @@ export interface DeviceDirectory {
|
|
|
65
73
|
get(tenant: TenantId, deviceId: string): Promise<DeviceRecord | undefined>;
|
|
66
74
|
revoke(tenant: TenantId, deviceId: string): Promise<void>;
|
|
67
75
|
list(tenant: TenantId): Promise<readonly DeviceRecord[]>;
|
|
76
|
+
/** Set-wise tenant observation; revoked devices never contribute presence. */
|
|
77
|
+
readiness(tenant: TenantId, presence: PresenceStore): Promise<TenantReadiness>;
|
|
78
|
+
/**
|
|
79
|
+
* Persist a capability snapshot obtained from an authenticated device
|
|
80
|
+
* message. Implementations may return `undefined` for an unknown/revoked
|
|
81
|
+
* device; callers must fail closed in that case.
|
|
82
|
+
*/
|
|
83
|
+
recordCapabilities(tenant: TenantId, input: {
|
|
84
|
+
readonly deviceId: string;
|
|
85
|
+
readonly capabilities: readonly string[];
|
|
86
|
+
}): Promise<DeviceRecord | undefined>;
|
|
68
87
|
/** Pre-tenant. Two callers only: `POST /byok/challenge` and `POST /byok/token`. Never exposed through the tenant facade. */
|
|
69
88
|
resolveByDeviceId(deviceId: string): Promise<DeviceRecord | undefined>;
|
|
70
89
|
}
|
|
@@ -115,16 +134,25 @@ export interface InboundDedupStore {
|
|
|
115
134
|
*/
|
|
116
135
|
checkAndRecord(tenant: TenantId, deviceId: string, envelopeId: string): Promise<boolean>;
|
|
117
136
|
}
|
|
118
|
-
export declare const TASK_ATTEMPT_STATUSES: readonly ['offered', 'claimed', 'running', 'complete', 'failed', 'cancelled'];
|
|
137
|
+
export declare const TASK_ATTEMPT_STATUSES: readonly ['offered', 'claimed', 'running', 'cancel_requested', 'complete', 'failed', 'cancelled'];
|
|
119
138
|
export type TaskAttemptStatus = (typeof TASK_ATTEMPT_STATUSES)[number];
|
|
120
139
|
export interface TaskAttempt {
|
|
121
140
|
readonly tenantId: TenantId;
|
|
122
141
|
readonly taskId: string;
|
|
123
142
|
/** The device the offer was addressed to. */
|
|
124
143
|
readonly deviceId: string;
|
|
125
|
-
/**
|
|
144
|
+
/** Exact Agent identity sealed when the strict Agent offer was opened. */
|
|
145
|
+
readonly agentRef?: AgentRef;
|
|
146
|
+
/** Set by `task.claim`, and only by the first one. Legacy attempts remain claimable by any tenant device until then; strict Agent attempts are target-device bound before this field is consulted. */
|
|
126
147
|
readonly ownerDeviceId?: string;
|
|
127
148
|
readonly status: TaskAttemptStatus;
|
|
149
|
+
/** Runtime-reported terminal cause from the first winning terminal. */
|
|
150
|
+
readonly terminalCause?: string;
|
|
151
|
+
/** Durable host cancellation authority. Its presence outranks later device terminal receipts. */
|
|
152
|
+
readonly cancellation?: {
|
|
153
|
+
readonly requestedAt: string;
|
|
154
|
+
readonly reason?: string;
|
|
155
|
+
};
|
|
128
156
|
readonly updatedAt: string;
|
|
129
157
|
}
|
|
130
158
|
export interface TaskAttemptStore {
|
|
@@ -132,8 +160,23 @@ export interface TaskAttemptStore {
|
|
|
132
160
|
open(tenant: TenantId, input: {
|
|
133
161
|
readonly taskId: string;
|
|
134
162
|
readonly deviceId: string;
|
|
163
|
+
readonly agentRef?: AgentRef;
|
|
135
164
|
}): Promise<TaskAttempt>;
|
|
165
|
+
/**
|
|
166
|
+
* Atomically reserve one strict Agent offer. `created: false` means the task
|
|
167
|
+
* id already had durable authority and no caller may append another offer.
|
|
168
|
+
*/
|
|
169
|
+
reserveAgentOffer(tenant: TenantId, input: {
|
|
170
|
+
readonly taskId: string;
|
|
171
|
+
readonly deviceId: string;
|
|
172
|
+
readonly agentRef: AgentRef;
|
|
173
|
+
}): Promise<{
|
|
174
|
+
readonly attempt: TaskAttempt;
|
|
175
|
+
readonly created: boolean;
|
|
176
|
+
}>;
|
|
136
177
|
get(tenant: TenantId, taskId: string): Promise<TaskAttempt | undefined>;
|
|
178
|
+
/** Batch lookup used by mailbox projection; implementations must not turn one poll into N queries. */
|
|
179
|
+
getMany(tenant: TenantId, taskIds: readonly string[]): Promise<readonly TaskAttempt[]>;
|
|
137
180
|
/** First claim wins the ownership; a later claim by the same device is idempotent. No-op (returns `undefined`) for a task this tenant never offered. */
|
|
138
181
|
claim(tenant: TenantId, input: {
|
|
139
182
|
readonly taskId: string;
|
|
@@ -143,8 +186,28 @@ export interface TaskAttemptStore {
|
|
|
143
186
|
recordStatus(tenant: TenantId, input: {
|
|
144
187
|
readonly taskId: string;
|
|
145
188
|
readonly status: TaskAttemptStatus;
|
|
189
|
+
readonly agentRef?: AgentRef;
|
|
190
|
+
readonly terminalCause?: string;
|
|
146
191
|
}): Promise<TaskAttempt | undefined>;
|
|
147
192
|
}
|
|
193
|
+
export interface TaskCancellationRequest {
|
|
194
|
+
readonly taskId: string;
|
|
195
|
+
readonly proposedMessageId: string;
|
|
196
|
+
readonly reason?: string;
|
|
197
|
+
readonly materialize: (seq: number, messageId: string) => MailboxBody | Promise<MailboxBody>;
|
|
198
|
+
}
|
|
199
|
+
export interface TaskCancellationMutation {
|
|
200
|
+
readonly attempt: TaskAttempt;
|
|
201
|
+
/** Absent only when the task was already complete or failed before cancellation. */
|
|
202
|
+
readonly message?: MailboxMessage;
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* One atomic authority for the cancellation tombstone plus its durable device
|
|
206
|
+
* delivery. Implementations must commit both or neither.
|
|
207
|
+
*/
|
|
208
|
+
export interface TaskCancellationStore {
|
|
209
|
+
request(tenant: TenantId, input: TaskCancellationRequest): Promise<TaskCancellationMutation | undefined>;
|
|
210
|
+
}
|
|
148
211
|
export interface RequestReceipt {
|
|
149
212
|
readonly tenantId: TenantId;
|
|
150
213
|
readonly key: string;
|
|
@@ -168,6 +231,22 @@ export interface RequestReceiptStore {
|
|
|
168
231
|
}>;
|
|
169
232
|
get(tenant: TenantId, key: string): Promise<RequestReceipt | undefined>;
|
|
170
233
|
}
|
|
234
|
+
export interface AgentEgressRecord {
|
|
235
|
+
readonly tenantId: TenantId;
|
|
236
|
+
readonly deviceId: string;
|
|
237
|
+
readonly payload: AgentEgressReliablePayload;
|
|
238
|
+
/** Stable cloud-generated receipt identity echoed on every exact replay. */
|
|
239
|
+
readonly receiptId: string;
|
|
240
|
+
readonly recordedAt: string;
|
|
241
|
+
}
|
|
242
|
+
export interface AgentEgressStore {
|
|
243
|
+
/** First event-id write wins; callers reject mismatches rather than updating. */
|
|
244
|
+
record(tenant: TenantId, input: Omit<AgentEgressRecord, 'tenantId' | 'recordedAt'>): Promise<{
|
|
245
|
+
readonly record: AgentEgressRecord;
|
|
246
|
+
readonly created: boolean;
|
|
247
|
+
}>;
|
|
248
|
+
get(tenant: TenantId, deviceId: string, eventId: string): Promise<AgentEgressRecord | undefined>;
|
|
249
|
+
}
|
|
171
250
|
export interface ProofRequestReceipt {
|
|
172
251
|
readonly tenantId: TenantId;
|
|
173
252
|
readonly deviceId: string;
|
|
@@ -214,6 +293,35 @@ export interface BlobContent {
|
|
|
214
293
|
readonly data: Uint8Array;
|
|
215
294
|
readonly contentType: string;
|
|
216
295
|
}
|
|
296
|
+
/**
|
|
297
|
+
* How a byte-proxying read can fail while the blob itself is known to exist.
|
|
298
|
+
*
|
|
299
|
+
* The split is about WHERE the failure landed relative to the upstream
|
|
300
|
+
* response, because that is the only part a proxy can observe and the only
|
|
301
|
+
* part an operator can act on: `blob_upstream_unavailable` means nothing came
|
|
302
|
+
* back at all (the upstream was unreachable/refused before its response
|
|
303
|
+
* started), so a retry may succeed unchanged; `blob_upstream_stream_interrupted`
|
|
304
|
+
* means the response HAD started and died mid-transfer, so whatever the caller
|
|
305
|
+
* already has is a truncated prefix, not a short blob.
|
|
306
|
+
*
|
|
307
|
+
* Both are 502 on the wire (see `BLOB_READ_ERROR_HTTP_STATUS` in
|
|
308
|
+
* `handlers/blobs.ts`) — the CODE carries the distinction, not the status.
|
|
309
|
+
*/
|
|
310
|
+
export declare const BLOB_READ_ERROR_CODES: readonly ['blob_upstream_unavailable', 'blob_upstream_stream_interrupted'];
|
|
311
|
+
export type BlobReadErrorCode = (typeof BLOB_READ_ERROR_CODES)[number];
|
|
312
|
+
/**
|
|
313
|
+
* The result of {@link BlobContentProxy.readContent}, in the same union idiom
|
|
314
|
+
* as {@link BlobWriteResult}. Note what is NOT in here: not-found stays
|
|
315
|
+
* `undefined` at the method's return type, so "no such blob" keeps its
|
|
316
|
+
* existing 404 meaning and never has to be spelled as a failure code.
|
|
317
|
+
*/
|
|
318
|
+
export type BlobReadResult = {
|
|
319
|
+
readonly ok: true;
|
|
320
|
+
readonly content: BlobContent;
|
|
321
|
+
} | {
|
|
322
|
+
readonly ok: false;
|
|
323
|
+
readonly code: BlobReadErrorCode;
|
|
324
|
+
};
|
|
217
325
|
/**
|
|
218
326
|
* The capability-minting half of blobs: what EVERY composition can honestly
|
|
219
327
|
* provide, whoever holds the bytes.
|
|
@@ -262,7 +370,8 @@ export interface CloudBlobStore {
|
|
|
262
370
|
export interface BlobContentProxy {
|
|
263
371
|
verifySignedUrl(blobId: string, action: 'put' | 'get', sig: string, exp: number): Promise<boolean>;
|
|
264
372
|
writeContent(blobId: string, data: Uint8Array): Promise<BlobWriteResult>;
|
|
265
|
-
|
|
373
|
+
/** `undefined` = no such blob (404); a `{ok:false}` result = the blob exists but its bytes could not be proxied (502, distinguished by {@link BlobReadErrorCode}). */
|
|
374
|
+
readContent(blobId: string): Promise<BlobReadResult | undefined>;
|
|
266
375
|
}
|
|
267
376
|
/**
|
|
268
377
|
* Step 0 of the inbound gate. S3a ships an allow-all reference (a hosted
|
|
@@ -288,11 +397,13 @@ export interface CloudStores {
|
|
|
288
397
|
readonly nonces: NonceStore;
|
|
289
398
|
readonly dedup: InboundDedupStore;
|
|
290
399
|
readonly tasks: TaskAttemptStore;
|
|
400
|
+
readonly cancellations: TaskCancellationStore;
|
|
291
401
|
readonly receipts: RequestReceiptStore;
|
|
402
|
+
readonly egress: AgentEgressStore;
|
|
292
403
|
readonly proofReceipts: ProofRequestReceiptStore;
|
|
293
404
|
readonly blobs: CloudBlobStore;
|
|
294
405
|
readonly rateLimiter: InboundRateLimiter;
|
|
295
406
|
}
|
|
296
407
|
/** Names of every port in {@link CloudStores}, in contract order. */
|
|
297
|
-
export declare const CLOUD_STORE_NAMES: readonly ['activity', 'approvals', 'devices', 'pairingCodes', 'nonces', 'dedup', 'tasks', 'receipts', 'proofReceipts', 'blobs', 'rateLimiter'];
|
|
408
|
+
export declare const CLOUD_STORE_NAMES: readonly ['activity', 'approvals', 'devices', 'pairingCodes', 'nonces', 'dedup', 'tasks', 'cancellations', 'receipts', 'egress', 'proofReceipts', 'blobs', 'rateLimiter'];
|
|
298
409
|
export type CloudStoreName = (typeof CLOUD_STORE_NAMES)[number];
|
package/dist/tenant-stores.d.ts
CHANGED
|
@@ -21,10 +21,10 @@
|
|
|
21
21
|
* presigned blob calls) are deliberately absent from this surface: a
|
|
22
22
|
* device-facing handler must not be able to reach them.
|
|
23
23
|
*/
|
|
24
|
-
import { type BoardClaimInput, type BoardItem, type BoardItemInput, type BoardListQuery, type BoardPage, type BoardStatusUpdateInput, type BoardUnclaimInput, type CoreStores, type MailboxAdvanceCursorInput, type MailboxAppendInput, type MailboxCursorState, type MailboxMessage, type MailboxPage, type MailboxReadQuery, type Principal, type PresenceHint, type PresenceHintInput, type StorageFinalizeInput, type StorageFinalizeResult, type StorageReservation, type StorageReservationInput, type TenantId } from '@byok-sdk/core';
|
|
24
|
+
import { type BoardClaimInput, type BoardItem, type BoardItemInput, type BoardListQuery, type BoardPage, type BoardStatusUpdateInput, type BoardUnclaimInput, type CoreStores, type MailboxAdvanceCursorInput, type MailboxAppendInput, type MailboxCursorState, type MailboxMessage, type MailboxPage, type MailboxReadQuery, type Principal, type PresenceHint, type PresenceHintInput, type TenantReadiness, type StorageFinalizeInput, type StorageFinalizeResult, type StorageReservation, type StorageReservationInput, type TenantId } from '@byok-sdk/core';
|
|
25
25
|
import type { ActivityAppendInput, ActivityTail } from './activity';
|
|
26
26
|
import type { ApprovalTimelineAppendInput, ApprovalTimelineTail } from './approval-timeline';
|
|
27
|
-
import type { BlobObservation, CloudStores, DeviceRecord, RequestReceipt, TaskAttempt, TaskAttemptStatus } from './stores/ports';
|
|
27
|
+
import type { BlobObservation, CloudStores, DeviceRecord, AgentEgressRecord, RequestReceipt, TaskCancellationMutation, TaskCancellationRequest, TaskAttempt, TaskAttemptStatus } from './stores/ports';
|
|
28
28
|
export interface TenantBoundMailbox {
|
|
29
29
|
append(input: MailboxAppendInput): Promise<MailboxMessage>;
|
|
30
30
|
/** Pure read. Never advances the cursor — the daemon's next poll is the only ack. */
|
|
@@ -57,13 +57,28 @@ export interface TenantBoundDevices {
|
|
|
57
57
|
get(deviceId: string): Promise<DeviceRecord | undefined>;
|
|
58
58
|
list(): Promise<readonly DeviceRecord[]>;
|
|
59
59
|
revoke(deviceId: string): Promise<void>;
|
|
60
|
+
/** Persist the authenticated device's own capability snapshot. */
|
|
61
|
+
recordCapabilities(input: {
|
|
62
|
+
readonly capabilities: readonly string[];
|
|
63
|
+
}): Promise<DeviceRecord | undefined>;
|
|
64
|
+
readiness(): Promise<TenantReadiness>;
|
|
60
65
|
}
|
|
61
66
|
export interface TenantBoundTaskAttempts {
|
|
62
67
|
open(input: {
|
|
63
68
|
readonly taskId: string;
|
|
64
69
|
readonly deviceId: string;
|
|
70
|
+
readonly agentRef?: TaskAttempt['agentRef'];
|
|
65
71
|
}): Promise<TaskAttempt>;
|
|
72
|
+
reserveAgentOffer(input: {
|
|
73
|
+
readonly taskId: string;
|
|
74
|
+
readonly deviceId: string;
|
|
75
|
+
readonly agentRef: NonNullable<TaskAttempt['agentRef']>;
|
|
76
|
+
}): Promise<{
|
|
77
|
+
readonly attempt: TaskAttempt;
|
|
78
|
+
readonly created: boolean;
|
|
79
|
+
}>;
|
|
66
80
|
get(taskId: string): Promise<TaskAttempt | undefined>;
|
|
81
|
+
getMany(taskIds: readonly string[]): Promise<readonly TaskAttempt[]>;
|
|
67
82
|
claim(input: {
|
|
68
83
|
readonly taskId: string;
|
|
69
84
|
readonly deviceId: string;
|
|
@@ -71,8 +86,13 @@ export interface TenantBoundTaskAttempts {
|
|
|
71
86
|
recordStatus(input: {
|
|
72
87
|
readonly taskId: string;
|
|
73
88
|
readonly status: TaskAttemptStatus;
|
|
89
|
+
readonly agentRef?: TaskAttempt['agentRef'];
|
|
90
|
+
readonly terminalCause?: TaskAttempt['terminalCause'];
|
|
74
91
|
}): Promise<TaskAttempt | undefined>;
|
|
75
92
|
}
|
|
93
|
+
export interface TenantBoundTaskCancellations {
|
|
94
|
+
request(input: TaskCancellationRequest): Promise<TaskCancellationMutation | undefined>;
|
|
95
|
+
}
|
|
76
96
|
export interface TenantBoundDedup {
|
|
77
97
|
checkAndRecord(deviceId: string, envelopeId: string): Promise<boolean>;
|
|
78
98
|
}
|
|
@@ -86,6 +106,13 @@ export interface TenantBoundReceipts {
|
|
|
86
106
|
}>;
|
|
87
107
|
get(key: string): Promise<RequestReceipt | undefined>;
|
|
88
108
|
}
|
|
109
|
+
export interface TenantBoundAgentEgress {
|
|
110
|
+
record(input: Omit<AgentEgressRecord, 'tenantId' | 'recordedAt'>): Promise<{
|
|
111
|
+
readonly record: AgentEgressRecord;
|
|
112
|
+
readonly created: boolean;
|
|
113
|
+
}>;
|
|
114
|
+
get(deviceId: string, eventId: string): Promise<AgentEgressRecord | undefined>;
|
|
115
|
+
}
|
|
89
116
|
export interface TenantBoundBlobs {
|
|
90
117
|
createUpload(reservation: StorageReservation): Promise<{
|
|
91
118
|
readonly blobId: string;
|
|
@@ -113,8 +140,10 @@ export interface TenantStores {
|
|
|
113
140
|
readonly approvals: TenantBoundApprovalTimeline;
|
|
114
141
|
readonly devices: TenantBoundDevices;
|
|
115
142
|
readonly tasks: TenantBoundTaskAttempts;
|
|
143
|
+
readonly cancellations: TenantBoundTaskCancellations;
|
|
116
144
|
readonly dedup: TenantBoundDedup;
|
|
117
145
|
readonly receipts: TenantBoundReceipts;
|
|
146
|
+
readonly egress: TenantBoundAgentEgress;
|
|
118
147
|
readonly blobs: TenantBoundBlobs;
|
|
119
148
|
readonly quota: TenantBoundQuota;
|
|
120
149
|
readonly rateLimiter: TenantBoundRateLimiter;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type BlobRef } from '@byok-sdk/protocol';
|
|
1
|
+
import { type AgentRef, type BlobRef, type TerminalInferenceUsage } from '@byok-sdk/protocol';
|
|
2
2
|
import type { RequestReceipt } from './stores/ports';
|
|
3
3
|
/**
|
|
4
4
|
* The typed terminal read model — the hosted counterpart of the embedded
|
|
@@ -10,6 +10,8 @@ import type { RequestReceipt } from './stores/ports';
|
|
|
10
10
|
export interface TerminalResult {
|
|
11
11
|
readonly taskId: string;
|
|
12
12
|
readonly state: 'complete' | 'failed' | 'cancelled';
|
|
13
|
+
/** Exact Agent identity echoed by the winning terminal, when Agent-bound. */
|
|
14
|
+
readonly agentRef?: AgentRef;
|
|
13
15
|
readonly summary?: string;
|
|
14
16
|
readonly sessionRef?: string;
|
|
15
17
|
readonly artifactRefs?: readonly BlobRef[];
|
|
@@ -20,7 +22,15 @@ export interface TerminalResult {
|
|
|
20
22
|
* `resultDocument` extractor.
|
|
21
23
|
*/
|
|
22
24
|
readonly document?: unknown;
|
|
25
|
+
/**
|
|
26
|
+
* Device/runtime terminal observation copied from the canonical winning
|
|
27
|
+
* receipt. It is telemetry only — never cloud storage usage, billing, quota
|
|
28
|
+
* or entitlement authority.
|
|
29
|
+
*/
|
|
30
|
+
readonly usage?: TerminalInferenceUsage;
|
|
23
31
|
readonly reason?: string;
|
|
32
|
+
/** Terminal cause projection; currently the protocol's terminal reason. */
|
|
33
|
+
readonly terminalCause?: string;
|
|
24
34
|
readonly retryable?: boolean;
|
|
25
35
|
/** When the receipt store wrote the terminal fact — the first one, by its own first-write-wins rule. */
|
|
26
36
|
readonly recordedAt: string;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@byok-sdk/cloud",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.1",
|
|
4
4
|
"description": "BYOK SDK hosted device surface: stateless frozen-v1 HTTP handlers over tenant-first @byok-sdk/core ports",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -44,8 +44,8 @@
|
|
|
44
44
|
"clean": "rm -rf dist"
|
|
45
45
|
},
|
|
46
46
|
"dependencies": {
|
|
47
|
-
"@byok-sdk/core": "0.
|
|
48
|
-
"@byok-sdk/protocol": "0.
|
|
47
|
+
"@byok-sdk/core": "0.6.1",
|
|
48
|
+
"@byok-sdk/protocol": "0.6.1",
|
|
49
49
|
"hono": "^4.12.30",
|
|
50
50
|
"zod": "^4.4.3"
|
|
51
51
|
}
|