@byok-sdk/core 0.30.1 → 0.31.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,3170 +1,15 @@
1
1
  import { z } from 'zod';
2
2
 
3
- // src/errors.ts
4
- var CORE_ERROR_CODES = {
5
- routine_schedule_invalid: "routine_schedule_invalid",
6
- retention_input_invalid: "retention_input_invalid",
7
- retention_collect_failed: "retention_collect_failed",
8
- // identity + content addressing
9
- tenant_id_invalid: "tenant_id_invalid",
10
- content_hash_invalid: "content_hash_invalid",
11
- // caller-supplied instants (see `time.ts`)
12
- timestamp_not_canonical: "timestamp_not_canonical",
13
- // capability declaration (ADR-010)
14
- capability_declaration_invalid: "capability_declaration_invalid",
15
- capability_unavailable: "capability_unavailable",
16
- // device proof (§12.6.3)
17
- proof_envelope_invalid: "proof_envelope_invalid",
18
- proof_canonicalization_failed: "proof_canonicalization_failed",
19
- // device assertion (plan device-assertion-broker) — deliberately its own
20
- // code rather than reusing `proof_envelope_invalid`: the two envelopes are
21
- // non-interchangeable by design, and one shared "envelope was bad" code is
22
- // exactly how a log stops being able to say which authentication surface was
23
- // probed.
24
- assertion_envelope_invalid: "assertion_envelope_invalid",
25
- // sealed provider provisioning (plan web-sealed-provisioning). Messages
26
- // never carry plaintext, ciphertext, or key material — only which check
27
- // failed.
28
- webcrypto_unavailable: "webcrypto_unavailable",
29
- sealed_secret_invalid: "sealed_secret_invalid",
30
- sealed_secret_open_failed: "sealed_secret_open_failed",
31
- provider_provisioning_request_invalid: "provider_provisioning_request_invalid",
32
- // mailbox (§12.7.3)
33
- mailbox_message_not_found: "mailbox_message_not_found",
34
- mailbox_cursor_regression: "mailbox_cursor_regression",
35
- mailbox_cursor_ahead_of_delivery: "mailbox_cursor_ahead_of_delivery",
36
- // board coordination (§12.3)
37
- board_item_not_found: "board_item_not_found",
38
- board_item_exists: "board_item_exists",
39
- board_transition_invalid: "board_transition_invalid",
40
- board_status_conflict: "board_status_conflict",
41
- board_claim_conflict: "board_claim_conflict",
42
- board_not_held: "board_not_held",
43
- // truth records (§12.3, §12.6.4)
44
- truth_record_not_found: "truth_record_not_found",
45
- terminal_conflict: "terminal_conflict",
46
- truth_revision_conflict: "truth_revision_conflict",
47
- // presence + activity hints (§12.3)
48
- activity_capacity_invalid: "activity_capacity_invalid",
49
- activity_batch_invalid: "activity_batch_invalid",
50
- hint_ttl_invalid: "hint_ttl_invalid",
51
- hint_rate_limited: "hint_rate_limited",
52
- // object manifest (§12.7.4, §12.7.8)
53
- object_key_prefix_invalid: "object_key_prefix_invalid",
54
- object_not_found: "object_not_found",
55
- object_state_invalid: "object_state_invalid",
56
- // storage entitlement / usage / reservation (§12.7.6-12.7.7)
57
- storage_entitlement_missing: "storage_entitlement_missing",
58
- storage_entitlement_version_conflict: "storage_entitlement_version_conflict",
59
- storage_reservation_not_found: "storage_reservation_not_found",
60
- storage_object_too_large: "storage_object_too_large",
61
- storage_quota_exceeded: "storage_quota_exceeded",
62
- storage_reservation_expired: "storage_reservation_expired",
63
- storage_integrity_mismatch: "storage_integrity_mismatch",
64
- storage_write_suspended: "storage_write_suspended",
65
- // skill packs (plan `skill-pack-delivery-channel`)
66
- skill_pack_manifest_invalid: "skill_pack_manifest_invalid",
67
- skill_pack_frontmatter_invalid: "skill_pack_frontmatter_invalid"
68
- };
69
- var ByokCoreError = class extends Error {
70
- code;
71
- constructor(code, message, options) {
72
- super(message, options);
73
- this.name = "ByokCoreError";
74
- this.code = code;
75
- }
76
- };
77
- var CoreConflictError = class extends ByokCoreError {
78
- current;
79
- observedAt;
80
- constructor(code, message, current, observedAt, options) {
81
- super(code, message, options);
82
- this.name = "CoreConflictError";
83
- this.current = current;
84
- this.observedAt = observedAt;
85
- }
86
- };
87
- function isCoreError(value, code) {
88
- if (!(value instanceof ByokCoreError)) return false;
89
- return code === void 0 || value.code === code;
90
- }
91
- function isCoreConflictError(value, code) {
92
- if (!(value instanceof CoreConflictError)) return false;
93
- return code === void 0 || value.code === code;
94
- }
95
-
96
- // src/tenant.ts
97
- var TENANT_ID_MAX_LENGTH = 200;
98
- var TENANT_KEY_SEPARATOR = "\0";
99
- function tenantId(value) {
100
- const candidate = value;
101
- if (typeof candidate !== "string") {
102
- throw new ByokCoreError(
103
- "tenant_id_invalid",
104
- `Tenant id must be a string, received ${typeof candidate}.`
105
- );
106
- }
107
- if (candidate.length === 0) {
108
- throw new ByokCoreError("tenant_id_invalid", "Tenant id must not be empty.");
109
- }
110
- if (candidate.trim() !== candidate) {
111
- throw new ByokCoreError(
112
- "tenant_id_invalid",
113
- "Tenant id must not have leading or trailing whitespace."
114
- );
115
- }
116
- if (candidate.length > TENANT_ID_MAX_LENGTH) {
117
- throw new ByokCoreError(
118
- "tenant_id_invalid",
119
- `Tenant id must be at most ${TENANT_ID_MAX_LENGTH} characters.`
120
- );
121
- }
122
- if (candidate.includes(TENANT_KEY_SEPARATOR)) {
123
- throw new ByokCoreError("tenant_id_invalid", "Tenant id must not contain a NUL character.");
124
- }
125
- return candidate;
126
- }
127
- function isTenantId(value) {
128
- if (typeof value !== "string") return false;
129
- return value.length > 0 && value.trim() === value && value.length <= TENANT_ID_MAX_LENGTH && !value.includes(TENANT_KEY_SEPARATOR);
130
- }
131
- function tenantKey(tenant, ...parts) {
132
- return [tenant, ...parts].join(TENANT_KEY_SEPARATOR);
133
- }
134
-
135
- // src/principals.ts
136
- var PRINCIPAL_KINDS = ["device", "control-plane"];
137
- function isDevicePrincipal(principal) {
138
- return principal.kind === "device";
139
- }
140
- function isControlPlanePrincipal(principal) {
141
- return principal.kind === "control-plane";
142
- }
143
- function principalTenant(principal) {
144
- return principal.tenantId;
145
- }
146
-
147
- // src/time.ts
148
- var CANONICAL_TIMESTAMP_PATTERN = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/;
149
- function isCanonicalTimestamp(value) {
150
- if (typeof value !== "string") return false;
151
- if (!CANONICAL_TIMESTAMP_PATTERN.test(value)) return false;
152
- const parsed = Date.parse(value);
153
- if (!Number.isFinite(parsed)) return false;
154
- return new Date(parsed).toISOString() === value;
155
- }
156
- function assertCanonicalTimestamp(value, field) {
157
- if (!isCanonicalTimestamp(value)) {
158
- throw new ByokCoreError(
159
- "timestamp_not_canonical",
160
- `${field} must be a canonical ISO-8601 UTC instant (YYYY-MM-DDTHH:mm:ss.sssZ), received ${JSON.stringify(value)}.`
161
- );
162
- }
163
- return value;
164
- }
165
-
166
- // src/mailbox.ts
167
- var MAILBOX_MESSAGE_STATES = ["pending", "acked", "expired"];
168
-
169
- // src/board.ts
170
- var BOARD_STATUSES = ["todo", "in_progress", "in_review", "done", "closed"];
171
- var BOARD_TRANSITIONS = {
172
- todo: ["in_progress", "closed"],
173
- in_progress: ["todo", "in_review", "closed"],
174
- in_review: ["in_progress", "done", "closed"],
175
- done: [],
176
- closed: []
177
- };
178
- function isLegalBoardTransition(from, to) {
179
- return BOARD_TRANSITIONS[from].includes(to);
180
- }
181
-
182
- // src/truth.ts
183
- var TRUTH_RECORD_KINDS = ["task.terminal", "profile", "memory"];
184
-
185
- // src/presence.ts
186
- var PRESENCE_LEVELS = ["online", "thinking", "working", "error", "offline"];
187
-
188
- // src/blob.ts
189
- var CONTENT_HASH_PATTERN = /^sha256:[0-9a-f]{64}$/;
190
- function contentHash(value) {
191
- if (!CONTENT_HASH_PATTERN.test(value)) {
192
- throw new ByokCoreError(
193
- "content_hash_invalid",
194
- `Content hash must match ${CONTENT_HASH_PATTERN.source}, received ${JSON.stringify(value)}.`
195
- );
196
- }
197
- return value;
198
- }
199
- function isContentHash(value) {
200
- return typeof value === "string" && CONTENT_HASH_PATTERN.test(value);
201
- }
202
- var OBJECT_KEY_PREFIX_PATTERN = /^[a-z0-9][a-z0-9._-]*(\/[a-z0-9][a-z0-9._-]*)*$/;
203
- function objectKeyPrefix(value) {
204
- if (!OBJECT_KEY_PREFIX_PATTERN.test(value)) {
205
- throw new ByokCoreError(
206
- "object_key_prefix_invalid",
207
- `Object key prefix must match ${OBJECT_KEY_PREFIX_PATTERN.source}, received ${JSON.stringify(value)}.`
208
- );
209
- }
210
- return value;
211
- }
212
- function tenantObjectKey(tenant, hash, prefix) {
213
- const hex2 = hash.slice("sha256:".length);
214
- const key = `tenants/${tenant}/objects/sha256/${hex2}`;
215
- return prefix === void 0 ? key : `${prefix}/${key}`;
216
- }
217
- var OBJECT_STATES = ["pending", "committed", "delete_pending", "deleted"];
218
- var OBJECT_STATE_TRANSITIONS = {
219
- pending: ["committed", "delete_pending"],
220
- committed: ["delete_pending"],
221
- delete_pending: ["deleted", "committed"],
222
- deleted: []
223
- };
224
- function isLegalObjectTransition(from, to) {
225
- return OBJECT_STATE_TRANSITIONS[from].includes(to);
226
- }
227
-
228
- // src/quota.ts
229
- var STORAGE_WRITE_KINDS = ["object", "inline"];
230
- var STORAGE_RESERVATION_STATES = [
231
- "reserved",
232
- "committed",
233
- "aborted",
234
- "expired"
235
- ];
236
- var STORAGE_WRITE_POSTURES = ["normal", "warning", "blocked", "suspended"];
237
- var STORAGE_ERROR_CODES = [
238
- "storage_object_too_large",
239
- "storage_quota_exceeded",
240
- "storage_reservation_expired",
241
- "storage_integrity_mismatch",
242
- "storage_write_suspended"
243
- ];
244
- var STORAGE_ERROR_HTTP_STATUS = {
245
- storage_object_too_large: 413,
246
- storage_quota_exceeded: 507,
247
- storage_reservation_expired: 409,
248
- storage_integrity_mismatch: 422,
249
- storage_write_suspended: 423
250
- };
251
- var CAPABILITY_DECLARATION_SCHEMA_ID = "byok-capabilities-v1";
252
- var CAPABILITY_NAME_PATTERN = /^[a-z0-9]+(?:[._-][a-z0-9]+)*$/;
253
- var CapabilityDeclarationSchema = z.object({
254
- schema: z.literal(CAPABILITY_DECLARATION_SCHEMA_ID),
255
- /** Monotonic per deployment. Lets a client cache and detect a rollout. */
256
- version: z.number().int().nonnegative(),
257
- capabilities: z.array(z.string().regex(CAPABILITY_NAME_PATTERN))
258
- }).superRefine((declaration, ctx) => {
259
- const seen = /* @__PURE__ */ new Set();
260
- for (const [index, name] of declaration.capabilities.entries()) {
261
- if (seen.has(name)) {
262
- ctx.addIssue({
263
- code: "custom",
264
- path: ["capabilities", index],
265
- message: `Duplicate capability ${JSON.stringify(name)}.`
266
- });
267
- }
268
- seen.add(name);
269
- }
270
- });
271
- function parseCapabilityDeclaration(input) {
272
- const result = CapabilityDeclarationSchema.safeParse(input);
273
- if (!result.success) {
274
- throw new ByokCoreError(
275
- "capability_declaration_invalid",
276
- `Invalid capability declaration: ${result.error.issues.map((issue) => `${issue.path.join(".") || "<root>"}: ${issue.message}`).join("; ")}`,
277
- { cause: result.error }
278
- );
279
- }
280
- return result.data;
281
- }
282
- function hasCapability(declaration, capability) {
283
- return declaration.capabilities.includes(capability);
284
- }
285
- function assertCapability(declaration, capability) {
286
- if (!hasCapability(declaration, capability)) {
287
- throw new ByokCoreError(
288
- "capability_unavailable",
289
- `Capability ${JSON.stringify(capability)} is not declared by this deployment.`
290
- );
291
- }
292
- }
293
- var SKILL_PACK_MANIFEST_SCHEMA_ID = "byok-skill-pack-v1";
294
- var SKILL_PACK_NAME_PATTERN = /^[a-z0-9][a-z0-9._-]*$/;
295
- var SKILL_PACK_NAME_MAX_LENGTH = 64;
296
- var SKILL_PACK_DESCRIPTION_MAX_LENGTH = 1024;
297
- var SKILL_PACK_VERSION_PATTERN = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)$/;
298
- var SKILL_PACK_FILE_PATH_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._-]*(\/[A-Za-z0-9][A-Za-z0-9._-]*)*$/;
299
- var SKILL_PACK_FILE_PATH_MAX_LENGTH = 200;
300
- var SKILL_PACK_ENTRY_PATH = "SKILL.md";
301
- var SKILL_PACK_FILE_MAX_BYTES = 262144;
302
- var SKILL_PACK_MAX_BYTES = 1048576;
303
- var SKILL_PACK_MAX_FILES = 64;
304
- var SKILL_PACK_FORBIDDEN_FIELDS = [
305
- "exec",
306
- "command",
307
- "entrypoint",
308
- "run",
309
- "script",
310
- "shell",
311
- "env",
312
- "environment",
313
- "credential",
314
- "credentials",
315
- "secret",
316
- "secrets",
317
- "token",
318
- "apiKey",
319
- "api_key",
320
- "allowedTools",
321
- "allowed-tools",
322
- "hooks",
323
- "preinstall",
324
- "postinstall"
325
- ];
326
- var ContentHashSchema = z.string().regex(CONTENT_HASH_PATTERN).transform((value) => contentHash(value));
327
- var SkillPackFileSchema = z.strictObject({
328
- path: z.string().min(1).max(SKILL_PACK_FILE_PATH_MAX_LENGTH).regex(SKILL_PACK_FILE_PATH_PATTERN),
329
- contentHash: ContentHashSchema,
330
- byteSize: z.number().int().nonnegative().max(SKILL_PACK_FILE_MAX_BYTES)
331
- });
332
- var SkillPackManifestSchema = z.strictObject({
333
- schema: z.literal(SKILL_PACK_MANIFEST_SCHEMA_ID),
334
- name: z.string().min(1).max(SKILL_PACK_NAME_MAX_LENGTH).regex(SKILL_PACK_NAME_PATTERN),
335
- version: z.string().regex(SKILL_PACK_VERSION_PATTERN),
336
- description: z.string().min(1).max(SKILL_PACK_DESCRIPTION_MAX_LENGTH),
337
- files: z.array(SkillPackFileSchema).min(1).max(SKILL_PACK_MAX_FILES),
338
- contentHash: ContentHashSchema
339
- });
340
- function parseSkillPackManifest(input) {
341
- if (typeof input === "object" && input !== null) {
342
- const present = SKILL_PACK_FORBIDDEN_FIELDS.filter(
343
- (field) => Object.prototype.hasOwnProperty.call(input, field)
344
- );
345
- if (present.length > 0) {
346
- throw new ByokCoreError(
347
- "skill_pack_manifest_invalid",
348
- `A skill pack manifest carries no executable or credential surface; refusing ${present.map((field) => JSON.stringify(field)).join(", ")}.`
349
- );
350
- }
351
- }
352
- const result = SkillPackManifestSchema.safeParse(input);
353
- if (!result.success) {
354
- throw new ByokCoreError(
355
- "skill_pack_manifest_invalid",
356
- `Invalid skill pack manifest: ${result.error.issues.map((issue) => `${issue.path.join(".") || "<root>"}: ${issue.message}`).join("; ")}`,
357
- { cause: result.error }
358
- );
359
- }
360
- return result.data;
361
- }
362
- function skillPackContentHashInput(manifest) {
363
- const rows = [...manifest.files].sort((left, right) => left.path < right.path ? -1 : left.path > right.path ? 1 : 0).map((file) => `${file.path}
3
+ var CORE_ERROR_CODES={routine_schedule_invalid:"routine_schedule_invalid",retention_input_invalid:"retention_input_invalid",retention_collect_failed:"retention_collect_failed",tenant_id_invalid:"tenant_id_invalid",content_hash_invalid:"content_hash_invalid",timestamp_not_canonical:"timestamp_not_canonical",capability_declaration_invalid:"capability_declaration_invalid",capability_unavailable:"capability_unavailable",proof_envelope_invalid:"proof_envelope_invalid",proof_canonicalization_failed:"proof_canonicalization_failed",assertion_envelope_invalid:"assertion_envelope_invalid",webcrypto_unavailable:"webcrypto_unavailable",sealed_secret_invalid:"sealed_secret_invalid",sealed_secret_open_failed:"sealed_secret_open_failed",provider_provisioning_request_invalid:"provider_provisioning_request_invalid",mailbox_message_not_found:"mailbox_message_not_found",mailbox_cursor_regression:"mailbox_cursor_regression",mailbox_cursor_ahead_of_delivery:"mailbox_cursor_ahead_of_delivery",board_item_not_found:"board_item_not_found",board_item_exists:"board_item_exists",board_transition_invalid:"board_transition_invalid",board_status_conflict:"board_status_conflict",board_claim_conflict:"board_claim_conflict",board_not_held:"board_not_held",truth_record_not_found:"truth_record_not_found",terminal_conflict:"terminal_conflict",truth_revision_conflict:"truth_revision_conflict",activity_capacity_invalid:"activity_capacity_invalid",activity_batch_invalid:"activity_batch_invalid",hint_ttl_invalid:"hint_ttl_invalid",hint_rate_limited:"hint_rate_limited",object_key_prefix_invalid:"object_key_prefix_invalid",object_not_found:"object_not_found",object_state_invalid:"object_state_invalid",storage_entitlement_missing:"storage_entitlement_missing",storage_entitlement_version_conflict:"storage_entitlement_version_conflict",storage_reservation_not_found:"storage_reservation_not_found",storage_object_too_large:"storage_object_too_large",storage_quota_exceeded:"storage_quota_exceeded",storage_reservation_expired:"storage_reservation_expired",storage_integrity_mismatch:"storage_integrity_mismatch",storage_write_suspended:"storage_write_suspended",skill_pack_manifest_invalid:"skill_pack_manifest_invalid",skill_pack_frontmatter_invalid:"skill_pack_frontmatter_invalid"};var ByokCoreError=class extends Error{code;constructor(code,message,options){super(message,options);this.name="ByokCoreError";this.code=code;}};var CoreConflictError=class extends ByokCoreError{current;observedAt;constructor(code,message,current,observedAt,options){super(code,message,options);this.name="CoreConflictError";this.current=current;this.observedAt=observedAt;}};function isCoreError(value,code){if(!(value instanceof ByokCoreError))return false;return code===void 0||value.code===code}function isCoreConflictError(value,code){if(!(value instanceof CoreConflictError))return false;return code===void 0||value.code===code}var TENANT_ID_MAX_LENGTH=200;var TENANT_KEY_SEPARATOR="\0";function tenantId(value){const candidate=value;if(typeof candidate!=="string"){throw new ByokCoreError("tenant_id_invalid",`Tenant id must be a string, received ${typeof candidate}.`)}if(candidate.length===0){throw new ByokCoreError("tenant_id_invalid","Tenant id must not be empty.")}if(candidate.trim()!==candidate){throw new ByokCoreError("tenant_id_invalid","Tenant id must not have leading or trailing whitespace.")}if(candidate.length>TENANT_ID_MAX_LENGTH){throw new ByokCoreError("tenant_id_invalid",`Tenant id must be at most ${TENANT_ID_MAX_LENGTH} characters.`)}if(candidate.includes(TENANT_KEY_SEPARATOR)){throw new ByokCoreError("tenant_id_invalid","Tenant id must not contain a NUL character.")}return candidate}function isTenantId(value){if(typeof value!=="string")return false;return value.length>0&&value.trim()===value&&value.length<=TENANT_ID_MAX_LENGTH&&!value.includes(TENANT_KEY_SEPARATOR)}function tenantKey(tenant,...parts){return [tenant,...parts].join(TENANT_KEY_SEPARATOR)}var PRINCIPAL_KINDS=["device","control-plane"];function isDevicePrincipal(principal){return principal.kind==="device"}function isControlPlanePrincipal(principal){return principal.kind==="control-plane"}function principalTenant(principal){return principal.tenantId}var CANONICAL_TIMESTAMP_PATTERN=/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/;function isCanonicalTimestamp(value){if(typeof value!=="string")return false;if(!CANONICAL_TIMESTAMP_PATTERN.test(value))return false;const parsed=Date.parse(value);if(!Number.isFinite(parsed))return false;return new Date(parsed).toISOString()===value}function assertCanonicalTimestamp(value,field){if(!isCanonicalTimestamp(value)){throw new ByokCoreError("timestamp_not_canonical",`${field} must be a canonical ISO-8601 UTC instant (YYYY-MM-DDTHH:mm:ss.sssZ), received ${JSON.stringify(value)}.`)}return value}var MAILBOX_MESSAGE_STATES=["pending","acked","expired"];var BOARD_STATUSES=["todo","in_progress","in_review","done","closed"];var BOARD_TRANSITIONS={todo:["in_progress","closed"],in_progress:["todo","in_review","closed"],in_review:["in_progress","done","closed"],done:[],closed:[]};function isLegalBoardTransition(from,to){return BOARD_TRANSITIONS[from].includes(to)}var TRUTH_RECORD_KINDS=["task.terminal","profile","memory"];var PRESENCE_LEVELS=["online","thinking","working","error","offline"];var CONTENT_HASH_PATTERN=/^sha256:[0-9a-f]{64}$/;function contentHash(value){if(!CONTENT_HASH_PATTERN.test(value)){throw new ByokCoreError("content_hash_invalid",`Content hash must match ${CONTENT_HASH_PATTERN.source}, received ${JSON.stringify(value)}.`)}return value}function isContentHash(value){return typeof value==="string"&&CONTENT_HASH_PATTERN.test(value)}var OBJECT_KEY_PREFIX_PATTERN=/^[a-z0-9][a-z0-9._-]*(\/[a-z0-9][a-z0-9._-]*)*$/;function objectKeyPrefix(value){if(!OBJECT_KEY_PREFIX_PATTERN.test(value)){throw new ByokCoreError("object_key_prefix_invalid",`Object key prefix must match ${OBJECT_KEY_PREFIX_PATTERN.source}, received ${JSON.stringify(value)}.`)}return value}function tenantObjectKey(tenant,hash,prefix){const hex2=hash.slice("sha256:".length);const key=`tenants/${tenant}/objects/sha256/${hex2}`;return prefix===void 0?key:`${prefix}/${key}`}var OBJECT_STATES=["pending","committed","delete_pending","deleted"];var OBJECT_STATE_TRANSITIONS={pending:["committed","delete_pending"],committed:["delete_pending"],delete_pending:["deleted","committed"],deleted:[]};function isLegalObjectTransition(from,to){return OBJECT_STATE_TRANSITIONS[from].includes(to)}var STORAGE_WRITE_KINDS=["object","inline"];var STORAGE_RESERVATION_STATES=["reserved","committed","aborted","expired"];var STORAGE_WRITE_POSTURES=["normal","warning","blocked","suspended"];var STORAGE_ERROR_CODES=["storage_object_too_large","storage_quota_exceeded","storage_reservation_expired","storage_integrity_mismatch","storage_write_suspended"];var STORAGE_ERROR_HTTP_STATUS={storage_object_too_large:413,storage_quota_exceeded:507,storage_reservation_expired:409,storage_integrity_mismatch:422,storage_write_suspended:423};var CAPABILITY_DECLARATION_SCHEMA_ID="byok-capabilities-v1";var CAPABILITY_NAME_PATTERN=/^[a-z0-9]+(?:[._-][a-z0-9]+)*$/;var CapabilityDeclarationSchema=z.object({schema:z.literal(CAPABILITY_DECLARATION_SCHEMA_ID),version:z.number().int().nonnegative(),capabilities:z.array(z.string().regex(CAPABILITY_NAME_PATTERN))}).superRefine((declaration,ctx)=>{const seen=new Set;for(const[index,name]of declaration.capabilities.entries()){if(seen.has(name)){ctx.addIssue({code:"custom",path:["capabilities",index],message:`Duplicate capability ${JSON.stringify(name)}.`});}seen.add(name);}});function parseCapabilityDeclaration(input){const result=CapabilityDeclarationSchema.safeParse(input);if(!result.success){throw new ByokCoreError("capability_declaration_invalid",`Invalid capability declaration: ${result.error.issues.map(issue=>`${issue.path.join(".")||"<root>"}: ${issue.message}`).join("; ")}`,{cause:result.error})}return result.data}function hasCapability(declaration,capability){return declaration.capabilities.includes(capability)}function assertCapability(declaration,capability){if(!hasCapability(declaration,capability)){throw new ByokCoreError("capability_unavailable",`Capability ${JSON.stringify(capability)} is not declared by this deployment.`)}}var SKILL_PACK_MANIFEST_SCHEMA_ID="byok-skill-pack-v1";var SKILL_PACK_NAME_PATTERN=/^[a-z0-9][a-z0-9._-]*$/;var SKILL_PACK_NAME_MAX_LENGTH=64;var SKILL_PACK_DESCRIPTION_MAX_LENGTH=1024;var SKILL_PACK_VERSION_PATTERN=/^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)$/;var SKILL_PACK_FILE_PATH_PATTERN=/^[A-Za-z0-9][A-Za-z0-9._-]*(\/[A-Za-z0-9][A-Za-z0-9._-]*)*$/;var SKILL_PACK_FILE_PATH_MAX_LENGTH=200;var SKILL_PACK_ENTRY_PATH="SKILL.md";var SKILL_PACK_FILE_MAX_BYTES=262144;var SKILL_PACK_MAX_BYTES=1048576;var SKILL_PACK_MAX_FILES=64;var SKILL_PACK_FORBIDDEN_FIELDS=["exec","command","entrypoint","run","script","shell","env","environment","credential","credentials","secret","secrets","token","apiKey","api_key","allowedTools","allowed-tools","hooks","preinstall","postinstall"];var ContentHashSchema=z.string().regex(CONTENT_HASH_PATTERN).transform(value=>contentHash(value));var SkillPackFileSchema=z.strictObject({path:z.string().min(1).max(SKILL_PACK_FILE_PATH_MAX_LENGTH).regex(SKILL_PACK_FILE_PATH_PATTERN),contentHash:ContentHashSchema,byteSize:z.number().int().nonnegative().max(SKILL_PACK_FILE_MAX_BYTES)});var SkillPackManifestSchema=z.strictObject({schema:z.literal(SKILL_PACK_MANIFEST_SCHEMA_ID),name:z.string().min(1).max(SKILL_PACK_NAME_MAX_LENGTH).regex(SKILL_PACK_NAME_PATTERN),version:z.string().regex(SKILL_PACK_VERSION_PATTERN),description:z.string().min(1).max(SKILL_PACK_DESCRIPTION_MAX_LENGTH),files:z.array(SkillPackFileSchema).min(1).max(SKILL_PACK_MAX_FILES),contentHash:ContentHashSchema});function parseSkillPackManifest(input){if(typeof input==="object"&&input!==null){const present=SKILL_PACK_FORBIDDEN_FIELDS.filter(field=>Object.prototype.hasOwnProperty.call(input,field));if(present.length>0){throw new ByokCoreError("skill_pack_manifest_invalid",`A skill pack manifest carries no executable or credential surface; refusing ${present.map(field=>JSON.stringify(field)).join(", ")}.`)}}const result=SkillPackManifestSchema.safeParse(input);if(!result.success){throw new ByokCoreError("skill_pack_manifest_invalid",`Invalid skill pack manifest: ${result.error.issues.map(issue=>`${issue.path.join(".")||"<root>"}: ${issue.message}`).join("; ")}`,{cause:result.error})}return result.data}function skillPackContentHashInput(manifest){const rows=[...manifest.files].sort((left,right)=>left.path<right.path?-1:left.path>right.path?1:0).map(file=>`${file.path}
364
4
  ${file.contentHash}
365
5
  ${file.byteSize}
366
- `);
367
- return [
368
- `${SKILL_PACK_MANIFEST_SCHEMA_ID}
369
- `,
370
- `${manifest.name}
371
- `,
372
- `${manifest.version}
373
- `,
374
- `${manifest.description.length}
375
- `,
376
- `${manifest.description}
377
- `,
378
- `${manifest.files.length}
379
- `,
380
- ...rows
381
- ].join("");
382
- }
383
- var SKILL_PACK_REJECTIONS = [
384
- "path-unsafe",
385
- "duplicate-path",
386
- "entry-missing",
387
- "file-count-over-cap",
388
- "file-over-cap",
389
- "pack-over-cap",
390
- "size-mismatch",
391
- "hash-mismatch",
392
- "name-mismatch"
393
- ];
394
- function isSkillPackPathSafe(path) {
395
- return path.length > 0 && path.length <= SKILL_PACK_FILE_PATH_MAX_LENGTH && SKILL_PACK_FILE_PATH_PATTERN.test(path);
396
- }
397
- function checkSkillPackManifest(manifest) {
398
- if (manifest.files.length > SKILL_PACK_MAX_FILES) {
399
- return {
400
- ok: false,
401
- reason: "file-count-over-cap",
402
- detail: `${manifest.files.length} files exceeds the ${SKILL_PACK_MAX_FILES} file limit.`
403
- };
404
- }
405
- const seen = /* @__PURE__ */ new Set();
406
- let bytes = 0;
407
- for (const file of manifest.files) {
408
- if (!isSkillPackPathSafe(file.path)) {
409
- return { ok: false, reason: "path-unsafe", detail: `${JSON.stringify(file.path)} is not a safe relative path.` };
410
- }
411
- if (seen.has(file.path)) {
412
- return { ok: false, reason: "duplicate-path", detail: `${JSON.stringify(file.path)} is declared twice.` };
413
- }
414
- seen.add(file.path);
415
- if (file.byteSize > SKILL_PACK_FILE_MAX_BYTES) {
416
- return {
417
- ok: false,
418
- reason: "file-over-cap",
419
- detail: `${JSON.stringify(file.path)} declares ${file.byteSize} bytes, over the ${SKILL_PACK_FILE_MAX_BYTES} byte per-file limit.`
420
- };
421
- }
422
- bytes += file.byteSize;
423
- }
424
- if (!seen.has(SKILL_PACK_ENTRY_PATH)) {
425
- return {
426
- ok: false,
427
- reason: "entry-missing",
428
- detail: `A skill pack must carry ${SKILL_PACK_ENTRY_PATH}.`
429
- };
430
- }
431
- if (bytes > SKILL_PACK_MAX_BYTES) {
432
- return {
433
- ok: false,
434
- reason: "pack-over-cap",
435
- detail: `${bytes} declared bytes exceeds the ${SKILL_PACK_MAX_BYTES} byte pack limit.`
436
- };
437
- }
438
- return { ok: true, bytes };
439
- }
440
- function checkSkillPackFileContent(declared, observed) {
441
- if (!isSkillPackPathSafe(declared.path)) {
442
- return { ok: false, reason: "path-unsafe", detail: `${JSON.stringify(declared.path)} is not a safe relative path.` };
443
- }
444
- if (observed.byteSize > SKILL_PACK_FILE_MAX_BYTES) {
445
- return {
446
- ok: false,
447
- reason: "file-over-cap",
448
- detail: `${JSON.stringify(declared.path)} delivered ${observed.byteSize} bytes, over the ${SKILL_PACK_FILE_MAX_BYTES} byte per-file limit.`
449
- };
450
- }
451
- if (observed.byteSize !== declared.byteSize) {
452
- return {
453
- ok: false,
454
- reason: "size-mismatch",
455
- detail: `${JSON.stringify(declared.path)} declared ${declared.byteSize} bytes and delivered ${observed.byteSize}.`
456
- };
457
- }
458
- if (observed.contentHash !== declared.contentHash) {
459
- return {
460
- ok: false,
461
- reason: "hash-mismatch",
462
- detail: `${JSON.stringify(declared.path)} declared ${declared.contentHash} and delivered ${observed.contentHash}.`
463
- };
464
- }
465
- return { ok: true, bytes: observed.byteSize };
466
- }
467
- var SKILL_FRONTMATTER_FIELDS = ["name", "description"];
468
- var FRONTMATTER_DELIMITER = "---";
469
- var FRONTMATTER_LINE_PATTERN = /^([A-Za-z][A-Za-z0-9_-]*):[ \t]*(.*)$/;
470
- function unquote(value) {
471
- const trimmed = value.trim();
472
- if (trimmed.length >= 2) {
473
- const first = trimmed[0];
474
- const last = trimmed[trimmed.length - 1];
475
- if (first === '"' && last === '"' || first === "'" && last === "'") {
476
- const inner = trimmed.slice(1, -1);
477
- return inner.includes(first) ? void 0 : inner;
478
- }
479
- }
480
- return trimmed;
481
- }
482
- function parseSkillFrontmatter(text) {
483
- const reject = (message) => {
484
- throw new ByokCoreError("skill_pack_frontmatter_invalid", message);
485
- };
486
- const lines = text.split("\n");
487
- if (lines[0]?.trimEnd() !== FRONTMATTER_DELIMITER) {
488
- reject(`A skill file must open with a ${FRONTMATTER_DELIMITER} frontmatter delimiter.`);
489
- }
490
- const closing = lines.findIndex((line, index) => index > 0 && line.trimEnd() === FRONTMATTER_DELIMITER);
491
- if (closing < 0) reject(`The frontmatter block is not closed by a ${FRONTMATTER_DELIMITER} line.`);
492
- const fields = /* @__PURE__ */ new Map();
493
- for (const line of lines.slice(1, closing)) {
494
- if (line.trim().length === 0) continue;
495
- const match = FRONTMATTER_LINE_PATTERN.exec(line);
496
- if (match === null) reject(`Frontmatter line ${JSON.stringify(line)} is not a \`key: value\` pair.`);
497
- const key = match[1];
498
- if (!SKILL_FRONTMATTER_FIELDS.includes(key)) {
499
- reject(
500
- `Frontmatter key ${JSON.stringify(key)} is not one of ${SKILL_FRONTMATTER_FIELDS.join(", ")}; a skill declares no tools, hooks, or environment.`
501
- );
502
- }
503
- if (fields.has(key)) reject(`Frontmatter key ${JSON.stringify(key)} is declared twice.`);
504
- const value = unquote(match[2]);
505
- if (value === void 0 || value.length === 0) {
506
- reject(`Frontmatter key ${JSON.stringify(key)} has no usable value.`);
507
- }
508
- fields.set(key, value);
509
- }
510
- const name = fields.get("name");
511
- const description = fields.get("description");
512
- if (name === void 0) reject("Frontmatter is missing `name`.");
513
- if (description === void 0) reject("Frontmatter is missing `description`.");
514
- if (name.length > SKILL_PACK_NAME_MAX_LENGTH || !SKILL_PACK_NAME_PATTERN.test(name)) {
515
- reject(
516
- `Frontmatter \`name\` must match ${SKILL_PACK_NAME_PATTERN.source} and be at most ${SKILL_PACK_NAME_MAX_LENGTH} characters.`
517
- );
518
- }
519
- if (description.length > SKILL_PACK_DESCRIPTION_MAX_LENGTH) {
520
- reject(`Frontmatter \`description\` exceeds ${SKILL_PACK_DESCRIPTION_MAX_LENGTH} characters.`);
521
- }
522
- return { name, description };
523
- }
524
- function checkSkillPackEntry(manifest, entryText) {
525
- const frontmatter = parseSkillFrontmatter(entryText);
526
- if (frontmatter.name !== manifest.name) {
527
- return {
528
- ok: false,
529
- reason: "name-mismatch",
530
- detail: `${SKILL_PACK_ENTRY_PATH} declares ${JSON.stringify(frontmatter.name)} but the manifest declares ${JSON.stringify(manifest.name)}.`
531
- };
532
- }
533
- return { ok: true, bytes: new TextEncoder().encode(entryText).length };
534
- }
535
-
536
- // src/stores.ts
537
- var CORE_STORE_NAMES = [
538
- "mailbox",
539
- "board",
540
- "truth",
541
- "presence",
542
- "objects",
543
- "quota",
544
- "skillPacks"
545
- ];
546
-
547
- // src/ports-contract.ts
548
- var CORE_NON_COMPOSITION_PORT_NAMES = [];
549
- var CORE_PORT_METHODS = {
550
- mailbox: [
551
- "append",
552
- "readUsage",
553
- "readAfter",
554
- "recordDelivery",
555
- "advanceCursor",
556
- "readCursor",
557
- "collectExpired"
558
- ],
559
- board: ["create", "get", "list", "claim", "unclaim", "updateStatus"],
560
- truth: ["writeTerminal", "writeSnapshot", "getRecord", "listManifest"],
561
- presence: ["publish", "read", "list", "collectExpired"],
562
- objects: [
563
- "putManifest",
564
- "commit",
565
- "get",
566
- "list",
567
- "addReference",
568
- "removeReference",
569
- "markDeletePending",
570
- "markDeleted",
571
- "collectExpired"
572
- ],
573
- quota: [
574
- "readEntitlement",
575
- "writeEntitlement",
576
- "readUsage",
577
- "readStatus",
578
- "readReservation",
579
- "reserve",
580
- "finalizeReservation",
581
- "abortReservation",
582
- "collectExpired"
583
- ],
584
- skillPacks: ["publish", "get", "list", "readFile"]
585
- };
586
- var CORE_PORT_INTERFACES = {
587
- mailbox: "MailboxStore",
588
- board: "BoardStore",
589
- truth: "TruthStore",
590
- presence: "PresenceStore",
591
- objects: "ObjectStore",
592
- quota: "QuotaStore",
593
- skillPacks: "SkillPackStore"
594
- };
595
- var DEVICE_PROOF_SCHEMA_ID = "byok-device-proof-v1";
596
- var DEVICE_PROOF_DOMAIN_PREFIX = "byok-device-proof-v1\n";
597
- var DEVICE_PROOF_VERSION = 1;
598
- var DEVICE_PROOF_ALGORITHMS = ["ed25519"];
599
- var DEVICE_PROOF_HEADER = "x-byok-device-proof";
600
- var BASE64URL_PATTERN = /^[A-Za-z0-9_-]+$/;
601
- var HTTP_METHOD_PATTERN = /^[A-Z]+$/;
602
- function canonicalizationFailure(path, reason) {
603
- return new ByokCoreError(
604
- "proof_canonicalization_failed",
605
- `Cannot canonicalize ${path}: ${reason}`
606
- );
607
- }
608
- function isPlainObject(value) {
609
- const prototype = Object.getPrototypeOf(value);
610
- return prototype === Object.prototype || prototype === null;
611
- }
612
- function canonicalizeValue(value, path, seen) {
613
- if (value === null) return "null";
614
- switch (typeof value) {
615
- case "boolean":
616
- return value ? "true" : "false";
617
- case "string":
618
- return JSON.stringify(value);
619
- case "number": {
620
- if (!Number.isSafeInteger(value)) {
621
- throw canonicalizationFailure(
622
- path,
623
- `only safe integers are canonicalizable, received ${String(value)}`
624
- );
625
- }
626
- return String(value === 0 ? 0 : value);
627
- }
628
- case "undefined":
629
- throw canonicalizationFailure(path, "undefined has no canonical form");
630
- case "bigint":
631
- throw canonicalizationFailure(path, "bigint is not a JSON type");
632
- case "function":
633
- case "symbol":
634
- throw canonicalizationFailure(path, `${typeof value} is not a JSON type`);
635
- case "object":
636
- break;
637
- default:
638
- throw canonicalizationFailure(path, `unsupported type ${typeof value}`);
639
- }
640
- const objectValue = value;
641
- if (seen.has(objectValue)) {
642
- throw canonicalizationFailure(path, "circular reference");
643
- }
644
- seen.add(objectValue);
645
- try {
646
- if (Array.isArray(objectValue)) {
647
- const parts2 = objectValue.map(
648
- (entry, index) => canonicalizeValue(entry, `${path}[${index}]`, seen)
649
- );
650
- return `[${parts2.join(",")}]`;
651
- }
652
- if (!isPlainObject(objectValue)) {
653
- throw canonicalizationFailure(
654
- path,
655
- "only plain objects and arrays are canonicalizable"
656
- );
657
- }
658
- const keys = Object.keys(objectValue).sort();
659
- const record = objectValue;
660
- const parts = keys.map((key) => {
661
- const encodedKey = JSON.stringify(key);
662
- const encodedValue = canonicalizeValue(record[key], `${path}.${key}`, seen);
663
- return `${encodedKey}:${encodedValue}`;
664
- });
665
- return `{${parts.join(",")}}`;
666
- } finally {
667
- seen.delete(objectValue);
668
- }
669
- }
670
- function canonicalizeJson(value) {
671
- return canonicalizeValue(value, "<root>", /* @__PURE__ */ new Set());
672
- }
673
- function canonicalizeJsonBytes(value) {
674
- return new TextEncoder().encode(canonicalizeJson(value));
675
- }
676
- var DeviceProofProtectedClaimsSchema = z.strictObject({
677
- version: z.literal(DEVICE_PROOF_VERSION),
678
- tenantId: z.string().min(1),
679
- productId: z.string().min(1),
680
- deviceId: z.string().min(1),
681
- /** Identity of the signing key, resolved against the device row at verify time. */
682
- keyId: z.string().min(1),
683
- /** Rotation generation. An older epoch is rejected by the verifier, not here. */
684
- keyEpoch: z.number().int().nonnegative(),
685
- /** Idempotency key: exact replay returns the original result, reuse with a different body is a conflict. */
686
- requestId: z.string().min(1),
687
- /** Logical operation, e.g. `truth.write`. */
688
- operation: z.string().min(1),
689
- /** Tenant-scoped resource the operation targets. */
690
- resource: z.string().min(1),
691
- method: z.string().regex(HTTP_METHOD_PATTERN).optional(),
692
- path: z.string().startsWith("/").optional(),
693
- operationId: z.string().min(1).optional(),
694
- bodySha256: z.string().regex(CONTENT_HASH_PATTERN),
695
- /** Bound alongside the hash so a truncation attack cannot hide behind a hash collision claim. */
696
- bodySize: z.number().int().nonnegative(),
697
- issuedAt: z.iso.datetime(),
698
- expiresAt: z.iso.datetime().optional(),
699
- nonce: z.string().min(1).optional()
700
- }).superRefine((claims, ctx) => {
701
- const hasRequestLine = claims.method !== void 0 && claims.path !== void 0;
702
- const hasOperationId = claims.operationId !== void 0;
703
- if (hasRequestLine === hasOperationId) {
704
- ctx.addIssue({
705
- code: "custom",
706
- message: "Claims must carry exactly one request binding: both `method` and `path`, or `operationId`."
707
- });
708
- }
709
- if (!hasRequestLine && (claims.method !== void 0 || claims.path !== void 0)) {
710
- ctx.addIssue({
711
- code: "custom",
712
- message: "`method` and `path` must be supplied together."
713
- });
714
- }
715
- });
716
- var DeviceProofEnvelopeV1Schema = z.strictObject({
717
- schema: z.literal(DEVICE_PROOF_SCHEMA_ID),
718
- algorithm: z.enum(DEVICE_PROOF_ALGORITHMS),
719
- protected: DeviceProofProtectedClaimsSchema,
720
- /** base64url, unpadded. */
721
- signature: z.string().regex(BASE64URL_PATTERN)
722
- });
723
- function parseDeviceProofEnvelope(input) {
724
- const result = DeviceProofEnvelopeV1Schema.safeParse(input);
725
- if (!result.success) {
726
- throw new ByokCoreError(
727
- "proof_envelope_invalid",
728
- `Invalid device proof envelope: ${result.error.issues.map((issue) => `${issue.path.join(".") || "<root>"}: ${issue.message}`).join("; ")}`,
729
- { cause: result.error }
730
- );
731
- }
732
- return result.data;
733
- }
734
- function deviceProofCanonicalClaims(claims) {
735
- const canonical = {
736
- version: claims.version,
737
- tenantId: claims.tenantId,
738
- productId: claims.productId,
739
- deviceId: claims.deviceId,
740
- keyId: claims.keyId,
741
- keyEpoch: claims.keyEpoch,
742
- requestId: claims.requestId,
743
- operation: claims.operation,
744
- resource: claims.resource,
745
- bodySha256: claims.bodySha256,
746
- bodySize: claims.bodySize,
747
- issuedAt: claims.issuedAt
748
- };
749
- if (claims.method !== void 0) canonical["method"] = claims.method;
750
- if (claims.path !== void 0) canonical["path"] = claims.path;
751
- if (claims.operationId !== void 0) canonical["operationId"] = claims.operationId;
752
- if (claims.expiresAt !== void 0) canonical["expiresAt"] = claims.expiresAt;
753
- if (claims.nonce !== void 0) canonical["nonce"] = claims.nonce;
754
- return canonical;
755
- }
756
- function deviceProofCanonicalJson(claims) {
757
- return canonicalizeJson(deviceProofCanonicalClaims(claims));
758
- }
759
- function deviceProofSigningInput(claims) {
760
- return new TextEncoder().encode(
761
- DEVICE_PROOF_DOMAIN_PREFIX + deviceProofCanonicalJson(claims)
762
- );
763
- }
764
- var DEVICE_ASSERTION_SCHEMA_ID = "byok-device-assertion-v1";
765
- var DEVICE_ASSERTION_DOMAIN_PREFIX = "byok-device-assertion-v1\n";
766
- var DEVICE_ASSERTION_VERSION = 1;
767
- var TASK_ASSERTION_SCHEMA_ID = "byok-task-assertion-v1";
768
- var TASK_ASSERTION_DOMAIN_PREFIX = "byok-task-assertion-v1\n";
769
- var TASK_ASSERTION_VERSION = 1;
770
- var DEVICE_ASSERTION_ALGORITHMS = ["ed25519"];
771
- var DEVICE_ASSERTION_DEFAULT_TTL_MS = 12e4;
772
- var DEVICE_ASSERTION_MAX_TTL_MS = 3e5;
773
- var DEVICE_ASSERTION_AUDIENCE_MAX_BYTES = 256;
774
- var JTI_PATTERN = /^[A-Za-z0-9_-]{22}$/;
775
- var SIGNATURE_PATTERN = /^[A-Za-z0-9_-]{86}$/;
776
- function utf8ByteLength(value) {
777
- return new TextEncoder().encode(value).length;
778
- }
779
- var ASSERTION_AUDIENCE = z.string().min(1).refine((value) => utf8ByteLength(value) <= DEVICE_ASSERTION_AUDIENCE_MAX_BYTES, {
780
- message: `audience must be at most ${DEVICE_ASSERTION_AUDIENCE_MAX_BYTES} UTF-8 bytes`
781
- });
782
- var DeviceAssertionClaimsSchema = z.strictObject({
783
- version: z.literal(DEVICE_ASSERTION_VERSION),
784
- /** The paired server's origin, normalized (`new URL(serverUrl).origin`). */
785
- issuer: z.string().min(1),
786
- productId: z.string().min(1),
787
- /** The device row this assertion claims to be. A lookup key, never authority — the row is the authority. */
788
- deviceId: z.string().min(1),
789
- /** Exactly one audience, compared with `===` by every verifier. Never an array, never a prefix. */
790
- audience: ASSERTION_AUDIENCE,
791
- /** 128-bit CSPRNG token, base64url unpadded. The verifier burns it; the signer never reuses one. */
792
- jti: z.string().regex(JTI_PATTERN),
793
- issuedAt: z.iso.datetime(),
794
- expiresAt: z.iso.datetime()
795
- });
796
- var DeviceAssertionEnvelopeV1Schema = z.strictObject({
797
- schema: z.literal(DEVICE_ASSERTION_SCHEMA_ID),
798
- algorithm: z.enum(DEVICE_ASSERTION_ALGORITHMS),
799
- protected: DeviceAssertionClaimsSchema,
800
- /** Raw 64-byte Ed25519 signature, base64url unpadded. */
801
- signature: z.string().regex(SIGNATURE_PATTERN)
802
- });
803
- function parseDeviceAssertionEnvelope(input) {
804
- const result = DeviceAssertionEnvelopeV1Schema.safeParse(input);
805
- if (!result.success) {
806
- throw new ByokCoreError(
807
- "assertion_envelope_invalid",
808
- `Invalid device assertion envelope: ${result.error.issues.map((issue) => `${issue.path.join(".") || "<root>"}: ${issue.message}`).join("; ")}`,
809
- { cause: result.error }
810
- );
811
- }
812
- return result.data;
813
- }
814
- function deviceAssertionCanonicalClaims(claims) {
815
- const canonical = {
816
- version: claims.version,
817
- issuer: claims.issuer,
818
- productId: claims.productId,
819
- deviceId: claims.deviceId,
820
- audience: claims.audience,
821
- jti: claims.jti,
822
- issuedAt: claims.issuedAt,
823
- expiresAt: claims.expiresAt
824
- };
825
- return canonical;
826
- }
827
- function deviceAssertionCanonicalJson(claims) {
828
- return canonicalizeJson(deviceAssertionCanonicalClaims(claims));
829
- }
830
- function deviceAssertionSigningInput(claims) {
831
- return new TextEncoder().encode(
832
- DEVICE_ASSERTION_DOMAIN_PREFIX + deviceAssertionCanonicalJson(claims)
833
- );
834
- }
835
- function parseInstant(value) {
836
- const parsed = Date.parse(value);
837
- return Number.isFinite(parsed) ? parsed : void 0;
838
- }
839
- var EDDSA_JWK_X_PATTERN = /^[A-Za-z0-9_-]{43}$/;
840
- function isUsableDeviceRow(row) {
841
- if (row === null || typeof row !== "object") return false;
842
- if (row.revoked !== false) return false;
843
- const key = row.publicKeyJwkX;
844
- return typeof key === "string" && EDDSA_JWK_X_PATTERN.test(key);
845
- }
846
- function resolveMaxLifetimeMs(requested) {
847
- const maxLifetimeMs = requested ?? DEVICE_ASSERTION_MAX_TTL_MS;
848
- if (!Number.isSafeInteger(maxLifetimeMs) || maxLifetimeMs <= 0 || maxLifetimeMs > DEVICE_ASSERTION_MAX_TTL_MS) {
849
- return void 0;
850
- }
851
- return maxLifetimeMs;
852
- }
853
- function assertionWindowAdmits(claims, now, maxLifetimeMs) {
854
- const issuedAt = parseInstant(claims.issuedAt);
855
- const expiresAt = parseInstant(claims.expiresAt);
856
- if (issuedAt === void 0 || expiresAt === void 0) return false;
857
- if (expiresAt <= issuedAt) return false;
858
- if (expiresAt - issuedAt > maxLifetimeMs) return false;
859
- const instant = now.getTime();
860
- if (!Number.isFinite(instant)) return false;
861
- return instant >= issuedAt && instant < expiresAt;
862
- }
863
- async function verifyDeviceAssertion(input, deps) {
864
- const maxLifetimeMs = resolveMaxLifetimeMs(deps.maxLifetimeMs);
865
- if (maxLifetimeMs === void 0) return void 0;
866
- let envelope;
867
- try {
868
- envelope = parseDeviceAssertionEnvelope(input);
869
- } catch {
870
- return void 0;
871
- }
872
- const claims = envelope.protected;
873
- const row = await deps.lookupDevice(claims.deviceId);
874
- if (!isUsableDeviceRow(row)) return void 0;
875
- if (!assertionWindowAdmits(claims, deps.now, maxLifetimeMs)) return void 0;
876
- const verified = await deps.verifier.verify({
877
- algorithm: envelope.algorithm,
878
- // The row's key, never an envelope-supplied one.
879
- publicKey: row.publicKeyJwkX,
880
- signature: envelope.signature,
881
- signingInput: deviceAssertionSigningInput(claims)
882
- });
883
- if (!verified) return void 0;
884
- return claims;
885
- }
886
- function isNonEmptyExactString(value) {
887
- return typeof value === "string" && value.length > 0 && value.trim() === value;
888
- }
889
- function isAuthorityRow(row, requestedDeviceId) {
890
- if (row === null || typeof row !== "object") return false;
891
- const candidate = row;
892
- return isTenantId(candidate.tenantId) && isNonEmptyExactString(candidate.productId) && candidate.deviceId === requestedDeviceId;
893
- }
894
- async function authenticateDeviceAssertion(input, deps) {
895
- if (!isNonEmptyExactString(deps.expected.issuer) || !isNonEmptyExactString(deps.expected.productId) || !isNonEmptyExactString(deps.expected.audience) || utf8ByteLength(deps.expected.audience) > DEVICE_ASSERTION_AUDIENCE_MAX_BYTES) {
896
- return void 0;
897
- }
898
- let authorityRow;
899
- const claims = await verifyDeviceAssertion(input, {
900
- verifier: deps.verifier,
901
- lookupDevice: async (deviceId) => {
902
- const row = await deps.lookupDevice(deviceId);
903
- if (!isAuthorityRow(row, deviceId)) return void 0;
904
- authorityRow = row;
905
- return { publicKeyJwkX: row.publicKeyJwkX, revoked: row.revoked };
906
- },
907
- now: deps.now,
908
- ...deps.maxLifetimeMs === void 0 ? {} : { maxLifetimeMs: deps.maxLifetimeMs }
909
- });
910
- if (claims === void 0 || authorityRow === void 0) return void 0;
911
- if (claims.issuer !== deps.expected.issuer || claims.productId !== deps.expected.productId || claims.audience !== deps.expected.audience || authorityRow.productId !== deps.expected.productId || authorityRow.deviceId !== claims.deviceId) {
912
- return void 0;
913
- }
914
- const consumed = await deps.replay.consume({
915
- schema: DEVICE_ASSERTION_SCHEMA_ID,
916
- tenantId: authorityRow.tenantId,
917
- issuer: claims.issuer,
918
- productId: authorityRow.productId,
919
- deviceId: authorityRow.deviceId,
920
- audience: claims.audience,
921
- jti: claims.jti,
922
- expiresAt: claims.expiresAt
923
- });
924
- if (!consumed) return void 0;
925
- return {
926
- lane: "device",
927
- device: {
928
- kind: "device",
929
- tenantId: authorityRow.tenantId,
930
- productId: authorityRow.productId,
931
- deviceId: authorityRow.deviceId
932
- },
933
- issuer: claims.issuer,
934
- audience: claims.audience,
935
- jti: claims.jti,
936
- issuedAt: claims.issuedAt,
937
- expiresAt: claims.expiresAt
938
- };
939
- }
940
- var TASK_ASSERTION_AGENT_REF_MAX_BYTES = 160;
941
- var TASK_ASSERTION_TOOLSET_ID_MAX_LENGTH = 128;
942
- var AGENT_REF_VALUE = z.string().min(1).max(TASK_ASSERTION_AGENT_REF_MAX_BYTES).refine((value) => utf8ByteLength(value) <= TASK_ASSERTION_AGENT_REF_MAX_BYTES, {
943
- message: `AgentRef values must not exceed ${TASK_ASSERTION_AGENT_REF_MAX_BYTES} UTF-8 bytes`
944
- }).regex(/^[^\u0000-\u001f\u007f\r\n]+$/u, "AgentRef values must not contain control characters");
945
- var WINDOWS_RESERVED_AGENT_SEGMENT = /^(?:con|prn|aux|nul|com[1-9]|lpt[1-9])(?:\..*)?$/iu;
946
- var TaskAssertionAgentRefSchema = z.object({
947
- agentId: AGENT_REF_VALUE.regex(/^[^\\/:<>"|?*]+$/u, "agentId must be one portable pathname segment").refine((value) => value !== "." && value !== "..", "agentId must not be a dot segment").refine(
948
- (value) => !/[. ]$/u.test(value) && !WINDOWS_RESERVED_AGENT_SEGMENT.test(value),
949
- "agentId must be a portable Windows-safe pathname segment"
950
- ),
951
- /**
952
- * From the FROZEN offer. Contract §8.1 forbids substituting the current
953
- * revision read from a working directory's `profile.json`; this module
954
- * cannot enforce that (it never reads a file) — the signer's contract does.
955
- */
956
- profileRevision: AGENT_REF_VALUE
957
- }).strict();
958
- var TOOLSET_ID_VALUE = z.string().min(1).max(TASK_ASSERTION_TOOLSET_ID_MAX_LENGTH).regex(/^[a-z0-9]+(?:[._-][a-z0-9]+)*$/u, "toolset ids must be lowercase logical identifiers");
959
- var TASK_ID_VALUE = z.string().min(1);
960
- var TaskAssertionClaimsSchema = z.strictObject({
961
- version: z.literal(TASK_ASSERTION_VERSION),
962
- /** The paired server's origin, normalized (`new URL(serverUrl).origin`). */
963
- issuer: z.string().min(1),
964
- productId: z.string().min(1),
965
- /** The device row this assertion claims to be. A lookup key, never authority. */
966
- deviceId: z.string().min(1),
967
- /** Exactly one audience, compared with `===`. Participates in the replay key (§8.2(2)). */
968
- audience: ASSERTION_AUDIENCE,
969
- /** Same 128-bit CSPRNG token shape the device lane uses; burned once per `schema` segment. */
970
- jti: z.string().regex(JTI_PATTERN),
971
- issuedAt: z.iso.datetime(),
972
- expiresAt: z.iso.datetime(),
973
- /** The only execution locator (§8.1). No attemptId is minted beside it. */
974
- taskId: TASK_ID_VALUE,
975
- /** The frozen offer's Agent identity, at the SDK's full AgentRef strength. */
976
- agentRef: TaskAssertionAgentRefSchema,
977
- /** The frozen toolset this invocation belongs to (the wire's `toolset` field). */
978
- toolsetId: TOOLSET_ID_VALUE
979
- });
980
- var TaskAssertionEnvelopeV1Schema = z.strictObject({
981
- schema: z.literal(TASK_ASSERTION_SCHEMA_ID),
982
- algorithm: z.enum(DEVICE_ASSERTION_ALGORITHMS),
983
- protected: TaskAssertionClaimsSchema,
984
- /** Raw 64-byte Ed25519 signature, base64url unpadded — the device lane's encoding, unchanged. */
985
- signature: z.string().regex(SIGNATURE_PATTERN)
986
- });
987
- function parseTaskAssertionEnvelope(input) {
988
- const result = TaskAssertionEnvelopeV1Schema.safeParse(input);
989
- if (!result.success) {
990
- throw new ByokCoreError(
991
- "assertion_envelope_invalid",
992
- `Invalid task assertion envelope: ${result.error.issues.map((issue) => `${issue.path.join(".") || "<root>"}: ${issue.message}`).join("; ")}`,
993
- { cause: result.error }
994
- );
995
- }
996
- return result.data;
997
- }
998
- function taskAssertionCanonicalClaims(claims) {
999
- const canonical = {
1000
- version: claims.version,
1001
- issuer: claims.issuer,
1002
- productId: claims.productId,
1003
- deviceId: claims.deviceId,
1004
- audience: claims.audience,
1005
- jti: claims.jti,
1006
- issuedAt: claims.issuedAt,
1007
- expiresAt: claims.expiresAt,
1008
- taskId: claims.taskId,
1009
- agentRef: {
1010
- agentId: claims.agentRef.agentId,
1011
- profileRevision: claims.agentRef.profileRevision
1012
- },
1013
- toolsetId: claims.toolsetId
1014
- };
1015
- return canonical;
1016
- }
1017
- function taskAssertionCanonicalJson(claims) {
1018
- return canonicalizeJson(taskAssertionCanonicalClaims(claims));
1019
- }
1020
- function taskAssertionSigningInput(claims) {
1021
- return new TextEncoder().encode(
1022
- TASK_ASSERTION_DOMAIN_PREFIX + taskAssertionCanonicalJson(claims)
1023
- );
1024
- }
1025
- async function verifyTaskAssertion(input, deps) {
1026
- const maxLifetimeMs = resolveMaxLifetimeMs(deps.maxLifetimeMs);
1027
- if (maxLifetimeMs === void 0) return void 0;
1028
- let envelope;
1029
- try {
1030
- envelope = parseTaskAssertionEnvelope(input);
1031
- } catch {
1032
- return void 0;
1033
- }
1034
- const claims = envelope.protected;
1035
- const row = await deps.lookupDevice(claims.deviceId);
1036
- if (!isUsableDeviceRow(row)) return void 0;
1037
- if (!assertionWindowAdmits(claims, deps.now, maxLifetimeMs)) return void 0;
1038
- const verified = await deps.verifier.verify({
1039
- algorithm: envelope.algorithm,
1040
- publicKey: row.publicKeyJwkX,
1041
- signature: envelope.signature,
1042
- signingInput: taskAssertionSigningInput(claims)
1043
- });
1044
- if (!verified) return void 0;
1045
- return claims;
1046
- }
1047
- async function authenticateTaskAssertion(input, deps) {
1048
- if (!isNonEmptyExactString(deps.expected.issuer) || !isNonEmptyExactString(deps.expected.productId) || !isNonEmptyExactString(deps.expected.audience) || utf8ByteLength(deps.expected.audience) > DEVICE_ASSERTION_AUDIENCE_MAX_BYTES) {
1049
- return void 0;
1050
- }
1051
- let authorityRow;
1052
- const claims = await verifyTaskAssertion(input, {
1053
- verifier: deps.verifier,
1054
- lookupDevice: async (deviceId) => {
1055
- const row = await deps.lookupDevice(deviceId);
1056
- if (!isAuthorityRow(row, deviceId)) return void 0;
1057
- authorityRow = row;
1058
- return { publicKeyJwkX: row.publicKeyJwkX, revoked: row.revoked };
1059
- },
1060
- now: deps.now,
1061
- ...deps.maxLifetimeMs === void 0 ? {} : { maxLifetimeMs: deps.maxLifetimeMs }
1062
- });
1063
- if (claims === void 0 || authorityRow === void 0) return void 0;
1064
- if (claims.issuer !== deps.expected.issuer || claims.productId !== deps.expected.productId || claims.audience !== deps.expected.audience || authorityRow.productId !== deps.expected.productId || authorityRow.deviceId !== claims.deviceId) {
1065
- return void 0;
1066
- }
1067
- const consumed = await deps.replay.consume({
1068
- schema: TASK_ASSERTION_SCHEMA_ID,
1069
- tenantId: authorityRow.tenantId,
1070
- issuer: claims.issuer,
1071
- productId: authorityRow.productId,
1072
- deviceId: authorityRow.deviceId,
1073
- audience: claims.audience,
1074
- jti: claims.jti,
1075
- expiresAt: claims.expiresAt
1076
- });
1077
- if (!consumed) return void 0;
1078
- return {
1079
- lane: "task",
1080
- device: {
1081
- kind: "device",
1082
- tenantId: authorityRow.tenantId,
1083
- productId: authorityRow.productId,
1084
- deviceId: authorityRow.deviceId
1085
- },
1086
- issuer: claims.issuer,
1087
- audience: claims.audience,
1088
- jti: claims.jti,
1089
- issuedAt: claims.issuedAt,
1090
- expiresAt: claims.expiresAt,
1091
- taskId: claims.taskId,
1092
- agentRef: claims.agentRef,
1093
- toolsetId: claims.toolsetId
1094
- };
1095
- }
1096
-
1097
- // src/webcrypto.ts
1098
- function webCryptoSubtle() {
1099
- const subtle = globalThis.crypto?.subtle;
1100
- if (subtle === void 0) {
1101
- throw new ByokCoreError("webcrypto_unavailable", "WebCrypto subtle is unavailable in this runtime");
1102
- }
1103
- return subtle;
1104
- }
1105
-
1106
- // src/hpke.ts
1107
- var KEM_ID = 16;
1108
- var KDF_ID = 1;
1109
- var AEAD_ID = 1;
1110
- var HPKE_P256_PUBLIC_KEY_BYTES = 65;
1111
- var N_SECRET = 32;
1112
- var N_K = 16;
1113
- var N_N = 12;
1114
- var N_H = 32;
1115
- var HPKE_AEAD_TAG_BYTES = 16;
1116
- var MODE_BASE = 0;
1117
- var HPKE_V1 = utf8("HPKE-v1");
1118
- var KEM_SUITE_ID = concat(utf8("KEM"), i2osp(KEM_ID, 2));
1119
- var HPKE_SUITE_ID = concat(utf8("HPKE"), i2osp(KEM_ID, 2), i2osp(KDF_ID, 2), i2osp(AEAD_ID, 2));
1120
- var EMPTY = new Uint8Array(0);
1121
- var ZERO_SALT = new Uint8Array(N_H);
1122
- function utf8(value) {
1123
- return new TextEncoder().encode(value);
1124
- }
1125
- function concat(...parts) {
1126
- const length = parts.reduce((total, part) => total + part.byteLength, 0);
1127
- const out = new Uint8Array(length);
1128
- let offset = 0;
1129
- for (const part of parts) {
1130
- out.set(part, offset);
1131
- offset += part.byteLength;
1132
- }
1133
- return out;
1134
- }
1135
- function i2osp(value, length) {
1136
- if (!Number.isSafeInteger(value) || value < 0) {
1137
- throw new ByokCoreError("sealed_secret_invalid", "HPKE integer encoding requires a non-negative safe integer");
1138
- }
1139
- const out = new Uint8Array(length);
1140
- let remaining = value;
1141
- for (let index = length - 1; index >= 0; index -= 1) {
1142
- out[index] = remaining % 256;
1143
- remaining = Math.floor(remaining / 256);
1144
- }
1145
- if (remaining !== 0) {
1146
- throw new ByokCoreError("sealed_secret_invalid", "HPKE integer does not fit the requested width");
1147
- }
1148
- return out;
1149
- }
1150
- function copy(bytes) {
1151
- return new Uint8Array(bytes);
1152
- }
1153
- async function hmacSha256(key, data) {
1154
- const subtle = webCryptoSubtle();
1155
- const hmacKey = await subtle.importKey("raw", copy(key), { name: "HMAC", hash: "SHA-256" }, false, ["sign"]);
1156
- return new Uint8Array(await subtle.sign("HMAC", hmacKey, copy(data)));
1157
- }
1158
- async function hkdfExpand(prk, info, length) {
1159
- if (length > 255 * N_H) {
1160
- throw new ByokCoreError("sealed_secret_invalid", "HKDF output length exceeds its limit");
1161
- }
1162
- const out = new Uint8Array(length);
1163
- let previous = EMPTY;
1164
- let offset = 0;
1165
- for (let counter = 1; offset < length; counter += 1) {
1166
- previous = await hmacSha256(prk, concat(previous, info, Uint8Array.of(counter)));
1167
- const take = Math.min(previous.byteLength, length - offset);
1168
- out.set(previous.subarray(0, take), offset);
1169
- offset += take;
1170
- }
1171
- return out;
1172
- }
1173
- async function labeledExtract(suiteId, salt, label, ikm) {
1174
- return hmacSha256(salt.byteLength === 0 ? ZERO_SALT : salt, concat(HPKE_V1, suiteId, utf8(label), ikm));
1175
- }
1176
- async function labeledExpand(suiteId, prk, label, info, length) {
1177
- return hkdfExpand(prk, concat(i2osp(length, 2), HPKE_V1, suiteId, utf8(label), info), length);
1178
- }
1179
- async function importP256PublicKey(raw) {
1180
- if (raw.byteLength !== HPKE_P256_PUBLIC_KEY_BYTES || raw[0] !== 4) {
1181
- throw new ByokCoreError("sealed_secret_invalid", "P-256 public key must be a 65-byte uncompressed point");
1182
- }
1183
- try {
1184
- return await webCryptoSubtle().importKey("raw", copy(raw), { name: "ECDH", namedCurve: "P-256" }, true, []);
1185
- } catch (cause) {
1186
- throw new ByokCoreError("sealed_secret_invalid", "P-256 public key is not a valid curve point", { cause });
1187
- }
1188
- }
1189
- async function ecdhP256(privateKey, publicKey) {
1190
- const bits = await webCryptoSubtle().deriveBits({ name: "ECDH", public: publicKey }, privateKey, N_SECRET * 8);
1191
- return new Uint8Array(bits);
1192
- }
1193
- async function extractAndExpand(dh, kemContext) {
1194
- const eaePrk = await labeledExtract(KEM_SUITE_ID, EMPTY, "eae_prk", dh);
1195
- return labeledExpand(KEM_SUITE_ID, eaePrk, "shared_secret", kemContext, N_SECRET);
1196
- }
1197
- async function keyScheduleBase(sharedSecret, info) {
1198
- const pskIdHash = await labeledExtract(HPKE_SUITE_ID, EMPTY, "psk_id_hash", EMPTY);
1199
- const infoHash = await labeledExtract(HPKE_SUITE_ID, EMPTY, "info_hash", info);
1200
- const context = concat(Uint8Array.of(MODE_BASE), pskIdHash, infoHash);
1201
- const secret = await labeledExtract(HPKE_SUITE_ID, sharedSecret, "secret", EMPTY);
1202
- return {
1203
- sharedSecret,
1204
- key: await labeledExpand(HPKE_SUITE_ID, secret, "key", context, N_K),
1205
- baseNonce: await labeledExpand(HPKE_SUITE_ID, secret, "base_nonce", context, N_N),
1206
- exporterSecret: await labeledExpand(HPKE_SUITE_ID, secret, "exp", context, N_H)
1207
- };
1208
- }
1209
- async function setupBaseSender(recipientPublicKey, info, ephemeral) {
1210
- const pkR = await importP256PublicKey(recipientPublicKey);
1211
- let pair = ephemeral;
1212
- if (pair === void 0) {
1213
- const subtle = webCryptoSubtle();
1214
- const generated = await subtle.generateKey({ name: "ECDH", namedCurve: "P-256" }, true, ["deriveBits"]);
1215
- pair = {
1216
- privateKey: generated.privateKey,
1217
- publicKey: new Uint8Array(await subtle.exportKey("raw", generated.publicKey))
1218
- };
1219
- }
1220
- const enc = copy(pair.publicKey);
1221
- const dh = await ecdhP256(pair.privateKey, pkR);
1222
- const sharedSecret = await extractAndExpand(dh, concat(enc, recipientPublicKey));
1223
- return { enc, schedule: await keyScheduleBase(sharedSecret, info) };
1224
- }
1225
- async function setupBaseRecipient(enc, recipientPrivateKey, recipientPublicKey, info) {
1226
- if (recipientPublicKey.byteLength !== HPKE_P256_PUBLIC_KEY_BYTES || recipientPublicKey[0] !== 4) {
1227
- throw new ByokCoreError("sealed_secret_invalid", "P-256 recipient public key must be a 65-byte uncompressed point");
1228
- }
1229
- const pkE = await importP256PublicKey(enc);
1230
- let dh;
1231
- try {
1232
- dh = await ecdhP256(recipientPrivateKey, pkE);
1233
- } catch (cause) {
1234
- throw new ByokCoreError("sealed_secret_open_failed", "HPKE decapsulation failed", { cause });
1235
- }
1236
- const sharedSecret = await extractAndExpand(dh, concat(enc, recipientPublicKey));
1237
- return keyScheduleBase(sharedSecret, info);
1238
- }
1239
- function hpkeNonce(baseNonce, sequence) {
1240
- const seq = i2osp(sequence, N_N);
1241
- const out = new Uint8Array(N_N);
1242
- for (let index = 0; index < N_N; index += 1) out[index] = baseNonce[index] ^ seq[index];
1243
- return out;
1244
- }
1245
- async function aesGcmKey(key, usage) {
1246
- return webCryptoSubtle().importKey("raw", copy(key), { name: "AES-GCM" }, false, [usage]);
1247
- }
1248
- async function hpkeAeadSeal(schedule, sequence, aad, plaintext) {
1249
- const ciphertext = await webCryptoSubtle().encrypt(
1250
- { name: "AES-GCM", iv: hpkeNonce(schedule.baseNonce, sequence), additionalData: copy(aad), tagLength: 128 },
1251
- await aesGcmKey(schedule.key, "encrypt"),
1252
- copy(plaintext)
1253
- );
1254
- return new Uint8Array(ciphertext);
1255
- }
1256
- async function hpkeAeadOpen(schedule, sequence, aad, ciphertext) {
1257
- if (ciphertext.byteLength < HPKE_AEAD_TAG_BYTES) {
1258
- throw new ByokCoreError("sealed_secret_invalid", "HPKE ciphertext is shorter than its authentication tag");
1259
- }
1260
- try {
1261
- const plaintext = await webCryptoSubtle().decrypt(
1262
- { name: "AES-GCM", iv: hpkeNonce(schedule.baseNonce, sequence), additionalData: copy(aad), tagLength: 128 },
1263
- await aesGcmKey(schedule.key, "decrypt"),
1264
- copy(ciphertext)
1265
- );
1266
- return new Uint8Array(plaintext);
1267
- } catch (cause) {
1268
- throw new ByokCoreError("sealed_secret_open_failed", "HPKE ciphertext failed authentication", { cause });
1269
- }
1270
- }
1271
- async function hpkeSealBase(input) {
1272
- const { enc, schedule } = await setupBaseSender(input.recipientPublicKey, input.info);
1273
- return { enc, ciphertext: await hpkeAeadSeal(schedule, 0, input.aad, input.plaintext) };
1274
- }
1275
- async function hpkeOpenBase(input) {
1276
- const schedule = await setupBaseRecipient(input.enc, input.recipientPrivateKey, input.recipientPublicKey, input.info);
1277
- return hpkeAeadOpen(schedule, 0, input.aad, input.ciphertext);
1278
- }
1279
-
1280
- // src/total-parse.ts
1281
- function safeParseTotal(schema, value) {
1282
- try {
1283
- return schema.safeParse(value);
1284
- } catch {
1285
- return { success: false };
1286
- }
1287
- }
1288
-
1289
- // src/sealed-provider-secret.ts
1290
- var PROVIDER_SECRET_HPKE_SUITE = "hpke-base.dhkem-p256-hkdf-sha256.hkdf-sha256.aes-128-gcm";
1291
- var PROVIDER_SECRET_HPKE_INFO = "byok-sdk.provider-secret.v1";
1292
- var PROVIDER_PROVISIONING_REQUEST_VERSION = 1;
1293
- var PROVIDER_PROVISIONING_OPERATIONS = ["configure", "update_model", "replace_secret", "delete"];
1294
- var PROVIDER_PROVISIONING_SECRET_OPERATIONS = ["configure", "replace_secret"];
1295
- var PROVIDER_PROVISIONING_MAX_TTL_MS = 15 * 60 * 1e3;
1296
- var PROVIDER_SECRET_PAD_BLOCK_BYTES = 256;
1297
- var PROVIDER_SECRET_MAX_PADDED_BYTES = 4096;
1298
- var PROVIDER_SECRET_MAX_BYTES = PROVIDER_SECRET_MAX_PADDED_BYTES - 2;
1299
- var SEALING_KEY_ID_LENGTH = 22;
1300
- var B64URL_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
1301
- var B64URL_PATTERN = /^[A-Za-z0-9_-]*$/;
1302
- function encodeBase64Url(bytes) {
1303
- let out = "";
1304
- let index = 0;
1305
- for (; index + 3 <= bytes.length; index += 3) {
1306
- const value = bytes[index] << 16 | bytes[index + 1] << 8 | bytes[index + 2];
1307
- out += B64URL_ALPHABET[value >> 18 & 63] + B64URL_ALPHABET[value >> 12 & 63] + B64URL_ALPHABET[value >> 6 & 63] + B64URL_ALPHABET[value & 63];
1308
- }
1309
- const rest = bytes.length - index;
1310
- if (rest === 1) {
1311
- const value = bytes[index] << 16;
1312
- out += B64URL_ALPHABET[value >> 18 & 63] + B64URL_ALPHABET[value >> 12 & 63];
1313
- } else if (rest === 2) {
1314
- const value = bytes[index] << 16 | bytes[index + 1] << 8;
1315
- out += B64URL_ALPHABET[value >> 18 & 63] + B64URL_ALPHABET[value >> 12 & 63] + B64URL_ALPHABET[value >> 6 & 63];
1316
- }
1317
- return out;
1318
- }
1319
- function decodeBase64Url(value, field) {
1320
- if (!B64URL_PATTERN.test(value) || value.length % 4 === 1) {
1321
- throw new ByokCoreError("sealed_secret_invalid", `${field} is not canonical base64url`);
1322
- }
1323
- const out = new Uint8Array(Math.floor(value.length * 3 / 4));
1324
- let buffer = 0;
1325
- let bits = 0;
1326
- let offset = 0;
1327
- for (const char of value) {
1328
- buffer = buffer << 6 | B64URL_ALPHABET.indexOf(char);
1329
- bits += 6;
1330
- if (bits >= 8) {
1331
- bits -= 8;
1332
- out[offset] = buffer >> bits & 255;
1333
- offset += 1;
1334
- }
1335
- }
1336
- if (encodeBase64Url(out) !== value) {
1337
- throw new ByokCoreError("sealed_secret_invalid", `${field} is not canonical base64url`);
1338
- }
1339
- return out;
1340
- }
1341
- function base64UrlField(bytes) {
1342
- return z.string().superRefine((value, ctx) => {
1343
- try {
1344
- const decoded = decodeBase64Url(value, "field");
1345
- if (bytes.exact !== void 0 && decoded.byteLength !== bytes.exact) {
1346
- ctx.addIssue({ code: "custom", message: `must decode to exactly ${bytes.exact} bytes` });
1347
- }
1348
- if (bytes.max !== void 0 && decoded.byteLength > bytes.max) {
1349
- ctx.addIssue({ code: "custom", message: `must decode to at most ${bytes.max} bytes` });
1350
- }
1351
- } catch {
1352
- ctx.addIssue({ code: "custom", message: "must be canonical unpadded base64url" });
1353
- }
1354
- });
1355
- }
1356
- async function sha256(bytes) {
1357
- return new Uint8Array(await webCryptoSubtle().digest("SHA-256", new Uint8Array(bytes)));
1358
- }
1359
- function hex(bytes) {
1360
- let out = "";
1361
- for (const byte of bytes) out += byte.toString(16).padStart(2, "0");
1362
- return out;
1363
- }
1364
- async function contentDigest(value) {
1365
- return `sha256:${hex(await sha256(canonicalizeJsonBytes(value)))}`;
1366
- }
1367
- var SealingPublicJwkSchema = z.strictObject({
1368
- kty: z.literal("EC"),
1369
- crv: z.literal("P-256"),
1370
- x: base64UrlField({ exact: 32 }),
1371
- y: base64UrlField({ exact: 32 })
1372
- });
1373
- var SealingKeyIdSchema = z.string().regex(new RegExp(`^[A-Za-z0-9_-]{${SEALING_KEY_ID_LENGTH}}$`));
1374
- function parseSealingPublicJwk(input) {
1375
- const result = safeParseTotal(SealingPublicJwkSchema, input);
1376
- if (!result.success) {
1377
- throw new ByokCoreError("sealed_secret_invalid", "Sealing public key must be a strict P-256 EC public JWK");
1378
- }
1379
- return result.data;
1380
- }
1381
- function sealingPublicKeyBytes(publicJwk) {
1382
- const jwk = parseSealingPublicJwk(publicJwk);
1383
- const raw = new Uint8Array(HPKE_P256_PUBLIC_KEY_BYTES);
1384
- raw[0] = 4;
1385
- raw.set(decodeBase64Url(jwk.x, "x"), 1);
1386
- raw.set(decodeBase64Url(jwk.y, "y"), 33);
1387
- return raw;
1388
- }
1389
- async function deriveSealingKeyId(publicJwk) {
1390
- const raw = sealingPublicKeyBytes(publicJwk);
1391
- await importP256PublicKey(raw);
1392
- return encodeBase64Url(await sha256(raw)).slice(0, SEALING_KEY_ID_LENGTH);
1393
- }
1394
- var SealingKeyClaimV1Schema = z.strictObject({
1395
- version: z.literal(1),
1396
- keyId: SealingKeyIdSchema,
1397
- epoch: z.number().int().positive().max(Number.MAX_SAFE_INTEGER),
1398
- publicJwk: SealingPublicJwkSchema
1399
- });
1400
- function sealingKeyClaimCanonicalBytes(claim) {
1401
- const result = safeParseTotal(SealingKeyClaimV1Schema, claim);
1402
- if (!result.success) {
1403
- throw new ByokCoreError("sealed_secret_invalid", "Sealing key claim is invalid");
1404
- }
1405
- const parsed = result.data;
1406
- return canonicalizeJsonBytes({
1407
- version: parsed.version,
1408
- keyId: parsed.keyId,
1409
- epoch: parsed.epoch,
1410
- publicJwk: { kty: parsed.publicJwk.kty, crv: parsed.publicJwk.crv, x: parsed.publicJwk.x, y: parsed.publicJwk.y }
1411
- });
1412
- }
1413
- var OpaqueIdSchema = z.string().min(1).max(200).superRefine((value, ctx) => {
1414
- if (value.trim() !== value || /[\u0000-\u001f\u007f]/u.test(value)) {
1415
- ctx.addIssue({ code: "custom", message: "must not carry surrounding whitespace or control characters" });
1416
- }
1417
- });
1418
- var CanonicalTimestampSchema = z.string().refine(isCanonicalTimestamp, "must be a canonical ISO-8601 UTC instant");
1419
- var ProfileRefSchema = z.string().min(1).max(64).regex(/^[a-z0-9]+(?:[-_][a-z0-9]+)*$/u);
1420
- var ProviderKindSchema = z.string().min(1).max(64).regex(/^[a-z0-9]+(?:[-_][a-z0-9]+)*$/u);
1421
- var ModelIdSchema = z.string().min(1).max(160).refine((value) => value.trim() === value && !/[\u0000-\u001f\u007f]/u.test(value), "model id is invalid");
1422
- var CapabilitySchema = z.string().min(1).max(64).regex(/^[a-z0-9]+(?:-[a-z0-9]+)*$/u);
1423
- var PiModelJsonSchema = z.custom((value) => {
1424
- if (value === null || typeof value !== "object" || Array.isArray(value)) return false;
1425
- try {
1426
- canonicalizeJson(value);
1427
- return true;
1428
- } catch {
1429
- return false;
1430
- }
1431
- }, "piModel must be a canonical JSON object");
1432
- var ModelConfigFields = {
1433
- agentId: OpaqueIdSchema,
1434
- providerKind: ProviderKindSchema,
1435
- modelId: ModelIdSchema,
1436
- piModel: PiModelJsonSchema,
1437
- capabilities: z.array(CapabilitySchema).max(8).refine((values) => new Set(values).size === values.length, "capabilities must be unique")
1438
- };
1439
- var ProviderProvisioningConfigV1Schema = z.discriminatedUnion("operation", [
1440
- z.strictObject({ operation: z.literal("configure"), ...ModelConfigFields }),
1441
- z.strictObject({ operation: z.literal("update_model"), ...ModelConfigFields }),
1442
- z.strictObject({ operation: z.literal("replace_secret"), agentId: OpaqueIdSchema, providerKind: ProviderKindSchema }),
1443
- z.strictObject({ operation: z.literal("delete"), agentId: OpaqueIdSchema })
1444
- ]);
1445
- var ProviderProvisioningExpectedProfileSchema = z.union([
1446
- z.null(),
1447
- z.strictObject({
1448
- profileRef: ProfileRefSchema,
1449
- profileRevision: z.string().regex(/^(?:0|[1-9][0-9]{0,19})$/u),
1450
- profileHash: z.string().regex(CONTENT_HASH_PATTERN)
1451
- })
1452
- ]);
1453
- var ProviderProvisioningIdentityRevisionSchema = z.string().min(1).max(128).regex(/^[A-Za-z0-9._:-]+$/u);
1454
- var ProviderProvisioningHeaderV1Schema = z.strictObject({
1455
- tenantId: OpaqueIdSchema,
1456
- deviceId: OpaqueIdSchema,
1457
- agentId: OpaqueIdSchema,
1458
- requestId: OpaqueIdSchema,
1459
- operation: z.enum(PROVIDER_PROVISIONING_OPERATIONS),
1460
- /** Non-secret, strictly increasing per bot (A3); a device rejects anything not above its high-watermark. */
1461
- operationGeneration: z.number().int().positive().max(Number.MAX_SAFE_INTEGER),
1462
- expectedProfile: ProviderProvisioningExpectedProfileSchema,
1463
- /** The device enrollment revision the Host authorized against (F2). */
1464
- expectedEnrollmentRevision: ProviderProvisioningIdentityRevisionSchema,
1465
- /** The agent placement revision the Host authorized against (F2). */
1466
- expectedPlacementRevision: ProviderProvisioningIdentityRevisionSchema,
1467
- configDigest: z.string().regex(CONTENT_HASH_PATTERN),
1468
- issuedAt: CanonicalTimestampSchema,
1469
- expiresAt: CanonicalTimestampSchema
1470
- });
1471
- var MAX_CIPHERTEXT_BYTES = PROVIDER_SECRET_MAX_PADDED_BYTES + HPKE_AEAD_TAG_BYTES;
1472
- var SealedProviderSecretV1Schema = z.strictObject({
1473
- suite: z.literal(PROVIDER_SECRET_HPKE_SUITE),
1474
- keyId: SealingKeyIdSchema,
1475
- enc: base64UrlField({ exact: HPKE_P256_PUBLIC_KEY_BYTES }),
1476
- ciphertext: base64UrlField({ max: MAX_CIPHERTEXT_BYTES })
1477
- });
1478
- function isSecretOperation(operation) {
1479
- return PROVIDER_PROVISIONING_SECRET_OPERATIONS.includes(operation);
1480
- }
1481
- var ProviderProvisioningRequestV1Schema = z.strictObject({
1482
- version: z.literal(PROVIDER_PROVISIONING_REQUEST_VERSION),
1483
- header: ProviderProvisioningHeaderV1Schema,
1484
- config: ProviderProvisioningConfigV1Schema,
1485
- sealed: SealedProviderSecretV1Schema.optional()
1486
- }).superRefine((request, ctx) => {
1487
- if (request.header.operation !== request.config.operation) {
1488
- ctx.addIssue({ code: "custom", path: ["config", "operation"], message: "config operation must equal header operation" });
1489
- }
1490
- if (request.header.agentId !== request.config.agentId) {
1491
- ctx.addIssue({ code: "custom", path: ["config", "agentId"], message: "config agentId must equal header agentId" });
1492
- }
1493
- if (isSecretOperation(request.header.operation) !== (request.sealed !== void 0)) {
1494
- ctx.addIssue({ code: "custom", path: ["sealed"], message: "a sealed secret is required exactly for configure and replace_secret" });
1495
- }
1496
- });
1497
- function parseProviderProvisioningRequest(input) {
1498
- const result = safeParseTotal(ProviderProvisioningRequestV1Schema, input);
1499
- if (!result.success) {
1500
- throw new ByokCoreError(
1501
- "provider_provisioning_request_invalid",
1502
- `Invalid provider provisioning request: ${(result.error?.issues ?? [{ path: [], message: "is not readable" }]).map((issue) => `${issue.path.join(".") || "<root>"}: ${issue.message}`).join("; ")}`
1503
- );
1504
- }
1505
- return result.data;
1506
- }
1507
- function configJson(config) {
1508
- return config;
1509
- }
1510
- async function providerProvisioningConfigDigest(config) {
1511
- const result = safeParseTotal(ProviderProvisioningConfigV1Schema, config);
1512
- if (!result.success) {
1513
- throw new ByokCoreError("provider_provisioning_request_invalid", "Provider provisioning config is invalid");
1514
- }
1515
- return contentDigest(configJson(result.data));
1516
- }
1517
- function providerSecretAadBytes(header, keyId) {
1518
- const parsed = safeParseTotal(ProviderProvisioningHeaderV1Schema, header);
1519
- if (!parsed.success || !safeParseTotal(SealingKeyIdSchema, keyId).success) {
1520
- throw new ByokCoreError("provider_provisioning_request_invalid", "Provider provisioning AAD input is invalid");
1521
- }
1522
- const value = parsed.data;
1523
- return canonicalizeJsonBytes({
1524
- v: PROVIDER_PROVISIONING_REQUEST_VERSION,
1525
- tenantId: value.tenantId,
1526
- deviceId: value.deviceId,
1527
- keyId,
1528
- requestId: value.requestId,
1529
- agentId: value.agentId,
1530
- operation: value.operation,
1531
- operationGeneration: value.operationGeneration,
1532
- expectedProfile: value.expectedProfile === null ? null : {
1533
- profileRef: value.expectedProfile.profileRef,
1534
- profileRevision: value.expectedProfile.profileRevision,
1535
- profileHash: value.expectedProfile.profileHash
1536
- },
1537
- expectedEnrollmentRevision: value.expectedEnrollmentRevision,
1538
- expectedPlacementRevision: value.expectedPlacementRevision,
1539
- configDigest: value.configDigest,
1540
- issuedAt: value.issuedAt,
1541
- expiresAt: value.expiresAt
1542
- });
1543
- }
1544
- async function providerProvisioningRequestDigest(request) {
1545
- const parsed = parseProviderProvisioningRequest(request);
1546
- return contentDigest(parsed);
1547
- }
1548
- async function assertProviderProvisioningConfigDigest(request) {
1549
- const parsed = parseProviderProvisioningRequest(request);
1550
- if (await providerProvisioningConfigDigest(parsed.config) !== parsed.header.configDigest) {
1551
- throw new ByokCoreError("provider_provisioning_request_invalid", "Provider provisioning config digest does not match its header");
1552
- }
1553
- }
1554
- function padProviderSecret(secret) {
1555
- const bytes = new TextEncoder().encode(secret);
1556
- if (bytes.byteLength === 0 || bytes.byteLength > PROVIDER_SECRET_MAX_BYTES) {
1557
- throw new ByokCoreError("sealed_secret_invalid", `Provider secret must be 1 to ${PROVIDER_SECRET_MAX_BYTES} UTF-8 bytes`);
1558
- }
1559
- const total = Math.ceil((bytes.byteLength + 2) / PROVIDER_SECRET_PAD_BLOCK_BYTES) * PROVIDER_SECRET_PAD_BLOCK_BYTES;
1560
- const out = new Uint8Array(total);
1561
- out[0] = bytes.byteLength >> 8;
1562
- out[1] = bytes.byteLength & 255;
1563
- out.set(bytes, 2);
1564
- return out;
1565
- }
1566
- function unpadProviderSecret(padded) {
1567
- if (padded.byteLength < PROVIDER_SECRET_PAD_BLOCK_BYTES || padded.byteLength > PROVIDER_SECRET_MAX_PADDED_BYTES || padded.byteLength % PROVIDER_SECRET_PAD_BLOCK_BYTES !== 0) {
1568
- throw new ByokCoreError("sealed_secret_invalid", "Padded provider secret has an invalid length");
1569
- }
1570
- const length = padded[0] << 8 | padded[1];
1571
- if (length === 0 || length + 2 > padded.byteLength || padded.byteLength - (length + 2) >= PROVIDER_SECRET_PAD_BLOCK_BYTES) {
1572
- throw new ByokCoreError("sealed_secret_invalid", "Padded provider secret has an invalid length prefix");
1573
- }
1574
- for (let index = length + 2; index < padded.byteLength; index += 1) {
1575
- if (padded[index] !== 0) {
1576
- throw new ByokCoreError("sealed_secret_invalid", "Padded provider secret has non-zero padding");
1577
- }
1578
- }
1579
- try {
1580
- return new TextDecoder("utf-8", { fatal: true, ignoreBOM: true }).decode(padded.subarray(2, length + 2));
1581
- } catch {
1582
- throw new ByokCoreError("sealed_secret_invalid", "Provider secret is not valid UTF-8");
1583
- }
1584
- }
1585
- var INFO_BYTES = () => new TextEncoder().encode(PROVIDER_SECRET_HPKE_INFO);
1586
- async function sealProviderProvisioningRequest(input) {
1587
- const configResult = safeParseTotal(ProviderProvisioningConfigV1Schema, input.config);
1588
- if (!configResult.success) {
1589
- throw new ByokCoreError("provider_provisioning_request_invalid", "Provider provisioning config is invalid");
1590
- }
1591
- const config = configResult.data;
1592
- const headerResult = safeParseTotal(ProviderProvisioningHeaderV1Schema, {
1593
- ...input.header,
1594
- configDigest: await providerProvisioningConfigDigest(config)
1595
- });
1596
- if (!headerResult.success) {
1597
- throw new ByokCoreError("provider_provisioning_request_invalid", "Provider provisioning header is invalid");
1598
- }
1599
- const header = headerResult.data;
1600
- const needsSecret = isSecretOperation(header.operation);
1601
- if (needsSecret !== (input.secret !== void 0) || needsSecret !== (input.recipient !== void 0)) {
1602
- throw new ByokCoreError(
1603
- "provider_provisioning_request_invalid",
1604
- "A secret and recipient are required exactly for configure and replace_secret"
1605
- );
1606
- }
1607
- let sealed;
1608
- if (needsSecret) {
1609
- const recipient = input.recipient;
1610
- const publicJwk = parseSealingPublicJwk(recipient.publicJwk);
1611
- if (await deriveSealingKeyId(publicJwk) !== recipient.keyId) {
1612
- throw new ByokCoreError("sealed_secret_invalid", "Sealing key id does not match its public key");
1613
- }
1614
- const { enc, ciphertext } = await hpkeSealBase({
1615
- recipientPublicKey: sealingPublicKeyBytes(publicJwk),
1616
- info: INFO_BYTES(),
1617
- aad: providerSecretAadBytes(header, recipient.keyId),
1618
- plaintext: padProviderSecret(input.secret)
1619
- });
1620
- sealed = {
1621
- suite: PROVIDER_SECRET_HPKE_SUITE,
1622
- keyId: recipient.keyId,
1623
- enc: encodeBase64Url(enc),
1624
- ciphertext: encodeBase64Url(ciphertext)
1625
- };
1626
- }
1627
- return parseProviderProvisioningRequest({
1628
- version: PROVIDER_PROVISIONING_REQUEST_VERSION,
1629
- header,
1630
- config,
1631
- ...sealed === void 0 ? {} : { sealed }
1632
- });
1633
- }
1634
- async function openProviderProvisioningSecret(input) {
1635
- const request = parseProviderProvisioningRequest(input.request);
1636
- const sealed = request.sealed;
1637
- if (sealed === void 0) {
1638
- throw new ByokCoreError("provider_provisioning_request_invalid", "Provider provisioning request carries no sealed secret");
1639
- }
1640
- const publicJwk = parseSealingPublicJwk(input.recipient.publicJwk);
1641
- if (sealed.keyId !== input.recipient.keyId || await deriveSealingKeyId(publicJwk) !== input.recipient.keyId) {
1642
- throw new ByokCoreError("sealed_secret_invalid", "Sealed secret is not addressed to this sealing key");
1643
- }
1644
- await assertProviderProvisioningConfigDigest(request);
1645
- const ciphertext = decodeBase64Url(sealed.ciphertext, "ciphertext");
1646
- const paddedLength = ciphertext.byteLength - HPKE_AEAD_TAG_BYTES;
1647
- if (paddedLength < PROVIDER_SECRET_PAD_BLOCK_BYTES || paddedLength > PROVIDER_SECRET_MAX_PADDED_BYTES || paddedLength % PROVIDER_SECRET_PAD_BLOCK_BYTES !== 0) {
1648
- throw new ByokCoreError("sealed_secret_invalid", "Sealed secret ciphertext has an invalid length");
1649
- }
1650
- const padded = await hpkeOpenBase({
1651
- recipientPrivateKey: input.recipient.privateKey,
1652
- recipientPublicKey: sealingPublicKeyBytes(publicJwk),
1653
- enc: decodeBase64Url(sealed.enc, "enc"),
1654
- info: INFO_BYTES(),
1655
- aad: providerSecretAadBytes(request.header, sealed.keyId),
1656
- ciphertext
1657
- });
1658
- try {
1659
- return unpadProviderSecret(padded);
1660
- } finally {
1661
- padded.fill(0);
1662
- }
1663
- }
1664
-
1665
- // src/agent-memory-intent.ts
1666
- var AGENT_MEMORY_INTENT_DIGEST_VERSION = "byok-agent-memory-intent-v1";
1667
- async function agentMemoryIntentOperationDigest(input) {
1668
- const canonical = {
1669
- v: AGENT_MEMORY_INTENT_DIGEST_VERSION,
1670
- tenantId: input.tenantId,
1671
- deviceId: input.deviceId,
1672
- intentId: input.intentId,
1673
- agentRef: { agentId: input.agentRef.agentId, profileRevision: input.agentRef.profileRevision },
1674
- path: input.path,
1675
- operation: input.operation,
1676
- baseRevision: input.baseRevision,
1677
- targetRevision: input.targetRevision,
1678
- approvalRef: input.approvalRef
1679
- };
1680
- const digest = new Uint8Array(
1681
- await webCryptoSubtle().digest("SHA-256", new Uint8Array(canonicalizeJsonBytes(canonical)))
1682
- );
1683
- let hex2 = "";
1684
- for (const byte of digest) hex2 += byte.toString(16).padStart(2, "0");
1685
- return `sha256:${hex2}`;
1686
- }
1687
-
1688
- // src/pairing.ts
1689
- var NONCE_SIGNING_DOMAIN = "byok-nonce-v1\n";
1690
- function nonceSigningBytes(nonce) {
1691
- return new TextEncoder().encode(NONCE_SIGNING_DOMAIN + nonce);
1692
- }
1693
-
1694
- // src/in-memory/storage-ledger.ts
1695
- var InMemoryStorageLedger = class {
1696
- constructor(clock) {
1697
- this.clock = clock;
1698
- }
1699
- clock;
1700
- #usage = /* @__PURE__ */ new Map();
1701
- #reservations = /* @__PURE__ */ new Map();
1702
- #committed = /* @__PURE__ */ new Map();
1703
- readReservation(key) {
1704
- return this.#reservations.get(key);
1705
- }
1706
- writeReservation(key, record) {
1707
- this.#reservations.set(key, record);
1708
- }
1709
- deleteReservation(key) {
1710
- this.#reservations.delete(key);
1711
- }
1712
- reservations() {
1713
- return this.#reservations.entries();
1714
- }
1715
- readUsage(tenant) {
1716
- let usage = this.#usage.get(tenant);
1717
- if (usage === void 0) {
1718
- usage = { committedObjectBytes: 0n, committedInlineBytes: 0n, reservedBytes: 0n, objectCount: 0n, updatedAt: this.clock.now().toISOString() };
1719
- this.#usage.set(tenant, usage);
1720
- }
1721
- return usage;
1722
- }
1723
- writeUsage(tenant, usage) {
1724
- this.#usage.set(tenant, usage);
1725
- }
1726
- isCounted(tenant, hash) {
1727
- return this.#committed.get(tenant)?.has(hash) === true;
1728
- }
1729
- recordCommitted(tenant, reservation) {
1730
- let hashes = this.#committed.get(tenant);
1731
- if (hashes === void 0) {
1732
- hashes = /* @__PURE__ */ new Map();
1733
- this.#committed.set(tenant, hashes);
1734
- }
1735
- hashes.set(reservation.contentHash, { kind: reservation.kind, bytes: reservation.expectedBytes });
1736
- }
1737
- hasReservedObject(tenant, hash) {
1738
- for (const { reservation } of this.#reservations.values()) {
1739
- if (reservation.tenantId === tenant && reservation.kind === "object" && reservation.contentHash === hash && reservation.state === "reserved") return true;
1740
- }
1741
- return false;
1742
- }
1743
- reservedObjectHashes(tenant) {
1744
- return [...this.#reservations.values()].filter(({ reservation }) => reservation.tenantId === tenant && reservation.kind === "object" && reservation.state === "reserved").map(({ reservation }) => reservation.contentHash);
1745
- }
1746
- prepareCollectedObjectRelease(tenant, hash, byteSize, now) {
1747
- const hashes = this.#committed.get(tenant);
1748
- const accounted = hashes?.get(hash);
1749
- if (accounted === void 0 || accounted.kind === "inline") return { releasedBytes: 0n, commit: () => {
1750
- } };
1751
- const usage = this.#usage.get(tenant);
1752
- if (usage === void 0 || accounted.bytes !== byteSize || usage.committedObjectBytes < accounted.bytes || usage.objectCount < 1n) {
1753
- throw new ByokCoreError("storage_integrity_mismatch", "Collected object accounting does not match the storage ledger.");
1754
- }
1755
- return { releasedBytes: accounted.bytes, commit: () => {
1756
- hashes.delete(hash);
1757
- this.writeUsage(tenant, { ...usage, committedObjectBytes: usage.committedObjectBytes - accounted.bytes, objectCount: usage.objectCount - 1n, updatedAt: now });
1758
- } };
1759
- }
1760
- releaseCollectedObject(tenant, hash, byteSize, now) {
1761
- const release = this.prepareCollectedObjectRelease(tenant, hash, byteSize, now);
1762
- release.commit();
1763
- return release.releasedBytes;
1764
- }
1765
- };
1766
-
1767
- // src/retention.ts
1768
- function assertCollectExpiredInput(input) {
1769
- assertCanonicalTimestamp(input.now, "now");
1770
- if (!Number.isSafeInteger(input.limit) || input.limit <= 0) {
1771
- throw new ByokCoreError("retention_input_invalid", "collectExpired limit must be a positive safe integer.");
1772
- }
1773
- }
1774
- var CollectExpiredFailure = class extends ByokCoreError {
1775
- constructor(committed, failedRows, more, options) {
1776
- super("retention_collect_failed", "Retention collection did not complete.", options);
1777
- this.committed = committed;
1778
- this.failedRows = failedRows;
1779
- this.more = more;
1780
- }
1781
- committed;
1782
- failedRows;
1783
- more;
1784
- };
1785
-
1786
- // src/in-memory/presence.ts
1787
- function assertTtl(ttlMs) {
1788
- if (!Number.isFinite(ttlMs) || ttlMs <= 0) {
1789
- throw new ByokCoreError(
1790
- "hint_ttl_invalid",
1791
- `Hint ttl must be a positive number of milliseconds, received ${String(ttlMs)}.`
1792
- );
1793
- }
1794
- }
1795
- function assertMinimumInterval(minimumIntervalMs) {
1796
- if (!Number.isFinite(minimumIntervalMs) || minimumIntervalMs < 0) {
1797
- throw new ByokCoreError(
1798
- "hint_ttl_invalid",
1799
- `Hint minimum interval must be a non-negative number of milliseconds, received ${String(minimumIntervalMs)}.`
1800
- );
1801
- }
1802
- }
1803
- var InMemoryPresenceStore = class {
1804
- #hints = /* @__PURE__ */ new Map();
1805
- #clock;
1806
- constructor(clock) {
1807
- this.#clock = clock;
1808
- }
1809
- async publish(tenant, input) {
1810
- assertTtl(input.ttlMs);
1811
- assertMinimumInterval(input.minimumIntervalMs);
1812
- const now = this.#clock.now();
1813
- const key = tenantKey(tenant, input.deviceId);
1814
- const existing = this.#hints.get(key);
1815
- if (existing && now.toISOString() < existing.expiresAt && Math.max(0, Number(now) - Date.parse(existing.observedAt)) < input.minimumIntervalMs) {
1816
- throw new ByokCoreError(
1817
- "hint_rate_limited",
1818
- `Presence for ${input.deviceId} was published more recently than the configured minimum interval.`
1819
- );
1820
- }
1821
- const hint = {
1822
- tenantId: tenant,
1823
- deviceId: input.deviceId,
1824
- level: input.level,
1825
- ...input.detail === void 0 ? {} : { detail: input.detail },
1826
- ...input.configuredToolsets === void 0 ? {} : { configuredToolsets: Object.freeze([...input.configuredToolsets]) },
1827
- ...input.clientVersion === void 0 ? {} : { clientVersion: input.clientVersion },
1828
- ...input.protocolVersions === void 0 ? {} : { protocolVersions: Object.freeze([...input.protocolVersions]) },
1829
- ...input.runtimes === void 0 ? {} : {
1830
- runtimes: Object.freeze(
1831
- input.runtimes.map(
1832
- (runtime) => Object.freeze({
1833
- id: runtime.id,
1834
- ...runtime.version === void 0 ? {} : { version: runtime.version },
1835
- ...runtime.authPresent === void 0 ? {} : { authPresent: runtime.authPresent }
1836
- })
1837
- )
1838
- )
1839
- },
1840
- observedAt: now.toISOString(),
1841
- expiresAt: new Date(now.getTime() + input.ttlMs).toISOString()
1842
- };
1843
- this.#hints.set(key, hint);
1844
- return hint;
1845
- }
1846
- async read(tenant, deviceId) {
1847
- const key = tenantKey(tenant, deviceId);
1848
- const hint = this.#hints.get(key);
1849
- if (hint === void 0) return void 0;
1850
- if (this.#isExpired(hint.expiresAt)) {
1851
- this.#hints.delete(key);
1852
- return void 0;
1853
- }
1854
- return hint;
1855
- }
1856
- async list(tenant) {
1857
- const prefix = tenantKey(tenant, "");
1858
- const live = [];
1859
- for (const [key, hint] of [...this.#hints.entries()]) {
1860
- if (!key.startsWith(prefix)) continue;
1861
- if (this.#isExpired(hint.expiresAt)) {
1862
- this.#hints.delete(key);
1863
- continue;
1864
- }
1865
- live.push(hint);
1866
- }
1867
- live.sort((left, right) => left.deviceId.localeCompare(right.deviceId));
1868
- return live;
1869
- }
1870
- async collectExpired(tenant, input) {
1871
- assertCollectExpiredInput(input);
1872
- let removed = 0, more = false;
1873
- for (const [key, hint] of this.#hints) {
1874
- if (hint.tenantId !== tenant || hint.expiresAt > input.now) continue;
1875
- if (removed === input.limit) {
1876
- more = true;
1877
- break;
1878
- }
1879
- this.#hints.delete(key);
1880
- removed++;
1881
- }
1882
- return { removed, expired: 0, releasedBytes: 0n, more };
1883
- }
1884
- #isExpired(expiresAt) {
1885
- return this.#clock.now().toISOString() >= expiresAt;
1886
- }
1887
- };
1888
-
1889
- // src/in-memory/board.ts
1890
- var DEFAULT_LIST_LIMIT = 50;
1891
- var InMemoryBoardStore = class {
1892
- #items = /* @__PURE__ */ new Map();
1893
- #seqByTenant = /* @__PURE__ */ new Map();
1894
- #clock;
1895
- constructor(clock) {
1896
- this.#clock = clock;
1897
- }
1898
- async create(tenant, input) {
1899
- const key = tenantKey(tenant, input.itemId);
1900
- if (this.#items.has(key)) {
1901
- throw new ByokCoreError(
1902
- "board_item_exists",
1903
- `Board item ${input.itemId} already exists in this tenant.`
1904
- );
1905
- }
1906
- const now = this.#now();
1907
- const item = {
1908
- tenantId: tenant,
1909
- itemId: input.itemId,
1910
- channel: input.channel,
1911
- title: input.title,
1912
- status: input.status ?? "todo",
1913
- boardSeq: this.#nextSeq(tenant),
1914
- createdAt: now,
1915
- updatedAt: now
1916
- };
1917
- this.#items.set(key, item);
1918
- return item;
1919
- }
1920
- async get(tenant, itemId) {
1921
- return this.#items.get(tenantKey(tenant, itemId));
1922
- }
1923
- async list(tenant, query) {
1924
- const afterSeq = query.afterSeq ?? 0;
1925
- const limit = query.limit ?? DEFAULT_LIST_LIMIT;
1926
- const prefix = tenantKey(tenant, "");
1927
- const matches = [];
1928
- for (const [key, item] of this.#items.entries()) {
1929
- if (!key.startsWith(prefix)) continue;
1930
- if (item.boardSeq <= afterSeq) continue;
1931
- if (query.channel !== void 0 && item.channel !== query.channel) continue;
1932
- if (query.status !== void 0 && item.status !== query.status) continue;
1933
- matches.push(item);
1934
- }
1935
- matches.sort((left, right) => left.boardSeq - right.boardSeq);
1936
- const page = matches.slice(0, limit);
1937
- return {
1938
- items: page,
1939
- nextSeq: page.at(-1)?.boardSeq ?? afterSeq,
1940
- hasMore: matches.length > page.length
1941
- };
1942
- }
1943
- async claim(tenant, input) {
1944
- const item = this.#require(tenant, input.itemId);
1945
- const expectedStatus = input.expectedStatus ?? "todo";
1946
- if (item.assignee !== void 0) {
1947
- if (item.assignee.holderId === input.holderId) {
1948
- if (input.expectedStatus !== void 0 && item.status !== input.expectedStatus) {
1949
- throw new CoreConflictError(
1950
- "board_status_conflict",
1951
- `Board item ${input.itemId} is ${item.status}, not ${input.expectedStatus}.`,
1952
- item,
1953
- this.#now()
1954
- );
1955
- }
1956
- return item;
1957
- }
1958
- throw new CoreConflictError(
1959
- "board_claim_conflict",
1960
- `Board item ${input.itemId} is held by ${item.assignee.holderId}.`,
1961
- item,
1962
- this.#now()
1963
- );
1964
- }
1965
- if (item.status !== expectedStatus) {
1966
- throw new CoreConflictError(
1967
- "board_status_conflict",
1968
- `Board item ${input.itemId} is ${item.status}, not ${expectedStatus}.`,
1969
- item,
1970
- this.#now()
1971
- );
1972
- }
1973
- if (item.status !== "todo" && item.status !== "in_progress") {
1974
- throw new CoreConflictError(
1975
- "board_transition_invalid",
1976
- `Board item ${input.itemId} cannot be claimed from ${item.status}.`,
1977
- item,
1978
- this.#now()
1979
- );
1980
- }
1981
- const now = this.#now();
1982
- const claimed = {
1983
- ...item,
1984
- status: item.status === "todo" ? "in_progress" : item.status,
1985
- assignee: { holderId: input.holderId, heldSince: now },
1986
- boardSeq: this.#nextSeq(tenant),
1987
- updatedAt: now
1988
- };
1989
- this.#items.set(tenantKey(tenant, input.itemId), claimed);
1990
- return claimed;
1991
- }
1992
- async unclaim(tenant, input) {
1993
- const item = this.#require(tenant, input.itemId);
1994
- if (item.assignee === void 0) {
1995
- throw new ByokCoreError(
1996
- "board_not_held",
1997
- `Board item ${input.itemId} is not held by anyone.`
1998
- );
1999
- }
2000
- if (item.assignee.holderId !== input.holderId) {
2001
- throw new CoreConflictError(
2002
- "board_claim_conflict",
2003
- `Board item ${input.itemId} is held by ${item.assignee.holderId}, not ${input.holderId}.`,
2004
- item,
2005
- this.#now()
2006
- );
2007
- }
2008
- const now = this.#now();
2009
- const released = {
2010
- tenantId: item.tenantId,
2011
- itemId: item.itemId,
2012
- channel: item.channel,
2013
- title: item.title,
2014
- status: item.status === "in_progress" ? "todo" : item.status,
2015
- boardSeq: this.#nextSeq(tenant),
2016
- createdAt: item.createdAt,
2017
- updatedAt: now
2018
- };
2019
- this.#items.set(tenantKey(tenant, input.itemId), released);
2020
- return released;
2021
- }
2022
- async updateStatus(tenant, input) {
2023
- const item = this.#require(tenant, input.itemId);
2024
- if (item.status !== input.expectedStatus) {
2025
- throw new CoreConflictError(
2026
- "board_status_conflict",
2027
- `Board item ${input.itemId} is ${item.status}, not ${input.expectedStatus}.`,
2028
- item,
2029
- this.#now()
2030
- );
2031
- }
2032
- if (input.holderId !== void 0 && item.assignee?.holderId !== input.holderId) {
2033
- throw new CoreConflictError(
2034
- "board_claim_conflict",
2035
- `Board item ${input.itemId} is not held by ${input.holderId}.`,
2036
- item,
2037
- this.#now()
2038
- );
2039
- }
2040
- if (!isLegalBoardTransition(input.expectedStatus, input.status)) {
2041
- throw new CoreConflictError(
2042
- "board_transition_invalid",
2043
- `${input.expectedStatus} to ${input.status} is not a legal board transition.`,
2044
- item,
2045
- this.#now()
2046
- );
2047
- }
2048
- const now = this.#now();
2049
- const updated = {
2050
- ...item,
2051
- status: input.status,
2052
- boardSeq: this.#nextSeq(tenant),
2053
- updatedAt: now
2054
- };
2055
- this.#items.set(tenantKey(tenant, input.itemId), updated);
2056
- return updated;
2057
- }
2058
- #require(tenant, itemId) {
2059
- const item = this.#items.get(tenantKey(tenant, itemId));
2060
- if (item === void 0) {
2061
- throw new ByokCoreError(
2062
- "board_item_not_found",
2063
- `Board item ${itemId} does not exist in this tenant.`
2064
- );
2065
- }
2066
- return item;
2067
- }
2068
- #nextSeq(tenant) {
2069
- const next = (this.#seqByTenant.get(tenant) ?? 0) + 1;
2070
- this.#seqByTenant.set(tenant, next);
2071
- return next;
2072
- }
2073
- #now() {
2074
- return this.#clock.now().toISOString();
2075
- }
2076
- };
2077
-
2078
- // src/in-memory/mailbox.ts
2079
- var DEFAULT_READ_LIMIT = 50;
2080
- var InMemoryMailboxStore = class {
2081
- #devices = /* @__PURE__ */ new Map();
2082
- #clock;
2083
- #quota;
2084
- /** The mailbox reads its limit from quota; quota never reads the mailbox. */
2085
- constructor(clock, quota) {
2086
- this.#clock = clock;
2087
- this.#quota = quota;
2088
- }
2089
- async append(tenant, input) {
2090
- const device = this.#device(tenant, input.deviceId);
2091
- const appended = device.mutationTail.then(async () => {
2092
- const existing = device.byMessageId.get(input.messageId);
2093
- if (existing !== void 0) return existing;
2094
- const limit = input.admission === "cancellation" ? void 0 : await this.#mailboxLimit(tenant);
2095
- const seq = device.nextSeq;
2096
- const materialized = await input.materialize(seq);
2097
- if (limit !== void 0) {
2098
- const used = this.#usedBytes(tenant);
2099
- if (used + materialized.byteSize > limit) {
2100
- throw new ByokCoreError(
2101
- "storage_quota_exceeded",
2102
- `Mailbox would reach ${String(used + materialized.byteSize)} bytes, over the limit of ${String(limit)} bytes.`
2103
- );
2104
- }
2105
- }
2106
- const message = {
2107
- tenantId: tenant,
2108
- deviceId: input.deviceId,
2109
- seq,
2110
- messageId: input.messageId,
2111
- ...materialized,
2112
- state: "pending",
2113
- appendedAt: this.#now()
2114
- };
2115
- device.nextSeq += 1;
2116
- device.messages.push(message);
2117
- device.byMessageId.set(message.messageId, message);
2118
- return message;
2119
- });
2120
- device.mutationTail = appended.then(
2121
- () => void 0,
2122
- () => void 0
2123
- );
2124
- return appended;
2125
- }
2126
- async readUsage(tenant) {
2127
- return { bytes: this.#usedBytes(tenant) };
2128
- }
2129
- async readAfter(tenant, query) {
2130
- const device = this.#devices.get(tenantKey(tenant, query.deviceId));
2131
- const limit = query.limit ?? DEFAULT_READ_LIMIT;
2132
- if (device === void 0) {
2133
- return { messages: [], nextSeq: query.afterSeq, hasMore: false, recoverableFrom: 1 };
2134
- }
2135
- const pending = device.messages.filter((message) => message.state === "pending" && message.seq > query.afterSeq).sort((left, right) => left.seq - right.seq);
2136
- const page = pending.slice(0, limit);
2137
- const last = page.at(-1);
2138
- return {
2139
- messages: page,
2140
- // Reading is not acknowledging: the returned position is a *read* cursor.
2141
- // Nothing above was mutated, so an identical call replays the same page.
2142
- nextSeq: last?.seq ?? query.afterSeq,
2143
- hasMore: pending.length > page.length,
2144
- // Computed over the WHOLE mailbox, not the queried slice: the floor is a
2145
- // property of the device's history, and reporting it relative to
2146
- // `afterSeq` would make every cursor look recoverable from itself.
2147
- recoverableFrom: this.#recoverableFrom(device)
2148
- };
2149
- }
2150
- async advanceCursor(tenant, input) {
2151
- const device = this.#device(tenant, input.deviceId);
2152
- const advanced = device.mutationTail.then(() => {
2153
- if (input.ackedSeq < device.ackedSeq) {
2154
- throw new CoreConflictError(
2155
- "mailbox_cursor_regression",
2156
- `Cursor for device ${input.deviceId} is at ${device.ackedSeq}; refusing to move it back to ${input.ackedSeq}.`,
2157
- this.#cursorState(tenant, input.deviceId, device),
2158
- this.#now()
2159
- );
2160
- }
2161
- if (input.ackedSeq > device.deliveredSeq) {
2162
- throw new CoreConflictError(
2163
- "mailbox_cursor_ahead_of_delivery",
2164
- `Cursor for device ${input.deviceId} was delivered through ${device.deliveredSeq}; refusing to acknowledge future cursor ${input.ackedSeq}.`,
2165
- this.#cursorState(tenant, input.deviceId, device),
2166
- this.#now()
2167
- );
2168
- }
2169
- device.ackedSeq = input.ackedSeq;
2170
- device.cursorUpdatedAt = this.#now();
2171
- for (const [index, message] of device.messages.entries()) {
2172
- if (message.state === "pending" && message.seq <= input.ackedSeq) {
2173
- device.messages[index] = { ...message, state: "acked" };
2174
- device.byMessageId.set(message.messageId, device.messages[index]);
2175
- }
2176
- }
2177
- return this.#cursorState(tenant, input.deviceId, device);
2178
- });
2179
- device.mutationTail = advanced.then(
2180
- () => void 0,
2181
- () => void 0
2182
- );
2183
- return advanced;
2184
- }
2185
- async recordDelivery(tenant, input) {
2186
- const device = this.#device(tenant, input.deviceId);
2187
- const recorded = device.mutationTail.then(() => {
2188
- if (input.deliveredSeq > device.deliveredSeq) {
2189
- device.deliveredSeq = input.deliveredSeq;
2190
- }
2191
- return this.#cursorState(tenant, input.deviceId, device);
2192
- });
2193
- device.mutationTail = recorded.then(
2194
- () => void 0,
2195
- () => void 0
2196
- );
2197
- return recorded;
2198
- }
2199
- async readCursor(tenant, deviceId) {
2200
- const device = this.#devices.get(tenantKey(tenant, deviceId));
2201
- if (device === void 0) {
2202
- return { tenantId: tenant, deviceId, deliveredSeq: 0, ackedSeq: 0, updatedAt: this.#now() };
2203
- }
2204
- return this.#cursorState(tenant, deviceId, device);
2205
- }
2206
- async collectExpired(tenant, input) {
2207
- assertCollectExpiredInput(input);
2208
- assertCanonicalTimestamp(input.ackedBefore, "ackedBefore");
2209
- assertCanonicalTimestamp(input.expireUnackedBefore, "expireUnackedBefore");
2210
- let removed = 0, expired = 0, more = false;
2211
- try {
2212
- phases: for (const phase of ["acked", "pending"]) {
2213
- for (const [key, device] of this.#devices) {
2214
- if (!key.startsWith(tenantKey(tenant, ""))) continue;
2215
- const collected = device.mutationTail.then(() => {
2216
- for (let index = 0; index < device.messages.length; ) {
2217
- const message = device.messages[index];
2218
- const cutoff = phase === "acked" ? input.ackedBefore : input.expireUnackedBefore;
2219
- if (message.state !== phase || message.appendedAt >= cutoff) {
2220
- index++;
2221
- continue;
2222
- }
2223
- if (removed + expired === input.limit) {
2224
- more = true;
2225
- return;
2226
- }
2227
- if (phase === "acked") {
2228
- device.messages.splice(index, 1);
2229
- device.byMessageId.delete(message.messageId);
2230
- removed++;
2231
- } else {
2232
- const retired = { ...message, state: "expired" };
2233
- device.messages[index] = retired;
2234
- device.byMessageId.set(retired.messageId, retired);
2235
- expired++;
2236
- index++;
2237
- }
2238
- }
2239
- });
2240
- device.mutationTail = collected.then(() => void 0, () => void 0);
2241
- await collected;
2242
- if (more) break phases;
2243
- }
2244
- }
2245
- } catch (cause) {
2246
- throw new CollectExpiredFailure({ removed, expired, releasedBytes: 0n }, 0, void 0, { cause });
2247
- }
2248
- return { removed, expired, releasedBytes: 0n, more };
2249
- }
2250
- async #mailboxLimit(tenant) {
2251
- const entitlement = await this.#quota.readEntitlement(tenant);
2252
- if (entitlement === void 0) {
2253
- throw new ByokCoreError(
2254
- "storage_entitlement_missing",
2255
- "No storage entitlement has been issued for this tenant."
2256
- );
2257
- }
2258
- return entitlement.mailboxLimitBytes;
2259
- }
2260
- /** Every retained row of the tenant, in any state, on any device. */
2261
- #usedBytes(tenant) {
2262
- const prefix = tenantKey(tenant, "");
2263
- let bytes = 0n;
2264
- for (const [key, device] of this.#devices.entries()) {
2265
- if (!key.startsWith(prefix)) continue;
2266
- for (const message of device.messages) bytes += message.byteSize;
2267
- }
2268
- return bytes;
2269
- }
2270
- #device(tenant, deviceId) {
2271
- if (deviceId.length === 0) {
2272
- throw new ByokCoreError("mailbox_message_not_found", "Device id must not be empty.");
2273
- }
2274
- const key = tenantKey(tenant, deviceId);
2275
- const existing = this.#devices.get(key);
2276
- if (existing !== void 0) return existing;
2277
- const created = {
2278
- nextSeq: 1,
2279
- deliveredSeq: 0,
2280
- ackedSeq: 0,
2281
- cursorUpdatedAt: this.#now(),
2282
- mutationTail: Promise.resolve(),
2283
- messages: [],
2284
- byMessageId: /* @__PURE__ */ new Map()
2285
- };
2286
- this.#devices.set(key, created);
2287
- return created;
2288
- }
2289
- /**
2290
- * One past the highest row this device lost, which is exactly the highest
2291
- * `expired` seq: dead-lettered rows are kept rather than deleted (§12.7.5),
2292
- * so the loss stays observable and needs no column of its own to remember it.
2293
- *
2294
- * `acked` is deliberately not counted. A consumed row is not a lost one, and
2295
- * counting it would turn a daemon re-polling from an old cursor — which the
2296
- * at-least-once contract explicitly allows — into a hard failure.
2297
- */
2298
- #recoverableFrom(device) {
2299
- let lost = 0;
2300
- for (const message of device.messages) {
2301
- if (message.state === "expired" && message.seq > lost) lost = message.seq;
2302
- }
2303
- return lost + 1;
2304
- }
2305
- #cursorState(tenant, deviceId, device) {
2306
- return {
2307
- tenantId: tenant,
2308
- deviceId,
2309
- deliveredSeq: device.deliveredSeq,
2310
- ackedSeq: device.ackedSeq,
2311
- updatedAt: device.cursorUpdatedAt
2312
- };
2313
- }
2314
- #now() {
2315
- return this.#clock.now().toISOString();
2316
- }
2317
- };
2318
-
2319
- // src/in-memory/blob.ts
2320
- var DEFAULT_LIST_LIMIT2 = 100;
2321
- var InMemoryObjectStore = class {
2322
- constructor(clock, maintenance) {
2323
- this.maintenance = maintenance;
2324
- this.#clock = clock;
2325
- if (maintenance === void 0 || maintenance.ledger === void 0 || typeof maintenance.bytes?.deleteObject !== "function") {
2326
- throw new ByokCoreError("retention_input_invalid", "ObjectStore requires its shared ledger and an explicit byte authority.");
2327
- }
2328
- }
2329
- maintenance;
2330
- #manifest = /* @__PURE__ */ new Map();
2331
- #references = /* @__PURE__ */ new Map();
2332
- #clock;
2333
- #deleteCursor = /* @__PURE__ */ new Map();
2334
- async putManifest(tenant, input) {
2335
- const key = tenantKey(tenant, input.hash);
2336
- const existing = this.#manifest.get(key);
2337
- if (existing !== void 0 && existing.state !== "deleted") return existing;
2338
- const now = this.#now();
2339
- const entry = {
2340
- tenantId: tenant,
2341
- hash: input.hash,
2342
- byteSize: input.byteSize,
2343
- contentType: input.contentType,
2344
- state: "pending",
2345
- refCount: 0,
2346
- createdAt: now,
2347
- updatedAt: now
2348
- };
2349
- this.#manifest.set(key, entry);
2350
- return entry;
2351
- }
2352
- async commit(tenant, input) {
2353
- const entry = this.#require(tenant, input.hash);
2354
- if (entry.byteSize !== input.observedByteSize || entry.contentType !== input.observedContentType) {
2355
- throw new ByokCoreError(
2356
- "storage_integrity_mismatch",
2357
- `Observed object ${input.hash} (${String(input.observedByteSize)} bytes, ${input.observedContentType}) does not match the declared manifest.`
2358
- );
2359
- }
2360
- if (entry.state === "committed") return entry;
2361
- return this.#transition(tenant, entry, "committed");
2362
- }
2363
- async get(tenant, hash) {
2364
- return this.#manifest.get(tenantKey(tenant, hash));
2365
- }
2366
- async list(tenant, query) {
2367
- if (query.deletePendingBefore !== void 0) {
2368
- assertCanonicalTimestamp(query.deletePendingBefore, "deletePendingBefore");
2369
- }
2370
- const prefix = tenantKey(tenant, "");
2371
- const matches = [];
2372
- for (const [key, entry] of this.#manifest.entries()) {
2373
- if (!key.startsWith(prefix)) continue;
2374
- if (query.state !== void 0 && entry.state !== query.state) continue;
2375
- if (query.deletePendingBefore !== void 0) {
2376
- if (entry.deletePendingAt === void 0) continue;
2377
- if (entry.deletePendingAt >= query.deletePendingBefore) continue;
2378
- }
2379
- matches.push(entry);
2380
- }
2381
- matches.sort((left, right) => left.hash.localeCompare(right.hash));
2382
- return matches.slice(0, query.limit ?? DEFAULT_LIST_LIMIT2);
2383
- }
2384
- async addReference(tenant, input) {
2385
- const entry = this.#require(tenant, input.hash);
2386
- if (entry.state !== "committed") {
2387
- throw new ByokCoreError(
2388
- "object_state_invalid",
2389
- `Only committed objects can be referenced; ${input.hash} is ${entry.state}.`
2390
- );
2391
- }
2392
- const refKey = tenantKey(tenant, input.hash, input.refKind, input.refId);
2393
- if (!this.#references.has(refKey)) {
2394
- this.#references.set(refKey, {
2395
- tenantId: tenant,
2396
- hash: input.hash,
2397
- refKind: input.refKind,
2398
- refId: input.refId,
2399
- createdAt: this.#now()
2400
- });
2401
- }
2402
- return this.#recount(tenant, entry);
2403
- }
2404
- async removeReference(tenant, input) {
2405
- const entry = this.#require(tenant, input.hash);
2406
- this.#references.delete(tenantKey(tenant, input.hash, input.refKind, input.refId));
2407
- return this.#recount(tenant, entry);
2408
- }
2409
- async markDeletePending(tenant, hash) {
2410
- const entry = this.#require(tenant, hash);
2411
- if (entry.refCount !== 0) {
2412
- throw new ByokCoreError(
2413
- "object_state_invalid",
2414
- `Object ${hash} still has ${entry.refCount} reference(s).`
2415
- );
2416
- }
2417
- return this.#transition(tenant, entry, "delete_pending");
2418
- }
2419
- async markDeleted(tenant, hash) {
2420
- const entry = this.#require(tenant, hash);
2421
- return this.#transition(tenant, entry, "deleted");
2422
- }
2423
- async collectExpired(tenant, input) {
2424
- assertCollectExpiredInput(input);
2425
- assertCanonicalTimestamp(input.orphanBefore, "orphanBefore");
2426
- const eligible = (entry) => entry.tenantId === tenant && entry.refCount === 0 && !this.maintenance.ledger.hasReservedObject(tenant, entry.hash) && (entry.state === "delete_pending" || (entry.state === "pending" || entry.state === "committed") && entry.updatedAt < input.orphanBefore);
2427
- let removed = 0, releasedBytes = 0n, more = false, visited = 0, failures = 0;
2428
- let firstFailure;
2429
- const cursor = this.#deleteCursor.get(tenant) ?? "";
2430
- const entries = [...this.#manifest.values()].filter(eligible);
2431
- const pending = entries.filter((entry) => entry.state === "delete_pending").sort((a, b) => a.hash.localeCompare(b.hash));
2432
- const first = pending.filter((entry) => entry.hash > cursor).slice(0, input.limit);
2433
- const fresh = entries.filter((entry) => entry.state !== "delete_pending").sort((a, b) => a.updatedAt.localeCompare(b.updatedAt) || a.hash.localeCompare(b.hash)).slice(0, input.limit - first.length);
2434
- const wrapped = pending.filter((entry) => entry.hash <= cursor).slice(0, input.limit - first.length - fresh.length);
2435
- const candidates = [...first, ...fresh, ...wrapped];
2436
- more = entries.length > candidates.length;
2437
- for (const candidate of candidates) {
2438
- if (visited++ === input.limit) {
2439
- more = true;
2440
- break;
2441
- }
2442
- const entry = this.#manifest.get(tenantKey(tenant, candidate.hash));
2443
- if (!eligible(entry)) continue;
2444
- const pending2 = entry.state === "delete_pending" ? entry : { ...entry, state: "delete_pending", deletePendingAt: input.now, updatedAt: input.now };
2445
- this.#manifest.set(tenantKey(tenant, entry.hash), pending2);
2446
- try {
2447
- const release = this.maintenance.ledger.prepareCollectedObjectRelease(tenant, entry.hash, entry.byteSize, input.now);
2448
- this.maintenance.bytes.deleteObject(tenant, entry.hash);
2449
- release.commit();
2450
- const released = release.releasedBytes;
2451
- this.#manifest.set(tenantKey(tenant, entry.hash), { ...pending2, state: "deleted", updatedAt: input.now });
2452
- removed++;
2453
- releasedBytes += released;
2454
- } catch (cause) {
2455
- failures++;
2456
- firstFailure ??= cause;
2457
- }
2458
- }
2459
- this.#deleteCursor.set(tenant, wrapped.at(-1)?.hash ?? first.at(-1)?.hash ?? "");
2460
- if (failures > 0) throw new CollectExpiredFailure({ removed, expired: 0, releasedBytes }, failures, true, { cause: firstFailure });
2461
- return { removed, expired: 0, releasedBytes, more };
2462
- }
2463
- #transition(tenant, entry, next) {
2464
- if (!isLegalObjectTransition(entry.state, next)) {
2465
- throw new ByokCoreError(
2466
- "object_state_invalid",
2467
- `${entry.state} to ${next} is not a legal object manifest transition.`
2468
- );
2469
- }
2470
- const now = this.#now();
2471
- const updated = {
2472
- ...entry,
2473
- state: next,
2474
- updatedAt: now,
2475
- ...next === "delete_pending" ? { deletePendingAt: now } : {}
2476
- };
2477
- this.#manifest.set(tenantKey(tenant, entry.hash), updated);
2478
- return updated;
2479
- }
2480
- #recount(tenant, entry) {
2481
- const refPrefix = tenantKey(tenant, entry.hash, "");
2482
- let refCount = 0;
2483
- for (const key of this.#references.keys()) {
2484
- if (key.startsWith(refPrefix)) refCount += 1;
2485
- }
2486
- const updated = { ...entry, refCount, updatedAt: this.#now() };
2487
- this.#manifest.set(tenantKey(tenant, entry.hash), updated);
2488
- return updated;
2489
- }
2490
- #require(tenant, hash) {
2491
- const entry = this.#manifest.get(tenantKey(tenant, hash));
2492
- if (entry === void 0) {
2493
- throw new ByokCoreError(
2494
- "object_not_found",
2495
- `Object ${hash} has no manifest row in this tenant.`
2496
- );
2497
- }
2498
- return entry;
2499
- }
2500
- #now() {
2501
- return this.#clock.now().toISOString();
2502
- }
2503
- };
2504
-
2505
- // src/in-memory/quota.ts
2506
- var WARNING_NUMERATOR = 80n;
2507
- var WARNING_DENOMINATOR = 100n;
2508
- var InMemoryQuotaStore = class {
2509
- constructor(clock, objects, ledger) {
2510
- this.ledger = ledger;
2511
- this.#clock = clock;
2512
- this.#objects = objects;
2513
- }
2514
- ledger;
2515
- #entitlements = /* @__PURE__ */ new Map();
2516
- #clock;
2517
- #objects;
2518
- async readEntitlement(tenant) {
2519
- return this.#entitlements.get(tenant);
2520
- }
2521
- async writeEntitlement(tenant, input) {
2522
- if (input.downgradeGraceUntil !== void 0) {
2523
- assertCanonicalTimestamp(input.downgradeGraceUntil, "downgradeGraceUntil");
2524
- }
2525
- const existing = this.#entitlements.get(tenant);
2526
- if (existing !== void 0 && input.version <= existing.version) {
2527
- throw new CoreConflictError(
2528
- "storage_entitlement_version_conflict",
2529
- `Entitlement is at version ${String(existing.version)}; refusing to apply version ${String(input.version)}.`,
2530
- existing,
2531
- this.#now()
2532
- );
2533
- }
2534
- const entitlement = {
2535
- tenantId: tenant,
2536
- version: input.version,
2537
- hardLimitBytes: input.hardLimitBytes,
2538
- maxObjectBytes: input.maxObjectBytes,
2539
- maxInlineBytes: input.maxInlineBytes,
2540
- mailboxLimitBytes: input.mailboxLimitBytes,
2541
- retentionPolicyId: input.retentionPolicyId,
2542
- ...input.downgradeGraceUntil === void 0 ? {} : { downgradeGraceUntil: input.downgradeGraceUntil }
2543
- };
2544
- this.#entitlements.set(tenant, entitlement);
2545
- return entitlement;
2546
- }
2547
- async readUsage(tenant) {
2548
- return this.#usageOf(tenant);
2549
- }
2550
- async readStatus(tenant) {
2551
- const entitlement = this.#requireEntitlement(tenant);
2552
- const usage = this.#usageOf(tenant);
2553
- const used = this.#usedBytes(usage);
2554
- const graceActive = entitlement.downgradeGraceUntil !== void 0 && this.#now() < entitlement.downgradeGraceUntil;
2555
- return {
2556
- entitlement,
2557
- usage,
2558
- posture: this.#posture(entitlement, usage, graceActive),
2559
- availableBytes: used >= entitlement.hardLimitBytes ? 0n : entitlement.hardLimitBytes - used,
2560
- graceActive
2561
- };
2562
- }
2563
- async readReservation(tenant, reservationId) {
2564
- return this.ledger.readReservation(tenantKey(tenant, reservationId))?.reservation;
2565
- }
2566
- async reserve(tenant, input) {
2567
- const entitlement = this.#requireEntitlement(tenant);
2568
- await Array.from(this.#expireReservations(tenant));
2569
- const key = tenantKey(tenant, input.reservationId);
2570
- const existing = this.ledger.readReservation(key);
2571
- if (existing !== void 0) {
2572
- if (existing.reservation.state === "reserved") {
2573
- if (existing.reservation.kind !== input.kind || existing.reservation.expectedBytes !== input.expectedBytes || existing.reservation.contentHash !== input.contentHash || existing.reservation.contentType !== input.contentType) {
2574
- throw new ByokCoreError(
2575
- "storage_integrity_mismatch",
2576
- `Reservation ${input.reservationId} already binds a different storage declaration.`
2577
- );
2578
- }
2579
- return existing.reservation;
2580
- }
2581
- throw new ByokCoreError(
2582
- "storage_reservation_expired",
2583
- `Reservation ${input.reservationId} is already ${existing.reservation.state}.`
2584
- );
2585
- }
2586
- const usage = this.#usageOf(tenant);
2587
- const graceActive = entitlement.downgradeGraceUntil !== void 0 && this.#now() < entitlement.downgradeGraceUntil;
2588
- if (this.#posture(entitlement, usage, graceActive) === "suspended") {
2589
- throw new ByokCoreError(
2590
- "storage_write_suspended",
2591
- "Tenant is over its hard limit and its downgrade grace has ended; durable writes are suspended."
2592
- );
2593
- }
2594
- const perObjectLimit = input.kind === "object" ? entitlement.maxObjectBytes : entitlement.maxInlineBytes;
2595
- if (input.expectedBytes > perObjectLimit) {
2596
- throw new ByokCoreError(
2597
- "storage_object_too_large",
2598
- `${String(input.expectedBytes)} bytes exceeds the ${input.kind} limit of ${String(perObjectLimit)} bytes.`
2599
- );
2600
- }
2601
- if (this.#usedBytes(usage) + input.expectedBytes > entitlement.hardLimitBytes) {
2602
- throw new ByokCoreError(
2603
- "storage_quota_exceeded",
2604
- `Reserving ${String(input.expectedBytes)} bytes would exceed the hard limit of ${String(entitlement.hardLimitBytes)} bytes.`
2605
- );
2606
- }
2607
- const now = this.#now();
2608
- const reservation = {
2609
- tenantId: tenant,
2610
- reservationId: input.reservationId,
2611
- state: "reserved",
2612
- kind: input.kind,
2613
- expectedBytes: input.expectedBytes,
2614
- contentHash: input.contentHash,
2615
- contentType: input.contentType,
2616
- createdAt: now,
2617
- expiresAt: new Date(this.#clock.now().getTime() + input.ttlMs).toISOString()
2618
- };
2619
- this.ledger.writeReservation(key, { reservation, deduplicated: false });
2620
- this.#setUsage(tenant, { ...usage, reservedBytes: usage.reservedBytes + input.expectedBytes });
2621
- return reservation;
2622
- }
2623
- async finalizeReservation(tenant, input) {
2624
- const key = tenantKey(tenant, input.reservationId);
2625
- const record = this.#requireReservation(tenant, input.reservationId);
2626
- const reservation = record.reservation;
2627
- if (reservation.state === "committed") {
2628
- return {
2629
- reservation,
2630
- usage: this.#usageOf(tenant),
2631
- deduplicated: record.deduplicated
2632
- };
2633
- }
2634
- if (reservation.state !== "reserved") {
2635
- throw new ByokCoreError(
2636
- "storage_reservation_expired",
2637
- `Reservation ${input.reservationId} is ${reservation.state}.`
2638
- );
2639
- }
2640
- if (this.#now() >= reservation.expiresAt) {
2641
- this.#settle(tenant, key, record, "expired");
2642
- throw new ByokCoreError(
2643
- "storage_reservation_expired",
2644
- `Reservation ${input.reservationId} expired at ${reservation.expiresAt}.`
2645
- );
2646
- }
2647
- if (input.observedByteSize !== reservation.expectedBytes || input.observedContentType !== reservation.contentType) {
2648
- this.#settle(tenant, key, record, "aborted");
2649
- throw new ByokCoreError(
2650
- "storage_integrity_mismatch",
2651
- `Observed object does not match reservation ${input.reservationId}.`
2652
- );
2653
- }
2654
- if (reservation.kind === "object") {
2655
- try {
2656
- await this.#objects.commit(tenant, {
2657
- hash: reservation.contentHash,
2658
- observedByteSize: input.observedByteSize,
2659
- observedContentType: input.observedContentType
2660
- });
2661
- } catch (error) {
2662
- this.#settle(tenant, key, record, "aborted");
2663
- if (error instanceof ByokCoreError && (error.code === "object_not_found" || error.code === "object_state_invalid")) {
2664
- throw new ByokCoreError(
2665
- "storage_integrity_mismatch",
2666
- `Reservation ${input.reservationId} has no committable object manifest.`,
2667
- { cause: error }
2668
- );
2669
- }
2670
- throw error;
2671
- }
2672
- }
2673
- const deduplicated = this.ledger.isCounted(tenant, reservation.contentHash);
2674
- const settled = this.#settle(tenant, key, record, "committed", deduplicated);
2675
- if (!deduplicated) {
2676
- this.ledger.recordCommitted(tenant, reservation);
2677
- const current = this.#usageOf(tenant);
2678
- this.#setUsage(tenant, {
2679
- ...current,
2680
- committedObjectBytes: reservation.kind === "object" ? current.committedObjectBytes + reservation.expectedBytes : current.committedObjectBytes,
2681
- committedInlineBytes: reservation.kind === "inline" ? current.committedInlineBytes + reservation.expectedBytes : current.committedInlineBytes,
2682
- objectCount: reservation.kind === "object" ? current.objectCount + 1n : current.objectCount
2683
- });
2684
- }
2685
- return { reservation: settled, usage: this.#usageOf(tenant), deduplicated };
2686
- }
2687
- async abortReservation(tenant, reservationId) {
2688
- const key = tenantKey(tenant, reservationId);
2689
- const record = this.#requireReservation(tenant, reservationId);
2690
- if (record.reservation.state !== "reserved") return record.reservation;
2691
- return this.#settle(tenant, key, record, "aborted");
2692
- }
2693
- *#expireReservations(tenant, now = this.#now(), limit = Number.MAX_SAFE_INTEGER) {
2694
- const prefix = tenantKey(tenant, "");
2695
- let expired = 0;
2696
- for (const [key, record] of this.ledger.reservations()) {
2697
- if (!key.startsWith(prefix) || record.reservation.state !== "reserved" || now < record.reservation.expiresAt) continue;
2698
- yield this.#settle(tenant, key, record, "expired", false, now);
2699
- if (++expired === limit) return;
2700
- }
2701
- }
2702
- async collectExpired(tenant, input) {
2703
- assertCollectExpiredInput(input);
2704
- assertCanonicalTimestamp(input.settledBefore, "settledBefore");
2705
- let expired = 0, releasedBytes = 0n, removed = 0, more = false;
2706
- try {
2707
- for (const reservation of this.#expireReservations(tenant, input.now, input.limit)) {
2708
- expired++;
2709
- releasedBytes += reservation.expectedBytes;
2710
- }
2711
- } catch (cause) {
2712
- throw new CollectExpiredFailure({ removed, expired, releasedBytes }, 1, true, { cause });
2713
- }
2714
- const prefix = tenantKey(tenant, "");
2715
- try {
2716
- for (const [key, record] of this.ledger.reservations()) {
2717
- if (!key.startsWith(prefix)) continue;
2718
- const row = record.reservation;
2719
- const shouldExpire = row.state === "reserved" && row.expiresAt <= input.now;
2720
- const shouldRemove = (row.state === "expired" || row.state === "aborted") && row.settledAt !== void 0 && row.settledAt < input.settledBefore;
2721
- if (!shouldExpire && !shouldRemove) continue;
2722
- if (shouldExpire || removed + expired === input.limit) {
2723
- more = true;
2724
- continue;
2725
- }
2726
- this.ledger.deleteReservation(key);
2727
- removed++;
2728
- }
2729
- } catch (cause) {
2730
- throw new CollectExpiredFailure({ removed, expired, releasedBytes }, 0, void 0, { cause });
2731
- }
2732
- return { removed, expired, releasedBytes, more };
2733
- }
2734
- #settle(tenant, key, record, state, deduplicated = false, settledAt = this.#now()) {
2735
- const settled = {
2736
- ...record.reservation,
2737
- state,
2738
- settledAt
2739
- };
2740
- const usage = this.#usageOf(tenant);
2741
- if (usage.reservedBytes < record.reservation.expectedBytes) {
2742
- throw new ByokCoreError("storage_integrity_mismatch", "Reservation settlement would underflow reserved usage.");
2743
- }
2744
- this.ledger.writeReservation(key, { reservation: settled, deduplicated });
2745
- this.#setUsage(tenant, { ...usage, reservedBytes: usage.reservedBytes - record.reservation.expectedBytes });
2746
- return settled;
2747
- }
2748
- #posture(entitlement, usage, graceActive) {
2749
- const used = this.#usedBytes(usage);
2750
- if (used >= entitlement.hardLimitBytes) {
2751
- const graceConfigured = entitlement.downgradeGraceUntil !== void 0;
2752
- return graceConfigured && !graceActive ? "suspended" : "blocked";
2753
- }
2754
- if (entitlement.hardLimitBytes > 0n && used * WARNING_DENOMINATOR >= entitlement.hardLimitBytes * WARNING_NUMERATOR) {
2755
- return "warning";
2756
- }
2757
- return "normal";
2758
- }
2759
- #usedBytes(usage) {
2760
- return usage.committedObjectBytes + usage.committedInlineBytes + usage.reservedBytes;
2761
- }
2762
- #usageOf(tenant) {
2763
- return this.ledger.readUsage(tenant);
2764
- }
2765
- #setUsage(tenant, usage) {
2766
- this.ledger.writeUsage(tenant, { ...usage, updatedAt: this.#now() });
2767
- }
2768
- #requireEntitlement(tenant) {
2769
- const entitlement = this.#entitlements.get(tenant);
2770
- if (entitlement === void 0) {
2771
- throw new ByokCoreError(
2772
- "storage_entitlement_missing",
2773
- "No storage entitlement has been issued for this tenant."
2774
- );
2775
- }
2776
- return entitlement;
2777
- }
2778
- #requireReservation(tenant, reservationId) {
2779
- const record = this.ledger.readReservation(tenantKey(tenant, reservationId));
2780
- if (record === void 0) {
2781
- throw new ByokCoreError(
2782
- "storage_reservation_not_found",
2783
- `Reservation ${reservationId} does not exist in this tenant.`
2784
- );
2785
- }
2786
- return record;
2787
- }
2788
- #now() {
2789
- return this.#clock.now().toISOString();
2790
- }
2791
- };
2792
-
2793
- // src/in-memory/skill-pack.ts
2794
- var DEFAULT_LIST_LIMIT3 = 50;
2795
- var InMemorySkillPackStore = class {
2796
- #packs = /* @__PURE__ */ new Map();
2797
- async publish(tenant, input) {
2798
- const { manifest } = input;
2799
- const structural = checkSkillPackManifest(manifest);
2800
- if (!structural.ok) {
2801
- throw new ByokCoreError(
2802
- "skill_pack_manifest_invalid",
2803
- `Refusing to publish ${JSON.stringify(manifest.name)}: ${structural.reason} \u2014 ${structural.detail}`
2804
- );
2805
- }
2806
- const contents = /* @__PURE__ */ new Map();
2807
- for (const file of input.files) {
2808
- if (contents.has(file.path)) {
2809
- throw new ByokCoreError(
2810
- "skill_pack_manifest_invalid",
2811
- `Refusing to publish ${JSON.stringify(manifest.name)}: ${JSON.stringify(file.path)} was supplied twice.`
2812
- );
2813
- }
2814
- contents.set(file.path, file.content);
2815
- }
2816
- for (const declared of manifest.files) {
2817
- const content = contents.get(declared.path);
2818
- if (content === void 0) {
2819
- throw new ByokCoreError(
2820
- "skill_pack_manifest_invalid",
2821
- `Refusing to publish ${JSON.stringify(manifest.name)}: ${JSON.stringify(declared.path)} is declared but was not supplied.`
2822
- );
2823
- }
2824
- const bytes = new TextEncoder().encode(content).length;
2825
- if (bytes !== declared.byteSize) {
2826
- throw new ByokCoreError(
2827
- "skill_pack_manifest_invalid",
2828
- `Refusing to publish ${JSON.stringify(manifest.name)}: ${JSON.stringify(declared.path)} declares ${declared.byteSize} bytes and supplies ${bytes}.`
2829
- );
2830
- }
2831
- }
2832
- if (contents.size !== manifest.files.length) {
2833
- throw new ByokCoreError(
2834
- "skill_pack_manifest_invalid",
2835
- `Refusing to publish ${JSON.stringify(manifest.name)}: ${contents.size} files were supplied for ${manifest.files.length} declared rows.`
2836
- );
2837
- }
2838
- const entry = checkSkillPackEntry(manifest, contents.get(SKILL_PACK_ENTRY_PATH));
2839
- if (!entry.ok) {
2840
- throw new ByokCoreError(
2841
- "skill_pack_manifest_invalid",
2842
- `Refusing to publish ${JSON.stringify(manifest.name)}: ${entry.reason} \u2014 ${entry.detail}`
2843
- );
2844
- }
2845
- this.#packs.set(tenantKey(tenant, manifest.name), { manifest, contents });
2846
- return manifest;
2847
- }
2848
- async get(tenant, name) {
2849
- return this.#packs.get(tenantKey(tenant, name))?.manifest;
2850
- }
2851
- async list(tenant, query) {
2852
- const prefix = tenantKey(tenant, "");
2853
- const manifests = [];
2854
- for (const [key, pack] of this.#packs.entries()) {
2855
- if (!key.startsWith(prefix)) continue;
2856
- manifests.push(pack.manifest);
2857
- }
2858
- manifests.sort((left, right) => left.name < right.name ? -1 : left.name > right.name ? 1 : 0);
2859
- return manifests.slice(0, query.limit ?? DEFAULT_LIST_LIMIT3);
2860
- }
2861
- async readFile(tenant, name, path) {
2862
- const pack = this.#packs.get(tenantKey(tenant, name));
2863
- if (pack === void 0) return void 0;
2864
- const declared = pack.manifest.files.find((file) => file.path === path);
2865
- const content = pack.contents.get(path);
2866
- if (declared === void 0 || content === void 0) return void 0;
2867
- return {
2868
- path: declared.path,
2869
- contentHash: declared.contentHash,
2870
- byteSize: declared.byteSize,
2871
- content
2872
- };
2873
- }
2874
- };
2875
-
2876
- // src/in-memory/truth.ts
2877
- var DEFAULT_MANIFEST_LIMIT = 100;
2878
- var InMemoryTruthStore = class {
2879
- #records = /* @__PURE__ */ new Map();
2880
- #clock;
2881
- constructor(clock) {
2882
- this.#clock = clock;
2883
- }
2884
- async writeTerminal(tenant, input) {
2885
- const key = tenantKey(tenant, "task.terminal", input.taskId);
2886
- const existing = this.#records.get(key);
2887
- if (existing !== void 0) {
2888
- if (existing.contentHash === input.contentHash) {
2889
- return existing;
2890
- }
2891
- throw new CoreConflictError(
2892
- "terminal_conflict",
2893
- `Task ${input.taskId} already has an immutable terminal record with a different hash.`,
2894
- existing,
2895
- this.#now()
2896
- );
2897
- }
2898
- const record = {
2899
- tenantId: tenant,
2900
- kind: "task.terminal",
2901
- recordKey: input.taskId,
2902
- rev: 1,
2903
- contentHash: input.contentHash,
2904
- byteSize: input.byteSize,
2905
- body: input.body,
2906
- ...input.label === void 0 ? {} : { label: input.label },
2907
- ...input.requestId === void 0 ? {} : { requestId: input.requestId },
2908
- writtenAt: this.#now()
2909
- };
2910
- this.#records.set(key, record);
2911
- return record;
2912
- }
2913
- async writeSnapshot(tenant, input) {
2914
- const key = tenantKey(tenant, input.kind, input.recordKey);
2915
- const existing = this.#records.get(key);
2916
- const currentRev = existing?.rev ?? 0;
2917
- if (input.expectedRev !== currentRev) {
2918
- throw new CoreConflictError(
2919
- "truth_revision_conflict",
2920
- `Record ${input.kind}/${input.recordKey} is at rev ${currentRev}, not ${input.expectedRev}.`,
2921
- existing,
2922
- this.#now()
2923
- );
2924
- }
2925
- const record = {
2926
- tenantId: tenant,
2927
- kind: input.kind,
2928
- recordKey: input.recordKey,
2929
- rev: currentRev + 1,
2930
- contentHash: input.contentHash,
2931
- byteSize: input.byteSize,
2932
- body: input.body,
2933
- ...input.label === void 0 ? {} : { label: input.label },
2934
- ...input.requestId === void 0 ? {} : { requestId: input.requestId },
2935
- writtenAt: this.#now()
2936
- };
2937
- this.#records.set(key, record);
2938
- return record;
2939
- }
2940
- async getRecord(tenant, selector) {
2941
- return this.#records.get(tenantKey(tenant, selector.kind, selector.recordKey));
2942
- }
2943
- async listManifest(tenant, query) {
2944
- const prefix = tenantKey(tenant, "");
2945
- const entries = [];
2946
- for (const [key, record] of this.#records.entries()) {
2947
- if (!key.startsWith(prefix)) continue;
2948
- if (query.kind !== void 0 && record.kind !== query.kind) continue;
2949
- if (query.keyPrefix !== void 0 && !record.recordKey.startsWith(query.keyPrefix)) {
2950
- continue;
2951
- }
2952
- entries.push({
2953
- kind: record.kind,
2954
- recordKey: record.recordKey,
2955
- rev: record.rev,
2956
- contentHash: record.contentHash,
2957
- byteSize: record.byteSize,
2958
- ...record.label === void 0 ? {} : { label: record.label },
2959
- updatedAt: record.writtenAt
2960
- });
2961
- }
2962
- entries.sort(
2963
- (left, right) => left.kind.localeCompare(right.kind) || left.recordKey.localeCompare(right.recordKey)
2964
- );
2965
- return entries.slice(0, query.limit ?? DEFAULT_MANIFEST_LIMIT);
2966
- }
2967
- #now() {
2968
- return this.#clock.now().toISOString();
2969
- }
2970
- };
2971
-
2972
- // src/in-memory/clock.ts
2973
- var IN_MEMORY_CLOCK_EPOCH = "2026-01-01T00:00:00.000Z";
2974
- function createMutableClock(start = new Date(IN_MEMORY_CLOCK_EPOCH)) {
2975
- let current = start.getTime();
2976
- return {
2977
- now() {
2978
- return new Date(current);
2979
- },
2980
- advance(ms) {
2981
- current += ms;
2982
- },
2983
- set(instant) {
2984
- current = instant.getTime();
2985
- }
2986
- };
2987
- }
2988
-
2989
- // src/in-memory/device-assertion-replay.ts
2990
- function replayKey(input) {
2991
- return JSON.stringify([
2992
- input.tenantId,
2993
- input.issuer,
2994
- input.productId,
2995
- input.deviceId,
2996
- input.audience,
2997
- input.schema,
2998
- input.jti
2999
- ]);
3000
- }
3001
- var InMemoryDeviceAssertionReplayAuthority = class {
3002
- #expiresAtByKey = /* @__PURE__ */ new Map();
3003
- async consume(input) {
3004
- const expiresAt = Date.parse(input.expiresAt);
3005
- if (!Number.isFinite(expiresAt)) throw new Error("device assertion replay expiry is invalid");
3006
- const key = replayKey(input);
3007
- if (this.#expiresAtByKey.has(key)) return false;
3008
- this.#expiresAtByKey.set(key, expiresAt);
3009
- return true;
3010
- }
3011
- async collectExpired(tenant, input) {
3012
- assertCollectExpiredInput(input);
3013
- const cutoff = Date.parse(input.now);
3014
- let removed = 0, more = false;
3015
- for (const [key, expiresAt] of this.#expiresAtByKey) {
3016
- if (JSON.parse(key)[0] !== tenant || expiresAt > cutoff) continue;
3017
- if (removed === input.limit) {
3018
- more = true;
3019
- break;
3020
- }
3021
- this.#expiresAtByKey.delete(key);
3022
- removed++;
3023
- }
3024
- return { removed, expired: 0, releasedBytes: 0n, more };
3025
- }
3026
- };
3027
-
3028
- // src/in-memory/index.ts
3029
- function createInMemoryCoreStores(options) {
3030
- const clock = options.clock ?? createMutableClock();
3031
- const ledger = new InMemoryStorageLedger(clock);
3032
- const objects = new InMemoryObjectStore(clock, { ledger, bytes: options.objectBytes });
3033
- const quota = new InMemoryQuotaStore(clock, objects, ledger);
3034
- const stores = {
3035
- mailbox: new InMemoryMailboxStore(clock, quota),
3036
- board: new InMemoryBoardStore(clock),
3037
- truth: new InMemoryTruthStore(clock),
3038
- presence: new InMemoryPresenceStore(clock),
3039
- objects,
3040
- quota,
3041
- skillPacks: new InMemorySkillPackStore()
3042
- };
3043
- return { stores, clock };
3044
- }
3045
- function createInMemoryCoreCompositionWithClock(objectBytes) {
3046
- const clock = createMutableClock();
3047
- return { stores: createInMemoryCoreStores({ clock, objectBytes }).stores, clock };
3048
- }
3049
-
3050
- // src/routine-schedule.ts
3051
- var ROUTINE_DAYS_OF_WEEK = Object.freeze(["mon", "tue", "wed", "thu", "fri", "sat", "sun"]);
3052
- var ROUTINE_HORIZON_MS = 368 * 864e5;
3053
- var ROUTINE_MIN_NOW = Date.UTC(2e3, 0, 1);
3054
- var ROUTINE_MAX_NOW = Date.UTC(2200, 0, 1);
3055
- var KEYS = /* @__PURE__ */ new Set(["minute", "hour", "daysOfWeek", "dayOfMonth", "month", "tz"]);
3056
- var COMMON_YEAR_DAYS = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
3057
- var TZ_NAME = /^[A-Za-z][A-Za-z0-9_+-]*(?:\/[A-Za-z0-9_+-]+)*$/;
3058
- var DAY_MS = 864e5;
3059
- var UTC_DAY_NAME = ["sun", "mon", "tue", "wed", "thu", "fri", "sat"];
3060
- var ScheduleError = class extends Error {
3061
- code;
3062
- constructor(code, message) {
3063
- super(message);
3064
- this.code = code;
3065
- }
3066
- };
3067
- var invalid = (message) => new ScheduleError("schedule_invalid", message);
3068
- function integer(value, name, min, max) {
3069
- if (typeof value !== "number" || !Number.isInteger(value) || value < min || value > max) throw invalid(`${name} must be an integer ${min}-${max}`);
3070
- return value;
3071
- }
3072
- var formatters = /* @__PURE__ */ new Map();
3073
- function formatter(tz) {
3074
- let f = formatters.get(tz);
3075
- if (!f) {
3076
- f = new Intl.DateTimeFormat("en-US", { timeZone: tz, hourCycle: "h23", year: "numeric", month: "numeric", day: "numeric", hour: "numeric", minute: "numeric", second: "numeric" });
3077
- if (formatters.size >= 64) formatters.delete(formatters.keys().next().value);
3078
- formatters.set(tz, f);
3079
- }
3080
- return f;
3081
- }
3082
- function checkTz(tz) {
3083
- if (typeof tz !== "string" || !TZ_NAME.test(tz)) throw new ScheduleError("tz_invalid", `${JSON.stringify(tz)} is not an IANA zone name`);
3084
- try {
3085
- formatter(tz);
3086
- } catch {
3087
- throw new ScheduleError("tz_invalid", `"${tz}" is not a known IANA zone`);
3088
- }
3089
- return tz;
3090
- }
3091
- function parse(schedule) {
3092
- if (schedule === null || typeof schedule !== "object" || Array.isArray(schedule)) throw invalid("schedule must be an object");
3093
- for (const key of Object.keys(schedule)) if (!KEYS.has(key)) throw invalid(`unknown field ${key}`);
3094
- const p = { minute: integer(schedule.minute, "minute", 0, 59), hour: integer(schedule.hour, "hour", 0, 23), tz: checkTz(schedule.tz) };
3095
- const s = schedule;
3096
- if (s.daysOfWeek !== void 0 && s.dayOfMonth !== void 0) throw invalid("daysOfWeek and dayOfMonth cannot both appear");
3097
- if (s.month !== void 0 && s.dayOfMonth === void 0) throw invalid("month needs dayOfMonth");
3098
- if (s.daysOfWeek !== void 0) {
3099
- if (!Array.isArray(s.daysOfWeek) || s.daysOfWeek.length < 1 || s.daysOfWeek.length > 7) throw invalid("daysOfWeek must list 1-7 days");
3100
- for (const d of s.daysOfWeek) if (!ROUTINE_DAYS_OF_WEEK.includes(d)) throw invalid(`unknown day ${JSON.stringify(d)}`);
3101
- p.days = new Set(s.daysOfWeek);
3102
- }
3103
- if (s.dayOfMonth !== void 0) {
3104
- p.dayOfMonth = integer(s.dayOfMonth, "dayOfMonth", 1, 31);
3105
- if (s.month !== void 0) {
3106
- p.month = integer(s.month, "month", 1, 12);
3107
- if (p.month === 2 && p.dayOfMonth === 29) {
3108
- throw new ScheduleError("schedule_beyond_horizon", "February 29 exists only in leap years; the next one can be up to 8 years away, beyond the 368-day horizon");
3109
- }
3110
- if (p.dayOfMonth > COMMON_YEAR_DAYS[p.month - 1]) {
3111
- throw new ScheduleError("schedule_unreachable", `month ${p.month} has no day ${p.dayOfMonth}`);
3112
- }
3113
- }
3114
- }
3115
- return p;
3116
- }
3117
- function checkRoutineSchedule(schedule) {
3118
- try {
3119
- parse(schedule);
3120
- return { ok: true };
3121
- } catch (e) {
3122
- if (e instanceof ScheduleError) return { ok: false, code: e.code, message: e.message };
3123
- throw e;
3124
- }
3125
- }
3126
- function localAsUtc(tz, instant) {
3127
- const p = {};
3128
- for (const part of formatter(tz).formatToParts(instant)) if (part.type !== "literal") p[part.type] = Number(part.value);
3129
- return Date.UTC(p.year, p.month - 1, p.day, p.hour, p.minute, p.second);
3130
- }
3131
- var offsetAt = (tz, instant) => localAsUtc(tz, instant) - Math.floor(instant / 1e3) * 1e3;
3132
- function resolve(tz, local) {
3133
- const before = offsetAt(tz, local - DAY_MS);
3134
- const offsets = /* @__PURE__ */ new Set([before, offsetAt(tz, local), offsetAt(tz, local + DAY_MS)]);
3135
- const valid = [...offsets].map((o) => local - o).filter((i) => offsetAt(tz, i) === local - i);
3136
- return valid.length ? { instant: Math.min(...valid), shifted: false } : { instant: local - before, shifted: true };
3137
- }
3138
- function matchesDate(p, y, m, d) {
3139
- if (p.days) return p.days.has(UTC_DAY_NAME[new Date(Date.UTC(y, m - 1, d)).getUTCDay()]);
3140
- if (p.dayOfMonth !== void 0) return d === p.dayOfMonth && (p.month === void 0 || m === p.month);
3141
- return true;
3142
- }
3143
- function nextRoutineOccurrence(schedule, now) {
3144
- if (!Number.isSafeInteger(now) || now < ROUTINE_MIN_NOW || now >= ROUTINE_MAX_NOW) {
3145
- throw new RangeError("now must be a safe integer epoch ms in [2000-01-01, 2200-01-01)");
3146
- }
3147
- let p;
3148
- try {
3149
- p = parse(schedule);
3150
- } catch (error) {
3151
- if (error instanceof ScheduleError) throw new ByokCoreError("routine_schedule_invalid", error.message);
3152
- throw error;
3153
- }
3154
- const start = localAsUtc(p.tz, now);
3155
- const day0 = Date.UTC(new Date(start).getUTCFullYear(), new Date(start).getUTCMonth(), new Date(start).getUTCDate());
3156
- let best = Infinity;
3157
- for (let k = -1; k <= 368; k += 1) {
3158
- const day = new Date(day0 + k * DAY_MS);
3159
- const y = day.getUTCFullYear(), m = day.getUTCMonth() + 1, d = day.getUTCDate();
3160
- if (!matchesDate(p, y, m, d)) continue;
3161
- const { instant, shifted } = resolve(p.tz, Date.UTC(y, m - 1, d, p.hour, p.minute));
3162
- if (instant <= now) continue;
3163
- best = Math.min(best, instant);
3164
- if (!shifted) break;
3165
- }
3166
- return best - now <= ROUTINE_HORIZON_MS ? best : null;
3167
- }
6
+ `);return [`${SKILL_PACK_MANIFEST_SCHEMA_ID}
7
+ `,`${manifest.name}
8
+ `,`${manifest.version}
9
+ `,`${manifest.description.length}
10
+ `,`${manifest.description}
11
+ `,`${manifest.files.length}
12
+ `,...rows].join("")}var SKILL_PACK_REJECTIONS=["path-unsafe","duplicate-path","entry-missing","file-count-over-cap","file-over-cap","pack-over-cap","size-mismatch","hash-mismatch","name-mismatch"];function isSkillPackPathSafe(path){return path.length>0&&path.length<=SKILL_PACK_FILE_PATH_MAX_LENGTH&&SKILL_PACK_FILE_PATH_PATTERN.test(path)}function checkSkillPackManifest(manifest){if(manifest.files.length>SKILL_PACK_MAX_FILES){return {ok:false,reason:"file-count-over-cap",detail:`${manifest.files.length} files exceeds the ${SKILL_PACK_MAX_FILES} file limit.`}}const seen=new Set;let bytes=0;for(const file of manifest.files){if(!isSkillPackPathSafe(file.path)){return {ok:false,reason:"path-unsafe",detail:`${JSON.stringify(file.path)} is not a safe relative path.`}}if(seen.has(file.path)){return {ok:false,reason:"duplicate-path",detail:`${JSON.stringify(file.path)} is declared twice.`}}seen.add(file.path);if(file.byteSize>SKILL_PACK_FILE_MAX_BYTES){return {ok:false,reason:"file-over-cap",detail:`${JSON.stringify(file.path)} declares ${file.byteSize} bytes, over the ${SKILL_PACK_FILE_MAX_BYTES} byte per-file limit.`}}bytes+=file.byteSize;}if(!seen.has(SKILL_PACK_ENTRY_PATH)){return {ok:false,reason:"entry-missing",detail:`A skill pack must carry ${SKILL_PACK_ENTRY_PATH}.`}}if(bytes>SKILL_PACK_MAX_BYTES){return {ok:false,reason:"pack-over-cap",detail:`${bytes} declared bytes exceeds the ${SKILL_PACK_MAX_BYTES} byte pack limit.`}}return {ok:true,bytes}}function checkSkillPackFileContent(declared,observed){if(!isSkillPackPathSafe(declared.path)){return {ok:false,reason:"path-unsafe",detail:`${JSON.stringify(declared.path)} is not a safe relative path.`}}if(observed.byteSize>SKILL_PACK_FILE_MAX_BYTES){return {ok:false,reason:"file-over-cap",detail:`${JSON.stringify(declared.path)} delivered ${observed.byteSize} bytes, over the ${SKILL_PACK_FILE_MAX_BYTES} byte per-file limit.`}}if(observed.byteSize!==declared.byteSize){return {ok:false,reason:"size-mismatch",detail:`${JSON.stringify(declared.path)} declared ${declared.byteSize} bytes and delivered ${observed.byteSize}.`}}if(observed.contentHash!==declared.contentHash){return {ok:false,reason:"hash-mismatch",detail:`${JSON.stringify(declared.path)} declared ${declared.contentHash} and delivered ${observed.contentHash}.`}}return {ok:true,bytes:observed.byteSize}}var SKILL_FRONTMATTER_FIELDS=["name","description"];var FRONTMATTER_DELIMITER="---";var FRONTMATTER_LINE_PATTERN=/^([A-Za-z][A-Za-z0-9_-]*):[ \t]*(.*)$/;function unquote(value){const trimmed=value.trim();if(trimmed.length>=2){const first=trimmed[0];const last=trimmed[trimmed.length-1];if(first==='"'&&last==='"'||first==="'"&&last==="'"){const inner=trimmed.slice(1,-1);return inner.includes(first)?void 0:inner}}return trimmed}function parseSkillFrontmatter(text){const reject=message=>{throw new ByokCoreError("skill_pack_frontmatter_invalid",message)};const lines=text.split("\n");if(lines[0]?.trimEnd()!==FRONTMATTER_DELIMITER){reject(`A skill file must open with a ${FRONTMATTER_DELIMITER} frontmatter delimiter.`);}const closing=lines.findIndex((line,index)=>index>0&&line.trimEnd()===FRONTMATTER_DELIMITER);if(closing<0)reject(`The frontmatter block is not closed by a ${FRONTMATTER_DELIMITER} line.`);const fields=new Map;for(const line of lines.slice(1,closing)){if(line.trim().length===0)continue;const match=FRONTMATTER_LINE_PATTERN.exec(line);if(match===null)reject(`Frontmatter line ${JSON.stringify(line)} is not a \`key: value\` pair.`);const key=match[1];if(!SKILL_FRONTMATTER_FIELDS.includes(key)){reject(`Frontmatter key ${JSON.stringify(key)} is not one of ${SKILL_FRONTMATTER_FIELDS.join(", ")}; a skill declares no tools, hooks, or environment.`);}if(fields.has(key))reject(`Frontmatter key ${JSON.stringify(key)} is declared twice.`);const value=unquote(match[2]);if(value===void 0||value.length===0){reject(`Frontmatter key ${JSON.stringify(key)} has no usable value.`);}fields.set(key,value);}const name=fields.get("name");const description=fields.get("description");if(name===void 0)reject("Frontmatter is missing `name`.");if(description===void 0)reject("Frontmatter is missing `description`.");if(name.length>SKILL_PACK_NAME_MAX_LENGTH||!SKILL_PACK_NAME_PATTERN.test(name)){reject(`Frontmatter \`name\` must match ${SKILL_PACK_NAME_PATTERN.source} and be at most ${SKILL_PACK_NAME_MAX_LENGTH} characters.`);}if(description.length>SKILL_PACK_DESCRIPTION_MAX_LENGTH){reject(`Frontmatter \`description\` exceeds ${SKILL_PACK_DESCRIPTION_MAX_LENGTH} characters.`);}return {name,description}}function checkSkillPackEntry(manifest,entryText){const frontmatter=parseSkillFrontmatter(entryText);if(frontmatter.name!==manifest.name){return {ok:false,reason:"name-mismatch",detail:`${SKILL_PACK_ENTRY_PATH} declares ${JSON.stringify(frontmatter.name)} but the manifest declares ${JSON.stringify(manifest.name)}.`}}return {ok:true,bytes:new TextEncoder().encode(entryText).length}}var CORE_STORE_NAMES=["mailbox","board","truth","presence","objects","quota","skillPacks"];var CORE_NON_COMPOSITION_PORT_NAMES=[];var CORE_PORT_METHODS={mailbox:["append","readUsage","readAfter","recordDelivery","advanceCursor","readCursor","collectExpired"],board:["create","get","list","claim","unclaim","updateStatus"],truth:["writeTerminal","writeSnapshot","getRecord","listManifest"],presence:["publish","read","list","collectExpired"],objects:["putManifest","commit","get","list","addReference","removeReference","markDeletePending","markDeleted","collectExpired"],quota:["readEntitlement","writeEntitlement","readUsage","readStatus","readReservation","reserve","finalizeReservation","abortReservation","collectExpired"],skillPacks:["publish","get","list","readFile"]};var CORE_PORT_INTERFACES={mailbox:"MailboxStore",board:"BoardStore",truth:"TruthStore",presence:"PresenceStore",objects:"ObjectStore",quota:"QuotaStore",skillPacks:"SkillPackStore"};var DEVICE_PROOF_SCHEMA_ID="byok-device-proof-v1";var DEVICE_PROOF_DOMAIN_PREFIX="byok-device-proof-v1\n";var DEVICE_PROOF_VERSION=1;var DEVICE_PROOF_ALGORITHMS=["ed25519"];var DEVICE_PROOF_HEADER="x-byok-device-proof";var BASE64URL_PATTERN=/^[A-Za-z0-9_-]+$/;var HTTP_METHOD_PATTERN=/^[A-Z]+$/;function canonicalizationFailure(path,reason){return new ByokCoreError("proof_canonicalization_failed",`Cannot canonicalize ${path}: ${reason}`)}function isPlainObject(value){const prototype=Object.getPrototypeOf(value);return prototype===Object.prototype||prototype===null}function canonicalizeValue(value,path,seen){if(value===null)return "null";switch(typeof value){case "boolean":return value?"true":"false";case "string":return JSON.stringify(value);case "number":{if(!Number.isSafeInteger(value)){throw canonicalizationFailure(path,`only safe integers are canonicalizable, received ${String(value)}`)}return String(value===0?0:value)}case "undefined":throw canonicalizationFailure(path,"undefined has no canonical form");case "bigint":throw canonicalizationFailure(path,"bigint is not a JSON type");case "function":case "symbol":throw canonicalizationFailure(path,`${typeof value} is not a JSON type`);case "object":break;default:throw canonicalizationFailure(path,`unsupported type ${typeof value}`)}const objectValue=value;if(seen.has(objectValue)){throw canonicalizationFailure(path,"circular reference")}seen.add(objectValue);try{if(Array.isArray(objectValue)){const parts2=objectValue.map((entry,index)=>canonicalizeValue(entry,`${path}[${index}]`,seen));return `[${parts2.join(",")}]`}if(!isPlainObject(objectValue)){throw canonicalizationFailure(path,"only plain objects and arrays are canonicalizable")}const keys=Object.keys(objectValue).sort();const record=objectValue;const parts=keys.map(key=>{const encodedKey=JSON.stringify(key);const encodedValue=canonicalizeValue(record[key],`${path}.${key}`,seen);return `${encodedKey}:${encodedValue}`});return `{${parts.join(",")}}`}finally{seen.delete(objectValue);}}function canonicalizeJson(value){return canonicalizeValue(value,"<root>",new Set)}function canonicalizeJsonBytes(value){return new TextEncoder().encode(canonicalizeJson(value))}var DeviceProofProtectedClaimsSchema=z.strictObject({version:z.literal(DEVICE_PROOF_VERSION),tenantId:z.string().min(1),productId:z.string().min(1),deviceId:z.string().min(1),keyId:z.string().min(1),keyEpoch:z.number().int().nonnegative(),requestId:z.string().min(1),operation:z.string().min(1),resource:z.string().min(1),method:z.string().regex(HTTP_METHOD_PATTERN).optional(),path:z.string().startsWith("/").optional(),operationId:z.string().min(1).optional(),bodySha256:z.string().regex(CONTENT_HASH_PATTERN),bodySize:z.number().int().nonnegative(),issuedAt:z.iso.datetime(),expiresAt:z.iso.datetime().optional(),nonce:z.string().min(1).optional()}).superRefine((claims,ctx)=>{const hasRequestLine=claims.method!==void 0&&claims.path!==void 0;const hasOperationId=claims.operationId!==void 0;if(hasRequestLine===hasOperationId){ctx.addIssue({code:"custom",message:"Claims must carry exactly one request binding: both `method` and `path`, or `operationId`."});}if(!hasRequestLine&&(claims.method!==void 0||claims.path!==void 0)){ctx.addIssue({code:"custom",message:"`method` and `path` must be supplied together."});}});var DeviceProofEnvelopeV1Schema=z.strictObject({schema:z.literal(DEVICE_PROOF_SCHEMA_ID),algorithm:z.enum(DEVICE_PROOF_ALGORITHMS),protected:DeviceProofProtectedClaimsSchema,signature:z.string().regex(BASE64URL_PATTERN)});function parseDeviceProofEnvelope(input){const result=DeviceProofEnvelopeV1Schema.safeParse(input);if(!result.success){throw new ByokCoreError("proof_envelope_invalid",`Invalid device proof envelope: ${result.error.issues.map(issue=>`${issue.path.join(".")||"<root>"}: ${issue.message}`).join("; ")}`,{cause:result.error})}return result.data}function deviceProofCanonicalClaims(claims){const canonical={version:claims.version,tenantId:claims.tenantId,productId:claims.productId,deviceId:claims.deviceId,keyId:claims.keyId,keyEpoch:claims.keyEpoch,requestId:claims.requestId,operation:claims.operation,resource:claims.resource,bodySha256:claims.bodySha256,bodySize:claims.bodySize,issuedAt:claims.issuedAt};if(claims.method!==void 0)canonical["method"]=claims.method;if(claims.path!==void 0)canonical["path"]=claims.path;if(claims.operationId!==void 0)canonical["operationId"]=claims.operationId;if(claims.expiresAt!==void 0)canonical["expiresAt"]=claims.expiresAt;if(claims.nonce!==void 0)canonical["nonce"]=claims.nonce;return canonical}function deviceProofCanonicalJson(claims){return canonicalizeJson(deviceProofCanonicalClaims(claims))}function deviceProofSigningInput(claims){return new TextEncoder().encode(DEVICE_PROOF_DOMAIN_PREFIX+deviceProofCanonicalJson(claims))}var DEVICE_ASSERTION_SCHEMA_ID="byok-device-assertion-v1";var DEVICE_ASSERTION_DOMAIN_PREFIX="byok-device-assertion-v1\n";var DEVICE_ASSERTION_VERSION=1;var TASK_ASSERTION_SCHEMA_ID="byok-task-assertion-v1";var TASK_ASSERTION_DOMAIN_PREFIX="byok-task-assertion-v1\n";var TASK_ASSERTION_VERSION=1;var DEVICE_ASSERTION_ALGORITHMS=["ed25519"];var DEVICE_ASSERTION_DEFAULT_TTL_MS=12e4;var DEVICE_ASSERTION_MAX_TTL_MS=3e5;var DEVICE_ASSERTION_AUDIENCE_MAX_BYTES=256;var JTI_PATTERN=/^[A-Za-z0-9_-]{22}$/;var SIGNATURE_PATTERN=/^[A-Za-z0-9_-]{86}$/;function utf8ByteLength(value){return new TextEncoder().encode(value).length}var ASSERTION_AUDIENCE=z.string().min(1).refine(value=>utf8ByteLength(value)<=DEVICE_ASSERTION_AUDIENCE_MAX_BYTES,{message:`audience must be at most ${DEVICE_ASSERTION_AUDIENCE_MAX_BYTES} UTF-8 bytes`});var DeviceAssertionClaimsSchema=z.strictObject({version:z.literal(DEVICE_ASSERTION_VERSION),issuer:z.string().min(1),productId:z.string().min(1),deviceId:z.string().min(1),audience:ASSERTION_AUDIENCE,jti:z.string().regex(JTI_PATTERN),issuedAt:z.iso.datetime(),expiresAt:z.iso.datetime()});var DeviceAssertionEnvelopeV1Schema=z.strictObject({schema:z.literal(DEVICE_ASSERTION_SCHEMA_ID),algorithm:z.enum(DEVICE_ASSERTION_ALGORITHMS),protected:DeviceAssertionClaimsSchema,signature:z.string().regex(SIGNATURE_PATTERN)});function parseDeviceAssertionEnvelope(input){const result=DeviceAssertionEnvelopeV1Schema.safeParse(input);if(!result.success){throw new ByokCoreError("assertion_envelope_invalid",`Invalid device assertion envelope: ${result.error.issues.map(issue=>`${issue.path.join(".")||"<root>"}: ${issue.message}`).join("; ")}`,{cause:result.error})}return result.data}function deviceAssertionCanonicalClaims(claims){const canonical={version:claims.version,issuer:claims.issuer,productId:claims.productId,deviceId:claims.deviceId,audience:claims.audience,jti:claims.jti,issuedAt:claims.issuedAt,expiresAt:claims.expiresAt};return canonical}function deviceAssertionCanonicalJson(claims){return canonicalizeJson(deviceAssertionCanonicalClaims(claims))}function deviceAssertionSigningInput(claims){return new TextEncoder().encode(DEVICE_ASSERTION_DOMAIN_PREFIX+deviceAssertionCanonicalJson(claims))}function parseInstant(value){const parsed=Date.parse(value);return Number.isFinite(parsed)?parsed:void 0}var EDDSA_JWK_X_PATTERN=/^[A-Za-z0-9_-]{43}$/;function isUsableDeviceRow(row){if(row===null||typeof row!=="object")return false;if(row.revoked!==false)return false;const key=row.publicKeyJwkX;return typeof key==="string"&&EDDSA_JWK_X_PATTERN.test(key)}function resolveMaxLifetimeMs(requested){const maxLifetimeMs=requested??DEVICE_ASSERTION_MAX_TTL_MS;if(!Number.isSafeInteger(maxLifetimeMs)||maxLifetimeMs<=0||maxLifetimeMs>DEVICE_ASSERTION_MAX_TTL_MS){return void 0}return maxLifetimeMs}function assertionWindowAdmits(claims,now,maxLifetimeMs){const issuedAt=parseInstant(claims.issuedAt);const expiresAt=parseInstant(claims.expiresAt);if(issuedAt===void 0||expiresAt===void 0)return false;if(expiresAt<=issuedAt)return false;if(expiresAt-issuedAt>maxLifetimeMs)return false;const instant=now.getTime();if(!Number.isFinite(instant))return false;return instant>=issuedAt&&instant<expiresAt}async function verifyDeviceAssertion(input,deps){const maxLifetimeMs=resolveMaxLifetimeMs(deps.maxLifetimeMs);if(maxLifetimeMs===void 0)return void 0;let envelope;try{envelope=parseDeviceAssertionEnvelope(input);}catch{return void 0}const claims=envelope.protected;const row=await deps.lookupDevice(claims.deviceId);if(!isUsableDeviceRow(row))return void 0;if(!assertionWindowAdmits(claims,deps.now,maxLifetimeMs))return void 0;const verified=await deps.verifier.verify({algorithm:envelope.algorithm,publicKey:row.publicKeyJwkX,signature:envelope.signature,signingInput:deviceAssertionSigningInput(claims)});if(!verified)return void 0;return claims}function isNonEmptyExactString(value){return typeof value==="string"&&value.length>0&&value.trim()===value}function isAuthorityRow(row,requestedDeviceId){if(row===null||typeof row!=="object")return false;const candidate=row;return isTenantId(candidate.tenantId)&&isNonEmptyExactString(candidate.productId)&&candidate.deviceId===requestedDeviceId}async function authenticateDeviceAssertion(input,deps){if(!isNonEmptyExactString(deps.expected.issuer)||!isNonEmptyExactString(deps.expected.productId)||!isNonEmptyExactString(deps.expected.audience)||utf8ByteLength(deps.expected.audience)>DEVICE_ASSERTION_AUDIENCE_MAX_BYTES){return void 0}let authorityRow;const claims=await verifyDeviceAssertion(input,{verifier:deps.verifier,lookupDevice:async deviceId=>{const row=await deps.lookupDevice(deviceId);if(!isAuthorityRow(row,deviceId))return void 0;authorityRow=row;return {publicKeyJwkX:row.publicKeyJwkX,revoked:row.revoked}},now:deps.now,...deps.maxLifetimeMs===void 0?{}:{maxLifetimeMs:deps.maxLifetimeMs}});if(claims===void 0||authorityRow===void 0)return void 0;if(claims.issuer!==deps.expected.issuer||claims.productId!==deps.expected.productId||claims.audience!==deps.expected.audience||authorityRow.productId!==deps.expected.productId||authorityRow.deviceId!==claims.deviceId){return void 0}const consumed=await deps.replay.consume({schema:DEVICE_ASSERTION_SCHEMA_ID,tenantId:authorityRow.tenantId,issuer:claims.issuer,productId:authorityRow.productId,deviceId:authorityRow.deviceId,audience:claims.audience,jti:claims.jti,expiresAt:claims.expiresAt});if(!consumed)return void 0;return {lane:"device",device:{kind:"device",tenantId:authorityRow.tenantId,productId:authorityRow.productId,deviceId:authorityRow.deviceId},issuer:claims.issuer,audience:claims.audience,jti:claims.jti,issuedAt:claims.issuedAt,expiresAt:claims.expiresAt}}var TASK_ASSERTION_AGENT_REF_MAX_BYTES=160;var TASK_ASSERTION_TOOLSET_ID_MAX_LENGTH=128;var AGENT_REF_VALUE=z.string().min(1).max(TASK_ASSERTION_AGENT_REF_MAX_BYTES).refine(value=>utf8ByteLength(value)<=TASK_ASSERTION_AGENT_REF_MAX_BYTES,{message:`AgentRef values must not exceed ${TASK_ASSERTION_AGENT_REF_MAX_BYTES} UTF-8 bytes`}).regex(/^[^\u0000-\u001f\u007f\r\n]+$/u,"AgentRef values must not contain control characters");var WINDOWS_RESERVED_AGENT_SEGMENT=/^(?:con|prn|aux|nul|com[1-9]|lpt[1-9])(?:\..*)?$/iu;var TaskAssertionAgentRefSchema=z.object({agentId:AGENT_REF_VALUE.regex(/^[^\\/:<>"|?*]+$/u,"agentId must be one portable pathname segment").refine(value=>value!=="."&&value!=="..","agentId must not be a dot segment").refine(value=>!/[. ]$/u.test(value)&&!WINDOWS_RESERVED_AGENT_SEGMENT.test(value),"agentId must be a portable Windows-safe pathname segment"),profileRevision:AGENT_REF_VALUE}).strict();var TOOLSET_ID_VALUE=z.string().min(1).max(TASK_ASSERTION_TOOLSET_ID_MAX_LENGTH).regex(/^[a-z0-9]+(?:[._-][a-z0-9]+)*$/u,"toolset ids must be lowercase logical identifiers");var TASK_ID_VALUE=z.string().min(1);var TaskAssertionClaimsSchema=z.strictObject({version:z.literal(TASK_ASSERTION_VERSION),issuer:z.string().min(1),productId:z.string().min(1),deviceId:z.string().min(1),audience:ASSERTION_AUDIENCE,jti:z.string().regex(JTI_PATTERN),issuedAt:z.iso.datetime(),expiresAt:z.iso.datetime(),taskId:TASK_ID_VALUE,agentRef:TaskAssertionAgentRefSchema,toolsetId:TOOLSET_ID_VALUE});var TaskAssertionEnvelopeV1Schema=z.strictObject({schema:z.literal(TASK_ASSERTION_SCHEMA_ID),algorithm:z.enum(DEVICE_ASSERTION_ALGORITHMS),protected:TaskAssertionClaimsSchema,signature:z.string().regex(SIGNATURE_PATTERN)});function parseTaskAssertionEnvelope(input){const result=TaskAssertionEnvelopeV1Schema.safeParse(input);if(!result.success){throw new ByokCoreError("assertion_envelope_invalid",`Invalid task assertion envelope: ${result.error.issues.map(issue=>`${issue.path.join(".")||"<root>"}: ${issue.message}`).join("; ")}`,{cause:result.error})}return result.data}function taskAssertionCanonicalClaims(claims){const canonical={version:claims.version,issuer:claims.issuer,productId:claims.productId,deviceId:claims.deviceId,audience:claims.audience,jti:claims.jti,issuedAt:claims.issuedAt,expiresAt:claims.expiresAt,taskId:claims.taskId,agentRef:{agentId:claims.agentRef.agentId,profileRevision:claims.agentRef.profileRevision},toolsetId:claims.toolsetId};return canonical}function taskAssertionCanonicalJson(claims){return canonicalizeJson(taskAssertionCanonicalClaims(claims))}function taskAssertionSigningInput(claims){return new TextEncoder().encode(TASK_ASSERTION_DOMAIN_PREFIX+taskAssertionCanonicalJson(claims))}async function verifyTaskAssertion(input,deps){const maxLifetimeMs=resolveMaxLifetimeMs(deps.maxLifetimeMs);if(maxLifetimeMs===void 0)return void 0;let envelope;try{envelope=parseTaskAssertionEnvelope(input);}catch{return void 0}const claims=envelope.protected;const row=await deps.lookupDevice(claims.deviceId);if(!isUsableDeviceRow(row))return void 0;if(!assertionWindowAdmits(claims,deps.now,maxLifetimeMs))return void 0;const verified=await deps.verifier.verify({algorithm:envelope.algorithm,publicKey:row.publicKeyJwkX,signature:envelope.signature,signingInput:taskAssertionSigningInput(claims)});if(!verified)return void 0;return claims}async function authenticateTaskAssertion(input,deps){if(!isNonEmptyExactString(deps.expected.issuer)||!isNonEmptyExactString(deps.expected.productId)||!isNonEmptyExactString(deps.expected.audience)||utf8ByteLength(deps.expected.audience)>DEVICE_ASSERTION_AUDIENCE_MAX_BYTES){return void 0}let authorityRow;const claims=await verifyTaskAssertion(input,{verifier:deps.verifier,lookupDevice:async deviceId=>{const row=await deps.lookupDevice(deviceId);if(!isAuthorityRow(row,deviceId))return void 0;authorityRow=row;return {publicKeyJwkX:row.publicKeyJwkX,revoked:row.revoked}},now:deps.now,...deps.maxLifetimeMs===void 0?{}:{maxLifetimeMs:deps.maxLifetimeMs}});if(claims===void 0||authorityRow===void 0)return void 0;if(claims.issuer!==deps.expected.issuer||claims.productId!==deps.expected.productId||claims.audience!==deps.expected.audience||authorityRow.productId!==deps.expected.productId||authorityRow.deviceId!==claims.deviceId){return void 0}const consumed=await deps.replay.consume({schema:TASK_ASSERTION_SCHEMA_ID,tenantId:authorityRow.tenantId,issuer:claims.issuer,productId:authorityRow.productId,deviceId:authorityRow.deviceId,audience:claims.audience,jti:claims.jti,expiresAt:claims.expiresAt});if(!consumed)return void 0;return {lane:"task",device:{kind:"device",tenantId:authorityRow.tenantId,productId:authorityRow.productId,deviceId:authorityRow.deviceId},issuer:claims.issuer,audience:claims.audience,jti:claims.jti,issuedAt:claims.issuedAt,expiresAt:claims.expiresAt,taskId:claims.taskId,agentRef:claims.agentRef,toolsetId:claims.toolsetId}}function webCryptoSubtle(){const subtle=globalThis.crypto?.subtle;if(subtle===void 0){throw new ByokCoreError("webcrypto_unavailable","WebCrypto subtle is unavailable in this runtime")}return subtle}var KEM_ID=16;var KDF_ID=1;var AEAD_ID=1;var HPKE_P256_PUBLIC_KEY_BYTES=65;var N_SECRET=32;var N_K=16;var N_N=12;var N_H=32;var HPKE_AEAD_TAG_BYTES=16;var MODE_BASE=0;var HPKE_V1=utf8("HPKE-v1");var KEM_SUITE_ID=concat(utf8("KEM"),i2osp(KEM_ID,2));var HPKE_SUITE_ID=concat(utf8("HPKE"),i2osp(KEM_ID,2),i2osp(KDF_ID,2),i2osp(AEAD_ID,2));var EMPTY=new Uint8Array(0);var ZERO_SALT=new Uint8Array(N_H);function utf8(value){return new TextEncoder().encode(value)}function concat(...parts){const length=parts.reduce((total,part)=>total+part.byteLength,0);const out=new Uint8Array(length);let offset=0;for(const part of parts){out.set(part,offset);offset+=part.byteLength;}return out}function i2osp(value,length){if(!Number.isSafeInteger(value)||value<0){throw new ByokCoreError("sealed_secret_invalid","HPKE integer encoding requires a non-negative safe integer")}const out=new Uint8Array(length);let remaining=value;for(let index=length-1;index>=0;index-=1){out[index]=remaining%256;remaining=Math.floor(remaining/256);}if(remaining!==0){throw new ByokCoreError("sealed_secret_invalid","HPKE integer does not fit the requested width")}return out}function copy(bytes){return new Uint8Array(bytes)}async function hmacSha256(key,data){const subtle=webCryptoSubtle();const hmacKey=await subtle.importKey("raw",copy(key),{name:"HMAC",hash:"SHA-256"},false,["sign"]);return new Uint8Array(await subtle.sign("HMAC",hmacKey,copy(data)))}async function hkdfExpand(prk,info,length){if(length>255*N_H){throw new ByokCoreError("sealed_secret_invalid","HKDF output length exceeds its limit")}const out=new Uint8Array(length);let previous=EMPTY;let offset=0;for(let counter=1;offset<length;counter+=1){previous=await hmacSha256(prk,concat(previous,info,Uint8Array.of(counter)));const take=Math.min(previous.byteLength,length-offset);out.set(previous.subarray(0,take),offset);offset+=take;}return out}async function labeledExtract(suiteId,salt,label,ikm){return hmacSha256(salt.byteLength===0?ZERO_SALT:salt,concat(HPKE_V1,suiteId,utf8(label),ikm))}async function labeledExpand(suiteId,prk,label,info,length){return hkdfExpand(prk,concat(i2osp(length,2),HPKE_V1,suiteId,utf8(label),info),length)}async function importP256PublicKey(raw){if(raw.byteLength!==HPKE_P256_PUBLIC_KEY_BYTES||raw[0]!==4){throw new ByokCoreError("sealed_secret_invalid","P-256 public key must be a 65-byte uncompressed point")}try{return await webCryptoSubtle().importKey("raw",copy(raw),{name:"ECDH",namedCurve:"P-256"},true,[])}catch(cause){throw new ByokCoreError("sealed_secret_invalid","P-256 public key is not a valid curve point",{cause})}}async function ecdhP256(privateKey,publicKey){const bits=await webCryptoSubtle().deriveBits({name:"ECDH",public:publicKey},privateKey,N_SECRET*8);return new Uint8Array(bits)}async function extractAndExpand(dh,kemContext){const eaePrk=await labeledExtract(KEM_SUITE_ID,EMPTY,"eae_prk",dh);return labeledExpand(KEM_SUITE_ID,eaePrk,"shared_secret",kemContext,N_SECRET)}async function keyScheduleBase(sharedSecret,info){const pskIdHash=await labeledExtract(HPKE_SUITE_ID,EMPTY,"psk_id_hash",EMPTY);const infoHash=await labeledExtract(HPKE_SUITE_ID,EMPTY,"info_hash",info);const context=concat(Uint8Array.of(MODE_BASE),pskIdHash,infoHash);const secret=await labeledExtract(HPKE_SUITE_ID,sharedSecret,"secret",EMPTY);return {sharedSecret,key:await labeledExpand(HPKE_SUITE_ID,secret,"key",context,N_K),baseNonce:await labeledExpand(HPKE_SUITE_ID,secret,"base_nonce",context,N_N),exporterSecret:await labeledExpand(HPKE_SUITE_ID,secret,"exp",context,N_H)}}async function setupBaseSender(recipientPublicKey,info,ephemeral){const pkR=await importP256PublicKey(recipientPublicKey);let pair=ephemeral;if(pair===void 0){const subtle=webCryptoSubtle();const generated=await subtle.generateKey({name:"ECDH",namedCurve:"P-256"},true,["deriveBits"]);pair={privateKey:generated.privateKey,publicKey:new Uint8Array(await subtle.exportKey("raw",generated.publicKey))};}const enc=copy(pair.publicKey);const dh=await ecdhP256(pair.privateKey,pkR);const sharedSecret=await extractAndExpand(dh,concat(enc,recipientPublicKey));return {enc,schedule:await keyScheduleBase(sharedSecret,info)}}async function setupBaseRecipient(enc,recipientPrivateKey,recipientPublicKey,info){if(recipientPublicKey.byteLength!==HPKE_P256_PUBLIC_KEY_BYTES||recipientPublicKey[0]!==4){throw new ByokCoreError("sealed_secret_invalid","P-256 recipient public key must be a 65-byte uncompressed point")}const pkE=await importP256PublicKey(enc);let dh;try{dh=await ecdhP256(recipientPrivateKey,pkE);}catch(cause){throw new ByokCoreError("sealed_secret_open_failed","HPKE decapsulation failed",{cause})}const sharedSecret=await extractAndExpand(dh,concat(enc,recipientPublicKey));return keyScheduleBase(sharedSecret,info)}function hpkeNonce(baseNonce,sequence){const seq=i2osp(sequence,N_N);const out=new Uint8Array(N_N);for(let index=0;index<N_N;index+=1)out[index]=baseNonce[index]^seq[index];return out}async function aesGcmKey(key,usage){return webCryptoSubtle().importKey("raw",copy(key),{name:"AES-GCM"},false,[usage])}async function hpkeAeadSeal(schedule,sequence,aad,plaintext){const ciphertext=await webCryptoSubtle().encrypt({name:"AES-GCM",iv:hpkeNonce(schedule.baseNonce,sequence),additionalData:copy(aad),tagLength:128},await aesGcmKey(schedule.key,"encrypt"),copy(plaintext));return new Uint8Array(ciphertext)}async function hpkeAeadOpen(schedule,sequence,aad,ciphertext){if(ciphertext.byteLength<HPKE_AEAD_TAG_BYTES){throw new ByokCoreError("sealed_secret_invalid","HPKE ciphertext is shorter than its authentication tag")}try{const plaintext=await webCryptoSubtle().decrypt({name:"AES-GCM",iv:hpkeNonce(schedule.baseNonce,sequence),additionalData:copy(aad),tagLength:128},await aesGcmKey(schedule.key,"decrypt"),copy(ciphertext));return new Uint8Array(plaintext)}catch(cause){throw new ByokCoreError("sealed_secret_open_failed","HPKE ciphertext failed authentication",{cause})}}async function hpkeSealBase(input){const{enc,schedule}=await setupBaseSender(input.recipientPublicKey,input.info);return {enc,ciphertext:await hpkeAeadSeal(schedule,0,input.aad,input.plaintext)}}async function hpkeOpenBase(input){const schedule=await setupBaseRecipient(input.enc,input.recipientPrivateKey,input.recipientPublicKey,input.info);return hpkeAeadOpen(schedule,0,input.aad,input.ciphertext)}function safeParseTotal(schema,value){try{return schema.safeParse(value)}catch{return {success:false}}}var PROVIDER_SECRET_HPKE_SUITE="hpke-base.dhkem-p256-hkdf-sha256.hkdf-sha256.aes-128-gcm";var PROVIDER_SECRET_HPKE_INFO="byok-sdk.provider-secret.v1";var PROVIDER_PROVISIONING_REQUEST_VERSION=1;var PROVIDER_PROVISIONING_OPERATIONS=["configure","update_model","replace_secret","delete"];var PROVIDER_PROVISIONING_SECRET_OPERATIONS=["configure","replace_secret"];var PROVIDER_PROVISIONING_MAX_TTL_MS=15*60*1e3;var PROVIDER_SECRET_PAD_BLOCK_BYTES=256;var PROVIDER_SECRET_MAX_PADDED_BYTES=4096;var PROVIDER_SECRET_MAX_BYTES=PROVIDER_SECRET_MAX_PADDED_BYTES-2;var SEALING_KEY_ID_LENGTH=22;var B64URL_ALPHABET="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";var B64URL_PATTERN=/^[A-Za-z0-9_-]*$/;function encodeBase64Url(bytes){let out="";let index=0;for(;index+3<=bytes.length;index+=3){const value=bytes[index]<<16|bytes[index+1]<<8|bytes[index+2];out+=B64URL_ALPHABET[value>>18&63]+B64URL_ALPHABET[value>>12&63]+B64URL_ALPHABET[value>>6&63]+B64URL_ALPHABET[value&63];}const rest=bytes.length-index;if(rest===1){const value=bytes[index]<<16;out+=B64URL_ALPHABET[value>>18&63]+B64URL_ALPHABET[value>>12&63];}else if(rest===2){const value=bytes[index]<<16|bytes[index+1]<<8;out+=B64URL_ALPHABET[value>>18&63]+B64URL_ALPHABET[value>>12&63]+B64URL_ALPHABET[value>>6&63];}return out}function decodeBase64Url(value,field){if(!B64URL_PATTERN.test(value)||value.length%4===1){throw new ByokCoreError("sealed_secret_invalid",`${field} is not canonical base64url`)}const out=new Uint8Array(Math.floor(value.length*3/4));let buffer=0;let bits=0;let offset=0;for(const char of value){buffer=buffer<<6|B64URL_ALPHABET.indexOf(char);bits+=6;if(bits>=8){bits-=8;out[offset]=buffer>>bits&255;offset+=1;}}if(encodeBase64Url(out)!==value){throw new ByokCoreError("sealed_secret_invalid",`${field} is not canonical base64url`)}return out}function base64UrlField(bytes){return z.string().superRefine((value,ctx)=>{try{const decoded=decodeBase64Url(value,"field");if(bytes.exact!==void 0&&decoded.byteLength!==bytes.exact){ctx.addIssue({code:"custom",message:`must decode to exactly ${bytes.exact} bytes`});}if(bytes.max!==void 0&&decoded.byteLength>bytes.max){ctx.addIssue({code:"custom",message:`must decode to at most ${bytes.max} bytes`});}}catch{ctx.addIssue({code:"custom",message:"must be canonical unpadded base64url"});}})}async function sha256(bytes){return new Uint8Array(await webCryptoSubtle().digest("SHA-256",new Uint8Array(bytes)))}function hex(bytes){let out="";for(const byte of bytes)out+=byte.toString(16).padStart(2,"0");return out}async function contentDigest(value){return `sha256:${hex(await sha256(canonicalizeJsonBytes(value)))}`}var SealingPublicJwkSchema=z.strictObject({kty:z.literal("EC"),crv:z.literal("P-256"),x:base64UrlField({exact:32}),y:base64UrlField({exact:32})});var SealingKeyIdSchema=z.string().regex(new RegExp(`^[A-Za-z0-9_-]{${SEALING_KEY_ID_LENGTH}}$`));function parseSealingPublicJwk(input){const result=safeParseTotal(SealingPublicJwkSchema,input);if(!result.success){throw new ByokCoreError("sealed_secret_invalid","Sealing public key must be a strict P-256 EC public JWK")}return result.data}function sealingPublicKeyBytes(publicJwk){const jwk=parseSealingPublicJwk(publicJwk);const raw=new Uint8Array(HPKE_P256_PUBLIC_KEY_BYTES);raw[0]=4;raw.set(decodeBase64Url(jwk.x,"x"),1);raw.set(decodeBase64Url(jwk.y,"y"),33);return raw}async function deriveSealingKeyId(publicJwk){const raw=sealingPublicKeyBytes(publicJwk);await importP256PublicKey(raw);return encodeBase64Url(await sha256(raw)).slice(0,SEALING_KEY_ID_LENGTH)}var SealingKeyClaimV1Schema=z.strictObject({version:z.literal(1),keyId:SealingKeyIdSchema,epoch:z.number().int().positive().max(Number.MAX_SAFE_INTEGER),publicJwk:SealingPublicJwkSchema});function sealingKeyClaimCanonicalBytes(claim){const result=safeParseTotal(SealingKeyClaimV1Schema,claim);if(!result.success){throw new ByokCoreError("sealed_secret_invalid","Sealing key claim is invalid")}const parsed=result.data;return canonicalizeJsonBytes({version:parsed.version,keyId:parsed.keyId,epoch:parsed.epoch,publicJwk:{kty:parsed.publicJwk.kty,crv:parsed.publicJwk.crv,x:parsed.publicJwk.x,y:parsed.publicJwk.y}})}var OpaqueIdSchema=z.string().min(1).max(200).superRefine((value,ctx)=>{if(value.trim()!==value||/[\u0000-\u001f\u007f]/u.test(value)){ctx.addIssue({code:"custom",message:"must not carry surrounding whitespace or control characters"});}});var CanonicalTimestampSchema=z.string().refine(isCanonicalTimestamp,"must be a canonical ISO-8601 UTC instant");var ProfileRefSchema=z.string().min(1).max(64).regex(/^[a-z0-9]+(?:[-_][a-z0-9]+)*$/u);var ProviderKindSchema=z.string().min(1).max(64).regex(/^[a-z0-9]+(?:[-_][a-z0-9]+)*$/u);var ModelIdSchema=z.string().min(1).max(160).refine(value=>value.trim()===value&&!/[\u0000-\u001f\u007f]/u.test(value),"model id is invalid");var CapabilitySchema=z.string().min(1).max(64).regex(/^[a-z0-9]+(?:-[a-z0-9]+)*$/u);var PiModelJsonSchema=z.custom(value=>{if(value===null||typeof value!=="object"||Array.isArray(value))return false;try{canonicalizeJson(value);return true}catch{return false}},"piModel must be a canonical JSON object");var ModelConfigFields={agentId:OpaqueIdSchema,providerKind:ProviderKindSchema,modelId:ModelIdSchema,piModel:PiModelJsonSchema,capabilities:z.array(CapabilitySchema).max(8).refine(values=>new Set(values).size===values.length,"capabilities must be unique")};var ProviderProvisioningConfigV1Schema=z.discriminatedUnion("operation",[z.strictObject({operation:z.literal("configure"),...ModelConfigFields}),z.strictObject({operation:z.literal("update_model"),...ModelConfigFields}),z.strictObject({operation:z.literal("replace_secret"),agentId:OpaqueIdSchema,providerKind:ProviderKindSchema}),z.strictObject({operation:z.literal("delete"),agentId:OpaqueIdSchema})]);var ProviderProvisioningExpectedProfileSchema=z.union([z.null(),z.strictObject({profileRef:ProfileRefSchema,profileRevision:z.string().regex(/^(?:0|[1-9][0-9]{0,19})$/u),profileHash:z.string().regex(CONTENT_HASH_PATTERN)})]);var ProviderProvisioningIdentityRevisionSchema=z.string().min(1).max(128).regex(/^[A-Za-z0-9._:-]+$/u);var ProviderProvisioningHeaderV1Schema=z.strictObject({tenantId:OpaqueIdSchema,deviceId:OpaqueIdSchema,agentId:OpaqueIdSchema,requestId:OpaqueIdSchema,operation:z.enum(PROVIDER_PROVISIONING_OPERATIONS),operationGeneration:z.number().int().positive().max(Number.MAX_SAFE_INTEGER),expectedProfile:ProviderProvisioningExpectedProfileSchema,expectedEnrollmentRevision:ProviderProvisioningIdentityRevisionSchema,expectedPlacementRevision:ProviderProvisioningIdentityRevisionSchema,configDigest:z.string().regex(CONTENT_HASH_PATTERN),issuedAt:CanonicalTimestampSchema,expiresAt:CanonicalTimestampSchema});var MAX_CIPHERTEXT_BYTES=PROVIDER_SECRET_MAX_PADDED_BYTES+HPKE_AEAD_TAG_BYTES;var SealedProviderSecretV1Schema=z.strictObject({suite:z.literal(PROVIDER_SECRET_HPKE_SUITE),keyId:SealingKeyIdSchema,enc:base64UrlField({exact:HPKE_P256_PUBLIC_KEY_BYTES}),ciphertext:base64UrlField({max:MAX_CIPHERTEXT_BYTES})});function isSecretOperation(operation){return PROVIDER_PROVISIONING_SECRET_OPERATIONS.includes(operation)}var ProviderProvisioningRequestV1Schema=z.strictObject({version:z.literal(PROVIDER_PROVISIONING_REQUEST_VERSION),header:ProviderProvisioningHeaderV1Schema,config:ProviderProvisioningConfigV1Schema,sealed:SealedProviderSecretV1Schema.optional()}).superRefine((request,ctx)=>{if(request.header.operation!==request.config.operation){ctx.addIssue({code:"custom",path:["config","operation"],message:"config operation must equal header operation"});}if(request.header.agentId!==request.config.agentId){ctx.addIssue({code:"custom",path:["config","agentId"],message:"config agentId must equal header agentId"});}if(isSecretOperation(request.header.operation)!==(request.sealed!==void 0)){ctx.addIssue({code:"custom",path:["sealed"],message:"a sealed secret is required exactly for configure and replace_secret"});}});function parseProviderProvisioningRequest(input){const result=safeParseTotal(ProviderProvisioningRequestV1Schema,input);if(!result.success){throw new ByokCoreError("provider_provisioning_request_invalid",`Invalid provider provisioning request: ${(result.error?.issues??[{path:[],message:"is not readable"}]).map(issue=>`${issue.path.join(".")||"<root>"}: ${issue.message}`).join("; ")}`)}return result.data}function configJson(config){return config}async function providerProvisioningConfigDigest(config){const result=safeParseTotal(ProviderProvisioningConfigV1Schema,config);if(!result.success){throw new ByokCoreError("provider_provisioning_request_invalid","Provider provisioning config is invalid")}return contentDigest(configJson(result.data))}function providerSecretAadBytes(header,keyId){const parsed=safeParseTotal(ProviderProvisioningHeaderV1Schema,header);if(!parsed.success||!safeParseTotal(SealingKeyIdSchema,keyId).success){throw new ByokCoreError("provider_provisioning_request_invalid","Provider provisioning AAD input is invalid")}const value=parsed.data;return canonicalizeJsonBytes({v:PROVIDER_PROVISIONING_REQUEST_VERSION,tenantId:value.tenantId,deviceId:value.deviceId,keyId,requestId:value.requestId,agentId:value.agentId,operation:value.operation,operationGeneration:value.operationGeneration,expectedProfile:value.expectedProfile===null?null:{profileRef:value.expectedProfile.profileRef,profileRevision:value.expectedProfile.profileRevision,profileHash:value.expectedProfile.profileHash},expectedEnrollmentRevision:value.expectedEnrollmentRevision,expectedPlacementRevision:value.expectedPlacementRevision,configDigest:value.configDigest,issuedAt:value.issuedAt,expiresAt:value.expiresAt})}async function providerProvisioningRequestDigest(request){const parsed=parseProviderProvisioningRequest(request);return contentDigest(parsed)}async function assertProviderProvisioningConfigDigest(request){const parsed=parseProviderProvisioningRequest(request);if(await providerProvisioningConfigDigest(parsed.config)!==parsed.header.configDigest){throw new ByokCoreError("provider_provisioning_request_invalid","Provider provisioning config digest does not match its header")}}function padProviderSecret(secret){const bytes=new TextEncoder().encode(secret);if(bytes.byteLength===0||bytes.byteLength>PROVIDER_SECRET_MAX_BYTES){throw new ByokCoreError("sealed_secret_invalid",`Provider secret must be 1 to ${PROVIDER_SECRET_MAX_BYTES} UTF-8 bytes`)}const total=Math.ceil((bytes.byteLength+2)/PROVIDER_SECRET_PAD_BLOCK_BYTES)*PROVIDER_SECRET_PAD_BLOCK_BYTES;const out=new Uint8Array(total);out[0]=bytes.byteLength>>8;out[1]=bytes.byteLength&255;out.set(bytes,2);return out}function unpadProviderSecret(padded){if(padded.byteLength<PROVIDER_SECRET_PAD_BLOCK_BYTES||padded.byteLength>PROVIDER_SECRET_MAX_PADDED_BYTES||padded.byteLength%PROVIDER_SECRET_PAD_BLOCK_BYTES!==0){throw new ByokCoreError("sealed_secret_invalid","Padded provider secret has an invalid length")}const length=padded[0]<<8|padded[1];if(length===0||length+2>padded.byteLength||padded.byteLength-(length+2)>=PROVIDER_SECRET_PAD_BLOCK_BYTES){throw new ByokCoreError("sealed_secret_invalid","Padded provider secret has an invalid length prefix")}for(let index=length+2;index<padded.byteLength;index+=1){if(padded[index]!==0){throw new ByokCoreError("sealed_secret_invalid","Padded provider secret has non-zero padding")}}try{return new TextDecoder("utf-8",{fatal:true,ignoreBOM:true}).decode(padded.subarray(2,length+2))}catch{throw new ByokCoreError("sealed_secret_invalid","Provider secret is not valid UTF-8")}}var INFO_BYTES=()=>new TextEncoder().encode(PROVIDER_SECRET_HPKE_INFO);async function sealProviderProvisioningRequest(input){const configResult=safeParseTotal(ProviderProvisioningConfigV1Schema,input.config);if(!configResult.success){throw new ByokCoreError("provider_provisioning_request_invalid","Provider provisioning config is invalid")}const config=configResult.data;const headerResult=safeParseTotal(ProviderProvisioningHeaderV1Schema,{...input.header,configDigest:await providerProvisioningConfigDigest(config)});if(!headerResult.success){throw new ByokCoreError("provider_provisioning_request_invalid","Provider provisioning header is invalid")}const header=headerResult.data;const needsSecret=isSecretOperation(header.operation);if(needsSecret!==(input.secret!==void 0)||needsSecret!==(input.recipient!==void 0)){throw new ByokCoreError("provider_provisioning_request_invalid","A secret and recipient are required exactly for configure and replace_secret")}let sealed;if(needsSecret){const recipient=input.recipient;const publicJwk=parseSealingPublicJwk(recipient.publicJwk);if(await deriveSealingKeyId(publicJwk)!==recipient.keyId){throw new ByokCoreError("sealed_secret_invalid","Sealing key id does not match its public key")}const{enc,ciphertext}=await hpkeSealBase({recipientPublicKey:sealingPublicKeyBytes(publicJwk),info:INFO_BYTES(),aad:providerSecretAadBytes(header,recipient.keyId),plaintext:padProviderSecret(input.secret)});sealed={suite:PROVIDER_SECRET_HPKE_SUITE,keyId:recipient.keyId,enc:encodeBase64Url(enc),ciphertext:encodeBase64Url(ciphertext)};}return parseProviderProvisioningRequest({version:PROVIDER_PROVISIONING_REQUEST_VERSION,header,config,...sealed===void 0?{}:{sealed}})}async function openProviderProvisioningSecret(input){const request=parseProviderProvisioningRequest(input.request);const sealed=request.sealed;if(sealed===void 0){throw new ByokCoreError("provider_provisioning_request_invalid","Provider provisioning request carries no sealed secret")}const publicJwk=parseSealingPublicJwk(input.recipient.publicJwk);if(sealed.keyId!==input.recipient.keyId||await deriveSealingKeyId(publicJwk)!==input.recipient.keyId){throw new ByokCoreError("sealed_secret_invalid","Sealed secret is not addressed to this sealing key")}await assertProviderProvisioningConfigDigest(request);const ciphertext=decodeBase64Url(sealed.ciphertext,"ciphertext");const paddedLength=ciphertext.byteLength-HPKE_AEAD_TAG_BYTES;if(paddedLength<PROVIDER_SECRET_PAD_BLOCK_BYTES||paddedLength>PROVIDER_SECRET_MAX_PADDED_BYTES||paddedLength%PROVIDER_SECRET_PAD_BLOCK_BYTES!==0){throw new ByokCoreError("sealed_secret_invalid","Sealed secret ciphertext has an invalid length")}const padded=await hpkeOpenBase({recipientPrivateKey:input.recipient.privateKey,recipientPublicKey:sealingPublicKeyBytes(publicJwk),enc:decodeBase64Url(sealed.enc,"enc"),info:INFO_BYTES(),aad:providerSecretAadBytes(request.header,sealed.keyId),ciphertext});try{return unpadProviderSecret(padded)}finally{padded.fill(0);}}var AGENT_MEMORY_INTENT_DIGEST_VERSION="byok-agent-memory-intent-v1";async function agentMemoryIntentOperationDigest(input){const canonical={v:AGENT_MEMORY_INTENT_DIGEST_VERSION,tenantId:input.tenantId,deviceId:input.deviceId,intentId:input.intentId,agentRef:{agentId:input.agentRef.agentId,profileRevision:input.agentRef.profileRevision},path:input.path,operation:input.operation,baseRevision:input.baseRevision,targetRevision:input.targetRevision,approvalRef:input.approvalRef};const digest=new Uint8Array(await webCryptoSubtle().digest("SHA-256",new Uint8Array(canonicalizeJsonBytes(canonical))));let hex2="";for(const byte of digest)hex2+=byte.toString(16).padStart(2,"0");return `sha256:${hex2}`}var NONCE_SIGNING_DOMAIN="byok-nonce-v1\n";function nonceSigningBytes(nonce){return new TextEncoder().encode(NONCE_SIGNING_DOMAIN+nonce)}var InMemoryStorageLedger=class{constructor(clock){this.clock=clock;}clock;#usage=new Map;#reservations=new Map;#committed=new Map;readReservation(key){return this.#reservations.get(key)}writeReservation(key,record){this.#reservations.set(key,record);}deleteReservation(key){this.#reservations.delete(key);}reservations(){return this.#reservations.entries()}readUsage(tenant){let usage=this.#usage.get(tenant);if(usage===void 0){usage={committedObjectBytes:0n,committedInlineBytes:0n,reservedBytes:0n,objectCount:0n,updatedAt:this.clock.now().toISOString()};this.#usage.set(tenant,usage);}return usage}writeUsage(tenant,usage){this.#usage.set(tenant,usage);}isCounted(tenant,hash){return this.#committed.get(tenant)?.has(hash)===true}recordCommitted(tenant,reservation){let hashes=this.#committed.get(tenant);if(hashes===void 0){hashes=new Map;this.#committed.set(tenant,hashes);}hashes.set(reservation.contentHash,{kind:reservation.kind,bytes:reservation.expectedBytes});}hasReservedObject(tenant,hash){for(const{reservation}of this.#reservations.values()){if(reservation.tenantId===tenant&&reservation.kind==="object"&&reservation.contentHash===hash&&reservation.state==="reserved")return true}return false}reservedObjectHashes(tenant){return [...this.#reservations.values()].filter(({reservation})=>reservation.tenantId===tenant&&reservation.kind==="object"&&reservation.state==="reserved").map(({reservation})=>reservation.contentHash)}prepareCollectedObjectRelease(tenant,hash,byteSize,now){const hashes=this.#committed.get(tenant);const accounted=hashes?.get(hash);if(accounted===void 0||accounted.kind==="inline")return {releasedBytes:0n,commit:()=>{}};const usage=this.#usage.get(tenant);if(usage===void 0||accounted.bytes!==byteSize||usage.committedObjectBytes<accounted.bytes||usage.objectCount<1n){throw new ByokCoreError("storage_integrity_mismatch","Collected object accounting does not match the storage ledger.")}return {releasedBytes:accounted.bytes,commit:()=>{hashes.delete(hash);this.writeUsage(tenant,{...usage,committedObjectBytes:usage.committedObjectBytes-accounted.bytes,objectCount:usage.objectCount-1n,updatedAt:now});}}}releaseCollectedObject(tenant,hash,byteSize,now){const release=this.prepareCollectedObjectRelease(tenant,hash,byteSize,now);release.commit();return release.releasedBytes}};function assertCollectExpiredInput(input){assertCanonicalTimestamp(input.now,"now");if(!Number.isSafeInteger(input.limit)||input.limit<=0){throw new ByokCoreError("retention_input_invalid","collectExpired limit must be a positive safe integer.")}}var CollectExpiredFailure=class extends ByokCoreError{constructor(committed,failedRows,more,options){super("retention_collect_failed","Retention collection did not complete.",options);this.committed=committed;this.failedRows=failedRows;this.more=more;}committed;failedRows;more};function assertTtl(ttlMs){if(!Number.isFinite(ttlMs)||ttlMs<=0){throw new ByokCoreError("hint_ttl_invalid",`Hint ttl must be a positive number of milliseconds, received ${String(ttlMs)}.`)}}function assertMinimumInterval(minimumIntervalMs){if(!Number.isFinite(minimumIntervalMs)||minimumIntervalMs<0){throw new ByokCoreError("hint_ttl_invalid",`Hint minimum interval must be a non-negative number of milliseconds, received ${String(minimumIntervalMs)}.`)}}var InMemoryPresenceStore=class{#hints=new Map;#clock;constructor(clock){this.#clock=clock;}async publish(tenant,input){assertTtl(input.ttlMs);assertMinimumInterval(input.minimumIntervalMs);const now=this.#clock.now();const key=tenantKey(tenant,input.deviceId);const existing=this.#hints.get(key);if(existing&&now.toISOString()<existing.expiresAt&&Math.max(0,Number(now)-Date.parse(existing.observedAt))<input.minimumIntervalMs){throw new ByokCoreError("hint_rate_limited",`Presence for ${input.deviceId} was published more recently than the configured minimum interval.`)}const hint={tenantId:tenant,deviceId:input.deviceId,level:input.level,...input.detail===void 0?{}:{detail:input.detail},...input.configuredToolsets===void 0?{}:{configuredToolsets:Object.freeze([...input.configuredToolsets])},...input.clientVersion===void 0?{}:{clientVersion:input.clientVersion},...input.protocolVersions===void 0?{}:{protocolVersions:Object.freeze([...input.protocolVersions])},...input.runtimes===void 0?{}:{runtimes:Object.freeze(input.runtimes.map(runtime=>Object.freeze({id:runtime.id,...runtime.version===void 0?{}:{version:runtime.version},...runtime.authPresent===void 0?{}:{authPresent:runtime.authPresent}})))},observedAt:now.toISOString(),expiresAt:new Date(now.getTime()+input.ttlMs).toISOString()};this.#hints.set(key,hint);return hint}async read(tenant,deviceId){const key=tenantKey(tenant,deviceId);const hint=this.#hints.get(key);if(hint===void 0)return void 0;if(this.#isExpired(hint.expiresAt)){this.#hints.delete(key);return void 0}return hint}async list(tenant){const prefix=tenantKey(tenant,"");const live=[];for(const[key,hint]of [...this.#hints.entries()]){if(!key.startsWith(prefix))continue;if(this.#isExpired(hint.expiresAt)){this.#hints.delete(key);continue}live.push(hint);}live.sort((left,right)=>left.deviceId.localeCompare(right.deviceId));return live}async collectExpired(tenant,input){assertCollectExpiredInput(input);let removed=0,more=false;for(const[key,hint]of this.#hints){if(hint.tenantId!==tenant||hint.expiresAt>input.now)continue;if(removed===input.limit){more=true;break}this.#hints.delete(key);removed++;}return {removed,expired:0,releasedBytes:0n,more}}#isExpired(expiresAt){return this.#clock.now().toISOString()>=expiresAt}};var DEFAULT_LIST_LIMIT=50;var InMemoryBoardStore=class{#items=new Map;#seqByTenant=new Map;#clock;constructor(clock){this.#clock=clock;}async create(tenant,input){const key=tenantKey(tenant,input.itemId);if(this.#items.has(key)){throw new ByokCoreError("board_item_exists",`Board item ${input.itemId} already exists in this tenant.`)}const now=this.#now();const item={tenantId:tenant,itemId:input.itemId,channel:input.channel,title:input.title,status:input.status??"todo",boardSeq:this.#nextSeq(tenant),createdAt:now,updatedAt:now};this.#items.set(key,item);return item}async get(tenant,itemId){return this.#items.get(tenantKey(tenant,itemId))}async list(tenant,query){const afterSeq=query.afterSeq??0;const limit=query.limit??DEFAULT_LIST_LIMIT;const prefix=tenantKey(tenant,"");const matches=[];for(const[key,item]of this.#items.entries()){if(!key.startsWith(prefix))continue;if(item.boardSeq<=afterSeq)continue;if(query.channel!==void 0&&item.channel!==query.channel)continue;if(query.status!==void 0&&item.status!==query.status)continue;matches.push(item);}matches.sort((left,right)=>left.boardSeq-right.boardSeq);const page=matches.slice(0,limit);return {items:page,nextSeq:page.at(-1)?.boardSeq??afterSeq,hasMore:matches.length>page.length}}async claim(tenant,input){const item=this.#require(tenant,input.itemId);const expectedStatus=input.expectedStatus??"todo";if(item.assignee!==void 0){if(item.assignee.holderId===input.holderId){if(input.expectedStatus!==void 0&&item.status!==input.expectedStatus){throw new CoreConflictError("board_status_conflict",`Board item ${input.itemId} is ${item.status}, not ${input.expectedStatus}.`,item,this.#now())}return item}throw new CoreConflictError("board_claim_conflict",`Board item ${input.itemId} is held by ${item.assignee.holderId}.`,item,this.#now())}if(item.status!==expectedStatus){throw new CoreConflictError("board_status_conflict",`Board item ${input.itemId} is ${item.status}, not ${expectedStatus}.`,item,this.#now())}if(item.status!=="todo"&&item.status!=="in_progress"){throw new CoreConflictError("board_transition_invalid",`Board item ${input.itemId} cannot be claimed from ${item.status}.`,item,this.#now())}const now=this.#now();const claimed={...item,status:item.status==="todo"?"in_progress":item.status,assignee:{holderId:input.holderId,heldSince:now},boardSeq:this.#nextSeq(tenant),updatedAt:now};this.#items.set(tenantKey(tenant,input.itemId),claimed);return claimed}async unclaim(tenant,input){const item=this.#require(tenant,input.itemId);if(item.assignee===void 0){throw new ByokCoreError("board_not_held",`Board item ${input.itemId} is not held by anyone.`)}if(item.assignee.holderId!==input.holderId){throw new CoreConflictError("board_claim_conflict",`Board item ${input.itemId} is held by ${item.assignee.holderId}, not ${input.holderId}.`,item,this.#now())}const now=this.#now();const released={tenantId:item.tenantId,itemId:item.itemId,channel:item.channel,title:item.title,status:item.status==="in_progress"?"todo":item.status,boardSeq:this.#nextSeq(tenant),createdAt:item.createdAt,updatedAt:now};this.#items.set(tenantKey(tenant,input.itemId),released);return released}async updateStatus(tenant,input){const item=this.#require(tenant,input.itemId);if(item.status!==input.expectedStatus){throw new CoreConflictError("board_status_conflict",`Board item ${input.itemId} is ${item.status}, not ${input.expectedStatus}.`,item,this.#now())}if(input.holderId!==void 0&&item.assignee?.holderId!==input.holderId){throw new CoreConflictError("board_claim_conflict",`Board item ${input.itemId} is not held by ${input.holderId}.`,item,this.#now())}if(!isLegalBoardTransition(input.expectedStatus,input.status)){throw new CoreConflictError("board_transition_invalid",`${input.expectedStatus} to ${input.status} is not a legal board transition.`,item,this.#now())}const now=this.#now();const updated={...item,status:input.status,boardSeq:this.#nextSeq(tenant),updatedAt:now};this.#items.set(tenantKey(tenant,input.itemId),updated);return updated}#require(tenant,itemId){const item=this.#items.get(tenantKey(tenant,itemId));if(item===void 0){throw new ByokCoreError("board_item_not_found",`Board item ${itemId} does not exist in this tenant.`)}return item}#nextSeq(tenant){const next=(this.#seqByTenant.get(tenant)??0)+1;this.#seqByTenant.set(tenant,next);return next}#now(){return this.#clock.now().toISOString()}};var DEFAULT_READ_LIMIT=50;var InMemoryMailboxStore=class{#devices=new Map;#clock;#quota;constructor(clock,quota){this.#clock=clock;this.#quota=quota;}async append(tenant,input){const device=this.#device(tenant,input.deviceId);const appended=device.mutationTail.then(async()=>{const existing=device.byMessageId.get(input.messageId);if(existing!==void 0)return existing;const limit=input.admission==="cancellation"?void 0:await this.#mailboxLimit(tenant);const seq=device.nextSeq;const materialized=await input.materialize(seq);if(limit!==void 0){const used=this.#usedBytes(tenant);if(used+materialized.byteSize>limit){throw new ByokCoreError("storage_quota_exceeded",`Mailbox would reach ${String(used+materialized.byteSize)} bytes, over the limit of ${String(limit)} bytes.`)}}const message={tenantId:tenant,deviceId:input.deviceId,seq,messageId:input.messageId,...materialized,state:"pending",appendedAt:this.#now()};device.nextSeq+=1;device.messages.push(message);device.byMessageId.set(message.messageId,message);return message});device.mutationTail=appended.then(()=>void 0,()=>void 0);return appended}async readUsage(tenant){return {bytes:this.#usedBytes(tenant)}}async readAfter(tenant,query){const device=this.#devices.get(tenantKey(tenant,query.deviceId));const limit=query.limit??DEFAULT_READ_LIMIT;if(device===void 0){return {messages:[],nextSeq:query.afterSeq,hasMore:false,recoverableFrom:1}}const pending=device.messages.filter(message=>message.state==="pending"&&message.seq>query.afterSeq).sort((left,right)=>left.seq-right.seq);const page=pending.slice(0,limit);const last=page.at(-1);return {messages:page,nextSeq:last?.seq??query.afterSeq,hasMore:pending.length>page.length,recoverableFrom:this.#recoverableFrom(device)}}async advanceCursor(tenant,input){const device=this.#device(tenant,input.deviceId);const advanced=device.mutationTail.then(()=>{if(input.ackedSeq<device.ackedSeq){throw new CoreConflictError("mailbox_cursor_regression",`Cursor for device ${input.deviceId} is at ${device.ackedSeq}; refusing to move it back to ${input.ackedSeq}.`,this.#cursorState(tenant,input.deviceId,device),this.#now())}if(input.ackedSeq>device.deliveredSeq){throw new CoreConflictError("mailbox_cursor_ahead_of_delivery",`Cursor for device ${input.deviceId} was delivered through ${device.deliveredSeq}; refusing to acknowledge future cursor ${input.ackedSeq}.`,this.#cursorState(tenant,input.deviceId,device),this.#now())}device.ackedSeq=input.ackedSeq;device.cursorUpdatedAt=this.#now();for(const[index,message]of device.messages.entries()){if(message.state==="pending"&&message.seq<=input.ackedSeq){device.messages[index]={...message,state:"acked"};device.byMessageId.set(message.messageId,device.messages[index]);}}return this.#cursorState(tenant,input.deviceId,device)});device.mutationTail=advanced.then(()=>void 0,()=>void 0);return advanced}async recordDelivery(tenant,input){const device=this.#device(tenant,input.deviceId);const recorded=device.mutationTail.then(()=>{if(input.deliveredSeq>device.deliveredSeq){device.deliveredSeq=input.deliveredSeq;}return this.#cursorState(tenant,input.deviceId,device)});device.mutationTail=recorded.then(()=>void 0,()=>void 0);return recorded}async readCursor(tenant,deviceId){const device=this.#devices.get(tenantKey(tenant,deviceId));if(device===void 0){return {tenantId:tenant,deviceId,deliveredSeq:0,ackedSeq:0,updatedAt:this.#now()}}return this.#cursorState(tenant,deviceId,device)}async collectExpired(tenant,input){assertCollectExpiredInput(input);assertCanonicalTimestamp(input.ackedBefore,"ackedBefore");assertCanonicalTimestamp(input.expireUnackedBefore,"expireUnackedBefore");let removed=0,expired=0,more=false;try{phases:for(const phase of ["acked","pending"]){for(const[key,device]of this.#devices){if(!key.startsWith(tenantKey(tenant,"")))continue;const collected=device.mutationTail.then(()=>{for(let index=0;index<device.messages.length;){const message=device.messages[index];const cutoff=phase==="acked"?input.ackedBefore:input.expireUnackedBefore;if(message.state!==phase||message.appendedAt>=cutoff){index++;continue}if(removed+expired===input.limit){more=true;return}if(phase==="acked"){device.messages.splice(index,1);device.byMessageId.delete(message.messageId);removed++;}else {const retired={...message,state:"expired"};device.messages[index]=retired;device.byMessageId.set(retired.messageId,retired);expired++;index++;}}});device.mutationTail=collected.then(()=>void 0,()=>void 0);await collected;if(more)break phases}}}catch(cause){throw new CollectExpiredFailure({removed,expired,releasedBytes:0n},0,void 0,{cause})}return {removed,expired,releasedBytes:0n,more}}async#mailboxLimit(tenant){const entitlement=await this.#quota.readEntitlement(tenant);if(entitlement===void 0){throw new ByokCoreError("storage_entitlement_missing","No storage entitlement has been issued for this tenant.")}return entitlement.mailboxLimitBytes}#usedBytes(tenant){const prefix=tenantKey(tenant,"");let bytes=0n;for(const[key,device]of this.#devices.entries()){if(!key.startsWith(prefix))continue;for(const message of device.messages)bytes+=message.byteSize;}return bytes}#device(tenant,deviceId){if(deviceId.length===0){throw new ByokCoreError("mailbox_message_not_found","Device id must not be empty.")}const key=tenantKey(tenant,deviceId);const existing=this.#devices.get(key);if(existing!==void 0)return existing;const created={nextSeq:1,deliveredSeq:0,ackedSeq:0,cursorUpdatedAt:this.#now(),mutationTail:Promise.resolve(),messages:[],byMessageId:new Map};this.#devices.set(key,created);return created}#recoverableFrom(device){let lost=0;for(const message of device.messages){if(message.state==="expired"&&message.seq>lost)lost=message.seq;}return lost+1}#cursorState(tenant,deviceId,device){return {tenantId:tenant,deviceId,deliveredSeq:device.deliveredSeq,ackedSeq:device.ackedSeq,updatedAt:device.cursorUpdatedAt}}#now(){return this.#clock.now().toISOString()}};var DEFAULT_LIST_LIMIT2=100;var InMemoryObjectStore=class{constructor(clock,maintenance){this.maintenance=maintenance;this.#clock=clock;if(maintenance===void 0||maintenance.ledger===void 0||typeof maintenance.bytes?.deleteObject!=="function"){throw new ByokCoreError("retention_input_invalid","ObjectStore requires its shared ledger and an explicit byte authority.")}}maintenance;#manifest=new Map;#references=new Map;#clock;#deleteCursor=new Map;async putManifest(tenant,input){const key=tenantKey(tenant,input.hash);const existing=this.#manifest.get(key);if(existing!==void 0&&existing.state!=="deleted")return existing;const now=this.#now();const entry={tenantId:tenant,hash:input.hash,byteSize:input.byteSize,contentType:input.contentType,state:"pending",refCount:0,createdAt:now,updatedAt:now};this.#manifest.set(key,entry);return entry}async commit(tenant,input){const entry=this.#require(tenant,input.hash);if(entry.byteSize!==input.observedByteSize||entry.contentType!==input.observedContentType){throw new ByokCoreError("storage_integrity_mismatch",`Observed object ${input.hash} (${String(input.observedByteSize)} bytes, ${input.observedContentType}) does not match the declared manifest.`)}if(entry.state==="committed")return entry;return this.#transition(tenant,entry,"committed")}async get(tenant,hash){return this.#manifest.get(tenantKey(tenant,hash))}async list(tenant,query){if(query.deletePendingBefore!==void 0){assertCanonicalTimestamp(query.deletePendingBefore,"deletePendingBefore");}const prefix=tenantKey(tenant,"");const matches=[];for(const[key,entry]of this.#manifest.entries()){if(!key.startsWith(prefix))continue;if(query.state!==void 0&&entry.state!==query.state)continue;if(query.deletePendingBefore!==void 0){if(entry.deletePendingAt===void 0)continue;if(entry.deletePendingAt>=query.deletePendingBefore)continue}matches.push(entry);}matches.sort((left,right)=>left.hash.localeCompare(right.hash));return matches.slice(0,query.limit??DEFAULT_LIST_LIMIT2)}async addReference(tenant,input){const entry=this.#require(tenant,input.hash);if(entry.state!=="committed"){throw new ByokCoreError("object_state_invalid",`Only committed objects can be referenced; ${input.hash} is ${entry.state}.`)}const refKey=tenantKey(tenant,input.hash,input.refKind,input.refId);if(!this.#references.has(refKey)){this.#references.set(refKey,{tenantId:tenant,hash:input.hash,refKind:input.refKind,refId:input.refId,createdAt:this.#now()});}return this.#recount(tenant,entry)}async removeReference(tenant,input){const entry=this.#require(tenant,input.hash);this.#references.delete(tenantKey(tenant,input.hash,input.refKind,input.refId));return this.#recount(tenant,entry)}async markDeletePending(tenant,hash){const entry=this.#require(tenant,hash);if(entry.refCount!==0){throw new ByokCoreError("object_state_invalid",`Object ${hash} still has ${entry.refCount} reference(s).`)}return this.#transition(tenant,entry,"delete_pending")}async markDeleted(tenant,hash){const entry=this.#require(tenant,hash);return this.#transition(tenant,entry,"deleted")}async collectExpired(tenant,input){assertCollectExpiredInput(input);assertCanonicalTimestamp(input.orphanBefore,"orphanBefore");const eligible=entry=>entry.tenantId===tenant&&entry.refCount===0&&!this.maintenance.ledger.hasReservedObject(tenant,entry.hash)&&(entry.state==="delete_pending"||(entry.state==="pending"||entry.state==="committed")&&entry.updatedAt<input.orphanBefore);let removed=0,releasedBytes=0n,more=false,visited=0,failures=0;let firstFailure;const cursor=this.#deleteCursor.get(tenant)??"";const entries=[...this.#manifest.values()].filter(eligible);const pending=entries.filter(entry=>entry.state==="delete_pending").sort((a,b)=>a.hash.localeCompare(b.hash));const first=pending.filter(entry=>entry.hash>cursor).slice(0,input.limit);const fresh=entries.filter(entry=>entry.state!=="delete_pending").sort((a,b)=>a.updatedAt.localeCompare(b.updatedAt)||a.hash.localeCompare(b.hash)).slice(0,input.limit-first.length);const wrapped=pending.filter(entry=>entry.hash<=cursor).slice(0,input.limit-first.length-fresh.length);const candidates=[...first,...fresh,...wrapped];more=entries.length>candidates.length;for(const candidate of candidates){if(visited++===input.limit){more=true;break}const entry=this.#manifest.get(tenantKey(tenant,candidate.hash));if(!eligible(entry))continue;const pending2=entry.state==="delete_pending"?entry:{...entry,state:"delete_pending",deletePendingAt:input.now,updatedAt:input.now};this.#manifest.set(tenantKey(tenant,entry.hash),pending2);try{const release=this.maintenance.ledger.prepareCollectedObjectRelease(tenant,entry.hash,entry.byteSize,input.now);this.maintenance.bytes.deleteObject(tenant,entry.hash);release.commit();const released=release.releasedBytes;this.#manifest.set(tenantKey(tenant,entry.hash),{...pending2,state:"deleted",updatedAt:input.now});removed++;releasedBytes+=released;}catch(cause){failures++;firstFailure??=cause;}}this.#deleteCursor.set(tenant,wrapped.at(-1)?.hash??first.at(-1)?.hash??"");if(failures>0)throw new CollectExpiredFailure({removed,expired:0,releasedBytes},failures,true,{cause:firstFailure});return {removed,expired:0,releasedBytes,more}}#transition(tenant,entry,next){if(!isLegalObjectTransition(entry.state,next)){throw new ByokCoreError("object_state_invalid",`${entry.state} to ${next} is not a legal object manifest transition.`)}const now=this.#now();const updated={...entry,state:next,updatedAt:now,...next==="delete_pending"?{deletePendingAt:now}:{}};this.#manifest.set(tenantKey(tenant,entry.hash),updated);return updated}#recount(tenant,entry){const refPrefix=tenantKey(tenant,entry.hash,"");let refCount=0;for(const key of this.#references.keys()){if(key.startsWith(refPrefix))refCount+=1;}const updated={...entry,refCount,updatedAt:this.#now()};this.#manifest.set(tenantKey(tenant,entry.hash),updated);return updated}#require(tenant,hash){const entry=this.#manifest.get(tenantKey(tenant,hash));if(entry===void 0){throw new ByokCoreError("object_not_found",`Object ${hash} has no manifest row in this tenant.`)}return entry}#now(){return this.#clock.now().toISOString()}};var WARNING_NUMERATOR=80n;var WARNING_DENOMINATOR=100n;var InMemoryQuotaStore=class{constructor(clock,objects,ledger){this.ledger=ledger;this.#clock=clock;this.#objects=objects;}ledger;#entitlements=new Map;#clock;#objects;async readEntitlement(tenant){return this.#entitlements.get(tenant)}async writeEntitlement(tenant,input){if(input.downgradeGraceUntil!==void 0){assertCanonicalTimestamp(input.downgradeGraceUntil,"downgradeGraceUntil");}const existing=this.#entitlements.get(tenant);if(existing!==void 0&&input.version<=existing.version){throw new CoreConflictError("storage_entitlement_version_conflict",`Entitlement is at version ${String(existing.version)}; refusing to apply version ${String(input.version)}.`,existing,this.#now())}const entitlement={tenantId:tenant,version:input.version,hardLimitBytes:input.hardLimitBytes,maxObjectBytes:input.maxObjectBytes,maxInlineBytes:input.maxInlineBytes,mailboxLimitBytes:input.mailboxLimitBytes,retentionPolicyId:input.retentionPolicyId,...input.downgradeGraceUntil===void 0?{}:{downgradeGraceUntil:input.downgradeGraceUntil}};this.#entitlements.set(tenant,entitlement);return entitlement}async readUsage(tenant){return this.#usageOf(tenant)}async readStatus(tenant){const entitlement=this.#requireEntitlement(tenant);const usage=this.#usageOf(tenant);const used=this.#usedBytes(usage);const graceActive=entitlement.downgradeGraceUntil!==void 0&&this.#now()<entitlement.downgradeGraceUntil;return {entitlement,usage,posture:this.#posture(entitlement,usage,graceActive),availableBytes:used>=entitlement.hardLimitBytes?0n:entitlement.hardLimitBytes-used,graceActive}}async readReservation(tenant,reservationId){return this.ledger.readReservation(tenantKey(tenant,reservationId))?.reservation}async reserve(tenant,input){const entitlement=this.#requireEntitlement(tenant);await Array.from(this.#expireReservations(tenant));const key=tenantKey(tenant,input.reservationId);const existing=this.ledger.readReservation(key);if(existing!==void 0){if(existing.reservation.state==="reserved"){if(existing.reservation.kind!==input.kind||existing.reservation.expectedBytes!==input.expectedBytes||existing.reservation.contentHash!==input.contentHash||existing.reservation.contentType!==input.contentType){throw new ByokCoreError("storage_integrity_mismatch",`Reservation ${input.reservationId} already binds a different storage declaration.`)}return existing.reservation}throw new ByokCoreError("storage_reservation_expired",`Reservation ${input.reservationId} is already ${existing.reservation.state}.`)}const usage=this.#usageOf(tenant);const graceActive=entitlement.downgradeGraceUntil!==void 0&&this.#now()<entitlement.downgradeGraceUntil;if(this.#posture(entitlement,usage,graceActive)==="suspended"){throw new ByokCoreError("storage_write_suspended","Tenant is over its hard limit and its downgrade grace has ended; durable writes are suspended.")}const perObjectLimit=input.kind==="object"?entitlement.maxObjectBytes:entitlement.maxInlineBytes;if(input.expectedBytes>perObjectLimit){throw new ByokCoreError("storage_object_too_large",`${String(input.expectedBytes)} bytes exceeds the ${input.kind} limit of ${String(perObjectLimit)} bytes.`)}if(this.#usedBytes(usage)+input.expectedBytes>entitlement.hardLimitBytes){throw new ByokCoreError("storage_quota_exceeded",`Reserving ${String(input.expectedBytes)} bytes would exceed the hard limit of ${String(entitlement.hardLimitBytes)} bytes.`)}const now=this.#now();const reservation={tenantId:tenant,reservationId:input.reservationId,state:"reserved",kind:input.kind,expectedBytes:input.expectedBytes,contentHash:input.contentHash,contentType:input.contentType,createdAt:now,expiresAt:new Date(this.#clock.now().getTime()+input.ttlMs).toISOString()};this.ledger.writeReservation(key,{reservation,deduplicated:false});this.#setUsage(tenant,{...usage,reservedBytes:usage.reservedBytes+input.expectedBytes});return reservation}async finalizeReservation(tenant,input){const key=tenantKey(tenant,input.reservationId);const record=this.#requireReservation(tenant,input.reservationId);const reservation=record.reservation;if(reservation.state==="committed"){return {reservation,usage:this.#usageOf(tenant),deduplicated:record.deduplicated}}if(reservation.state!=="reserved"){throw new ByokCoreError("storage_reservation_expired",`Reservation ${input.reservationId} is ${reservation.state}.`)}if(this.#now()>=reservation.expiresAt){this.#settle(tenant,key,record,"expired");throw new ByokCoreError("storage_reservation_expired",`Reservation ${input.reservationId} expired at ${reservation.expiresAt}.`)}if(input.observedByteSize!==reservation.expectedBytes||input.observedContentType!==reservation.contentType){this.#settle(tenant,key,record,"aborted");throw new ByokCoreError("storage_integrity_mismatch",`Observed object does not match reservation ${input.reservationId}.`)}if(reservation.kind==="object"){try{await this.#objects.commit(tenant,{hash:reservation.contentHash,observedByteSize:input.observedByteSize,observedContentType:input.observedContentType});}catch(error){this.#settle(tenant,key,record,"aborted");if(error instanceof ByokCoreError&&(error.code==="object_not_found"||error.code==="object_state_invalid")){throw new ByokCoreError("storage_integrity_mismatch",`Reservation ${input.reservationId} has no committable object manifest.`,{cause:error})}throw error}}const deduplicated=this.ledger.isCounted(tenant,reservation.contentHash);const settled=this.#settle(tenant,key,record,"committed",deduplicated);if(!deduplicated){this.ledger.recordCommitted(tenant,reservation);const current=this.#usageOf(tenant);this.#setUsage(tenant,{...current,committedObjectBytes:reservation.kind==="object"?current.committedObjectBytes+reservation.expectedBytes:current.committedObjectBytes,committedInlineBytes:reservation.kind==="inline"?current.committedInlineBytes+reservation.expectedBytes:current.committedInlineBytes,objectCount:reservation.kind==="object"?current.objectCount+1n:current.objectCount});}return {reservation:settled,usage:this.#usageOf(tenant),deduplicated}}async abortReservation(tenant,reservationId){const key=tenantKey(tenant,reservationId);const record=this.#requireReservation(tenant,reservationId);if(record.reservation.state!=="reserved")return record.reservation;return this.#settle(tenant,key,record,"aborted")}*#expireReservations(tenant,now=this.#now(),limit=Number.MAX_SAFE_INTEGER){const prefix=tenantKey(tenant,"");let expired=0;for(const[key,record]of this.ledger.reservations()){if(!key.startsWith(prefix)||record.reservation.state!=="reserved"||now<record.reservation.expiresAt)continue;yield this.#settle(tenant,key,record,"expired",false,now);if(++expired===limit)return}}async collectExpired(tenant,input){assertCollectExpiredInput(input);assertCanonicalTimestamp(input.settledBefore,"settledBefore");let expired=0,releasedBytes=0n,removed=0,more=false;try{for(const reservation of this.#expireReservations(tenant,input.now,input.limit)){expired++;releasedBytes+=reservation.expectedBytes;}}catch(cause){throw new CollectExpiredFailure({removed,expired,releasedBytes},1,true,{cause})}const prefix=tenantKey(tenant,"");try{for(const[key,record]of this.ledger.reservations()){if(!key.startsWith(prefix))continue;const row=record.reservation;const shouldExpire=row.state==="reserved"&&row.expiresAt<=input.now;const shouldRemove=(row.state==="expired"||row.state==="aborted")&&row.settledAt!==void 0&&row.settledAt<input.settledBefore;if(!shouldExpire&&!shouldRemove)continue;if(shouldExpire||removed+expired===input.limit){more=true;continue}this.ledger.deleteReservation(key);removed++;}}catch(cause){throw new CollectExpiredFailure({removed,expired,releasedBytes},0,void 0,{cause})}return {removed,expired,releasedBytes,more}}#settle(tenant,key,record,state,deduplicated=false,settledAt=this.#now()){const settled={...record.reservation,state,settledAt};const usage=this.#usageOf(tenant);if(usage.reservedBytes<record.reservation.expectedBytes){throw new ByokCoreError("storage_integrity_mismatch","Reservation settlement would underflow reserved usage.")}this.ledger.writeReservation(key,{reservation:settled,deduplicated});this.#setUsage(tenant,{...usage,reservedBytes:usage.reservedBytes-record.reservation.expectedBytes});return settled}#posture(entitlement,usage,graceActive){const used=this.#usedBytes(usage);if(used>=entitlement.hardLimitBytes){const graceConfigured=entitlement.downgradeGraceUntil!==void 0;return graceConfigured&&!graceActive?"suspended":"blocked"}if(entitlement.hardLimitBytes>0n&&used*WARNING_DENOMINATOR>=entitlement.hardLimitBytes*WARNING_NUMERATOR){return "warning"}return "normal"}#usedBytes(usage){return usage.committedObjectBytes+usage.committedInlineBytes+usage.reservedBytes}#usageOf(tenant){return this.ledger.readUsage(tenant)}#setUsage(tenant,usage){this.ledger.writeUsage(tenant,{...usage,updatedAt:this.#now()});}#requireEntitlement(tenant){const entitlement=this.#entitlements.get(tenant);if(entitlement===void 0){throw new ByokCoreError("storage_entitlement_missing","No storage entitlement has been issued for this tenant.")}return entitlement}#requireReservation(tenant,reservationId){const record=this.ledger.readReservation(tenantKey(tenant,reservationId));if(record===void 0){throw new ByokCoreError("storage_reservation_not_found",`Reservation ${reservationId} does not exist in this tenant.`)}return record}#now(){return this.#clock.now().toISOString()}};var DEFAULT_LIST_LIMIT3=50;var InMemorySkillPackStore=class{#packs=new Map;async publish(tenant,input){const{manifest}=input;const structural=checkSkillPackManifest(manifest);if(!structural.ok){throw new ByokCoreError("skill_pack_manifest_invalid",`Refusing to publish ${JSON.stringify(manifest.name)}: ${structural.reason} \u2014 ${structural.detail}`)}const contents=new Map;for(const file of input.files){if(contents.has(file.path)){throw new ByokCoreError("skill_pack_manifest_invalid",`Refusing to publish ${JSON.stringify(manifest.name)}: ${JSON.stringify(file.path)} was supplied twice.`)}contents.set(file.path,file.content);}for(const declared of manifest.files){const content=contents.get(declared.path);if(content===void 0){throw new ByokCoreError("skill_pack_manifest_invalid",`Refusing to publish ${JSON.stringify(manifest.name)}: ${JSON.stringify(declared.path)} is declared but was not supplied.`)}const bytes=new TextEncoder().encode(content).length;if(bytes!==declared.byteSize){throw new ByokCoreError("skill_pack_manifest_invalid",`Refusing to publish ${JSON.stringify(manifest.name)}: ${JSON.stringify(declared.path)} declares ${declared.byteSize} bytes and supplies ${bytes}.`)}}if(contents.size!==manifest.files.length){throw new ByokCoreError("skill_pack_manifest_invalid",`Refusing to publish ${JSON.stringify(manifest.name)}: ${contents.size} files were supplied for ${manifest.files.length} declared rows.`)}const entry=checkSkillPackEntry(manifest,contents.get(SKILL_PACK_ENTRY_PATH));if(!entry.ok){throw new ByokCoreError("skill_pack_manifest_invalid",`Refusing to publish ${JSON.stringify(manifest.name)}: ${entry.reason} \u2014 ${entry.detail}`)}this.#packs.set(tenantKey(tenant,manifest.name),{manifest,contents});return manifest}async get(tenant,name){return this.#packs.get(tenantKey(tenant,name))?.manifest}async list(tenant,query){const prefix=tenantKey(tenant,"");const manifests=[];for(const[key,pack]of this.#packs.entries()){if(!key.startsWith(prefix))continue;manifests.push(pack.manifest);}manifests.sort((left,right)=>left.name<right.name?-1:left.name>right.name?1:0);return manifests.slice(0,query.limit??DEFAULT_LIST_LIMIT3)}async readFile(tenant,name,path){const pack=this.#packs.get(tenantKey(tenant,name));if(pack===void 0)return void 0;const declared=pack.manifest.files.find(file=>file.path===path);const content=pack.contents.get(path);if(declared===void 0||content===void 0)return void 0;return {path:declared.path,contentHash:declared.contentHash,byteSize:declared.byteSize,content}}};var DEFAULT_MANIFEST_LIMIT=100;var InMemoryTruthStore=class{#records=new Map;#clock;constructor(clock){this.#clock=clock;}async writeTerminal(tenant,input){const key=tenantKey(tenant,"task.terminal",input.taskId);const existing=this.#records.get(key);if(existing!==void 0){if(existing.contentHash===input.contentHash){return existing}throw new CoreConflictError("terminal_conflict",`Task ${input.taskId} already has an immutable terminal record with a different hash.`,existing,this.#now())}const record={tenantId:tenant,kind:"task.terminal",recordKey:input.taskId,rev:1,contentHash:input.contentHash,byteSize:input.byteSize,body:input.body,...input.label===void 0?{}:{label:input.label},...input.requestId===void 0?{}:{requestId:input.requestId},writtenAt:this.#now()};this.#records.set(key,record);return record}async writeSnapshot(tenant,input){const key=tenantKey(tenant,input.kind,input.recordKey);const existing=this.#records.get(key);const currentRev=existing?.rev??0;if(input.expectedRev!==currentRev){throw new CoreConflictError("truth_revision_conflict",`Record ${input.kind}/${input.recordKey} is at rev ${currentRev}, not ${input.expectedRev}.`,existing,this.#now())}const record={tenantId:tenant,kind:input.kind,recordKey:input.recordKey,rev:currentRev+1,contentHash:input.contentHash,byteSize:input.byteSize,body:input.body,...input.label===void 0?{}:{label:input.label},...input.requestId===void 0?{}:{requestId:input.requestId},writtenAt:this.#now()};this.#records.set(key,record);return record}async getRecord(tenant,selector){return this.#records.get(tenantKey(tenant,selector.kind,selector.recordKey))}async listManifest(tenant,query){const prefix=tenantKey(tenant,"");const entries=[];for(const[key,record]of this.#records.entries()){if(!key.startsWith(prefix))continue;if(query.kind!==void 0&&record.kind!==query.kind)continue;if(query.keyPrefix!==void 0&&!record.recordKey.startsWith(query.keyPrefix)){continue}entries.push({kind:record.kind,recordKey:record.recordKey,rev:record.rev,contentHash:record.contentHash,byteSize:record.byteSize,...record.label===void 0?{}:{label:record.label},updatedAt:record.writtenAt});}entries.sort((left,right)=>left.kind.localeCompare(right.kind)||left.recordKey.localeCompare(right.recordKey));return entries.slice(0,query.limit??DEFAULT_MANIFEST_LIMIT)}#now(){return this.#clock.now().toISOString()}};var IN_MEMORY_CLOCK_EPOCH="2026-01-01T00:00:00.000Z";function createMutableClock(start=new Date(IN_MEMORY_CLOCK_EPOCH)){let current=start.getTime();return {now(){return new Date(current)},advance(ms){current+=ms;},set(instant){current=instant.getTime();}}}function replayKey(input){return JSON.stringify([input.tenantId,input.issuer,input.productId,input.deviceId,input.audience,input.schema,input.jti])}var InMemoryDeviceAssertionReplayAuthority=class{#expiresAtByKey=new Map;async consume(input){const expiresAt=Date.parse(input.expiresAt);if(!Number.isFinite(expiresAt))throw new Error("device assertion replay expiry is invalid");const key=replayKey(input);if(this.#expiresAtByKey.has(key))return false;this.#expiresAtByKey.set(key,expiresAt);return true}async collectExpired(tenant,input){assertCollectExpiredInput(input);const cutoff=Date.parse(input.now);let removed=0,more=false;for(const[key,expiresAt]of this.#expiresAtByKey){if(JSON.parse(key)[0]!==tenant||expiresAt>cutoff)continue;if(removed===input.limit){more=true;break}this.#expiresAtByKey.delete(key);removed++;}return {removed,expired:0,releasedBytes:0n,more}}};function createInMemoryCoreStores(options){const clock=options.clock??createMutableClock();const ledger=new InMemoryStorageLedger(clock);const objects=new InMemoryObjectStore(clock,{ledger,bytes:options.objectBytes});const quota=new InMemoryQuotaStore(clock,objects,ledger);const stores={mailbox:new InMemoryMailboxStore(clock,quota),board:new InMemoryBoardStore(clock),truth:new InMemoryTruthStore(clock),presence:new InMemoryPresenceStore(clock),objects,quota,skillPacks:new InMemorySkillPackStore};return {stores,clock}}function createInMemoryCoreCompositionWithClock(objectBytes){const clock=createMutableClock();return {stores:createInMemoryCoreStores({clock,objectBytes}).stores,clock}}var ROUTINE_DAYS_OF_WEEK=Object.freeze(["mon","tue","wed","thu","fri","sat","sun"]);var ROUTINE_HORIZON_MS=368*864e5;var ROUTINE_MIN_NOW=Date.UTC(2e3,0,1);var ROUTINE_MAX_NOW=Date.UTC(2200,0,1);var KEYS=new Set(["minute","hour","daysOfWeek","dayOfMonth","month","tz"]);var COMMON_YEAR_DAYS=[31,28,31,30,31,30,31,31,30,31,30,31];var TZ_NAME=/^[A-Za-z][A-Za-z0-9_+-]*(?:\/[A-Za-z0-9_+-]+)*$/;var DAY_MS=864e5;var UTC_DAY_NAME=["sun","mon","tue","wed","thu","fri","sat"];var ScheduleError=class extends Error{code;constructor(code,message){super(message);this.code=code;}};var invalid=message=>new ScheduleError("schedule_invalid",message);function integer(value,name,min,max){if(typeof value!=="number"||!Number.isInteger(value)||value<min||value>max)throw invalid(`${name} must be an integer ${min}-${max}`);return value}var formatters=new Map;function formatter(tz){let f=formatters.get(tz);if(!f){f=new Intl.DateTimeFormat("en-US",{timeZone:tz,hourCycle:"h23",year:"numeric",month:"numeric",day:"numeric",hour:"numeric",minute:"numeric",second:"numeric"});if(formatters.size>=64)formatters.delete(formatters.keys().next().value);formatters.set(tz,f);}return f}function checkTz(tz){if(typeof tz!=="string"||!TZ_NAME.test(tz))throw new ScheduleError("tz_invalid",`${JSON.stringify(tz)} is not an IANA zone name`);try{formatter(tz);}catch{throw new ScheduleError("tz_invalid",`"${tz}" is not a known IANA zone`)}return tz}function parse(schedule){if(schedule===null||typeof schedule!=="object"||Array.isArray(schedule))throw invalid("schedule must be an object");for(const key of Object.keys(schedule))if(!KEYS.has(key))throw invalid(`unknown field ${key}`);const p={minute:integer(schedule.minute,"minute",0,59),hour:integer(schedule.hour,"hour",0,23),tz:checkTz(schedule.tz)};const s=schedule;if(s.daysOfWeek!==void 0&&s.dayOfMonth!==void 0)throw invalid("daysOfWeek and dayOfMonth cannot both appear");if(s.month!==void 0&&s.dayOfMonth===void 0)throw invalid("month needs dayOfMonth");if(s.daysOfWeek!==void 0){if(!Array.isArray(s.daysOfWeek)||s.daysOfWeek.length<1||s.daysOfWeek.length>7)throw invalid("daysOfWeek must list 1-7 days");for(const d of s.daysOfWeek)if(!ROUTINE_DAYS_OF_WEEK.includes(d))throw invalid(`unknown day ${JSON.stringify(d)}`);p.days=new Set(s.daysOfWeek);}if(s.dayOfMonth!==void 0){p.dayOfMonth=integer(s.dayOfMonth,"dayOfMonth",1,31);if(s.month!==void 0){p.month=integer(s.month,"month",1,12);if(p.month===2&&p.dayOfMonth===29){throw new ScheduleError("schedule_beyond_horizon","February 29 exists only in leap years; the next one can be up to 8 years away, beyond the 368-day horizon")}if(p.dayOfMonth>COMMON_YEAR_DAYS[p.month-1]){throw new ScheduleError("schedule_unreachable",`month ${p.month} has no day ${p.dayOfMonth}`)}}}return p}function checkRoutineSchedule(schedule){try{parse(schedule);return {ok:true}}catch(e){if(e instanceof ScheduleError)return {ok:false,code:e.code,message:e.message};throw e}}function localAsUtc(tz,instant){const p={};for(const part of formatter(tz).formatToParts(instant))if(part.type!=="literal")p[part.type]=Number(part.value);return Date.UTC(p.year,p.month-1,p.day,p.hour,p.minute,p.second)}var offsetAt=(tz,instant)=>localAsUtc(tz,instant)-Math.floor(instant/1e3)*1e3;function resolve(tz,local){const before=offsetAt(tz,local-DAY_MS);const offsets=new Set([before,offsetAt(tz,local),offsetAt(tz,local+DAY_MS)]);const valid=[...offsets].map(o=>local-o).filter(i=>offsetAt(tz,i)===local-i);return valid.length?{instant:Math.min(...valid),shifted:false}:{instant:local-before,shifted:true}}function matchesDate(p,y,m,d){if(p.days)return p.days.has(UTC_DAY_NAME[new Date(Date.UTC(y,m-1,d)).getUTCDay()]);if(p.dayOfMonth!==void 0)return d===p.dayOfMonth&&(p.month===void 0||m===p.month);return true}function nextRoutineOccurrence(schedule,now){if(!Number.isSafeInteger(now)||now<ROUTINE_MIN_NOW||now>=ROUTINE_MAX_NOW){throw new RangeError("now must be a safe integer epoch ms in [2000-01-01, 2200-01-01)")}let p;try{p=parse(schedule);}catch(error){if(error instanceof ScheduleError)throw new ByokCoreError("routine_schedule_invalid",error.message);throw error}const start=localAsUtc(p.tz,now);const day0=Date.UTC(new Date(start).getUTCFullYear(),new Date(start).getUTCMonth(),new Date(start).getUTCDate());let best=Infinity;for(let k=-1;k<=368;k+=1){const day=new Date(day0+k*DAY_MS);const y=day.getUTCFullYear(),m=day.getUTCMonth()+1,d=day.getUTCDate();if(!matchesDate(p,y,m,d))continue;const{instant,shifted}=resolve(p.tz,Date.UTC(y,m-1,d,p.hour,p.minute));if(instant<=now)continue;best=Math.min(best,instant);if(!shifted)break}return best-now<=ROUTINE_HORIZON_MS?best:null}
3168
13
 
3169
14
  export { AGENT_MEMORY_INTENT_DIGEST_VERSION, BOARD_STATUSES, BOARD_TRANSITIONS, ByokCoreError, CANONICAL_TIMESTAMP_PATTERN, CAPABILITY_DECLARATION_SCHEMA_ID, CAPABILITY_NAME_PATTERN, CONTENT_HASH_PATTERN, CORE_ERROR_CODES, CORE_NON_COMPOSITION_PORT_NAMES, CORE_PORT_INTERFACES, CORE_PORT_METHODS, CORE_STORE_NAMES, CapabilityDeclarationSchema, CollectExpiredFailure, CoreConflictError, DEVICE_ASSERTION_ALGORITHMS, DEVICE_ASSERTION_AUDIENCE_MAX_BYTES, DEVICE_ASSERTION_DEFAULT_TTL_MS, DEVICE_ASSERTION_DOMAIN_PREFIX, DEVICE_ASSERTION_MAX_TTL_MS, DEVICE_ASSERTION_SCHEMA_ID, DEVICE_ASSERTION_VERSION, DEVICE_PROOF_ALGORITHMS, DEVICE_PROOF_DOMAIN_PREFIX, DEVICE_PROOF_HEADER, DEVICE_PROOF_SCHEMA_ID, DEVICE_PROOF_VERSION, DeviceAssertionClaimsSchema, DeviceAssertionEnvelopeV1Schema, DeviceProofEnvelopeV1Schema, DeviceProofProtectedClaimsSchema, IN_MEMORY_CLOCK_EPOCH, InMemoryBoardStore, InMemoryDeviceAssertionReplayAuthority, InMemoryMailboxStore, InMemoryObjectStore, InMemoryPresenceStore, InMemoryQuotaStore, InMemorySkillPackStore, InMemoryStorageLedger, InMemoryTruthStore, MAILBOX_MESSAGE_STATES, NONCE_SIGNING_DOMAIN, OBJECT_KEY_PREFIX_PATTERN, OBJECT_STATES, OBJECT_STATE_TRANSITIONS, PRESENCE_LEVELS, PRINCIPAL_KINDS, PROVIDER_PROVISIONING_MAX_TTL_MS, PROVIDER_PROVISIONING_OPERATIONS, PROVIDER_PROVISIONING_REQUEST_VERSION, PROVIDER_PROVISIONING_SECRET_OPERATIONS, PROVIDER_SECRET_HPKE_INFO, PROVIDER_SECRET_HPKE_SUITE, PROVIDER_SECRET_MAX_BYTES, PROVIDER_SECRET_MAX_PADDED_BYTES, PROVIDER_SECRET_PAD_BLOCK_BYTES, ProviderProvisioningConfigV1Schema, ProviderProvisioningExpectedProfileSchema, ProviderProvisioningHeaderV1Schema, ProviderProvisioningIdentityRevisionSchema, ProviderProvisioningRequestV1Schema, ROUTINE_DAYS_OF_WEEK, ROUTINE_HORIZON_MS, ROUTINE_MAX_NOW, ROUTINE_MIN_NOW, SEALING_KEY_ID_LENGTH, SKILL_FRONTMATTER_FIELDS, SKILL_PACK_DESCRIPTION_MAX_LENGTH, SKILL_PACK_ENTRY_PATH, SKILL_PACK_FILE_MAX_BYTES, SKILL_PACK_FILE_PATH_MAX_LENGTH, SKILL_PACK_FILE_PATH_PATTERN, SKILL_PACK_FORBIDDEN_FIELDS, SKILL_PACK_MANIFEST_SCHEMA_ID, SKILL_PACK_MAX_BYTES, SKILL_PACK_MAX_FILES, SKILL_PACK_NAME_MAX_LENGTH, SKILL_PACK_NAME_PATTERN, SKILL_PACK_REJECTIONS, SKILL_PACK_VERSION_PATTERN, STORAGE_ERROR_CODES, STORAGE_ERROR_HTTP_STATUS, STORAGE_RESERVATION_STATES, STORAGE_WRITE_KINDS, STORAGE_WRITE_POSTURES, SealedProviderSecretV1Schema, SealingKeyClaimV1Schema, SealingKeyIdSchema, SealingPublicJwkSchema, SkillPackFileSchema, SkillPackManifestSchema, TASK_ASSERTION_AGENT_REF_MAX_BYTES, TASK_ASSERTION_DOMAIN_PREFIX, TASK_ASSERTION_SCHEMA_ID, TASK_ASSERTION_TOOLSET_ID_MAX_LENGTH, TASK_ASSERTION_VERSION, TENANT_ID_MAX_LENGTH, TENANT_KEY_SEPARATOR, TRUTH_RECORD_KINDS, TaskAssertionAgentRefSchema, TaskAssertionClaimsSchema, TaskAssertionEnvelopeV1Schema, agentMemoryIntentOperationDigest, assertCanonicalTimestamp, assertCapability, assertCollectExpiredInput, assertProviderProvisioningConfigDigest, authenticateDeviceAssertion, authenticateTaskAssertion, canonicalizeJson, canonicalizeJsonBytes, checkRoutineSchedule, checkSkillPackEntry, checkSkillPackFileContent, checkSkillPackManifest, contentHash, createInMemoryCoreCompositionWithClock, createInMemoryCoreStores, createMutableClock, deriveSealingKeyId, deviceAssertionCanonicalClaims, deviceAssertionCanonicalJson, deviceAssertionSigningInput, deviceProofCanonicalClaims, deviceProofCanonicalJson, deviceProofSigningInput, hasCapability, isCanonicalTimestamp, isContentHash, isControlPlanePrincipal, isCoreConflictError, isCoreError, isDevicePrincipal, isLegalBoardTransition, isLegalObjectTransition, isSkillPackPathSafe, isTenantId, nextRoutineOccurrence, nonceSigningBytes, objectKeyPrefix, openProviderProvisioningSecret, parseCapabilityDeclaration, parseDeviceAssertionEnvelope, parseDeviceProofEnvelope, parseProviderProvisioningRequest, parseSkillFrontmatter, parseSkillPackManifest, parseTaskAssertionEnvelope, principalTenant, providerProvisioningConfigDigest, providerProvisioningRequestDigest, providerSecretAadBytes, sealProviderProvisioningRequest, sealingKeyClaimCanonicalBytes, skillPackContentHashInput, taskAssertionCanonicalClaims, taskAssertionCanonicalJson, taskAssertionSigningInput, tenantId, tenantKey, tenantObjectKey, verifyDeviceAssertion, verifyTaskAssertion };
3170
15
  //# sourceMappingURL=index.js.map