@fortemi/core 2026.9.0 → 2026.9.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -2072,6 +2072,54 @@ var migration0023 = {
2072
2072
  `
2073
2073
  };
2074
2074
 
2075
+ // src/migrations/0024_source_upsert_contract.ts
2076
+ var migration0024 = {
2077
+ version: 24,
2078
+ name: "0024_source_upsert_contract",
2079
+ sql: `
2080
+ ALTER TABLE source_import_run ADD COLUMN IF NOT EXISTS external_run_id TEXT;
2081
+ ALTER TABLE source_import_run ADD COLUMN IF NOT EXISTS source_id TEXT;
2082
+ ALTER TABLE source_import_run ADD COLUMN IF NOT EXISTS source_schema_version TEXT;
2083
+ ALTER TABLE source_import_run ADD COLUMN IF NOT EXISTS workspace_id TEXT;
2084
+ UPDATE source_import_run SET external_run_id = id WHERE external_run_id IS NULL;
2085
+ UPDATE source_import_run SET source_schema_version = 'legacy' WHERE source_schema_version IS NULL;
2086
+ ALTER TABLE source_import_run ALTER COLUMN external_run_id SET NOT NULL;
2087
+ ALTER TABLE source_import_run ALTER COLUMN source_schema_version SET NOT NULL;
2088
+ ALTER TABLE source_import_run ADD CONSTRAINT source_import_run_contract_lengths
2089
+ CHECK (
2090
+ length(external_run_id) BETWEEN 1 AND 200
2091
+ AND (source_id IS NULL OR length(source_id) BETWEEN 1 AND 500)
2092
+ AND length(source_schema_version) BETWEEN 1 AND 100
2093
+ AND (workspace_id IS NULL OR length(workspace_id) BETWEEN 1 AND 500)
2094
+ );
2095
+ CREATE UNIQUE INDEX IF NOT EXISTS idx_source_import_run_scope
2096
+ ON source_import_run(tenant_id, COALESCE(archive_id, ''), namespace, external_run_id);
2097
+
2098
+ ALTER TABLE source_identity ADD COLUMN IF NOT EXISTS source_id TEXT;
2099
+ ALTER TABLE source_identity ADD CONSTRAINT source_identity_source_id_length
2100
+ CHECK (source_id IS NULL OR length(source_id) BETWEEN 1 AND 500);
2101
+
2102
+ CREATE TABLE IF NOT EXISTS source_import_batch (
2103
+ id TEXT PRIMARY KEY,
2104
+ tenant_id TEXT NOT NULL DEFAULT 'default',
2105
+ archive_id TEXT,
2106
+ namespace TEXT NOT NULL,
2107
+ batch_id TEXT NOT NULL,
2108
+ request_digest TEXT NOT NULL,
2109
+ import_run_id TEXT NOT NULL,
2110
+ outcome TEXT NOT NULL,
2111
+ checkpoint JSONB NOT NULL DEFAULT '{}',
2112
+ receipt JSONB NOT NULL DEFAULT '{}',
2113
+ created_at TIMESTAMPTZ NOT NULL DEFAULT now()
2114
+ );
2115
+
2116
+ CREATE UNIQUE INDEX IF NOT EXISTS idx_source_import_batch_scope
2117
+ ON source_import_batch(tenant_id, COALESCE(archive_id, ''), namespace, batch_id);
2118
+ CREATE INDEX IF NOT EXISTS idx_source_import_batch_run
2119
+ ON source_import_batch(import_run_id);
2120
+ `
2121
+ };
2122
+
2075
2123
  // src/migrations/index.ts
2076
2124
  var allMigrations = [
2077
2125
  migration0001,
@@ -2096,7 +2144,8 @@ var allMigrations = [
2096
2144
  migration0020,
2097
2145
  migration0021,
2098
2146
  migration0022,
2099
- migration0023
2147
+ migration0023,
2148
+ migration0024
2100
2149
  ];
2101
2150
 
2102
2151
  // src/data-archive.ts
@@ -5461,52 +5510,79 @@ function createLazyBlobStore(archiveName, options) {
5461
5510
  }
5462
5511
 
5463
5512
  // src/repositories/source-upsert-repository.ts
5464
- var DEFAULT_MAX_ITEMS = 500;
5513
+ var SOURCE_UPSERT_CONTRACT_VERSION = "1.0.0";
5514
+ var SOURCE_UPSERT_MAX_ITEMS = 500;
5465
5515
  function assertSource(input) {
5466
- if (!input.namespace || input.namespace.length > 128) throw new Error("Source namespace is required and must be <= 128 characters");
5467
- if (!input.external_id || input.external_id.length > 1024) throw new Error("Source external_id is required and must be <= 1024 characters");
5468
- if (!input.source_schema_version || input.source_schema_version.length > 64) {
5469
- throw new Error("Source schema version is required and must be <= 64 characters");
5470
- }
5471
- if (!input.import_run_id || input.import_run_id.length > 128) throw new Error("Source import_run_id is required and must be <= 128 characters");
5516
+ if (!input.tenant_id || input.tenant_id.length > 200) throw new Error("invalid_batch_metadata");
5517
+ if (!input.namespace || input.namespace.length > 200) throw new Error("invalid_batch_metadata");
5518
+ if (!input.external_id || input.external_id.length > 1e3) throw new Error("invalid_item");
5519
+ if (!input.source_schema_version || input.source_schema_version.length > 100) throw new Error("invalid_batch_metadata");
5520
+ if (!input.import_run_id || input.import_run_id.length > 200) throw new Error("invalid_batch_metadata");
5472
5521
  }
5473
- function sourceHash(source) {
5522
+ function sourceIdentityHash(source) {
5474
5523
  return computeHash(new TextEncoder().encode([
5475
5524
  source.tenant_id ?? "default",
5476
- source.archive_id ?? "",
5525
+ source.archive_id ?? "public",
5477
5526
  source.namespace,
5478
- source.external_id
5527
+ source.external_id,
5528
+ ""
5479
5529
  ].join("\0")));
5480
5530
  }
5481
- function contentDigest(content) {
5531
+ function sourceContentDigest(content) {
5482
5532
  return computeHash(new TextEncoder().encode(content));
5483
5533
  }
5534
+ function sourceRequestDigest(items, options) {
5535
+ return computeHash(new TextEncoder().encode(JSON.stringify({
5536
+ items: items.map((item) => ({
5537
+ source: item.source,
5538
+ title: item.title ?? null,
5539
+ content: item.content,
5540
+ content_digest: item.content_digest ?? null,
5541
+ format: item.format ?? "markdown",
5542
+ visibility: item.visibility ?? "private",
5543
+ metadata: item.metadata ?? null,
5544
+ policy: item.policy ?? options.policy ?? "version"
5545
+ })),
5546
+ checkpoint: options.checkpoint ?? null,
5547
+ dry_run: options.dryRun === true
5548
+ })));
5549
+ }
5550
+ function deriveSourceBatchId(requestDigest) {
5551
+ return `derived-${requestDigest.slice("sha256:".length, "sha256:".length + 32)}`;
5552
+ }
5553
+ function sourceRunRecordId(source) {
5554
+ return computeHash(new TextEncoder().encode([
5555
+ source.tenant_id ?? "default",
5556
+ source.archive_id ?? "public",
5557
+ source.namespace,
5558
+ source.import_run_id,
5559
+ ""
5560
+ ].join("\0")));
5561
+ }
5562
+ function parseReceipt(value) {
5563
+ const parsed = typeof value === "string" ? JSON.parse(value) : value;
5564
+ if (!parsed || typeof parsed !== "object") return null;
5565
+ return parsed;
5566
+ }
5567
+ function duplicateReceipt(receipt) {
5568
+ const items = receipt.items.map((item) => ({ ...item, outcome: "unchanged", reason: void 0, reason_code: void 0 }));
5569
+ return finish(receipt.import_run_id, receipt.batch_id, false, "duplicate", items, receipt.checkpoint);
5570
+ }
5484
5571
  async function insertNote(tx, input, noteId, digest2) {
5485
5572
  const originalId = generateId();
5486
5573
  if (input.source.archive_id) {
5487
- await tx.query(
5488
- `INSERT INTO archive (id, name)
5489
- VALUES ($1, $2)
5490
- ON CONFLICT (id) DO NOTHING`,
5491
- [input.source.archive_id, input.source.archive_id]
5492
- );
5574
+ await tx.query("INSERT INTO archive (id, name) VALUES ($1, $2) ON CONFLICT (id) DO NOTHING", [input.source.archive_id, input.source.archive_id]);
5493
5575
  }
5494
5576
  await tx.query(
5495
5577
  `INSERT INTO note (id, archive_id, title, format, source, visibility)
5496
5578
  VALUES ($1, $2, $3, $4, $5, $6)`,
5497
- [
5498
- noteId,
5499
- input.source.archive_id ?? null,
5500
- input.title ?? null,
5501
- input.format ?? "markdown",
5502
- `source:${input.source.namespace}`,
5503
- input.visibility ?? "private"
5504
- ]
5579
+ [noteId, input.source.archive_id ?? null, input.title ?? null, input.format ?? "markdown", `source:${input.source.namespace}`, input.visibility ?? "private"]
5505
5580
  );
5581
+ await tx.query("INSERT INTO note_original (id, note_id, content, content_hash) VALUES ($1, $2, $3, $4)", [originalId, noteId, input.content, digest2]);
5506
5582
  await tx.query(
5507
- `INSERT INTO note_original (id, note_id, content, content_hash)
5508
- VALUES ($1, $2, $3, $4)`,
5509
- [originalId, noteId, input.content, digest2]
5583
+ `INSERT INTO note_revision (id, note_id, revision_number, type, content, ai_metadata)
5584
+ VALUES ($1, $2, 1, 'source-import', $3, $4::jsonb)`,
5585
+ [generateId(), noteId, input.content, JSON.stringify(input.metadata ?? null)]
5510
5586
  );
5511
5587
  await tx.query(
5512
5588
  `INSERT INTO note_revised_current (note_id, content, ai_metadata)
@@ -5514,55 +5590,26 @@ async function insertNote(tx, input, noteId, digest2) {
5514
5590
  [noteId, input.content, JSON.stringify(input.metadata ?? null)]
5515
5591
  );
5516
5592
  }
