@byok-sdk/cloud 0.14.0 → 0.16.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 +87 -27
- package/dist/index.js.map +1 -1
- package/dist/offer-identity.d.ts +4 -0
- package/dist/stores/in-memory/device-directory.d.ts +2 -0
- package/dist/stores/in-memory/task-attempts.d.ts +1 -0
- package/dist/stores/ports.d.ts +5 -0
- package/dist/tenant-stores.d.ts +3 -0
- package/dist/terminal-result.d.ts +4 -1
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -1,11 +1,30 @@
|
|
|
1
1
|
import { BOARD_STATUSES, CapabilityDeclarationSchema, NONCE_SIGNING_DOMAIN, hasCapability, isTenantId, tenantId, principalTenant, parseDeviceProofEnvelope, deviceProofSigningInput, contentHash, parseCapabilityDeclaration, ByokCoreError, tenantKey, PRESENCE_LEVELS, createInMemoryCoreStores, authenticateDeviceAssertion, assertCapability, 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, PROTOCOL_VERSION, DAEMON_TO_SERVER_TYPES, AGENT_EGRESS_RELIABLE_ACK_CAPABILITY, encodeEnvelope, decodeEnvelope, AgentHomeProjectionCompletionRequestSchema, BYOK_PAIR_PATH, BYOK_CHALLENGE_PATH, BYOK_TOKEN_PATH, BYOK_CAPABILITIES_PATH, BYOK_AGENT_MEMORY_PROJECTIONS_PATH, BYOK_EVENTS_PATH, BYOK_MESSAGES_PATH, BYOK_AGENT_HOME_PROJECTION_COMPLETION_ROUTE, 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, AgentHomeProjectionPayloadSchema, AgentContentReadPayloadSchema, AGENT_EGRESS_POLICY_CAPABILITY, AGENT_EGRESS_FRESH_SESSION_CAPABILITY, AGENT_MESSAGE_EGRESS_CAPABILITY, TERMINAL_PROJECTION_SELECTION_CAPABILITY, TaskOfferForAgentWithEgressFreshPayloadSchema, AgentMessageServerContextSchema, TaskOfferForAgentWithEgressPayloadSchema, TaskOfferForAgentPayloadSchema, AgentMemoryProjectionMutationSchema, AgentMemoryProjectionReceiptSchema, AGENT_MEMORY_PROJECTION_MAX_ORDERING_VALUE, AgentMemoryProjectionEraseResultSchema, STRICT_AGENT_ONLY_CAPABILITY, AGENT_CONTENT_ARTIFACT_READ_CAPABILITY, AGENT_CONTENT_TRANSCRIPT_READ_CAPABILITY, AGENT_CONTENT_WORKSPACE_READ_CAPABILITY, AGENT_HOME_PROJECTION_CAPABILITY, AgentRefSchema, PairRequestSchema, PairResponseSchema, ChallengeRequestSchema, TokenRequestSchema, AgentMemoryProjectionCommitRequestSchema, agentMemoryProjectionBase64UrlByteLength, MessagesSendRequestSchema, PresencePublishRequestSchema, CreateBlobRequestSchema, byokBlobContentPath, AgentEgressAckPayloadSchema, AgentContentReceiptPayloadSchema } from '@byok-sdk/protocol';
|
|
3
|
+
import { AgentEventOrUnknownSchema, PROTOCOL_VERSION, DAEMON_TO_SERVER_TYPES, AGENT_EGRESS_RELIABLE_ACK_CAPABILITY, encodeEnvelope, decodeEnvelope, AgentHomeProjectionCompletionRequestSchema, BYOK_PAIR_PATH, BYOK_CHALLENGE_PATH, BYOK_TOKEN_PATH, BYOK_CAPABILITIES_PATH, BYOK_AGENT_MEMORY_PROJECTIONS_PATH, BYOK_EVENTS_PATH, BYOK_MESSAGES_PATH, BYOK_AGENT_HOME_PROJECTION_COMPLETION_ROUTE, 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, AgentHomeProjectionPayloadSchema, AgentContentReadPayloadSchema, AGENT_EGRESS_POLICY_CAPABILITY, AGENT_EGRESS_FRESH_SESSION_CAPABILITY, AGENT_MESSAGE_EGRESS_CAPABILITY, TERMINAL_PROJECTION_SELECTION_CAPABILITY, TaskOfferForAgentWithEgressFreshPayloadSchema, AgentMessageServerContextSchema, TaskOfferForAgentWithEgressPayloadSchema, TaskOfferForAgentPayloadSchema, AgentMemoryProjectionMutationSchema, AgentMemoryProjectionReceiptSchema, AGENT_MEMORY_PROJECTION_MAX_ORDERING_VALUE, AgentMemoryProjectionEraseResultSchema, HarnessIdSchema, STRICT_AGENT_ONLY_CAPABILITY, AGENT_CONTENT_ARTIFACT_READ_CAPABILITY, AGENT_CONTENT_TRANSCRIPT_READ_CAPABILITY, AGENT_CONTENT_WORKSPACE_READ_CAPABILITY, AGENT_HOME_PROJECTION_CAPABILITY, AgentRefSchema, PairRequestSchema, PairResponseSchema, ChallengeRequestSchema, TokenRequestSchema, AgentMemoryProjectionCommitRequestSchema, agentMemoryProjectionBase64UrlByteLength, MAILBOX_READ_AHEAD_MAX_SEQS, MAILBOX_READ_AHEAD_CAPABILITY, MessagesSendRequestSchema, PresencePublishRequestSchema, CreateBlobRequestSchema, byokBlobContentPath, AgentEgressAckPayloadSchema, AgentContentReceiptPayloadSchema } from '@byok-sdk/protocol';
|
|
4
4
|
import { z } from 'zod';
|
|
5
5
|
import { Hono } from 'hono';
|
|
6
6
|
|
|
7
7
|
// src/index.ts
|
|
8
8
|
|
|
9
|
+
// src/offer-identity.ts
|
|
10
|
+
function uuidFromSha256(value) {
|
|
11
|
+
const match = /^sha256:([0-9a-f]{64})$/.exec(value);
|
|
12
|
+
if (match === null) throw new Error("CloudCrypto.sha256 must return canonical sha256 hex");
|
|
13
|
+
const digest = match[1];
|
|
14
|
+
if (digest === void 0) throw new Error("CloudCrypto.sha256 must return canonical sha256 hex");
|
|
15
|
+
const hex = digest.slice(0, 32).split("");
|
|
16
|
+
hex[12] = "8";
|
|
17
|
+
hex[16] = ["8", "9", "a", "b"][Number.parseInt(hex[16], 16) & 3];
|
|
18
|
+
return `${hex.slice(0, 8).join("")}-${hex.slice(8, 12).join("")}-${hex.slice(12, 16).join("")}-${hex.slice(16, 20).join("")}-${hex.slice(20).join("")}`;
|
|
19
|
+
}
|
|
20
|
+
async function taskOfferMessageId(tenant, taskId, deviceId, agentRef, sha256) {
|
|
21
|
+
return uuidFromSha256(await sha256(new TextEncoder().encode(JSON.stringify({ domain: "byok:task-offer", tenant, taskId, deviceId, agentRef }))));
|
|
22
|
+
}
|
|
23
|
+
async function hashOfferBytes(bytes) {
|
|
24
|
+
const hash = new Uint8Array(await crypto.subtle.digest("SHA-256", new Uint8Array(bytes)));
|
|
25
|
+
return `sha256:${Array.from(hash, (byte) => byte.toString(16).padStart(2, "0")).join("")}`;
|
|
26
|
+
}
|
|
27
|
+
|
|
9
28
|
// src/approval-control.ts
|
|
10
29
|
var StaleApprovalError = class extends Error {
|
|
11
30
|
constructor(taskId, requestedApprovalId, currentApprovalId) {
|
|
@@ -668,7 +687,8 @@ function tenantStoresFor(principal, root) {
|
|
|
668
687
|
revoke: (deviceId) => cloud.devices.revoke(tenant, deviceId),
|
|
669
688
|
recordCapabilities: (input) => principal.kind === "device" ? cloud.devices.recordCapabilities(tenant, {
|
|
670
689
|
deviceId: principal.deviceId,
|
|
671
|
-
capabilities: input.capabilities
|
|
690
|
+
capabilities: input.capabilities,
|
|
691
|
+
...input.harnesses === void 0 ? {} : { harnesses: input.harnesses }
|
|
672
692
|
}) : Promise.resolve(void 0),
|
|
673
693
|
readiness: () => cloud.devices.readiness(tenant, core.presence)
|
|
674
694
|
},
|
|
@@ -1029,6 +1049,8 @@ function tokenHandler(deps) {
|
|
|
1029
1049
|
};
|
|
1030
1050
|
}
|
|
1031
1051
|
var CLOUD_PROTOCOL_CAPABILITIES = [
|
|
1052
|
+
"custom-harness",
|
|
1053
|
+
MAILBOX_READ_AHEAD_CAPABILITY,
|
|
1032
1054
|
"result-document",
|
|
1033
1055
|
// The kernel's inbound handles `task.approval_resolved` on both the plain
|
|
1034
1056
|
// and the approval-timeline paths (`packages/cloud/src/inbound.ts:576,610`).
|
|
@@ -1065,7 +1087,13 @@ function eventsHandler(deps) {
|
|
|
1065
1087
|
}
|
|
1066
1088
|
cursor = parsedCursor;
|
|
1067
1089
|
}
|
|
1090
|
+
const afterRaw = c.req.query("afterSeq");
|
|
1091
|
+
const afterSeq = afterRaw === void 0 ? cursor : Number(afterRaw);
|
|
1092
|
+
if (!Number.isSafeInteger(afterSeq) || afterSeq < cursor || afterSeq - cursor > MAILBOX_READ_AHEAD_MAX_SEQS) {
|
|
1093
|
+
return c.json({ error: "invalid read-ahead offset" }, 400);
|
|
1094
|
+
}
|
|
1068
1095
|
const acked = await stores.mailbox.readCursor(device.deviceId);
|
|
1096
|
+
if (afterRaw !== void 0 && afterSeq > acked.deliveredSeq) return c.json({ error: "read offset exceeds delivered watermark" }, 409);
|
|
1069
1097
|
if (cursor > acked.ackedSeq) {
|
|
1070
1098
|
try {
|
|
1071
1099
|
await stores.mailbox.advanceCursor({ deviceId: device.deviceId, ackedSeq: cursor });
|
|
@@ -1078,7 +1106,7 @@ function eventsHandler(deps) {
|
|
|
1078
1106
|
}
|
|
1079
1107
|
const attempts = Math.max(1, Math.ceil(deps.longPollHoldMs / deps.longPollIntervalMs));
|
|
1080
1108
|
for (let attempt = 0; attempt < attempts; attempt += 1) {
|
|
1081
|
-
let scanCursor =
|
|
1109
|
+
let scanCursor = afterSeq;
|
|
1082
1110
|
while (true) {
|
|
1083
1111
|
const page = await stores.mailbox.readAfter({
|
|
1084
1112
|
deviceId: device.deviceId,
|
|
@@ -1122,7 +1150,7 @@ function eventsHandler(deps) {
|
|
|
1122
1150
|
}
|
|
1123
1151
|
const response = {
|
|
1124
1152
|
events: [],
|
|
1125
|
-
cursor,
|
|
1153
|
+
cursor: afterSeq,
|
|
1126
1154
|
capabilities: CLOUD_PROTOCOL_CAPABILITIES
|
|
1127
1155
|
};
|
|
1128
1156
|
return c.json(response, 200);
|
|
@@ -1385,6 +1413,7 @@ function agentRefEchoRequired(envelope) {
|
|
|
1385
1413
|
return envelope.type === "task.claim" || envelope.type === "task.decline" || envelope.type === "task.complete" || envelope.type === "task.fail" || envelope.type === "task.cancelled";
|
|
1386
1414
|
}
|
|
1387
1415
|
async function handleInboundEnvelope(stores, deviceId, envelope, activityBounds = DEFAULT_ACTIVITY_BOUNDS, agentMessageConsume, observer) {
|
|
1416
|
+
if (envelope.v !== PROTOCOL_VERSION) return "rejected";
|
|
1388
1417
|
const outcome = await applyInboundGate(
|
|
1389
1418
|
stores,
|
|
1390
1419
|
deviceId,
|
|
@@ -1404,12 +1433,13 @@ async function applyInboundGate(stores, deviceId, envelope, activityBounds, agen
|
|
|
1404
1433
|
if (!await stores.rateLimiter.consume(deviceId)) return "rate_limited";
|
|
1405
1434
|
if (envelope.type === "conn.hello") {
|
|
1406
1435
|
if (envelope.payload.deviceId !== deviceId) return "rejected";
|
|
1436
|
+
if (envelope.payload.harnesses !== void 0 && !envelope.payload.capabilities.includes("custom-harness")) return "rejected";
|
|
1407
1437
|
if (!envelope.payload.protocolVersions.includes(PROTOCOL_VERSION)) return "rejected";
|
|
1408
1438
|
const device = await stores.devices.get(deviceId);
|
|
1409
1439
|
if (device === void 0 || device.revoked || device.productId !== envelope.payload.productId) {
|
|
1410
1440
|
return "rejected";
|
|
1411
1441
|
}
|
|
1412
|
-
if (await stores.devices.recordCapabilities({ capabilities: envelope.payload.capabilities }) === void 0) {
|
|
1442
|
+
if (await stores.devices.recordCapabilities({ capabilities: envelope.payload.capabilities, ...envelope.payload.harnesses === void 0 ? {} : { harnesses: envelope.payload.harnesses } }) === void 0) {
|
|
1413
1443
|
return "rejected";
|
|
1414
1444
|
}
|
|
1415
1445
|
return completeInboundEnvelope(stores, deviceId, envelope.id, void 0);
|
|
@@ -1456,6 +1486,34 @@ async function applyInboundGate(stores, deviceId, envelope, activityBounds, agen
|
|
|
1456
1486
|
if (attempt === void 0 || attempt.deviceId !== deviceId) return "rejected";
|
|
1457
1487
|
if (attempt?.ownerDeviceId !== void 0 && attempt.ownerDeviceId !== deviceId) return "rejected";
|
|
1458
1488
|
if (envelope.type === "task.claim" && envelope.payload.deviceId !== deviceId) return "rejected";
|
|
1489
|
+
if (envelope.type === "task.claim") {
|
|
1490
|
+
const harnessId = envelope.payload.harnessId;
|
|
1491
|
+
if (harnessId !== void 0 && envelope.payload.runtime !== void 0) return "rejected";
|
|
1492
|
+
if (attempt.ownerDeviceId !== void 0 && (harnessId !== void 0 || attempt.claimedHarnessId !== void 0) && harnessId !== attempt.claimedHarnessId) return "rejected";
|
|
1493
|
+
const device = await stores.devices.get(deviceId);
|
|
1494
|
+
if (harnessId !== void 0 && (!device?.capabilities?.includes("custom-harness") || !device.harnesses?.some((harness) => harness.id === harnessId))) return "rejected";
|
|
1495
|
+
const messageId = await taskOfferMessageId(stores.tenant, taskId, deviceId, attempt.agentRef, hashOfferBytes);
|
|
1496
|
+
const offer = await stores.receipts.get(`task-offer:${messageId}`);
|
|
1497
|
+
if (offer !== void 0) {
|
|
1498
|
+
const sealed = JSON.parse(offer.body);
|
|
1499
|
+
if (harnessId !== void 0 && (sealed.payload.runtime !== void 0 || sealed.payload.dispatchSelection !== void 0)) return "rejected";
|
|
1500
|
+
if (sealed.payload.harnessId !== void 0 && sealed.payload.harnessId !== harnessId) return "rejected";
|
|
1501
|
+
} else if (harnessId !== void 0) return "rejected";
|
|
1502
|
+
}
|
|
1503
|
+
let unclaimedInterruption = false;
|
|
1504
|
+
if (envelope.type === "task.fail" && envelope.payload.recovery !== void 0) {
|
|
1505
|
+
if (envelope.payload.reason !== "daemon_interrupted" || envelope.payload.retryable !== false) return "rejected";
|
|
1506
|
+
const messageId = await taskOfferMessageId(stores.tenant, taskId, deviceId, attempt.agentRef, hashOfferBytes);
|
|
1507
|
+
if (envelope.payload.recovery.offerId !== messageId) return "rejected";
|
|
1508
|
+
const offer = await stores.receipts.get(`task-offer:${messageId}`);
|
|
1509
|
+
if (offer === void 0) return "rejected";
|
|
1510
|
+
const sealed = JSON.parse(offer.body);
|
|
1511
|
+
const harnessId = envelope.payload.harnessId;
|
|
1512
|
+
if (sealed.payload.harnessId !== void 0 && sealed.payload.harnessId !== harnessId) return "rejected";
|
|
1513
|
+
if (harnessId !== void 0 && (sealed.payload.runtime !== void 0 || sealed.payload.dispatchSelection !== void 0)) return "rejected";
|
|
1514
|
+
unclaimedInterruption = attempt.ownerDeviceId === void 0 && attempt.claimedHarnessId === void 0;
|
|
1515
|
+
}
|
|
1516
|
+
if ((envelope.type === "task.complete" || envelope.type === "task.fail" || envelope.type === "task.cancelled") && !unclaimedInterruption && envelope.payload.harnessId !== attempt.claimedHarnessId) return "rejected";
|
|
1459
1517
|
if (attempt?.agentRef !== void 0 && agentRefEchoRequired(envelope) && !sameAgentRef(attempt.agentRef, inboundAgentRef(envelope))) {
|
|
1460
1518
|
return "rejected";
|
|
1461
1519
|
}
|
|
@@ -1487,7 +1545,8 @@ async function applyInboundGate(stores, deviceId, envelope, activityBounds, agen
|
|
|
1487
1545
|
throw caught;
|
|
1488
1546
|
}
|
|
1489
1547
|
}
|
|
1490
|
-
await applyLifecycle(stores, deviceId, taskId, envelope, activityBounds, attempt?.agentRef);
|
|
1548
|
+
const lifecycleOutcome = await applyLifecycle(stores, deviceId, taskId, envelope, activityBounds, attempt?.agentRef);
|
|
1549
|
+
if (lifecycleOutcome === "rejected") return "rejected";
|
|
1491
1550
|
return completeInboundEnvelope(stores, deviceId, envelope.id, attempt?.agentRef);
|
|
1492
1551
|
}
|
|
1493
1552
|
async function completeInboundEnvelope(stores, deviceId, envelopeId, agentRef) {
|
|
@@ -1496,14 +1555,17 @@ async function completeInboundEnvelope(stores, deviceId, envelopeId, agentRef) {
|
|
|
1496
1555
|
}
|
|
1497
1556
|
async function applyLifecycle(stores, deviceId, taskId, envelope, activityBounds, persistedAgentRef) {
|
|
1498
1557
|
switch (envelope.type) {
|
|
1499
|
-
case "task.claim":
|
|
1500
|
-
await stores.tasks.claim({
|
|
1558
|
+
case "task.claim": {
|
|
1559
|
+
const claimed = await stores.tasks.claim({
|
|
1501
1560
|
taskId,
|
|
1502
1561
|
deviceId,
|
|
1503
1562
|
...envelope.payload.runtime === void 0 ? {} : { runtime: envelope.payload.runtime },
|
|
1563
|
+
...envelope.payload.harnessId === void 0 ? {} : { harnessId: envelope.payload.harnessId },
|
|
1504
1564
|
...envelope.payload.capabilities === void 0 ? {} : { capabilities: envelope.payload.capabilities }
|
|
1505
1565
|
});
|
|
1566
|
+
if (claimed === void 0 || claimed.ownerDeviceId !== deviceId || claimed.claimedRuntime !== envelope.payload.runtime || claimed.claimedHarnessId !== envelope.payload.harnessId) return "rejected";
|
|
1506
1567
|
return;
|
|
1568
|
+
}
|
|
1507
1569
|
case "task.started":
|
|
1508
1570
|
await stores.tasks.recordStatus({
|
|
1509
1571
|
taskId,
|
|
@@ -2620,6 +2682,7 @@ function projectTerminalResult(taskId, receipt) {
|
|
|
2620
2682
|
sessionRef: envelope.payload.sessionRef,
|
|
2621
2683
|
...envelope.payload.artifactRefs !== void 0 ? { artifactRefs: envelope.payload.artifactRefs } : {},
|
|
2622
2684
|
...envelope.payload.document !== void 0 ? { document: envelope.payload.document } : {},
|
|
2685
|
+
...envelope.payload.harnessId === void 0 ? {} : { harnessId: envelope.payload.harnessId },
|
|
2623
2686
|
...envelope.payload.usage !== void 0 ? { usage: envelope.payload.usage } : {},
|
|
2624
2687
|
recordedAt: receipt.recordedAt
|
|
2625
2688
|
};
|
|
@@ -2628,9 +2691,11 @@ function projectTerminalResult(taskId, receipt) {
|
|
|
2628
2691
|
taskId,
|
|
2629
2692
|
state: "failed",
|
|
2630
2693
|
...envelope.payload.agentRef === void 0 ? {} : { agentRef: envelope.payload.agentRef },
|
|
2694
|
+
...envelope.payload.recovery === void 0 ? {} : { recovery: envelope.payload.recovery },
|
|
2631
2695
|
reason: envelope.payload.reason,
|
|
2632
2696
|
terminalCause: envelope.payload.reason,
|
|
2633
2697
|
...envelope.payload.retryable !== void 0 ? { retryable: envelope.payload.retryable } : {},
|
|
2698
|
+
...envelope.payload.harnessId === void 0 ? {} : { harnessId: envelope.payload.harnessId },
|
|
2634
2699
|
...envelope.payload.usage !== void 0 ? { usage: envelope.payload.usage } : {},
|
|
2635
2700
|
recordedAt: receipt.recordedAt
|
|
2636
2701
|
};
|
|
@@ -2656,6 +2721,7 @@ function projectTerminalResult(taskId, receipt) {
|
|
|
2656
2721
|
...envelope.payload.agentRef === void 0 ? {} : { agentRef: envelope.payload.agentRef },
|
|
2657
2722
|
...envelope.payload.reason !== void 0 ? { reason: envelope.payload.reason } : {},
|
|
2658
2723
|
...envelope.payload.reason === void 0 ? {} : { terminalCause: envelope.payload.reason },
|
|
2724
|
+
...envelope.payload.harnessId === void 0 ? {} : { harnessId: envelope.payload.harnessId },
|
|
2659
2725
|
...envelope.payload.usage !== void 0 ? { usage: envelope.payload.usage } : {},
|
|
2660
2726
|
recordedAt: receipt.recordedAt
|
|
2661
2727
|
};
|
|
@@ -2679,16 +2745,6 @@ function isTerminalAttemptStatus(status) {
|
|
|
2679
2745
|
function sameAgentRef2(expected, actual) {
|
|
2680
2746
|
return actual?.agentId === expected.agentId && actual.profileRevision === expected.profileRevision;
|
|
2681
2747
|
}
|
|
2682
|
-
function uuidFromSha256(value) {
|
|
2683
|
-
const match = /^sha256:([0-9a-f]{64})$/.exec(value);
|
|
2684
|
-
if (match === null) throw new Error("CloudCrypto.sha256 must return canonical sha256 hex");
|
|
2685
|
-
const digest = match[1];
|
|
2686
|
-
if (digest === void 0) throw new Error("CloudCrypto.sha256 must return canonical sha256 hex");
|
|
2687
|
-
const hex = digest.slice(0, 32).split("");
|
|
2688
|
-
hex[12] = "8";
|
|
2689
|
-
hex[16] = ["8", "9", "a", "b"][Number.parseInt(hex[16], 16) & 3];
|
|
2690
|
-
return `${hex.slice(0, 8).join("")}-${hex.slice(8, 12).join("")}-${hex.slice(12, 16).join("")}-${hex.slice(16, 20).join("")}-${hex.slice(20).join("")}`;
|
|
2691
|
-
}
|
|
2692
2748
|
function sameOfferEnvelope(expected, actual) {
|
|
2693
2749
|
return expected.id === actual.id && expected.type === actual.type && expected.task_id === actual.task_id && JSON.stringify(expected.payload) === JSON.stringify(actual.payload);
|
|
2694
2750
|
}
|
|
@@ -2895,13 +2951,16 @@ function createByokCloud(options) {
|
|
|
2895
2951
|
async function enqueueTaskEnvelope(tenant, deviceId, requestedTaskId, agentRef, buildEnvelope, beforeMailbox) {
|
|
2896
2952
|
const stores = tenantStoresFor(controlPlane(tenant), root);
|
|
2897
2953
|
const taskId = requestedTaskId ?? `task_${options.crypto.randomUuid()}`;
|
|
2898
|
-
const messageId =
|
|
2899
|
-
|
|
2900
|
-
|
|
2901
|
-
|
|
2902
|
-
|
|
2903
|
-
)
|
|
2904
|
-
|
|
2954
|
+
const messageId = await taskOfferMessageId(tenant, taskId, deviceId, agentRef, (bytes) => options.crypto.sha256(bytes));
|
|
2955
|
+
const proposed = buildEnvelope(taskId, 0, messageId);
|
|
2956
|
+
const selection = proposed.payload;
|
|
2957
|
+
if (selection.harnessId !== void 0) {
|
|
2958
|
+
HarnessIdSchema.parse(selection.harnessId);
|
|
2959
|
+
if (selection.runtime !== void 0 || selection.dispatchSelection !== void 0) throw new ByokCloudError("coordination_input_invalid", "harnessId conflicts with built-in runtime selection");
|
|
2960
|
+
await assertAgentCapabilities(tenant, deviceId, ["custom-harness"]);
|
|
2961
|
+
const device = await options.cloud.devices.get(tenant, deviceId);
|
|
2962
|
+
if (!device?.harnesses?.some((harness) => harness.id === selection.harnessId)) throw new ByokCloudError("coordination_input_invalid", "requested custom harness is not advertised by this device");
|
|
2963
|
+
}
|
|
2905
2964
|
const reservation = agentRef === void 0 ? void 0 : await stores.tasks.reserveAgentOffer({ taskId, deviceId, agentRef });
|
|
2906
2965
|
const openedBeforeAppend = reservation?.attempt ?? (agentRef === void 0 ? await stores.tasks.open({ taskId, deviceId }) : void 0);
|
|
2907
2966
|
if (openedBeforeAppend === void 0 || openedBeforeAppend.deviceId !== deviceId || (agentRef === void 0 ? openedBeforeAppend.agentRef !== void 0 : openedBeforeAppend.agentRef === void 0 || !sameAgentRef2(agentRef, openedBeforeAppend.agentRef))) {
|
|
@@ -2918,7 +2977,6 @@ function createByokCloud(options) {
|
|
|
2918
2977
|
`Task ${taskId} already has a durable delivered attempt and cannot be enqueued again.`
|
|
2919
2978
|
);
|
|
2920
2979
|
}
|
|
2921
|
-
const proposed = buildEnvelope(taskId, 0, messageId);
|
|
2922
2980
|
const proposedBody = JSON.stringify({
|
|
2923
2981
|
deviceId,
|
|
2924
2982
|
type: proposed.type,
|
|
@@ -3983,7 +4041,8 @@ var InMemoryDeviceDirectory = class {
|
|
|
3983
4041
|
if (record === void 0 || record.revoked) return void 0;
|
|
3984
4042
|
const updated = {
|
|
3985
4043
|
...record,
|
|
3986
|
-
capabilities: Object.freeze([...input.capabilities])
|
|
4044
|
+
capabilities: Object.freeze([...input.capabilities]),
|
|
4045
|
+
harnesses: Object.freeze((input.harnesses ?? []).map((value) => Object.freeze({ ...value })))
|
|
3987
4046
|
};
|
|
3988
4047
|
this.#byTenant.set(key, updated);
|
|
3989
4048
|
return updated;
|
|
@@ -4365,6 +4424,7 @@ var InMemoryTaskAttemptStore = class {
|
|
|
4365
4424
|
...existing,
|
|
4366
4425
|
ownerDeviceId: input.deviceId,
|
|
4367
4426
|
...input.runtime === void 0 ? {} : { claimedRuntime: input.runtime },
|
|
4427
|
+
...input.harnessId === void 0 ? {} : { claimedHarnessId: input.harnessId },
|
|
4368
4428
|
...input.capabilities === void 0 ? {} : { claimedRuntimeCapabilities: { ...input.capabilities } },
|
|
4369
4429
|
status: "claimed",
|
|
4370
4430
|
updatedAt: this.#now()
|