@byok-sdk/cloud 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/activity.d.ts +13 -0
- package/dist/agent-home-projections.d.ts +2 -2
- package/dist/agent-reliability.d.ts +9 -0
- package/dist/approval-control.d.ts +75 -0
- package/dist/approval-timeline.d.ts +60 -14
- package/dist/auth/bearer.d.ts +17 -0
- package/dist/cloud.d.ts +116 -5
- package/dist/composition/in-memory.d.ts +4 -0
- package/dist/coordination.d.ts +8 -0
- package/dist/errors.d.ts +9 -0
- package/dist/handlers/events.d.ts +7 -1
- package/dist/handlers/messages.d.ts +7 -0
- package/dist/inbound.d.ts +42 -4
- package/dist/index.d.ts +11 -7
- package/dist/index.js +747 -145
- package/dist/index.js.map +1 -1
- package/dist/steer-control.d.ts +81 -0
- package/dist/stores/in-memory/agent-egress.d.ts +1 -1
- package/dist/stores/in-memory/approval-timeline.d.ts +2 -1
- package/dist/stores/in-memory/dedup.d.ts +8 -5
- package/dist/stores/in-memory/task-attempts.d.ts +10 -2
- package/dist/stores/ports.d.ts +106 -9
- package/dist/tenant-stores.d.ts +8 -1
- package/package.json +3 -3
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GAP-2: why a `cloud.steerTask` call was refused, and the one input that
|
|
3
|
+
* decides it.
|
|
4
|
+
*
|
|
5
|
+
* The reference server (`ConnectionHub.steerTask`, `packages/server/src/hub.ts`)
|
|
6
|
+
* reaches this decision from `TaskSnapshot.claimedRuntimeCapabilities` — the
|
|
7
|
+
* capability block the CLAIMING adapter reported for itself on its own
|
|
8
|
+
* `task.claim`, frozen at the `Offered -> Claimed` transition. Cloud keeps the
|
|
9
|
+
* same single source: {@link TaskAttempt.claimedRuntimeCapabilities}, written by
|
|
10
|
+
* the claim that wins the ownership CAS (`stores/ports.ts`) and never again.
|
|
11
|
+
*
|
|
12
|
+
* The gap this closes is not cosmetic: only pi's adapter implements steering,
|
|
13
|
+
* and Claude's and Codex's THROW on receiving `task.steer`, which stalls that
|
|
14
|
+
* device's redelivery cursor at that seq forever. So the refusal has to happen
|
|
15
|
+
* before an envelope exists, from a fact that shares a lifecycle with the
|
|
16
|
+
* task-to-runtime binding.
|
|
17
|
+
*
|
|
18
|
+
* Fail-closed on unknown, deliberately: `steer_unsupported_runtime` covers both
|
|
19
|
+
* "the claiming adapter reported `steer: false`" and "this attempt carries no
|
|
20
|
+
* capability snapshot at all" (a daemon whose claim predates the field, an
|
|
21
|
+
* attempt claimed before migration `0018`). Refusing an unknown is a
|
|
22
|
+
* recoverable, operator-visible error; guessing "supported" reintroduces the
|
|
23
|
+
* permanent cursor stall this gate exists to prevent. There is deliberately NO
|
|
24
|
+
* runtime-id allow-list here either — a table mapping `pi -> steerable` would be
|
|
25
|
+
* a second capability authority in the coordination plane, drifting the moment a
|
|
26
|
+
* runtime gains or loses the feature, and the daemon already reports the truth.
|
|
27
|
+
*
|
|
28
|
+
* SINGLE SOURCE, deliberately: the gate reads the claim snapshot and NOTHING
|
|
29
|
+
* from the connection layer. Cloud's connection-level equivalent is
|
|
30
|
+
* `DeviceRecord.capabilities`, written from a bearer-authenticated `conn.hello`
|
|
31
|
+
* (`inbound.ts`) — discovery data describing a device BUILD, not the adapter
|
|
32
|
+
* that took this task. A device can reconnect later with a different adapter
|
|
33
|
+
* set; a task already running must keep being judged against what was true when
|
|
34
|
+
* it was claimed. Same pin the reference server holds in
|
|
35
|
+
* `packages/server/src/__tests__/steer-runtime-capability-gate.test.ts`.
|
|
36
|
+
*/
|
|
37
|
+
import type { RuntimeId } from '@byok-sdk/protocol';
|
|
38
|
+
import type { TaskAttemptStatus } from './stores/ports';
|
|
39
|
+
/**
|
|
40
|
+
* Stable strings a caller switches on (an operator UI, an HTTP surface mapping
|
|
41
|
+
* this to a status code) rather than matching error text. Byte-identical to the
|
|
42
|
+
* reference server's `SteerRejectionCode` (`packages/server/src/hub.ts`), so a
|
|
43
|
+
* host that moves from the embedded server to the hosted kernel keeps its own
|
|
44
|
+
* mapping unchanged.
|
|
45
|
+
*
|
|
46
|
+
* - `task_terminal` — the attempt already reached `complete`/`failed`/
|
|
47
|
+
* `cancelled`. Checked FIRST, so a terminal attempt that is also (obviously)
|
|
48
|
+
* not running reports the more specific truth, and a steer racing a terminal
|
|
49
|
+
* transition always resolves terminal-first.
|
|
50
|
+
* - `task_not_running` — the attempt exists and is live, but is `offered`/
|
|
51
|
+
* `claimed`/`cancel_requested`; there is no running turn to steer yet.
|
|
52
|
+
* - `steer_unsupported_runtime` — the runtime that CLAIMED this attempt cannot
|
|
53
|
+
* be steered, per the claim-time capability snapshot. A MISSING snapshot
|
|
54
|
+
* rejects under this same code: unknown is not supported.
|
|
55
|
+
*/
|
|
56
|
+
export type SteerRejectionCode = 'steer_unsupported_runtime' | 'task_not_running' | 'task_terminal';
|
|
57
|
+
/**
|
|
58
|
+
* Thrown by `ByokCloud.steerTask` instead of a generic `Error`, so a caller can
|
|
59
|
+
* tell WHY a steer was refused without matching on message text — the same
|
|
60
|
+
* typed-error idiom as `StaleApprovalError` (`approval-control.ts`).
|
|
61
|
+
*
|
|
62
|
+
* A distinct class rather than a `CloudErrorCode`, for the same reason as
|
|
63
|
+
* `StaleApprovalError`: it carries the state and the runtime the caller needs to
|
|
64
|
+
* explain the refusal, and a code alone cannot.
|
|
65
|
+
*
|
|
66
|
+
* Thrown before any mailbox row is allocated — a refused steer is a NON-EVENT,
|
|
67
|
+
* exactly like a refused approval.
|
|
68
|
+
*/
|
|
69
|
+
export declare class SteerRejectedError extends Error {
|
|
70
|
+
readonly taskId: string;
|
|
71
|
+
readonly code: SteerRejectionCode;
|
|
72
|
+
/** The attempt's status at the moment the steer was refused. */
|
|
73
|
+
readonly status: TaskAttemptStatus;
|
|
74
|
+
/** {@link TaskAttempt.claimedRuntime} — `undefined` when nothing was ever recorded, which is itself a reason `steer_unsupported_runtime` can fire. */
|
|
75
|
+
readonly runtime: RuntimeId | undefined;
|
|
76
|
+
constructor(taskId: string, code: SteerRejectionCode,
|
|
77
|
+
/** The attempt's status at the moment the steer was refused. */
|
|
78
|
+
status: TaskAttemptStatus,
|
|
79
|
+
/** {@link TaskAttempt.claimedRuntime} — `undefined` when nothing was ever recorded, which is itself a reason `steer_unsupported_runtime` can fire. */
|
|
80
|
+
runtime: RuntimeId | undefined);
|
|
81
|
+
}
|
|
@@ -8,5 +8,5 @@ export declare class InMemoryAgentEgressStore implements AgentEgressStore {
|
|
|
8
8
|
readonly record: AgentEgressRecord;
|
|
9
9
|
readonly created: boolean;
|
|
10
10
|
}>;
|
|
11
|
-
get(tenant: TenantId, deviceId: string, eventId: string): Promise<AgentEgressRecord | undefined>;
|
|
11
|
+
get(tenant: TenantId, deviceId: string, agentRef: AgentEgressRecord['payload']['agentRef'], eventId: string): Promise<AgentEgressRecord | undefined>;
|
|
12
12
|
}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import type { Clock, TenantId } from '@byok-sdk/core';
|
|
2
|
-
import { type ApprovalTimelineAppendInput, type ApprovalTimelineStore, type ApprovalTimelineTail } from '../../approval-timeline';
|
|
2
|
+
import { type ApprovalTimelineAppendInput, type ApprovalTimelineResolvePendingInput, type ApprovalTimelineResolvePendingResult, type ApprovalTimelineStore, type ApprovalTimelineTail } from '../../approval-timeline';
|
|
3
3
|
export declare class InMemoryApprovalTimelineStore implements ApprovalTimelineStore {
|
|
4
4
|
#private;
|
|
5
5
|
private readonly clock;
|
|
6
6
|
constructor(clock: Clock);
|
|
7
7
|
append(tenant: TenantId, input: ApprovalTimelineAppendInput): Promise<ApprovalTimelineTail>;
|
|
8
|
+
resolvePending(tenant: TenantId, input: ApprovalTimelineResolvePendingInput): Promise<ApprovalTimelineResolvePendingResult>;
|
|
8
9
|
read(tenant: TenantId, taskId: string): Promise<ApprovalTimelineTail | undefined>;
|
|
9
10
|
}
|
|
@@ -1,17 +1,20 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* In-memory {@link InboundDedupStore} (N3).
|
|
3
3
|
*
|
|
4
|
-
* A bounded ring per (tenant, device)
|
|
5
|
-
* at-least-once (§9), so
|
|
6
|
-
*
|
|
7
|
-
*
|
|
4
|
+
* A bounded ring per physical (tenant, device) or strict (tenant, device,
|
|
5
|
+
* AgentRef) fact, not an unbounded set: the wire is at-least-once (§9), so
|
|
6
|
+
* this makes processing at-most-once without letting a chatty device or Agent
|
|
7
|
+
* grow memory without limit. Check-and-record is one call, so a composition
|
|
8
|
+
* cannot accidentally split it into a racy read-then-write.
|
|
8
9
|
*/
|
|
9
10
|
import { type TenantId } from '@byok-sdk/core';
|
|
11
|
+
import type { AgentRef } from '@byok-sdk/protocol';
|
|
10
12
|
import type { InboundDedupStore } from '../ports';
|
|
11
|
-
/** Ids retained per
|
|
13
|
+
/** Ids retained per physical or Agent-bound key. Same depth as the reference server's ring. */
|
|
12
14
|
export declare const DEDUP_RING_CAPACITY = 1024;
|
|
13
15
|
export declare class InMemoryInboundDedupStore implements InboundDedupStore {
|
|
14
16
|
#private;
|
|
15
17
|
constructor(capacity?: number);
|
|
16
18
|
checkAndRecord(tenant: TenantId, deviceId: string, envelopeId: string): Promise<boolean>;
|
|
19
|
+
checkAndRecordAgent(tenant: TenantId, deviceId: string, agentRef: AgentRef, envelopeId: string): Promise<boolean>;
|
|
17
20
|
}
|
|
@@ -16,7 +16,11 @@
|
|
|
16
16
|
* unfalsifiable.
|
|
17
17
|
*/
|
|
18
18
|
import { type Clock, type TenantId } from '@byok-sdk/core';
|
|
19
|
-
import
|
|
19
|
+
import type { RuntimeCapabilities, RuntimeId } from '@byok-sdk/protocol';
|
|
20
|
+
import { type AgentMessageAdmission, type AgentRef, type TaskAttempt, type TaskAttemptListQuery, type TaskAttemptPage, type TaskAttemptStatus, type TaskAttemptStore } from '../ports';
|
|
21
|
+
interface StoredAgentMessageAdmission extends AgentMessageAdmission {
|
|
22
|
+
readonly deviceId: string;
|
|
23
|
+
}
|
|
20
24
|
export declare class InMemoryTaskAttemptStore implements TaskAttemptStore {
|
|
21
25
|
#private;
|
|
22
26
|
constructor(clock: Clock, state?: InMemoryTaskAttemptState);
|
|
@@ -54,9 +58,12 @@ export declare class InMemoryTaskAttemptStore implements TaskAttemptStore {
|
|
|
54
58
|
}): Promise<AgentMessageAdmission | undefined>;
|
|
55
59
|
get(tenant: TenantId, taskId: string): Promise<TaskAttempt | undefined>;
|
|
56
60
|
getMany(tenant: TenantId, taskIds: readonly string[]): Promise<readonly TaskAttempt[]>;
|
|
61
|
+
list(tenant: TenantId, query: TaskAttemptListQuery): Promise<TaskAttemptPage>;
|
|
57
62
|
claim(tenant: TenantId, input: {
|
|
58
63
|
taskId: string;
|
|
59
64
|
deviceId: string;
|
|
65
|
+
runtime?: RuntimeId;
|
|
66
|
+
capabilities?: RuntimeCapabilities;
|
|
60
67
|
}): Promise<TaskAttempt | undefined>;
|
|
61
68
|
recordStatus(tenant: TenantId, input: {
|
|
62
69
|
readonly taskId: string;
|
|
@@ -69,9 +76,10 @@ export declare class InMemoryTaskAttemptStore implements TaskAttemptStore {
|
|
|
69
76
|
export declare class InMemoryTaskAttemptState {
|
|
70
77
|
#private;
|
|
71
78
|
readonly attempts: Map<string, TaskAttempt>;
|
|
72
|
-
readonly messageAdmissions: Map<string,
|
|
79
|
+
readonly messageAdmissions: Map<string, StoredAgentMessageAdmission>;
|
|
73
80
|
constructor(clock: Clock);
|
|
74
81
|
now(): string;
|
|
75
82
|
/** Serialize every state-changing operation for one tenant/task key. */
|
|
76
83
|
mutate<T>(key: string, operation: () => T | Promise<T>): Promise<T>;
|
|
77
84
|
}
|
|
85
|
+
export {};
|
package/dist/stores/ports.d.ts
CHANGED
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
* bytes (see the blobs section below).
|
|
34
34
|
*/
|
|
35
35
|
import type { MailboxBody, MailboxMessage, PresenceStore, StorageReservation, TenantId, TenantReadiness } from '@byok-sdk/core';
|
|
36
|
-
import type { AgentEgressReliablePayload, AgentRef } from '@byok-sdk/protocol';
|
|
36
|
+
import type { AgentEgressReliablePayload, AgentRef, RuntimeCapabilities, RuntimeId } from '@byok-sdk/protocol';
|
|
37
37
|
export type { AgentEgressReliablePayload, AgentRef } from '@byok-sdk/protocol';
|
|
38
38
|
import type { ActivityStore } from '../activity';
|
|
39
39
|
import type { ApprovalTimelineStore } from '../approval-timeline';
|
|
@@ -184,12 +184,17 @@ export interface NonceStore {
|
|
|
184
184
|
}
|
|
185
185
|
export interface InboundDedupStore {
|
|
186
186
|
/**
|
|
187
|
-
*
|
|
188
|
-
*
|
|
189
|
-
*
|
|
190
|
-
* unbounded set.
|
|
187
|
+
* Device-physical envelope ledger (`conn.hello` and facts with no Agent
|
|
188
|
+
* binding). `true` when `envelopeId` was already seen for this (tenant,
|
|
189
|
+
* device); otherwise records it and returns `false`.
|
|
191
190
|
*/
|
|
192
191
|
checkAndRecord(tenant: TenantId, deviceId: string, envelopeId: string): Promise<boolean>;
|
|
192
|
+
/**
|
|
193
|
+
* Agent-bound envelope ledger. The AgentRef is required: a same-device
|
|
194
|
+
* second Agent must never resolve or suppress this Agent's completed fact.
|
|
195
|
+
* Retention remains bounded per exact (tenant, device, AgentRef).
|
|
196
|
+
*/
|
|
197
|
+
checkAndRecordAgent(tenant: TenantId, deviceId: string, agentRef: AgentRef, envelopeId: string): Promise<boolean>;
|
|
193
198
|
}
|
|
194
199
|
export declare const TASK_ATTEMPT_STATUSES: readonly ['offered', 'claimed', 'running', 'cancel_requested', 'complete', 'failed', 'cancelled'];
|
|
195
200
|
export type TaskAttemptStatus = (typeof TASK_ATTEMPT_STATUSES)[number];
|
|
@@ -209,6 +214,40 @@ export interface TaskAttempt {
|
|
|
209
214
|
/** 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. */
|
|
210
215
|
readonly ownerDeviceId?: string;
|
|
211
216
|
readonly status: TaskAttemptStatus;
|
|
217
|
+
/**
|
|
218
|
+
* The runtime the CLAIMING daemon reported for itself on its own
|
|
219
|
+
* `task.claim` (`TaskClaimPayload.runtime`), snapshotted at the
|
|
220
|
+
* `offered -> claimed` transition and never written again — a redelivered or
|
|
221
|
+
* losing claim leaves it exactly as the winning claim left it.
|
|
222
|
+
*
|
|
223
|
+
* ADR-028 forbids cloud holding EXECUTION state (a live `running`/`thinking`
|
|
224
|
+
* phase, a turn, a PID, the current tool call) — quantities that keep
|
|
225
|
+
* changing while an attempt runs. This is not one: it is written once, at
|
|
226
|
+
* the moment ownership is decided, and is an attribution fact about the
|
|
227
|
+
* attempt. Byte-for-byte the same semantics as the reference server's
|
|
228
|
+
* `TaskSnapshot.claimedRuntime` (`packages/server/src/types.ts`).
|
|
229
|
+
*
|
|
230
|
+
* Absent for a daemon whose `task.claim` carried no `runtime` at all.
|
|
231
|
+
*/
|
|
232
|
+
readonly claimedRuntime?: RuntimeId;
|
|
233
|
+
/**
|
|
234
|
+
* The capability block the CLAIMING adapter reported for ITSELF on that same
|
|
235
|
+
* `task.claim` (`TaskClaimPayload.capabilities`), snapshotted under exactly
|
|
236
|
+
* the same write-once rule as {@link TaskAttempt.claimedRuntime}.
|
|
237
|
+
*
|
|
238
|
+
* This is the ONLY input to the steer gate (`steer-control.ts`,
|
|
239
|
+
* `ByokCloud.steerTask`). The connection-level snapshot cloud keeps —
|
|
240
|
+
* `DeviceRecord.capabilities`, written from `conn.hello`
|
|
241
|
+
* (`inbound.ts`) — describes a device BUILD and never feeds it: a device can
|
|
242
|
+
* reconnect later with a different adapter set, while a task that is already
|
|
243
|
+
* running must keep being judged against what was true when it was claimed.
|
|
244
|
+
* Same single-source rule the reference server pins
|
|
245
|
+
* (`packages/server/src/__tests__/steer-runtime-capability-gate.test.ts`).
|
|
246
|
+
*
|
|
247
|
+
* Absent for a daemon that predates the field, which the gate reads as
|
|
248
|
+
* "unknown" and refuses — unknown is not supported.
|
|
249
|
+
*/
|
|
250
|
+
readonly claimedRuntimeCapabilities?: RuntimeCapabilities;
|
|
212
251
|
/** Runtime-reported terminal cause from the first winning terminal. */
|
|
213
252
|
readonly terminalCause?: string;
|
|
214
253
|
/** Durable host cancellation authority. Its presence outranks later device terminal receipts. */
|
|
@@ -218,6 +257,43 @@ export interface TaskAttempt {
|
|
|
218
257
|
};
|
|
219
258
|
readonly updatedAt: string;
|
|
220
259
|
}
|
|
260
|
+
/**
|
|
261
|
+
* One bounded page request over a tenant's task attempts.
|
|
262
|
+
*
|
|
263
|
+
* Keyset-paged by `taskId` ASCENDING, deliberately, and NOT chronologically: an
|
|
264
|
+
* attempt carries no monotonic sequence, and `updatedAt` moves under an
|
|
265
|
+
* in-flight page (a status transition would re-order rows mid-walk and let a
|
|
266
|
+
* caller skip or repeat one). `taskId` is `task_<uuid>` — stable, unique per
|
|
267
|
+
* tenant, and never rewritten — so it is the only column that gives a walk the
|
|
268
|
+
* exactly-once property. The order is therefore arbitrary but TOTAL, which is
|
|
269
|
+
* what a paged read model needs; a caller that wants recency sorts a page
|
|
270
|
+
* itself.
|
|
271
|
+
*
|
|
272
|
+
* Adding a sequence column to get chronological order was rejected: it is a
|
|
273
|
+
* migration across every composition to serve a read-model nicety, and a second
|
|
274
|
+
* ordering authority to keep consistent with the row it decorates.
|
|
275
|
+
*/
|
|
276
|
+
export interface TaskAttemptListQuery {
|
|
277
|
+
/** Maximum attempts in the page. Required and fail-closed: a non-positive or non-integer limit is rejected, never silently defaulted or clamped. */
|
|
278
|
+
readonly limit: number;
|
|
279
|
+
/**
|
|
280
|
+
* The `nextCursor` from the previous page — opaque to the caller, and an
|
|
281
|
+
* EXCLUSIVE lower bound on `taskId`. Absent starts at the beginning. A cursor
|
|
282
|
+
* naming a task that has since been deleted still resolves: it is a bound,
|
|
283
|
+
* not a lookup.
|
|
284
|
+
*/
|
|
285
|
+
readonly cursor?: string;
|
|
286
|
+
}
|
|
287
|
+
export interface TaskAttemptPage {
|
|
288
|
+
readonly attempts: readonly TaskAttempt[];
|
|
289
|
+
/**
|
|
290
|
+
* Pass as the next request's `cursor`. ABSENT means this page is the end of
|
|
291
|
+
* the walk — not "start over" — so a caller stops on absence rather than on
|
|
292
|
+
* an empty page, and a page that exactly fills `limit` with nothing after it
|
|
293
|
+
* still terminates.
|
|
294
|
+
*/
|
|
295
|
+
readonly nextCursor?: string;
|
|
296
|
+
}
|
|
221
297
|
export interface TaskAttemptStore {
|
|
222
298
|
/** Called when an offer is enqueued: records the pending attempt with no owner yet. */
|
|
223
299
|
open(tenant: TenantId, input: {
|
|
@@ -240,8 +316,10 @@ export interface TaskAttemptStore {
|
|
|
240
316
|
/**
|
|
241
317
|
* Atomically binds one message payload to a live task before an external
|
|
242
318
|
* consumer can run. `pending` is an existing exact reservation whose terminal
|
|
243
|
-
* receipt has not been recorded yet; callers
|
|
244
|
-
*
|
|
319
|
+
* receipt has not been recorded yet; callers retry the same product consumer
|
|
320
|
+
* contract so its own durable idempotency can reconcile a commit whose local
|
|
321
|
+
* finalization failed. Exact pending lookup precedes the live-task gate, while
|
|
322
|
+
* every new reservation remains lifecycle-gated.
|
|
245
323
|
*/
|
|
246
324
|
reserveAgentMessage(tenant: TenantId, input: {
|
|
247
325
|
readonly taskId: string;
|
|
@@ -267,10 +345,28 @@ export interface TaskAttemptStore {
|
|
|
267
345
|
get(tenant: TenantId, taskId: string): Promise<TaskAttempt | undefined>;
|
|
268
346
|
/** Batch lookup used by mailbox projection; implementations must not turn one poll into N queries. */
|
|
269
347
|
getMany(tenant: TenantId, taskIds: readonly string[]): Promise<readonly TaskAttempt[]>;
|
|
270
|
-
/**
|
|
348
|
+
/**
|
|
349
|
+
* One bounded page of this tenant's attempts, keyset-paged by `taskId` — see
|
|
350
|
+
* {@link TaskAttemptListQuery} for why that key and not a timestamp.
|
|
351
|
+
* Implementations must read one page in one query (`taskId > cursor ORDER BY
|
|
352
|
+
* taskId LIMIT limit + 1`), never the whole tenant filtered in memory.
|
|
353
|
+
*/
|
|
354
|
+
list(tenant: TenantId, query: TaskAttemptListQuery): Promise<TaskAttemptPage>;
|
|
355
|
+
/**
|
|
356
|
+
* First claim wins the ownership; a later claim by the same device is
|
|
357
|
+
* idempotent. No-op (returns `undefined`) for a task this tenant never
|
|
358
|
+
* offered.
|
|
359
|
+
*
|
|
360
|
+
* `runtime`/`capabilities` are the claiming daemon's self-report, written
|
|
361
|
+
* ONLY by the claim that actually wins the ownership CAS — an idempotent
|
|
362
|
+
* re-claim (and a losing one) must leave the recorded snapshot untouched,
|
|
363
|
+
* including when it carries a different or absent value.
|
|
364
|
+
*/
|
|
271
365
|
claim(tenant: TenantId, input: {
|
|
272
366
|
readonly taskId: string;
|
|
273
367
|
readonly deviceId: string;
|
|
368
|
+
readonly runtime?: RuntimeId;
|
|
369
|
+
readonly capabilities?: RuntimeCapabilities;
|
|
274
370
|
}): Promise<TaskAttempt | undefined>;
|
|
275
371
|
/** Record a lifecycle transition. No-op (returns `undefined`) for an unknown task — same shape as the reference server's per-type handlers. */
|
|
276
372
|
recordStatus(tenant: TenantId, input: {
|
|
@@ -335,7 +431,8 @@ export interface AgentEgressStore {
|
|
|
335
431
|
readonly record: AgentEgressRecord;
|
|
336
432
|
readonly created: boolean;
|
|
337
433
|
}>;
|
|
338
|
-
|
|
434
|
+
/** Exact reliable fact lookup; device plus event id alone is ambiguous. */
|
|
435
|
+
get(tenant: TenantId, deviceId: string, agentRef: AgentRef, eventId: string): Promise<AgentEgressRecord | undefined>;
|
|
339
436
|
}
|
|
340
437
|
export interface ProofRequestReceipt {
|
|
341
438
|
readonly tenantId: TenantId;
|
package/dist/tenant-stores.d.ts
CHANGED
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
* device-facing handler must not be able to reach them.
|
|
23
23
|
*/
|
|
24
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 MailboxRecordDeliveryInput, type Principal, type PresenceHint, type PresenceHintInput, type TenantReadiness, type StorageFinalizeInput, type StorageFinalizeResult, type StorageReservation, type StorageReservationInput, type TenantId } from '@byok-sdk/core';
|
|
25
|
+
import type { AgentRef, RuntimeCapabilities, RuntimeId } from '@byok-sdk/protocol';
|
|
25
26
|
import type { ActivityAppendInput, ActivityTail } from './activity';
|
|
26
27
|
import type { ApprovalTimelineAppendInput, ApprovalTimelineTail } from './approval-timeline';
|
|
27
28
|
import type { BlobObservation, CloudStores, DeviceRecord, AgentEgressRecord, RequestReceipt, TaskCancellationMutation, TaskCancellationRequest, TaskAttempt, TaskAttemptStatus } from './stores/ports';
|
|
@@ -99,9 +100,12 @@ export interface TenantBoundTaskAttempts {
|
|
|
99
100
|
}): Promise<import('./stores/ports').AgentMessageAdmission | undefined>;
|
|
100
101
|
get(taskId: string): Promise<TaskAttempt | undefined>;
|
|
101
102
|
getMany(taskIds: readonly string[]): Promise<readonly TaskAttempt[]>;
|
|
103
|
+
list(query: import('./stores/ports').TaskAttemptListQuery): Promise<import('./stores/ports').TaskAttemptPage>;
|
|
102
104
|
claim(input: {
|
|
103
105
|
readonly taskId: string;
|
|
104
106
|
readonly deviceId: string;
|
|
107
|
+
readonly runtime?: RuntimeId;
|
|
108
|
+
readonly capabilities?: RuntimeCapabilities;
|
|
105
109
|
}): Promise<TaskAttempt | undefined>;
|
|
106
110
|
recordStatus(input: {
|
|
107
111
|
readonly taskId: string;
|
|
@@ -114,7 +118,10 @@ export interface TenantBoundTaskCancellations {
|
|
|
114
118
|
request(input: TaskCancellationRequest): Promise<TaskCancellationMutation | undefined>;
|
|
115
119
|
}
|
|
116
120
|
export interface TenantBoundDedup {
|
|
121
|
+
/** Device-physical envelope ledger. */
|
|
117
122
|
checkAndRecord(deviceId: string, envelopeId: string): Promise<boolean>;
|
|
123
|
+
/** Strict Agent-bound envelope ledger. */
|
|
124
|
+
checkAndRecordAgent(deviceId: string, agentRef: AgentRef, envelopeId: string): Promise<boolean>;
|
|
118
125
|
}
|
|
119
126
|
export interface TenantBoundReceipts {
|
|
120
127
|
record(input: {
|
|
@@ -131,7 +138,7 @@ export interface TenantBoundAgentEgress {
|
|
|
131
138
|
readonly record: AgentEgressRecord;
|
|
132
139
|
readonly created: boolean;
|
|
133
140
|
}>;
|
|
134
|
-
get(deviceId: string, eventId: string): Promise<AgentEgressRecord | undefined>;
|
|
141
|
+
get(deviceId: string, agentRef: AgentRef, eventId: string): Promise<AgentEgressRecord | undefined>;
|
|
135
142
|
}
|
|
136
143
|
export interface TenantBoundBlobs {
|
|
137
144
|
createUpload(reservation: StorageReservation): Promise<{
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@byok-sdk/cloud",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.13.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.
|
|
48
|
-
"@byok-sdk/protocol": "0.
|
|
47
|
+
"@byok-sdk/core": "0.13.0",
|
|
48
|
+
"@byok-sdk/protocol": "0.13.0",
|
|
49
49
|
"hono": "^4.12.30",
|
|
50
50
|
"zod": "^4.4.3"
|
|
51
51
|
}
|