@byok-sdk/cloud 0.2.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/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
- import { BOARD_STATUSES, PRESENCE_LEVELS, CapabilityDeclarationSchema, hasCapability, isTenantId, tenantId, principalTenant, parseDeviceProofEnvelope, deviceProofSigningInput, contentHash, parseCapabilityDeclaration, ByokCoreError, tenantKey, createInMemoryCoreStores, assertCapability, isCoreConflictError, isCoreError, TRUTH_RECORD_KINDS, STORAGE_ERROR_CODES, STORAGE_ERROR_HTTP_STATUS } from '@byok-sdk/core';
2
- export { isTenantId, tenantId } from '@byok-sdk/core';
3
- import { AgentEventOrUnknownSchema, DAEMON_TO_SERVER_TYPES, encodeEnvelope, createEnvelope, PairRequestSchema, ChallengeRequestSchema, TokenRequestSchema, decodeEnvelope, MessagesSendRequestSchema, CreateBlobRequestSchema } from '@byok-sdk/protocol';
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
+ export { DEVICE_PROOF_HEADER, NONCE_SIGNING_DOMAIN, isTenantId, tenantId } from '@byok-sdk/core';
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
 
@@ -160,9 +160,6 @@ function createHmacTokenSigner(secret, clock) {
160
160
  }
161
161
  };
162
162
  }
163
-
164
- // src/auth/verify.ts
165
- var NONCE_SIGNING_DOMAIN = "byok-nonce-v1\n";
166
163
  function verifyNonceSignature(crypto, devicePublicKey, nonce, signature) {
167
164
  return crypto.verifyEd25519(devicePublicKey, NONCE_SIGNING_DOMAIN + nonce, signature);
168
165
  }
@@ -256,16 +253,30 @@ var CLOUD_CAPABILITIES = {
256
253
  /** Bounded task activity batch publication. */
257
254
  activityTail: "activity.tail",
258
255
  /** Request-bound device proof record manifest/read/write surface (S6). */
259
- truthRecords: "truth.records"
256
+ truthRecords: "truth.records",
257
+ /**
258
+ * Tenant-scoped skill pack distribution: the manifest list plus the per-file
259
+ * content route a paired device installs from.
260
+ *
261
+ * Hosted HTTP on purpose. A pack is DECLARATIVE CONTENT a device pulls after
262
+ * reading this declaration — not a message — so `@byok-sdk/protocol` gains
263
+ * nothing from it and the frozen v1 envelope stays untouched. Composition-
264
+ * bound like `truth.records`: a deployment that declares it without supplying
265
+ * a `SkillPackStore` is refused at construction rather than publishing two
266
+ * routes it would then 404.
267
+ */
268
+ skillPacks: "skills.pack"
260
269
  };
261
270
  var CapabilitiesResponseSchema = CapabilityDeclarationSchema;
262
271
  function fullCapabilityDeclaration(version = 1, options = {}) {
263
272
  return {
264
273
  schema: "byok-capabilities-v1",
265
274
  version,
266
- capabilities: Object.values(CLOUD_CAPABILITIES).filter(
267
- (capability) => capability !== CLOUD_CAPABILITIES.truthRecords || options.includeTruthRecords === true
268
- )
275
+ capabilities: Object.values(CLOUD_CAPABILITIES).filter((capability) => {
276
+ if (capability === CLOUD_CAPABILITIES.truthRecords) return options.includeTruthRecords === true;
277
+ if (capability === CLOUD_CAPABILITIES.skillPacks) return options.includeSkillPacks === true;
278
+ return true;
279
+ })
269
280
  };
270
281
  }
