@creatornader/agent-bridge 0.2.0 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (115) hide show
  1. package/CHANGELOG.md +86 -4
  2. package/README.md +567 -55
  3. package/ROADMAP.md +114 -0
  4. package/SECURITY.md +90 -0
  5. package/SKILL.md +68 -2
  6. package/bin/agent-bridge +2 -2
  7. package/clients/claude-desktop.json +3 -1
  8. package/dist/archive.d.ts +163 -0
  9. package/dist/archive.js +50 -0
  10. package/dist/archive.js.map +1 -0
  11. package/dist/bridge-domain-CBX3IVqC.d.ts +153 -0
  12. package/dist/{bridge-service-CfqMcMAn.d.ts → bridge-service-hX1ZQp4W.d.ts} +15 -5
  13. package/dist/bridge-store-DPVSCwfs.d.ts +102 -0
  14. package/dist/bridge.d.ts +76 -17
  15. package/dist/bridge.js +52 -24
  16. package/dist/chunk-2DFNOLMO.js +618 -0
  17. package/dist/chunk-2DFNOLMO.js.map +1 -0
  18. package/dist/{chunk-KVR4JBRF.js → chunk-2EVLG3YA.js} +2 -2
  19. package/dist/{chunk-KVR4JBRF.js.map → chunk-2EVLG3YA.js.map} +1 -1
  20. package/dist/chunk-45BZGB77.js +709 -0
  21. package/dist/chunk-45BZGB77.js.map +1 -0
  22. package/dist/chunk-4VB44KJU.js +230 -0
  23. package/dist/chunk-4VB44KJU.js.map +1 -0
  24. package/dist/chunk-5GMB3A77.js +1013 -0
  25. package/dist/chunk-5GMB3A77.js.map +1 -0
  26. package/dist/chunk-A6W4QALF.js +1258 -0
  27. package/dist/chunk-A6W4QALF.js.map +1 -0
  28. package/dist/chunk-BZLMF5O5.js +328 -0
  29. package/dist/chunk-BZLMF5O5.js.map +1 -0
  30. package/dist/{chunk-AXL2I7FH.js → chunk-CEXBMOI5.js} +240 -79
  31. package/dist/chunk-CEXBMOI5.js.map +1 -0
  32. package/dist/chunk-GADH244D.js +527 -0
  33. package/dist/chunk-GADH244D.js.map +1 -0
  34. package/dist/chunk-MBPZXTJA.js +2428 -0
  35. package/dist/chunk-MBPZXTJA.js.map +1 -0
  36. package/dist/chunk-NCO7RLBM.js +146 -0
  37. package/dist/chunk-NCO7RLBM.js.map +1 -0
  38. package/dist/chunk-Q6PPMXJC.js +1050 -0
  39. package/dist/chunk-Q6PPMXJC.js.map +1 -0
  40. package/dist/{chunk-4KAOBC5X.js → chunk-SPSMZOCJ.js} +190 -165
  41. package/dist/chunk-SPSMZOCJ.js.map +1 -0
  42. package/dist/chunk-UAHMP6GC.js +54 -0
  43. package/dist/chunk-UAHMP6GC.js.map +1 -0
  44. package/dist/{chunk-YD6YJATN.js → chunk-VOQIDTCJ.js} +4 -2
  45. package/dist/chunk-VOQIDTCJ.js.map +1 -0
  46. package/dist/chunk-WAI2IYIQ.js +554 -0
  47. package/dist/chunk-WAI2IYIQ.js.map +1 -0
  48. package/dist/cli.d.ts +2 -1
  49. package/dist/cli.js +3555 -214
  50. package/dist/cli.js.map +1 -1
  51. package/dist/client-config.d.ts +1 -1
  52. package/dist/client-config.js +1 -1
  53. package/dist/client-runtime.d.ts +10 -6
  54. package/dist/client-runtime.js +9 -6
  55. package/dist/contracts/generate.d.ts +2 -0
  56. package/dist/contracts/generate.js +247 -0
  57. package/dist/contracts/generate.js.map +1 -0
  58. package/dist/dr.d.ts +324 -0
  59. package/dist/dr.js +124 -0
  60. package/dist/dr.js.map +1 -0
  61. package/dist/gateway-CggQ8vTC.d.ts +78 -0
  62. package/dist/gateway-main.js +190 -8
  63. package/dist/gateway-main.js.map +1 -1
  64. package/dist/gateway.d.ts +4 -3
  65. package/dist/gateway.js +4 -3
  66. package/dist/index.js +11 -8
  67. package/dist/index.js.map +1 -1
  68. package/dist/postgres-bridge-store-Bfd3hRId.d.ts +49 -0
  69. package/dist/server.d.ts +3 -2
  70. package/dist/server.js +13 -8
  71. package/dist/sqlite.d.ts +94 -12
  72. package/dist/sqlite.js +7 -4
  73. package/docs/architecture-v2.md +394 -0
  74. package/docs/decisions/0001-protocol-layers-and-acknowledgment-semantics.md +85 -0
  75. package/docs/decisions/0002-canonical-operation-contract-registry.md +46 -0
  76. package/docs/decisions/0003-host-adapters-and-consumer-instance-keys.md +88 -0
  77. package/docs/ecosystem.md +86 -0
  78. package/docs/postmortems/2026-07-08-wrapper-source-drift.md +82 -0
  79. package/docs/troubleshooting.md +89 -0
  80. package/llms.txt +107 -6
  81. package/openapi/agent-bridge-v2.openapi.json +9610 -0
  82. package/package.json +24 -4
  83. package/schemas/agent-bridge-v2.capabilities.json +1829 -0
  84. package/schemas/agent-bridge-v2.mcp.json +2074 -0
  85. package/schemas/agent-bridge-v2.schema.json +4560 -0
  86. package/sql/migrations/008_message_projects.sql +36 -0
  87. package/sql/migrations/009_mailbox_query_indexes.sql +28 -0
  88. package/sql/migrations/010_publisher_delivery_policy.sql +210 -0
  89. package/sql/migrations/011_credential_security.sql +875 -0
  90. package/sql/migrations/012_request_authority.sql +177 -0
  91. package/sql/migrations/013_row_isolation.sql +495 -0
  92. package/sql/migrations/014_owner_control_plane.sql +1641 -0
  93. package/sql/migrations/015_portable_archives.sql +1813 -0
  94. package/sql/migrations/016_native_dr_fence.sql +1005 -0
  95. package/bin/CLAUDE.md +0 -11
  96. package/dist/bridge-domain-BcnBeHOn.d.ts +0 -97
  97. package/dist/bridge-store-BuGjxTn4.d.ts +0 -61
  98. package/dist/chunk-4KAOBC5X.js.map +0 -1
  99. package/dist/chunk-6MAZ6YLZ.js +0 -146
  100. package/dist/chunk-6MAZ6YLZ.js.map +0 -1
  101. package/dist/chunk-AXL2I7FH.js.map +0 -1
  102. package/dist/chunk-BV6W4AJK.js +0 -211
  103. package/dist/chunk-BV6W4AJK.js.map +0 -1
  104. package/dist/chunk-CKSKUBDQ.js +0 -153
  105. package/dist/chunk-CKSKUBDQ.js.map +0 -1
  106. package/dist/chunk-JOUBDRYX.js +0 -272
  107. package/dist/chunk-JOUBDRYX.js.map +0 -1
  108. package/dist/chunk-JTC5YURG.js +0 -49
  109. package/dist/chunk-JTC5YURG.js.map +0 -1
  110. package/dist/chunk-O26W5QHS.js +0 -409
  111. package/dist/chunk-O26W5QHS.js.map +0 -1
  112. package/dist/chunk-WEULPMUY.js +0 -988
  113. package/dist/chunk-WEULPMUY.js.map +0 -1
  114. package/dist/chunk-YD6YJATN.js.map +0 -1
  115. package/dist/gateway-BULozv3K.d.ts +0 -54
