@byok-sdk/cloud 0.8.1 → 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 +481 -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, STRICT_AGENT_ONLY_CAPABILITY, 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,
|
|
@@ -2506,6 +2677,54 @@ function createByokCloud(options) {
|
|
|
2506
2677
|
receipt
|
|
2507
2678
|
);
|
|
2508
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
|
+
}
|
|
2509
2728
|
async function enqueueReliableEgressAck(stores, record) {
|
|
2510
2729
|
const payload = AgentEgressAckPayloadSchema.parse({
|
|
2511
2730
|
agentRef: record.payload.agentRef,
|
|
@@ -2573,6 +2792,25 @@ function createByokCloud(options) {
|
|
|
2573
2792
|
);
|
|
2574
2793
|
}
|
|
2575
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
|
+
}
|
|
2576
2814
|
return {
|
|
2577
2815
|
fetch: registry.fetch,
|
|
2578
2816
|
routes: registry.routes,
|
|
@@ -2602,7 +2840,10 @@ function createByokCloud(options) {
|
|
|
2602
2840
|
);
|
|
2603
2841
|
},
|
|
2604
2842
|
async enqueueAgentOffer(tenant, deviceId, input) {
|
|
2605
|
-
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
|
+
]);
|
|
2606
2847
|
const payload = TaskOfferForAgentPayloadSchema.parse(input.payload);
|
|
2607
2848
|
return enqueueTaskEnvelope(
|
|
2608
2849
|
tenant,
|
|
@@ -2616,32 +2857,60 @@ function createByokCloud(options) {
|
|
|
2616
2857
|
await assertAgentCapabilities(tenant, deviceId, [
|
|
2617
2858
|
AGENT_HOME_CONTRACT_CAPABILITY,
|
|
2618
2859
|
AGENT_EGRESS_POLICY_CAPABILITY,
|
|
2619
|
-
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]
|
|
2620
2863
|
]);
|
|
2621
2864
|
const payload = TaskOfferForAgentWithEgressPayloadSchema.parse(input.payload);
|
|
2622
|
-
|
|
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(
|
|
2623
2870
|
tenant,
|
|
2624
2871
|
deviceId,
|
|
2625
2872
|
input.taskId,
|
|
2626
2873
|
payload.agentRef,
|
|
2627
|
-
(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
|
+
}
|
|
2628
2882
|
);
|
|
2883
|
+
return enqueued;
|
|
2629
2884
|
},
|
|
2630
2885
|
async enqueueFreshAgentEgressOffer(tenant, deviceId, input) {
|
|
2631
2886
|
await assertAgentCapabilities(tenant, deviceId, [
|
|
2632
2887
|
AGENT_HOME_CONTRACT_CAPABILITY,
|
|
2633
2888
|
AGENT_EGRESS_POLICY_CAPABILITY,
|
|
2634
2889
|
AGENT_EGRESS_RELIABLE_ACK_CAPABILITY,
|
|
2635
|
-
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]
|
|
2636
2893
|
]);
|
|
2637
2894
|
const payload = TaskOfferForAgentWithEgressFreshPayloadSchema.parse(input.payload);
|
|
2638
|
-
|
|
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(
|
|
2639
2900
|
tenant,
|
|
2640
2901
|
deviceId,
|
|
2641
2902
|
input.taskId,
|
|
2642
2903
|
payload.agentRef,
|
|
2643
|
-
(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
|
+
}
|
|
2644
2912
|
);
|
|
2913
|
+
return enqueued;
|
|
2645
2914
|
},
|
|
2646
2915
|
async enqueueAgentContentRead(tenant, deviceId, input) {
|
|
2647
2916
|
const payload = AgentContentReadPayloadSchema.parse(input.payload);
|
|
@@ -2722,6 +2991,7 @@ function createByokCloud(options) {
|
|
|
2722
2991
|
},
|
|
2723
2992
|
getAgentHomeProjectionStatus,
|
|
2724
2993
|
completeAgentHomeProjection,
|
|
2994
|
+
eraseAgentMemoryProjection,
|
|
2725
2995
|
async cancelTask(tenant, taskId, reason) {
|
|
2726
2996
|
const stores = tenantStoresFor(controlPlane(tenant), root);
|
|
2727
2997
|
const proposedMessageId = options.crypto.randomUuid();
|
|
@@ -2822,7 +3092,7 @@ function createByokCloud(options) {
|
|
|
2822
3092
|
}
|
|
2823
3093
|
};
|
|
2824
3094
|
}
|
|
2825
|
-
function assertNoOverDeclaration(declaration, contentProxy, truthCommitter, truthObjectDownloads, skillPacks) {
|
|
3095
|
+
function assertNoOverDeclaration(declaration, contentProxy, truthCommitter, truthObjectDownloads, skillPacks, agentMemoryProjectionAuthorizer, agentMemoryProjectionStore) {
|
|
2826
3096
|
if (declares(declaration, CLOUD_CAPABILITIES.boardSse) && !declares(declaration, CLOUD_CAPABILITIES.boardCoordination)) {
|
|
2827
3097
|
throw new ByokCloudError(
|
|
2828
3098
|
"capability_over_declared",
|
|
@@ -2853,6 +3123,12 @@ function assertNoOverDeclaration(declaration, contentProxy, truthCommitter, trut
|
|
|
2853
3123
|
`This deployment declares ${CLOUD_CAPABILITIES.skillPacks} but was given no SkillPackStore, so both /byok/skill-packs routes would be published and unserved.`
|
|
2854
3124
|
);
|
|
2855
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
|
+
}
|
|
2856
3132
|
}
|
|
2857
3133
|
function parseDeclaration(declaration) {
|
|
2858
3134
|
try {
|
|
@@ -3606,10 +3882,15 @@ function createInMemoryByokCloud(options = {}) {
|
|
|
3606
3882
|
crypto: crypto2,
|
|
3607
3883
|
tokenSigner,
|
|
3608
3884
|
clock,
|
|
3609
|
-
capabilities: options.capabilities ?? fullCapabilityDeclaration(
|
|
3885
|
+
capabilities: options.capabilities ?? fullCapabilityDeclaration(void 0, {
|
|
3886
|
+
includeAgentMemoryProjection: options.agentMemoryProjectionAuthorizer !== void 0 && options.agentMemoryProjectionStore !== void 0
|
|
3887
|
+
}),
|
|
3610
3888
|
...options.truthCommitter === void 0 ? {} : { truthCommitter: options.truthCommitter },
|
|
3611
3889
|
...options.truthObjectDownloads === void 0 ? {} : { truthObjectDownloads: options.truthObjectDownloads },
|
|
3612
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 },
|
|
3613
3894
|
...options.maxBlobSizeBytes !== void 0 ? { maxBlobSizeBytes: options.maxBlobSizeBytes } : {},
|
|
3614
3895
|
...options.longPollHoldMs !== void 0 ? { longPollHoldMs: options.longPollHoldMs } : {},
|
|
3615
3896
|
...options.longPollIntervalMs !== void 0 ? { longPollIntervalMs: options.longPollIntervalMs } : {},
|
|
@@ -3634,6 +3915,187 @@ function createInMemoryByokCloud(options = {}) {
|
|
|
3634
3915
|
});
|
|
3635
3916
|
return { cloud, core, stores, blobContentProxy, clock, crypto: crypto2 };
|
|
3636
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
|
+
};
|
|
3637
4099
|
function authenticateHostedDeviceAssertion(input, deps) {
|
|
3638
4100
|
return authenticateDeviceAssertion(input, {
|
|
3639
4101
|
verifier: {
|
|
@@ -3852,6 +4314,6 @@ var CLOUD_PORT_INTERFACES = {
|
|
|
3852
4314
|
rateLimiter: "InboundRateLimiter"
|
|
3853
4315
|
};
|
|
3854
4316
|
|
|
3855
|
-
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 };
|
|
3856
4318
|
//# sourceMappingURL=index.js.map
|
|
3857
4319
|
//# sourceMappingURL=index.js.map
|