271
282
  function declares(declaration, capability) {
@@ -279,11 +290,9 @@ var CLOUD_ERROR_CODES = {
279
290
  /** The composition handed a device row whose tenant is not a mintable `TenantId`. */
280
291
  device_tenant_invalid: "device_tenant_invalid",
281
292
  /**
282
- * The mailbox assigned a row `seq` that disagrees with the delivery `seq`
283
- * baked into the enqueued envelope. Loud rather than silent: those two
284
- * numbers ARE the daemon's redelivery cursor, and a composition whose
285
- * mailbox numbers rows differently from `DeviceSequenceStore` would
286
- * mis-deliver every subsequent poll.
293
+ * The mailbox committed a row `seq` that disagrees with the delivery `seq`
294
+ * its body factory baked into the envelope. Loud rather than silent: those
295
+ * two numbers ARE the daemon's redelivery cursor.
287
296
  */
288
297
  mailbox_seq_mismatch: "mailbox_seq_mismatch",
289
298
  /** A capability declaration the host supplied that core refused. */
@@ -300,6 +309,13 @@ var CLOUD_ERROR_CODES = {
300
309
  capability_over_declared: "capability_over_declared",
301
310
  /** Host-supplied board labels or coordination input exceeded the explicit contract. */
302
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",
303
319
  /** A progress/activity batch exceeded the configured event or byte ceiling. */
304
320
  activity_batch_too_large: "activity_batch_too_large"
305
321
  };
@@ -457,9 +473,6 @@ function tenantStoresFor(principal, root) {
457
473
  finalizeReservation: (input) => core.quota.finalizeReservation(tenant, input),
458
474
  abortReservation: (reservationId) => core.quota.abortReservation(tenant, reservationId)
459
475
  },
460
- sequence: {
461
- next: (deviceId) => cloud.sequence.next(tenant, deviceId)
462
- },
463
476
  rateLimiter: {
464
477
  consume: (deviceId) => cloud.rateLimiter.consume(tenant, deviceId)
465
478
  }
@@ -1047,7 +1060,8 @@ function boardFailure(c, caught) {
1047
1060
  }
1048
1061
  var PresenceBodySchema = z.object({
1049
1062
  level: z.enum(PRESENCE_LEVELS),
1050
- detail: z.string().optional()
1063
+ detail: z.string().optional(),
1064
+ configuredToolsets: ConfiguredToolsetsSchema.optional()
1051
1065
  });
1052
1066
  var ActivityBodySchema = z.object({
1053
1067
  taskId: z.string().min(1).max(200),
@@ -1069,6 +1083,7 @@ function presencePublishHandler(deps) {
1069
1083
  deviceId: authenticated.device.deviceId,
1070
1084
  level: parsed.data.level,
1071
1085
  ...parsed.data.detail === void 0 ? {} : { detail: parsed.data.detail },
1086
+ ...parsed.data.configuredToolsets === void 0 ? {} : { configuredToolsets: parsed.data.configuredToolsets },
1072
1087
  ttlMs: deps.ttlMs,
1073
1088
  minimumIntervalMs: deps.minimumIntervalMs
1074
1089
  }),
@@ -1104,6 +1119,33 @@ function activityAppendHandler(deps) {
1104
1119
  }
1105
1120
  };
1106
1121
  }
1122
+ var DEFAULT_SKILL_PACK_PAGE_LIMIT = 50;
1123
+ function skillPackListHandler(deps) {
1124
+ return async (c) => {
1125
+ const device = await authenticateBearer(c.req.header("authorization"), deps.bearer);
1126
+ if (device === void 0) return unauthorized(c);
1127
+ const packs = await deps.skillPacks.list(principalTenant(device), { limit: deps.pageLimit });
1128
+ return c.json({ packs }, 200);
1129
+ };
1130
+ }
1131
+ function skillPackFileHandler(deps) {
1132
+ return async (c) => {
1133
+ const device = await authenticateBearer(c.req.header("authorization"), deps.bearer);
1134
+ if (device === void 0) return unauthorized(c);
1135
+ const name = c.req.param("name");
1136
+ const path = c.req.param("path");
1137
+ if (name === void 0 || path === void 0) return notFound(c);
1138
+ const file = await deps.skillPacks.readFile(principalTenant(device), name, path);
1139
+ if (file === void 0) return notFound(c);
1140
+ return c.json(file, 200);
1141
+ };
1142
+ }
1143
+ function unauthorized(c) {
1144
+ return c.json({ error: "unauthorized" }, 401);
1145
+ }
1146
+ function notFound(c) {
1147
+ return c.json({ error: "skill_pack_not_found" }, 404);
1148
+ }
1107
1149
  var DEFAULT_DEVICE_PROOF_CLOCK_SKEW_MS = 6e4;
1108
1150
  var MAX_DEVICE_PROOF_CLOCK_SKEW_MS = 5 * 6e4;
1109
1151
  var DEFAULT_DEVICE_PROOF_MAX_LIFETIME_MS = 5 * 6e4;
@@ -1268,7 +1310,6 @@ function isTruthCommitError(value) {
1268
1310
  }
1269
1311
 
1270
1312
  // src/handlers/truth.ts
1271
- var DEVICE_PROOF_HEADER = "x-byok-device-proof";
1272
1313
  var DEFAULT_MAX_TRUTH_REQUEST_BYTES = 2 * 1024 * 1024;
1273
1314
  var MAX_DEVICE_PROOF_HEADER_BYTES = 16 * 1024;
1274
1315
  var EMPTY_BODY = new Uint8Array();
@@ -1280,7 +1321,7 @@ function truthManifestHandler(deps) {
1280
1321
  operation: "truth.list",
1281
1322
  resource: "records"
1282
1323
  });
