@byok-sdk/cloud 0.4.2 → 0.6.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.
@@ -7,7 +7,7 @@
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;
@@ -15,5 +15,6 @@ export declare class InMemoryDeviceDirectory implements DeviceDirectory {
15
15
  get(tenant: TenantId, deviceId: string): Promise<DeviceRecord | undefined>;
16
16
  revoke(tenant: TenantId, deviceId: string): Promise<void>;
17
17
  list(tenant: TenantId): Promise<readonly DeviceRecord[]>;
18
+ readiness(tenant: TenantId, presence: PresenceStore): Promise<TenantReadiness>;
18
19
  resolveByDeviceId(deviceId: string): Promise<DeviceRecord | undefined>;
19
20
  }
@@ -19,6 +19,7 @@ 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';
24
25
  /**
@@ -34,4 +35,4 @@ export interface InMemoryCloudComposition {
34
35
  readonly stores: CloudStores;
35
36
  readonly blobContentProxy: BlobContentProxy;
36
37
  }
37
- export declare function createInMemoryCloudStores(clock: Clock, crypto: CloudCrypto, objects: ObjectStore): InMemoryCloudComposition;
38
+ export declare function createInMemoryCloudStores(clock: Clock, crypto: CloudCrypto, objects: ObjectStore, mailbox: import('@byok-sdk/core').MailboxStore): InMemoryCloudComposition;
@@ -19,12 +19,13 @@ import { type Clock, type TenantId } from '@byok-sdk/core';
19
19
  import type { 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
24
  taskId: string;
25
25
  deviceId: string;
26
26
  }): Promise<TaskAttempt>;
27
27
  get(tenant: TenantId, taskId: string): Promise<TaskAttempt | undefined>;
28
+ getMany(tenant: TenantId, taskIds: readonly string[]): Promise<readonly TaskAttempt[]>;
28
29
  claim(tenant: TenantId, input: {
29
30
  taskId: string;
30
31
  deviceId: string;
@@ -34,3 +35,12 @@ export declare class InMemoryTaskAttemptStore implements TaskAttemptStore {
34
35
  status: TaskAttemptStatus;
35
36
  }): Promise<TaskAttempt | undefined>;
36
37
  }
38
+ /** Shared mutable state for the task and cancellation reference ports. */
39
+ export declare class InMemoryTaskAttemptState {
40
+ #private;
41
+ readonly attempts: Map<string, TaskAttempt>;
42
+ constructor(clock: Clock);
43
+ now(): string;
44
+ /** Serialize every state-changing operation for one tenant/task key. */
45
+ mutate<T>(key: string, operation: () => T | Promise<T>): Promise<T>;
46
+ }
@@ -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
+ }
@@ -34,7 +34,7 @@
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
38
  import type { ActivityStore } from '../activity';
39
39
  import type { ApprovalTimelineStore } from '../approval-timeline';
