@byok-sdk/cloud 0.2.0 → 0.3.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 { 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';
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. */
@@ -457,9 +466,6 @@ function tenantStoresFor(principal, root) {
457
466
  finalizeReservation: (input) => core.quota.finalizeReservation(tenant, input),
458
467
  abortReservation: (reservationId) => core.quota.abortReservation(tenant, reservationId)
459
468
  },
460
- sequence: {
461
- next: (deviceId) => cloud.sequence.next(tenant, deviceId)
462
- },
463
469
  rateLimiter: {
464
470
  consume: (deviceId) => cloud.rateLimiter.consume(tenant, deviceId)
465
471
  }
@@ -1104,6 +1110,33 @@ function activityAppendHandler(deps) {
1104
1110
  }
1105
1111
  };
1106
1112
  }
1113
+ var DEFAULT_SKILL_PACK_PAGE_LIMIT = 50;
1114
+ function skillPackListHandler(deps) {
1115
+ return async (c) => {
1116
+ const device = await authenticateBearer(c.req.header("authorization"), deps.bearer);
1117
+ if (device === void 0) return unauthorized(c);
1118
+ const packs = await deps.skillPacks.list(principalTenant(device), { limit: deps.pageLimit });
1119
+ return c.json({ packs }, 200);
1120
+ };
1121
+ }
1122
+ function skillPackFileHandler(deps) {
1123
+ return async (c) => {
1124
+ const device = await authenticateBearer(c.req.header("authorization"), deps.bearer);
1125
+ if (device === void 0) return unauthorized(c);
1126
+ const name = c.req.param("name");
1127
+ const path = c.req.param("path");
1128
+ if (name === void 0 || path === void 0) return notFound(c);
1129
+ const file = await deps.skillPacks.readFile(principalTenant(device), name, path);
1130
+ if (file === void 0) return notFound(c);
1131
+ return c.json(file, 200);
1132
+ };
1133
+ }
1134
+ function unauthorized(c) {
1135
+ return c.json({ error: "unauthorized" }, 401);
1136
+ }
1137
+ function notFound(c) {
1138
+ return c.json({ error: "skill_pack_not_found" }, 404);
1139
+ }
1107
1140
  var DEFAULT_DEVICE_PROOF_CLOCK_SKEW_MS = 6e4;
1108
1141
  var MAX_DEVICE_PROOF_CLOCK_SKEW_MS = 5 * 6e4;
1109
1142
  var DEFAULT_DEVICE_PROOF_MAX_LIFETIME_MS = 5 * 6e4;
@@ -1268,7 +1301,6 @@ function isTruthCommitError(value) {
1268
1301
  }
1269
1302
 
1270
1303
  // src/handlers/truth.ts
1271
- var DEVICE_PROOF_HEADER = "x-byok-device-proof";
1272
1304
  var DEFAULT_MAX_TRUTH_REQUEST_BYTES = 2 * 1024 * 1024;
1273
1305
  var MAX_DEVICE_PROOF_HEADER_BYTES = 16 * 1024;
1274
1306
  var EMPTY_BODY = new Uint8Array();
@@ -1280,7 +1312,7 @@ function truthManifestHandler(deps) {
1280
1312
  operation: "truth.list",
1281
1313
  resource: "records"
1282
1314
  });
1283
- if (authenticated === void 0) return unauthorized(c);
1315
+ if (authenticated === void 0) return unauthorized2(c);
1284
1316
  const query = manifestQuery(c);
1285
1317
  if (query === void 0) return c.json({ error: "invalid truth manifest query" }, 400);
1286
1318
  const records = await deps.truth.listManifest(principalTenant(authenticated.device), query);
@@ -1295,7 +1327,7 @@ function truthGetHandler(deps) {
1295
1327
  operation: "truth.read",
1296
1328
  resource
1297
1329
  });
1298
- if (authenticated === void 0) return unauthorized(c);
1330
+ if (authenticated === void 0) return unauthorized2(c);
1299
1331
  const selector = recordSelector(c);
1300
1332
  if (selector === void 0) return c.json({ error: "invalid truth record selector" }, 400);