1283
- if (authenticated === void 0) return unauthorized(c);
1324
+ if (authenticated === void 0) return unauthorized2(c);
1284
1325
  const query = manifestQuery(c);
1285
1326
  if (query === void 0) return c.json({ error: "invalid truth manifest query" }, 400);
1286
1327
  const records = await deps.truth.listManifest(principalTenant(authenticated.device), query);
@@ -1295,7 +1336,7 @@ function truthGetHandler(deps) {
1295
1336
  operation: "truth.read",
1296
1337
  resource
1297
1338
  });
1298
- if (authenticated === void 0) return unauthorized(c);
1339
+ if (authenticated === void 0) return unauthorized2(c);
1299
1340
  const selector = recordSelector(c);
1300
1341
  if (selector === void 0) return c.json({ error: "invalid truth record selector" }, 400);
1301
1342
  const record = await deps.truth.getRecord(principalTenant(authenticated.device), selector);
@@ -1322,7 +1363,7 @@ function truthPutHandler(deps) {
1322
1363
  operation: "truth.write",
1323
1364
  resource: rawRecordResource(c)
1324
1365
  });
1325
- if (authenticated === void 0) return unauthorized(c);
1366
+ if (authenticated === void 0) return unauthorized2(c);
1326
1367
  const selector = recordSelector(c);
1327
1368
  if (selector === void 0) return c.json({ error: "invalid truth record selector" }, 400);
1328
1369
  const parsedJson = parseJsonBytes(body);
@@ -1467,7 +1508,7 @@ function parseJsonBytes(bytes) {
1467
1508
  return void 0;
1468
1509
  }
1469
1510
  }
