@byok-sdk/cloud 0.3.0 → 0.4.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/README.md CHANGED
@@ -4,7 +4,7 @@ Stateless hosted BYOK HTTP handlers and an in-memory reference composition over
4
4
  tenant-first `@byok-sdk/core` ports. It owns device-facing protocol/auth/policy
5
5
  logic but no durable database or object-storage driver.
6
6
 
7
- Pair it with `@byok-sdk/cloud-postgres` for Postgres + R2 production storage.
7
+ Pair it with `@byok-sdk/cloud-dataplane` for Postgres + R2 production storage.
8
8
 
9
9
  Hosted compositions enqueue the distinct toolset offer message explicitly:
10
10
 
@@ -22,6 +22,31 @@ await cloud.enqueueToolsetOffer(tenantId, deviceId, {
22
22
 
23
23
  Unlike the live self-hosted coordinator, this stateless enqueue API cannot
24
24
  infer current device capabilities; the host must route to a device known to
25
- advertise `toolset-selection`.
25
+ advertise `toolset-selection`. `listPresence(tenant)` includes the optional
26
+ `configuredToolsets` reported by each live daemon, so the host can narrow
27
+ candidate devices before enqueue. This is TTL-bounded discovery, not execution
28
+ authority: the daemon still resolves every required ID locally and declines
29
+ fail-closed if its configuration changed.
30
+
31
+ Reading a task's outcome goes through the same first terminal fact twice:
32
+ `readTerminalReceipt(tenant, taskId)` returns the stored envelope raw, and
33
+ `readTaskResult(tenant, taskId)` decodes that same receipt into a typed
34
+ `TerminalResult` — the state, plus `summary`/`sessionRef`/`artifactRefs`/
35
+ `document` on a completion or `reason`/`retryable` on a failure — projected
36
+ verbatim with no re-validation:
37
+
38
+ ```ts
39
+ const result = await cloud.readTaskResult(tenantId, taskId);
40
+ if (result === undefined) {
41
+ // No terminal fact yet. A declined task records none — read the attempt
42
+ // status with `readTaskAttempt(tenant, taskId)` for that case.
43
+ } else if (result.state === 'failed' && result.retryable) {
44
+ // re-offer
45
+ }
46
+ ```
47
+
48
+ `document` is absent, never null, when the daemon sent none; a receipt whose
49
+ stored body is not a terminal envelope throws `ByokCloudError` rather than
50
+ returning a best-effort shape.
26
51
 
27
52
  MIT licensed. Node.js 22.19.0 or newer.
package/dist/cloud.d.ts CHANGED
@@ -20,6 +20,7 @@ import type { TokenSigner } from './auth/tokens';
20
20
  import type { CloudCrypto } from './crypto/port';
21
21
  import { type RouteDescriptor } from './router/registry';
22
22
  import type { BlobContentProxy, CloudStores, DeviceRecord, PairingCodeInfo, RequestReceipt, TaskAttempt } from './stores/ports';
23
+ import { type TerminalResult } from './terminal-result';
23
24
  import type { TruthCommitter, TruthObjectDownloads } from './truth/contract';
24
25
  /** Matches the reference server's ceiling (§7). */
25
26
  export declare const DEFAULT_MAX_BLOB_SIZE_BYTES: number;
@@ -127,6 +128,19 @@ export interface ByokCloud {
127
128
  readTaskAttempt(tenant: TenantId, taskId: string): Promise<TaskAttempt | undefined>;
128
129
  /** The recorded terminal for a task — the first one, re-encoded canonically under the frozen v1 codec (see `recordTerminal`, `inbound.ts`: the stored body is `encodeEnvelope` of the zod-parsed envelope, not the device's original byte sequence). */
129
130
  readTerminalReceipt(tenant: TenantId, taskId: string): Promise<RequestReceipt | undefined>;
131
+ /**
132
+ * Host control plane: the same first terminal, decoded into the typed read
133
+ * model ({@link TerminalResult}) so a host reads result fields, not envelope
134
+ * prose. `undefined` ONLY means no terminal fact is recorded yet:
135
+ * first-terminal-wins is inherited from the receipt store
136
+ * ({@link ByokCloud.readTerminalReceipt} reads the same row), and a declined
137
+ * task records no terminal at all — use {@link ByokCloud.readTaskAttempt}
138
+ * for that attempt status. An absent `document` covers both a legacy
139
+ * pre-`result-document` daemon build and a daemon with no `resultDocument`
140
+ * extractor; a receipt whose body is not a terminal envelope throws rather
141
+ * than returning a best-effort shape.
142
+ */
143
+ readTaskResult(tenant: TenantId, taskId: string): Promise<TerminalResult | undefined>;
130
144
  listDevices(tenant: TenantId): Promise<readonly DeviceRecord[]>;
131
145
  revokeDevice(tenant: TenantId, deviceId: string): Promise<void>;
132
146
  /** Host control plane: create a board row from explicit producer labels. */
package/dist/errors.d.ts CHANGED
@@ -32,6 +32,13 @@ export declare const CLOUD_ERROR_CODES: {
32
32
  readonly capability_over_declared: 'capability_over_declared';
33
33
  /** Host-supplied board labels or coordination input exceeded the explicit contract. */
34
34
  readonly coordination_input_invalid: 'coordination_input_invalid';
35
+ /**
36
+ * A terminal receipt whose stored body is not a terminal envelope — either
37
+ * undecodable or a non-terminal type. Whatever wrote that row broke the
38
+ * receipt-store contract, so the typed read model fails closed instead of
39
+ * projecting a best-effort shape.
40
+ */
41
+ readonly terminal_receipt_unreadable: 'terminal_receipt_unreadable';
35
42
  /** A progress/activity batch exceeded the configured event or byte ceiling. */
36
43
  readonly activity_batch_too_large: 'activity_batch_too_large';
37
44
  };
package/dist/index.d.ts CHANGED
@@ -40,6 +40,8 @@ export { createWebCrypto } from './crypto/web-crypto';
40
40
  export type { CloudCrypto } from './crypto/port';
41
41
  export { handleInboundEnvelope, terminalReceiptKey } from './inbound';
42
42
  export type { InboundOutcome } from './inbound';
43
+ export { projectTerminalResult } from './terminal-result';
44
+ export type { TerminalResult } from './terminal-result';
43
45
  export { tenantStoresFor } from './tenant-stores';
44
46
  export type { TenantBoundActivity, TenantBoundBoard, CloudRootStores, TenantBoundBlobs, TenantBoundDedup, TenantBoundDevices, TenantBoundMailbox, TenantBoundPresence, TenantBoundQuota, TenantBoundRateLimiter, TenantBoundReceipts, TenantBoundTaskAttempts, TenantStores, } from './tenant-stores';
45
47
  export { DEFAULT_ACTIVITY_BOUNDS, DEFAULT_ACTIVITY_CAPACITY, DEFAULT_ACTIVITY_MAX_BYTES, DEFAULT_ACTIVITY_MAX_EVENTS, DEFAULT_ACTIVITY_TTL_MS, DEFAULT_BOARD_CHANNEL_MAX_BYTES, DEFAULT_BOARD_TITLE_MAX_BYTES, DEFAULT_PRESENCE_DETAIL_MAX_BYTES, DEFAULT_PRESENCE_MINIMUM_INTERVAL_MS, DEFAULT_PRESENCE_TTL_MS, } from './coordination';
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { BOARD_STATUSES, PRESENCE_LEVELS, CapabilityDeclarationSchema, NONCE_SIGNING_DOMAIN, hasCapability, isTenantId, tenantId, principalTenant, parseDeviceProofEnvelope, deviceProofSigningInput, parseCapabilityDeclaration, ByokCoreError, tenantKey, createInMemoryCoreStores, assertCapability, contentHash, isCoreConflictError, isCoreError, DEVICE_PROOF_HEADER, TRUTH_RECORD_KINDS, STORAGE_ERROR_CODES, STORAGE_ERROR_HTTP_STATUS } from '@byok-sdk/core';
2
2
  export { DEVICE_PROOF_HEADER, NONCE_SIGNING_DOMAIN, isTenantId, tenantId } from '@byok-sdk/core';
3
- import { AgentEventOrUnknownSchema, DAEMON_TO_SERVER_TYPES, encodeEnvelope, BYOK_PAIR_PATH, BYOK_CHALLENGE_PATH, BYOK_TOKEN_PATH, BYOK_CAPABILITIES_PATH, BYOK_EVENTS_PATH, BYOK_MESSAGES_PATH, BYOK_BOARD_PATH, BYOK_BOARD_CLAIM_ROUTE, BYOK_BOARD_UNCLAIM_ROUTE, BYOK_BOARD_STATUS_ROUTE, BYOK_BOARD_STREAM_PATH, BYOK_PRESENCE_PATH, BYOK_ACTIVITY_PATH, BYOK_RECORDS_PATH, BYOK_RECORD_ROUTE, BYOK_SKILL_PACKS_PATH, BYOK_SKILL_PACK_FILE_ROUTE, BYOK_BLOBS_PATH, BYOK_BLOB_FINALIZE_ROUTE, BYOK_BLOB_URL_ROUTE, BYOK_BLOB_CONTENT_ROUTE, decodeEnvelope, createEnvelope, PairRequestSchema, ChallengeRequestSchema, TokenRequestSchema, MessagesSendRequestSchema, CreateBlobRequestSchema, byokBlobContentPath } from '@byok-sdk/protocol';
3
+ import { ConfiguredToolsetsSchema, AgentEventOrUnknownSchema, DAEMON_TO_SERVER_TYPES, encodeEnvelope, decodeEnvelope, BYOK_PAIR_PATH, BYOK_CHALLENGE_PATH, BYOK_TOKEN_PATH, BYOK_CAPABILITIES_PATH, BYOK_EVENTS_PATH, BYOK_MESSAGES_PATH, BYOK_BOARD_PATH, BYOK_BOARD_CLAIM_ROUTE, BYOK_BOARD_UNCLAIM_ROUTE, BYOK_BOARD_STATUS_ROUTE, BYOK_BOARD_STREAM_PATH, BYOK_PRESENCE_PATH, BYOK_ACTIVITY_PATH, BYOK_RECORDS_PATH, BYOK_RECORD_ROUTE, BYOK_SKILL_PACKS_PATH, BYOK_SKILL_PACK_FILE_ROUTE, BYOK_BLOBS_PATH, BYOK_BLOB_FINALIZE_ROUTE, BYOK_BLOB_URL_ROUTE, BYOK_BLOB_CONTENT_ROUTE, createEnvelope, PairRequestSchema, ChallengeRequestSchema, TokenRequestSchema, MessagesSendRequestSchema, CreateBlobRequestSchema, byokBlobContentPath } from '@byok-sdk/protocol';
4
4
  import { z } from 'zod';
5
5
  import { Hono } from 'hono';
6
6
 
@@ -309,6 +309,13 @@ var CLOUD_ERROR_CODES = {
309
309
  capability_over_declared: "capability_over_declared",
310
310
  /** Host-supplied board labels or coordination input exceeded the explicit contract. */
311
311
  coordination_input_invalid: "coordination_input_invalid",
312
+ /**
313
+ * A terminal receipt whose stored body is not a terminal envelope — either
314
+ * undecodable or a non-terminal type. Whatever wrote that row broke the
315
+ * receipt-store contract, so the typed read model fails closed instead of
316
+ * projecting a best-effort shape.
317
+ */
318
+ terminal_receipt_unreadable: "terminal_receipt_unreadable",
312
319
  /** A progress/activity batch exceeded the configured event or byte ceiling. */
313
320
  activity_batch_too_large: "activity_batch_too_large"
314
321
  };
@@ -1053,7 +1060,8 @@ function boardFailure(c, caught) {
1053
1060
  }
1054
1061
  var PresenceBodySchema = z.object({
1055
1062
  level: z.enum(PRESENCE_LEVELS),
1056
- detail: z.string().optional()
1063
+ detail: z.string().optional(),
1064
+ configuredToolsets: ConfiguredToolsetsSchema.optional()
1057
1065
  });
1058
1066
  var ActivityBodySchema = z.object({
1059
1067
  taskId: z.string().min(1).max(200),
@@ -1075,6 +1083,7 @@ function presencePublishHandler(deps) {
1075
1083
  deviceId: authenticated.device.deviceId,
1076
1084
  level: parsed.data.level,
1077
1085
  ...parsed.data.detail === void 0 ? {} : { detail: parsed.data.detail },
1086
+ ...parsed.data.configuredToolsets === void 0 ? {} : { configuredToolsets: parsed.data.configuredToolsets },
1078
1087
  ttlMs: deps.ttlMs,
1079
1088
  minimumIntervalMs: deps.minimumIntervalMs
1080
1089
  }),
@@ -1555,6 +1564,50 @@ var CloudRouteRegistry = class {
1555
1564
  return this.#app.fetch;
1556
1565
  }
1557
1566
  };
1567
+ function projectTerminalResult(taskId, receipt) {
1568
+ let envelope;
1569
+ try {
1570
+ envelope = decodeEnvelope(receipt.body);
1571
+ } catch (cause) {
1572
+ throw new ByokCloudError(
1573
+ "terminal_receipt_unreadable",
1574
+ `The terminal receipt for task ${taskId} holds a body that is not a decodable envelope.`,
1575
+ { cause }
1576
+ );
1577
+ }
1578
+ switch (envelope.type) {
1579
+ case "task.complete":
1580
+ return {
1581
+ taskId,
1582
+ state: "complete",
1583
+ summary: envelope.payload.summary,
1584
+ sessionRef: envelope.payload.sessionRef,
1585
+ ...envelope.payload.artifactRefs !== void 0 ? { artifactRefs: envelope.payload.artifactRefs } : {},
1586
+ ...envelope.payload.document !== void 0 ? { document: envelope.payload.document } : {},
1587
+ recordedAt: receipt.recordedAt
1588
+ };
1589
+ case "task.fail":
1590
+ return {
1591
+ taskId,
1592
+ state: "failed",
1593
+ reason: envelope.payload.reason,
1594
+ ...envelope.payload.retryable !== void 0 ? { retryable: envelope.payload.retryable } : {},
1595
+ recordedAt: receipt.recordedAt
1596
+ };
1597
+ case "task.cancelled":
1598
+ return {
1599
+ taskId,
1600
+ state: "cancelled",
1601
+ ...envelope.payload.reason !== void 0 ? { reason: envelope.payload.reason } : {},
1602
+ recordedAt: receipt.recordedAt
1603
+ };
1604
+ default:
1605
+ throw new ByokCloudError(
1606
+ "terminal_receipt_unreadable",
1607
+ `The terminal receipt for task ${taskId} holds a ${envelope.type} envelope, which is not a terminal type.`
1608
+ );
1609
+ }
1610
+ }
1558
1611
 
1559
1612
  // src/cloud.ts
1560
1613
  var DEFAULT_MAX_BLOB_SIZE_BYTES = 100 * 1024 * 1024;
@@ -1786,6 +1839,12 @@ function createByokCloud(options) {
1786
1839
  readTerminalReceipt(tenant, taskId) {
1787
1840
  return tenantStoresFor(controlPlane(tenant), root).receipts.get(terminalReceiptKey(taskId));
1788
1841
  },
1842
+ async readTaskResult(tenant, taskId) {
1843
+ const receipt = await tenantStoresFor(controlPlane(tenant), root).receipts.get(
1844
+ terminalReceiptKey(taskId)
1845
+ );
1846
+ return receipt === void 0 ? void 0 : projectTerminalResult(taskId, receipt);
1847
+ },
1789
1848
  listDevices(tenant) {
1790
1849
  return tenantStoresFor(controlPlane(tenant), root).devices.list();
1791
1850
  },
@@ -2479,6 +2538,6 @@ var CLOUD_PORT_INTERFACES = {
2479
2538
  rateLimiter: "InboundRateLimiter"
2480
2539
  };
2481
2540
 
2482
- export { ACCESS_TOKEN_TTL_SECONDS, AllowAllRateLimiter, BLOB_URL_TTL_MS, BoardFeedClient, BoardFeedRetryableError, BoardFeedStoppedError, ByokCloudError, CLOUD_CAPABILITIES, CLOUD_ERROR_CODES, CLOUD_PORT_INTERFACES, CLOUD_PORT_METHODS, CLOUD_STORE_NAMES, CapabilitiesResponseSchema, CloudRouteRegistry, DEDUP_RING_CAPACITY, DEFAULT_ACTIVITY_BOUNDS, DEFAULT_ACTIVITY_CAPACITY, DEFAULT_ACTIVITY_MAX_BYTES, DEFAULT_ACTIVITY_MAX_EVENTS, DEFAULT_ACTIVITY_TTL_MS, DEFAULT_BOARD_CHANNEL_MAX_BYTES, DEFAULT_BOARD_PAGE_LIMIT, DEFAULT_BOARD_STREAM_HEARTBEAT_INTERVAL_MS, DEFAULT_BOARD_STREAM_QUERY_INTERVAL_MS, DEFAULT_BOARD_STREAM_RECONCILIATION_INTERVAL_MS, DEFAULT_BOARD_TITLE_MAX_BYTES, DEFAULT_DEVICE_PROOF_CLOCK_SKEW_MS, DEFAULT_DEVICE_PROOF_MAX_LIFETIME_MS, DEFAULT_EVENTS_PAGE_LIMIT, DEFAULT_LONG_POLL_HOLD_MS, DEFAULT_LONG_POLL_INTERVAL_MS, DEFAULT_MAX_BLOB_SIZE_BYTES, DEFAULT_MAX_TRUTH_REQUEST_BYTES, DEFAULT_PRESENCE_DETAIL_MAX_BYTES, DEFAULT_PRESENCE_MINIMUM_INTERVAL_MS, DEFAULT_PRESENCE_TTL_MS, DEFAULT_SKILL_PACK_PAGE_LIMIT, DEVICE_IDENTITY_PROOF_KEY_EPOCH, DEVICE_IDENTITY_PROOF_KEY_ID, InMemoryBlobContentProxy, InMemoryCloudBlobStore, InMemoryDeviceDirectory, InMemoryInboundDedupStore, InMemoryNonceStore, InMemoryPairingCodeStore, InMemoryProofRequestReceiptStore, InMemoryRequestReceiptStore, InMemoryTaskAttemptStore, MAX_DEVICE_PROOF_CLOCK_SKEW_MS, MAX_DEVICE_PROOF_HEADER_BYTES, MAX_DEVICE_PROOF_MAX_LIFETIME_MS, NONCE_TTL_MS, PAIRING_CODE_TTL_MS, ROUTE_CLASSES, ROUTE_METHODS, TASK_ATTEMPT_STATUSES, TRUTH_BATCH_MAX_RECORDS, TRUTH_INLINE_CONTENT_TYPE, TRUTH_LABEL_MAX_LENGTH, TRUTH_MANIFEST_MAX_LIMIT, TRUTH_RECORD_CAPABILITY, TRUTH_RECORD_KEY_MAX_LENGTH, TRUTH_REQUEST_ID_MAX_LENGTH, TruthBodyInputSchema, TruthCommitError, TruthCommitResponseSchema, TruthRecordKeySchema, TruthRecordMetadataSchema, TruthWriteRequestSchema, authenticateBearer, authenticateDeviceProof, createAuthPlane, createByokCloud, createHmacTokenSigner, createInMemoryBlobs, createInMemoryByokCloud, createInMemoryCloudStores, createWebCrypto, declares, extractBearerToken, fullCapabilityDeclaration, handleInboundEnvelope, isCloudError, isTruthCommitError, routeKey, tenantStoresFor, terminalReceiptKey, truthManifestMetadata, truthRecordMetadata, verifyNonceSignature };
2541
+ export { ACCESS_TOKEN_TTL_SECONDS, AllowAllRateLimiter, BLOB_URL_TTL_MS, BoardFeedClient, BoardFeedRetryableError, BoardFeedStoppedError, ByokCloudError, CLOUD_CAPABILITIES, CLOUD_ERROR_CODES, CLOUD_PORT_INTERFACES, CLOUD_PORT_METHODS, CLOUD_STORE_NAMES, CapabilitiesResponseSchema, CloudRouteRegistry, DEDUP_RING_CAPACITY, DEFAULT_ACTIVITY_BOUNDS, DEFAULT_ACTIVITY_CAPACITY, DEFAULT_ACTIVITY_MAX_BYTES, DEFAULT_ACTIVITY_MAX_EVENTS, DEFAULT_ACTIVITY_TTL_MS, DEFAULT_BOARD_CHANNEL_MAX_BYTES, DEFAULT_BOARD_PAGE_LIMIT, DEFAULT_BOARD_STREAM_HEARTBEAT_INTERVAL_MS, DEFAULT_BOARD_STREAM_QUERY_INTERVAL_MS, DEFAULT_BOARD_STREAM_RECONCILIATION_INTERVAL_MS, DEFAULT_BOARD_TITLE_MAX_BYTES, DEFAULT_DEVICE_PROOF_CLOCK_SKEW_MS, DEFAULT_DEVICE_PROOF_MAX_LIFETIME_MS, DEFAULT_EVENTS_PAGE_LIMIT, DEFAULT_LONG_POLL_HOLD_MS, DEFAULT_LONG_POLL_INTERVAL_MS, DEFAULT_MAX_BLOB_SIZE_BYTES, DEFAULT_MAX_TRUTH_REQUEST_BYTES, DEFAULT_PRESENCE_DETAIL_MAX_BYTES, DEFAULT_PRESENCE_MINIMUM_INTERVAL_MS, DEFAULT_PRESENCE_TTL_MS, DEFAULT_SKILL_PACK_PAGE_LIMIT, DEVICE_IDENTITY_PROOF_KEY_EPOCH, DEVICE_IDENTITY_PROOF_KEY_ID, InMemoryBlobContentProxy, InMemoryCloudBlobStore, InMemoryDeviceDirectory, InMemoryInboundDedupStore, InMemoryNonceStore, InMemoryPairingCodeStore, InMemoryProofRequestReceiptStore, InMemoryRequestReceiptStore, InMemoryTaskAttemptStore, MAX_DEVICE_PROOF_CLOCK_SKEW_MS, MAX_DEVICE_PROOF_HEADER_BYTES, MAX_DEVICE_PROOF_MAX_LIFETIME_MS, NONCE_TTL_MS, PAIRING_CODE_TTL_MS, ROUTE_CLASSES, ROUTE_METHODS, TASK_ATTEMPT_STATUSES, TRUTH_BATCH_MAX_RECORDS, TRUTH_INLINE_CONTENT_TYPE, TRUTH_LABEL_MAX_LENGTH, TRUTH_MANIFEST_MAX_LIMIT, TRUTH_RECORD_CAPABILITY, TRUTH_RECORD_KEY_MAX_LENGTH, TRUTH_REQUEST_ID_MAX_LENGTH, TruthBodyInputSchema, TruthCommitError, TruthCommitResponseSchema, TruthRecordKeySchema, TruthRecordMetadataSchema, TruthWriteRequestSchema, authenticateBearer, authenticateDeviceProof, createAuthPlane, createByokCloud, createHmacTokenSigner, createInMemoryBlobs, createInMemoryByokCloud, createInMemoryCloudStores, createWebCrypto, declares, extractBearerToken, fullCapabilityDeclaration, handleInboundEnvelope, isCloudError, isTruthCommitError, projectTerminalResult, routeKey, tenantStoresFor, terminalReceiptKey, truthManifestMetadata, truthRecordMetadata, verifyNonceSignature };
2483
2542
  //# sourceMappingURL=index.js.map
2484
2543
  //# sourceMappingURL=index.js.map