@byok-sdk/cloud 0.8.0 → 0.9.0-rc.1
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/agent-memory-projection.d.ts +72 -0
- package/dist/capabilities.d.ts +8 -0
- package/dist/cloud.d.ts +28 -1
- package/dist/composition/in-memory.d.ts +5 -1
- package/dist/errors.d.ts +16 -0
- package/dist/handlers/agent-memory-projections.d.ts +15 -0
- package/dist/handlers/messages.d.ts +14 -1
- package/dist/handlers/shared.d.ts +12 -0
- package/dist/inbound.d.ts +14 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.js +492 -19
- package/dist/index.js.map +1 -1
- package/dist/stores/in-memory/agent-memory-projection.d.ts +29 -0
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
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, AgentHomeProjectionCompletionRequestSchema, decodeEnvelope, BYOK_PAIR_PATH, BYOK_CHALLENGE_PATH, BYOK_TOKEN_PATH, BYOK_CAPABILITIES_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, TaskOfferForAgentWithEgressFreshPayloadSchema, TaskOfferForAgentWithEgressPayloadSchema, TaskOfferForAgentPayloadSchema, AGENT_CONTENT_ARTIFACT_READ_CAPABILITY, AGENT_CONTENT_TRANSCRIPT_READ_CAPABILITY, AGENT_CONTENT_WORKSPACE_READ_CAPABILITY, AGENT_HOME_PROJECTION_CAPABILITY,
|
|
3
|
+
import { AgentEventOrUnknownSchema, PROTOCOL_VERSION, DAEMON_TO_SERVER_TYPES, AGENT_EGRESS_RELIABLE_ACK_CAPABILITY, encodeEnvelope, AgentHomeProjectionCompletionRequestSchema, decodeEnvelope, 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, PairRequestSchema, PairResponseSchema, ChallengeRequestSchema, TokenRequestSchema, AgentMemoryProjectionCommitRequestSchema, agentMemoryProjectionBase64UrlByteLength, MessagesSendRequestSchema, PresencePublishRequestSchema, CreateBlobRequestSchema, byokBlobContentPath, AgentEgressAckPayloadSchema, AgentContentReceiptPayloadSchema } from '@byok-sdk/protocol';
|
|
4
4
|
import { z } from 'zod';
|
|
5
5
|
import { Hono } from 'hono';
|
|
6
6
|
|
|
@@ -265,7 +265,9 @@ var CLOUD_CAPABILITIES = {
|
|
|
265
265
|
* a `SkillPackStore` is refused at construction rather than publishing two
|
|
266
266
|
* routes it would then 404.
|
|
267
267
|
*/
|
|
268
|
-
skillPacks: "skills.pack"
|
|
268
|
+
skillPacks: "skills.pack",
|
|
269
|
+
/** Optional, one-way redacted Agent-memory snapshot mutation route. */
|
|
270
|
+
agentMemoryProjection: "agent.memory.projection"
|
|
269
271
|
};
|
|
270
272
|
var CapabilitiesResponseSchema = CapabilityDeclarationSchema;
|
|
271
273
|
function fullCapabilityDeclaration(version = 1, options = {}) {
|
|
@@ -275,6 +277,9 @@ function fullCapabilityDeclaration(version = 1, options = {}) {
|
|
|
275
277
|
capabilities: Object.values(CLOUD_CAPABILITIES).filter((capability) => {
|
|
276
278
|
if (capability === CLOUD_CAPABILITIES.truthRecords) return options.includeTruthRecords === true;
|
|
277
279
|
if (capability === CLOUD_CAPABILITIES.skillPacks) return options.includeSkillPacks === true;
|
|
280
|
+
if (capability === CLOUD_CAPABILITIES.agentMemoryProjection) {
|
|
281
|
+
return options.includeAgentMemoryProjection === true;
|
|
282
|
+
}
|
|
278
283
|
return true;
|
|
279
284
|
})
|
|
280
285
|
};
|
|
@@ -339,7 +344,23 @@ var CLOUD_ERROR_CODES = {
|
|
|
339
344
|
/** A task-free completion did not exactly echo its immutable desired projection binding. */
|
|
340
345
|
agent_home_projection_receipt_mismatch: "agent_home_projection_receipt_mismatch",
|
|
341
346
|
/** A receipt-store row at the projection namespace violated the frozen projection schema. */
|
|
342
|
-
agent_home_projection_receipt_invalid: "agent_home_projection_receipt_invalid"
|
|
347
|
+
agent_home_projection_receipt_invalid: "agent_home_projection_receipt_invalid",
|
|
348
|
+
/** A hosted Agent-memory mutation did not match the durable task/device/AgentRef binding. */
|
|
349
|
+
agent_memory_projection_task_mismatch: "agent_memory_projection_task_mismatch",
|
|
350
|
+
/** The embedder-owned grant/consent authority denied a hosted memory mutation. */
|
|
351
|
+
agent_memory_projection_authorization_denied: "agent_memory_projection_authorization_denied",
|
|
352
|
+
/** A redacted snapshot's decoded bytes did not match its declared hash or byte count. */
|
|
353
|
+
agent_memory_projection_hash_mismatch: "agent_memory_projection_hash_mismatch",
|
|
354
|
+
/** A writer epoch/source sequence already names a different immutable projection mutation. */
|
|
355
|
+
agent_memory_projection_replay_mismatch: "agent_memory_projection_replay_mismatch",
|
|
356
|
+
/** A hosted memory mutation came from an older writer epoch. */
|
|
357
|
+
agent_memory_projection_stale_epoch: "agent_memory_projection_stale_epoch",
|
|
358
|
+
/** A server-side erase fence prevents a deleted writer epoch from re-entering. */
|
|
359
|
+
agent_memory_projection_erased_epoch: "agent_memory_projection_erased_epoch",
|
|
360
|
+
/** A prior erase reached the protocol writer-epoch ceiling and cannot mint a later writer. */
|
|
361
|
+
agent_memory_projection_epoch_exhausted: "agent_memory_projection_epoch_exhausted",
|
|
362
|
+
/** A hosted memory mutation skipped or reset a required source sequence. */
|
|
363
|
+
agent_memory_projection_sequence_gap: "agent_memory_projection_sequence_gap"
|
|
343
364
|
};
|
|
344
365
|
var ByokCloudError = class extends Error {
|
|
345
366
|
code;
|
|
@@ -600,6 +621,63 @@ async function readJsonBody(c) {
|
|
|
600
621
|
return void 0;
|
|
601
622
|
}
|
|
602
623
|
}
|
|
624
|
+
async function readBoundedJsonBody(c, maximum) {
|
|
625
|
+
const declaredLength = c.req.header("content-length");
|
|
626
|
+
if (declaredLength !== void 0) {
|
|
627
|
+
const parsedLength = parseContentLength(declaredLength);
|
|
628
|
+
if (parsedLength !== void 0 && parsedLength > BigInt(maximum)) {
|
|
629
|
+
return { body: void 0, tooLarge: true };
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
const stream = c.req.raw.body;
|
|
633
|
+
if (stream === null) return { body: void 0, tooLarge: false };
|
|
634
|
+
const reader = stream.getReader();
|
|
635
|
+
const chunks = [];
|
|
636
|
+
let total = 0;
|
|
637
|
+
try {
|
|
638
|
+
while (true) {
|
|
639
|
+
const { done, value } = await reader.read();
|
|
640
|
+
if (done) break;
|
|
641
|
+
if (value.byteLength > maximum - total) {
|
|
642
|
+
try {
|
|
643
|
+
await reader.cancel();
|
|
644
|
+
} catch {
|
|
645
|
+
}
|
|
646
|
+
return { body: void 0, tooLarge: true };
|
|
647
|
+
}
|
|
648
|
+
if (value.byteLength === 0) continue;
|
|
649
|
+
chunks.push(value);
|
|
650
|
+
total += value.byteLength;
|
|
651
|
+
}
|
|
652
|
+
} catch {
|
|
653
|
+
return { body: void 0, tooLarge: false };
|
|
654
|
+
} finally {
|
|
655
|
+
reader.releaseLock();
|
|
656
|
+
}
|
|
657
|
+
const bytes = new Uint8Array(total);
|
|
658
|
+
let offset = 0;
|
|
659
|
+
for (const chunk of chunks) {
|
|
660
|
+
bytes.set(chunk, offset);
|
|
661
|
+
offset += chunk.byteLength;
|
|
662
|
+
}
|
|
663
|
+
return { body: parseJsonBytes(bytes), tooLarge: false };
|
|
664
|
+
}
|
|
665
|
+
function parseContentLength(value) {
|
|
666
|
+
const normalized = value.trim();
|
|
667
|
+
if (!/^\d+$/u.test(normalized)) return void 0;
|
|
668
|
+
try {
|
|
669
|
+
return BigInt(normalized);
|
|
670
|
+
} catch {
|
|
671
|
+
return void 0;
|
|
672
|
+
}
|
|
673
|
+
}
|
|
674
|
+
function parseJsonBytes(bytes) {
|
|
675
|
+
try {
|
|
676
|
+
return JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(bytes));
|
|
677
|
+
} catch {
|
|
678
|
+
return void 0;
|
|
679
|
+
}
|
|
680
|
+
}
|
|
603
681
|
async function authenticateDevice(c, deps) {
|
|
604
682
|
const device = await authenticateBearer(c.req.header("authorization"), deps.bearer);
|
|
605
683
|
if (device === void 0) return void 0;
|
|
@@ -958,6 +1036,45 @@ async function projectTerminalToReview(board, taskId) {
|
|
|
958
1036
|
}
|
|
959
1037
|
|
|
960
1038
|
// src/inbound.ts
|
|
1039
|
+
async function handleAgentMessagePublish(stores, deviceId, taskId, payload, consume) {
|
|
1040
|
+
const device = await stores.devices.get(deviceId);
|
|
1041
|
+
const attempt = await stores.tasks.get(taskId);
|
|
1042
|
+
const binding = await stores.receipts.get(`agent-message-offer:${deviceId}:${taskId}`);
|
|
1043
|
+
if (device === void 0 || device.revoked || !hasCapabilities(device.capabilities, [AGENT_MESSAGE_EGRESS_CAPABILITY]) || attempt === void 0 || attempt.deviceId !== deviceId || binding === void 0 || !sameAgentRef(attempt.agentRef, payload.agentRef)) return { outcome: "rejected" };
|
|
1044
|
+
let frozen;
|
|
1045
|
+
try {
|
|
1046
|
+
frozen = JSON.parse(binding.body);
|
|
1047
|
+
} catch {
|
|
1048
|
+
return { outcome: "rejected" };
|
|
1049
|
+
}
|
|
1050
|
+
if (!sameAgentRef(frozen.agentRef, payload.agentRef) || frozen.context === void 0 || frozen.requirement?.contract !== payload.contract || frozen.sessionRef !== void 0 && frozen.sessionRef !== payload.sessionRef || frozen.requirement.contentType !== payload.contentType || typeof frozen.requirement.maxBytes !== "number" || payload.byteCount > frozen.requirement.maxBytes) return { outcome: "rejected" };
|
|
1051
|
+
const taskMessageKey = `agent-message-task:${deviceId}:${taskId}`;
|
|
1052
|
+
const taskMessageBody = JSON.stringify(payload);
|
|
1053
|
+
const lockedTaskMessage = await stores.receipts.record({ key: taskMessageKey, body: taskMessageBody });
|
|
1054
|
+
if (!lockedTaskMessage.created && lockedTaskMessage.receipt.body !== taskMessageBody) return { outcome: "rejected" };
|
|
1055
|
+
const receiptKey2 = `agent-message:${deviceId}:${payload.messageId}`;
|
|
1056
|
+
const previous = await stores.receipts.get(receiptKey2);
|
|
1057
|
+
if (previous !== void 0) {
|
|
1058
|
+
const decoded = JSON.parse(previous.body);
|
|
1059
|
+
if (JSON.stringify(decoded.payload) !== JSON.stringify(payload)) return { outcome: "rejected" };
|
|
1060
|
+
return { outcome: "duplicate", disposition: decoded.disposition };
|
|
1061
|
+
}
|
|
1062
|
+
const decision = consume === void 0 ? { outcome: "held", reasonCode: "consumer_unavailable" } : await consume({ tenant: stores.tenant, deviceId, taskId, context: frozen.context, payload });
|
|
1063
|
+
const disposition = {
|
|
1064
|
+
agentRef: payload.agentRef,
|
|
1065
|
+
sessionRef: payload.sessionRef,
|
|
1066
|
+
contract: payload.contract,
|
|
1067
|
+
messageId: payload.messageId,
|
|
1068
|
+
cursor: payload.cursor,
|
|
1069
|
+
contentHash: payload.contentHash,
|
|
1070
|
+
outcome: decision.outcome,
|
|
1071
|
+
receiptId: crypto.randomUUID(),
|
|
1072
|
+
...decision.reasonCode === void 0 ? {} : { reasonCode: decision.reasonCode }
|
|
1073
|
+
};
|
|
1074
|
+
const recorded = await stores.receipts.record({ key: receiptKey2, body: JSON.stringify({ payload, disposition }) });
|
|
1075
|
+
if (!recorded.created && recorded.receipt.body !== JSON.stringify({ payload, disposition })) return { outcome: "rejected" };
|
|
1076
|
+
return { outcome: "accepted", disposition };
|
|
1077
|
+
}
|
|
961
1078
|
function terminalReceiptKey(taskId) {
|
|
962
1079
|
return `task:${taskId}:terminal`;
|
|
963
1080
|
}
|
|
@@ -1210,13 +1327,23 @@ function messagesHandler(deps) {
|
|
|
1210
1327
|
let accepted = 0;
|
|
1211
1328
|
let rejected = 0;
|
|
1212
1329
|
for (const envelope of parsed.data.messages) {
|
|
1213
|
-
const
|
|
1330
|
+
const messageResult = envelope.type === "agent.message.publish" ? await handleAgentMessagePublish(
|
|
1331
|
+
stores,
|
|
1332
|
+
device.deviceId,
|
|
1333
|
+
envelope.task_id,
|
|
1334
|
+
envelope.payload,
|
|
1335
|
+
deps.agentMessage?.consume
|
|
1336
|
+
) : void 0;
|
|
1337
|
+
const outcome = messageResult?.outcome ?? await handleInboundEnvelope(
|
|
1214
1338
|
stores,
|
|
1215
1339
|
device.deviceId,
|
|
1216
1340
|
envelope,
|
|
1217
1341
|
deps.activityBounds
|
|
1218
1342
|
);
|
|
1219
1343
|
if (outcome === "rate_limited") return c.json({ error: "rate limit exceeded" }, 429);
|
|
1344
|
+
if (envelope.type === "agent.message.publish" && messageResult?.disposition !== void 0) {
|
|
1345
|
+
await deps.appendAgentMessageDisposition(stores, device.deviceId, envelope.task_id, messageResult.disposition);
|
|
1346
|
+
}
|
|
1220
1347
|
if (envelope.type === "agent.egress.reliable" && (outcome === "accepted" || outcome === "duplicate")) {
|
|
1221
1348
|
const record = await stores.egress.get(device.deviceId, envelope.payload.eventId);
|
|
1222
1349
|
if (record === void 0) {
|
|
@@ -1263,6 +1390,36 @@ function agentHomeProjectionCompletionHandler(deps) {
|
|
|
1263
1390
|
}
|
|
1264
1391
|
};
|
|
1265
1392
|
}
|
|
1393
|
+
var DEFAULT_MAX_AGENT_MEMORY_PROJECTION_REQUEST_BYTES = 1 * 1024 * 1024;
|
|
1394
|
+
function agentMemoryProjectionHandler(deps) {
|
|
1395
|
+
return async (c) => {
|
|
1396
|
+
const authenticated = await authenticateDevice(c, deps);
|
|
1397
|
+
if (authenticated === void 0) return c.json({ error: "unauthorized" }, 401);
|
|
1398
|
+
const body = await readBoundedJsonBody(c, DEFAULT_MAX_AGENT_MEMORY_PROJECTION_REQUEST_BYTES);
|
|
1399
|
+
if (body.tooLarge) return c.json({ error: "Agent-memory projection request body too large" }, 413);
|
|
1400
|
+
const parsed = AgentMemoryProjectionCommitRequestSchema.safeParse(body.body);
|
|
1401
|
+
if (!parsed.success) return c.json({ error: "invalid Agent-memory projection mutation" }, 422);
|
|
1402
|
+
if (agentMemoryProjectionBase64UrlByteLength(parsed.data.snapshot.redactedBytes) === void 0) {
|
|
1403
|
+
return c.json({ error: "invalid Agent-memory projection mutation" }, 422);
|
|
1404
|
+
}
|
|
1405
|
+
const decoded = base64UrlDecode(parsed.data.snapshot.redactedBytes);
|
|
1406
|
+
if (decoded === void 0 || decoded.byteLength !== parsed.data.snapshot.redactedByteCount) {
|
|
1407
|
+
return c.json({ error: "invalid Agent-memory projection mutation" }, 422);
|
|
1408
|
+
}
|
|
1409
|
+
try {
|
|
1410
|
+
return c.json(
|
|
1411
|
+
await deps.commit(authenticated.stores, authenticated.device.deviceId, parsed.data, decoded),
|
|
1412
|
+
200
|
|
1413
|
+
);
|
|
1414
|
+
} catch (error) {
|
|
1415
|
+
if (isCloudError(error, "agent_memory_projection_authorization_denied") || isCloudError(error, "agent_memory_projection_task_mismatch") || isCloudError(error, "agent_memory_projection_replay_mismatch") || isCloudError(error, "agent_memory_projection_stale_epoch") || isCloudError(error, "agent_memory_projection_erased_epoch") || isCloudError(error, "agent_memory_projection_epoch_exhausted") || isCloudError(error, "agent_memory_projection_sequence_gap")) return c.json({ error: error.code }, 409);
|
|
1416
|
+
if (isCloudError(error, "agent_memory_projection_hash_mismatch")) {
|
|
1417
|
+
return c.json({ error: error.code }, 422);
|
|
1418
|
+
}
|
|
1419
|
+
throw error;
|
|
1420
|
+
}
|
|
1421
|
+
};
|
|
1422
|
+
}
|
|
1266
1423
|
var DEFAULT_BOARD_PAGE_LIMIT = 50;
|
|
1267
1424
|
var DEFAULT_BOARD_STREAM_QUERY_INTERVAL_MS = 5e3;
|
|
1268
1425
|
var DEFAULT_BOARD_STREAM_HEARTBEAT_INTERVAL_MS = 15e3;
|
|
@@ -1809,7 +1966,7 @@ function truthPutHandler(deps) {
|
|
|
1809
1966
|
if (authenticated === void 0) return unauthorized2(c);
|
|
1810
1967
|
const selector = recordSelector(c);
|
|
1811
1968
|
if (selector === void 0) return c.json({ error: "invalid truth record selector" }, 400);
|
|
1812
|
-
const parsedJson =
|
|
1969
|
+
const parsedJson = parseJsonBytes2(body);
|
|
1813
1970
|
const parsed = TruthWriteRequestSchema.safeParse(parsedJson);
|
|
1814
1971
|
if (!parsed.success) return c.json({ error: "invalid truth write request" }, 400);
|
|
1815
1972
|
const writes = prepareWrites(selector.kind, selector.recordKey, parsed.data);
|
|
@@ -1944,7 +2101,7 @@ async function readBoundedBody(c, maximum) {
|
|
|
1944
2101
|
return void 0;
|
|
1945
2102
|
}
|
|
1946
2103
|
}
|
|
1947
|
-
function
|
|
2104
|
+
function parseJsonBytes2(bytes) {
|
|
1948
2105
|
try {
|
|
1949
2106
|
return JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(bytes));
|
|
1950
2107
|
} catch {
|
|
@@ -2188,7 +2345,9 @@ function createByokCloud(options) {
|
|
|
2188
2345
|
options.blobContentProxy,
|
|
2189
2346
|
options.truthCommitter,
|
|
2190
2347
|
options.truthObjectDownloads,
|
|
2191
|
-
options.skillPacks
|
|
2348
|
+
options.skillPacks,
|
|
2349
|
+
options.agentMemoryProjectionAuthorizer,
|
|
2350
|
+
options.agentMemoryProjectionStore
|
|
2192
2351
|
);
|
|
2193
2352
|
const root = { core: options.core, cloud: options.cloud };
|
|
2194
2353
|
const auth = createAuthPlane({
|
|
@@ -2216,6 +2375,15 @@ function createByokCloud(options) {
|
|
|
2216
2375
|
{ method: "GET", path: BYOK_CAPABILITIES_PATH, class: "public" },
|
|
2217
2376
|
capabilitiesHandler({ declaration })
|
|
2218
2377
|
);
|
|
2378
|
+
if (declares(declaration, CLOUD_CAPABILITIES.agentMemoryProjection)) {
|
|
2379
|
+
registry.register(
|
|
2380
|
+
{ method: "POST", path: BYOK_AGENT_MEMORY_PROJECTIONS_PATH, class: "device" },
|
|
2381
|
+
agentMemoryProjectionHandler({
|
|
2382
|
+
...deviceRouteDeps,
|
|
2383
|
+
commit: (stores, deviceId, mutation, redactedBytes) => commitAgentMemoryProjectionFromStores(stores, deviceId, mutation, redactedBytes)
|
|
2384
|
+
})
|
|
2385
|
+
);
|
|
2386
|
+
}
|
|
2219
2387
|
if (declares(declaration, CLOUD_CAPABILITIES.eventsLongPoll)) {
|
|
2220
2388
|
registry.register(
|
|
2221
2389
|
{ method: "GET", path: BYOK_EVENTS_PATH, class: "device" },
|
|
@@ -2234,7 +2402,9 @@ function createByokCloud(options) {
|
|
|
2234
2402
|
...deviceRouteDeps,
|
|
2235
2403
|
activityBounds,
|
|
2236
2404
|
appendReliableEgressAck: enqueueReliableEgressAck,
|
|
2237
|
-
appendContentReceiptAck: enqueueContentReceiptAck
|
|
2405
|
+
appendContentReceiptAck: enqueueContentReceiptAck,
|
|
2406
|
+
agentMessage: options.agentMessage,
|
|
2407
|
+
appendAgentMessageDisposition: enqueueAgentMessageDisposition
|
|
2238
2408
|
})
|
|
2239
2409
|
);
|
|
2240
2410
|
}
|
|
@@ -2360,7 +2530,7 @@ function createByokCloud(options) {
|
|
|
2360
2530
|
function controlPlane(tenant) {
|
|
2361
2531
|
return { kind: "control-plane", tenantId: tenant, operatorId };
|
|
2362
2532
|
}
|
|
2363
|
-
async function enqueueTaskEnvelope(tenant, deviceId, requestedTaskId, agentRef, buildEnvelope) {
|
|
2533
|
+
async function enqueueTaskEnvelope(tenant, deviceId, requestedTaskId, agentRef, buildEnvelope, beforeMailbox) {
|
|
2364
2534
|
const stores = tenantStoresFor(controlPlane(tenant), root);
|
|
2365
2535
|
const taskId = requestedTaskId ?? `task_${options.crypto.randomUuid()}`;
|
|
2366
2536
|
const messageId = options.crypto.randomUuid();
|
|
@@ -2380,6 +2550,7 @@ function createByokCloud(options) {
|
|
|
2380
2550
|
}
|
|
2381
2551
|
let message;
|
|
2382
2552
|
try {
|
|
2553
|
+
await beforeMailbox?.(stores, taskId);
|
|
2383
2554
|
message = await stores.mailbox.append({
|
|
2384
2555
|
deviceId,
|
|
2385
2556
|
messageId,
|
|
@@ -2435,6 +2606,15 @@ function createByokCloud(options) {
|
|
|
2435
2606
|
);
|
|
2436
2607
|
}
|
|
2437
2608
|
}
|
|
2609
|
+
async function assertLegacyAdmission(tenant, deviceId) {
|
|
2610
|
+
const device = await options.cloud.devices.get(tenant, deviceId);
|
|
2611
|
+
if (device?.capabilities?.includes(STRICT_AGENT_ONLY_CAPABILITY) === true) {
|
|
2612
|
+
throw new ByokCloudError(
|
|
2613
|
+
"agent_capability_missing",
|
|
2614
|
+
`Device ${deviceId} advertises strict-agent-only; refusing legacy offer before mailbox append.`
|
|
2615
|
+
);
|
|
2616
|
+
}
|
|
2617
|
+
}
|
|
2438
2618
|
function contentReadCapability2(surface) {
|
|
2439
2619
|
switch (surface) {
|
|
2440
2620
|
case "workspace":
|
|
@@ -2497,6 +2677,54 @@ function createByokCloud(options) {
|
|
|
2497
2677
|
receipt
|
|
2498
2678
|
);
|
|
2499
2679
|
}
|
|
2680
|
+
async function commitAgentMemoryProjectionFromStores(stores, deviceId, mutationInput, redactedBytes) {
|
|
2681
|
+
const mutation = AgentMemoryProjectionCommitRequestSchema.parse(mutationInput);
|
|
2682
|
+
const attempt = await stores.tasks.get(mutation.taskId);
|
|
2683
|
+
if (attempt === void 0 || attempt.deviceId !== deviceId || !sameAgentRef2(mutation.agentRef, attempt.agentRef)) {
|
|
2684
|
+
throw new ByokCloudError(
|
|
2685
|
+
"agent_memory_projection_task_mismatch",
|
|
2686
|
+
"The Agent-memory projection does not match an exact task/device/AgentRef binding."
|
|
2687
|
+
);
|
|
2688
|
+
}
|
|
2689
|
+
const authorizer = options.agentMemoryProjectionAuthorizer;
|
|
2690
|
+
const store = options.agentMemoryProjectionStore;
|
|
2691
|
+
if (authorizer === void 0 || store === void 0) {
|
|
2692
|
+
throw new ByokCloudError(
|
|
2693
|
+
"agent_capability_missing",
|
|
2694
|
+
"Hosted Agent-memory projection requires both an authorizer and a projection store."
|
|
2695
|
+
);
|
|
2696
|
+
}
|
|
2697
|
+
const authorization = await authorizer.authorize({
|
|
2698
|
+
tenantId: stores.tenant,
|
|
2699
|
+
deviceId,
|
|
2700
|
+
taskId: mutation.taskId,
|
|
2701
|
+
agentRef: mutation.agentRef,
|
|
2702
|
+
sessionRef: mutation.sessionRef,
|
|
2703
|
+
runtimeId: mutation.runtimeId,
|
|
2704
|
+
grantRef: mutation.grantRef,
|
|
2705
|
+
writerEpoch: mutation.writerEpoch,
|
|
2706
|
+
policyRevision: mutation.policyRevision
|
|
2707
|
+
});
|
|
2708
|
+
if (authorization.outcome !== "authorized") {
|
|
2709
|
+
throw new ByokCloudError(
|
|
2710
|
+
"agent_memory_projection_authorization_denied",
|
|
2711
|
+
`The Agent-memory projection grant was denied: ${authorization.reasonCode}`
|
|
2712
|
+
);
|
|
2713
|
+
}
|
|
2714
|
+
return store.commit({ tenantId: stores.tenant, deviceId, mutation, redactedBytes });
|
|
2715
|
+
}
|
|
2716
|
+
async function eraseAgentMemoryProjection(tenant, agentId) {
|
|
2717
|
+
const authorizer = options.agentMemoryProjectionAuthorizer;
|
|
2718
|
+
const store = options.agentMemoryProjectionStore;
|
|
2719
|
+
if (authorizer === void 0 || store === void 0) {
|
|
2720
|
+
throw new ByokCloudError(
|
|
2721
|
+
"agent_capability_missing",
|
|
2722
|
+
"Hosted Agent-memory projection is not configured for this deployment."
|
|
2723
|
+
);
|
|
2724
|
+
}
|
|
2725
|
+
await authorizer.revoke({ tenantId: tenant, agentId });
|
|
2726
|
+
return store.erase({ tenantId: tenant, agentId });
|
|
2727
|
+
}
|
|
2500
2728
|
async function enqueueReliableEgressAck(stores, record) {
|
|
2501
2729
|
const payload = AgentEgressAckPayloadSchema.parse({
|
|
2502
2730
|
agentRef: record.payload.agentRef,
|
|
@@ -2564,6 +2792,25 @@ function createByokCloud(options) {
|
|
|
2564
2792
|
);
|
|
2565
2793
|
}
|
|
2566
2794
|
}
|
|
2795
|
+
async function enqueueAgentMessageDisposition(stores, deviceId, taskId, payload) {
|
|
2796
|
+
const message = await stores.mailbox.append({
|
|
2797
|
+
deviceId,
|
|
2798
|
+
messageId: `agent-message-disposition:${payload.receiptId}`,
|
|
2799
|
+
materialize: async (seq) => {
|
|
2800
|
+
const body = encodeEnvelope(createEnvelope("agent.message.disposition", payload, {
|
|
2801
|
+
id: payload.receiptId,
|
|
2802
|
+
taskId,
|
|
2803
|
+
seq
|
|
2804
|
+
}));
|
|
2805
|
+
const bytes = new TextEncoder().encode(body);
|
|
2806
|
+
return { body, bodyHash: contentHash(await options.crypto.sha256(bytes)), byteSize: BigInt(bytes.length) };
|
|
2807
|
+
}
|
|
2808
|
+
});
|
|
2809
|
+
const decoded = decodeEnvelope(message.body);
|
|
2810
|
+
if (decoded.type !== "agent.message.disposition" || decoded.seq !== message.seq) {
|
|
2811
|
+
throw new ByokCloudError("mailbox_receipt_mismatch", "Agent message disposition mailbox readback mismatched.");
|
|
2812
|
+
}
|
|
2813
|
+
}
|
|
2567
2814
|
return {
|
|
2568
2815
|
fetch: registry.fetch,
|
|
2569
2816
|
routes: registry.routes,
|
|
@@ -2573,6 +2820,7 @@ function createByokCloud(options) {
|
|
|
2573
2820
|
return auth.createPairingCode(tenant, input);
|
|
2574
2821
|
},
|
|
2575
2822
|
async enqueueOffer(tenant, deviceId, input) {
|
|
2823
|
+
await assertLegacyAdmission(tenant, deviceId);
|
|
2576
2824
|
return enqueueTaskEnvelope(
|
|
2577
2825
|
tenant,
|
|
2578
2826
|
deviceId,
|
|
@@ -2582,6 +2830,7 @@ function createByokCloud(options) {
|
|
|
2582
2830
|
);
|
|
2583
2831
|
},
|
|
2584
2832
|
async enqueueToolsetOffer(tenant, deviceId, input) {
|
|
2833
|
+
await assertLegacyAdmission(tenant, deviceId);
|
|
2585
2834
|
return enqueueTaskEnvelope(
|
|
2586
2835
|
tenant,
|
|
2587
2836
|
deviceId,
|
|
@@ -2591,7 +2840,10 @@ function createByokCloud(options) {
|
|
|
2591
2840
|
);
|
|
2592
2841
|
},
|
|
2593
2842
|
async enqueueAgentOffer(tenant, deviceId, input) {
|
|
2594
|
-
await assertAgentCapabilities(tenant, deviceId, [
|
|
2843
|
+
await assertAgentCapabilities(tenant, deviceId, [
|
|
2844
|
+
AGENT_HOME_CONTRACT_CAPABILITY,
|
|
2845
|
+
...input.payload.terminalProjection === void 0 ? [] : [TERMINAL_PROJECTION_SELECTION_CAPABILITY]
|
|
2846
|
+
]);
|
|
2595
2847
|
const payload = TaskOfferForAgentPayloadSchema.parse(input.payload);
|
|
2596
2848
|
return enqueueTaskEnvelope(
|
|
2597
2849
|
tenant,
|
|
@@ -2605,32 +2857,60 @@ function createByokCloud(options) {
|
|
|
2605
2857
|
await assertAgentCapabilities(tenant, deviceId, [
|
|
2606
2858
|
AGENT_HOME_CONTRACT_CAPABILITY,
|
|
2607
2859
|
AGENT_EGRESS_POLICY_CAPABILITY,
|
|
2608
|
-
AGENT_EGRESS_RELIABLE_ACK_CAPABILITY
|
|
2860
|
+
AGENT_EGRESS_RELIABLE_ACK_CAPABILITY,
|
|
2861
|
+
...input.payload.messageEgress === void 0 ? [] : [AGENT_MESSAGE_EGRESS_CAPABILITY],
|
|
2862
|
+
...input.payload.terminalProjection === void 0 ? [] : [TERMINAL_PROJECTION_SELECTION_CAPABILITY]
|
|
2609
2863
|
]);
|
|
2610
2864
|
const payload = TaskOfferForAgentWithEgressPayloadSchema.parse(input.payload);
|
|
2611
|
-
|
|
2865
|
+
if (payload.messageEgress === void 0 && input.agentMessageContext !== void 0) {
|
|
2866
|
+
throw new Error("agentMessageContext requires messageEgress");
|
|
2867
|
+
}
|
|
2868
|
+
const messageContext = payload.messageEgress === void 0 ? void 0 : AgentMessageServerContextSchema.parse(input.agentMessageContext);
|
|
2869
|
+
const enqueued = await enqueueTaskEnvelope(
|
|
2612
2870
|
tenant,
|
|
2613
2871
|
deviceId,
|
|
2614
2872
|
input.taskId,
|
|
2615
2873
|
payload.agentRef,
|
|
2616
|
-
(taskId, seq, messageId) => createEnvelope("task.offer_for_agent_with_egress", payload, { id: messageId, taskId, seq })
|
|
2874
|
+
(taskId, seq, messageId) => createEnvelope("task.offer_for_agent_with_egress", payload, { id: messageId, taskId, seq }),
|
|
2875
|
+
payload.messageEgress === void 0 ? void 0 : async (stores, taskId) => {
|
|
2876
|
+
const body = JSON.stringify({ agentRef: payload.agentRef, sessionRef: payload.sessionRef, requirement: payload.messageEgress, context: messageContext });
|
|
2877
|
+
const recorded = await stores.receipts.record({ key: `agent-message-offer:${deviceId}:${taskId}`, body });
|
|
2878
|
+
if (!recorded.created && recorded.receipt.body !== body) {
|
|
2879
|
+
throw new ByokCloudError("agent_content_request_mismatch", `Task ${taskId} already has a different Agent message context.`);
|
|
2880
|
+
}
|
|
2881
|
+
}
|
|
2617
2882
|
);
|
|
2883
|
+
return enqueued;
|
|
2618
2884
|
},
|
|
2619
2885
|
async enqueueFreshAgentEgressOffer(tenant, deviceId, input) {
|
|
2620
2886
|
await assertAgentCapabilities(tenant, deviceId, [
|
|
2621
2887
|
AGENT_HOME_CONTRACT_CAPABILITY,
|
|
2622
2888
|
AGENT_EGRESS_POLICY_CAPABILITY,
|
|
2623
2889
|
AGENT_EGRESS_RELIABLE_ACK_CAPABILITY,
|
|
2624
|
-
AGENT_EGRESS_FRESH_SESSION_CAPABILITY
|
|
2890
|
+
AGENT_EGRESS_FRESH_SESSION_CAPABILITY,
|
|
2891
|
+
...input.payload.messageEgress === void 0 ? [] : [AGENT_MESSAGE_EGRESS_CAPABILITY],
|
|
2892
|
+
...input.payload.terminalProjection === void 0 ? [] : [TERMINAL_PROJECTION_SELECTION_CAPABILITY]
|
|
2625
2893
|
]);
|
|
2626
2894
|
const payload = TaskOfferForAgentWithEgressFreshPayloadSchema.parse(input.payload);
|
|
2627
|
-
|
|
2895
|
+
if (payload.messageEgress === void 0 && input.agentMessageContext !== void 0) {
|
|
2896
|
+
throw new Error("agentMessageContext requires messageEgress");
|
|
2897
|
+
}
|
|
2898
|
+
const messageContext = payload.messageEgress === void 0 ? void 0 : AgentMessageServerContextSchema.parse(input.agentMessageContext);
|
|
2899
|
+
const enqueued = await enqueueTaskEnvelope(
|
|
2628
2900
|
tenant,
|
|
2629
2901
|
deviceId,
|
|
2630
2902
|
input.taskId,
|
|
2631
2903
|
payload.agentRef,
|
|
2632
|
-
(taskId, seq, messageId) => createEnvelope("task.offer_for_agent_with_egress_fresh", payload, { id: messageId, taskId, seq })
|
|
2904
|
+
(taskId, seq, messageId) => createEnvelope("task.offer_for_agent_with_egress_fresh", payload, { id: messageId, taskId, seq }),
|
|
2905
|
+
payload.messageEgress === void 0 ? void 0 : async (stores, taskId) => {
|
|
2906
|
+
const body = JSON.stringify({ agentRef: payload.agentRef, requirement: payload.messageEgress, context: messageContext });
|
|
2907
|
+
const recorded = await stores.receipts.record({ key: `agent-message-offer:${deviceId}:${taskId}`, body });
|
|
2908
|
+
if (!recorded.created && recorded.receipt.body !== body) {
|
|
2909
|
+
throw new ByokCloudError("agent_content_request_mismatch", `Task ${taskId} already has a different Agent message context.`);
|
|
2910
|
+
}
|
|
2911
|
+
}
|
|
2633
2912
|
);
|
|
2913
|
+
return enqueued;
|
|
2634
2914
|
},
|
|
2635
2915
|
async enqueueAgentContentRead(tenant, deviceId, input) {
|
|
2636
2916
|
const payload = AgentContentReadPayloadSchema.parse(input.payload);
|
|
@@ -2711,6 +2991,7 @@ function createByokCloud(options) {
|
|
|
2711
2991
|
},
|
|
2712
2992
|
getAgentHomeProjectionStatus,
|
|
2713
2993
|
completeAgentHomeProjection,
|
|
2994
|
+
eraseAgentMemoryProjection,
|
|
2714
2995
|
async cancelTask(tenant, taskId, reason) {
|
|
2715
2996
|
const stores = tenantStoresFor(controlPlane(tenant), root);
|
|
2716
2997
|
const proposedMessageId = options.crypto.randomUuid();
|
|
@@ -2811,7 +3092,7 @@ function createByokCloud(options) {
|
|
|
2811
3092
|
}
|
|
2812
3093
|
};
|
|
2813
3094
|
}
|
|
2814
|
-
function assertNoOverDeclaration(declaration, contentProxy, truthCommitter, truthObjectDownloads, skillPacks) {
|
|
3095
|
+
function assertNoOverDeclaration(declaration, contentProxy, truthCommitter, truthObjectDownloads, skillPacks, agentMemoryProjectionAuthorizer, agentMemoryProjectionStore) {
|
|
2815
3096
|
if (declares(declaration, CLOUD_CAPABILITIES.boardSse) && !declares(declaration, CLOUD_CAPABILITIES.boardCoordination)) {
|
|
2816
3097
|
throw new ByokCloudError(
|
|
2817
3098
|
"capability_over_declared",
|
|
@@ -2842,6 +3123,12 @@ function assertNoOverDeclaration(declaration, contentProxy, truthCommitter, trut
|
|
|
2842
3123
|
`This deployment declares ${CLOUD_CAPABILITIES.skillPacks} but was given no SkillPackStore, so both /byok/skill-packs routes would be published and unserved.`
|
|
2843
3124
|
);
|
|
2844
3125
|
}
|
|
3126
|
+
if (declares(declaration, CLOUD_CAPABILITIES.agentMemoryProjection) && (agentMemoryProjectionAuthorizer === void 0 || agentMemoryProjectionStore === void 0)) {
|
|
3127
|
+
throw new ByokCloudError(
|
|
3128
|
+
"capability_over_declared",
|
|
3129
|
+
`This deployment declares ${CLOUD_CAPABILITIES.agentMemoryProjection} but was given no complete AgentMemoryProjectionAuthorizer/AgentMemoryProjectionStore pair.`
|
|
3130
|
+
);
|
|
3131
|
+
}
|
|
2845
3132
|
}
|
|
2846
3133
|
function parseDeclaration(declaration) {
|
|
2847
3134
|
try {
|
|
@@ -3595,10 +3882,15 @@ function createInMemoryByokCloud(options = {}) {
|
|
|
3595
3882
|
crypto: crypto2,
|
|
3596
3883
|
tokenSigner,
|
|
3597
3884
|
clock,
|
|
3598
|
-
capabilities: options.capabilities ?? fullCapabilityDeclaration(
|
|
3885
|
+
capabilities: options.capabilities ?? fullCapabilityDeclaration(void 0, {
|
|
3886
|
+
includeAgentMemoryProjection: options.agentMemoryProjectionAuthorizer !== void 0 && options.agentMemoryProjectionStore !== void 0
|
|
3887
|
+
}),
|
|
3599
3888
|
...options.truthCommitter === void 0 ? {} : { truthCommitter: options.truthCommitter },
|
|
3600
3889
|
...options.truthObjectDownloads === void 0 ? {} : { truthObjectDownloads: options.truthObjectDownloads },
|
|
3601
3890
|
...options.operatorId !== void 0 ? { operatorId: options.operatorId } : {},
|
|
3891
|
+
...options.agentMessage === void 0 ? {} : { agentMessage: options.agentMessage },
|
|
3892
|
+
...options.agentMemoryProjectionAuthorizer === void 0 ? {} : { agentMemoryProjectionAuthorizer: options.agentMemoryProjectionAuthorizer },
|
|
3893
|
+
...options.agentMemoryProjectionStore === void 0 ? {} : { agentMemoryProjectionStore: options.agentMemoryProjectionStore },
|
|
3602
3894
|
...options.maxBlobSizeBytes !== void 0 ? { maxBlobSizeBytes: options.maxBlobSizeBytes } : {},
|
|
3603
3895
|
...options.longPollHoldMs !== void 0 ? { longPollHoldMs: options.longPollHoldMs } : {},
|
|
3604
3896
|
...options.longPollIntervalMs !== void 0 ? { longPollIntervalMs: options.longPollIntervalMs } : {},
|
|
@@ -3623,6 +3915,187 @@ function createInMemoryByokCloud(options = {}) {
|
|
|
3623
3915
|
});
|
|
3624
3916
|
return { cloud, core, stores, blobContentProxy, clock, crypto: crypto2 };
|
|
3625
3917
|
}
|
|
3918
|
+
function agentKey(tenantId4, agentId) {
|
|
3919
|
+
return `${tenantId4}\0${agentId}`;
|
|
3920
|
+
}
|
|
3921
|
+
function permitKey(input) {
|
|
3922
|
+
return JSON.stringify([
|
|
3923
|
+
input.tenantId,
|
|
3924
|
+
input.deviceId,
|
|
3925
|
+
input.taskId,
|
|
3926
|
+
input.agentRef.agentId,
|
|
3927
|
+
input.agentRef.profileRevision,
|
|
3928
|
+
input.sessionRef,
|
|
3929
|
+
input.runtimeId,
|
|
3930
|
+
input.grantRef,
|
|
3931
|
+
input.writerEpoch,
|
|
3932
|
+
input.policyRevision
|
|
3933
|
+
]);
|
|
3934
|
+
}
|
|
3935
|
+
function receiptKey(input) {
|
|
3936
|
+
const { mutation } = input;
|
|
3937
|
+
return `${agentKey(input.tenantId, mutation.agentRef.agentId)}\0${mutation.writerEpoch}\0${mutation.sourceSeq}`;
|
|
3938
|
+
}
|
|
3939
|
+
function sameMutationReceipt(receipt, input) {
|
|
3940
|
+
const { mutation } = input;
|
|
3941
|
+
return receipt.tenantId === input.tenantId && receipt.deviceId === input.deviceId && receipt.taskId === mutation.taskId && receipt.agentRef.agentId === mutation.agentRef.agentId && receipt.agentRef.profileRevision === mutation.agentRef.profileRevision && receipt.sessionRef === mutation.sessionRef && receipt.runtimeId === mutation.runtimeId && receipt.grantRef === mutation.grantRef && receipt.writerEpoch === mutation.writerEpoch && receipt.sourceSeq === mutation.sourceSeq && receipt.mutationId === mutation.mutationId && receipt.policyRevision === mutation.policyRevision && receipt.redactedHash === mutation.snapshot.redactedHash && receipt.redactedByteCount === mutation.snapshot.redactedByteCount;
|
|
3942
|
+
}
|
|
3943
|
+
var InMemoryAgentMemoryProjectionStore = class {
|
|
3944
|
+
#clock;
|
|
3945
|
+
#crypto;
|
|
3946
|
+
#heads = /* @__PURE__ */ new Map();
|
|
3947
|
+
#snapshots = /* @__PURE__ */ new Map();
|
|
3948
|
+
#receipts = /* @__PURE__ */ new Map();
|
|
3949
|
+
/** Minimum legal future epoch after server-side erasure; contains no body. */
|
|
3950
|
+
#eraseFences = /* @__PURE__ */ new Map();
|
|
3951
|
+
#mutationTails = /* @__PURE__ */ new Map();
|
|
3952
|
+
constructor(clock, crypto2) {
|
|
3953
|
+
this.#clock = clock;
|
|
3954
|
+
this.#crypto = crypto2;
|
|
3955
|
+
}
|
|
3956
|
+
async commit(input) {
|
|
3957
|
+
const mutation = AgentMemoryProjectionMutationSchema.parse(input.mutation);
|
|
3958
|
+
if (input.redactedBytes.byteLength !== mutation.snapshot.redactedByteCount) {
|
|
3959
|
+
throw new ByokCloudError(
|
|
3960
|
+
"agent_memory_projection_hash_mismatch",
|
|
3961
|
+
"Decoded redacted snapshot bytes do not match redactedByteCount."
|
|
3962
|
+
);
|
|
3963
|
+
}
|
|
3964
|
+
const hash = await this.#crypto.sha256(input.redactedBytes);
|
|
3965
|
+
if (hash !== mutation.snapshot.redactedHash) {
|
|
3966
|
+
throw new ByokCloudError(
|
|
3967
|
+
"agent_memory_projection_hash_mismatch",
|
|
3968
|
+
"Decoded redacted snapshot bytes do not match redactedHash."
|
|
3969
|
+
);
|
|
3970
|
+
}
|
|
3971
|
+
const exactInput = { ...input, mutation };
|
|
3972
|
+
const key = agentKey(input.tenantId, mutation.agentRef.agentId);
|
|
3973
|
+
return this.#mutate(key, () => {
|
|
3974
|
+
const prior = this.#receipts.get(receiptKey(exactInput));
|
|
3975
|
+
if (prior !== void 0) {
|
|
3976
|
+
if (!sameMutationReceipt(prior, exactInput)) {
|
|
3977
|
+
throw new ByokCloudError(
|
|
3978
|
+
"agent_memory_projection_replay_mismatch",
|
|
3979
|
+
"A memory projection epoch and source sequence already names a different immutable mutation."
|
|
3980
|
+
);
|
|
3981
|
+
}
|
|
3982
|
+
return AgentMemoryProjectionReceiptSchema.parse({ ...prior, outcome: "idempotent" });
|
|
3983
|
+
}
|
|
3984
|
+
const fence = this.#eraseFences.get(key);
|
|
3985
|
+
if (fence !== void 0 && mutation.writerEpoch < fence) {
|
|
3986
|
+
throw new ByokCloudError("agent_memory_projection_erased_epoch", "The memory projection writerEpoch was erased and cannot be replayed.");
|
|
3987
|
+
}
|
|
3988
|
+
const head = this.#heads.get(key);
|
|
3989
|
+
if (head === void 0) {
|
|
3990
|
+
if (mutation.sourceSeq !== 1) {
|
|
3991
|
+
throw new ByokCloudError(
|
|
3992
|
+
"agent_memory_projection_sequence_gap",
|
|
3993
|
+
"The first memory projection mutation must use sourceSeq 1."
|
|
3994
|
+
);
|
|
3995
|
+
}
|
|
3996
|
+
} else if (mutation.writerEpoch < head.writerEpoch) {
|
|
3997
|
+
throw new ByokCloudError("agent_memory_projection_stale_epoch", "The memory projection writerEpoch is stale.");
|
|
3998
|
+
} else if (mutation.writerEpoch > head.writerEpoch) {
|
|
3999
|
+
if (mutation.sourceSeq !== 1) {
|
|
4000
|
+
throw new ByokCloudError(
|
|
4001
|
+
"agent_memory_projection_sequence_gap",
|
|
4002
|
+
"A new memory projection writerEpoch must start at sourceSeq 1."
|
|
4003
|
+
);
|
|
4004
|
+
}
|
|
4005
|
+
} else if (mutation.sourceSeq !== head.sourceSeq + 1) {
|
|
4006
|
+
throw new ByokCloudError(
|
|
4007
|
+
"agent_memory_projection_sequence_gap",
|
|
4008
|
+
"A memory projection mutation must advance sourceSeq exactly by one."
|
|
4009
|
+
);
|
|
4010
|
+
}
|
|
4011
|
+
const recordedAt = this.#clock.now().toISOString();
|
|
4012
|
+
const metering = {
|
|
4013
|
+
meteringReceiptId: this.#crypto.randomUuid(),
|
|
4014
|
+
acceptedRedactedBytes: mutation.snapshot.redactedByteCount,
|
|
4015
|
+
recordedAt
|
|
4016
|
+
};
|
|
4017
|
+
const receipt = AgentMemoryProjectionReceiptSchema.parse({
|
|
4018
|
+
outcome: "accepted",
|
|
4019
|
+
tenantId: input.tenantId,
|
|
4020
|
+
deviceId: input.deviceId,
|
|
4021
|
+
taskId: mutation.taskId,
|
|
4022
|
+
agentRef: mutation.agentRef,
|
|
4023
|
+
sessionRef: mutation.sessionRef,
|
|
4024
|
+
runtimeId: mutation.runtimeId,
|
|
4025
|
+
grantRef: mutation.grantRef,
|
|
4026
|
+
writerEpoch: mutation.writerEpoch,
|
|
4027
|
+
sourceSeq: mutation.sourceSeq,
|
|
4028
|
+
mutationId: mutation.mutationId,
|
|
4029
|
+
policyRevision: mutation.policyRevision,
|
|
4030
|
+
redactedHash: mutation.snapshot.redactedHash,
|
|
4031
|
+
redactedByteCount: mutation.snapshot.redactedByteCount,
|
|
4032
|
+
metering
|
|
4033
|
+
});
|
|
4034
|
+
this.#snapshots.set(key, {
|
|
4035
|
+
redactedBytes: input.redactedBytes.slice(),
|
|
4036
|
+
redactedHash: mutation.snapshot.redactedHash,
|
|
4037
|
+
redactedByteCount: mutation.snapshot.redactedByteCount
|
|
4038
|
+
});
|
|
4039
|
+
this.#heads.set(key, { writerEpoch: mutation.writerEpoch, sourceSeq: mutation.sourceSeq });
|
|
4040
|
+
this.#receipts.set(receiptKey(exactInput), receipt);
|
|
4041
|
+
return receipt;
|
|
4042
|
+
});
|
|
4043
|
+
}
|
|
4044
|
+
async erase(input) {
|
|
4045
|
+
const key = agentKey(input.tenantId, input.agentId);
|
|
4046
|
+
return this.#mutate(key, () => {
|
|
4047
|
+
const priorHeadEpoch = this.#heads.get(key)?.writerEpoch ?? 0;
|
|
4048
|
+
const nextWriterEpoch = Math.max(this.#eraseFences.get(key) ?? 1, priorHeadEpoch + 1);
|
|
4049
|
+
if (nextWriterEpoch > AGENT_MEMORY_PROJECTION_MAX_ORDERING_VALUE) {
|
|
4050
|
+
throw new ByokCloudError("agent_memory_projection_epoch_exhausted", "The memory projection writerEpoch cannot advance after erase.");
|
|
4051
|
+
}
|
|
4052
|
+
this.#heads.delete(key);
|
|
4053
|
+
this.#snapshots.delete(key);
|
|
4054
|
+
const prefix = `${key}\0`;
|
|
4055
|
+
for (const receiptKeyValue of this.#receipts.keys()) {
|
|
4056
|
+
if (receiptKeyValue.startsWith(prefix)) this.#receipts.delete(receiptKeyValue);
|
|
4057
|
+
}
|
|
4058
|
+
this.#eraseFences.set(key, nextWriterEpoch);
|
|
4059
|
+
return AgentMemoryProjectionEraseResultSchema.parse({ nextWriterEpoch });
|
|
4060
|
+
});
|
|
4061
|
+
}
|
|
4062
|
+
async #mutate(key, operation) {
|
|
4063
|
+
const previous = this.#mutationTails.get(key) ?? Promise.resolve();
|
|
4064
|
+
const result = previous.then(operation);
|
|
4065
|
+
const tail = result.then(() => void 0, () => void 0);
|
|
4066
|
+
this.#mutationTails.set(key, tail);
|
|
4067
|
+
try {
|
|
4068
|
+
return await result;
|
|
4069
|
+
} finally {
|
|
4070
|
+
if (this.#mutationTails.get(key) === tail) this.#mutationTails.delete(key);
|
|
4071
|
+
}
|
|
4072
|
+
}
|
|
4073
|
+
};
|
|
4074
|
+
var InMemoryAgentMemoryProjectionAuthorizer = class {
|
|
4075
|
+
#grants = /* @__PURE__ */ new Map();
|
|
4076
|
+
/** Highest host-issued epoch observed for each tenant/Agent authority. */
|
|
4077
|
+
#highestEpochs = /* @__PURE__ */ new Map();
|
|
4078
|
+
grant(input) {
|
|
4079
|
+
const key = agentKey(input.tenantId, input.agentRef.agentId);
|
|
4080
|
+
const highestEpoch = this.#highestEpochs.get(key);
|
|
4081
|
+
if (highestEpoch !== void 0 && input.writerEpoch < highestEpoch) return;
|
|
4082
|
+
if (highestEpoch === void 0 || input.writerEpoch > highestEpoch) {
|
|
4083
|
+
for (const [grantKey, grant] of this.#grants) {
|
|
4084
|
+
if (grant.tenantId === input.tenantId && grant.agentRef.agentId === input.agentRef.agentId && grant.writerEpoch < input.writerEpoch) this.#grants.delete(grantKey);
|
|
4085
|
+
}
|
|
4086
|
+
this.#highestEpochs.set(key, input.writerEpoch);
|
|
4087
|
+
}
|
|
4088
|
+
this.#grants.set(permitKey(input), { ...input, agentRef: { ...input.agentRef } });
|
|
4089
|
+
}
|
|
4090
|
+
async authorize(input) {
|
|
4091
|
+
return this.#grants.has(permitKey(input)) ? { outcome: "authorized" } : { outcome: "denied", reasonCode: "grant_not_authorized" };
|
|
4092
|
+
}
|
|
4093
|
+
async revoke(input) {
|
|
4094
|
+
for (const [key, grant] of this.#grants) {
|
|
4095
|
+
if (grant.tenantId === input.tenantId && grant.agentRef.agentId === input.agentId) this.#grants.delete(key);
|
|
4096
|
+
}
|
|
4097
|
+
}
|
|
4098
|
+
};
|
|
3626
4099
|
function authenticateHostedDeviceAssertion(input, deps) {
|
|
3627
4100
|
return authenticateDeviceAssertion(input, {
|
|
3628
4101
|
verifier: {
|
|
@@ -3841,6 +4314,6 @@ var CLOUD_PORT_INTERFACES = {
|
|
|
3841
4314
|
rateLimiter: "InboundRateLimiter"
|
|
3842
4315
|
};
|
|
3843
4316
|
|
|
3844
|
-
export { ACCESS_TOKEN_TTL_SECONDS, AGENT_HOME_CONTRACT_CAPABILITY, APPROVAL_SUMMARY_MAX_BYTES, ActivityAppendRequestSchema, AllowAllRateLimiter, ApprovalObservationSchema, ApprovalTimelineEventSchema, BLOB_READ_ERROR_CODES, 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_APPROVAL_TIMELINE_CAPACITY, DEFAULT_APPROVAL_TIMELINE_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, InMemoryActivityStore, InMemoryAgentEgressStore, InMemoryBlobContentProxy, InMemoryCloudBlobStore, InMemoryDeviceDirectory, InMemoryInboundDedupStore, InMemoryNonceStore, InMemoryPairingCodeStore, InMemoryProofRequestReceiptStore, InMemoryRequestReceiptStore, InMemoryTaskAttemptStore, InMemoryTaskCancellationStore, 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, TimelineEventSchema, TruthBodyInputSchema, TruthCommitError, TruthCommitResponseSchema, TruthRecordKeySchema, TruthRecordMetadataSchema, TruthWriteRequestSchema, activityCursor, agentHomeProjectionCompletionKey, agentHomeProjectionRequestKey, approvalTimelineCursor, authenticateBearer, authenticateDeviceProof, authenticateHostedDeviceAssertion, createAuthPlane, createByokCloud, createHmacTokenSigner, createInMemoryBlobs, createInMemoryByokCloud, createInMemoryCloudStores, createWebCrypto, declares, extractBearerToken, fullCapabilityDeclaration, handleInboundEnvelope, isCloudError, isTruthCommitError, parseApprovalObservations, parseTimelineEvents, projectTerminalResult, projectTimelineEvents, readAgentHomeProjectionStatus, recordAgentHomeProjectionCompletion, routeKey, tenantStoresFor, terminalReceiptKey, truthManifestMetadata, truthRecordMetadata, validateActivityAppend, validateApprovalTimelineAppend, verifyNonceSignature };
|
|
4317
|
+
export { ACCESS_TOKEN_TTL_SECONDS, AGENT_HOME_CONTRACT_CAPABILITY, APPROVAL_SUMMARY_MAX_BYTES, ActivityAppendRequestSchema, AllowAllRateLimiter, ApprovalObservationSchema, ApprovalTimelineEventSchema, BLOB_READ_ERROR_CODES, 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_APPROVAL_TIMELINE_CAPACITY, DEFAULT_APPROVAL_TIMELINE_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, InMemoryActivityStore, InMemoryAgentEgressStore, InMemoryAgentMemoryProjectionAuthorizer, InMemoryAgentMemoryProjectionStore, InMemoryBlobContentProxy, InMemoryCloudBlobStore, InMemoryDeviceDirectory, InMemoryInboundDedupStore, InMemoryNonceStore, InMemoryPairingCodeStore, InMemoryProofRequestReceiptStore, InMemoryRequestReceiptStore, InMemoryTaskAttemptStore, InMemoryTaskCancellationStore, 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, TimelineEventSchema, TruthBodyInputSchema, TruthCommitError, TruthCommitResponseSchema, TruthRecordKeySchema, TruthRecordMetadataSchema, TruthWriteRequestSchema, activityCursor, agentHomeProjectionCompletionKey, agentHomeProjectionRequestKey, approvalTimelineCursor, authenticateBearer, authenticateDeviceProof, authenticateHostedDeviceAssertion, createAuthPlane, createByokCloud, createHmacTokenSigner, createInMemoryBlobs, createInMemoryByokCloud, createInMemoryCloudStores, createWebCrypto, declares, extractBearerToken, fullCapabilityDeclaration, handleInboundEnvelope, isCloudError, isTruthCommitError, parseApprovalObservations, parseTimelineEvents, projectTerminalResult, projectTimelineEvents, readAgentHomeProjectionStatus, recordAgentHomeProjectionCompletion, routeKey, tenantStoresFor, terminalReceiptKey, truthManifestMetadata, truthRecordMetadata, validateActivityAppend, validateApprovalTimelineAppend, verifyNonceSignature };
|
|
3845
4318
|
//# sourceMappingURL=index.js.map
|
|
3846
4319
|
//# sourceMappingURL=index.js.map
|