5517
- async function updateNote(tx, input, noteId, outcome) {
5593
+ async function updateNote(tx, input, noteId, outcome, digest2) {
5518
5594
  if (input.source.archive_id) {
5519
- await tx.query(
5520
- `INSERT INTO archive (id, name)
5521
- VALUES ($1, $2)
5522
- ON CONFLICT (id) DO NOTHING`,
5523
- [input.source.archive_id, input.source.archive_id]
5524
- );
5595
+ await tx.query("INSERT INTO archive (id, name) VALUES ($1, $2) ON CONFLICT (id) DO NOTHING", [input.source.archive_id, input.source.archive_id]);
5525
5596
  }
5526
5597
  if (outcome === "versioned") {
5527
- const count2 = await tx.query(
5528
- `SELECT COUNT(*) AS count FROM note_revision WHERE note_id = $1`,
5529
- [noteId]
5530
- );
5531
- const current = await tx.query(
5532
- `SELECT content, ai_metadata FROM note_revised_current WHERE note_id = $1`,
5533
- [noteId]
5598
+ const count2 = await tx.query("SELECT COUNT(*) AS count FROM note_revision WHERE note_id = $1", [noteId]);
5599
+ await tx.query(
5600
+ `INSERT INTO note_revision (id, note_id, revision_number, type, content, ai_metadata)
5601
+ VALUES ($1, $2, $3, 'source-import', $4, $5::jsonb)`,
5602
+ [generateId(), noteId, Number.parseInt(count2.rows[0]?.count ?? "0", 10) + 1, input.content, JSON.stringify(input.metadata ?? null)]
5534
5603
  );
5535
- const nextRevision = Number.parseInt(count2.rows[0]?.count ?? "0", 10) + 1;
5536
- if (current.rows[0]) {
5537
- await tx.query(
5538
- `INSERT INTO note_revision (id, note_id, revision_number, type, content, ai_metadata)
5539
- VALUES ($1, $2, $3, 'source-import', $4, $5::jsonb)`,
5540
- [
5541
- generateId(),
5542
- noteId,
5543
- nextRevision,
5544
- current.rows[0].content,
5545
- JSON.stringify(current.rows[0].ai_metadata ?? null)
5546
- ]
5547
- );
5548
- }
5604
+ } else {
5605
+ await tx.query("UPDATE note_original SET content = $1, content_hash = $2 WHERE note_id = $3", [input.content, digest2, noteId]);
5549
5606
  }
5550
5607
  await tx.query(
5551
- `UPDATE note
5552
- SET title = $1, format = $2, visibility = $3, archive_id = $4, updated_at = now(), deleted_at = NULL
5553
- WHERE id = $5`,
5554
- [
5555
- input.title ?? null,
5556
- input.format ?? "markdown",
5557
- input.visibility ?? "private",
5558
- input.source.archive_id ?? null,
5559
- noteId
5560
- ]
5608
+ `UPDATE note SET title = $1, format = $2, visibility = $3, archive_id = $4, updated_at = now(), deleted_at = NULL WHERE id = $5`,
5609
+ [input.title ?? null, input.format ?? "markdown", input.visibility ?? "private", input.source.archive_id ?? null, noteId]
5561
5610
  );
5562
5611
  await tx.query(
5563
- `UPDATE note_revised_current
5564
- SET content = $1, ai_metadata = $2::jsonb, is_user_edited = false, updated_at = now()
5565
- WHERE note_id = $3`,
5612
+ `UPDATE note_revised_current SET content = $1, ai_metadata = $2::jsonb, is_user_edited = false, updated_at = now() WHERE note_id = $3`,
5566
5613
  [input.content, JSON.stringify(input.metadata ?? null), noteId]
5567
5614
  );
5568
5615
  }
@@ -5571,184 +5618,260 @@ var SourceUpsertRepository = class {
5571
5618
  this.db = db;
5572
5619
  this.events = events;
5573
5620
  }
5574
- async upsertBatch(items, options = {}) {
5575
- const maxItems = options.maxItems ?? DEFAULT_MAX_ITEMS;
5576
- if (items.length > maxItems) throw new Error(`Source upsert batch exceeds the ${maxItems} item bound`);
5577
- if (items.length === 0) {
5578
- return {
5579
- import_run_id: "",
5580
- dry_run: options.dryRun === true,
5581
- outcomes: [],
5582
- counts: { inserted: 0, unchanged: 0, versioned: 0, replaced: 0, conflict: 0, rejected: 0 }
5583
- };
5584
- }
5585
- const outcomes = [];
5586
- for (const [index, item] of items.entries()) {
5587
- try {
5588
- assertSource(item.source);
5589
- outcomes.push({
5590
- index,
5591
- outcome: "rejected",
5592
- external_id_hash: sourceHash(item.source),
5593
- content_digest: contentDigest(item.content)
5594
- });
5595
- } catch (error) {
5596
- outcomes.push({
5597
- index,
5598
- outcome: "rejected",
5599
- external_id_hash: item.source ? sourceHash({ ...item.source, external_id: item.source.external_id ?? "" }) : "",
5600
- content_digest: contentDigest(item.content ?? ""),
5601
- reason: error instanceof Error ? error.message : String(error)
5602
- });
5603
- }
5621
+ async upsertRequest(request, scope = {}) {
5622
+ const tenant = scope.tenant_id ?? "default";
5623
+ const memory = scope.archive_id ?? null;
5624
+ const items = request.items.map((item) => ({
5625
+ source: {
5626
+ tenant_id: tenant,
5627
+ archive_id: memory,
5628
+ namespace: request.source_namespace,
5629
+ external_id: item.external_id,
5630
+ source_schema_version: request.source_schema_version,
5631
+ import_run_id: request.import_run_id,
5632
+ source_id: request.source_id,
5633
+ workspace_id: request.workspace_id,
5634
+ caller_stable_id: item.caller_stable_id
5635
+ },
5636
+ title: item.title,
5637
+ content: item.content,
5638
+ content_digest: item.content_digest,
5639
+ format: item.format,
5640
+ metadata: item.metadata,
5641
+ policy: item.policy ?? request.policy
5642
+ }));
5643
+ const invalidCommon = request.source_id !== void 0 && (request.source_id.length === 0 || request.source_id.length > 500) || request.workspace_id !== void 0 && (request.workspace_id.length === 0 || request.workspace_id.length > 500);
5644
+ if (invalidCommon) {
5645
+ const batchId = request.batch_id ?? deriveSourceBatchId(sourceRequestDigest(items, {}));
5646
+ const rejected = items.map((item, index) => ({
5647
+ index,
5648
+ outcome: "rejected",
5649
+ external_id_hash: sourceIdentityHash(item.source),
5650
+ content_digest: sourceContentDigest(item.content),
5651
+ reason_code: "invalid_batch_metadata"
5652
+ }));
5653
+ return contractResponse(finish(request.import_run_id, batchId, request.dry_run === true, "rejected", rejected, request.checkpoint));
5604
5654
  }
5605
- if (outcomes.some((outcome) => outcome.reason)) {
5606
- return this.finish(items[0].source?.import_run_id ?? "", options.dryRun === true, outcomes);
5655
+ return contractResponse(await this.upsertBatch(items, {
5656
+ dryRun: request.dry_run,
5657
+ batchId: request.batch_id,
5658
+ checkpoint: request.checkpoint,
5659
+ policy: request.policy
5660
+ }));
5661
+ }
5662
+ async upsertBatch(items, options = {}) {
5663
+ const maxItems = options.maxItems ?? SOURCE_UPSERT_MAX_ITEMS;
5664
+ const importRunId = items[0]?.source.import_run_id ?? "";
5665
+ const requestDigest = sourceRequestDigest(items, options);
5666
+ const batchId = options.batchId ?? deriveSourceBatchId(requestDigest);
5667
+ const batchReason = batchId.length === 0 || batchId.length > 200 ? "invalid_batch_metadata" : JSON.stringify(options.checkpoint ?? {}).length > 65536 ? "checkpoint_too_large" : void 0;
5668
+ const validation = validateItems(items, maxItems, batchReason);
5669
+ if (validation) {
5670
+ return finish(importRunId, batchId, options.dryRun === true, "rejected", validation, options.checkpoint);
5607
5671
  }
5608
5672
  if (options.dryRun) {
5609
- const preview = [];
5610
- for (const [index, item] of items.entries()) {
5611
- const externalIdHash = sourceHash(item.source);
5612
- const digest2 = contentDigest(item.content);
5613
- const existing = await this.db.query(
5614
- `SELECT note_id, content_digest
5615
- FROM source_identity
5616
- WHERE tenant_id = $1
5617
- AND archive_id IS NOT DISTINCT FROM $2
5618
- AND namespace = $3
5619
- AND external_id = $4
5620
- LIMIT 1`,
5621
- [item.source.tenant_id ?? "default", item.source.archive_id ?? null, item.source.namespace, item.source.external_id]
5622
- );
5623
- if (existing.rows.length === 0) {
5624
- preview.push({ index, outcome: "inserted", external_id_hash: externalIdHash, content_digest: digest2 });
5625
- } else if (existing.rows[0].content_digest === digest2) {
5626
- preview.push({ index, outcome: "unchanged", note_id: existing.rows[0].note_id, external_id_hash: externalIdHash, content_digest: digest2 });
5627
- } else if ((item.policy ?? "version") === "conflict") {
5628
- preview.push({ index, outcome: "conflict", note_id: existing.rows[0].note_id, external_id_hash: externalIdHash, content_digest: digest2 });
5629
- } else {
5630
- preview.push({
5673
+ const preview = await previewItems(this.db, items, options.policy);
5674
+ return finish(importRunId, batchId, true, "preview", preview, options.checkpoint);
5675
+ }
5676
+ const response = await this.db.transaction(async (tx) => {
5677
+ const prior = await tx.query(
5678
+ `SELECT request_digest, receipt FROM source_import_batch
5679
+ WHERE tenant_id = $1 AND archive_id IS NOT DISTINCT FROM $2 AND namespace = $3 AND batch_id = $4 LIMIT 1`,
5680
+ [items[0].source.tenant_id ?? "default", items[0].source.archive_id ?? null, items[0].source.namespace, batchId]
5681
+ );
5682
+ if (prior.rows[0]) {
5683
+ if (prior.rows[0].request_digest !== requestDigest) {
5684
+ const rejected = items.map((item, index) => ({
5631
5685
  index,
5632
- outcome: item.policy === "replace" ? "replaced" : "versioned",
5633
- note_id: existing.rows[0].note_id,
5634
- external_id_hash: externalIdHash,
5635
- content_digest: digest2
5636
- });
5686
+ outcome: "rejected",
5687
+ external_id_hash: sourceIdentityHash(item.source),
5688
+ content_digest: sourceContentDigest(item.content),
5689
+ reason_code: "batch_id_reused_with_different_request"
5690
+ }));
5691
+ return finish(importRunId, batchId, false, "rejected", rejected, options.checkpoint);
5692
+ }
5693
+ const receipt = parseReceipt(prior.rows[0].receipt);
5694
+ if (!receipt) throw new Error("Stored source upsert receipt is invalid");
5695
+ return duplicateReceipt(receipt);
5696
+ }
5697
+ for (const item of items) {
5698
+ if (!item.source.caller_stable_id) continue;
5699
+ const collision = await tx.query("SELECT id FROM note WHERE id = $1 LIMIT 1", [item.source.caller_stable_id]);
5700
+ if (collision.rows[0]) {
5701
+ const mapped = await findExisting(tx, item);
5702
+ if (mapped?.note_id === item.source.caller_stable_id) continue;
5703
+ const rejected = items.map((candidate, index) => ({
5704
+ index,
5705
+ outcome: "rejected",
5706
+ external_id_hash: sourceIdentityHash(candidate.source),
5707
+ content_digest: sourceContentDigest(candidate.content),
5708
+ reason_code: "caller_stable_id_conflict"
5709
+ }));
5710
+ return finish(importRunId, batchId, false, "rejected", rejected, options.checkpoint);
5637
5711
  }
5638
5712
  }
5639
- return this.finish(items[0].source.import_run_id, true, preview);
5640
- }
5641
- await this.db.transaction(async (tx) => {
5713
+ const outcomes = [];
5642
5714
  for (const [index, item] of items.entries()) {
5643
- const externalIdHash = sourceHash(item.source);
5644
- const digest2 = contentDigest(item.content);
5645
- const existing = await tx.query(
5646
- `SELECT note_id, content_digest
5647
- FROM source_identity
5648
- WHERE tenant_id = $1
5649
- AND archive_id IS NOT DISTINCT FROM $2
5650
- AND namespace = $3
5651
- AND external_id = $4
5652
- LIMIT 1`,
5653
- [item.source.tenant_id ?? "default", item.source.archive_id ?? null, item.source.namespace, item.source.external_id]
5654
- );
5655
- if (existing.rows.length === 0) {
5715
+ const externalIdHash = sourceIdentityHash(item.source);
5716
+ const digest2 = sourceContentDigest(item.content);
5717
+ const existing = await findExisting(tx, item);
5718
+ if (!existing) {
5656
5719
  const noteId = item.source.caller_stable_id ?? generateId();
5657
5720
  await insertNote(tx, item, noteId, digest2);
5658
5721
  await tx.query(
5659
5722
  `INSERT INTO source_identity
5660
- (id, tenant_id, archive_id, namespace, external_id, external_id_hash,
5661
- source_schema_version, content_digest, import_run_id, caller_stable_id, note_id)
5662
- VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11)`,
5663
- [
5664
- generateId(),
5665
- item.source.tenant_id ?? "default",
5666
- item.source.archive_id ?? null,
5667
- item.source.namespace,
5668
- item.source.external_id,
5669
- externalIdHash,
5670
- item.source.source_schema_version,
5671
- digest2,
5672
- item.source.import_run_id,
5673
- item.source.caller_stable_id ?? null,
5674
- noteId
5675
- ]
5723
+ (id, tenant_id, archive_id, namespace, external_id, external_id_hash, source_id, source_schema_version, content_digest, import_run_id, caller_stable_id, note_id)
5724
+ VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12)`,
5725
+ [generateId(), item.source.tenant_id ?? "default", item.source.archive_id ?? null, item.source.namespace, item.source.external_id, externalIdHash, item.source.source_id ?? null, item.source.source_schema_version, digest2, item.source.import_run_id, item.source.caller_stable_id ?? null, noteId]
5676
5726
  );
5677
- outcomes[index] = { index, outcome: "inserted", note_id: noteId, external_id_hash: externalIdHash, content_digest: digest2 };
5727
+ outcomes.push({ index, outcome: "inserted", note_id: noteId, external_id_hash: externalIdHash, content_digest: digest2 });
5678
5728
  continue;
5679
5729
  }
5680
- const row = existing.rows[0];
5681
- if (row.content_digest === digest2) {
5682
- outcomes[index] = { index, outcome: "unchanged", note_id: row.note_id, external_id_hash: externalIdHash, content_digest: digest2 };
5730
+ if (existing.content_digest === digest2) {
5731
+ outcomes.push({ index, outcome: "unchanged", note_id: existing.note_id, external_id_hash: externalIdHash, content_digest: digest2 });
5683
5732
  continue;
5684
5733
  }
5685
- const policy = item.policy ?? "version";
5734
+ const policy = item.policy ?? options.policy ?? "version";
5686
5735
  if (policy === "conflict") {
5687
- outcomes[index] = { index, outcome: "conflict", note_id: row.note_id, external_id_hash: externalIdHash, content_digest: digest2 };
5736
+ outcomes.push({ index, outcome: "conflict", note_id: existing.note_id, external_id_hash: externalIdHash, content_digest: digest2 });
5688
5737
  continue;
5689
5738
  }
5690
5739
  const outcome = policy === "replace" ? "replaced" : "versioned";
5691
- await updateNote(tx, item, row.note_id, outcome);
5692
- await tx.query(
5693
- `UPDATE source_identity
5694
- SET source_schema_version = $1, content_digest = $2, import_run_id = $3, updated_at = now()
5695
- WHERE note_id = $4
5696
- AND tenant_id = $5
5697
- AND archive_id IS NOT DISTINCT FROM $6
5698
- AND namespace = $7
5699
- AND external_id = $8`,
5700
- [
5701
- item.source.source_schema_version,
5702
- digest2,
5703
- item.source.import_run_id,
5704
- row.note_id,
5705
- item.source.tenant_id ?? "default",
5706
- item.source.archive_id ?? null,
5707
- item.source.namespace,
5708
- item.source.external_id
5709
- ]
5710
- );
5711
- outcomes[index] = { index, outcome, note_id: row.note_id, external_id_hash: externalIdHash, content_digest: digest2 };
5712
- }
5713
- if (hasMaterialChange(outcomes)) {
5740
+ await updateNote(tx, item, existing.note_id, outcome, digest2);
5714
5741
  await tx.query(
5715
- `INSERT INTO source_import_run (id, tenant_id, archive_id, namespace, completed_at, checkpoint, receipt)
5716
- VALUES ($1, $2, $3, $4, now(), $5::jsonb, $6::jsonb)
5717
- ON CONFLICT (id) DO UPDATE
5718
- SET completed_at = EXCLUDED.completed_at,
5719
- checkpoint = EXCLUDED.checkpoint,
5720
- receipt = EXCLUDED.receipt`,
5721
- [
5722
- items[0].source.import_run_id,
5723
- items[0].source.tenant_id ?? "default",
5724
- items[0].source.archive_id ?? null,
5725
- items[0].source.namespace,
5726
- JSON.stringify({ item_count: items.length }),
5727
- JSON.stringify({ counts: countOutcomes(outcomes) })
5728
- ]
5742
+ `UPDATE source_identity SET source_id = $1, source_schema_version = $2, content_digest = $3, import_run_id = $4, updated_at = now()
5743
+ WHERE note_id = $5 AND tenant_id = $6 AND archive_id IS NOT DISTINCT FROM $7 AND namespace = $8 AND external_id = $9`,
5744
+ [item.source.source_id ?? null, item.source.source_schema_version, digest2, item.source.import_run_id, existing.note_id, item.source.tenant_id ?? "default", item.source.archive_id ?? null, item.source.namespace, item.source.external_id]
5729
5745
  );
5746
+ outcomes.push({ index, outcome, note_id: existing.note_id, external_id_hash: externalIdHash, content_digest: digest2 });
5730
5747
  }
5748
+ const committed = finish(importRunId, batchId, false, "committed", outcomes, options.checkpoint);
5749
+ await tx.query(
5750
+ `INSERT INTO source_import_run (id, tenant_id, archive_id, namespace, external_run_id, source_id, source_schema_version, workspace_id, completed_at, checkpoint, receipt)
5751
+ VALUES ($1, $2, $3, $4, $5, $6, $7, $8, now(), $9::jsonb, $10::jsonb)
5752
+ ON CONFLICT (id) DO UPDATE SET source_id = EXCLUDED.source_id, source_schema_version = EXCLUDED.source_schema_version, workspace_id = EXCLUDED.workspace_id, completed_at = EXCLUDED.completed_at, checkpoint = EXCLUDED.checkpoint, receipt = EXCLUDED.receipt`,
5753
+ [sourceRunRecordId(items[0].source), items[0].source.tenant_id ?? "default", items[0].source.archive_id ?? null, items[0].source.namespace, importRunId, items[0].source.source_id ?? null, items[0].source.source_schema_version, items[0].source.workspace_id ?? null, JSON.stringify(options.checkpoint ?? {}), JSON.stringify(redactedReceipt(committed))]
5754
+ );
5755
+ await tx.query(
5756
+ `INSERT INTO source_import_batch (id, tenant_id, archive_id, namespace, batch_id, request_digest, import_run_id, outcome, checkpoint, receipt)
5757
+ VALUES ($1, $2, $3, $4, $5, $6, $7, 'committed', $8::jsonb, $9::jsonb)`,
5758
+ [generateId(), items[0].source.tenant_id ?? "default", items[0].source.archive_id ?? null, items[0].source.namespace, batchId, requestDigest, importRunId, JSON.stringify(options.checkpoint ?? {}), JSON.stringify(redactedReceipt(committed))]
5759
+ );
5760
+ return committed;
5731
5761
  });
5732
- if (hasMaterialChange(outcomes)) {
5733
- this.events?.emit("source.upserted", { importRunId: items[0].source.import_run_id, counts: countOutcomes(outcomes) });
5762
+ if (response.outcome === "committed" && hasMaterialChange(response.items)) {
5763
+ this.events?.emit("source.upserted", { importRunId, counts: response.counts });
5734
5764
  }
5735
- return this.finish(items[0].source.import_run_id, false, outcomes);
5736
- }
5737
- finish(importRunId, dryRun, outcomes) {
5738
- return { import_run_id: importRunId, dry_run: dryRun, outcomes, counts: countOutcomes(outcomes) };
5765
+ return response;
5739
5766
  }
5740
5767
  };
5768
+ async function findExisting(tx, item) {
5769
+ const result = await tx.query(
5770
+ `SELECT note_id, content_digest FROM source_identity
5771
+ WHERE tenant_id = $1 AND archive_id IS NOT DISTINCT FROM $2 AND namespace = $3 AND external_id = $4 LIMIT 1`,
5772
+ [item.source.tenant_id ?? "default", item.source.archive_id ?? null, item.source.namespace, item.source.external_id]
5773
+ );
5774
+ return result.rows[0] ?? null;
5775
+ }
5776
+ async function previewItems(db, items, batchPolicy) {
5777
+ const preview = [];
5778
+ for (const [index, item] of items.entries()) {
5779
+ const external_id_hash = sourceIdentityHash(item.source);
5780
+ const content_digest = sourceContentDigest(item.content);
5781
+ const existing = await findExisting(db, item);
5782
+ if (!existing) preview.push({ index, outcome: "inserted", external_id_hash, content_digest });
5783
+ else if (existing.content_digest === content_digest) preview.push({ index, outcome: "unchanged", note_id: existing.note_id, external_id_hash, content_digest });
5784
+ else if ((item.policy ?? batchPolicy ?? "version") === "conflict") preview.push({ index, outcome: "conflict", note_id: existing.note_id, external_id_hash, content_digest });
5785
+ else preview.push({ index, outcome: (item.policy ?? batchPolicy) === "replace" ? "replaced" : "versioned", note_id: existing.note_id, external_id_hash, content_digest });
5786
+ }
5787
+ return preview;
5788
+ }
5789
+ function validateItems(items, maxItems, initialReason) {
5790
+ const seen = /* @__PURE__ */ new Set();
5791
+ const stableIds = /* @__PURE__ */ new Set();
5792
+ const rejected = [];
5793
+ let batchReason = initialReason ?? (items.length === 0 || items.length > maxItems ? "batch_size_out_of_bounds" : null);
5794
+ for (const [index, item] of items.entries()) {
5795
+ const digest2 = sourceContentDigest(item.content ?? "");
5796
+ let reason = batchReason;
5797
+ try {
5798
+ assertSource(item.source);
5799
+ if (!item.content || item.content.length > 4194304) reason ??= "invalid_item";
5800
+ if ((item.format ?? "markdown").length > 100 || (item.title?.length ?? 0) > 2e3 || JSON.stringify(item.metadata ?? {}).length > 262144) reason ??= "invalid_item";
5801
+ if (item.content_digest && item.content_digest !== digest2) reason ??= "content_digest_mismatch";
5802
+ const identity = `${item.source.tenant_id ?? "default"}\0${item.source.archive_id ?? ""}\0${item.source.namespace}\0${item.source.external_id}`;
5803
+ if (seen.has(identity)) reason ??= "duplicate_external_id_in_batch";
5804
+ seen.add(identity);
5805
+ if (item.source.caller_stable_id && stableIds.has(item.source.caller_stable_id)) reason ??= "caller_stable_id_conflict";
5806
+ if (item.source.caller_stable_id) stableIds.add(item.source.caller_stable_id);
5807
+ } catch (error) {
5808
+ const code = error instanceof Error ? error.message : "invalid_item";
5809
+ reason ??= code === "invalid_batch_metadata" ? code : "invalid_item";
5810
+ }
5811
+ if (reason) {
5812
+ batchReason = reason;
5813
+ rejected.push({ index, outcome: "rejected", external_id_hash: sourceIdentityHash(item.source), content_digest: digest2, reason_code: reason });
5814
+ }
5815
+ }
5816
+ if (!batchReason) return null;
5817
+ if (rejected.length === items.length) return rejected;
5818
+ return items.map((item, index) => rejected.find((result) => result.index === index) ?? {
5819
+ index,
5820
+ outcome: "rejected",
5821
+ external_id_hash: sourceIdentityHash(item.source),
5822
+ content_digest: sourceContentDigest(item.content),
5823
+ reason_code: batchReason
5824
+ });
5825
+ }
5826
+ function finish(importRunId, batchId, dryRun, outcome, items, checkpoint) {
5827
+ return {
5828
+ contract_version: SOURCE_UPSERT_CONTRACT_VERSION,
5829
+ import_run_id: importRunId,
5830
+ batch_id: batchId,
5831
+ dry_run: dryRun,
5832
+ outcome,
5833
+ ...checkpoint ? { checkpoint } : {},
5834
+ items,
5835
+ outcomes: items,
5836
+ counts: countOutcomes(items)
5837
+ };
5838
+ }
5839
+ function redactedReceipt(receipt) {
5840
+ return { ...contractResponse(receipt), items: receipt.items.map(sanitizeItem) };
5841
+ }
5842
+ function contractResponse(receipt) {
5843
+ return {
5844
+ contract_version: receipt.contract_version,
5845
+ import_run_id: receipt.import_run_id,
5846
+ batch_id: receipt.batch_id,
5847
+ dry_run: receipt.dry_run,
5848
+ outcome: receipt.outcome,
5849
+ ...receipt.checkpoint ? { checkpoint: receipt.checkpoint } : {},
5850
+ counts: receipt.counts,
5851
+ items: receipt.items
5852
+ };
5853
+ }
5854
+ function sanitizeItem(item) {
5855
+ return {
5856
+ index: item.index,
5857
+ outcome: item.outcome,
5858
+ ...item.note_id ? { note_id: item.note_id } : {},
5859
+ external_id_hash: item.external_id_hash,
5860
+ content_digest: item.content_digest,
5861
+ ...item.reason_code ? { reason_code: item.reason_code } : {}
5862
+ };
5863
+ }
5741
5864
  function hasMaterialChange(outcomes) {
5742
- return outcomes.some((outcome) => outcome.outcome === "inserted" || outcome.outcome === "versioned" || outcome.outcome === "replaced");
5865
+ return outcomes.some((item) => item.outcome === "inserted" || item.outcome === "versioned" || item.outcome === "replaced");
5743
5866
  }
5744
5867
  function countOutcomes(outcomes) {
5745
5868
  return {
5746
- inserted: outcomes.filter((outcome) => outcome.outcome === "inserted").length,
5747
- unchanged: outcomes.filter((outcome) => outcome.outcome === "unchanged").length,
5748
- versioned: outcomes.filter((outcome) => outcome.outcome === "versioned").length,
5749
- replaced: outcomes.filter((outcome) => outcome.outcome === "replaced").length,
5750
- conflict: outcomes.filter((outcome) => outcome.outcome === "conflict").length,
5751
- rejected: outcomes.filter((outcome) => outcome.outcome === "rejected").length
5869
+ inserted: outcomes.filter((item) => item.outcome === "inserted").length,
5870
+ unchanged: outcomes.filter((item) => item.outcome === "unchanged").length,
5871
+ versioned: outcomes.filter((item) => item.outcome === "versioned").length,
5872
+ replaced: outcomes.filter((item) => item.outcome === "replaced").length,
5873
+ conflict: outcomes.filter((item) => item.outcome === "conflict").length,
5874
+ rejected: outcomes.filter((item) => item.outcome === "rejected").length
5752
5875
  };
5753
5876
  }
5754
5877
 
@@ -29407,7 +29530,7 @@ function profileOf(value) {
29407
29530
  return typeof profile === "string" ? profile : void 0;
29408
29531
  }
29409
29532
  function validateShardManifest(value) {
29410
- let validate;
29533
+ let validate2;
29411
29534
  const profile = profileOf(value);
29412
29535
  if (profile === "core-v1") {
29413
29536
  const version = value && typeof value === "object" && !Array.isArray(value) ? coreSchemaVersion(String(value.version ?? "")) : void 0;
@@ -29417,7 +29540,7 @@ function validateShardManifest(value) {
29417
29540
  errors: ["(root) uses an unsupported canonical core-v1 schema version"]
29418
29541
  };
29419
29542
  }
29420
- validate = coreValidatorFor("manifest", version);
29543
+ validate2 = coreValidatorFor("manifest", version);
29421
29544
  } else if (profile === "record-v1") {
29422
29545
  const version = value && typeof value === "object" && !Array.isArray(value) ? recordSchemaVersion(String(value.version ?? "")) : void 0;
29423
29546
  if (!version) {
@@ -29426,7 +29549,7 @@ function validateShardManifest(value) {
29426
29549
  errors: ["(root) uses an unsupported canonical record-v1 schema version"]
29427
29550
  };
29428
29551
  }
29429
- validate = recordValidatorFor("manifest", version);
29552
+ validate2 = recordValidatorFor("manifest", version);
29430
29553
  } else if (profile === "full-v1") {
29431
29554
  const version = value && typeof value === "object" && !Array.isArray(value) ? fullSchemaVersion(String(value.version ?? "")) : void 0;
29432
29555
  if (!version) {
@@ -29435,12 +29558,12 @@ function validateShardManifest(value) {
29435
29558
  errors: ["(root) uses an unsupported canonical full-v1 schema version"]
29436
29559
  };
29437
29560
  }
29438
- validate = fullValidatorFor("manifest", version);
29561
+ validate2 = fullValidatorFor("manifest", version);
29439
29562
  } else {
29440
- validate = legacyValidatorFor("manifest");
29563
+ validate2 = legacyValidatorFor("manifest");
29441
29564
  }
29442
- const valid = validate(value);
29443
- return { valid, errors: formatErrors(validate.errors) };
29565
+ const valid = validate2(value);
29566
+ return { valid, errors: formatErrors(validate2.errors) };
29444
29567
  }
29445
29568
  function parseJsonArray(bytes, path) {
29446
29569
  if (!bytes) return { records: [], errors: [] };
@@ -29883,9 +30006,9 @@ async function validateFullV1ShardArchive(input) {
29883
30006
  return { valid: errors.length === 0, errors };
29884
30007
  }
29885
30008
  function validateShardComponentRecord(component, value, profile, version = CURRENT_SHARD_VERSION) {
29886
- let validate;
30009
+ let validate2;
29887
30010
  if (profile === "core-v1" && component in CORE_V1_COMPONENT_FILES) {
29888
- validate = coreValidatorFor(component, version);
30011
+ validate2 = coreValidatorFor(component, version);
29889
30012
  } else if (profile === "record-v1" && component in RECORD_V1_COMPONENT_FILES) {
29890
30013
  const canonicalVersion = recordSchemaVersion(version);
29891
30014
  if (!canonicalVersion) {
@@ -29894,7 +30017,7 @@ function validateShardComponentRecord(component, value, profile, version = CURRE
29894
30017
  errors: [`(root) uses an unsupported canonical record-v1 schema version ${version}`]
29895
30018
  };
29896
30019
  }
29897
- validate = recordValidatorFor(component, canonicalVersion);
30020
+ validate2 = recordValidatorFor(component, canonicalVersion);
29898
30021
  } else if (profile === "full-v1" && component in FULL_V1_COMPONENT_FILES) {
29899
30022
  const canonicalVersion = fullSchemaVersion(version);
29900
30023
  if (!canonicalVersion) {
@@ -29903,7 +30026,7 @@ function validateShardComponentRecord(component, value, profile, version = CURRE
29903
30026
  errors: [`(root) uses an unsupported canonical full-v1 schema version ${version}`]
29904
30027
  };
29905
30028
  }
29906
- validate = fullValidatorFor(component, canonicalVersion);
30029
+ validate2 = fullValidatorFor(component, canonicalVersion);
29907
30030
  } else {
29908
30031
  const legacyDef = LEGACY_COMPONENT_SCHEMA_DEFS[component];
29909
30032
  if (!legacyDef) {
@@ -29912,10 +30035,10 @@ function validateShardComponentRecord(component, value, profile, version = CURRE
29912
30035
  errors: [`(root) component '${component}' requires an explicit full-v1 profile`]
29913
30036
  };
29914
30037
  }
29915
- validate = legacyValidatorFor(legacyDef);
30038
+ validate2 = legacyValidatorFor(legacyDef);
29916
30039
  }
29917
- const valid = validate(value);
29918
- return { valid, errors: formatErrors(validate.errors) };
30040
+ const valid = validate2(value);
30041
+ return { valid, errors: formatErrors(validate2.errors) };
29919
30042
  }
29920
30043
  function assertShardComponentRecord(component, value, profile, version = CURRENT_SHARD_VERSION) {
29921
30044
  const result = validateShardComponentRecord(component, value, profile, version);
@@ -36228,7 +36351,7 @@ function getProjectedRecordValidator() {
36228
36351
  return projectedRecordValidator;
36229
36352
  }
36230
36353
  function validateAiwgFortemiIndexExportSchema(value) {
36231
- const validate = getExportValidator();
36354
+ const validate2 = getExportValidator();
36232
36355
  const schemaValue = value && typeof value === "object" && Array.isArray(value.items) ? {
36233
36356
  ...value,
36234
36357
  items: value.items.map((item) => {
@@ -36238,13 +36361,13 @@ function validateAiwgFortemiIndexExportSchema(value) {
36238
36361
  return schemaRecord;
36239
36362
  })
36240
36363
  } : value;
36241
- const valid = validate(schemaValue);
36242
- return { valid, errors: formatErrors2(validate.errors) };
36364
+ const valid = validate2(schemaValue);
36365
+ return { valid, errors: formatErrors2(validate2.errors) };
36243
36366
  }
36244
36367
  function validateAiwgFortemiProjectedRecordSchema(value) {
36245
- const validate = getProjectedRecordValidator();
36246
- const valid = validate(value);
36247
- return { valid, errors: formatErrors2(validate.errors) };
36368
+ const validate2 = getProjectedRecordValidator();
36369
+ const valid = validate2(value);
36370
+ return { valid, errors: formatErrors2(validate2.errors) };
36248
36371
  }
36249
36372
  var encoder5 = new TextEncoder();
36250
36373
  var UUID_NAMESPACE = "7ab5d1f8-29d2-5e35-9e2f-3a45de171a9e";
@@ -37235,6 +37358,7 @@ var RECORD_COLLECTIONS = [
37235
37358
  "note",
37236
37359
  "note_original",
37237
37360
  "note_revised_current",
37361
+ "note_revision",
37238
37362
  "note_tag",
37239
37363
  "link",
37240
37364
  "collection",
@@ -37244,6 +37368,7 @@ var RECORD_COLLECTIONS = [
37244
37368
  "shard_manifest",
37245
37369
  "source_identity",
37246
37370
  "source_import_run",
37371
+ "source_import_batch",
37247
37372
  "deletion_receipt"
37248
37373
  ];
37249
37374
  var RECORD_STORE_CAPABILITIES = {
@@ -37366,10 +37491,10 @@ var MemoryRecordStore = class {
37366
37491
  };
37367
37492
 
37368
37493
  // src/records/idb-record-store.ts
37369
- var DB_VERSION = 2;
37494
+ var DB_VERSION = 3;
37370
37495
  var JOURNAL_STORE = "journal";
37371
37496
  var META_STORE = "meta";
37372
- var RECORD_SCHEMA_VERSION = 2;
37497
+ var RECORD_SCHEMA_VERSION = 3;
37373
37498
  function requestToPromise(request) {
37374
37499
  return new Promise((resolve, reject) => {
37375
37500
  request.onsuccess = () => resolve(request.result);
@@ -37926,10 +38051,11 @@ function collectionsParentFirst(collections) {
37926
38051
  return ordered;
37927
38052
  }
37928
38053
  async function projectNotes(db, store) {
37929
- const [noteRows, originals, revised, tags, links, collections, memberships] = await Promise.all([
38054
+ const [noteRows, originals, revised, revisions, tags, links, collections, memberships] = await Promise.all([
37930
38055
  store.list("note"),
37931
38056
  store.list("note_original"),
37932
38057
  store.list("note_revised_current"),
38058
+ store.list("note_revision"),
37933
38059
  store.list("note_tag"),
37934
38060
  store.list("link"),
37935
38061
  store.list("collection"),
@@ -38026,6 +38152,28 @@ async function projectNotes(db, store) {
38026
38152
  ]
38027
38153
  );
38028
38154
  }
38155
+ for (const r of revisions) {
38156
+ await db.query(
38157
+ `INSERT INTO note_revision (id, note_id, revision_number, type, content, ai_metadata, model, created_at)
38158
+ VALUES ($1, $2, $3, $4, $5, $6::jsonb, $7, $8)
38159
+ ON CONFLICT (id) DO UPDATE SET
38160
+ revision_number = EXCLUDED.revision_number,
38161
+ type = EXCLUDED.type,
38162
+ content = EXCLUDED.content,
38163
+ ai_metadata = EXCLUDED.ai_metadata,
38164
+ model = EXCLUDED.model`,
38165
+ [
38166
+ r.id,
38167
+ r.note_id,
38168
+ r.revision_number,
38169
+ r.type,
38170
+ r.content,
38171
+ r.ai_metadata == null ? null : JSON.stringify(r.ai_metadata),
38172
+ r.model,
38173
+ r.created_at
38174
+ ]
38175
+ );
38176
+ }
38029
38177
  for (const t of tags) {
38030
38178
  await db.query(
38031
38179
  `INSERT INTO note_tag (id, note_id, tag, created_at)
@@ -38062,6 +38210,7 @@ async function projectNotes(db, store) {
38062
38210
  );
38063
38211
  return {
38064
38212
  notes: noteRows.length,
38213
+ revisions: revisions.length,
38065
38214
  tags: tags.length,
38066
38215
  links: links.length,
38067
38216
  collections: collections.length,
@@ -39296,177 +39445,283 @@ async function importShardToRecords(store, data, options) {
39296
39445
  function now() {
39297
39446
  return (/* @__PURE__ */ new Date()).toISOString();
39298
39447
  }
39299
- function contentDigest2(content) {
39300
- return computeHash(new TextEncoder().encode(content));
39301
- }
39302
- function sourceHash2(source) {
39303
- return computeHash(new TextEncoder().encode([
39304
- source.tenant_id ?? "default",
39305
- source.archive_id ?? "",
39306
- source.namespace,
39307
- source.external_id
39308
- ].join("\0")));
39309
- }
39310
39448
  function countOutcomes2(outcomes) {
39311
39449
  return {
39312
- inserted: outcomes.filter((outcome) => outcome.outcome === "inserted").length,
39313
- unchanged: outcomes.filter((outcome) => outcome.outcome === "unchanged").length,
39314
- versioned: outcomes.filter((outcome) => outcome.outcome === "versioned").length,
39315
- replaced: outcomes.filter((outcome) => outcome.outcome === "replaced").length,
39316
- conflict: outcomes.filter((outcome) => outcome.outcome === "conflict").length,
39317
- rejected: outcomes.filter((outcome) => outcome.outcome === "rejected").length
39450
+ inserted: outcomes.filter((item) => item.outcome === "inserted").length,
39451
+ unchanged: outcomes.filter((item) => item.outcome === "unchanged").length,
39452
+ versioned: outcomes.filter((item) => item.outcome === "versioned").length,
39453
+ replaced: outcomes.filter((item) => item.outcome === "replaced").length,
39454
+ conflict: outcomes.filter((item) => item.outcome === "conflict").length,
39455
+ rejected: outcomes.filter((item) => item.outcome === "rejected").length
39456
+ };
39457
+ }
39458
+ function finish2(importRunId, batchId, dryRun, outcome, items, checkpoint) {
39459
+ return {
39460
+ contract_version: SOURCE_UPSERT_CONTRACT_VERSION,
39461
+ import_run_id: importRunId,
39462
+ batch_id: batchId,
39463
+ dry_run: dryRun,
39464
+ outcome,
39465
+ ...checkpoint ? { checkpoint } : {},
39466
+ items,
39467
+ outcomes: items,
39468
+ counts: countOutcomes2(items)
39318
39469
  };
39319
39470
  }
39320
39471
  async function findSource(store, source) {
39321
39472
  const identities = await store.list("source_identity");
39322
39473
  return identities.find((identity) => identity.tenant_id === (source.tenant_id ?? "default") && identity.archive_id === (source.archive_id ?? null) && identity.namespace === source.namespace && identity.external_id === source.external_id) ?? null;
39323
39474
  }
39475
+ async function upsertRecordStoreRequest(store, request, scope = {}) {
39476
+ const items = request.items.map((item) => ({
39477
+ source: {
39478
+ tenant_id: scope.tenant_id ?? "default",
39479
+ archive_id: scope.archive_id ?? null,
39480
+ namespace: request.source_namespace,
39481
+ external_id: item.external_id,
39482
+ source_schema_version: request.source_schema_version,
39483
+ import_run_id: request.import_run_id,
39484
+ source_id: request.source_id,
39485
+ workspace_id: request.workspace_id,
39486
+ caller_stable_id: item.caller_stable_id
39487
+ },
39488
+ title: item.title,
39489
+ content: item.content,
39490
+ content_digest: item.content_digest,
39491
+ format: item.format,
39492
+ metadata: item.metadata,
39493
+ policy: item.policy ?? request.policy
39494
+ }));
39495
+ const result = await upsertRecordStoreSources(store, items, {
39496
+ dryRun: request.dry_run,
39497
+ batchId: request.batch_id,
39498
+ checkpoint: request.checkpoint,
39499
+ policy: request.policy
39500
+ });
39501
+ return {
39502
+ contract_version: result.contract_version,
39503
+ import_run_id: result.import_run_id,
39504
+ batch_id: result.batch_id,
39505
+ dry_run: result.dry_run,
39506
+ outcome: result.outcome,
39507
+ ...result.checkpoint ? { checkpoint: result.checkpoint } : {},
39508
+ counts: result.counts,
39509
+ items: result.items
39510
+ };
39511
+ }
39324
39512
  async function upsertRecordStoreSources(store, items, options = {}) {
39325
- const maxItems = options.maxItems ?? 500;
39326
- if (items.length > maxItems) throw new Error(`Source upsert batch exceeds the ${maxItems} item bound`);
39327
39513
  if (!store.applyBatch) throw new Error("RecordStore source upsert requires atomic applyBatch() support");
39328
- if (items.length === 0) {
39329
- return {
39330
- import_run_id: "",
39331
- dry_run: options.dryRun === true,
39332
- outcomes: [],
39333
- counts: { inserted: 0, unchanged: 0, versioned: 0, replaced: 0, conflict: 0, rejected: 0 }
39334
- };
39514
+ const importRunId = items[0]?.source.import_run_id ?? "";
39515
+ const requestDigest = sourceRequestDigest(items, options);
39516
+ const batchId = options.batchId ?? deriveSourceBatchId(requestDigest);
39517
+ const batchReason = batchId.length === 0 || batchId.length > 200 ? "invalid_batch_metadata" : JSON.stringify(options.checkpoint ?? {}).length > 65536 ? "checkpoint_too_large" : void 0;
39518
+ const validation = validate(items, options.maxItems ?? 500, batchReason);
39519
+ if (validation) return finish2(importRunId, batchId, options.dryRun === true, "rejected", validation, options.checkpoint);
39520
+ const batches = await store.list("source_import_batch");
39521
+ const prior = batches.find((batch2) => batch2.tenant_id === (items[0].source.tenant_id ?? "default") && batch2.archive_id === (items[0].source.archive_id ?? null) && batch2.namespace === items[0].source.namespace && batch2.batch_id === batchId);
39522
+ if (prior) {
39523
+ if (prior.request_digest !== requestDigest) {
39524
+ const rejected = items.map((item, index) => ({
39525
+ index,
39526
+ outcome: "rejected",
39527
+ external_id_hash: sourceIdentityHash(item.source),
39528
+ content_digest: sourceContentDigest(item.content),
39529
+ reason_code: "batch_id_reused_with_different_request"
39530
+ }));
39531
+ return finish2(importRunId, batchId, false, "rejected", rejected, options.checkpoint);
39532
+ }
39533
+ const receipt = prior.receipt;
39534
+ const unchanged = receipt.items.map((item) => ({ ...item, outcome: "unchanged", reason_code: void 0, reason: void 0 }));
39535
+ return finish2(importRunId, batchId, false, "duplicate", unchanged, receipt.checkpoint);
39335
39536
  }
39336
39537
  const outcomes = [];
39337
39538
  const mutations = [];
39338
39539
  const stamp = now();
39339
39540
  for (const [index, item] of items.entries()) {
39340
- const external_id_hash = sourceHash2(item.source);
39341
- const content_digest = contentDigest2(item.content);
39541
+ const external_id_hash = sourceIdentityHash(item.source);
39542
+ const content_digest = sourceContentDigest(item.content);
39342
39543
  const existing = await findSource(store, item.source);
39343
39544
  if (!existing) {
39344
39545
  const noteId = item.source.caller_stable_id ?? generateId();
39345
- outcomes.push({ index, outcome: "inserted", note_id: noteId, external_id_hash, content_digest });
39346
- if (!options.dryRun) {
39347
- const note2 = {
39348
- id: noteId,
39349
- archive_id: item.source.archive_id ?? null,
39350
- title: item.title ?? null,
39351
- format: item.format ?? "markdown",
39352
- source: `source:${item.source.namespace}`,
39353
- visibility: item.visibility ?? "private",
39354
- revision_mode: "standard",
39355
- is_starred: false,
39356
- is_pinned: false,
39357
- is_archived: false,
39358
- created_at: stamp,
39359
- updated_at: stamp,
39360
- deleted_at: null
39361
- };
39362
- const original = {
39363
- id: generateId(),
39364
- note_id: noteId,
39365
- content: item.content,
39366
- content_hash: content_digest,
39367
- created_at: stamp
39368
- };
39369
- const current2 = {
39370
- id: noteId,
39371
- content: item.content,
39372
- ai_metadata: item.metadata ?? null,
39373
- generation_count: 0,
39374
- model: null,
39375
- is_user_edited: false,
39376
- updated_at: stamp
39377
- };
39378
- const identity = {
39379
- id: generateId(),
39380
- tenant_id: item.source.tenant_id ?? "default",
39381
- archive_id: item.source.archive_id ?? null,
39382
- namespace: item.source.namespace,
39383
- external_id: item.source.external_id,
39384
- external_id_hash,
39385
- source_schema_version: item.source.source_schema_version,
39386
- content_digest,
39387
- import_run_id: item.source.import_run_id,
39388
- caller_stable_id: item.source.caller_stable_id ?? null,
39389
- note_id: noteId,
39390
- created_at: stamp,
39391
- updated_at: stamp
39392
- };
39393
- mutations.push(
39394
- { op: "put", collection: "note", record: note2 },
39395
- { op: "put", collection: "note_original", record: original },
39396
- { op: "put", collection: "note_revised_current", record: current2 },
39397
- { op: "put", collection: "source_identity", record: identity }
39398
- );
39546
+ if (await store.get("note", noteId)) {
39547
+ const rejected = items.map((candidate, candidateIndex) => ({
39548
+ index: candidateIndex,
39549
+ outcome: "rejected",
39550
+ external_id_hash: sourceIdentityHash(candidate.source),
39551
+ content_digest: sourceContentDigest(candidate.content),
39552
+ reason_code: "caller_stable_id_conflict"
39553
+ }));
39554
+ return finish2(importRunId, batchId, false, "rejected", rejected, options.checkpoint);
39399
39555
  }
39556
+ outcomes.push({ index, outcome: "inserted", note_id: noteId, external_id_hash, content_digest });
39557
+ if (!options.dryRun) addInsertMutations(mutations, item, noteId, content_digest, external_id_hash, stamp);
39400
39558
  continue;
39401
39559
  }
39402
39560
  if (existing.content_digest === content_digest) {
39403
39561
  outcomes.push({ index, outcome: "unchanged", note_id: existing.note_id, external_id_hash, content_digest });
39404
39562
  continue;
39405
39563
  }
39406
- const policy = item.policy ?? "version";
39564
+ const policy = item.policy ?? options.policy ?? "version";
39407
39565
  if (policy === "conflict") {
39408
39566
  outcomes.push({ index, outcome: "conflict", note_id: existing.note_id, external_id_hash, content_digest });
39409
39567
  continue;
39410
39568
  }
39411
39569
  const note = await store.get("note", existing.note_id);
39412
39570
  const current = await store.get("note_revised_current", existing.note_id);
39413
- if (!note || !current) {
39414
- outcomes.push({ index, outcome: "rejected", note_id: existing.note_id, external_id_hash, content_digest, reason: "source identity points to a missing note" });
39415
- continue;
39571
+ const originals = await store.list("note_original");
39572
+ const original = originals.find((candidate) => candidate.note_id === existing.note_id);
39573
+ if (!note || !current || !original) {
39574
+ const rejected = items.map((candidate, candidateIndex) => ({
39575
+ index: candidateIndex,
39576
+ outcome: "rejected",
39577
+ note_id: candidateIndex === index ? existing.note_id : void 0,
39578
+ external_id_hash: sourceIdentityHash(candidate.source),
39579
+ content_digest: sourceContentDigest(candidate.content),
39580
+ reason_code: "invalid_item"
39581
+ }));
39582
+ return finish2(importRunId, batchId, false, "rejected", rejected, options.checkpoint);
39416
39583
  }
39417
39584
  const outcome = policy === "replace" ? "replaced" : "versioned";
39418
39585
  outcomes.push({ index, outcome, note_id: existing.note_id, external_id_hash, content_digest });
39419
39586
  if (!options.dryRun) {
39420
- mutations.push(
39421
- {
39422
- op: "put",
39423
- collection: "note",
39424
- record: {
39425
- ...note,
39426
- title: item.title ?? null,
39427
- archive_id: item.source.archive_id ?? null,
39428
- format: item.format ?? "markdown",
39429
- visibility: item.visibility ?? "private",
39430
- deleted_at: null,
39431
- updated_at: stamp
39432
- }
39433
- },
39434
- {
39435
- op: "put",
39436
- collection: "note_revised_current",
39437
- record: { ...current, content: item.content, ai_metadata: item.metadata ?? null, is_user_edited: false, updated_at: stamp }
39438
- },
39439
- {
39440
- op: "put",
39441
- collection: "source_identity",
39442
- record: { ...existing, source_schema_version: item.source.source_schema_version, content_digest, import_run_id: item.source.import_run_id, updated_at: stamp }
39443
- }
39444
- );
39445
- }
39446
- }
39447
- if (!options.dryRun && hasMaterialChange2(outcomes)) {
39448
- const run = {
39449
- id: items[0].source.import_run_id,
39450
- tenant_id: items[0].source.tenant_id ?? "default",
39451
- archive_id: items[0].source.archive_id ?? null,
39452
- namespace: items[0].source.namespace,
39453
- started_at: stamp,
39454
- completed_at: stamp,
39455
- checkpoint: { item_count: items.length },
39456
- receipt: { counts: countOutcomes2(outcomes) }
39587
+ const revisionNumber = outcome === "versioned" ? (await store.list("note_revision")).filter((row) => row.note_id === existing.note_id).length + 1 : void 0;
39588
+ addUpdateMutations(mutations, item, existing, note, current, original, outcome, content_digest, stamp, revisionNumber);
39589
+ }
39590
+ }
39591
+ if (options.dryRun) return finish2(importRunId, batchId, true, "preview", outcomes, options.checkpoint);
39592
+ const committed = finish2(importRunId, batchId, false, "committed", outcomes, options.checkpoint);
39593
+ const contractReceipt = {
39594
+ contract_version: committed.contract_version,
39595
+ import_run_id: committed.import_run_id,
39596
+ batch_id: committed.batch_id,
39597
+ dry_run: committed.dry_run,
39598
+ outcome: committed.outcome,
39599
+ ...committed.checkpoint ? { checkpoint: committed.checkpoint } : {},
39600
+ counts: committed.counts,
39601
+ items: committed.items
39602
+ };
39603
+ const run = {
39604
+ id: sourceRunRecordId(items[0].source),
39605
+ external_run_id: importRunId,
39606
+ source_id: items[0].source.source_id ?? null,
39607
+ source_schema_version: items[0].source.source_schema_version,
39608
+ workspace_id: items[0].source.workspace_id ?? null,
39609
+ tenant_id: items[0].source.tenant_id ?? "default",
39610
+ archive_id: items[0].source.archive_id ?? null,
39611
+ namespace: items[0].source.namespace,
39612
+ started_at: stamp,
39613
+ completed_at: stamp,
39614
+ checkpoint: options.checkpoint ?? {},
39615
+ receipt: contractReceipt
39616
+ };
39617
+ const batch = {
39618
+ id: generateId(),
39619
+ tenant_id: items[0].source.tenant_id ?? "default",
39620
+ archive_id: items[0].source.archive_id ?? null,
39621
+ namespace: items[0].source.namespace,
39622
+ batch_id: batchId,
39623
+ request_digest: requestDigest,
39624
+ import_run_id: importRunId,
39625
+ outcome: "committed",
39626
+ checkpoint: options.checkpoint ?? {},
39627
+ receipt: contractReceipt,
39628
+ created_at: stamp
39629
+ };
39630
+ mutations.push(
39631
+ { op: "put", collection: "source_import_run", record: run },
39632
+ { op: "put", collection: "source_import_batch", record: batch }
39633
+ );
39634
+ await store.applyBatch(mutations);
39635
+ return committed;
39636
+ }
39637
+ function addInsertMutations(mutations, item, noteId, contentDigest, externalIdHash, stamp) {
39638
+ const note = {
39639
+ id: noteId,
39640
+ archive_id: item.source.archive_id ?? null,
39641
+ title: item.title ?? null,
39642
+ format: item.format ?? "markdown",
39643
+ source: `source:${item.source.namespace}`,
39644
+ visibility: item.visibility ?? "private",
39645
+ revision_mode: "standard",
39646
+ is_starred: false,
39647
+ is_pinned: false,
39648
+ is_archived: false,
39649
+ created_at: stamp,
39650
+ updated_at: stamp,
39651
+ deleted_at: null
39652
+ };
39653
+ const original = { id: generateId(), note_id: noteId, content: item.content, content_hash: contentDigest, created_at: stamp };
39654
+ const current = { id: noteId, content: item.content, ai_metadata: item.metadata ?? null, generation_count: 0, model: null, is_user_edited: false, updated_at: stamp };
39655
+ const revision = { id: generateId(), note_id: noteId, revision_number: 1, type: "source-import", content: item.content, ai_metadata: item.metadata ?? null, model: null, created_at: stamp };
39656
+ const identity = {
39657
+ id: generateId(),
39658
+ tenant_id: item.source.tenant_id ?? "default",
39659
+ archive_id: item.source.archive_id ?? null,
39660
+ namespace: item.source.namespace,
39661
+ external_id: item.source.external_id,
39662
+ external_id_hash: externalIdHash,
39663
+ source_id: item.source.source_id ?? null,
39664
+ source_schema_version: item.source.source_schema_version,
39665
+ content_digest: contentDigest,
39666
+ import_run_id: item.source.import_run_id,
39667
+ caller_stable_id: item.source.caller_stable_id ?? null,
39668
+ note_id: noteId,
39669
+ created_at: stamp,
39670
+ updated_at: stamp
39671
+ };
39672
+ mutations.push(
39673
+ { op: "put", collection: "note", record: note },
39674
+ { op: "put", collection: "note_original", record: original },
39675
+ { op: "put", collection: "note_revised_current", record: current },
39676
+ { op: "put", collection: "note_revision", record: revision },
39677
+ { op: "put", collection: "source_identity", record: identity }
39678
+ );
39679
+ }
39680
+ function addUpdateMutations(mutations, item, identity, note, current, original, outcome, contentDigest, stamp, revisionNumber) {
39681
+ mutations.push(
39682
+ { op: "put", collection: "note", record: { ...note, title: item.title ?? null, archive_id: item.source.archive_id ?? null, format: item.format ?? "markdown", visibility: item.visibility ?? "private", deleted_at: null, updated_at: stamp } },
39683
+ { op: "put", collection: "note_revised_current", record: { ...current, content: item.content, ai_metadata: item.metadata ?? null, is_user_edited: false, updated_at: stamp } },
39684
+ { op: "put", collection: "source_identity", record: { ...identity, source_id: item.source.source_id ?? null, source_schema_version: item.source.source_schema_version, content_digest: contentDigest, import_run_id: item.source.import_run_id, updated_at: stamp } }
39685
+ );
39686
+ if (outcome === "replaced") {
39687
+ mutations.push({ op: "put", collection: "note_original", record: { ...original, content: item.content, content_hash: contentDigest } });
39688
+ } else {
39689
+ const revision = {
39690
+ id: generateId(),
39691
+ note_id: identity.note_id,
39692
+ revision_number: revisionNumber ?? 1,
39693
+ type: "source-import",
39694
+ content: item.content,
39695
+ ai_metadata: item.metadata ?? null,
39696
+ model: null,
39697
+ created_at: stamp
39457
39698
  };
39458
- mutations.push({ op: "put", collection: "source_import_run", record: run });
39459
- await store.applyBatch(mutations);
39699
+ mutations.push({ op: "put", collection: "note_revision", record: revision });
39460
39700
  }
39461
- return {
39462
- import_run_id: items[0].source.import_run_id,
39463
- dry_run: options.dryRun === true,
39464
- outcomes,
39465
- counts: countOutcomes2(outcomes)
39466
- };
39467
39701
  }
39468
- function hasMaterialChange2(outcomes) {
39469
- return outcomes.some((outcome) => outcome.outcome === "inserted" || outcome.outcome === "versioned" || outcome.outcome === "replaced");
39702
+ function validate(items, maxItems, initialReason) {
39703
+ let reason = initialReason ?? (items.length === 0 || items.length > maxItems ? "batch_size_out_of_bounds" : null);
39704
+ const seen = /* @__PURE__ */ new Set();
39705
+ const stableIds = /* @__PURE__ */ new Set();
39706
+ for (const item of items) {
39707
+ if (!item.source.tenant_id || !item.source.namespace || !item.source.external_id || !item.source.source_schema_version || !item.source.import_run_id || !item.content) reason ??= "invalid_item";
39708
+ if ((item.source.source_id?.length ?? 0) > 500 || item.source.source_id === "" || (item.source.workspace_id?.length ?? 0) > 500 || item.source.workspace_id === "") reason ??= "invalid_batch_metadata";
39709
+ if ((item.format ?? "markdown").length > 100 || (item.title?.length ?? 0) > 2e3 || JSON.stringify(item.metadata ?? {}).length > 262144) reason ??= "invalid_item";
39710
+ if (item.content_digest && item.content_digest !== sourceContentDigest(item.content)) reason ??= "content_digest_mismatch";
39711
+ const key = `${item.source.tenant_id}\0${item.source.archive_id ?? ""}\0${item.source.namespace}\0${item.source.external_id}`;
39712
+ if (seen.has(key)) reason ??= "duplicate_external_id_in_batch";
39713
+ seen.add(key);
39714
+ if (item.source.caller_stable_id && stableIds.has(item.source.caller_stable_id)) reason ??= "caller_stable_id_conflict";
39715
+ if (item.source.caller_stable_id) stableIds.add(item.source.caller_stable_id);
39716
+ }
39717
+ if (!reason) return null;
39718
+ return items.map((item, index) => ({
39719
+ index,
39720
+ outcome: "rejected",
39721
+ external_id_hash: sourceIdentityHash(item.source),
39722
+ content_digest: sourceContentDigest(item.content ?? ""),
39723
+ reason_code: reason
39724
+ }));
39470
39725
  }
39471
39726
 
39472
39727
  // src/records/lifecycle-purge.ts
@@ -39586,8 +39841,8 @@ async function purgeRecordStoreGraph(store, selector, operationKey) {
39586
39841
  }
39587
39842
 
39588
39843
  // src/index.ts
39589
- var VERSION = "2026.9.0";
39844
+ var VERSION = "2026.9.1";
39590
39845
 
39591
- export { AIWG_SCAN_REQUIRED_FIELDS, AllowlistTrustStore, ArchiveManager, AttachmentsRepository, BridgeInferenceProvider, CORE_V1_COMPONENTS, CURRENT_MIGRATION_HEAD, CURRENT_SHARD_VERSION, CanonicalAttachmentsRepository, CanonicalNotesRepository, CapabilityManager, CaptureKnowledgeInputSchema, CollectionsRepository, CommunitiesRepository, DATASET_EXECUTION_CAPABILITY_IDS, DATASET_EXECUTION_CONTRACT, DATASET_EXECUTION_SCHEMA_VERSION, DATASET_INGEST_CONTRACT, DATASET_INGEST_SCHEMA_VERSION, DATASET_LINEAGE_CONTRACT, DATASET_LINEAGE_SCHEMA_VERSION, DATASET_MATERIALIZATION_CONTRACT, DATASET_MATERIALIZATION_KINDS, DATASET_MATERIALIZATION_SCHEMA_VERSION, DB_SNAPSHOT_SCHEMA_VERSION, DEFAULT_LARGE_DOCUMENT_CHARS, DEFAULT_LARGE_DOCUMENT_CHUNKS, DatasetIngestError, DatasetIngestExecutor, DatasetLineageLedger, DatasetMaterializationError, DbSnapshotVersionError, EMBED_REQUEST_KIND, EMBED_RESPONSE_KIND, EmbeddingSetsRepository, FORTEMI_BROWSER_LOCAL_DATASET_EXECUTION_DESCRIPTOR, FORTEMI_COMPATIBILITY_PATH, FORTEMI_COMPATIBILITY_STATES, FORTEMI_PORTABLE_SHARD_DATASET_EXECUTION_DESCRIPTOR, FORTEMI_REQUIRED_COMPATIBILITY_CAPABILITIES, FORTEMI_SERVER_COMPATIBILITY_REVISION, FORTEMI_STATIC_CACHE_DATASET_EXECUTION_DESCRIPTOR, FallbackRouter, FortemiToolManifest, GetNoteInputSchema, GraphRepository, IdbRecordStore, JOB_CAPABILITIES, JOB_PRIORITIES, JobQueueWorker, LINEAGE_ENTITY_KINDS, LINEAGE_RELATIONSHIP_KINDS, LOCAL_ENDPOINTS, LifecyclePurgeRepository, LineageValidationError, LinksRepository, ListNotesInputSchema, ManageArchiveInputSchema, ManageAttachmentsInputSchema, ManageCapabilitiesInputSchema, ManageCollectionsInputSchema, ManageLinksInputSchema, ManageNoteInputSchema, ManageTagsInputSchema, MemoryBlobStore, MemoryDatasetIngestStore, MemoryRecordStore, MigrationRunner, NotesRepository, OpenAICompatibleProvider, PGliteStorageBackend, PGliteStorageBackendFactory, PGliteWorkerClient, PGliteWorkerStorageBackend, PGliteWorkerStorageBackendFactory, ProvenanceRepository, ProviderRegistry, RECORD_COLLECTIONS, RECORD_SCHEMA_VERSION, RECORD_STORE_CAPABILITIES, REGISTERED_METADATA_PATHS, SHARD_FORMAT, SIGNATURE_ENTRY, SIGNING_ENVELOPE_VERSION, SUPPORTED_PGLITE_VERSION, SearchInputSchema, SearchRepository, SkosRepository, SourceUpsertRepository, TagsRepository, TransactionProxy, TypedEventBus, VERSION, aiRevisionHandler, aiwgFortemiIndexFromKnowledgeShard, aiwgFortemiIndexToCommunityGraph, aiwgFortemiIndexToKnowledgeShard, aiwgFortemiIndexToKnowledgeShardWithReport, allMigrations, appendPluginScript, assertAiwgFortemiChunkManifest, assertAiwgFortemiChunkPart, assertAiwgFortemiIndexExport, assertAiwgStaticEmbeddingSet, assertShardComponentRecord, buildAiwgChunkedIndex, buildAiwgStaticEmbeddingSet, buildMetadataPredicateConditions, buildNoteConditions, buildPluginCsp, captureKnowledge, chunkText, classifyError, classifyModel, clearPrefetchedShard, collectionFromShard, collectionToShard, compareDatasetIncrementalParity, computeBlobHash, computeHash, computeLineageDigest, computeSri, conceptTaggingHandler, configureInferenceRuntime, cosineSimilarity, createAiwgFetchChunkLoader, createAiwgFetchDetailLoader, createAiwgIndexController, createAiwgReviewDecisionExport, createBlobStore, createBridgeInferenceProviders, createCosineSemanticProvider, createCspReportHandler, createFortemi, createLazyBlobStore, createLegacyProvider, createLocalProviderProfile, createPGliteBackend, createPGliteInstance, createRecordBackend, createRecordStore, createRemoteBackend, createRoutes, createShardBackend, createShardCapabilityReport, createWorkerEmbedFunction, datasetDestinationScopeKey, defaultStorageBackendFactory, defineInferenceProvider, defineInferenceRuntime, defineLegacyInferenceProvider, defineOpenAICompatibleProvider, deriveDatasetIngestIdempotencyKey, detectCommunities, detectGpuCapabilities, detectInferenceCapabilities, digestDatasetMaterializationValue, discoverLocalProviders, dropAttachmentProjection, dropNoteProjection, dumpDbSnapshot, embeddingConfigToShard, embeddingFromShard, embeddingGenerationHandler, embeddingSetFromShard, embeddingSetMemberToShard, embeddingSetToShard, embeddingToShard, enqueueFullWorkflow, enqueueJob, enqueueNoteCreationJobs, estimateModelFit, estimateVramMB, estimateVramTier, executeDatasetMaterialization, executeDatasetRetrieval, exportShard, exportShardFromRecords, exportShardFromRecordsWithReport, exportShardWithReport, fetchAndValidateFortemiCompatibility, fetchPluginScript, filterAiwgRecordsByPrivacy, findAiwgStaticDuplicatePairs, formatFortemiCompatibilitySummary, fortemiCompatibilityUrl, fortemiManifest, fromPrefetched, generateId, getAiwgFortemiFacets, getAiwgFortemiIndexExportSchema, getConfiguredInferenceProviderId, getEmbedFunction, getEmbeddingTaskSelectionOptions, getFortemiBridge, getFortemiSecretStore, getJobQueueStatus, getKnowledgeShardContractReceipt, getKnowledgeShardProfileRegistry, getKnowledgeShardSchema, getLlmFunction, getNote, getPrefetchedSha256, getProviderRouteRequirementIssue, handleEmbedRequests, hasFortemiSecureSecrets, importShard, importShardToRecords, inferInferenceTaskCapability, inferLocalEmbeddingDimensions, isPluginScriptAllowed, isShardPrefetched, isShardSigningSupported, linkFromShard, linkToShard, linkingHandler, listNotes, manageArchive, manageAttachments, manageCapabilities, manageCollections, manageLinks, manageNote, manageTags, matchRoute, mergeInferenceRuntimeConfigs, migrateLegacyBlobStore, negotiateDatasetExecutionCapabilities, negotiateDatasetMaterializationProfile, noteFromShard, noteSkosTagToShard, noteToShard, openShard, packTarGz, parseCspReport, prefetchShard, previewRecordStorePurge, profileSupportError, projectAttachments, projectNotes, projectRecords, provenanceEdgeToShard, providerSatisfiesRouteRequirements, purgeRecordStoreGraph, queryAiwgFortemiIndex, queryAiwgHybridIndex, queryAiwgSemanticIndex, registerLlmCapability, registerSemanticCapability, registerSemanticCapabilityWorker, registerServiceWorker, restoreDbSnapshot, searchTool, selectBackend, selectEmbeddingTask, selectLlmModel, setEmbedFunction, setEmbeddingTaskSelectionOptions, setLlmFunction, sha256Hex, sidecarBlobDigests, signShard, skosConceptToShard, skosRelationToShard, skosSchemeToShard, suggestTags, tagsToShard, templateToShard, titleGenerationHandler, unpackTarGz, unregisterLlmCapability, unregisterSemanticCapability, upsertRecordStoreSources, urlLinkToShard, validateAiwgFortemiChunkManifest, validateAiwgFortemiChunkPart, validateAiwgFortemiIndexExport, validateAiwgFortemiIndexExportSchema, validateAiwgFortemiProjectedRecordSchema, validateAiwgStaticEmbeddingSet, validateChecksums, validateCoreV1ShardArchive, validateDatasetBenchmarkEvidence, validateDatasetExecutionDescriptor, validateFortemiCompatibilityResponse, validateFullV1ShardArchive, validateProviderRoute, validateRecordV1ShardArchive, validateShardArchive, validateShardComponentRecord, validateShardManifest, verifyDbSnapshotMeta, verifyShardSignature, verifySri };
39846
+ export { AIWG_SCAN_REQUIRED_FIELDS, AllowlistTrustStore, ArchiveManager, AttachmentsRepository, BridgeInferenceProvider, CORE_V1_COMPONENTS, CURRENT_MIGRATION_HEAD, CURRENT_SHARD_VERSION, CanonicalAttachmentsRepository, CanonicalNotesRepository, CapabilityManager, CaptureKnowledgeInputSchema, CollectionsRepository, CommunitiesRepository, DATASET_EXECUTION_CAPABILITY_IDS, DATASET_EXECUTION_CONTRACT, DATASET_EXECUTION_SCHEMA_VERSION, DATASET_INGEST_CONTRACT, DATASET_INGEST_SCHEMA_VERSION, DATASET_LINEAGE_CONTRACT, DATASET_LINEAGE_SCHEMA_VERSION, DATASET_MATERIALIZATION_CONTRACT, DATASET_MATERIALIZATION_KINDS, DATASET_MATERIALIZATION_SCHEMA_VERSION, DB_SNAPSHOT_SCHEMA_VERSION, DEFAULT_LARGE_DOCUMENT_CHARS, DEFAULT_LARGE_DOCUMENT_CHUNKS, DatasetIngestError, DatasetIngestExecutor, DatasetLineageLedger, DatasetMaterializationError, DbSnapshotVersionError, EMBED_REQUEST_KIND, EMBED_RESPONSE_KIND, EmbeddingSetsRepository, FORTEMI_BROWSER_LOCAL_DATASET_EXECUTION_DESCRIPTOR, FORTEMI_COMPATIBILITY_PATH, FORTEMI_COMPATIBILITY_STATES, FORTEMI_PORTABLE_SHARD_DATASET_EXECUTION_DESCRIPTOR, FORTEMI_REQUIRED_COMPATIBILITY_CAPABILITIES, FORTEMI_SERVER_COMPATIBILITY_REVISION, FORTEMI_STATIC_CACHE_DATASET_EXECUTION_DESCRIPTOR, FallbackRouter, FortemiToolManifest, GetNoteInputSchema, GraphRepository, IdbRecordStore, JOB_CAPABILITIES, JOB_PRIORITIES, JobQueueWorker, LINEAGE_ENTITY_KINDS, LINEAGE_RELATIONSHIP_KINDS, LOCAL_ENDPOINTS, LifecyclePurgeRepository, LineageValidationError, LinksRepository, ListNotesInputSchema, ManageArchiveInputSchema, ManageAttachmentsInputSchema, ManageCapabilitiesInputSchema, ManageCollectionsInputSchema, ManageLinksInputSchema, ManageNoteInputSchema, ManageTagsInputSchema, MemoryBlobStore, MemoryDatasetIngestStore, MemoryRecordStore, MigrationRunner, NotesRepository, OpenAICompatibleProvider, PGliteStorageBackend, PGliteStorageBackendFactory, PGliteWorkerClient, PGliteWorkerStorageBackend, PGliteWorkerStorageBackendFactory, ProvenanceRepository, ProviderRegistry, RECORD_COLLECTIONS, RECORD_SCHEMA_VERSION, RECORD_STORE_CAPABILITIES, REGISTERED_METADATA_PATHS, SHARD_FORMAT, SIGNATURE_ENTRY, SIGNING_ENVELOPE_VERSION, SOURCE_UPSERT_CONTRACT_VERSION, SOURCE_UPSERT_MAX_ITEMS, SUPPORTED_PGLITE_VERSION, SearchInputSchema, SearchRepository, SkosRepository, SourceUpsertRepository, TagsRepository, TransactionProxy, TypedEventBus, VERSION, aiRevisionHandler, aiwgFortemiIndexFromKnowledgeShard, aiwgFortemiIndexToCommunityGraph, aiwgFortemiIndexToKnowledgeShard, aiwgFortemiIndexToKnowledgeShardWithReport, allMigrations, appendPluginScript, assertAiwgFortemiChunkManifest, assertAiwgFortemiChunkPart, assertAiwgFortemiIndexExport, assertAiwgStaticEmbeddingSet, assertShardComponentRecord, buildAiwgChunkedIndex, buildAiwgStaticEmbeddingSet, buildMetadataPredicateConditions, buildNoteConditions, buildPluginCsp, captureKnowledge, chunkText, classifyError, classifyModel, clearPrefetchedShard, collectionFromShard, collectionToShard, compareDatasetIncrementalParity, computeBlobHash, computeHash, computeLineageDigest, computeSri, conceptTaggingHandler, configureInferenceRuntime, cosineSimilarity, createAiwgFetchChunkLoader, createAiwgFetchDetailLoader, createAiwgIndexController, createAiwgReviewDecisionExport, createBlobStore, createBridgeInferenceProviders, createCosineSemanticProvider, createCspReportHandler, createFortemi, createLazyBlobStore, createLegacyProvider, createLocalProviderProfile, createPGliteBackend, createPGliteInstance, createRecordBackend, createRecordStore, createRemoteBackend, createRoutes, createShardBackend, createShardCapabilityReport, createWorkerEmbedFunction, datasetDestinationScopeKey, defaultStorageBackendFactory, defineInferenceProvider, defineInferenceRuntime, defineLegacyInferenceProvider, defineOpenAICompatibleProvider, deriveDatasetIngestIdempotencyKey, detectCommunities, detectGpuCapabilities, detectInferenceCapabilities, digestDatasetMaterializationValue, discoverLocalProviders, dropAttachmentProjection, dropNoteProjection, dumpDbSnapshot, embeddingConfigToShard, embeddingFromShard, embeddingGenerationHandler, embeddingSetFromShard, embeddingSetMemberToShard, embeddingSetToShard, embeddingToShard, enqueueFullWorkflow, enqueueJob, enqueueNoteCreationJobs, estimateModelFit, estimateVramMB, estimateVramTier, executeDatasetMaterialization, executeDatasetRetrieval, exportShard, exportShardFromRecords, exportShardFromRecordsWithReport, exportShardWithReport, fetchAndValidateFortemiCompatibility, fetchPluginScript, filterAiwgRecordsByPrivacy, findAiwgStaticDuplicatePairs, formatFortemiCompatibilitySummary, fortemiCompatibilityUrl, fortemiManifest, fromPrefetched, generateId, getAiwgFortemiFacets, getAiwgFortemiIndexExportSchema, getConfiguredInferenceProviderId, getEmbedFunction, getEmbeddingTaskSelectionOptions, getFortemiBridge, getFortemiSecretStore, getJobQueueStatus, getKnowledgeShardContractReceipt, getKnowledgeShardProfileRegistry, getKnowledgeShardSchema, getLlmFunction, getNote, getPrefetchedSha256, getProviderRouteRequirementIssue, handleEmbedRequests, hasFortemiSecureSecrets, importShard, importShardToRecords, inferInferenceTaskCapability, inferLocalEmbeddingDimensions, isPluginScriptAllowed, isShardPrefetched, isShardSigningSupported, linkFromShard, linkToShard, linkingHandler, listNotes, manageArchive, manageAttachments, manageCapabilities, manageCollections, manageLinks, manageNote, manageTags, matchRoute, mergeInferenceRuntimeConfigs, migrateLegacyBlobStore, negotiateDatasetExecutionCapabilities, negotiateDatasetMaterializationProfile, noteFromShard, noteSkosTagToShard, noteToShard, openShard, packTarGz, parseCspReport, prefetchShard, previewRecordStorePurge, profileSupportError, projectAttachments, projectNotes, projectRecords, provenanceEdgeToShard, providerSatisfiesRouteRequirements, purgeRecordStoreGraph, queryAiwgFortemiIndex, queryAiwgHybridIndex, queryAiwgSemanticIndex, registerLlmCapability, registerSemanticCapability, registerSemanticCapabilityWorker, registerServiceWorker, restoreDbSnapshot, searchTool, selectBackend, selectEmbeddingTask, selectLlmModel, setEmbedFunction, setEmbeddingTaskSelectionOptions, setLlmFunction, sha256Hex, sidecarBlobDigests, signShard, skosConceptToShard, skosRelationToShard, skosSchemeToShard, suggestTags, tagsToShard, templateToShard, titleGenerationHandler, unpackTarGz, unregisterLlmCapability, unregisterSemanticCapability, upsertRecordStoreRequest, upsertRecordStoreSources, urlLinkToShard, validateAiwgFortemiChunkManifest, validateAiwgFortemiChunkPart, validateAiwgFortemiIndexExport, validateAiwgFortemiIndexExportSchema, validateAiwgFortemiProjectedRecordSchema, validateAiwgStaticEmbeddingSet, validateChecksums, validateCoreV1ShardArchive, validateDatasetBenchmarkEvidence, validateDatasetExecutionDescriptor, validateFortemiCompatibilityResponse, validateFullV1ShardArchive, validateProviderRoute, validateRecordV1ShardArchive, validateShardArchive, validateShardComponentRecord, validateShardManifest, verifyDbSnapshotMeta, verifyShardSignature, verifySri };
39592
39847
  //# sourceMappingURL=index.js.map
39593
39848
  //# sourceMappingURL=index.js.map