@@ -0,0 +1,1258 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ securePrivatePath,
4
+ verifyPrivatePathAccess
5
+ } from "./chunk-NCO7RLBM.js";
6
+ import {
7
+ validateMessageDraft,
8
+ validatePrincipal
9
+ } from "./chunk-BZLMF5O5.js";
10
+
11
+ // src/portable-archive-format.ts
12
+ import { createHash } from "crypto";
13
+ var PORTABLE_ARCHIVE_FORMAT = "agent-bridge.portable-archive";
14
+ var PORTABLE_ARCHIVE_VERSION = 1;
15
+ var PORTABLE_ARCHIVE_MAX_LINE_BYTES = 1024 * 1024;
16
+ var PORTABLE_ARCHIVE_MAX_BYTES = 256 * 1024 * 1024;
17
+ var PORTABLE_ARCHIVE_BATCH_BYTES = 4 * 1024 * 1024;
18
+ var PORTABLE_ARCHIVE_BATCH_ROWS = 1e3;
19
+ var UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/;
20
+ var REQUEST_UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
21
+ var CANONICAL_TIMESTAMP = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{6}Z$/;
22
+ var PortableArchiveError = class extends Error {
23
+ constructor(message3) {
24
+ super(message3);
25
+ this.code = "invalid_portable_archive";
26
+ }
27
+ };
28
+ function assertUnicode(value) {
29
+ if (value.includes("\0")) throw new PortableArchiveError("archive strings must not contain U+0000");
30
+ for (let index = 0; index < value.length; index += 1) {
31
+ const unit = value.charCodeAt(index);
32
+ if (unit >= 55296 && unit <= 56319) {
33
+ const next = value.charCodeAt(index + 1);
34
+ if (!(next >= 56320 && next <= 57343)) throw new PortableArchiveError("archive contains a lone surrogate");
35
+ index += 1;
36
+ } else if (unit >= 56320 && unit <= 57343) {
37
+ throw new PortableArchiveError("archive contains a lone surrogate");
38
+ }
39
+ }
40
+ }
41
+ function canonicalJson(value) {
42
+ if (value === null || typeof value === "boolean" || typeof value === "string") {
43
+ if (typeof value === "string") assertUnicode(value);
44
+ return JSON.stringify(value);
45
+ }
46
+ if (typeof value === "number") {
47
+ if (!Number.isFinite(value)) throw new PortableArchiveError("archive contains a non-finite number");
48
+ return JSON.stringify(value);
49
+ }
50
+ if (Array.isArray(value)) return `[${value.map(canonicalJson).join(",")}]`;
51
+ if (typeof value !== "object" || value === void 0) throw new PortableArchiveError("archive contains a non-JSON value");
52
+ const object = value;
53
+ return `{${Object.keys(object).sort().map((key) => {
54
+ assertUnicode(key);
55
+ return `${JSON.stringify(key)}:${canonicalJson(object[key])}`;
56
+ }).join(",")}}`;
57
+ }
58
+ function normalizeArchiveTimestamp(value, field) {
59
+ if (typeof value !== "string") throw new PortableArchiveError(`${field} must be a timestamp`);
60
+ const match = value.match(/^(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2})(?:\.(\d{1,6}))?(Z|[+-]\d{2}:\d{2})$/);
61
+ if (!match) throw new PortableArchiveError(`${field} must be RFC 3339 with an explicit offset`);
62
+ if (match[1].startsWith("0000-")) throw new PortableArchiveError(`${field} year must be between 0001 and 9999`);
63
+ const milliseconds = (match[2] ?? "").padEnd(3, "0").slice(0, 3);
64
+ const parsed = /* @__PURE__ */ new Date(`${match[1]}.${milliseconds}${match[3]}`);
65
+ if (Number.isNaN(parsed.getTime())) throw new PortableArchiveError(`${field} is invalid`);
66
+ const converted = parsed.toISOString();
67
+ if (!/^\d{4}-/.test(converted) || converted.startsWith("0000-")) throw new PortableArchiveError(`${field} conversion must remain between years 0001 and 9999`);
68
+ const canonicalPrefix = converted.slice(0, 19);
69
+ const micros = (match[2] ?? "").padEnd(6, "0");
70
+ return `${canonicalPrefix}.${micros}Z`;
71
+ }
72
+ function exactObject(value, keys, label) {
73
+ if (!value || typeof value !== "object" || Array.isArray(value)) throw new PortableArchiveError(`${label} must be an object`);
74
+ const object = value;
75
+ const actual = Object.keys(object).sort();
76
+ const expected = [...keys].sort();
77
+ if (actual.length !== expected.length || actual.some((key, index) => key !== expected[index])) {
78
+ throw new PortableArchiveError(`${label} has unexpected fields`);
79
+ }
80
+ return object;
81
+ }
82
+ var MESSAGE_KEYS = [
83
+ "atribReceiptId",
84
+ "causationId",
85
+ "content",
86
+ "contentType",
87
+ "correlationId",
88
+ "createdAt",
89
+ "data",
90
+ "deliveryPolicy",
91
+ "expiresAt",
92
+ "id",
93
+ "idempotencyKey",
94
+ "informedBy",
95
+ "metadata",
96
+ "priority",
97
+ "project",
98
+ "replyToId",
99
+ "source",
100
+ "targets",
101
+ "threadId",
102
+ "type"
103
+ ];
104
+ function validateArchiveWorkspace(value) {
105
+ if (typeof value !== "string" || value !== value.trim() || value.length < 1 || value.length > 128) {
106
+ throw new PortableArchiveError("workspace must be trimmed and between 1 and 128 characters");
107
+ }
108
+ assertUnicode(value);
109
+ return value;
110
+ }
111
+ function validatePortableArchiveRequestId(value) {
112
+ if (typeof value !== "string" || value !== value.trim() || !REQUEST_UUID.test(value)) {
113
+ throw new PortableArchiveError("archive exportRequestId must be a UUID");
114
+ }
115
+ return value.toLowerCase();
116
+ }
117
+ function string(value, field, nullable3 = false) {
118
+ if (nullable3 && value === null) return null;
119
+ if (typeof value !== "string") throw new PortableArchiveError(`${field} must be a string${nullable3 ? " or null" : ""}`);
120
+ assertUnicode(value);
121
+ return value;
122
+ }
123
+ function json(value, field) {
124
+ if (value === null) return null;
125
+ try {
126
+ canonicalJson(value);
127
+ } catch {
128
+ throw new PortableArchiveError(`${field} must be JSON`);
129
+ }
130
+ return value;
131
+ }
132
+ function stringArray(value, field) {
133
+ if (!Array.isArray(value) || value.some((entry) => typeof entry !== "string")) throw new PortableArchiveError(`${field} must be a string array`);
134
+ value.forEach((entry) => assertUnicode(entry));
135
+ return value;
136
+ }
137
+ function parsePortableArchiveMessage(value) {
138
+ const object = exactObject(value, MESSAGE_KEYS, "message");
139
+ const id = string(object.id, "message.id");
140
+ if (!UUID.test(id)) throw new PortableArchiveError("message.id must be a UUID");
141
+ const priority = string(object.priority, "message.priority");
142
+ if (!["info", "high", "urgent"].includes(priority)) throw new PortableArchiveError("message.priority is invalid");
143
+ const deliveryPolicy = object.deliveryPolicy;
144
+ if (!deliveryPolicy || typeof deliveryPolicy !== "object" || Array.isArray(deliveryPolicy)) throw new PortableArchiveError("message.deliveryPolicy must be an object");
145
+ const policy = deliveryPolicy;
146
+ if (policy.mode === "mailbox") {
147
+ exactObject(policy, ["mode"], "message.deliveryPolicy");
148
+ } else if (policy.mode === "leased") {
149
+ const keys = Object.keys(policy);
150
+ exactObject(policy, policy.notBefore === void 0 ? ["maxAttempts", "mode", "retryBaseDelayMs", "retryJitterRatio", "retryMaxDelayMs"] : ["maxAttempts", "mode", "notBefore", "retryBaseDelayMs", "retryJitterRatio", "retryMaxDelayMs"], "message.deliveryPolicy");
151
+ if (!Number.isSafeInteger(policy.maxAttempts) || Number(policy.maxAttempts) < 1 || Number(policy.maxAttempts) > 100 || !Number.isSafeInteger(policy.retryBaseDelayMs) || Number(policy.retryBaseDelayMs) < 1 || Number(policy.retryBaseDelayMs) > 36e5 || !Number.isSafeInteger(policy.retryMaxDelayMs) || Number(policy.retryMaxDelayMs) < Number(policy.retryBaseDelayMs) || Number(policy.retryMaxDelayMs) > 864e5 || typeof policy.retryJitterRatio !== "number" || !Number.isFinite(policy.retryJitterRatio) || Number(policy.retryJitterRatio) < 0 || Number(policy.retryJitterRatio) > 1 || keys.length < 5) {
152
+ throw new PortableArchiveError("message.deliveryPolicy is invalid");
153
+ }
154
+ } else throw new PortableArchiveError("message.deliveryPolicy.mode is invalid");
155
+ const createdAt = string(object.createdAt, "message.createdAt");
156
+ if (!CANONICAL_TIMESTAMP.test(createdAt) || normalizeArchiveTimestamp(createdAt, "message.createdAt") !== createdAt) throw new PortableArchiveError("message.createdAt is not canonical");
157
+ const expiresAt = string(object.expiresAt, "message.expiresAt", true);
158
+ if (expiresAt !== null && (!CANONICAL_TIMESTAMP.test(expiresAt) || normalizeArchiveTimestamp(expiresAt, "message.expiresAt") !== expiresAt)) throw new PortableArchiveError("message.expiresAt is not canonical");
159
+ const notBefore = policy.notBefore;
160
+ if (notBefore !== void 0 && (typeof notBefore !== "string" || !CANONICAL_TIMESTAMP.test(notBefore) || normalizeArchiveTimestamp(notBefore, "message.deliveryPolicy.notBefore") !== notBefore)) throw new PortableArchiveError("message.deliveryPolicy.notBefore is not canonical");
161
+ const targets = stringArray(object.targets, "message.targets");
162
+ if (policy.mode === "leased" && targets.length === 0) throw new PortableArchiveError("leased delivery requires at least one target");
163
+ if (typeof notBefore === "string" && expiresAt !== null && notBefore >= expiresAt) throw new PortableArchiveError("delivery notBefore must be before expiresAt");
164
+ const project = string(object.project, "message.project", true);
165
+ const source = string(object.source, "message.source");
166
+ const type = string(object.type, "message.type");
167
+ const content = string(object.content, "message.content");
168
+ const contentType = string(object.contentType, "message.contentType");
169
+ const data = json(object.data, "message.data");
170
+ const threadId = string(object.threadId, "message.threadId", true);
171
+ const replyToId = string(object.replyToId, "message.replyToId", true);
172
+ const correlationId = string(object.correlationId, "message.correlationId", true);
173
+ const causationId = string(object.causationId, "message.causationId", true);
174
+ const idempotencyKey = string(object.idempotencyKey, "message.idempotencyKey", true);
175
+ const atribReceiptId = string(object.atribReceiptId, "message.atribReceiptId", true);
176
+ const informedBy = stringArray(object.informedBy, "message.informedBy");
177
+ const metadata = json(object.metadata, "message.metadata");
178
+ const parsedPolicy = deliveryPolicy;
179
+ let normalized;
180
+ try {
181
+ const principal = validatePrincipal({ workspace: "archive", agent: source });
182
+ if (principal.agent !== source) throw new Error("message.source is not canonical");
183
+ normalized = validateMessageDraft({
184
+ id,
185
+ project: project ?? void 0,
186
+ type,
187
+ content,
188
+ contentType,
189
+ data,
190
+ targets,
191
+ threadId: threadId ?? void 0,
192
+ replyToId: replyToId ?? void 0,
193
+ correlationId: correlationId ?? void 0,
194
+ causationId: causationId ?? void 0,
195
+ priority,
196
+ expiresAt: expiresAt ?? void 0,
197
+ idempotencyKey: idempotencyKey ?? void 0,
198
+ atribReceiptId: atribReceiptId ?? void 0,
199
+ informedBy,
200
+ metadata,
201
+ deliveryPolicy: parsedPolicy
202
+ });
203
+ } catch (error) {
204
+ const reason = error instanceof Error ? error.message : "invalid message";
205
+ throw new PortableArchiveError(`message violates Agent Bridge domain constraints: ${reason}`);
206
+ }
207
+ const normalizedPolicy = normalized.deliveryPolicy.mode === "leased" && parsedPolicy.mode === "leased" && parsedPolicy.notBefore ? { ...normalized.deliveryPolicy, notBefore: parsedPolicy.notBefore } : normalized.deliveryPolicy;
208
+ const actual = {
209
+ project,
210
+ type,
211
+ content,
212
+ contentType,
213
+ data,
214
+ targets,
215
+ threadId,
216
+ replyToId,
217
+ correlationId,
218
+ causationId,
219
+ priority,
220
+ idempotencyKey,
221
+ atribReceiptId,
222
+ informedBy,
223
+ metadata,
224
+ deliveryPolicy: parsedPolicy
225
+ };
226
+ const expected = {
227
+ project: normalized.project ?? null,
228
+ type: normalized.type,
229
+ content: normalized.content,
230
+ contentType: normalized.contentType,
231
+ data: normalized.data ?? null,
232
+ targets: normalized.targets,
233
+ threadId: normalized.threadId ?? null,
234
+ replyToId: normalized.replyToId ?? null,
235
+ correlationId: normalized.correlationId ?? null,
236
+ causationId: normalized.causationId ?? null,
237
+ priority: normalized.priority,
238
+ idempotencyKey: normalized.idempotencyKey ?? null,
239
+ atribReceiptId: normalized.atribReceiptId ?? null,
240
+ informedBy: normalized.informedBy ?? [],
241
+ metadata: normalized.metadata ?? null,
242
+ deliveryPolicy: normalizedPolicy
243
+ };
244
+ if (normalized.id !== id || canonicalJson(actual) !== canonicalJson(expected)) {
245
+ throw new PortableArchiveError("message is not in canonical Agent Bridge domain form");
246
+ }
247
+ return {
248
+ id,
249
+ project,
250
+ source,
251
+ type,
252
+ content,
253
+ contentType,
254
+ data,
255
+ targets,
256
+ threadId,
257
+ replyToId,
258
+ correlationId,
259
+ causationId,
260
+ priority,
261
+ expiresAt,
262
+ idempotencyKey,
263
+ atribReceiptId,
264
+ informedBy,
265
+ metadata,
266
+ deliveryPolicy: parsedPolicy,
267
+ createdAt
268
+ };
269
+ }
270
+ function parsePortableArchiveReceipt(value) {
271
+ const object = exactObject(value, ["messageId", "principal", "readAt"], "receipt");
272
+ const messageId = string(object.messageId, "receipt.messageId");
273
+ if (!UUID.test(messageId)) throw new PortableArchiveError("receipt.messageId must be a UUID");
274
+ const readAt = string(object.readAt, "receipt.readAt");
275
+ if (!CANONICAL_TIMESTAMP.test(readAt) || normalizeArchiveTimestamp(readAt, "receipt.readAt") !== readAt) throw new PortableArchiveError("receipt.readAt is not canonical");
276
+ const principal = string(object.principal, "receipt.principal");
277
+ if (principal !== principal.trim() || principal.length < 1 || principal.length > 128) throw new PortableArchiveError("receipt.principal is invalid");
278
+ return { messageId, principal, readAt };
279
+ }
280
+ function encodePortableArchive(contents) {
281
+ const requestId = validatePortableArchiveRequestId(contents.exportRequestId);
282
+ const workspace = validateArchiveWorkspace(contents.workspace);
283
+ const messages = contents.messages.map(parsePortableArchiveMessage).sort((left, right) => left.createdAt < right.createdAt ? -1 : left.createdAt > right.createdAt ? 1 : left.id < right.id ? -1 : left.id > right.id ? 1 : 0);
284
+ if (new Set(messages.map((message3) => message3.id)).size !== messages.length) throw new PortableArchiveError("duplicate message id");
285
+ const messagesById = new Map(messages.map((message3) => [message3.id, message3]));
286
+ const byMessage = /* @__PURE__ */ new Map();
287
+ for (const receipt3 of contents.receipts.map(parsePortableArchiveReceipt)) {
288
+ const receiptMessage = messagesById.get(receipt3.messageId);
289
+ if (!receiptMessage) throw new PortableArchiveError("receipt references a missing message");
290
+ if (receiptMessage.targets.length > 0 && !receiptMessage.targets.includes(receipt3.principal)) throw new PortableArchiveError("receipt principal is not eligible for the message");
291
+ const list = byMessage.get(receipt3.messageId) ?? [];
292
+ if (list.some((entry) => entry.principal === receipt3.principal)) throw new PortableArchiveError("duplicate receipt");
293
+ list.push(receipt3);
294
+ byMessage.set(receipt3.messageId, list);
295
+ }
296
+ const lines = [canonicalJson({ exportRequestId: requestId, format: PORTABLE_ARCHIVE_FORMAT, kind: "header", version: PORTABLE_ARCHIVE_VERSION, workspace })];
297
+ for (const message3 of messages) {
298
+ lines.push(canonicalJson({ kind: "message", message: message3 }));
299
+ for (const receipt3 of (byMessage.get(message3.id) ?? []).sort((left, right) => left.principal < right.principal ? -1 : left.principal > right.principal ? 1 : 0)) lines.push(canonicalJson({ kind: "receipt", receipt: receipt3 }));
300
+ }
301
+ if (lines.some((line) => Buffer.byteLength(line) > PORTABLE_ARCHIVE_MAX_LINE_BYTES)) throw new PortableArchiveError("archive line exceeds 1 MiB");
302
+ const body = Buffer.from(`${lines.join("\n")}
303
+ `, "utf8");
304
+ const digest = `sha256:${createHash("sha256").update(body).digest("hex")}`;
305
+ const footer = canonicalJson({ digest, kind: "footer", messages: messages.length, receipts: contents.receipts.length });
306
+ const result = Buffer.concat([body, Buffer.from(`${footer}
307
+ `, "utf8")]);
308
+ if (result.length > PORTABLE_ARCHIVE_MAX_BYTES) throw new PortableArchiveError("archive exceeds maximum size");
309
+ return result;
310
+ }
311
+ function decodePortableArchive(input) {
312
+ const bytes = Buffer.from(input);
313
+ if (!bytes.length || bytes.length > PORTABLE_ARCHIVE_MAX_BYTES) throw new PortableArchiveError("archive size is invalid");
314
+ if (bytes[0] === 239 && bytes[1] === 187 && bytes[2] === 191) throw new PortableArchiveError("archive must not contain a BOM");
315
+ if (bytes[bytes.length - 1] !== 10 || bytes.includes(13) || bytes.includes(0)) throw new PortableArchiveError("archive must use UTF-8 and LF framing");
316
+ let text;
317
+ try {
318
+ text = new TextDecoder("utf-8", { fatal: true }).decode(bytes);
319
+ } catch {
320
+ throw new PortableArchiveError("archive is not valid UTF-8");
321
+ }
322
+ const lines = text.slice(0, -1).split("\n");
323
+ if (lines.length < 2 || lines.some((line) => !line || Buffer.byteLength(line) > PORTABLE_ARCHIVE_MAX_LINE_BYTES)) throw new PortableArchiveError("archive contains an invalid line");
324
+ const records = lines.map((line) => {
325
+ let value;
326
+ try {
327
+ value = JSON.parse(line);
328
+ } catch {
329
+ throw new PortableArchiveError("archive contains invalid JSON");
330
+ }
331
+ if (canonicalJson(value) !== line) throw new PortableArchiveError("archive contains noncanonical JSON");
332
+ return value;
333
+ });
334
+ const header = exactObject(records[0], ["exportRequestId", "format", "kind", "version", "workspace"], "header");
335
+ if (header.kind !== "header" || header.format !== PORTABLE_ARCHIVE_FORMAT || header.version !== PORTABLE_ARCHIVE_VERSION) throw new PortableArchiveError("unsupported archive header");
336
+ const requestId = validatePortableArchiveRequestId(header.exportRequestId);
337
+ if (requestId !== header.exportRequestId) throw new PortableArchiveError("archive header exportRequestId is not canonical");
338
+ const workspace = validateArchiveWorkspace(header.workspace);
339
+ const footer = exactObject(records[records.length - 1], ["digest", "kind", "messages", "receipts"], "footer");
340
+ if (footer.kind !== "footer" || typeof footer.digest !== "string" || !Number.isSafeInteger(footer.messages) || Number(footer.messages) < 0 || !Number.isSafeInteger(footer.receipts) || Number(footer.receipts) < 0) throw new PortableArchiveError("invalid archive footer");
341
+ const footerOffset = Buffer.byteLength(`${lines.slice(0, -1).join("\n")}
342
+ `);
343
+ const digest = `sha256:${createHash("sha256").update(bytes.subarray(0, footerOffset)).digest("hex")}`;
344
+ if (footer.digest !== digest) throw new PortableArchiveError("archive digest mismatch");
345
+ const messages = [];
346
+ const receipts = [];
347
+ const ids = /* @__PURE__ */ new Set();
348
+ const receiptKeys = /* @__PURE__ */ new Set();
349
+ let currentMessage;
350
+ let previousMessageKey = "";
351
+ let previousPrincipal = "";
352
+ for (const raw of records.slice(1, -1)) {
353
+ const record = raw;
354
+ if (record.kind === "message") {
355
+ exactObject(record, ["kind", "message"], "message record");
356
+ const message3 = parsePortableArchiveMessage(record.message);
357
+ const orderKey = `${message3.createdAt}\0${message3.id}`;
358
+ if (orderKey <= previousMessageKey || ids.has(message3.id)) throw new PortableArchiveError("messages are duplicated or out of order");
359
+ previousMessageKey = orderKey;
360
+ currentMessage = message3;
361
+ previousPrincipal = "";
362
+ ids.add(message3.id);
363
+ messages.push(message3);
364
+ } else if (record.kind === "receipt") {
365
+ exactObject(record, ["kind", "receipt"], "receipt record");
366
+ const receipt3 = parsePortableArchiveReceipt(record.receipt);
367
+ if (!currentMessage || receipt3.messageId !== currentMessage.id || receipt3.principal <= previousPrincipal) throw new PortableArchiveError("receipts are out of order or not adjacent to their message");
368
+ if (currentMessage.targets.length > 0 && !currentMessage.targets.includes(receipt3.principal)) throw new PortableArchiveError("receipt principal is not eligible for the message");
369
+ const key = `${receipt3.messageId}\0${receipt3.principal}`;
370
+ if (receiptKeys.has(key)) throw new PortableArchiveError("duplicate receipt");
371
+ previousPrincipal = receipt3.principal;
372
+ receiptKeys.add(key);
373
+ receipts.push(receipt3);
374
+ } else throw new PortableArchiveError("unknown archive record kind");
375
+ }
376
+ if (messages.length !== footer.messages || receipts.length !== footer.receipts) throw new PortableArchiveError("archive counts do not match footer");
377
+ return { exportRequestId: requestId, workspace, messages, receipts, digest };
378
+ }
379
+
380
+ // src/portable-archive-file.ts
381
+ import { createHash as createHash2 } from "crypto";
382
+ import {
383
+ closeSync,
384
+ constants,
385
+ fstatSync,
386
+ fsyncSync,
387
+ lstatSync,
388
+ openSync,
389
+ readSync
390
+ } from "fs";
391
+ import { dirname, resolve } from "path";
392
+ function exact(value, fields, label) {
393
+ if (!value || typeof value !== "object" || Array.isArray(value)) throw new PortableArchiveError(`${label} must be an object`);
394
+ const record = value;
395
+ const actual = Object.keys(record).sort();
396
+ const expected = [...fields].sort();
397
+ if (actual.length !== expected.length || actual.some((field, index) => field !== expected[index])) throw new PortableArchiveError(`${label} has unexpected fields`);
398
+ return record;
399
+ }
400
+ function parseLine(bytes, lineNumber) {
401
+ if (!bytes.length || bytes.length > PORTABLE_ARCHIVE_MAX_LINE_BYTES || bytes.includes(13) || bytes.includes(0)) {
402
+ throw new PortableArchiveError(`archive line ${lineNumber} is invalid`);
403
+ }
404
+ if (lineNumber === 1 && bytes[0] === 239 && bytes[1] === 187 && bytes[2] === 191) throw new PortableArchiveError("archive must not contain a BOM");
405
+ let line;
406
+ try {
407
+ line = new TextDecoder("utf-8", { fatal: true }).decode(bytes);
408
+ } catch {
409
+ throw new PortableArchiveError(`archive line ${lineNumber} is not valid UTF-8`);
410
+ }
411
+ let value;
412
+ try {
413
+ value = JSON.parse(line);
414
+ } catch {
415
+ throw new PortableArchiveError(`archive line ${lineNumber} contains invalid JSON`);
416
+ }
417
+ if (canonicalJson(value) !== line) throw new PortableArchiveError(`archive line ${lineNumber} contains noncanonical JSON`);
418
+ const outer = value;
419
+ let record;
420
+ if (outer.kind === "header") {
421
+ const header = exact(value, ["exportRequestId", "format", "kind", "version", "workspace"], "header");
422
+ if (header.format !== PORTABLE_ARCHIVE_FORMAT || header.version !== PORTABLE_ARCHIVE_VERSION) throw new PortableArchiveError("unsupported archive header");
423
+ const requestId = validatePortableArchiveRequestId(header.exportRequestId);
424
+ if (requestId !== header.exportRequestId) throw new PortableArchiveError("archive header exportRequestId is not canonical");
425
+ record = { kind: "header", requestId, workspace: validateArchiveWorkspace(header.workspace) };
426
+ } else if (outer.kind === "message") {
427
+ const wrapped = exact(value, ["kind", "message"], "message record");
428
+ record = { kind: "message", message: parsePortableArchiveMessage(wrapped.message) };
429
+ } else if (outer.kind === "receipt") {
430
+ const wrapped = exact(value, ["kind", "receipt"], "receipt record");
431
+ record = { kind: "receipt", receipt: parsePortableArchiveReceipt(wrapped.receipt) };
432
+ } else if (outer.kind === "footer") {
433
+ const footer = exact(value, ["digest", "kind", "messages", "receipts"], "footer");
434
+ if (typeof footer.digest !== "string" || !/^sha256:[a-f0-9]{64}$/.test(footer.digest) || !Number.isSafeInteger(footer.messages) || Number(footer.messages) < 0 || !Number.isSafeInteger(footer.receipts) || Number(footer.receipts) < 0) throw new PortableArchiveError("invalid archive footer");
435
+ record = { kind: "footer", digest: footer.digest, messages: Number(footer.messages), receipts: Number(footer.receipts) };
436
+ } else throw new PortableArchiveError(`archive line ${lineNumber} has an unknown record kind`);
437
+ return { raw: bytes, record };
438
+ }
439
+ function sameFile(left, right) {
440
+ return left.dev === right.dev && left.ino === right.ino && left.size === right.size && left.mtimeMs === right.mtimeMs && left.ctimeMs === right.ctimeMs;
441
+ }
442
+ var PortableArchiveFile = class {
443
+ constructor(path) {
444
+ this.closed = false;
445
+ const target = resolve(path);
446
+ this.path = target;
447
+ verifyPrivatePathAccess(dirname(target), "directory");
448
+ verifyPrivatePathAccess(target, "file");
449
+ const before = lstatSync(target);
450
+ const noFollow = typeof constants.O_NOFOLLOW === "number" ? constants.O_NOFOLLOW : 0;
451
+ this.descriptor = openSync(target, constants.O_RDWR | noFollow);
452
+ try {
453
+ this.identity = fstatSync(this.descriptor);
454
+ const after = lstatSync(target);
455
+ if (!this.identity.isFile() || before.isSymbolicLink() || !sameFile(before, this.identity) || !sameFile(after, this.identity)) {
456
+ throw new PortableArchiveError("archive path identity changed or is not a regular file");
457
+ }
458
+ if (this.identity.size < 1 || this.identity.size > PORTABLE_ARCHIVE_MAX_BYTES) throw new PortableArchiveError("archive size is invalid");
459
+ } catch (error) {
460
+ closeSync(this.descriptor);
461
+ throw error;
462
+ }
463
+ }
464
+ assertIdentity() {
465
+ if (this.closed) throw new PortableArchiveError("archive file is closed");
466
+ if (!sameFile(this.identity, fstatSync(this.descriptor))) throw new PortableArchiveError("archive file changed between passes");
467
+ }
468
+ *lines() {
469
+ this.assertIdentity();
470
+ const chunk = Buffer.allocUnsafe(64 * 1024);
471
+ let position = 0;
472
+ let pending = Buffer.alloc(0);
473
+ let lineNumber = 0;
474
+ let endedWithLf = false;
475
+ while (position < this.identity.size) {
476
+ const read = readSync(this.descriptor, chunk, 0, Math.min(chunk.length, this.identity.size - position), position);
477
+ if (read <= 0) throw new PortableArchiveError("archive ended before its recorded size");
478
+ position += read;
479
+ const input = pending.length ? Buffer.concat([pending, chunk.subarray(0, read)]) : Buffer.from(chunk.subarray(0, read));
480
+ let start = 0;
481
+ for (let index = 0; index < input.length; index += 1) {
482
+ if (input[index] !== 10) continue;
483
+ const line = input.subarray(start, index);
484
+ lineNumber += 1;
485
+ endedWithLf = true;
486
+ yield parseLine(line, lineNumber);
487
+ start = index + 1;
488
+ }
489
+ pending = Buffer.from(input.subarray(start));
490
+ if (pending.length > PORTABLE_ARCHIVE_MAX_LINE_BYTES) throw new PortableArchiveError("archive line exceeds 1 MiB");
491
+ if (pending.length) endedWithLf = false;
492
+ }
493
+ if (pending.length || !endedWithLf) throw new PortableArchiveError("archive must end with one LF");
494
+ this.assertIdentity();
495
+ }
496
+ *validatedRecords(expected) {
497
+ const digest = createHash2("sha256");
498
+ let requestId;
499
+ let workspace;
500
+ let footer;
501
+ let messages = 0;
502
+ let receipts = 0;
503
+ let previousMessage = "";
504
+ let currentMessage;
505
+ let previousPrincipal = "";
506
+ let lineNumber = 0;
507
+ for (const { raw, record } of this.lines()) {
508
+ lineNumber += 1;
509
+ if (footer) throw new PortableArchiveError("archive contains records after the footer");
510
+ if (record.kind !== "footer") digest.update(raw).update("\n");
511
+ if (lineNumber === 1) {
512
+ if (record.kind !== "header") throw new PortableArchiveError("archive must start with one header");
513
+ requestId = record.requestId;
514
+ workspace = record.workspace;
515
+ continue;
516
+ }
517
+ if (record.kind === "header") throw new PortableArchiveError("archive contains more than one header");
518
+ if (record.kind === "message") {
519
+ const order = `${record.message.createdAt}\0${record.message.id}`;
520
+ if (order <= previousMessage) throw new PortableArchiveError("messages are duplicated or out of order");
521
+ previousMessage = order;
522
+ currentMessage = record.message;
523
+ previousPrincipal = "";
524
+ messages += 1;
525
+ yield record;
526
+ } else if (record.kind === "receipt") {
527
+ if (!currentMessage || record.receipt.messageId !== currentMessage.id || record.receipt.principal <= previousPrincipal) throw new PortableArchiveError("receipts are out of order or not adjacent to their message");
528
+ if (currentMessage.targets.length > 0 && !currentMessage.targets.includes(record.receipt.principal)) throw new PortableArchiveError("receipt principal is not eligible for the message");
529
+ previousPrincipal = record.receipt.principal;
530
+ receipts += 1;
531
+ yield record;
532
+ } else footer = record;
533
+ }
534
+ if (!requestId || !workspace || !footer) throw new PortableArchiveError("archive footer is missing");
535
+ const calculated = `sha256:${digest.digest("hex")}`;
536
+ if (footer.digest !== calculated || footer.messages !== messages || footer.receipts !== receipts) throw new PortableArchiveError("archive footer verification failed");
537
+ const metadata = { exportRequestId: requestId, workspace, digest: calculated, messageCount: messages, receiptCount: receipts };
538
+ if (expected && (expected.exportRequestId !== metadata.exportRequestId || expected.workspace !== metadata.workspace || expected.digest !== metadata.digest || expected.messageCount !== metadata.messageCount || expected.receiptCount !== metadata.receiptCount)) {
539
+ throw new PortableArchiveError("archive changed between passes");
540
+ }
541
+ return metadata;
542
+ }
543
+ verify() {
544
+ const iterator = this.validatedRecords();
545
+ let next = iterator.next();
546
+ while (!next.done) next = iterator.next();
547
+ this.verified = next.value;
548
+ return next.value;
549
+ }
550
+ proveDurable() {
551
+ this.assertIdentity();
552
+ const before = lstatSync(this.path);
553
+ if (before.isSymbolicLink() || !sameFile(this.identity, before)) throw new PortableArchiveError("archive path changed before durability proof");
554
+ fsyncSync(this.descriptor);
555
+ const afterFileSync = lstatSync(this.path);
556
+ if (afterFileSync.isSymbolicLink() || !sameFile(this.identity, afterFileSync)) throw new PortableArchiveError("archive path changed during durability proof");
557
+ if (process.platform !== "win32") {
558
+ const directory = openSync(dirname(this.path), "r");
559
+ try {
560
+ fsyncSync(directory);
561
+ } finally {
562
+ closeSync(directory);
563
+ }
564
+ }
565
+ const afterDirectorySync = lstatSync(this.path);
566
+ if (afterDirectorySync.isSymbolicLink() || !sameFile(this.identity, afterDirectorySync)) throw new PortableArchiveError("archive path changed during durability proof");
567
+ }
568
+ async *messageBatches() {
569
+ const batch = [];
570
+ let batchBytes = 0;
571
+ const expected = this.verified ?? this.verify();
572
+ for (const record of this.validatedRecords(expected)) {
573
+ if (record.kind === "message") {
574
+ const bytes = Buffer.byteLength(canonicalJson({ kind: "message", message: record.message })) + 1;
575
+ if (batch.length && (batch.length === PORTABLE_ARCHIVE_BATCH_ROWS || batchBytes + bytes > PORTABLE_ARCHIVE_BATCH_BYTES)) {
576
+ yield batch.splice(0);
577
+ batchBytes = 0;
578
+ }
579
+ batch.push(record.message);
580
+ batchBytes += bytes;
581
+ }
582
+ }
583
+ if (batch.length) yield batch;
584
+ }
585
+ async *receiptBatches() {
586
+ const batch = [];
587
+ let batchBytes = 0;
588
+ const expected = this.verified ?? this.verify();
589
+ for (const record of this.validatedRecords(expected)) {
590
+ if (record.kind === "receipt") {
591
+ const bytes = Buffer.byteLength(canonicalJson({ kind: "receipt", receipt: record.receipt })) + 1;
592
+ if (batch.length && (batch.length === PORTABLE_ARCHIVE_BATCH_ROWS || batchBytes + bytes > PORTABLE_ARCHIVE_BATCH_BYTES)) {
593
+ yield batch.splice(0);
594
+ batchBytes = 0;
595
+ }
596
+ batch.push(record.receipt);
597
+ batchBytes += bytes;
598
+ }
599
+ }
600
+ if (batch.length) yield batch;
601
+ }
602
+ close() {
603
+ if (!this.closed) {
604
+ this.closed = true;
605
+ closeSync(this.descriptor);
606
+ }
607
+ }
608
+ };
609
+
610
+ // src/portable-archive.ts
611
+ import { createHash as createHash3 } from "crypto";
612
+ async function streamPortableArchive(session, workspaceValue, exportRequestIdValue, write) {
613
+ const workspace = validateArchiveWorkspace(workspaceValue);
614
+ const exportRequestId = validatePortableArchiveRequestId(exportRequestIdValue);
615
+ const hash = createHash3("sha256");
616
+ let messageCount = 0;
617
+ let receiptCount = 0;
618
+ let totalBytes = 0;
619
+ const line = async (value, includeInDigest = true) => {
620
+ const bytes = Buffer.from(`${canonicalJson(value)}
621
+ `, "utf8");
622
+ if (bytes.length - 1 > PORTABLE_ARCHIVE_MAX_LINE_BYTES) throw new PortableArchiveError("archive export line exceeds 1 MiB");
623
+ if (totalBytes + bytes.length > PORTABLE_ARCHIVE_MAX_BYTES) throw new PortableArchiveError("archive export exceeds maximum size");
624
+ if (includeInDigest) hash.update(bytes);
625
+ await write(bytes);
626
+ totalBytes += bytes.length;
627
+ };
628
+ await line({ exportRequestId, format: "agent-bridge.portable-archive", kind: "header", version: 1, workspace });
629
+ const messages = session.messages()[Symbol.asyncIterator]();
630
+ let nextMessage = await messages.next();
631
+ const receipts = session.receipts()[Symbol.asyncIterator]();
632
+ let nextReceipt = await receipts.next();
633
+ let previousMessage = "";
634
+ while (!nextMessage.done) {
635
+ const message3 = parsePortableArchiveMessage(nextMessage.value);
636
+ const order = `${message3.createdAt}\0${message3.id}`;
637
+ if (order <= previousMessage) throw new Error("archive export messages are duplicated or out of order");
638
+ previousMessage = order;
639
+ await line({ kind: "message", message: message3 });
640
+ messageCount += 1;
641
+ let previousPrincipal = "";
642
+ while (!nextReceipt.done && nextReceipt.value.messageId === message3.id) {
643
+ const receipt3 = parsePortableArchiveReceipt(nextReceipt.value);
644
+ if (receipt3.principal <= previousPrincipal) throw new Error("archive export receipts are duplicated or out of order");
645
+ if (message3.targets.length > 0 && !message3.targets.includes(receipt3.principal)) throw new Error("archive export receipt principal is not eligible");
646
+ previousPrincipal = receipt3.principal;
647
+ await line({ kind: "receipt", receipt: receipt3 });
648
+ receiptCount += 1;
649
+ nextReceipt = await receipts.next();
650
+ }
651
+ nextMessage = await messages.next();
652
+ }
653
+ if (!nextReceipt.done) throw new Error("archive export receipt references a missing message");
654
+ const digest = `sha256:${hash.digest("hex")}`;
655
+ await line({ digest, kind: "footer", messages: messageCount, receipts: receiptCount }, false);
656
+ return { exportRequestId, workspace, digest, messageCount, receiptCount };
657
+ }
658
+ async function exportPortableArchive(store, workspace, exportRequestIdValue) {
659
+ const exportRequestId = validatePortableArchiveRequestId(exportRequestIdValue);
660
+ const start = await store.beginExport(exportRequestId, workspace);
661
+ if (start.status === "completed") throw new PortableArchiveError("archive export request is already complete");
662
+ const session = start.session;
663
+ const chunks = [];
664
+ try {
665
+ await streamPortableArchive(session, workspace, exportRequestId, (bytes) => {
666
+ chunks.push(bytes);
667
+ });
668
+ await session.abandon("not_published");
669
+ return Buffer.concat(chunks);
670
+ } catch (error) {
671
+ await session.abandon("stream_failed").catch(() => void 0);
672
+ throw error;
673
+ } finally {
674
+ await session.close();
675
+ }
676
+ }
677
+ function inMemoryPasses(messages, receipts) {
678
+ return {
679
+ async *messageBatches() {
680
+ let batch = [];
681
+ let bytes = 0;
682
+ for (const message3 of messages) {
683
+ const size = Buffer.byteLength(canonicalJson({ kind: "message", message: message3 })) + 1;
684
+ if (batch.length && (batch.length === PORTABLE_ARCHIVE_BATCH_ROWS || bytes + size > PORTABLE_ARCHIVE_BATCH_BYTES)) {
685
+ yield batch;
686
+ batch = [];
687
+ bytes = 0;
688
+ }
689
+ batch.push(message3);
690
+ bytes += size;
691
+ }
692
+ if (batch.length) yield batch;
693
+ },
694
+ async *receiptBatches() {
695
+ let batch = [];
696
+ let bytes = 0;
697
+ for (const receipt3 of receipts) {
698
+ const size = Buffer.byteLength(canonicalJson({ kind: "receipt", receipt: receipt3 })) + 1;
699
+ if (batch.length && (batch.length === PORTABLE_ARCHIVE_BATCH_ROWS || bytes + size > PORTABLE_ARCHIVE_BATCH_BYTES)) {
700
+ yield batch;
701
+ batch = [];
702
+ bytes = 0;
703
+ }
704
+ batch.push(receipt3);
705
+ bytes += size;
706
+ }
707
+ if (batch.length) yield batch;
708
+ }
709
+ };
710
+ }
711
+ async function importPortableArchive(store, bytes, options) {
712
+ const archive = decodePortableArchive(bytes);
713
+ const requestId = validatePortableArchiveRequestId(options.requestId);
714
+ return store.importWorkspace(requestId, {
715
+ exportRequestId: archive.exportRequestId,
716
+ workspace: archive.workspace,
717
+ digest: archive.digest,
718
+ messageCount: archive.messages.length,
719
+ receiptCount: archive.receipts.length
720
+ }, inMemoryPasses(archive.messages, archive.receipts), { apply: options.apply === true });
721
+ }
722
+
723
+ // src/sqlite-portable-archive-store.ts
724
+ import { existsSync, lstatSync as lstatSync2 } from "fs";
725
+ import { dirname as dirname2 } from "path";
726
+ import { createRequire } from "module";
727
+ var require2 = createRequire(import.meta.url);
728
+ var parse = (value) => typeof value === "string" ? JSON.parse(value) : value;
729
+ var nullable = (value) => value == null ? null : String(value);
730
+ function message(row) {
731
+ const policy = parse(row.delivery_policy);
732
+ if (policy.mode === "leased" && policy.notBefore) policy.notBefore = normalizeArchiveTimestamp(policy.notBefore, "deliveryPolicy.notBefore");
733
+ return {
734
+ id: String(row.id),
735
+ project: nullable(row.project),
736
+ source: String(row.source),
737
+ type: String(row.type),
738
+ content: String(row.content),
739
+ contentType: String(row.content_type),
740
+ data: parse(row.data) ?? null,
741
+ targets: parse(row.targets) ?? [],
742
+ threadId: nullable(row.thread_id),
743
+ replyToId: nullable(row.reply_to_id),
744
+ correlationId: nullable(row.correlation_id),
745
+ causationId: nullable(row.causation_id),
746
+ priority: row.priority,
747
+ expiresAt: row.expires_at == null ? null : normalizeArchiveTimestamp(String(row.expires_at), "expiresAt"),
748
+ idempotencyKey: nullable(row.idempotency_key),
749
+ atribReceiptId: nullable(row.atrib_receipt_id),
750
+ informedBy: parse(row.informed_by) ?? [],
751
+ metadata: parse(row.metadata) ?? null,
752
+ deliveryPolicy: policy,
753
+ createdAt: normalizeArchiveTimestamp(String(row.created_at), "createdAt")
754
+ };
755
+ }
756
+ function receipt(row) {
757
+ return { messageId: String(row.message_id), principal: String(row.principal), readAt: normalizeArchiveTimestamp(String(row.read_at), "readAt") };
758
+ }
759
+ function canonicalBatchBytes(kind, batch) {
760
+ return batch.reduce((total, value) => total + Buffer.byteLength(canonicalJson({ kind, [kind]: value })) + 1, 0);
761
+ }
762
+ function assertPrivateDatabase(path) {
763
+ if (path === ":memory:") return void 0;
764
+ verifyPrivatePathAccess(dirname2(path), "directory");
765
+ securePrivatePath(path, "file");
766
+ const stat = lstatSync2(path);
767
+ if (!stat.isFile() || stat.isSymbolicLink()) throw new PortableArchiveError("local archive database must be a regular file");
768
+ if (typeof process.getuid === "function" && stat.uid !== process.getuid()) throw new PortableArchiveError("local archive database is not owned by the current user");
769
+ return stat;
770
+ }
771
+ var SQLitePortableArchiveStore = class {
772
+ constructor(path) {
773
+ this.path = path;
774
+ if (path !== ":memory:" && !existsSync(path)) throw new PortableArchiveError("local archive database does not exist");
775
+ const before = assertPrivateDatabase(path);
776
+ const { DatabaseSync } = require2("node:sqlite");
777
+ this.db = new DatabaseSync(path);
778
+ try {
779
+ if (before) {
780
+ const after = lstatSync2(path);
781
+ if (before.dev !== after.dev || before.ino !== after.ino || before.isSymbolicLink() || !after.isFile()) {
782
+ throw new PortableArchiveError("local archive database identity changed while opening");
783
+ }
784
+ verifyPrivatePathAccess(path, "file");
785
+ }
786
+ this.db.exec("PRAGMA foreign_keys=ON; PRAGMA busy_timeout=2000");
787
+ const tables = new Set(this.db.prepare("SELECT name FROM sqlite_master WHERE type='table'").all().map((row) => String(row.name)));
788
+ if (!tables.has("bridge_messages") || !tables.has("bridge_receipts")) throw new PortableArchiveError("database is not a canonical local Agent Bridge store");
789
+ if (tables.has("edge_outbox") || tables.has("edge_inbox")) throw new PortableArchiveError("edge databases cannot be used as portable archives");
790
+ } catch (error) {
791
+ try {
792
+ this.db.close();
793
+ } finally {
794
+ throw error;
795
+ }
796
+ }
797
+ }
798
+ async beginExport(requestIdValue, workspaceValue) {
799
+ validatePortableArchiveRequestId(requestIdValue);
800
+ const workspace = validateArchiveWorkspace(workspaceValue);
801
+ this.db.exec("BEGIN");
802
+ let ended = false;
803
+ const finish = (sql) => {
804
+ if (!ended) {
805
+ this.db.exec(sql);
806
+ ended = true;
807
+ }
808
+ };
809
+ const db = this.db;
810
+ const session = {
811
+ async *messages() {
812
+ for (const row of db.prepare("SELECT * FROM bridge_messages WHERE workspace=? ORDER BY created_at,id").iterate(workspace)) yield message(row);
813
+ },
814
+ async *receipts() {
815
+ for (const row of db.prepare(`SELECT receipt.message_id,receipt.principal,receipt.read_at
816
+ FROM bridge_receipts receipt JOIN bridge_messages message
817
+ ON message.workspace=receipt.workspace AND message.id=receipt.message_id
818
+ WHERE receipt.workspace=? ORDER BY message.created_at,message.id,receipt.principal`).iterate(workspace)) yield receipt(row);
819
+ },
820
+ async complete() {
821
+ finish("COMMIT");
822
+ },
823
+ async reconcile() {
824
+ finish("COMMIT");
825
+ },
826
+ async abandon() {
827
+ finish("ROLLBACK");
828
+ },
829
+ close() {
830
+ finish("ROLLBACK");
831
+ }
832
+ };
833
+ return { status: "active", session, replayed: false };
834
+ }
835
+ async importWorkspace(requestId, archive, passes, options) {
836
+ requestId = validatePortableArchiveRequestId(requestId);
837
+ validateArchiveWorkspace(archive.workspace);
838
+ this.db.exec("BEGIN IMMEDIATE");
839
+ let messagesCreated = 0;
840
+ let messagesReplayed = 0;
841
+ let receiptsCreated = 0;
842
+ let receiptsReplayed = 0;
843
+ try {
844
+ const selectMessage = this.db.prepare("SELECT * FROM bridge_messages WHERE workspace=? AND id=?");
845
+ const selectIdempotent = this.db.prepare("SELECT id FROM bridge_messages WHERE workspace=? AND source=? AND idempotency_key=?");
846
+ const insertMessage = this.db.prepare(`INSERT INTO bridge_messages (
847
+ id,workspace,project,source,type,content,content_type,data,targets,thread_id,reply_to_id,
848
+ correlation_id,causation_id,priority,expires_at,idempotency_key,atrib_receipt_id,informed_by,
849
+ metadata,delivery_policy,created_at
850
+ ) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)`);
851
+ let messageCount = 0;
852
+ for await (const batch of passes.messageBatches()) {
853
+ if (batch.length < 1 || batch.length > PORTABLE_ARCHIVE_BATCH_ROWS || canonicalBatchBytes("message", batch) > PORTABLE_ARCHIVE_BATCH_BYTES) throw new PortableArchiveError("archive message batch is invalid");
854
+ for (const value of batch) {
855
+ const archived = parsePortableArchiveMessage(value);
856
+ messageCount += 1;
857
+ const existingRow = selectMessage.get(archive.workspace, archived.id);
858
+ if (existingRow) {
859
+ if (canonicalJson(message(existingRow)) !== canonicalJson(archived)) throw new PortableArchiveError(`message conflict: ${archived.id}`);
860
+ messagesReplayed += 1;
861
+ continue;
862
+ }
863
+ if (archived.idempotencyKey) {
864
+ const idempotent = selectIdempotent.get(archive.workspace, archived.source, archived.idempotencyKey);
865
+ if (idempotent) throw new PortableArchiveError(`idempotency conflict: ${archived.idempotencyKey}`);
866
+ }
867
+ insertMessage.run(
868
+ archived.id,
869
+ archive.workspace,
870
+ archived.project,
871
+ archived.source,
872
+ archived.type,
873
+ archived.content,
874
+ archived.contentType,
875
+ archived.data === null ? null : JSON.stringify(archived.data),
876
+ JSON.stringify(archived.targets),
877
+ archived.threadId,
878
+ archived.replyToId,
879
+ archived.correlationId,
880
+ archived.causationId,
881
+ archived.priority,
882
+ archived.expiresAt,
883
+ archived.idempotencyKey,
884
+ archived.atribReceiptId,
885
+ archived.informedBy.length ? JSON.stringify(archived.informedBy) : null,
886
+ archived.metadata === null ? null : JSON.stringify(archived.metadata),
887
+ JSON.stringify(archived.deliveryPolicy),
888
+ archived.createdAt
889
+ );
890
+ messagesCreated += 1;
891
+ }
892
+ }
893
+ if (messageCount !== archive.messageCount) throw new PortableArchiveError("archive message count changed between passes");
894
+ const selectReceipt = this.db.prepare("SELECT read_at FROM bridge_receipts WHERE workspace=? AND message_id=? AND principal=?");
895
+ const selectReceiptMessage = this.db.prepare("SELECT targets FROM bridge_messages WHERE workspace=? AND id=?");
896
+ const insertReceipt = this.db.prepare("INSERT INTO bridge_receipts(workspace,message_id,principal,read_at) VALUES (?,?,?,?)");
897
+ let receiptCount = 0;
898
+ for await (const batch of passes.receiptBatches()) {
899
+ if (batch.length < 1 || batch.length > PORTABLE_ARCHIVE_BATCH_ROWS || canonicalBatchBytes("receipt", batch) > PORTABLE_ARCHIVE_BATCH_BYTES) throw new PortableArchiveError("archive receipt batch is invalid");
900
+ for (const value of batch) {
901
+ const archived = parsePortableArchiveReceipt(value);
902
+ receiptCount += 1;
903
+ const receiptMessage = selectReceiptMessage.get(archive.workspace, archived.messageId);
904
+ const targets = receiptMessage ? parse(receiptMessage.targets) : void 0;
905
+ if (!targets || targets.length > 0 && !targets.includes(archived.principal)) throw new PortableArchiveError(`receipt principal is not eligible: ${archived.messageId}/${archived.principal}`);
906
+ const existing = selectReceipt.get(archive.workspace, archived.messageId, archived.principal);
907
+ if (existing) {
908
+ if (normalizeArchiveTimestamp(String(existing.read_at), "readAt") !== archived.readAt) throw new PortableArchiveError(`receipt conflict: ${archived.messageId}/${archived.principal}`);
909
+ receiptsReplayed += 1;
910
+ } else {
911
+ insertReceipt.run(archive.workspace, archived.messageId, archived.principal, archived.readAt);
912
+ receiptsCreated += 1;
913
+ }
914
+ }
915
+ }
916
+ if (receiptCount !== archive.receiptCount) throw new PortableArchiveError("archive receipt count changed between passes");
917
+ this.db.exec(options.apply ? "COMMIT" : "ROLLBACK");
918
+ return { requestId, workspace: archive.workspace, digest: archive.digest, apply: options.apply, messages: { created: messagesCreated, replayed: messagesReplayed }, receipts: { created: receiptsCreated, replayed: receiptsReplayed } };
919
+ } catch (error) {
920
+ try {
921
+ this.db.exec("ROLLBACK");
922
+ } catch {
923
+ }
924
+ throw error;
925
+ }
926
+ }
927
+ close() {
928
+ this.db.close();
929
+ }
930
+ };
931
+
932
+ // src/postgres-portable-archive-store.ts
933
+ var parse2 = (value) => typeof value === "string" ? JSON.parse(value) : value;
934
+ var nullable2 = (value) => value == null ? null : String(value);
935
+ function message2(row) {
936
+ const deliveryPolicy = row.delivery_mode === "mailbox" ? { mode: "mailbox" } : {
937
+ mode: "leased",
938
+ maxAttempts: Number(row.delivery_max_attempts),
939
+ retryBaseDelayMs: Number(row.delivery_retry_base_delay_ms),
940
+ retryMaxDelayMs: Number(row.delivery_retry_max_delay_ms),
941
+ retryJitterRatio: Number(row.delivery_retry_jitter_ratio),
942
+ ...row.delivery_not_before ? { notBefore: normalizeArchiveTimestamp(String(row.delivery_not_before), "deliveryPolicy.notBefore") } : {}
943
+ };
944
+ return {
945
+ id: String(row.id),
946
+ project: nullable2(row.project),
947
+ source: String(row.source),
948
+ type: String(row.type),
949
+ content: String(row.content),
950
+ contentType: String(row.content_type),
951
+ data: parse2(row.data) ?? null,
952
+ targets: parse2(row.targets) ?? [],
953
+ threadId: nullable2(row.thread_id),
954
+ replyToId: nullable2(row.reply_to_id),
955
+ correlationId: nullable2(row.correlation_id),
956
+ causationId: nullable2(row.causation_id),
957
+ priority: row.priority,
958
+ expiresAt: row.expires_at ? normalizeArchiveTimestamp(String(row.expires_at), "expiresAt") : null,
959
+ idempotencyKey: nullable2(row.idempotency_key),
960
+ atribReceiptId: nullable2(row.atrib_receipt_id),
961
+ informedBy: parse2(row.informed_by) ?? [],
962
+ metadata: parse2(row.metadata) ?? null,
963
+ deliveryPolicy,
964
+ createdAt: normalizeArchiveTimestamp(String(row.created_at), "createdAt")
965
+ };
966
+ }
967
+ function receipt2(row) {
968
+ return {
969
+ messageId: String(row.message_id),
970
+ principal: String(row.principal),
971
+ readAt: normalizeArchiveTimestamp(String(row.read_at), "readAt")
972
+ };
973
+ }
974
+ var PostgresPortableArchiveStore = class {
975
+ constructor(db) {
976
+ this.db = db;
977
+ }
978
+ async beginExport(requestId, workspace) {
979
+ const normalizedRequestId = validatePortableArchiveRequestId(requestId);
980
+ const connection = this.db.connect ? await this.db.connect() : this.db;
981
+ let snapshotOpen = false;
982
+ let beginCommitted = false;
983
+ let beginReplayed = false;
984
+ let released = false;
985
+ let final = false;
986
+ let messagesRead = false;
987
+ let receiptsRead = false;
988
+ const release = () => {
989
+ if (!released && connection !== this.db && typeof connection.release === "function") connection.release();
990
+ released = true;
991
+ };
992
+ try {
993
+ await connection.query("BEGIN");
994
+ const begun = await connection.query(
995
+ "SELECT * FROM agent_bridge.archive_begin_operation($1,'export',$2,NULL,NULL,NULL)",
996
+ [normalizedRequestId, workspace]
997
+ );
998
+ const prior = begun.rows[0];
999
+ beginReplayed = Boolean(prior?.replayed);
1000
+ await connection.query("COMMIT");
1001
+ beginCommitted = true;
1002
+ if (prior?.completed) {
1003
+ release();
1004
+ return {
1005
+ status: "completed",
1006
+ metadata: {
1007
+ exportRequestId: normalizedRequestId,
1008
+ workspace,
1009
+ digest: `sha256:${String(prior.client_verified_digest)}`,
1010
+ messageCount: Number(prior.message_count),
1011
+ receiptCount: Number(prior.receipt_count),
1012
+ publishedAt: normalizeArchiveTimestamp(String(prior.published_at), "publishedAt")
1013
+ }
1014
+ };
1015
+ }
1016
+ if (beginReplayed) {
1017
+ const unavailable = async function* () {
1018
+ throw new Error("replayed archive export is reconciliation-only");
1019
+ };
1020
+ const reconciliationSession = {
1021
+ messages: unavailable,
1022
+ receipts: unavailable,
1023
+ complete: async () => {
1024
+ throw new Error("replayed archive export cannot be published again");
1025
+ },
1026
+ reconcile: async (result) => {
1027
+ if (final || result.exportRequestId !== normalizedRequestId || result.workspace !== workspace) {
1028
+ throw new Error("archive export session cannot be reconciled");
1029
+ }
1030
+ await connection.query(
1031
+ "SELECT * FROM agent_bridge.archive_reconcile_export($1,$2,$3,$4,$5,$6::timestamptz)",
1032
+ [normalizedRequestId, workspace, result.digest.slice("sha256:".length), result.messageCount, result.receiptCount, result.publishedAt]
1033
+ );
1034
+ final = true;
1035
+ },
1036
+ abandon: async (reason) => {
1037
+ if (final) return;
1038
+ await connection.query(
1039
+ "SELECT * FROM agent_bridge.archive_abandon_operation($1,$2,$3)",
1040
+ [normalizedRequestId, workspace, reason]
1041
+ );
1042
+ final = true;
1043
+ },
1044
+ close: release
1045
+ };
1046
+ return { status: "active", session: reconciliationSession, replayed: true };
1047
+ }
1048
+ await connection.query("BEGIN ISOLATION LEVEL REPEATABLE READ");
1049
+ snapshotOpen = true;
1050
+ await connection.query(
1051
+ "SELECT agent_bridge.archive_authorize_transaction($1,'export',$2,NULL)",
1052
+ [normalizedRequestId, workspace]
1053
+ );
1054
+ } catch (error) {
1055
+ await connection.query("ROLLBACK").catch(() => void 0);
1056
+ let abandonmentError;
1057
+ if (beginCommitted) {
1058
+ try {
1059
+ await connection.query(
1060
+ "SELECT * FROM agent_bridge.archive_abandon_operation($1,$2,'snapshot_failed')",
1061
+ [normalizedRequestId, workspace]
1062
+ );
1063
+ } catch (failure) {
1064
+ abandonmentError = failure;
1065
+ }
1066
+ }
1067
+ release();
1068
+ if (abandonmentError) {
1069
+ const combined = new Error("archive snapshot and abandonment both failed");
1070
+ combined.errors = [error, abandonmentError];
1071
+ throw combined;
1072
+ }
1073
+ throw error;
1074
+ }
1075
+ const endSnapshot = async (commit) => {
1076
+ if (!snapshotOpen) return;
1077
+ if (commit) {
1078
+ await connection.query(
1079
+ "SELECT agent_bridge.archive_close_transaction_authorization($1)",
1080
+ [normalizedRequestId]
1081
+ );
1082
+ await connection.query("COMMIT");
1083
+ } else {
1084
+ await connection.query("ROLLBACK");
1085
+ }
1086
+ snapshotOpen = false;
1087
+ };
1088
+ const session = {
1089
+ messages: async function* () {
1090
+ if (final || messagesRead) throw new Error("archive export message pass is unavailable");
1091
+ messagesRead = true;
1092
+ let createdAt = null;
1093
+ let id = null;
1094
+ for (; ; ) {
1095
+ const result = await connection.query(
1096
+ "SELECT * FROM agent_bridge.archive_export_messages($1,$2,$3::timestamptz,$4::uuid,200)",
1097
+ [normalizedRequestId, workspace, createdAt, id]
1098
+ );
1099
+ for (const row of result.rows) yield message2(row);
1100
+ if (result.rows.length === 0) return;
1101
+ const last = result.rows[result.rows.length - 1];
1102
+ createdAt = String(last.created_at);
1103
+ id = String(last.id);
1104
+ }
1105
+ },
1106
+ receipts: async function* () {
1107
+ if (final || receiptsRead) throw new Error("archive export receipt pass is unavailable");
1108
+ receiptsRead = true;
1109
+ let createdAt = null;
1110
+ let messageId = null;
1111
+ let principal = null;
1112
+ for (; ; ) {
1113
+ const result = await connection.query(
1114
+ "SELECT * FROM agent_bridge.archive_export_receipts($1,$2,$3::timestamptz,$4::uuid,$5,500)",
1115
+ [normalizedRequestId, workspace, createdAt, messageId, principal]
1116
+ );
1117
+ for (const row of result.rows) yield receipt2(row);
1118
+ if (result.rows.length === 0) return;
1119
+ const last = result.rows[result.rows.length - 1];
1120
+ createdAt = String(last.message_created_at);
1121
+ messageId = String(last.message_id);
1122
+ principal = String(last.principal);
1123
+ }
1124
+ },
1125
+ complete: async (result) => {
1126
+ if (final || result.exportRequestId !== normalizedRequestId || result.workspace !== workspace || !messagesRead || !receiptsRead) {
1127
+ throw new Error("archive export session cannot be completed");
1128
+ }
1129
+ await connection.query(
1130
+ "SELECT * FROM agent_bridge.archive_complete_export($1,$2,$3,$4,$5,$6::timestamptz,$7)",
1131
+ [normalizedRequestId, workspace, result.digest.slice("sha256:".length), result.messageCount, result.receiptCount, result.publishedAt, "published"]
1132
+ );
1133
+ await endSnapshot(true);
1134
+ final = true;
1135
+ },
1136
+ reconcile: async () => {
1137
+ throw new Error("fresh archive export cannot be reconciled");
1138
+ },
1139
+ abandon: async (reason) => {
1140
+ if (final) return;
1141
+ await endSnapshot(false);
1142
+ await connection.query(
1143
+ "SELECT * FROM agent_bridge.archive_abandon_operation($1,$2,$3)",
1144
+ [normalizedRequestId, workspace, reason]
1145
+ );
1146
+ final = true;
1147
+ },
1148
+ close: async () => {
1149
+ if (snapshotOpen) {
1150
+ await connection.query("ROLLBACK").catch(() => void 0);
1151
+ snapshotOpen = false;
1152
+ }
1153
+ release();
1154
+ }
1155
+ };
1156
+ return { status: "active", session, replayed: beginReplayed };
1157
+ }
1158
+ async importWorkspace(requestId, metadata, passes, options) {
1159
+ const normalizedRequestId = validatePortableArchiveRequestId(requestId);
1160
+ const connection = this.db.connect ? await this.db.connect() : this.db;
1161
+ try {
1162
+ await connection.query("BEGIN");
1163
+ const digest = metadata.digest.slice("sha256:".length);
1164
+ const begun = await connection.query(
1165
+ "SELECT * FROM agent_bridge.archive_begin_operation($1,'import',$2,$3,$4,$5)",
1166
+ [normalizedRequestId, metadata.workspace, digest, metadata.messageCount, metadata.receiptCount]
1167
+ );
1168
+ const prior = begun.rows[0];
1169
+ if (prior?.completed) {
1170
+ await connection.query("COMMIT");
1171
+ return {
1172
+ requestId: normalizedRequestId,
1173
+ workspace: metadata.workspace,
1174
+ digest: metadata.digest,
1175
+ apply: Boolean(prior.apply),
1176
+ messages: {
1177
+ created: Number(prior.message_inserted_count),
1178
+ replayed: Number(prior.message_count) - Number(prior.message_inserted_count)
1179
+ },
1180
+ receipts: {
1181
+ created: Number(prior.receipt_inserted_count),
1182
+ replayed: Number(prior.receipt_count) - Number(prior.receipt_inserted_count)
1183
+ }
1184
+ };
1185
+ }
1186
+ await connection.query(
1187
+ "SELECT agent_bridge.archive_authorize_transaction($1,'import',$2,$3)",
1188
+ [normalizedRequestId, metadata.workspace, digest]
1189
+ );
1190
+ let messageOrdinal = 0;
1191
+ for await (const batch of passes.messageBatches()) {
1192
+ await connection.query(
1193
+ "SELECT * FROM agent_bridge.archive_import_messages($1,$2,$3,$4,$5::jsonb)",
1194
+ [normalizedRequestId, metadata.workspace, digest, messageOrdinal, JSON.stringify(batch)]
1195
+ );
1196
+ messageOrdinal += 1;
1197
+ }
1198
+ let receiptOrdinal = 0;
1199
+ for await (const batch of passes.receiptBatches()) {
1200
+ await connection.query(
1201
+ "SELECT * FROM agent_bridge.archive_import_receipts($1,$2,$3,$4,$5::jsonb)",
1202
+ [normalizedRequestId, metadata.workspace, digest, receiptOrdinal, JSON.stringify(batch)]
1203
+ );
1204
+ receiptOrdinal += 1;
1205
+ }
1206
+ const completed = await connection.query(
1207
+ "SELECT * FROM agent_bridge.archive_complete_import($1,$2,$3)",
1208
+ [normalizedRequestId, metadata.workspace, options.apply]
1209
+ );
1210
+ const result = completed.rows[0];
1211
+ await connection.query(options.apply ? "COMMIT" : "ROLLBACK");
1212
+ return {
1213
+ requestId: normalizedRequestId,
1214
+ workspace: metadata.workspace,
1215
+ digest: metadata.digest,
1216
+ apply: options.apply,
1217
+ messages: {
1218
+ created: Number(result.message_inserted_count),
1219
+ replayed: Number(result.message_count) - Number(result.message_inserted_count)
1220
+ },
1221
+ receipts: {
1222
+ created: Number(result.receipt_inserted_count),
1223
+ replayed: Number(result.receipt_count) - Number(result.receipt_inserted_count)
1224
+ }
1225
+ };
1226
+ } catch (error) {
1227
+ await connection.query("ROLLBACK").catch(() => void 0);
1228
+ throw error;
1229
+ } finally {
1230
+ if (connection !== this.db && typeof connection.release === "function") connection.release();
1231
+ }
1232
+ }
1233
+ };
1234
+
1235
+ export {
1236
+ PORTABLE_ARCHIVE_FORMAT,
1237
+ PORTABLE_ARCHIVE_VERSION,
1238
+ PORTABLE_ARCHIVE_MAX_LINE_BYTES,
1239
+ PORTABLE_ARCHIVE_MAX_BYTES,
1240
+ PORTABLE_ARCHIVE_BATCH_BYTES,
1241
+ PORTABLE_ARCHIVE_BATCH_ROWS,
1242
+ PortableArchiveError,
1243
+ canonicalJson,
1244
+ normalizeArchiveTimestamp,
1245
+ validateArchiveWorkspace,
1246
+ validatePortableArchiveRequestId,
1247
+ parsePortableArchiveMessage,
1248
+ parsePortableArchiveReceipt,
1249
+ encodePortableArchive,
1250
+ decodePortableArchive,
1251
+ PortableArchiveFile,
1252
+ streamPortableArchive,
1253
+ exportPortableArchive,
1254
+ importPortableArchive,
1255
+ SQLitePortableArchiveStore,
1256
+ PostgresPortableArchiveStore
1257
+ };
1258
+ //# sourceMappingURL=chunk-A6W4QALF.js.map