1470
- function unauthorized(c) {
1511
+ function unauthorized2(c) {
1471
1512
  return c.json({ error: "unauthorized" }, 401);
1472
1513
  }
1473
1514
  function truthFailure(c, caught) {
@@ -1523,6 +1564,50 @@ var CloudRouteRegistry = class {
1523
1564
  return this.#app.fetch;
1524
1565
  }
1525
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
+ }
1526
1611
 
1527
1612
  // src/cloud.ts
1528
1613
  var DEFAULT_MAX_BLOB_SIZE_BYTES = 100 * 1024 * 1024;
@@ -1535,7 +1620,8 @@ function createByokCloud(options) {
1535
1620
  declaration,
1536
1621
  options.blobContentProxy,
1537
1622
  options.truthCommitter,
1538
- options.truthObjectDownloads
1623
+ options.truthObjectDownloads,
1624
+ options.skillPacks
1539
1625
  );
1540
1626
  const root = { core: options.core, cloud: options.cloud };
1541
1627
  const auth = createAuthPlane({
@@ -1556,16 +1642,16 @@ function createByokCloud(options) {
1556
1642
  capacity: options.activityCapacity ?? DEFAULT_ACTIVITY_CAPACITY,
1557
1643
  ttlMs: options.activityTtlMs ?? DEFAULT_ACTIVITY_TTL_MS
1558
1644
  };
1559
- registry.register({ method: "POST", path: "/byok/pair", class: "public" }, pairHandler({ auth }));
1560
- registry.register({ method: "POST", path: "/byok/challenge", class: "public" }, challengeHandler({ auth }));
1561
- registry.register({ method: "POST", path: "/byok/token", class: "public" }, tokenHandler({ auth }));
1645
+ registry.register({ method: "POST", path: BYOK_PAIR_PATH, class: "public" }, pairHandler({ auth }));
1646
+ registry.register({ method: "POST", path: BYOK_CHALLENGE_PATH, class: "public" }, challengeHandler({ auth }));
1647
+ registry.register({ method: "POST", path: BYOK_TOKEN_PATH, class: "public" }, tokenHandler({ auth }));
1562
1648
  registry.register(
1563
- { method: "GET", path: "/byok/capabilities", class: "public" },
1649
+ { method: "GET", path: BYOK_CAPABILITIES_PATH, class: "public" },
1564
1650
  capabilitiesHandler({ declaration })
1565
1651
  );
1566
1652
  if (declares(declaration, CLOUD_CAPABILITIES.eventsLongPoll)) {
1567
1653
  registry.register(
1568
- { method: "GET", path: "/byok/events", class: "device" },
1654
+ { method: "GET", path: BYOK_EVENTS_PATH, class: "device" },
1569
1655
  eventsHandler({
1570
1656
  ...deviceRouteDeps,
1571
1657
  longPollHoldMs: options.longPollHoldMs ?? DEFAULT_LONG_POLL_HOLD_MS,
@@ -1576,7 +1662,7 @@ function createByokCloud(options) {
1576
1662
  }
1577
1663
  if (declares(declaration, CLOUD_CAPABILITIES.messagesBatch)) {
1578
1664
  registry.register(
1579
- { method: "POST", path: "/byok/messages", class: "device" },
1665
+ { method: "POST", path: BYOK_MESSAGES_PATH, class: "device" },
1580
1666
  messagesHandler({ ...deviceRouteDeps, activityBounds })
1581
1667
  );
1582
1668
  }
@@ -1585,23 +1671,23 @@ function createByokCloud(options) {
1585
1671
  ...deviceRouteDeps,
1586
1672
  pageLimit: options.boardPageLimit ?? DEFAULT_BOARD_PAGE_LIMIT
1587
1673
  };
1588
- registry.register({ method: "GET", path: "/byok/board", class: "device" }, boardListHandler(boardDeps));
1674
+ registry.register({ method: "GET", path: BYOK_BOARD_PATH, class: "device" }, boardListHandler(boardDeps));
1589
1675
  registry.register(
1590
- { method: "POST", path: "/byok/board/:id/claim", class: "device" },
1676
+ { method: "POST", path: BYOK_BOARD_CLAIM_ROUTE, class: "device" },
1591
1677
  boardClaimHandler(deviceRouteDeps)
1592
1678
  );
1593
1679
  registry.register(
1594
- { method: "POST", path: "/byok/board/:id/unclaim", class: "device" },
1680
+ { method: "POST", path: BYOK_BOARD_UNCLAIM_ROUTE, class: "device" },
1595
1681
  boardUnclaimHandler(deviceRouteDeps)
1596
1682
  );
1597
1683
  registry.register(
1598
- { method: "POST", path: "/byok/board/:id/status", class: "device" },
1684
+ { method: "POST", path: BYOK_BOARD_STATUS_ROUTE, class: "device" },
1599
1685
  boardStatusHandler(deviceRouteDeps)
1600
1686
  );
1601
1687
  }
1602
1688
  if (declares(declaration, CLOUD_CAPABILITIES.boardSse)) {
1603
1689
  registry.register(
1604
- { method: "GET", path: "/byok/board/stream", class: "device" },
1690
+ { method: "GET", path: BYOK_BOARD_STREAM_PATH, class: "device" },
1605
1691
  boardStreamHandler({
1606
1692
  ...deviceRouteDeps,
1607
1693
  pageLimit: options.boardPageLimit ?? DEFAULT_BOARD_PAGE_LIMIT,
@@ -1613,7 +1699,7 @@ function createByokCloud(options) {
1613
1699
  }
1614
1700
  if (declares(declaration, CLOUD_CAPABILITIES.presenceHints)) {
1615
1701
  registry.register(
1616
- { method: "PUT", path: "/byok/presence", class: "device" },
1702
+ { method: "PUT", path: BYOK_PRESENCE_PATH, class: "device" },
1617
1703
  presencePublishHandler({
1618
1704
  ...deviceRouteDeps,
1619
1705
  ttlMs: options.presenceTtlMs ?? DEFAULT_PRESENCE_TTL_MS,
@@ -1624,7 +1710,7 @@ function createByokCloud(options) {
1624
1710
  }
1625
1711
  if (declares(declaration, CLOUD_CAPABILITIES.activityTail)) {
1626
1712
  registry.register(
1627
- { method: "POST", path: "/byok/activity", class: "device" },
1713
+ { method: "POST", path: BYOK_ACTIVITY_PATH, class: "device" },
1628
1714
  activityAppendHandler({ ...deviceRouteDeps, bounds: activityBounds })
1629
1715
  );
1630
1716
  }
@@ -1642,36 +1728,52 @@ function createByokCloud(options) {
1642
1728
  maxRequestBytes: options.maxTruthRequestBytes ?? DEFAULT_MAX_TRUTH_REQUEST_BYTES
1643
1729
  };
1644
1730
  registry.register(
1645
- { method: "GET", path: "/byok/records", class: "proof" },
1731
+ { method: "GET", path: BYOK_RECORDS_PATH, class: "proof" },
1646
1732
  truthManifestHandler(truthDeps)
1647
1733
  );
1648
1734
  registry.register(
1649
- { method: "GET", path: "/byok/records/:kind/:key", class: "proof" },
1735
+ { method: "GET", path: BYOK_RECORD_ROUTE, class: "proof" },
1650
1736
  truthGetHandler(truthDeps)
1651
1737
  );
1652
1738
  registry.register(
1653
- { method: "PUT", path: "/byok/records/:kind/:key", class: "proof" },
1739
+ { method: "PUT", path: BYOK_RECORD_ROUTE, class: "proof" },
1654
1740
  truthPutHandler(truthDeps)
1655
1741
  );
1656
1742
  }
1743
+ const skillPacks = options.skillPacks;
1744
+ if (skillPacks !== void 0 && declares(declaration, CLOUD_CAPABILITIES.skillPacks)) {
1745
+ const skillPackDeps = {
1746
+ bearer: deviceRouteDeps.bearer,
1747
+ skillPacks,
1748
+ pageLimit: options.skillPackPageLimit ?? DEFAULT_SKILL_PACK_PAGE_LIMIT
1749
+ };
1750
+ registry.register(
1751
+ { method: "GET", path: BYOK_SKILL_PACKS_PATH, class: "device" },
1752
+ skillPackListHandler(skillPackDeps)
1753
+ );
1754
+ registry.register(
1755
+ { method: "GET", path: BYOK_SKILL_PACK_FILE_ROUTE, class: "device" },
1756
+ skillPackFileHandler(skillPackDeps)
1757
+ );
1758
+ }
1657
1759
  if (declares(declaration, CLOUD_CAPABILITIES.blobsPresigned)) {
1658
1760
  const blobDeps = {
1659
1761
  ...deviceRouteDeps,
1660
1762
  maxBlobSizeBytes: options.maxBlobSizeBytes ?? DEFAULT_MAX_BLOB_SIZE_BYTES
1661
1763
  };
1662
- registry.register({ method: "POST", path: "/byok/blobs", class: "device" }, createBlobHandler(blobDeps));
1663
- registry.register({ method: "POST", path: "/byok/blobs/:id/finalize", class: "device" }, finalizeBlobHandler(blobDeps));
1664
- registry.register({ method: "GET", path: "/byok/blobs/:id/url", class: "device" }, blobDownloadUrlHandler(blobDeps));
1764
+ registry.register({ method: "POST", path: BYOK_BLOBS_PATH, class: "device" }, createBlobHandler(blobDeps));
1765
+ registry.register({ method: "POST", path: BYOK_BLOB_FINALIZE_ROUTE, class: "device" }, finalizeBlobHandler(blobDeps));
1766
+ registry.register({ method: "GET", path: BYOK_BLOB_URL_ROUTE, class: "device" }, blobDownloadUrlHandler(blobDeps));
1665
1767
  }
1666
1768
  const contentProxy = options.blobContentProxy;
1667
1769
  if (contentProxy !== void 0 && declares(declaration, CLOUD_CAPABILITIES.blobsContentProxy)) {
1668
1770
  const contentDeps = { contentProxy };
1669
1771
  registry.register(
1670
- { method: "PUT", path: "/byok/blobs/:id/content", class: "presigned" },
1772
+ { method: "PUT", path: BYOK_BLOB_CONTENT_ROUTE, class: "presigned" },
1671
1773
  blobUploadContentHandler(contentDeps)
1672
1774
  );
1673
1775
  registry.register(
1674
- { method: "GET", path: "/byok/blobs/:id/content", class: "presigned" },
1776
+ { method: "GET", path: BYOK_BLOB_CONTENT_ROUTE, class: "presigned" },
1675
1777
  blobDownloadContentHandler(contentDeps)
1676
1778
  );
1677
1779
  }
@@ -1679,6 +1781,34 @@ function createByokCloud(options) {
1679
1781
  function controlPlane(tenant) {
1680
1782
  return { kind: "control-plane", tenantId: tenant, operatorId };
1681
1783
  }
1784
+ async function enqueueTaskEnvelope(tenant, deviceId, requestedTaskId, buildEnvelope) {
1785
+ const stores = tenantStoresFor(controlPlane(tenant), root);
1786
+ const taskId = requestedTaskId ?? `task_${options.crypto.randomUuid()}`;
1787
+ const messageId = options.crypto.randomUuid();
1788
+ const message = await stores.mailbox.append({
1789
+ deviceId,
1790
+ messageId,
1791
+ materialize: async (seq) => {
1792
+ const envelope2 = buildEnvelope(taskId, seq, messageId);
1793
+ const body = encodeEnvelope(envelope2);
1794
+ const bytes = new TextEncoder().encode(body);
1795
+ return {
1796
+ body,
1797
+ bodyHash: contentHash(await options.crypto.sha256(bytes)),
1798
+ byteSize: BigInt(bytes.length)
1799
+ };
1800
+ }
1801
+ });
1802
+ const envelope = decodeEnvelope(message.body);
1803
+ if (envelope.seq !== message.seq) {
1804
+ throw new ByokCloudError(
1805
+ "mailbox_seq_mismatch",
1806
+ `Mailbox stored offer seq ${String(envelope.seq)} at row ${message.seq}; the daemon's redelivery cursor would be wrong.`
1807
+ );
1808
+ }
1809
+ const attempt = await stores.tasks.open({ taskId, deviceId });
1810
+ return { taskId, seq: message.seq, envelope, attempt };
1811
+ }
1682
1812
  return {
1683
1813
  fetch: registry.fetch,
1684
1814
  routes: registry.routes,
@@ -1688,27 +1818,20 @@ function createByokCloud(options) {
1688
1818
  return auth.createPairingCode(tenant, input);
1689
1819
  },
1690
1820
  async enqueueOffer(tenant, deviceId, input) {
1691
- const stores = tenantStoresFor(controlPlane(tenant), root);
1692
- const taskId = input.taskId ?? `task_${options.crypto.randomUuid()}`;
1693
- const seq = await stores.sequence.next(deviceId);
1694
- const envelope = createEnvelope("task.offer", input.payload, { taskId, seq });
1695
- const body = encodeEnvelope(envelope);
1696
- const bytes = new TextEncoder().encode(body);
1697
- const message = await stores.mailbox.append({
1821
+ return enqueueTaskEnvelope(
1822
+ tenant,
1698
1823
  deviceId,
1699
- body,
1700
- bodyHash: contentHash(await options.crypto.sha256(bytes)),
1701
- byteSize: BigInt(bytes.length),
1702
- messageId: envelope.id
1703
- });
1704
- if (message.seq !== seq) {
1705
- throw new ByokCloudError(
1706
- "mailbox_seq_mismatch",
1707
- `Mailbox numbered this offer ${message.seq} while the delivery sequence allocated ${seq}; the daemon's redelivery cursor would be wrong.`
1708
- );
1709
- }
1710
- const attempt = await stores.tasks.open({ taskId, deviceId });
1711
- return { taskId, seq, envelope, attempt };
1824
+ input.taskId,
1825
+ (taskId, seq, messageId) => createEnvelope("task.offer", input.payload, { id: messageId, taskId, seq })
1826
+ );
1827
+ },
1828
+ async enqueueToolsetOffer(tenant, deviceId, input) {
1829
+ return enqueueTaskEnvelope(
1830
+ tenant,
1831
+ deviceId,
1832
+ input.taskId,
1833
+ (taskId, seq, messageId) => createEnvelope("task.offer_with_toolsets", input.payload, { id: messageId, taskId, seq })
1834
+ );
1712
1835
  },
1713
1836
  readTaskAttempt(tenant, taskId) {
1714
1837
  return tenantStoresFor(controlPlane(tenant), root).tasks.get(taskId);
@@ -1716,6 +1839,12 @@ function createByokCloud(options) {
1716
1839
  readTerminalReceipt(tenant, taskId) {
1717
1840
  return tenantStoresFor(controlPlane(tenant), root).receipts.get(terminalReceiptKey(taskId));
1718
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
+ },
1719
1848
  listDevices(tenant) {
1720
1849
  return tenantStoresFor(controlPlane(tenant), root).devices.list();
1721
1850
  },
@@ -1747,7 +1876,7 @@ function createByokCloud(options) {
1747
1876
  }
1748
1877
  };
1749
1878
  }
1750
- function assertNoOverDeclaration(declaration, contentProxy, truthCommitter, truthObjectDownloads) {
1879
+ function assertNoOverDeclaration(declaration, contentProxy, truthCommitter, truthObjectDownloads, skillPacks) {
1751
1880
  if (declares(declaration, CLOUD_CAPABILITIES.boardSse) && !declares(declaration, CLOUD_CAPABILITIES.boardCoordination)) {
1752
1881
  throw new ByokCloudError(
1753
1882
  "capability_over_declared",
@@ -1772,6 +1901,12 @@ function assertNoOverDeclaration(declaration, contentProxy, truthCommitter, trut
1772
1901
  `This deployment declares ${CLOUD_CAPABILITIES.truthRecords} but was given no content-hash keyed TruthObjectDownloads authority.`
1773
1902
  );
1774
1903
  }
1904
+ if (skillPacks === void 0 && declares(declaration, CLOUD_CAPABILITIES.skillPacks)) {
1905
+ throw new ByokCloudError(
1906
+ "capability_over_declared",
1907
+ `This deployment declares ${CLOUD_CAPABILITIES.skillPacks} but was given no SkillPackStore, so both /byok/skill-packs routes would be published and unserved.`
1908
+ );
1909
+ }
1775
1910
  }
1776
1911
  function parseDeclaration(declaration) {
1777
1912
  try {
@@ -1809,7 +1944,7 @@ var InMemoryBlobRegistry = class {
1809
1944
  async signUrl(blobId, action) {
1810
1945
  const exp = this.clock.now().getTime() + this.urlTtlMs;
1811
1946
  const sig = await this.computeSig(blobId, action, exp);
1812
- return `/byok/blobs/${blobId}/content?sig=${sig}&exp=${exp}`;
1947
+ return `${byokBlobContentPath(blobId)}?sig=${sig}&exp=${exp}`;
1813
1948
  }
1814
1949
  computeSig(blobId, action, exp) {
1815
1950
  return this.crypto.hmacSha256(this.secret, `${blobId}:${action}:${exp}`);
@@ -1966,15 +2101,6 @@ var InMemoryDeviceDirectory = class {
1966
2101
  return key === void 0 ? void 0 : this.#byTenant.get(key);
1967
2102
  }
1968
2103
  };
1969
- var InMemoryDeviceSequenceStore = class {
1970
- #next = /* @__PURE__ */ new Map();
1971
- async next(tenant, deviceId) {
1972
- const key = tenantKey(tenant, deviceId);
1973
- const seq = this.#next.get(key) ?? 1;
1974
- this.#next.set(key, seq + 1);
1975
- return seq;
1976
- }
1977
- };
1978
2104
  var DEDUP_RING_CAPACITY = 1024;
1979
2105
  var InMemoryInboundDedupStore = class {
1980
2106
  #rings = /* @__PURE__ */ new Map();
@@ -2173,7 +2299,6 @@ function createInMemoryCloudStores(clock, crypto, objects) {
2173
2299
  tasks: new InMemoryTaskAttemptStore(clock),
2174
2300
  receipts: new InMemoryRequestReceiptStore(clock),
2175
2301
  proofReceipts: new InMemoryProofRequestReceiptStore(clock),
2176
- sequence: new InMemoryDeviceSequenceStore(),
2177
2302
  blobs: blobs.blobs,
2178
2303
  rateLimiter: new AllowAllRateLimiter()
2179
2304
  },
@@ -2225,7 +2350,9 @@ function createInMemoryByokCloud(options = {}) {
2225
2350
  ...options.activityMaxBytes === void 0 ? {} : { activityMaxBytes: options.activityMaxBytes },
2226
2351
  ...options.activityCapacity === void 0 ? {} : { activityCapacity: options.activityCapacity },
2227
2352
  ...options.activityTtlMs === void 0 ? {} : { activityTtlMs: options.activityTtlMs },
2228
- ...options.maxTruthRequestBytes === void 0 ? {} : { maxTruthRequestBytes: options.maxTruthRequestBytes }
2353
+ ...options.maxTruthRequestBytes === void 0 ? {} : { maxTruthRequestBytes: options.maxTruthRequestBytes },
2354
+ ...options.skillPacks === void 0 ? {} : { skillPacks: options.skillPacks },
2355
+ ...options.skillPackPageLimit === void 0 ? {} : { skillPackPageLimit: options.skillPackPageLimit }
2229
2356
  });
2230
2357
  return { cloud, core, stores, blobContentProxy, clock, crypto };
2231
2358
  }
@@ -2278,7 +2405,7 @@ var BoardFeedClient = class {
2278
2405
  return this.mode === "sse" ? this.#readSseOnce(afterSeq, signal) : this.#readPollOnce(afterSeq, signal);
2279
2406
  }
2280
2407
  async #readPollOnce(afterSeq, signal) {
2281
- const url = new URL("/byok/board", this.#base);
2408
+ const url = new URL(BYOK_BOARD_PATH, this.#base);
2282
2409
  url.searchParams.set("since", String(afterSeq));
2283
2410
  const response = await this.#fetch(url, {
2284
2411
  headers: { authorization: `Bearer ${this.#accessToken}` },
@@ -2288,7 +2415,7 @@ var BoardFeedClient = class {
2288
2415
  return { type: "poll", page: BoardFeedPageSchema.parse(await response.json()) };
2289
2416
  }
2290
2417
  async #readSseOnce(afterSeq, signal) {
2291
- const url = new URL("/byok/board/stream", this.#base);
2418
+ const url = new URL(BYOK_BOARD_STREAM_PATH, this.#base);
2292
2419
  const response = await this.#fetch(url, {
2293
2420
  headers: {
2294
2421
  accept: "text/event-stream",
@@ -2380,7 +2507,6 @@ var CLOUD_STORE_NAMES = [
2380
2507
  "tasks",
2381
2508
  "receipts",
2382
2509
  "proofReceipts",
2383
- "sequence",
2384
2510
  "blobs",
2385
2511
  "rateLimiter"
2386
2512
  ];
@@ -2394,7 +2520,6 @@ var CLOUD_PORT_METHODS = {
2394
2520
  tasks: ["open", "get", "claim", "recordStatus"],
2395
2521
  receipts: ["record", "get"],
2396
2522
  proofReceipts: ["record", "get"],
2397
- sequence: ["next"],
2398
2523
  // Three methods, not six: the byte-proxy trio moved to `BlobContentProxy`,
2399
2524
  // which is a composition input rather than a port and therefore has no row
2400
2525
  // in this table (docs/researches/s4a-dataplane-design.md §6).
@@ -2409,11 +2534,10 @@ var CLOUD_PORT_INTERFACES = {
2409
2534
  tasks: "TaskAttemptStore",
2410
2535
  receipts: "RequestReceiptStore",
2411
2536
  proofReceipts: "ProofRequestReceiptStore",
2412
- sequence: "DeviceSequenceStore",
2413
2537
  blobs: "CloudBlobStore",
2414
2538
  rateLimiter: "InboundRateLimiter"
2415
2539
  };
2416
2540
 
2417
- 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, DEVICE_IDENTITY_PROOF_KEY_EPOCH, DEVICE_IDENTITY_PROOF_KEY_ID, DEVICE_PROOF_HEADER, InMemoryBlobContentProxy, InMemoryCloudBlobStore, InMemoryDeviceDirectory, InMemoryDeviceSequenceStore, InMemoryInboundDedupStore, InMemoryNonceStore, InMemoryPairingCodeStore, InMemoryProofRequestReceiptStore, InMemoryRequestReceiptStore, InMemoryTaskAttemptStore, MAX_DEVICE_PROOF_CLOCK_SKEW_MS, MAX_DEVICE_PROOF_HEADER_BYTES, MAX_DEVICE_PROOF_MAX_LIFETIME_MS, NONCE_SIGNING_DOMAIN, 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 };
2418
2542
  //# sourceMappingURL=index.js.map
2419
2543
  //# sourceMappingURL=index.js.map