40
40
  export interface DeviceRecord {
@@ -65,6 +65,8 @@ export interface DeviceDirectory {
65
65
  get(tenant: TenantId, deviceId: string): Promise<DeviceRecord | undefined>;
66
66
  revoke(tenant: TenantId, deviceId: string): Promise<void>;
67
67
  list(tenant: TenantId): Promise<readonly DeviceRecord[]>;
68
+ /** Set-wise tenant observation; revoked devices never contribute presence. */
69
+ readiness(tenant: TenantId, presence: PresenceStore): Promise<TenantReadiness>;
68
70
  /** Pre-tenant. Two callers only: `POST /byok/challenge` and `POST /byok/token`. Never exposed through the tenant facade. */
69
71
  resolveByDeviceId(deviceId: string): Promise<DeviceRecord | undefined>;
70
72
  }
@@ -115,7 +117,7 @@ export interface InboundDedupStore {
115
117
  */
116
118
  checkAndRecord(tenant: TenantId, deviceId: string, envelopeId: string): Promise<boolean>;
117
119
  }
118
- export declare const TASK_ATTEMPT_STATUSES: readonly ['offered', 'claimed', 'running', 'complete', 'failed', 'cancelled'];
120
+ export declare const TASK_ATTEMPT_STATUSES: readonly ['offered', 'claimed', 'running', 'cancel_requested', 'complete', 'failed', 'cancelled'];
119
121
  export type TaskAttemptStatus = (typeof TASK_ATTEMPT_STATUSES)[number];
120
122
  export interface TaskAttempt {
121
123
  readonly tenantId: TenantId;
@@ -125,6 +127,11 @@ export interface TaskAttempt {
125
127
  /** Set by `task.claim`, and only by the first one. Until then the task has no owner and the gate lets any of this tenant's devices through, matching the reference server. */
126
128
  readonly ownerDeviceId?: string;
127
129
  readonly status: TaskAttemptStatus;
130
+ /** Durable host cancellation authority. Its presence outranks later device terminal receipts. */
131
+ readonly cancellation?: {
132
+ readonly requestedAt: string;
133
+ readonly reason?: string;
134
+ };
128
135
  readonly updatedAt: string;
129
136
  }
130
137
  export interface TaskAttemptStore {
@@ -134,6 +141,8 @@ export interface TaskAttemptStore {
134
141
  readonly deviceId: string;
135
142
  }): Promise<TaskAttempt>;
136
143
  get(tenant: TenantId, taskId: string): Promise<TaskAttempt | undefined>;
144
+ /** Batch lookup used by mailbox projection; implementations must not turn one poll into N queries. */
145
+ getMany(tenant: TenantId, taskIds: readonly string[]): Promise<readonly TaskAttempt[]>;
137
146
  /** 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
147
  claim(tenant: TenantId, input: {
139
148
  readonly taskId: string;
@@ -145,6 +154,24 @@ export interface TaskAttemptStore {
145
154
  readonly status: TaskAttemptStatus;
146
155
  }): Promise<TaskAttempt | undefined>;
147
156
  }
157
+ export interface TaskCancellationRequest {
158
+ readonly taskId: string;
159
+ readonly proposedMessageId: string;
160
+ readonly reason?: string;
161
+ readonly materialize: (seq: number, messageId: string) => MailboxBody | Promise<MailboxBody>;
162
+ }
163
+ export interface TaskCancellationMutation {
164
+ readonly attempt: TaskAttempt;
165
+ /** Absent only when the task was already complete or failed before cancellation. */
166
+ readonly message?: MailboxMessage;
167
+ }
168
+ /**
169
+ * One atomic authority for the cancellation tombstone plus its durable device
170
+ * delivery. Implementations must commit both or neither.
171
+ */
172
+ export interface TaskCancellationStore {
173
+ request(tenant: TenantId, input: TaskCancellationRequest): Promise<TaskCancellationMutation | undefined>;
174
+ }
148
175
  export interface RequestReceipt {
149
176
  readonly tenantId: TenantId;
150
177
  readonly key: string;
@@ -288,11 +315,12 @@ export interface CloudStores {
288
315
  readonly nonces: NonceStore;
289
316
  readonly dedup: InboundDedupStore;
290
317
  readonly tasks: TaskAttemptStore;
318
+ readonly cancellations: TaskCancellationStore;
291
319
  readonly receipts: RequestReceiptStore;
292
320
  readonly proofReceipts: ProofRequestReceiptStore;
293
321
  readonly blobs: CloudBlobStore;
294
322
  readonly rateLimiter: InboundRateLimiter;
295
323
  }
296
324
  /** 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'];
325
+ export declare const CLOUD_STORE_NAMES: readonly ['activity', 'approvals', 'devices', 'pairingCodes', 'nonces', 'dedup', 'tasks', 'cancellations', 'receipts', 'proofReceipts', 'blobs', 'rateLimiter'];
298
326
  export type CloudStoreName = (typeof CLOUD_STORE_NAMES)[number];
@@ -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, 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,6 +57,7 @@ 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
+ readiness(): Promise<TenantReadiness>;
60
61
  }
61
62
  export interface TenantBoundTaskAttempts {
62
63
  open(input: {
@@ -64,6 +65,7 @@ export interface TenantBoundTaskAttempts {
64
65
  readonly deviceId: string;
65
66
  }): Promise<TaskAttempt>;
66
67
  get(taskId: string): Promise<TaskAttempt | undefined>;
68
+ getMany(taskIds: readonly string[]): Promise<readonly TaskAttempt[]>;
67
69
  claim(input: {
68
70
  readonly taskId: string;
69
71
  readonly deviceId: string;
@@ -73,6 +75,9 @@ export interface TenantBoundTaskAttempts {
73
75
  readonly status: TaskAttemptStatus;
74
76
  }): Promise<TaskAttempt | undefined>;
75
77
  }
78
+ export interface TenantBoundTaskCancellations {
79
+ request(input: TaskCancellationRequest): Promise<TaskCancellationMutation | undefined>;
80
+ }
76
81
  export interface TenantBoundDedup {
77
82
  checkAndRecord(deviceId: string, envelopeId: string): Promise<boolean>;
78
83
  }
@@ -113,6 +118,7 @@ export interface TenantStores {
113
118
  readonly approvals: TenantBoundApprovalTimeline;
114
119
  readonly devices: TenantBoundDevices;
115
120
  readonly tasks: TenantBoundTaskAttempts;
121
+ readonly cancellations: TenantBoundTaskCancellations;
116
122
  readonly dedup: TenantBoundDedup;
117
123
  readonly receipts: TenantBoundReceipts;
118
124
  readonly blobs: TenantBoundBlobs;
@@ -1,4 +1,4 @@
1
- import { type BlobRef } from '@byok-sdk/protocol';
1
+ import { 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
@@ -20,6 +20,12 @@ export interface TerminalResult {
20
20
  * `resultDocument` extractor.
21
21
  */
22
22
  readonly document?: unknown;
23
+ /**
24
+ * Device/runtime terminal observation copied from the canonical winning
25
+ * receipt. It is telemetry only — never cloud storage usage, billing, quota
26
+ * or entitlement authority.
27
+ */
28
+ readonly usage?: TerminalInferenceUsage;
23
29
  readonly reason?: string;
24
30
  readonly retryable?: boolean;
25
31
  /** When the receipt store wrote the terminal fact — the first one, by its own first-write-wins rule. */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@byok-sdk/cloud",
3
- "version": "0.4.2",
3
+ "version": "0.6.0",
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.4.2",
48
- "@byok-sdk/protocol": "0.4.2",
47
+ "@byok-sdk/core": "0.6.0",
48
+ "@byok-sdk/protocol": "0.6.0",
49
49
  "hono": "^4.12.30",
50
50
  "zod": "^4.4.3"
51
51
  }