1301
1333
  const record = await deps.truth.getRecord(principalTenant(authenticated.device), selector);
@@ -1322,7 +1354,7 @@ function truthPutHandler(deps) {
1322
1354
  operation: "truth.write",
1323
1355
  resource: rawRecordResource(c)
1324
1356
  });
1325
- if (authenticated === void 0) return unauthorized(c);
1357
+ if (authenticated === void 0) return unauthorized2(c);
1326
1358
  const selector = recordSelector(c);
1327
1359
  if (selector === void 0) return c.json({ error: "invalid truth record selector" }, 400);
1328
1360
  const parsedJson = parseJsonBytes(body);
@@ -1467,7 +1499,7 @@ function parseJsonBytes(bytes) {
1467
1499
  return void 0;
1468
1500
  }
1469
1501
  }
1470
- function unauthorized(c) {
1502
+ function unauthorized2(c) {
1471
1503
  return c.json({ error: "unauthorized" }, 401);
1472
1504
  }
1473
1505
  function truthFailure(c, caught) {
@@ -1535,7 +1567,8 @@ function createByokCloud(options) {
1535
1567
  declaration,
1536
1568
  options.blobContentProxy,
1537
1569
  options.truthCommitter,
1538
- options.truthObjectDownloads
1570
+ options.truthObjectDownloads,
1571
+ options.skillPacks
1539
1572
  );
1540
1573
  const root = { core: options.core, cloud: options.cloud };
1541
1574
  const auth = createAuthPlane({
@@ -1556,16 +1589,16 @@ function createByokCloud(options) {
1556
1589
  capacity: options.activityCapacity ?? DEFAULT_ACTIVITY_CAPACITY,
1557
1590
  ttlMs: options.activityTtlMs ?? DEFAULT_ACTIVITY_TTL_MS
1558
1591
  };
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 }));
1592
+ registry.register({ method: "POST", path: BYOK_PAIR_PATH, class: "public" }, pairHandler({ auth }));
1593
+ registry.register({ method: "POST", path: BYOK_CHALLENGE_PATH, class: "public" }, challengeHandler({ auth }));
1594
+ registry.register({ method: "POST", path: BYOK_TOKEN_PATH, class: "public" }, tokenHandler({ auth }));
1562
1595
  registry.register(
1563
- { method: "GET", path: "/byok/capabilities", class: "public" },
1596
+ { method: "GET", path: BYOK_CAPABILITIES_PATH, class: "public" },
1564
1597
  capabilitiesHandler({ declaration })
1565
1598
  );
1566
1599
  if (declares(declaration, CLOUD_CAPABILITIES.eventsLongPoll)) {
1567
1600
  registry.register(
1568
- { method: "GET", path: "/byok/events", class: "device" },
1601
+ { method: "GET", path: BYOK_EVENTS_PATH, class: "device" },
1569
1602
  eventsHandler({
1570
1603
  ...deviceRouteDeps,
1571
1604
  longPollHoldMs: options.longPollHoldMs ?? DEFAULT_LONG_POLL_HOLD_MS,
@@ -1576,7 +1609,7 @@ function createByokCloud(options) {
1576
1609
  }
1577
1610
  if (declares(declaration, CLOUD_CAPABILITIES.messagesBatch)) {
1578
1611
  registry.register(
1579
- { method: "POST", path: "/byok/messages", class: "device" },
1612
+ { method: "POST", path: BYOK_MESSAGES_PATH, class: "device" },
1580
1613
  messagesHandler({ ...deviceRouteDeps, activityBounds })
1581
1614
  );
1582
1615
  }
@@ -1585,23 +1618,23 @@ function createByokCloud(options) {
1585
1618
  ...deviceRouteDeps,
1586
1619
  pageLimit: options.boardPageLimit ?? DEFAULT_BOARD_PAGE_LIMIT
1587
1620
  };
1588
- registry.register({ method: "GET", path: "/byok/board", class: "device" }, boardListHandler(boardDeps));
1621
+ registry.register({ method: "GET", path: BYOK_BOARD_PATH, class: "device" }, boardListHandler(boardDeps));
1589
1622
  registry.register(
1590
- { method: "POST", path: "/byok/board/:id/claim", class: "device" },
1623
+ { method: "POST", path: BYOK_BOARD_CLAIM_ROUTE, class: "device" },
1591
1624
  boardClaimHandler(deviceRouteDeps)
1592
1625
  );
1593
1626
  registry.register(
1594
- { method: "POST", path: "/byok/board/:id/unclaim", class: "device" },
1627
+ { method: "POST", path: BYOK_BOARD_UNCLAIM_ROUTE, class: "device" },
1595
1628
  boardUnclaimHandler(deviceRouteDeps)
1596
1629
  );
1597
1630
  registry.register(
1598
- { method: "POST", path: "/byok/board/:id/status", class: "device" },
1631
+ { method: "POST", path: BYOK_BOARD_STATUS_ROUTE, class: "device" },
1599
1632
  boardStatusHandler(deviceRouteDeps)
1600
1633
  );
1601
1634
  }
1602
1635
  if (declares(declaration, CLOUD_CAPABILITIES.boardSse)) {
1603
1636
  registry.register(
1604
- { method: "GET", path: "/byok/board/stream", class: "device" },
1637
+ { method: "GET", path: BYOK_BOARD_STREAM_PATH, class: "device" },
1605
1638
  boardStreamHandler({
1606
1639
  ...deviceRouteDeps,
1607
1640
  pageLimit: options.boardPageLimit ?? DEFAULT_BOARD_PAGE_LIMIT,
@@ -1613,7 +1646,7 @@ function createByokCloud(options) {
1613
1646
  }
1614
1647
  if (declares(declaration, CLOUD_CAPABILITIES.presenceHints)) {
1615
1648
  registry.register(
1616
- { method: "PUT", path: "/byok/presence", class: "device" },
1649
+ { method: "PUT", path: BYOK_PRESENCE_PATH, class: "device" },
1617
1650
  presencePublishHandler({
1618
1651
  ...deviceRouteDeps,
1619
1652
  ttlMs: options.presenceTtlMs ?? DEFAULT_PRESENCE_TTL_MS,
@@ -1624,7 +1657,7 @@ function createByokCloud(options) {
1624
1657
  }
1625
1658
  if (declares(declaration, CLOUD_CAPABILITIES.activityTail)) {
1626
1659
  registry.register(
1627
- { method: "POST", path: "/byok/activity", class: "device" },
1660
+ { method: "POST", path: BYOK_ACTIVITY_PATH, class: "device" },
1628
1661
  activityAppendHandler({ ...deviceRouteDeps, bounds: activityBounds })
1629
1662
  );
1630
1663
  }
@@ -1642,36 +1675,52 @@ function createByokCloud(options) {
1642
1675
  maxRequestBytes: options.maxTruthRequestBytes ?? DEFAULT_MAX_TRUTH_REQUEST_BYTES
1643
1676
  };
1644
1677
  registry.register(
1645
- { method: "GET", path: "/byok/records", class: "proof" },
1678
+ { method: "GET", path: BYOK_RECORDS_PATH, class: "proof" },
1646
1679
  truthManifestHandler(truthDeps)
1647
1680
  );
1648
1681
  registry.register(
1649
- { method: "GET", path: "/byok/records/:kind/:key", class: "proof" },
1682
+ { method: "GET", path: BYOK_RECORD_ROUTE, class: "proof" },
1650
1683
  truthGetHandler(truthDeps)
1651
1684
  );
1652
1685
  registry.register(
1653
- { method: "PUT", path: "/byok/records/:kind/:key", class: "proof" },
1686
+ { method: "PUT", path: BYOK_RECORD_ROUTE, class: "proof" },
1654
1687
  truthPutHandler(truthDeps)
1655
1688
  );
1656
1689
  }
1690
+ const skillPacks = options.skillPacks;
1691
+ if (skillPacks !== void 0 && declares(declaration, CLOUD_CAPABILITIES.skillPacks)) {
1692
+ const skillPackDeps = {
1693
+ bearer: deviceRouteDeps.bearer,
1694
+ skillPacks,
1695
+ pageLimit: options.skillPackPageLimit ?? DEFAULT_SKILL_PACK_PAGE_LIMIT
1696
+ };
1697
+ registry.register(
1698
+ { method: "GET", path: BYOK_SKILL_PACKS_PATH, class: "device" },
1699
+ skillPackListHandler(skillPackDeps)
1700
+ );
1701
+ registry.register(
1702
+ { method: "GET", path: BYOK_SKILL_PACK_FILE_ROUTE, class: "device" },
1703
+ skillPackFileHandler(skillPackDeps)
1704
+ );
1705
+ }
1657
1706
  if (declares(declaration, CLOUD_CAPABILITIES.blobsPresigned)) {
1658
1707
  const blobDeps = {
1659
1708
  ...deviceRouteDeps,
1660
1709
  maxBlobSizeBytes: options.maxBlobSizeBytes ?? DEFAULT_MAX_BLOB_SIZE_BYTES
1661
1710
  };
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));
1711
+ registry.register({ method: "POST", path: BYOK_BLOBS_PATH, class: "device" }, createBlobHandler(blobDeps));
1712
+ registry.register({ method: "POST", path: BYOK_BLOB_FINALIZE_ROUTE, class: "device" }, finalizeBlobHandler(blobDeps));
1713
+ registry.register({ method: "GET", path: BYOK_BLOB_URL_ROUTE, class: "device" }, blobDownloadUrlHandler(blobDeps));
1665
1714
  }
1666
1715
  const contentProxy = options.blobContentProxy;
1667
1716
  if (contentProxy !== void 0 && declares(declaration, CLOUD_CAPABILITIES.blobsContentProxy)) {
1668
1717
  const contentDeps = { contentProxy };
1669
1718
  registry.register(
1670
- { method: "PUT", path: "/byok/blobs/:id/content", class: "presigned" },
1719
+ { method: "PUT", path: BYOK_BLOB_CONTENT_ROUTE, class: "presigned" },
1671
1720
  blobUploadContentHandler(contentDeps)
1672
1721
  );
1673
1722
  registry.register(
1674
- { method: "GET", path: "/byok/blobs/:id/content", class: "presigned" },
1723
+ { method: "GET", path: BYOK_BLOB_CONTENT_ROUTE, class: "presigned" },
1675
1724
  blobDownloadContentHandler(contentDeps)
1676
1725
  );
1677
1726
  }
@@ -1679,6 +1728,34 @@ function createByokCloud(options) {
1679
1728
  function controlPlane(tenant) {
1680
1729
  return { kind: "control-plane", tenantId: tenant, operatorId };
1681
1730
  }
1731
+ async function enqueueTaskEnvelope(tenant, deviceId, requestedTaskId, buildEnvelope) {
1732
+ const stores = tenantStoresFor(controlPlane(tenant), root);
1733
+ const taskId = requestedTaskId ?? `task_${options.crypto.randomUuid()}`;
1734
+ const messageId = options.crypto.randomUuid();
1735
+ const message = await stores.mailbox.append({
1736
+ deviceId,
1737
+ messageId,
1738
+ materialize: async (seq) => {
1739
+ const envelope2 = buildEnvelope(taskId, seq, messageId);
1740
+ const body = encodeEnvelope(envelope2);
1741
+ const bytes = new TextEncoder().encode(body);
1742
+ return {
1743
+ body,
1744
+ bodyHash: contentHash(await options.crypto.sha256(bytes)),
1745
+ byteSize: BigInt(bytes.length)
1746
+ };
1747
+ }
1748
+ });
1749
+ const envelope = decodeEnvelope(message.body);
1750
+ if (envelope.seq !== message.seq) {
1751
+ throw new ByokCloudError(
1752
+ "mailbox_seq_mismatch",
1753
+ `Mailbox stored offer seq ${String(envelope.seq)} at row ${message.seq}; the daemon's redelivery cursor would be wrong.`
1754
+ );
1755
+ }
1756
+ const attempt = await stores.tasks.open({ taskId, deviceId });
1757
+ return { taskId, seq: message.seq, envelope, attempt };
1758
+ }
1682
1759
  return {
1683
1760
  fetch: registry.fetch,
1684
1761
  routes: registry.routes,
@@ -1688,27 +1765,20 @@ function createByokCloud(options) {
1688
1765
  return auth.createPairingCode(tenant, input);
1689
1766
  },
1690
1767
  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({
1768
+ return enqueueTaskEnvelope(
1769
+ tenant,
1698
1770
  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 };
1771
+ input.taskId,
1772
+ (taskId, seq, messageId) => createEnvelope("task.offer", input.payload, { id: messageId, taskId, seq })
1773
+ );
1774
+ },
1775
+ async enqueueToolsetOffer(tenant, deviceId, input) {
1776
+ return enqueueTaskEnvelope(
1777
+ tenant,
1778
+ deviceId,
1779
+ input.taskId,
1780
+ (taskId, seq, messageId) => createEnvelope("task.offer_with_toolsets", input.payload, { id: messageId, taskId, seq })
1781
+ );
1712
1782
  },
1713
1783
  readTaskAttempt(tenant, taskId) {
1714
1784
  return tenantStoresFor(controlPlane(tenant), root).tasks.get(taskId);
@@ -1747,7 +1817,7 @@ function createByokCloud(options) {
1747
1817
  }
1748
1818
  };
1749
1819
  }
1750
- function assertNoOverDeclaration(declaration, contentProxy, truthCommitter, truthObjectDownloads) {
1820
+ function assertNoOverDeclaration(declaration, contentProxy, truthCommitter, truthObjectDownloads, skillPacks) {
1751
1821
  if (declares(declaration, CLOUD_CAPABILITIES.boardSse) && !declares(declaration, CLOUD_CAPABILITIES.boardCoordination)) {
1752
1822
  throw new ByokCloudError(
1753
1823
  "capability_over_declared",
@@ -1772,6 +1842,12 @@ function assertNoOverDeclaration(declaration, contentProxy, truthCommitter, trut
1772
1842
  `This deployment declares ${CLOUD_CAPABILITIES.truthRecords} but was given no content-hash keyed TruthObjectDownloads authority.`
1773
1843
  );
1774
1844
  }
1845
+ if (skillPacks === void 0 && declares(declaration, CLOUD_CAPABILITIES.skillPacks)) {
1846
+ throw new ByokCloudError(
1847
+ "capability_over_declared",
1848
+ `This deployment declares ${CLOUD_CAPABILITIES.skillPacks} but was given no SkillPackStore, so both /byok/skill-packs routes would be published and unserved.`
1849
+ );
1850
+ }
1775
1851
  }
1776
1852
  function parseDeclaration(declaration) {
1777
1853
  try {
@@ -1809,7 +1885,7 @@ var InMemoryBlobRegistry = class {
1809
1885
  async signUrl(blobId, action) {
1810
1886
  const exp = this.clock.now().getTime() + this.urlTtlMs;
1811
1887
  const sig = await this.computeSig(blobId, action, exp);
1812
- return `/byok/blobs/${blobId}/content?sig=${sig}&exp=${exp}`;
1888
+ return `${byokBlobContentPath(blobId)}?sig=${sig}&exp=${exp}`;
1813
1889
  }
1814
1890
  computeSig(blobId, action, exp) {
1815
1891
  return this.crypto.hmacSha256(this.secret, `${blobId}:${action}:${exp}`);
@@ -1966,15 +2042,6 @@ var InMemoryDeviceDirectory = class {
1966
2042
  return key === void 0 ? void 0 : this.#byTenant.get(key);
1967
2043
  }
1968
2044
  };
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
2045
  var DEDUP_RING_CAPACITY = 1024;
1979
2046
  var InMemoryInboundDedupStore = class {
1980
2047
  #rings = /* @__PURE__ */ new Map();
@@ -2173,7 +2240,6 @@ function createInMemoryCloudStores(clock, crypto, objects) {
2173
2240
  tasks: new InMemoryTaskAttemptStore(clock),
2174
2241
  receipts: new InMemoryRequestReceiptStore(clock),
2175
2242
  proofReceipts: new InMemoryProofRequestReceiptStore(clock),
2176
- sequence: new InMemoryDeviceSequenceStore(),
2177
2243
  blobs: blobs.blobs,
2178
2244
  rateLimiter: new AllowAllRateLimiter()
2179
2245
  },
@@ -2225,7 +2291,9 @@ function createInMemoryByokCloud(options = {}) {
2225
2291
  ...options.activityMaxBytes === void 0 ? {} : { activityMaxBytes: options.activityMaxBytes },
2226
2292
  ...options.activityCapacity === void 0 ? {} : { activityCapacity: options.activityCapacity },
2227
2293
  ...options.activityTtlMs === void 0 ? {} : { activityTtlMs: options.activityTtlMs },
2228
- ...options.maxTruthRequestBytes === void 0 ? {} : { maxTruthRequestBytes: options.maxTruthRequestBytes }
2294
+ ...options.maxTruthRequestBytes === void 0 ? {} : { maxTruthRequestBytes: options.maxTruthRequestBytes },
2295
+ ...options.skillPacks === void 0 ? {} : { skillPacks: options.skillPacks },
2296
+ ...options.skillPackPageLimit === void 0 ? {} : { skillPackPageLimit: options.skillPackPageLimit }
2229
2297
  });
2230
2298
  return { cloud, core, stores, blobContentProxy, clock, crypto };
2231
2299
  }
@@ -2278,7 +2346,7 @@ var BoardFeedClient = class {
2278
2346
  return this.mode === "sse" ? this.#readSseOnce(afterSeq, signal) : this.#readPollOnce(afterSeq, signal);
2279
2347
  }
2280
2348
  async #readPollOnce(afterSeq, signal) {
2281
- const url = new URL("/byok/board", this.#base);
2349
+ const url = new URL(BYOK_BOARD_PATH, this.#base);
2282
2350
  url.searchParams.set("since", String(afterSeq));
2283
2351
  const response = await this.#fetch(url, {
2284
2352
  headers: { authorization: `Bearer ${this.#accessToken}` },
@@ -2288,7 +2356,7 @@ var BoardFeedClient = class {
2288
2356
  return { type: "poll", page: BoardFeedPageSchema.parse(await response.json()) };
2289
2357
  }
2290
2358
  async #readSseOnce(afterSeq, signal) {
2291
- const url = new URL("/byok/board/stream", this.#base);
2359
+ const url = new URL(BYOK_BOARD_STREAM_PATH, this.#base);
2292
2360
  const response = await this.#fetch(url, {
2293
2361
  headers: {
2294
2362
  accept: "text/event-stream",
@@ -2380,7 +2448,6 @@ var CLOUD_STORE_NAMES = [
2380
2448
  "tasks",
2381
2449
  "receipts",
2382
2450
  "proofReceipts",
2383
- "sequence",
2384
2451
  "blobs",
2385
2452
  "rateLimiter"
2386
2453
  ];
@@ -2394,7 +2461,6 @@ var CLOUD_PORT_METHODS = {
2394
2461
  tasks: ["open", "get", "claim", "recordStatus"],
2395
2462
  receipts: ["record", "get"],
2396
2463
  proofReceipts: ["record", "get"],
2397
- sequence: ["next"],
2398
2464
  // Three methods, not six: the byte-proxy trio moved to `BlobContentProxy`,
2399
2465
  // which is a composition input rather than a port and therefore has no row
2400
2466
  // in this table (docs/researches/s4a-dataplane-design.md §6).
@@ -2409,11 +2475,10 @@ var CLOUD_PORT_INTERFACES = {
2409
2475
  tasks: "TaskAttemptStore",
2410
2476
  receipts: "RequestReceiptStore",
2411
2477
  proofReceipts: "ProofRequestReceiptStore",
2412
- sequence: "DeviceSequenceStore",
2413
2478
  blobs: "CloudBlobStore",
2414
2479
  rateLimiter: "InboundRateLimiter"
2415
2480
  };
2416
2481
 
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 };
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 };
2418
2483
  //# sourceMappingURL=index.js.map
2419
2484
  //# sourceMappingURL=index.js.map