@opendatalabs/personal-server-ts-server 1.7.0 → 1.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/dist/api-auth.d.ts.map +1 -1
  2. package/dist/api-auth.js +1 -0
  3. package/dist/api-auth.js.map +1 -1
  4. package/dist/app.d.ts +12 -1
  5. package/dist/app.d.ts.map +1 -1
  6. package/dist/app.js +31 -3
  7. package/dist/app.js.map +1 -1
  8. package/dist/bootstrap.d.ts +15 -0
  9. package/dist/bootstrap.d.ts.map +1 -1
  10. package/dist/bootstrap.js +85 -2
  11. package/dist/bootstrap.js.map +1 -1
  12. package/dist/logging/access-log.d.ts +1 -1
  13. package/dist/logging/access-log.d.ts.map +1 -1
  14. package/dist/pending-blob-deletions.d.ts +10 -0
  15. package/dist/pending-blob-deletions.d.ts.map +1 -0
  16. package/dist/pending-blob-deletions.js +38 -0
  17. package/dist/pending-blob-deletions.js.map +1 -0
  18. package/dist/routes/data.d.ts +9 -1
  19. package/dist/routes/data.d.ts.map +1 -1
  20. package/dist/routes/data.js +2 -0
  21. package/dist/routes/data.js.map +1 -1
  22. package/dist/routes/derivatives.d.ts +33 -0
  23. package/dist/routes/derivatives.d.ts.map +1 -0
  24. package/dist/routes/derivatives.js +33 -0
  25. package/dist/routes/derivatives.js.map +1 -0
  26. package/dist/storage/index-manager.d.ts.map +1 -1
  27. package/dist/storage/index-manager.js +4 -2
  28. package/dist/storage/index-manager.js.map +1 -1
  29. package/dist/storage/index-schema.d.ts +1 -1
  30. package/dist/storage/index-schema.d.ts.map +1 -1
  31. package/dist/storage/index-schema.js +9 -2
  32. package/dist/storage/index-schema.js.map +1 -1
  33. package/dist/storage/node-data-storage.d.ts.map +1 -1
  34. package/dist/storage/node-data-storage.js +10 -0
  35. package/dist/storage/node-data-storage.js.map +1 -1
  36. package/dist/storage/question-store.d.ts +9 -0
  37. package/dist/storage/question-store.d.ts.map +1 -0
  38. package/dist/storage/question-store.js +114 -0
  39. package/dist/storage/question-store.js.map +1 -0
  40. package/dist/ui/ps-lite-debug.js +2810 -210
  41. package/package.json +3 -3
@@ -105697,6 +105697,11 @@ var ServerNotConfiguredError = class extends ProtocolError {
105697
105697
  super(500, "SERVER_NOT_CONFIGURED", "Server is not configured", details);
105698
105698
  }
105699
105699
  };
105700
+ var ContentTooLargeError = class extends ProtocolError {
105701
+ constructor(details) {
105702
+ super(413, "CONTENT_TOO_LARGE", "Content too large", details);
105703
+ }
105704
+ };
105700
105705
  var LineageInvalidError = class extends ProtocolError {
105701
105706
  constructor(message, details) {
105702
105707
  super(400, "LINEAGE_INVALID", message, details);
@@ -105729,7 +105734,7 @@ var LineageGatewayError = class extends ProtocolError {
105729
105734
  };
105730
105735
  var LineageCascadeUnavailableError = class extends ProtocolError {
105731
105736
  constructor(details) {
105732
- super(501, "LINEAGE_CASCADE_UNAVAILABLE", "DELETE ?cascade=lineage is specified but not implemented yet: it needs the durable (tombstone) deletion of every derivative at the gateway, which this server cannot perform. Delete scopes one at a time.", details);
105737
+ super(501, "LINEAGE_CASCADE_UNAVAILABLE", "DELETE ?cascade=lineage is specified but not implemented yet: the lineage walk that finds every derivative is missing. Delete scopes one at a time; each single-scope delete is durable.", details);
105733
105738
  }
105734
105739
  };
105735
105740
  var InvalidCascadeError = class extends ProtocolError {
@@ -105737,6 +105742,41 @@ var InvalidCascadeError = class extends ProtocolError {
105737
105742
  super(400, "INVALID_CASCADE", 'Unsupported cascade mode; the only supported value is "lineage"', details);
105738
105743
  }
105739
105744
  };
105745
+ var DeleteTombstoneFailedError = class extends ProtocolError {
105746
+ constructor(details) {
105747
+ super(502, "DELETE_TOMBSTONE_FAILED", "Gateway did not acknowledge the deletion tombstone; nothing was deleted", details);
105748
+ }
105749
+ };
105750
+ var DataDeletedError = class extends ProtocolError {
105751
+ constructor(details) {
105752
+ super(410, "DATA_DELETED", "Data point has been deleted", details);
105753
+ }
105754
+ };
105755
+ var DerivativeQuestionInvalidError = class extends ProtocolError {
105756
+ constructor(message, details) {
105757
+ super(400, "DERIVATIVE_QUESTION_INVALID", message, details);
105758
+ }
105759
+ };
105760
+ var DerivativeQuestionNotFoundError = class extends ProtocolError {
105761
+ constructor(details) {
105762
+ super(404, "DERIVATIVE_QUESTION_NOT_FOUND", "Question not found", details);
105763
+ }
105764
+ };
105765
+ var DerivativeCycleError = class extends ProtocolError {
105766
+ constructor(details) {
105767
+ super(409, "DERIVATIVE_CYCLE", `Registering this question would make "${details.derivedScope}" a transitive source of itself; recompute would never settle`, details);
105768
+ }
105769
+ };
105770
+ var DerivativeSourceNotGrantedError = class extends ProtocolError {
105771
+ constructor(details) {
105772
+ super(403, "DERIVATIVE_SOURCE_NOT_GRANTED", "The builder's grant does not cover reading every source scope of this question", details);
105773
+ }
105774
+ };
105775
+ var DerivativeComputeUnavailableError = class extends ProtocolError {
105776
+ constructor(details) {
105777
+ super(503, "DERIVATIVE_COMPUTE_UNAVAILABLE", "This server has no derivative compute configured", details);
105778
+ }
105779
+ };
105740
105780
 
105741
105781
  // ../core/dist/sync/data-point-id.js
105742
105782
  function computeDataPointId(ownerAddress, scope) {
@@ -109344,6 +109384,7 @@ async function createPersistentPsLiteStorage(adapter, persistence = createIndexe
109344
109384
  schemaId: entry.schemaId ?? null,
109345
109385
  version: version4,
109346
109386
  dataPointId: entry.dataPointId ?? null,
109387
+ afterTombstoneVersion: entry.afterTombstoneVersion ?? null,
109347
109388
  id: state.nextId,
109348
109389
  createdAt: (/* @__PURE__ */ new Date()).toISOString()
109349
109390
  };
@@ -109437,25 +109478,34 @@ async function createPersistentPsLiteStorage(adapter, persistence = createIndexe
109437
109478
  await persist();
109438
109479
  return deleted;
109439
109480
  },
109481
+ async deleteVersion(scope, collectedAt2) {
109482
+ const entry = state.entries.find((e10) => e10.scope === scope && e10.collectedAt === collectedAt2);
109483
+ if (!entry)
109484
+ return false;
109485
+ return removeEntry(entry);
109486
+ },
109440
109487
  async deleteByFileId(fileId) {
109441
109488
  const entry = state.entries.find((e10) => e10.fileId === fileId);
109442
109489
  if (!entry)
109443
109490
  return false;
109444
- const blobPath = envelopePath(entry.scope, entry.collectedAt);
109445
- await Promise.all([
109446
- fileStore.deleteEnvelope(blobPath),
109447
- fallbackStore.deleteEnvelope(blobPath),
109448
- fileStore.deleteBlockTree?.(blockTreePath(entry.scope, entry.collectedAt)) ?? Promise.resolve(),
109449
- fallbackStore.deleteBlockTree?.(blockTreePath(entry.scope, entry.collectedAt)) ?? Promise.resolve()
109450
- ]);
109451
- state = {
109452
- ...state,
109453
- entries: state.entries.filter((e10) => e10 !== entry)
109454
- };
109455
- await persist();
109456
- return true;
109491
+ return removeEntry(entry);
109457
109492
  }
109458
109493
  };
109494
+ async function removeEntry(entry) {
109495
+ const blobPath = envelopePath(entry.scope, entry.collectedAt);
109496
+ await Promise.all([
109497
+ fileStore.deleteEnvelope(blobPath),
109498
+ fallbackStore.deleteEnvelope(blobPath),
109499
+ fileStore.deleteBlockTree?.(blockTreePath(entry.scope, entry.collectedAt)) ?? Promise.resolve(),
109500
+ fallbackStore.deleteBlockTree?.(blockTreePath(entry.scope, entry.collectedAt)) ?? Promise.resolve()
109501
+ ]);
109502
+ state = {
109503
+ ...state,
109504
+ entries: state.entries.filter((e10) => e10 !== entry)
109505
+ };
109506
+ await persist();
109507
+ return true;
109508
+ }
109459
109509
  if (fileStore.readEnvelopePreview) {
109460
109510
  storagePort.readEnvelopePreview = async (scope, collectedAt2, { maxBytes }) => {
109461
109511
  const path = envelopePath(scope, collectedAt2);
@@ -109699,6 +109749,17 @@ var DEFAULTS = {
109699
109749
  enabled: true,
109700
109750
  serverAddr: "frpc.server.vana.org",
109701
109751
  serverPort: 7e3
109752
+ },
109753
+ inference: {
109754
+ // OpenAI-compatible chat completions endpoint the derivative compute
109755
+ // layer calls. Point it at the Vana inference relay (which holds the
109756
+ // provider key) or straight at a provider for local development.
109757
+ baseUrl: "https://inference.phala.com/v1",
109758
+ model: "z-ai/glm-5.2",
109759
+ // Newest-first items kept per source scope when a prompt is assembled.
109760
+ maxSourceItems: 50,
109761
+ // Quiet period after a source scope changes before a recompute starts.
109762
+ recomputeDebounceMs: 5e3
109702
109763
  }
109703
109764
  };
109704
109765
  var StorageBackend = external_exports.enum([
@@ -109752,7 +109813,13 @@ var ServerConfigSchema = external_exports.object({
109752
109813
  enabled: external_exports.boolean().default(DEFAULTS.tunnel.enabled),
109753
109814
  serverAddr: external_exports.string().default(DEFAULTS.tunnel.serverAddr),
109754
109815
  serverPort: external_exports.number().int().min(1).max(65535).default(DEFAULTS.tunnel.serverPort)
109755
- }).default(DEFAULTS.tunnel)
109816
+ }).default(DEFAULTS.tunnel),
109817
+ inference: external_exports.object({
109818
+ baseUrl: external_exports.url().default(DEFAULTS.inference.baseUrl),
109819
+ model: external_exports.string().min(1).default(DEFAULTS.inference.model),
109820
+ maxSourceItems: external_exports.number().int().min(1).max(1e4).default(DEFAULTS.inference.maxSourceItems),
109821
+ recomputeDebounceMs: external_exports.number().int().min(0).max(36e5).default(DEFAULTS.inference.recomputeDebounceMs)
109822
+ }).default(DEFAULTS.inference)
109756
109823
  });
109757
109824
 
109758
109825
  // ../lite/dist/state.js
@@ -110743,15 +110810,44 @@ function normalizeOffset(value) {
110743
110810
  function isRecord3(value) {
110744
110811
  return value !== null && typeof value === "object" && !Array.isArray(value);
110745
110812
  }
110813
+ var VISIBILITY_PAGE_SIZE = 200;
110746
110814
  async function listDataScopesContract(input) {
110747
110815
  const limit = normalizeLimit(input.limit);
110748
110816
  const offset = normalizeOffset(input.offset);
110749
- const result = input.storage.listScopes({
110750
- scopePrefix: input.scopePrefix,
110751
- limit,
110752
- offset
110753
- });
110754
- const scopes = await Promise.all(result.scopes.map(async (summary) => {
110817
+ let page;
110818
+ let total;
110819
+ if (input.isVisible) {
110820
+ const visible = [];
110821
+ for (let scan = 0; ; scan += VISIBILITY_PAGE_SIZE) {
110822
+ const batch = input.storage.listScopes({
110823
+ scopePrefix: input.scopePrefix,
110824
+ limit: VISIBILITY_PAGE_SIZE,
110825
+ offset: scan
110826
+ });
110827
+ for (const summary of batch.scopes) {
110828
+ const latest = input.storage.findEntry({
110829
+ scope: summary.scope,
110830
+ at: summary.latestCollectedAt
110831
+ });
110832
+ if (!latest || await input.isVisible(summary.scope, latest)) {
110833
+ visible.push(summary);
110834
+ }
110835
+ }
110836
+ if (batch.scopes.length < VISIBILITY_PAGE_SIZE)
110837
+ break;
110838
+ }
110839
+ total = visible.length;
110840
+ page = visible.slice(offset, offset + limit);
110841
+ } else {
110842
+ const result = input.storage.listScopes({
110843
+ scopePrefix: input.scopePrefix,
110844
+ limit,
110845
+ offset
110846
+ });
110847
+ page = result.scopes;
110848
+ total = result.total;
110849
+ }
110850
+ const scopes = await Promise.all(page.map(async (summary) => {
110755
110851
  const entry = input.storage.findEntry({
110756
110852
  scope: summary.scope,
110757
110853
  at: summary.latestCollectedAt
@@ -110759,7 +110855,7 @@ async function listDataScopesContract(input) {
110759
110855
  if (!entry) {
110760
110856
  return summary;
110761
110857
  }
110762
- const hasBlocks = typeof input.storage.canReadScopeBlocks === "function" ? await input.storage.canReadScopeBlocks(summary.scope, summary.latestCollectedAt) : typeof input.storage.hasScopeBlocks === "function" ? await input.storage.hasScopeBlocks(summary.scope, summary.latestCollectedAt) : false;
110858
+ const hasBlocks = typeof input.storage.hasScopeBlocks === "function" ? await input.storage.hasScopeBlocks(summary.scope, summary.latestCollectedAt) : false;
110763
110859
  return {
110764
110860
  ...summary,
110765
110861
  dataStatus: hasBlocks ? "ready" : "indexing",
@@ -110770,33 +110866,52 @@ async function listDataScopesContract(input) {
110770
110866
  ok: true,
110771
110867
  response: {
110772
110868
  scopes,
110773
- total: result.total,
110869
+ total,
110774
110870
  limit,
110775
110871
  offset
110776
110872
  }
110777
110873
  };
110778
110874
  }
110779
- function listDataVersionsContract(input) {
110875
+ async function listDataVersionsContract(input) {
110780
110876
  const scopeResult = parseDataScopeContract(input.scopeParam);
110781
110877
  if (!scopeResult.ok)
110782
110878
  return scopeResult;
110783
110879
  const limit = normalizeLimit(input.limit);
110784
110880
  const offset = normalizeOffset(input.offset);
110785
- const entries = input.storage.listVersions(scopeResult.scope, {
110786
- limit,
110787
- offset
110788
- });
110881
+ let page;
110882
+ let total;
110883
+ if (input.isVisible) {
110884
+ const visible = [];
110885
+ for (let scan = 0; ; scan += VISIBILITY_PAGE_SIZE) {
110886
+ const batch = input.storage.listVersions(scopeResult.scope, {
110887
+ limit: VISIBILITY_PAGE_SIZE,
110888
+ offset: scan
110889
+ });
110890
+ for (const entry of batch) {
110891
+ if (await input.isVisible(scopeResult.scope, entry)) {
110892
+ visible.push(entry);
110893
+ }
110894
+ }
110895
+ if (batch.length < VISIBILITY_PAGE_SIZE)
110896
+ break;
110897
+ }
110898
+ total = visible.length;
110899
+ page = visible.slice(offset, offset + limit);
110900
+ } else {
110901
+ page = input.storage.listVersions(scopeResult.scope, { limit, offset });
110902
+ total = input.storage.countVersions(scopeResult.scope);
110903
+ }
110789
110904
  return {
110790
110905
  ok: true,
110791
110906
  scope: scopeResult.scope,
110792
110907
  response: {
110793
110908
  scope: scopeResult.scope,
110794
- versions: entries.map((entry) => ({
110909
+ versions: page.map((entry) => ({
110795
110910
  fileId: entry.fileId,
110796
110911
  schemaId: entry.schemaId,
110797
110912
  collectedAt: entry.collectedAt
110798
110913
  })),
110799
- total: input.storage.countVersions(scopeResult.scope),
110914
+ total,
110800
110915
  limit,
110801
110916
  offset
110802
110917
  }
@@ -110873,7 +110988,8 @@ async function ingestDataContract(input) {
110873
110988
  path: writeResult.relativePath,
110874
110989
  scope: scopeResult.scope,
110875
110990
  collectedAt: input.collectedAt,
110876
- sizeBytes: writeResult.sizeBytes
110991
+ sizeBytes: writeResult.sizeBytes,
110992
+ afterTombstoneVersion: input.afterTombstoneVersion ?? null
110877
110993
  });
110878
110994
  return {
110879
110995
  ok: true,
@@ -110944,7 +111060,8 @@ async function ingestBinaryDataContract(input) {
110944
111060
  path: writeResult.relativePath,
110945
111061
  scope: scopeResult.scope,
110946
111062
  collectedAt: input.collectedAt,
110947
- sizeBytes: input.bytes.length
111063
+ sizeBytes: input.bytes.length,
111064
+ afterTombstoneVersion: input.afterTombstoneVersion ?? null
110948
111065
  });
110949
111066
  return {
110950
111067
  ok: true,
@@ -110954,15 +111071,6 @@ async function ingestBinaryDataContract(input) {
110954
111071
  writeResult
110955
111072
  };
110956
111073
  }
110957
- async function deleteDataScopeContract(input) {
110958
- const scopeResult = parseDataScopeContract(input.scopeParam);
110959
- if (!scopeResult.ok)
110960
- return scopeResult;
110961
- return {
110962
- ok: true,
110963
- deletedCount: await input.storage.deleteScope(scopeResult.scope)
110964
- };
110965
- }
110966
111074
  async function writeBlockSidecars(storage, envelope) {
110967
111075
  if (!storage.writeBlockManifest)
110968
111076
  return;
@@ -111548,6 +111656,548 @@ async function syncFileContract(input) {
111548
111656
  return contractOk({ fileId: input.fileId, status: "started" }, 202);
111549
111657
  }
111550
111658
 
111659
+ // ../core/dist/sync/tombstone.js
111660
+ var TOMBSTONE_DATA_HASH_LABEL = "vana.data-point.tombstone.v1";
111661
+ var TOMBSTONE_METADATA_HASH_LABEL = "vana.data-point.tombstone.metadata.v1";
111662
+ var TOMBSTONE_DATA_HASH = keccak256(stringToHex(TOMBSTONE_DATA_HASH_LABEL));
111663
+ var TOMBSTONE_METADATA_HASH = keccak256(stringToHex(TOMBSTONE_METADATA_HASH_LABEL));
111664
+ function isTombstoneRecord(record2) {
111665
+ return record2.dataHash.toLowerCase() === TOMBSTONE_DATA_HASH && record2.metadataHash.toLowerCase() === TOMBSTONE_METADATA_HASH;
111666
+ }
111667
+
111668
+ // ../core/dist/sync/scope-deletions.js
111669
+ var DEFAULT_SCOPE_DELETION_MAX_STALENESS_MS = 12e4;
111670
+ var DEFAULT_SCOPE_DELETION_GATEWAY_RETRY_MS = 15e3;
111671
+ var DEFAULT_MAX_LIVE_ENTRIES = 1e4;
111672
+ function createScopeDeletionTracker(options = {}) {
111673
+ const maxStalenessMs = options.maxStalenessMs ?? DEFAULT_SCOPE_DELETION_MAX_STALENESS_MS;
111674
+ const gatewayRetryMs = options.gatewayRetryMs ?? DEFAULT_SCOPE_DELETION_GATEWAY_RETRY_MS;
111675
+ const maxLiveEntries = options.maxLiveEntries ?? DEFAULT_MAX_LIVE_ENTRIES;
111676
+ const now = options.now ?? (() => /* @__PURE__ */ new Date());
111677
+ const nowMs = () => now().getTime();
111678
+ const deleted = /* @__PURE__ */ new Map();
111679
+ const live = /* @__PURE__ */ new Map();
111680
+ let lastFeedSyncMs = null;
111681
+ let lastGatewayFailureMs = null;
111682
+ const inflight = /* @__PURE__ */ new Map();
111683
+ function rememberLive(scope, at3) {
111684
+ deleted.delete(scope);
111685
+ live.delete(scope);
111686
+ live.set(scope, at3);
111687
+ while (live.size > maxLiveEntries) {
111688
+ const oldest = live.keys().next().value;
111689
+ if (oldest === void 0)
111690
+ break;
111691
+ live.delete(oldest);
111692
+ }
111693
+ }
111694
+ function rememberDeleted(scope, tombstone, source) {
111695
+ deleted.set(scope, {
111696
+ deletedAt: tombstone.deletedAt,
111697
+ version: normalizeVersion(tombstone.version),
111698
+ source,
111699
+ verifiedAtMs: nowMs()
111700
+ });
111701
+ live.delete(scope);
111702
+ }
111703
+ function isFresh(at3) {
111704
+ return at3 !== null && nowMs() - at3 <= maxStalenessMs;
111705
+ }
111706
+ function verdictFromRecord(scope, record2) {
111707
+ const deletedAt = deletionTimestamp(record2);
111708
+ if (deletedAt !== null) {
111709
+ const version4 = tombstoneVersion(record2);
111710
+ rememberDeleted(scope, { deletedAt, version: version4 }, "gateway");
111711
+ return {
111712
+ deleted: true,
111713
+ deletedAt,
111714
+ version: version4,
111715
+ source: "gateway",
111716
+ verified: true
111717
+ };
111718
+ }
111719
+ rememberLive(scope, nowMs());
111720
+ return { deleted: false, source: "gateway", verified: true };
111721
+ }
111722
+ async function lookup2(scope) {
111723
+ const feed = options.feed;
111724
+ const owner = options.serverOwner;
111725
+ if (!feed || !owner)
111726
+ return null;
111727
+ if (lastGatewayFailureMs !== null && nowMs() - lastGatewayFailureMs < gatewayRetryMs) {
111728
+ return null;
111729
+ }
111730
+ const pending = inflight.get(scope);
111731
+ if (pending)
111732
+ return pending;
111733
+ const request2 = (async () => {
111734
+ try {
111735
+ const record2 = await feed.getDataPoint({
111736
+ ownerAddress: owner,
111737
+ scope
111738
+ });
111739
+ lastGatewayFailureMs = null;
111740
+ return verdictFromRecord(scope, record2);
111741
+ } catch (err2) {
111742
+ lastGatewayFailureMs = nowMs();
111743
+ options.logger?.warn?.({
111744
+ scope,
111745
+ error: err2 instanceof Error ? err2.message : String(err2),
111746
+ retryAfterMs: gatewayRetryMs
111747
+ }, "Could not check gateway deletion state; serving last known state");
111748
+ return null;
111749
+ } finally {
111750
+ inflight.delete(scope);
111751
+ }
111752
+ })();
111753
+ inflight.set(scope, request2);
111754
+ return request2;
111755
+ }
111756
+ return {
111757
+ maxStalenessMs,
111758
+ markDeleted(scope, tombstone, source = "feed") {
111759
+ rememberDeleted(scope, tombstone, source);
111760
+ },
111761
+ markLive(scope) {
111762
+ rememberLive(scope, nowMs());
111763
+ },
111764
+ noteFeedSynced(at3, options2) {
111765
+ lastFeedSyncMs = (at3 ?? now()).getTime();
111766
+ if (!options2?.full)
111767
+ return;
111768
+ for (const tombstone of deleted.values()) {
111769
+ tombstone.verifiedAtMs = Math.max(tombstone.verifiedAtMs, lastFeedSyncMs);
111770
+ }
111771
+ },
111772
+ knownDeletion(scope) {
111773
+ const known = deleted.get(scope);
111774
+ return known === void 0 ? null : { deletedAt: known.deletedAt, version: known.version };
111775
+ },
111776
+ feedAgeMs() {
111777
+ return lastFeedSyncMs === null ? null : nowMs() - lastFeedSyncMs;
111778
+ },
111779
+ async resolve(scope, resolveOptions) {
111780
+ const known = deleted.get(scope);
111781
+ if (known !== void 0) {
111782
+ if (isFresh(known.verifiedAtMs)) {
111783
+ return {
111784
+ deleted: true,
111785
+ deletedAt: known.deletedAt,
111786
+ version: known.version,
111787
+ source: known.source,
111788
+ verified: true
111789
+ };
111790
+ }
111791
+ const rechecked = await lookup2(scope);
111792
+ if (rechecked !== null)
111793
+ return rechecked;
111794
+ return {
111795
+ deleted: true,
111796
+ deletedAt: known.deletedAt,
111797
+ version: known.version,
111798
+ source: known.source,
111799
+ verified: false
111800
+ };
111801
+ }
111802
+ if (isFresh(live.get(scope) ?? null)) {
111803
+ return { deleted: false, source: "gateway", verified: true };
111804
+ }
111805
+ const consult = resolveOptions?.consultGateway ?? "if-stale";
111806
+ if (consult === "if-stale" && isFresh(lastFeedSyncMs)) {
111807
+ return { deleted: false, source: "feed", verified: true };
111808
+ }
111809
+ return await lookup2(scope) ?? {
111810
+ deleted: false,
111811
+ source: "assumed-live",
111812
+ verified: false
111813
+ };
111814
+ }
111815
+ };
111816
+ }
111817
+ function deletionTimestamp(record2) {
111818
+ if (!record2)
111819
+ return null;
111820
+ if (record2.deletedAt)
111821
+ return record2.deletedAt;
111822
+ return isTombstoneRecord(record2) ? record2.addedAt : null;
111823
+ }
111824
+ function tombstoneVersion(record2) {
111825
+ return normalizeVersion(record2?.expectedVersion ?? null);
111826
+ }
111827
+ function normalizeVersion(value) {
111828
+ if (typeof value !== "string" || !/^\d+$/.test(value))
111829
+ return null;
111830
+ return BigInt(value) > 0n ? BigInt(value).toString() : null;
111831
+ }
111832
+ function isEntryCoveredByTombstone(entry, tombstone) {
111833
+ const version4 = normalizeVersion(tombstone.version);
111834
+ if (version4 === null)
111835
+ return true;
111836
+ const tombstoned = BigInt(version4);
111837
+ if (entry.dataPointId !== null)
111838
+ return BigInt(entry.version) <= tombstoned;
111839
+ const marker = entry.afterTombstoneVersion;
111840
+ if (marker === null || marker === void 0 || !Number.isSafeInteger(marker)) {
111841
+ return true;
111842
+ }
111843
+ return BigInt(marker) < tombstoned;
111844
+ }
111845
+
111846
+ // ../core/dist/sync/workers/delete.js
111847
+ var BLOB_DELETE_BATCH_SIZE = 15;
111848
+ function planBlobDeletions(storage, scope, tombstoneVersionValue) {
111849
+ const tombstone = { version: tombstoneVersionValue };
111850
+ const last2 = tombstoneVersionValue === null ? null : BigInt(tombstoneVersionValue);
111851
+ const keys = /* @__PURE__ */ new Set();
111852
+ const PAGE_SIZE = 500;
111853
+ for (let offset = 0; ; offset += PAGE_SIZE) {
111854
+ const entries = storage.listVersions(scope, { limit: PAGE_SIZE, offset });
111855
+ for (const entry of entries) {
111856
+ if (!isEntryCoveredByTombstone(entry, tombstone))
111857
+ continue;
111858
+ const version4 = BigInt(entry.version);
111859
+ if (last2 !== null && version4 >= 1n && version4 <= last2)
111860
+ continue;
111861
+ keys.add(version4.toString());
111862
+ }
111863
+ if (entries.length < PAGE_SIZE)
111864
+ break;
111865
+ }
111866
+ return {
111867
+ keys: [...keys].sort((a10, b10) => BigInt(a10) < BigInt(b10) ? -1 : 1),
111868
+ range: last2 === null || last2 < 1n ? null : { from: "1", to: last2.toString() }
111869
+ };
111870
+ }
111871
+ function takeFromRange(range, count) {
111872
+ const from = BigInt(range.from);
111873
+ const to3 = BigInt(range.to);
111874
+ const versions = [];
111875
+ let cursor = from;
111876
+ while (cursor <= to3 && versions.length < count) {
111877
+ versions.push(cursor.toString());
111878
+ cursor += 1n;
111879
+ }
111880
+ return {
111881
+ versions,
111882
+ rest: cursor <= to3 ? { from: cursor.toString(), to: range.to } : null
111883
+ };
111884
+ }
111885
+ function rangeSize(range) {
111886
+ return BigInt(range.to) - BigInt(range.from) + 1n;
111887
+ }
111888
+ function countPendingKeys(markers) {
111889
+ let total = 0n;
111890
+ for (const marker of markers) {
111891
+ if (marker.version !== null)
111892
+ total += 1n;
111893
+ else if (marker.range)
111894
+ total += rangeSize(marker.range);
111895
+ else
111896
+ total += 1n;
111897
+ }
111898
+ return total > BigInt(Number.MAX_SAFE_INTEGER) ? Number.MAX_SAFE_INTEGER : Number(total);
111899
+ }
111900
+ async function deleteScope(deps, scope) {
111901
+ const { storage, deleteData, pendingBlobDeletions, scopeDeletions, logger } = deps;
111902
+ const now = deps.now ?? (() => /* @__PURE__ */ new Date());
111903
+ const dataPointId = deps.serverOwner ? computeDataPointId(deps.serverOwner, scope) : null;
111904
+ const result = {
111905
+ scope,
111906
+ dataPointId,
111907
+ durable: false,
111908
+ steps: {
111909
+ gateway: { status: "skipped", reason: "sync-disabled" },
111910
+ storage: { status: "skipped", reason: "sync-disabled" },
111911
+ local: { status: "skipped" }
111912
+ },
111913
+ pendingBlobDeletion: false
111914
+ };
111915
+ if (deleteData) {
111916
+ let tombstoneVersionValue = null;
111917
+ let tombstoneKnown = false;
111918
+ try {
111919
+ let outcome = await deleteData.tombstone(scope);
111920
+ if (outcome.status === "not-registered") {
111921
+ let registry2 = await registryState(deps, scope);
111922
+ if (registry2.status === "live") {
111923
+ outcome = await deleteData.tombstone(scope);
111924
+ registry2 = await registryState(deps, scope);
111925
+ }
111926
+ if (outcome.status === "not-registered" && registry2.status !== "deleted-or-absent") {
111927
+ throw registry2.status === "unknown" ? registry2.error : new Error("Scope was registered concurrently while it was being deleted; retry the delete");
111928
+ }
111929
+ }
111930
+ if (outcome.status === "not-registered") {
111931
+ result.steps.gateway = { status: "skipped", reason: "not-registered" };
111932
+ } else {
111933
+ tombstoneKnown = true;
111934
+ tombstoneVersionValue = outcome.version === null ? null : tombstoneVersion({ expectedVersion: outcome.version });
111935
+ result.steps.gateway = {
111936
+ status: "ok",
111937
+ ...outcome.status === "already-deleted" && {
111938
+ reason: "already-deleted"
111939
+ },
111940
+ version: outcome.version,
111941
+ deletedAt: outcome.deletedAt
111942
+ };
111943
+ scopeDeletions?.markDeleted(scope, {
111944
+ deletedAt: outcome.deletedAt ?? now().toISOString(),
111945
+ version: outcome.version
111946
+ }, "local-delete");
111947
+ }
111948
+ result.durable = true;
111949
+ } catch (err2) {
111950
+ const message = errorMessage2(err2);
111951
+ result.steps.gateway = { status: "failed", error: message };
111952
+ result.steps.storage = { status: "skipped", reason: "gateway-failed" };
111953
+ result.steps.local = { status: "skipped", reason: "gateway-failed" };
111954
+ logger.error({ scope, dataPointId, error: message }, "Gateway tombstone failed; scope NOT deleted (local copy kept so sync cannot resurrect a half-deleted scope)");
111955
+ return result;
111956
+ }
111957
+ const plan = planBlobDeletions(storage, scope, tombstoneVersionValue);
111958
+ const batch = plan.keys.slice(0, BLOB_DELETE_BATCH_SIZE);
111959
+ const leftovers = plan.keys.slice(BLOB_DELETE_BATCH_SIZE).map((version4) => ({ scope, version: version4 }));
111960
+ if (plan.range) {
111961
+ const taken = takeFromRange(plan.range, BLOB_DELETE_BATCH_SIZE - batch.length);
111962
+ batch.push(...taken.versions);
111963
+ if (taken.rest)
111964
+ leftovers.push({ scope, version: null, range: taken.rest });
111965
+ }
111966
+ if (tombstoneKnown && tombstoneVersionValue === null) {
111967
+ leftovers.push({ scope, version: null });
111968
+ }
111969
+ const storageStep = await deleteBlobKeys({ deleteData, pendingBlobDeletions, logger }, scope, batch, leftovers);
111970
+ result.steps.storage = storageStep.step;
111971
+ result.pendingBlobDeletion = storageStep.pending > 0;
111972
+ }
111973
+ try {
111974
+ const deletedCount = await storage.deleteScope(scope);
111975
+ result.steps.local = { status: "ok", deletedCount };
111976
+ } catch (err2) {
111977
+ const message = errorMessage2(err2);
111978
+ result.steps.local = { status: "failed", error: message };
111979
+ logger.error({ scope, error: message }, "Local scope deletion failed");
111980
+ }
111981
+ logger.info({
111982
+ scope,
111983
+ dataPointId,
111984
+ durable: result.durable,
111985
+ gateway: result.steps.gateway.status,
111986
+ storage: result.steps.storage.status,
111987
+ local: result.steps.local.status
111988
+ }, "Scope deletion finished");
111989
+ return result;
111990
+ }
111991
+ async function deleteBlobKeys(deps, scope, batch, leftovers) {
111992
+ const { deleteData, pendingBlobDeletions, logger } = deps;
111993
+ let outcome;
111994
+ try {
111995
+ outcome = batch.length > 0 && deleteData ? await deleteData.deleteBlobVersions(scope, batch) : { deleted: [], missing: [], failed: [] };
111996
+ } catch (err2) {
111997
+ outcome = {
111998
+ deleted: [],
111999
+ missing: [],
112000
+ failed: batch.map((version4) => ({ version: version4, error: errorMessage2(err2) }))
112001
+ };
112002
+ }
112003
+ const completed = [...outcome.deleted, ...outcome.missing].map((version4) => ({ scope, version: version4 }));
112004
+ const remaining = [
112005
+ ...outcome.failed.map(({ version: version4 }) => ({ scope, version: version4 })),
112006
+ ...leftovers
112007
+ ];
112008
+ const remainingKeys = countPendingKeys(remaining);
112009
+ let recorded = remainingKeys;
112010
+ if (pendingBlobDeletions) {
112011
+ try {
112012
+ if (completed.length > 0)
112013
+ await pendingBlobDeletions.remove(completed);
112014
+ if (remaining.length > 0)
112015
+ await pendingBlobDeletions.add(remaining);
112016
+ } catch (markerErr) {
112017
+ recorded = 0;
112018
+ logger.error({ scope, error: errorMessage2(markerErr), keys: remainingKeys }, "Could not record pending blob deletion markers");
112019
+ }
112020
+ } else if (remaining.length > 0) {
112021
+ recorded = 0;
112022
+ logger.error({ scope, keys: remainingKeys }, "Blob deletions left unfinished with no marker store to retry them");
112023
+ }
112024
+ const counts = {
112025
+ blobsDeleted: outcome.deleted.length,
112026
+ blobsMissing: outcome.missing.length,
112027
+ blobsPending: remainingKeys
112028
+ };
112029
+ if (outcome.failed.length > 0) {
112030
+ const first = outcome.failed[0];
112031
+ logger.warn({
112032
+ scope,
112033
+ failed: outcome.failed.length,
112034
+ pending: recorded,
112035
+ error: first.error
112036
+ }, "Storage blob deletion failed for some keys after gateway tombstone; will retry");
112037
+ return {
112038
+ step: {
112039
+ status: "failed",
112040
+ error: `${outcome.failed.length} blob delete(s) failed: ${first.error}`,
112041
+ ...counts
112042
+ },
112043
+ pending: recorded
112044
+ };
112045
+ }
112046
+ if (remaining.length > 0) {
112047
+ logger.info({ scope, ...counts }, "Storage blob deletion continues on later sync cycles (rate-limited batch)");
112048
+ return { step: { status: "deferred", ...counts }, pending: recorded };
112049
+ }
112050
+ return { step: { status: "ok", ...counts }, pending: 0 };
112051
+ }
112052
+ async function retryPendingBlobDeletions(deps) {
112053
+ const { deleteData, pendingBlobDeletions, logger } = deps;
112054
+ const result = {
112055
+ completed: [],
112056
+ superseded: [],
112057
+ failed: [],
112058
+ remaining: 0
112059
+ };
112060
+ if (!deleteData || !pendingBlobDeletions)
112061
+ return result;
112062
+ let markers = await pendingBlobDeletions.list();
112063
+ if (markers.length === 0)
112064
+ return result;
112065
+ for (const marker of markers.filter((key) => key.version === null && !key.range)) {
112066
+ const registry2 = await registryState(deps, marker.scope);
112067
+ if (registry2.status === "unknown") {
112068
+ result.failed.push({
112069
+ scope: marker.scope,
112070
+ version: null,
112071
+ error: registry2.error.message
112072
+ });
112073
+ continue;
112074
+ }
112075
+ if (registry2.status === "live") {
112076
+ await pendingBlobDeletions.remove([marker]);
112077
+ result.superseded.push(marker.scope);
112078
+ logger.warn({
112079
+ scope: marker.scope,
112080
+ dataPointId: registry2.record.id,
112081
+ version: registry2.record.expectedVersion
112082
+ }, "Scope was re-added after its tombstone; dropping the unexpanded blob deletion marker so the live version's ciphertext survives");
112083
+ continue;
112084
+ }
112085
+ const plan = planBlobDeletions(deps.storage ?? { listVersions: () => [] }, marker.scope, registry2.status === "deleted" ? registry2.version : null);
112086
+ const expanded = plan.keys.map((version4) => ({
112087
+ scope: marker.scope,
112088
+ version: version4
112089
+ }));
112090
+ if (plan.range) {
112091
+ expanded.push({ scope: marker.scope, version: null, range: plan.range });
112092
+ }
112093
+ await pendingBlobDeletions.remove([marker]);
112094
+ await pendingBlobDeletions.add(expanded);
112095
+ }
112096
+ markers = await pendingBlobDeletions.list();
112097
+ let budget = BLOB_DELETE_BATCH_SIZE;
112098
+ const byScope = /* @__PURE__ */ new Map();
112099
+ const exactOrigin = /* @__PURE__ */ new Set();
112100
+ const advancedRanges = [];
112101
+ const enqueue = (scope, version4) => {
112102
+ const versions = byScope.get(scope) ?? [];
112103
+ versions.push(version4);
112104
+ byScope.set(scope, versions);
112105
+ };
112106
+ for (const marker of markers) {
112107
+ if (budget === 0)
112108
+ break;
112109
+ if (marker.version !== null) {
112110
+ enqueue(marker.scope, marker.version);
112111
+ exactOrigin.add(`${marker.scope}\0${marker.version}`);
112112
+ budget -= 1;
112113
+ }
112114
+ }
112115
+ for (const marker of markers) {
112116
+ if (budget === 0)
112117
+ break;
112118
+ if (marker.version === null && marker.range) {
112119
+ const taken = takeFromRange(marker.range, budget);
112120
+ for (const version4 of taken.versions)
112121
+ enqueue(marker.scope, version4);
112122
+ budget -= taken.versions.length;
112123
+ advancedRanges.push({
112124
+ old: marker,
112125
+ next: taken.rest ? { scope: marker.scope, version: null, range: taken.rest } : null
112126
+ });
112127
+ }
112128
+ }
112129
+ for (const [scope, versions] of byScope) {
112130
+ let outcome;
112131
+ try {
112132
+ outcome = await deleteData.deleteBlobVersions(scope, versions);
112133
+ } catch (err2) {
112134
+ const message = errorMessage2(err2);
112135
+ outcome = {
112136
+ deleted: [],
112137
+ missing: [],
112138
+ failed: versions.map((version4) => ({ version: version4, error: message }))
112139
+ };
112140
+ }
112141
+ const completed = [...outcome.deleted, ...outcome.missing].filter((version4) => exactOrigin.has(`${scope}\0${version4}`)).map((version4) => ({ scope, version: version4 }));
112142
+ if (completed.length > 0)
112143
+ await pendingBlobDeletions.remove(completed);
112144
+ result.completed.push(...[...outcome.deleted, ...outcome.missing].map((version4) => ({ scope, version: version4 })));
112145
+ const failedFromRange = outcome.failed.filter(({ version: version4 }) => !exactOrigin.has(`${scope}\0${version4}`)).map(({ version: version4 }) => ({ scope, version: version4 }));
112146
+ if (failedFromRange.length > 0) {
112147
+ await pendingBlobDeletions.add(failedFromRange);
112148
+ }
112149
+ for (const failure of outcome.failed) {
112150
+ result.failed.push({ scope, ...failure });
112151
+ }
112152
+ if (outcome.deleted.length + outcome.missing.length > 0) {
112153
+ logger.info({
112154
+ scope,
112155
+ deleted: outcome.deleted.length,
112156
+ missing: outcome.missing.length
112157
+ }, "Completed pending blob deletions");
112158
+ }
112159
+ if (outcome.failed.length > 0) {
112160
+ logger.warn({
112161
+ scope,
112162
+ failed: outcome.failed.length,
112163
+ error: outcome.failed[0].error
112164
+ }, "Pending blob deletion failed again");
112165
+ }
112166
+ }
112167
+ for (const { old, next } of advancedRanges) {
112168
+ await pendingBlobDeletions.remove([old]);
112169
+ if (next)
112170
+ await pendingBlobDeletions.add([next]);
112171
+ }
112172
+ result.remaining = (await pendingBlobDeletions.list()).length;
112173
+ return result;
112174
+ }
112175
+ async function registryState(deps, scope) {
112176
+ if (!deps.dataPointFeed || !deps.serverOwner) {
112177
+ return { status: "deleted-or-absent" };
112178
+ }
112179
+ let record2;
112180
+ try {
112181
+ record2 = await deps.dataPointFeed.getDataPoint({
112182
+ ownerAddress: deps.serverOwner,
112183
+ scope
112184
+ });
112185
+ } catch (err2) {
112186
+ return {
112187
+ status: "unknown",
112188
+ error: err2 instanceof Error ? err2 : new Error(String(err2))
112189
+ };
112190
+ }
112191
+ if (record2 === null)
112192
+ return { status: "deleted-or-absent" };
112193
+ if (deletionTimestamp(record2) === null)
112194
+ return { status: "live", record: record2 };
112195
+ return { status: "deleted", version: tombstoneVersion(record2) };
112196
+ }
112197
+ function errorMessage2(err2) {
112198
+ return err2 instanceof Error ? err2.message : String(err2);
112199
+ }
112200
+
111551
112201
  // ../core/dist/payment/x402.js
111552
112202
  function generateRecordId() {
111553
112203
  const bytes2 = new Uint8Array(32);
@@ -112028,6 +112678,63 @@ async function handleX402Cycle(input) {
112028
112678
  function collectedAt(now) {
112029
112679
  return now().toISOString().replace(/\.\d{3}Z$/, "Z");
112030
112680
  }
112681
+ async function resolveReadDeletion(deps, scope, entry) {
112682
+ if (!deps.scopeDeletions)
112683
+ return null;
112684
+ const verdict = await deps.scopeDeletions.resolve(scope, {
112685
+ consultGateway: entry ? "if-stale" : "always"
112686
+ });
112687
+ if (!verdict.deleted)
112688
+ return null;
112689
+ if (entry && !isEntryCoveredByTombstone(entry, verdict)) {
112690
+ return null;
112691
+ }
112692
+ return {
112693
+ scope,
112694
+ dataPointId: deps.serverOwner ? computeDataPointId(deps.serverOwner, scope) : null,
112695
+ deletedAt: verdict.deletedAt
112696
+ };
112697
+ }
112698
+ async function assertScopeNotDeleted(deps, scope, entry) {
112699
+ const deletion = await resolveReadDeletion(deps, scope, entry);
112700
+ if (deletion)
112701
+ throw new DataDeletedError(deletion);
112702
+ }
112703
+ function discoveryVisibility(deps) {
112704
+ if (!deps.scopeDeletions)
112705
+ return void 0;
112706
+ return async (scope, entry) => await resolveReadDeletion(deps, scope, entry) === null;
112707
+ }
112708
+ async function ingestTombstoneMarker(deps, scope) {
112709
+ if (!deps.scopeDeletions)
112710
+ return null;
112711
+ const verdict = await deps.scopeDeletions.resolve(scope);
112712
+ if (!verdict.deleted || verdict.version === null)
112713
+ return null;
112714
+ const version4 = Number(verdict.version);
112715
+ return Number.isSafeInteger(version4) ? version4 : null;
112716
+ }
112717
+ function apiLoggerAsLogger(logger) {
112718
+ const noop = () => void 0;
112719
+ return {
112720
+ debug: (payload, message) => (logger?.debug ?? noop)(payload, message ?? ""),
112721
+ info: (payload, message) => (logger?.info ?? noop)(payload, message ?? ""),
112722
+ warn: (payload, message) => (logger?.warn ?? noop)(payload, message ?? ""),
112723
+ error: (payload, message) => (logger?.error ?? noop)(payload, message ?? "")
112724
+ };
112725
+ }
112726
+ function notifyDataWritten(deps, event) {
112727
+ if (!deps.onDataWritten)
112728
+ return;
112729
+ try {
112730
+ deps.onDataWritten(event);
112731
+ } catch (err2) {
112732
+ deps.logger?.warn?.({
112733
+ scope: event.scope,
112734
+ error: err2 instanceof Error ? err2.message : String(err2)
112735
+ }, "onDataWritten hook failed; record already stored");
112736
+ }
112737
+ }
112031
112738
  function notifyNewData(syncManager) {
112032
112739
  if (!syncManager)
112033
112740
  return;
@@ -112083,24 +112790,6 @@ function resolveLineageGrantView(authResult) {
112083
112790
  }
112084
112791
  return { grantId };
112085
112792
  }
112086
- async function deleteOneScope(deps, scope) {
112087
- if (deps.syncManager?.deleteScopeRemote) {
112088
- try {
112089
- await deps.syncManager.deleteScopeRemote(scope);
112090
- } catch (err2) {
112091
- deps.logger?.info?.({ scope, error: err2.message }, "Remote scope deletion failed; proceeding with local delete");
112092
- }
112093
- }
112094
- const result = await deleteDataScopeContract({
112095
- storage: deps.storage,
112096
- scopeParam: scope
112097
- });
112098
- if (!result.ok) {
112099
- throw new ProtocolError(result.status, result.body.error, result.body.message);
112100
- }
112101
- deps.logger?.info?.({ scope, deletedCount: result.deletedCount }, "Scope deleted");
112102
- return result.deletedCount;
112103
- }
112104
112793
  async function handlePersonalServerDataRequest(request2, deps, options = {}) {
112105
112794
  return withApiErrors(async () => {
112106
112795
  const url2 = new URL(request2.url);
@@ -112113,7 +112802,8 @@ async function handlePersonalServerDataRequest(request2, deps, options = {}) {
112113
112802
  storage: deps.storage,
112114
112803
  scopePrefix: url2.searchParams.get("scopePrefix") ?? void 0,
112115
112804
  limit: normalizeLimit2(url2.searchParams.get("limit"), 20),
112116
- offset: normalizeLimit2(url2.searchParams.get("offset"), 0)
112805
+ offset: normalizeLimit2(url2.searchParams.get("offset"), 0),
112806
+ isVisible: discoveryVisibility(deps)
112117
112807
  });
112118
112808
  return jsonResponse(result.response);
112119
112809
  }
@@ -112122,14 +112812,18 @@ async function handlePersonalServerDataRequest(request2, deps, options = {}) {
112122
112812
  if (request2.method !== "GET")
112123
112813
  return methodNotAllowed();
112124
112814
  await deps.auth.authorizeBuilderList(request2);
112125
- const result = listDataVersionsContract({
112815
+ const result = await listDataVersionsContract({
112126
112816
  storage: deps.storage,
112127
112817
  scopeParam: decodePathPart(parts[0]),
112128
112818
  limit: normalizeLimit2(url2.searchParams.get("limit"), 20),
112129
- offset: normalizeLimit2(url2.searchParams.get("offset"), 0)
112819
+ offset: normalizeLimit2(url2.searchParams.get("offset"), 0),
112820
+ isVisible: discoveryVisibility(deps)
112130
112821
  });
112131
112822
  if (!result.ok)
112132
112823
  return contractErrorResponse(result);
112824
+ if (result.response.total === 0) {
112825
+ await assertScopeNotDeleted(deps, result.scope, void 0);
112826
+ }
112133
112827
  return jsonResponse(result.response);
112134
112828
  }
112135
112829
  if ((parts.length === 2 || parts.length === 3) && parts[1] === "lineage") {
@@ -112211,6 +112905,7 @@ async function handlePersonalServerDataRequest(request2, deps, options = {}) {
112211
112905
  fileId: url2.searchParams.get("fileId") ?? selectedEntry?.fileId ?? void 0,
112212
112906
  at: url2.searchParams.get("at") ?? void 0
112213
112907
  });
112908
+ await assertScopeNotDeleted(deps, scopeResult.scope, selectedEntry);
112214
112909
  const isOwnerSignal = authResult?.grantId === "owner" || authResult?.grantId === "policy-bypass";
112215
112910
  const builder = authResult?.builder;
112216
112911
  const resolvedGrantId = !isOwnerSignal && authResult?.grantId ? authResult.grantId : void 0;
@@ -112265,8 +112960,12 @@ async function handlePersonalServerDataRequest(request2, deps, options = {}) {
112265
112960
  fileId: url2.searchParams.get("fileId") ?? void 0,
112266
112961
  at: url2.searchParams.get("at") ?? void 0
112267
112962
  });
112268
- if (!result.ok)
112963
+ if (!result.ok) {
112964
+ if (result.status === 404) {
112965
+ await assertScopeNotDeleted(deps, scopeResult.scope, void 0);
112966
+ }
112269
112967
  return contractErrorResponse(result);
112968
+ }
112270
112969
  const logId = deps.createLogId?.() ?? crypto.randomUUID();
112271
112970
  const timestamp = (deps.now ?? (() => /* @__PURE__ */ new Date()))().toISOString();
112272
112971
  const ipAddress = request2.headers.get("x-forwarded-for") ?? request2.headers.get("x-real-ip") ?? "unknown";
@@ -112346,6 +113045,7 @@ async function handlePersonalServerDataRequest(request2, deps, options = {}) {
112346
113045
  return failWrite(contractErrorResponse(scopeResult));
112347
113046
  const collectedAtValue = collectedAt(deps.now ?? (() => /* @__PURE__ */ new Date()));
112348
113047
  const status2 = deps.syncManager ? "syncing" : "stored";
113048
+ const afterTombstoneVersion = await ingestTombstoneMarker(deps, scopeResult.scope);
112349
113049
  const logBuilderWrite = async () => {
112350
113050
  if (!writeAuth)
112351
113051
  return;
@@ -112383,7 +113083,8 @@ async function handlePersonalServerDataRequest(request2, deps, options = {}) {
112383
113083
  collectedAt: collectedAtValue,
112384
113084
  status: status2,
112385
113085
  attribution: writeAuth?.attribution,
112386
- lineage: lineage2
113086
+ lineage: lineage2,
113087
+ afterTombstoneVersion
112387
113088
  });
112388
113089
  if (!result2.ok)
112389
113090
  return failWrite(contractErrorResponse(result2));
@@ -112398,6 +113099,11 @@ async function handlePersonalServerDataRequest(request2, deps, options = {}) {
112398
113099
  }, "Binary data file ingested");
112399
113100
  await logBuilderWrite();
112400
113101
  notifyNewData(deps.syncManager);
113102
+ notifyDataWritten(deps, {
113103
+ scope: scopeResult.scope,
113104
+ collectedAt: collectedAtValue,
113105
+ lineageSources: lineage2?.sources
113106
+ });
112401
113107
  return jsonResponse(result2.response, { status: 201 });
112402
113108
  }
112403
113109
  const parsed = await parseJsonObjectBody(request2, "Request body must be valid JSON");
@@ -112411,7 +113117,8 @@ async function handlePersonalServerDataRequest(request2, deps, options = {}) {
112411
113117
  collectedAt: collectedAtValue,
112412
113118
  status: status2,
112413
113119
  attribution: writeAuth?.attribution,
112414
- lineage
113120
+ lineage,
113121
+ afterTombstoneVersion
112415
113122
  });
112416
113123
  if (!result.ok)
112417
113124
  return failWrite(contractErrorResponse(result));
@@ -112424,6 +113131,11 @@ async function handlePersonalServerDataRequest(request2, deps, options = {}) {
112424
113131
  }, "Data file ingested");
112425
113132
  await logBuilderWrite();
112426
113133
  notifyNewData(deps.syncManager);
113134
+ notifyDataWritten(deps, {
113135
+ scope: scopeResult.scope,
113136
+ collectedAt: collectedAtValue,
113137
+ lineageSources: lineage?.sources
113138
+ });
112427
113139
  return jsonResponse(result.response, { status: 201 });
112428
113140
  } catch (err2) {
112429
113141
  if (err2 instanceof IngestPersistedError) {
@@ -112450,8 +113162,44 @@ async function handlePersonalServerDataRequest(request2, deps, options = {}) {
112450
113162
  if (cascade === "lineage") {
112451
113163
  throw new LineageCascadeUnavailableError({ scope: parsed.scope });
112452
113164
  }
112453
- await deleteOneScope(deps, parsed.scope);
112454
- return new Response(null, { status: 204 });
113165
+ const result = deps.syncManager?.deleteScope ? await deps.syncManager.deleteScope(parsed.scope) : await deleteScope({
113166
+ storage: deps.storage,
113167
+ serverOwner: deps.serverOwner,
113168
+ deleteData: null,
113169
+ logger: apiLoggerAsLogger(deps.logger)
113170
+ }, parsed.scope);
113171
+ if (result.steps.gateway.status === "failed") {
113172
+ throw new DeleteTombstoneFailedError({
113173
+ scope: parsed.scope,
113174
+ result
113175
+ });
113176
+ }
113177
+ try {
113178
+ await deps.accessLogWriter.write({
113179
+ logId: deps.createLogId?.() ?? crypto.randomUUID(),
113180
+ grantId: "owner",
113181
+ builder: deps.serverOwner ?? "owner",
113182
+ action: "delete",
113183
+ scope: parsed.scope,
113184
+ timestamp: (deps.now ?? (() => /* @__PURE__ */ new Date()))().toISOString(),
113185
+ ipAddress: request2.headers.get("x-forwarded-for") ?? request2.headers.get("x-real-ip") ?? "unknown",
113186
+ userAgent: request2.headers.get("user-agent") ?? "unknown"
113187
+ });
113188
+ } catch (err2) {
113189
+ deps.logger?.warn?.({
113190
+ scope: parsed.scope,
113191
+ error: err2 instanceof Error ? err2.message : String(err2)
113192
+ }, "Delete access-log entry failed; scope already deleted");
113193
+ }
113194
+ deps.logger?.info?.({
113195
+ scope: parsed.scope,
113196
+ durable: result.durable,
113197
+ gateway: result.steps.gateway.status,
113198
+ storage: result.steps.storage.status,
113199
+ local: result.steps.local.status,
113200
+ deletedCount: result.steps.local.deletedCount
113201
+ }, "Scope deleted");
113202
+ return jsonResponse(result, { status: 200 });
112455
113203
  }
112456
113204
  return methodNotAllowed();
112457
113205
  });
@@ -112614,6 +113362,93 @@ async function handlePersonalServerOauthTokenRequest(request2, deps) {
112614
113362
  });
112615
113363
  }
112616
113364
 
113365
+ // ../core/dist/derivatives/types.js
113366
+ function questionRegistrationView(registration) {
113367
+ return {
113368
+ questionId: registration.questionId,
113369
+ derivedScope: registration.derivedScope,
113370
+ sourceScopes: [...registration.sourceScopes],
113371
+ question: registration.question,
113372
+ model: registration.model,
113373
+ registeredBy: registration.registeredBy,
113374
+ status: registration.status,
113375
+ error: registration.error,
113376
+ createdAt: registration.createdAt,
113377
+ updatedAt: registration.updatedAt,
113378
+ lastComputedAt: registration.lastComputedAt,
113379
+ derivedVersion: registration.derivedVersion,
113380
+ derivedCollectedAt: registration.derivedCollectedAt
113381
+ };
113382
+ }
113383
+
113384
+ // ../core/dist/derivatives/store.js
113385
+ function clone2(registration) {
113386
+ return {
113387
+ ...registration,
113388
+ sourceScopes: [...registration.sourceScopes],
113389
+ registeredBy: { ...registration.registeredBy }
113390
+ };
113391
+ }
113392
+ function matchesQuestionFilter(registration, filter) {
113393
+ if (!filter)
113394
+ return true;
113395
+ if (filter.derivedScope && registration.derivedScope !== filter.derivedScope)
113396
+ return false;
113397
+ if (filter.sourceScope && !registration.sourceScopes.includes(filter.sourceScope))
113398
+ return false;
113399
+ if (filter.builder) {
113400
+ const by2 = registration.registeredBy;
113401
+ if (by2.kind !== "builder" || by2.builder.toLowerCase() !== filter.builder.toLowerCase())
113402
+ return false;
113403
+ }
113404
+ return true;
113405
+ }
113406
+ function sortQuestions(registrations) {
113407
+ return [...registrations].sort((a10, b10) => a10.createdAt.localeCompare(b10.createdAt) || a10.questionId.localeCompare(b10.questionId));
113408
+ }
113409
+ function createInMemoryQuestionStore(options = {}) {
113410
+ const byId = /* @__PURE__ */ new Map();
113411
+ for (const registration of options.initial ?? []) {
113412
+ byId.set(registration.questionId, clone2(registration));
113413
+ }
113414
+ async function changed() {
113415
+ if (!options.onChange)
113416
+ return;
113417
+ await options.onChange(sortQuestions([...byId.values()]).map(clone2));
113418
+ }
113419
+ return {
113420
+ async list(filter) {
113421
+ return sortQuestions([...byId.values()].filter((registration) => matchesQuestionFilter(registration, filter))).map(clone2);
113422
+ },
113423
+ async get(questionId) {
113424
+ const registration = byId.get(questionId);
113425
+ return registration ? clone2(registration) : null;
113426
+ },
113427
+ async insert(registration) {
113428
+ if (byId.has(registration.questionId)) {
113429
+ throw new Error(`Question ${registration.questionId} is already registered`);
113430
+ }
113431
+ byId.set(registration.questionId, clone2(registration));
113432
+ await changed();
113433
+ },
113434
+ async update(questionId, patch) {
113435
+ const current = byId.get(questionId);
113436
+ if (!current)
113437
+ return null;
113438
+ const next = { ...current, ...patch };
113439
+ byId.set(questionId, next);
113440
+ await changed();
113441
+ return clone2(next);
113442
+ },
113443
+ async delete(questionId) {
113444
+ const existed = byId.delete(questionId);
113445
+ if (existed)
113446
+ await changed();
113447
+ return existed;
113448
+ }
113449
+ };
113450
+ }
113451
+
112617
113452
  // ../core/dist/policy/data-read.js
112618
113453
  function parseGrantExpiresAtSeconds(value) {
112619
113454
  if (value === null || value === void 0 || value === "0")
@@ -112703,6 +113538,1084 @@ async function verifyDataReadPolicy(input, ports) {
112703
113538
  return grant;
112704
113539
  }
112705
113540
 
113541
+ // ../core/dist/policy/data-write.js
113542
+ var WRITE_SCOPE_PREFIX = "write:";
113543
+ function isWriteScopeEntry(entry) {
113544
+ return entry.startsWith(WRITE_SCOPE_PREFIX);
113545
+ }
113546
+ function writeScopePatterns(grantScopes) {
113547
+ return grantScopes.filter(isWriteScopeEntry).map((entry) => entry.slice(WRITE_SCOPE_PREFIX.length)).filter((pattern) => pattern.length > 0);
113548
+ }
113549
+ function scopeCoveredByWriteGrant(requestedScope, grantScopes) {
113550
+ return writeScopePatterns(grantScopes).some((pattern) => scopeMatchesPattern(requestedScope, pattern));
113551
+ }
113552
+ async function verifyDataWritePolicy(input, ports) {
113553
+ const available = await ports.runtimeAvailability?.isAvailable();
113554
+ if (available === false) {
113555
+ throw new PsUnavailableError();
113556
+ }
113557
+ const builder = await ports.authSessionVerifier.getBuilder(input.signer);
113558
+ if (!builder) {
113559
+ throw new UnregisteredBuilderError();
113560
+ }
113561
+ if (!input.grantId) {
113562
+ throw new GrantRequiredError({
113563
+ reason: "No grantId bound to the write session"
113564
+ });
113565
+ }
113566
+ const grant = await ports.grantVerifier.getGrant(input.grantId);
113567
+ if (!grant) {
113568
+ throw new GrantRequiredError({
113569
+ reason: "Grant not found",
113570
+ grantId: input.grantId
113571
+ });
113572
+ }
113573
+ if (grant.revokedAt !== null) {
113574
+ throw new GrantRevokedError({ grantId: grant.id });
113575
+ }
113576
+ if (!grant.scopes || writeScopePatterns(grant.scopes).length === 0) {
113577
+ throw new ScopeMismatchError({
113578
+ requestedScope: input.requestedScope,
113579
+ reason: "Grant has no write scopes"
113580
+ });
113581
+ }
113582
+ if (grant.expiresAt !== null && grant.expiresAt !== void 0) {
113583
+ const expiresAtSec = parseGrantExpiresAtSeconds(grant.expiresAt);
113584
+ if (expiresAtSec === null) {
113585
+ throw new ScopeMismatchError({
113586
+ requestedScope: input.requestedScope,
113587
+ reason: "Grant expiry is invalid"
113588
+ });
113589
+ }
113590
+ if (expiresAtSec > 0) {
113591
+ const nowSec = Math.floor(Date.now() / 1e3);
113592
+ if (expiresAtSec < nowSec) {
113593
+ throw new GrantExpiredError({
113594
+ expiresAt: expiresAtSec
113595
+ });
113596
+ }
113597
+ }
113598
+ }
113599
+ if (!scopeCoveredByWriteGrant(input.requestedScope, grant.scopes)) {
113600
+ throw new ScopeMismatchError({
113601
+ requestedScope: input.requestedScope,
113602
+ grantedScopes: grant.scopes,
113603
+ reason: "Grant does not authorize writing to this scope"
113604
+ });
113605
+ }
113606
+ if (builder.id.toLowerCase() !== grant.granteeId.toLowerCase()) {
113607
+ throw new InvalidSignatureError3({
113608
+ reason: "Write signer is not the grant builder",
113609
+ expected: grant.granteeId,
113610
+ actual: input.signer
113611
+ });
113612
+ }
113613
+ if (!input.serverOwner) {
113614
+ throw new ServerNotConfiguredError({
113615
+ reason: "serverOwner is required to verify grant ownership"
113616
+ });
113617
+ }
113618
+ if (!grant.grantorAddress || grant.grantorAddress.toLowerCase() !== input.serverOwner.toLowerCase()) {
113619
+ throw new GrantOwnerMismatchError({
113620
+ grantId: grant.id,
113621
+ expected: input.serverOwner,
113622
+ actual: grant.grantorAddress ?? null
113623
+ });
113624
+ }
113625
+ await ports.writeFeeVerifier?.assertWriteAllowed({
113626
+ builder: input.signer,
113627
+ grant,
113628
+ scope: input.requestedScope
113629
+ });
113630
+ return grant;
113631
+ }
113632
+
113633
+ // ../core/dist/derivatives/registration.js
113634
+ var MAX_QUESTION_SOURCE_SCOPES = 16;
113635
+ var MAX_QUESTION_CHARS = 8e3;
113636
+ var MAX_MODEL_CHARS = 128;
113637
+ var MAX_ECHOED_SCOPE_CHARS = 128;
113638
+ var MODEL_ID = /^[A-Za-z0-9][A-Za-z0-9._:/-]*$/;
113639
+ function isRecord4(value) {
113640
+ return value !== null && typeof value === "object" && !Array.isArray(value);
113641
+ }
113642
+ function parseScope(value, field) {
113643
+ if (typeof value !== "string") {
113644
+ throw new DerivativeQuestionInvalidError(`${field} must be a scope string`, {
113645
+ field
113646
+ });
113647
+ }
113648
+ const parsed = parseDataScopeContract(value);
113649
+ if (!parsed.ok) {
113650
+ throw new DerivativeQuestionInvalidError(`${field} is not a valid scope: ${parsed.body.message}`, { field, scope: value.slice(0, MAX_ECHOED_SCOPE_CHARS) });
113651
+ }
113652
+ return parsed.scope;
113653
+ }
113654
+ function parseQuestionInput(body) {
113655
+ if (!isRecord4(body)) {
113656
+ throw new DerivativeQuestionInvalidError("Body must be a JSON object");
113657
+ }
113658
+ const derivedScope = parseScope(body.derivedScope, "derivedScope");
113659
+ if (!Array.isArray(body.sourceScopes) || body.sourceScopes.length === 0) {
113660
+ throw new DerivativeQuestionInvalidError("sourceScopes must be a non-empty array of scopes", { field: "sourceScopes" });
113661
+ }
113662
+ if (body.sourceScopes.length > MAX_QUESTION_SOURCE_SCOPES) {
113663
+ throw new DerivativeQuestionInvalidError(`sourceScopes lists ${body.sourceScopes.length} scopes; the maximum is ${MAX_QUESTION_SOURCE_SCOPES}`, { field: "sourceScopes", max: MAX_QUESTION_SOURCE_SCOPES });
113664
+ }
113665
+ const sourceScopes = [];
113666
+ for (const entry of body.sourceScopes) {
113667
+ const scope = parseScope(entry, "sourceScopes[]");
113668
+ if (sourceScopes.includes(scope)) {
113669
+ throw new DerivativeQuestionInvalidError("sourceScopes lists the same scope twice", { field: "sourceScopes", duplicate: scope });
113670
+ }
113671
+ if (scope === derivedScope) {
113672
+ throw new DerivativeQuestionInvalidError("derivedScope cannot be one of its own sources", { field: "sourceScopes", scope });
113673
+ }
113674
+ sourceScopes.push(scope);
113675
+ }
113676
+ if (typeof body.question !== "string" || body.question.trim() === "") {
113677
+ throw new DerivativeQuestionInvalidError("question must be a non-empty string", { field: "question" });
113678
+ }
113679
+ if (body.question.length > MAX_QUESTION_CHARS) {
113680
+ throw new DerivativeQuestionInvalidError(`question is ${body.question.length} characters; the maximum is ${MAX_QUESTION_CHARS}`, { field: "question", max: MAX_QUESTION_CHARS });
113681
+ }
113682
+ let model = null;
113683
+ if (body.model !== void 0 && body.model !== null) {
113684
+ if (typeof body.model !== "string" || body.model.length > MAX_MODEL_CHARS || !MODEL_ID.test(body.model)) {
113685
+ throw new DerivativeQuestionInvalidError("model must be a provider model id", { field: "model" });
113686
+ }
113687
+ model = body.model;
113688
+ }
113689
+ assertDerivedScopeNaming(derivedScope, sourceScopes);
113690
+ return { derivedScope, sourceScopes, question: body.question, model };
113691
+ }
113692
+ function findDerivationCycle(candidate, existing) {
113693
+ const sourcesOf = /* @__PURE__ */ new Map();
113694
+ const add2 = (derived, sources) => {
113695
+ const set2 = sourcesOf.get(derived) ?? /* @__PURE__ */ new Set();
113696
+ for (const source of sources)
113697
+ set2.add(source);
113698
+ sourcesOf.set(derived, set2);
113699
+ };
113700
+ for (const registration of existing) {
113701
+ add2(registration.derivedScope, registration.sourceScopes);
113702
+ }
113703
+ add2(candidate.derivedScope, candidate.sourceScopes);
113704
+ const target = candidate.derivedScope;
113705
+ const visited = /* @__PURE__ */ new Set();
113706
+ const stack = [
113707
+ { scope: target, path: [target] }
113708
+ ];
113709
+ while (stack.length > 0) {
113710
+ const { scope, path } = stack.pop();
113711
+ for (const source of sourcesOf.get(scope) ?? []) {
113712
+ if (source === target)
113713
+ return [...path, source];
113714
+ if (visited.has(source))
113715
+ continue;
113716
+ visited.add(source);
113717
+ stack.push({ scope: source, path: [...path, source] });
113718
+ }
113719
+ }
113720
+ return null;
113721
+ }
113722
+ async function createQuestionRegistration(input) {
113723
+ const parsed = parseQuestionInput(input.body);
113724
+ const cycle = findDerivationCycle(parsed, await input.store.list());
113725
+ if (cycle) {
113726
+ throw new DerivativeCycleError({
113727
+ derivedScope: parsed.derivedScope,
113728
+ path: cycle
113729
+ });
113730
+ }
113731
+ const at3 = input.now().toISOString();
113732
+ const registration = {
113733
+ questionId: input.questionId,
113734
+ derivedScope: parsed.derivedScope,
113735
+ sourceScopes: parsed.sourceScopes,
113736
+ question: parsed.question,
113737
+ model: parsed.model,
113738
+ registeredBy: input.registeredBy,
113739
+ status: "pending",
113740
+ error: null,
113741
+ createdAt: at3,
113742
+ updatedAt: at3,
113743
+ lastComputedAt: null,
113744
+ derivedVersion: null,
113745
+ derivedCollectedAt: null
113746
+ };
113747
+ await input.store.insert(registration);
113748
+ return registration;
113749
+ }
113750
+ function uncoveredSourceScopes(sourceScopes, grantScopes) {
113751
+ const readEntries = (grantScopes ?? []).filter((entry) => !isWriteScopeEntry(entry));
113752
+ return sourceScopes.filter((scope) => !scopeCoveredByGrant(scope, readEntries));
113753
+ }
113754
+
113755
+ // ../core/dist/derivatives/prompt.js
113756
+ var DEFAULT_MAX_SOURCE_ITEMS = 50;
113757
+ var DEFAULT_MAX_SOURCE_CHARS = 2e5;
113758
+ var TIMESTAMP_KEYS = [
113759
+ "collectedAt",
113760
+ "updatedAt",
113761
+ "updated_at",
113762
+ "update_time",
113763
+ "createdAt",
113764
+ "created_at",
113765
+ "create_time",
113766
+ "timestamp",
113767
+ "time",
113768
+ "date",
113769
+ "publishedAt",
113770
+ "published_at"
113771
+ ];
113772
+ var RESERVED_KEYS = /* @__PURE__ */ new Set(["$lineage", "$writtenBy", "$binary"]);
113773
+ function isRecord5(value) {
113774
+ return value !== null && typeof value === "object" && !Array.isArray(value);
113775
+ }
113776
+ function timestampOf(item) {
113777
+ if (!isRecord5(item))
113778
+ return null;
113779
+ for (const key of TIMESTAMP_KEYS) {
113780
+ const value = item[key];
113781
+ if (typeof value === "number" && Number.isFinite(value)) {
113782
+ return value < 1e12 ? value * 1e3 : value;
113783
+ }
113784
+ if (typeof value === "string") {
113785
+ const parsed = Date.parse(value);
113786
+ if (!Number.isNaN(parsed))
113787
+ return parsed;
113788
+ }
113789
+ }
113790
+ return null;
113791
+ }
113792
+ function sortNewestFirst(items) {
113793
+ const indexed = items.map((item, index) => ({
113794
+ item,
113795
+ index,
113796
+ at: timestampOf(item)
113797
+ }));
113798
+ const dated = indexed.filter((entry) => entry.at !== null).sort((a10, b10) => b10.at - a10.at || b10.index - a10.index);
113799
+ const undated = indexed.filter((entry) => entry.at === null).reverse();
113800
+ return [...dated, ...undated].map((entry) => entry.item);
113801
+ }
113802
+ function trimSourceData(data, options = {}) {
113803
+ const maxItems = Math.max(1, options.maxItems ?? DEFAULT_MAX_SOURCE_ITEMS);
113804
+ const maxChars = Math.max(1, options.maxChars ?? DEFAULT_MAX_SOURCE_CHARS);
113805
+ let kept = 0;
113806
+ let total = 0;
113807
+ const trimArray = (items, limit2) => {
113808
+ total += items.length;
113809
+ const sorted = sortNewestFirst(items).slice(0, limit2);
113810
+ kept += sorted.length;
113811
+ return sorted;
113812
+ };
113813
+ const build = (limit2) => {
113814
+ kept = 0;
113815
+ total = 0;
113816
+ if (Array.isArray(data))
113817
+ return trimArray(data, limit2);
113818
+ if (isRecord5(data)) {
113819
+ const out = {};
113820
+ for (const [key, value] of Object.entries(data)) {
113821
+ if (RESERVED_KEYS.has(key))
113822
+ continue;
113823
+ out[key] = Array.isArray(value) ? trimArray(value, limit2) : value;
113824
+ }
113825
+ return out;
113826
+ }
113827
+ return data;
113828
+ };
113829
+ let limit = maxItems;
113830
+ let result = build(limit);
113831
+ let text = JSON.stringify(result) ?? "null";
113832
+ while (text.length > maxChars && limit > 1) {
113833
+ limit = Math.max(1, Math.floor(limit / 2));
113834
+ result = build(limit);
113835
+ text = JSON.stringify(result) ?? "null";
113836
+ }
113837
+ if (text.length > maxChars) {
113838
+ return {
113839
+ data: `${text.slice(0, maxChars)}...[truncated]`,
113840
+ kept,
113841
+ total,
113842
+ truncated: true
113843
+ };
113844
+ }
113845
+ return { data: result, kept, total, truncated: false };
113846
+ }
113847
+ var SYSTEM_PROMPT = [
113848
+ "You answer a question about a person using ONLY the user data provided in the message.",
113849
+ "Do not use outside knowledge and do not guess; if the data does not support an answer, say so in the answer.",
113850
+ "Respond with a single JSON object and nothing else, with exactly these fields:",
113851
+ ' "answer": string, the answer to the question, written for the person the data belongs to;',
113852
+ ' "evidence": string, a short summary of which parts of the data support the answer.'
113853
+ ].join("\n");
113854
+ function buildQuestionMessages(input) {
113855
+ const sections = input.sources.map((source) => {
113856
+ const note = source.total > source.kept ? ` (newest ${source.kept} of ${source.total} items)` : "";
113857
+ const cut = source.truncated ? " (truncated)" : "";
113858
+ return [
113859
+ `### Scope: ${source.scope}${note}${cut}`,
113860
+ `Collected at: ${source.collectedAt}`,
113861
+ JSON.stringify(source.data)
113862
+ ].join("\n");
113863
+ });
113864
+ const user = [
113865
+ "## Question",
113866
+ input.question,
113867
+ "",
113868
+ "## User data",
113869
+ ...sections,
113870
+ "",
113871
+ "Answer the question as a JSON object with the fields answer and evidence."
113872
+ ].join("\n");
113873
+ return [
113874
+ { role: "system", content: SYSTEM_PROMPT },
113875
+ { role: "user", content: user }
113876
+ ];
113877
+ }
113878
+ function parseAnswer(content) {
113879
+ const candidates = [content.trim()];
113880
+ const fenced = /```(?:json)?\s*([\s\S]*?)```/i.exec(content);
113881
+ if (fenced?.[1])
113882
+ candidates.unshift(fenced[1].trim());
113883
+ const first = content.indexOf("{");
113884
+ const last2 = content.lastIndexOf("}");
113885
+ if (first !== -1 && last2 > first) {
113886
+ candidates.push(content.slice(first, last2 + 1));
113887
+ }
113888
+ for (const candidate of candidates) {
113889
+ try {
113890
+ const parsed = JSON.parse(candidate);
113891
+ if (isRecord5(parsed) && typeof parsed.answer === "string") {
113892
+ return {
113893
+ answer: parsed.answer,
113894
+ evidence: typeof parsed.evidence === "string" ? parsed.evidence : null
113895
+ };
113896
+ }
113897
+ } catch {
113898
+ }
113899
+ }
113900
+ return { answer: content.trim(), evidence: null };
113901
+ }
113902
+
113903
+ // ../core/dist/derivatives/inference.js
113904
+ var DEFAULT_INFERENCE_BASE_URL = "https://inference.phala.com/v1";
113905
+ var DEFAULT_INFERENCE_MODEL = "z-ai/glm-5.2";
113906
+ var DEFAULT_INFERENCE_TIMEOUT_MS = 12e4;
113907
+ var DEFAULT_INFERENCE_MAX_TOKENS = 2048;
113908
+ var DEFAULT_INFERENCE_REQUEST_FIELDS = {
113909
+ provider: { aci_verified: true, zdr: true }
113910
+ };
113911
+ var InferenceRequestError = class extends Error {
113912
+ status;
113913
+ constructor(message, status2) {
113914
+ super(message);
113915
+ this.status = status2;
113916
+ this.name = "InferenceRequestError";
113917
+ }
113918
+ };
113919
+ function isRecord6(value) {
113920
+ return value !== null && typeof value === "object" && !Array.isArray(value);
113921
+ }
113922
+ function readUsage(value) {
113923
+ if (!isRecord6(value))
113924
+ return void 0;
113925
+ const num2 = (v10) => typeof v10 === "number" ? v10 : void 0;
113926
+ const usage = {
113927
+ promptTokens: num2(value.prompt_tokens),
113928
+ completionTokens: num2(value.completion_tokens),
113929
+ totalTokens: num2(value.total_tokens)
113930
+ };
113931
+ return usage;
113932
+ }
113933
+ function readContent(body) {
113934
+ if (!isRecord6(body) || !Array.isArray(body.choices))
113935
+ return null;
113936
+ const first = body.choices[0];
113937
+ if (!isRecord6(first) || !isRecord6(first.message))
113938
+ return null;
113939
+ const content = first.message.content;
113940
+ if (typeof content === "string")
113941
+ return content;
113942
+ if (Array.isArray(content)) {
113943
+ const text = content.map((part) => isRecord6(part) && typeof part.text === "string" ? part.text : "").join("");
113944
+ return text;
113945
+ }
113946
+ return null;
113947
+ }
113948
+ function createOpenAiCompatibleInferenceProvider(options = {}) {
113949
+ const base = (options.baseUrl ?? DEFAULT_INFERENCE_BASE_URL).replace(/\/+$/, "");
113950
+ const defaultModel = options.model ?? DEFAULT_INFERENCE_MODEL;
113951
+ const timeoutMs = options.timeoutMs ?? DEFAULT_INFERENCE_TIMEOUT_MS;
113952
+ const doFetch = options.fetch ?? fetch;
113953
+ const requestFields = options.requestFields ?? DEFAULT_INFERENCE_REQUEST_FIELDS;
113954
+ return {
113955
+ defaultModel,
113956
+ async chat(input) {
113957
+ const headers = new Headers({ "Content-Type": "application/json" });
113958
+ if (options.apiKey) {
113959
+ headers.set("Authorization", `Bearer ${options.apiKey}`);
113960
+ }
113961
+ let messages = input.messages;
113962
+ if (options.encryption) {
113963
+ ({ messages } = await options.encryption.encryptRequest({
113964
+ messages,
113965
+ headers
113966
+ }));
113967
+ }
113968
+ const body = {
113969
+ ...requestFields,
113970
+ model: input.model || defaultModel,
113971
+ messages,
113972
+ max_tokens: input.maxTokens ?? DEFAULT_INFERENCE_MAX_TOKENS
113973
+ };
113974
+ let response;
113975
+ try {
113976
+ response = await doFetch(`${base}/chat/completions`, {
113977
+ method: "POST",
113978
+ headers,
113979
+ body: JSON.stringify(body),
113980
+ signal: AbortSignal.timeout(timeoutMs)
113981
+ });
113982
+ } catch (err2) {
113983
+ const name = err2 instanceof Error ? err2.name : "Error";
113984
+ throw new InferenceRequestError(`inference request failed before a response (${name})`, null);
113985
+ }
113986
+ if (!response.ok) {
113987
+ throw new InferenceRequestError(`inference request failed with status ${response.status}`, response.status);
113988
+ }
113989
+ let parsed;
113990
+ try {
113991
+ parsed = await response.json();
113992
+ } catch {
113993
+ throw new InferenceRequestError("inference response was not JSON", response.status);
113994
+ }
113995
+ let content = readContent(parsed);
113996
+ if (content === null || content.trim() === "") {
113997
+ throw new InferenceRequestError("inference response carried no assistant content", response.status);
113998
+ }
113999
+ if (options.encryption) {
114000
+ content = await options.encryption.decryptResponse({
114001
+ content,
114002
+ headers: response.headers
114003
+ });
114004
+ }
114005
+ const receiptId = response.headers.get("x-receipt-id") ?? void 0;
114006
+ const aciIdentity = response.headers.get("x-aci-identity") ?? void 0;
114007
+ return {
114008
+ content,
114009
+ usage: readUsage(isRecord6(parsed) ? parsed.usage : void 0),
114010
+ ...receiptId ? { receiptId } : {},
114011
+ ...aciIdentity ? { aciIdentity } : {}
114012
+ };
114013
+ }
114014
+ };
114015
+ }
114016
+
114017
+ // ../core/dist/derivatives/compute.js
114018
+ var DEFAULT_RETRY_DELAYS_MS = [1e3, 4e3];
114019
+ function isRetryableInferenceError(err2) {
114020
+ return err2 instanceof InferenceRequestError && (err2.status === null || err2.status === 429 || err2.status >= 500);
114021
+ }
114022
+ async function withRetries(deps, attempt, retryable) {
114023
+ const delays = deps.retryDelaysMs ?? DEFAULT_RETRY_DELAYS_MS;
114024
+ const sleep2 = deps.sleep ?? ((ms3) => new Promise((r10) => setTimeout(r10, ms3)));
114025
+ for (let index = 0; ; index += 1) {
114026
+ try {
114027
+ return await attempt();
114028
+ } catch (err2) {
114029
+ if (index >= delays.length || !retryable(err2))
114030
+ throw err2;
114031
+ await sleep2(delays[index]);
114032
+ }
114033
+ }
114034
+ }
114035
+ var ComputeFailure = class extends Error {
114036
+ constructor(message) {
114037
+ super(message);
114038
+ this.name = "ComputeFailure";
114039
+ }
114040
+ };
114041
+ function shortError(err2) {
114042
+ if (err2 instanceof ComputeFailure)
114043
+ return err2.message;
114044
+ if (err2 instanceof InferenceRequestError)
114045
+ return err2.message;
114046
+ if (err2 instanceof ProtocolError)
114047
+ return `${err2.errorCode}: ${err2.message}`;
114048
+ return `compute failed (${err2 instanceof Error ? err2.name : "Error"})`;
114049
+ }
114050
+ function collectedAtStamp(now, isTaken) {
114051
+ const base = now();
114052
+ base.setUTCMilliseconds(0);
114053
+ for (let bump = 0; bump < 60; bump += 1) {
114054
+ const candidate = new Date(base.getTime() + bump * 1e3).toISOString().replace(/\.\d{3}Z$/, "Z");
114055
+ if (!isTaken(candidate))
114056
+ return candidate;
114057
+ }
114058
+ throw new ComputeFailure("could not allocate a version stamp");
114059
+ }
114060
+ async function tombstoneMarker(scopeDeletions, scope) {
114061
+ if (!scopeDeletions)
114062
+ return null;
114063
+ const verdict = await scopeDeletions.resolve(scope);
114064
+ if (!verdict.deleted || verdict.version === null)
114065
+ return null;
114066
+ const version4 = Number(verdict.version);
114067
+ return Number.isSafeInteger(version4) ? version4 : null;
114068
+ }
114069
+ function localScopesById2(storage, serverOwner) {
114070
+ const byId = /* @__PURE__ */ new Map();
114071
+ for (let offset = 0; ; offset += LOCAL_SCOPE_SCAN_PAGE) {
114072
+ const { scopes, total } = storage.listScopes({
114073
+ limit: LOCAL_SCOPE_SCAN_PAGE,
114074
+ offset
114075
+ });
114076
+ for (const summary of scopes) {
114077
+ byId.set(computeDataPointId(serverOwner, summary.scope), summary.scope);
114078
+ }
114079
+ if (scopes.length === 0 || offset + scopes.length >= total)
114080
+ break;
114081
+ }
114082
+ return byId;
114083
+ }
114084
+ async function assertNoLineageCycle(deps, registration, serverOwner, sourceLineage) {
114085
+ const derivedId = computeDataPointId(serverOwner, registration.derivedScope);
114086
+ let byId = null;
114087
+ const visited = /* @__PURE__ */ new Set();
114088
+ const stack = [];
114089
+ for (const [scope, sources] of sourceLineage) {
114090
+ for (const id2 of sources)
114091
+ stack.push({ id: id2, path: [scope] });
114092
+ }
114093
+ while (stack.length > 0) {
114094
+ const { id: id2, path } = stack.pop();
114095
+ if (id2 === derivedId) {
114096
+ throw new DerivativeCycleError({
114097
+ derivedScope: registration.derivedScope,
114098
+ path: [registration.derivedScope, ...path, registration.derivedScope]
114099
+ });
114100
+ }
114101
+ if (visited.has(id2))
114102
+ continue;
114103
+ visited.add(id2);
114104
+ byId ??= localScopesById2(deps.storage, serverOwner);
114105
+ const scope = byId.get(id2);
114106
+ if (!scope)
114107
+ continue;
114108
+ const entry = deps.storage.findEntry({ scope });
114109
+ if (!entry)
114110
+ continue;
114111
+ let sources = [];
114112
+ try {
114113
+ const envelope = await deps.storage.readEnvelope(scope, entry.collectedAt);
114114
+ sources = readStoredLineage(envelope.data)?.sources ?? [];
114115
+ } catch {
114116
+ }
114117
+ for (const next of sources)
114118
+ stack.push({ id: next, path: [...path, scope] });
114119
+ }
114120
+ }
114121
+ async function loadSource(deps, scope) {
114122
+ const entry = deps.storage.findEntry({ scope });
114123
+ const deletion = await resolveReadDeletion({ scopeDeletions: deps.scopeDeletions, serverOwner: deps.serverOwner }, scope, entry);
114124
+ if (deletion) {
114125
+ throw new ComputeFailure(`source scope ${scope} is deleted`);
114126
+ }
114127
+ if (!entry) {
114128
+ throw new ComputeFailure(`source scope ${scope} has no local data`);
114129
+ }
114130
+ let envelope;
114131
+ try {
114132
+ envelope = await deps.storage.readEnvelope(scope, entry.collectedAt);
114133
+ } catch {
114134
+ throw new ComputeFailure(`source scope ${scope} could not be read`);
114135
+ }
114136
+ let lineageSources = [];
114137
+ try {
114138
+ lineageSources = readStoredLineage(envelope.data)?.sources ?? [];
114139
+ } catch {
114140
+ }
114141
+ const raw = isBinaryEnvelope(envelope) ? {
114142
+ binary: true,
114143
+ note: "binary record; its content is not included in the prompt"
114144
+ } : envelope.data;
114145
+ const trimmed = trimSourceData(raw, {
114146
+ maxItems: deps.maxSourceItems,
114147
+ maxChars: deps.maxSourceChars
114148
+ });
114149
+ return {
114150
+ source: {
114151
+ scope,
114152
+ collectedAt: entry.collectedAt,
114153
+ version: entry.version,
114154
+ data: trimmed.data,
114155
+ kept: trimmed.kept,
114156
+ total: trimmed.total,
114157
+ truncated: trimmed.truncated
114158
+ },
114159
+ lineageSources
114160
+ };
114161
+ }
114162
+ async function assertGrantStillValid(deps, registration) {
114163
+ if (registration.registeredBy.kind !== "builder")
114164
+ return;
114165
+ if (!deps.writePolicyPorts) {
114166
+ throw new ComputeFailure("builder grant verification is not configured");
114167
+ }
114168
+ if (!deps.serverOwner) {
114169
+ throw new ComputeFailure("server owner is not configured");
114170
+ }
114171
+ const { builder, grantId } = registration.registeredBy;
114172
+ const ports = deps.writePolicyPorts;
114173
+ const serverOwner = deps.serverOwner;
114174
+ const grant = await withRetries(deps, () => verifyDataWritePolicy({
114175
+ signer: builder,
114176
+ grantId,
114177
+ requestedScope: registration.derivedScope,
114178
+ serverOwner
114179
+ }, ports), (err2) => !(err2 instanceof ProtocolError));
114180
+ const uncovered = uncoveredSourceScopes(registration.sourceScopes, grant.scopes ?? []);
114181
+ if (uncovered.length > 0) {
114182
+ throw new DerivativeSourceNotGrantedError({ scopes: uncovered });
114183
+ }
114184
+ }
114185
+ async function computeQuestion(questionId, deps) {
114186
+ const now = deps.now ?? (() => /* @__PURE__ */ new Date());
114187
+ if (await deps.runtimeAvailability?.isAvailable() === false) {
114188
+ return { status: "skipped", reason: "runtime-unavailable" };
114189
+ }
114190
+ const registration = await deps.store.get(questionId);
114191
+ if (!registration)
114192
+ return { status: "skipped", reason: "unknown-question" };
114193
+ try {
114194
+ if (!deps.serverOwner) {
114195
+ throw new ComputeFailure("server owner is not configured");
114196
+ }
114197
+ const serverOwner = deps.serverOwner;
114198
+ await assertGrantStillValid(deps, registration);
114199
+ assertDerivedScopeNaming(registration.derivedScope, registration.sourceScopes);
114200
+ const sources = [];
114201
+ const sourceLineage = /* @__PURE__ */ new Map();
114202
+ for (const scope of registration.sourceScopes) {
114203
+ const loaded = await loadSource(deps, scope);
114204
+ sources.push(loaded.source);
114205
+ sourceLineage.set(scope, loaded.lineageSources);
114206
+ }
114207
+ await assertNoLineageCycle(deps, registration, serverOwner, sourceLineage);
114208
+ const messages = buildQuestionMessages({
114209
+ question: registration.question,
114210
+ sources
114211
+ });
114212
+ const model = registration.model ?? deps.provider.defaultModel;
114213
+ const reply = await withRetries(deps, () => deps.provider.chat({ model, messages, maxTokens: deps.maxTokens }), isRetryableInferenceError);
114214
+ const parsed = parseAnswer(reply.content);
114215
+ const computedAt = now().toISOString();
114216
+ const lineageIds = registration.sourceScopes.map((scope) => computeDataPointId(serverOwner, scope));
114217
+ const record2 = {
114218
+ questionId: registration.questionId,
114219
+ question: registration.question,
114220
+ answer: parsed.answer,
114221
+ evidence: parsed.evidence,
114222
+ model,
114223
+ computedAt,
114224
+ sources: sources.map((source) => ({
114225
+ scope: source.scope,
114226
+ version: source.version,
114227
+ collectedAt: source.collectedAt
114228
+ })),
114229
+ lineage: lineageIds,
114230
+ ...reply.receiptId || reply.aciIdentity ? {
114231
+ inference: {
114232
+ ...reply.receiptId ? { receiptId: reply.receiptId } : {},
114233
+ ...reply.aciIdentity ? { aciIdentity: reply.aciIdentity } : {}
114234
+ }
114235
+ } : {}
114236
+ };
114237
+ const lineage = {
114238
+ sources: lineageIds,
114239
+ writtenAt: computedAt
114240
+ };
114241
+ const collectedAt2 = collectedAtStamp(now, (candidate) => deps.storage.findEntry({
114242
+ scope: registration.derivedScope,
114243
+ at: candidate
114244
+ })?.collectedAt === candidate);
114245
+ const written = await ingestDataContract({
114246
+ storage: deps.storage,
114247
+ scopeParam: registration.derivedScope,
114248
+ body: record2,
114249
+ collectedAt: collectedAt2,
114250
+ status: deps.syncManager ? "syncing" : "stored",
114251
+ lineage,
114252
+ afterTombstoneVersion: await tombstoneMarker(deps.scopeDeletions, registration.derivedScope)
114253
+ });
114254
+ if (!written.ok) {
114255
+ throw new ComputeFailure(`derived record rejected: ${written.body.error}`);
114256
+ }
114257
+ const entry = deps.storage.findEntry({
114258
+ scope: registration.derivedScope,
114259
+ at: collectedAt2
114260
+ });
114261
+ const updated = await deps.store.update(questionId, {
114262
+ status: "ready",
114263
+ error: null,
114264
+ updatedAt: computedAt,
114265
+ lastComputedAt: computedAt,
114266
+ derivedVersion: entry?.version ?? null,
114267
+ derivedCollectedAt: collectedAt2
114268
+ });
114269
+ if (deps.syncManager?.notifyNewData) {
114270
+ deps.syncManager.notifyNewData();
114271
+ } else if (deps.syncManager?.trigger) {
114272
+ void deps.syncManager.trigger().catch(() => void 0);
114273
+ }
114274
+ try {
114275
+ deps.onDerivedWritten?.({
114276
+ scope: registration.derivedScope,
114277
+ collectedAt: collectedAt2,
114278
+ lineageSources: lineageIds
114279
+ });
114280
+ } catch (err2) {
114281
+ deps.logger?.warn?.({
114282
+ questionId,
114283
+ derivedScope: registration.derivedScope,
114284
+ error: err2 instanceof Error ? err2.name : String(err2)
114285
+ }, "onDerivedWritten hook failed; derivative already written");
114286
+ }
114287
+ deps.logger?.info?.({
114288
+ questionId,
114289
+ derivedScope: registration.derivedScope,
114290
+ sourceScopes: registration.sourceScopes,
114291
+ model,
114292
+ version: entry?.version ?? null,
114293
+ receiptId: reply.receiptId ?? null
114294
+ }, "Derivative question computed");
114295
+ return {
114296
+ status: "ready",
114297
+ registration: updated ?? { ...registration, status: "ready" }
114298
+ };
114299
+ } catch (err2) {
114300
+ const error51 = shortError(err2);
114301
+ const at3 = now().toISOString();
114302
+ const updated = await deps.store.update(questionId, {
114303
+ status: "failed",
114304
+ error: error51,
114305
+ updatedAt: at3
114306
+ });
114307
+ deps.logger?.warn?.({ questionId, derivedScope: registration.derivedScope, error: error51 }, "Derivative question compute failed");
114308
+ return {
114309
+ status: "failed",
114310
+ registration: updated ?? { ...registration, status: "failed", error: error51 },
114311
+ error: error51
114312
+ };
114313
+ }
114314
+ }
114315
+
114316
+ // ../core/dist/derivatives/scheduler.js
114317
+ var defaultTimers = {
114318
+ setTimeout: (callback, ms3) => setTimeout(callback, ms3),
114319
+ clearTimeout: (handle) => clearTimeout(handle)
114320
+ };
114321
+ function createRecomputeScheduler(options) {
114322
+ const debounceMs = options.debounceMs ?? 5e3;
114323
+ const timers = options.timers ?? defaultTimers;
114324
+ const now = options.now ?? (() => /* @__PURE__ */ new Date());
114325
+ const states = /* @__PURE__ */ new Map();
114326
+ const pending = /* @__PURE__ */ new Set();
114327
+ let stopped = false;
114328
+ function track(promise2) {
114329
+ pending.add(promise2);
114330
+ const done = () => pending.delete(promise2);
114331
+ promise2.then(done, done);
114332
+ return promise2;
114333
+ }
114334
+ function warn(payload, message) {
114335
+ options.logger?.warn?.(payload, message);
114336
+ }
114337
+ function stateFor(questionId) {
114338
+ let state = states.get(questionId);
114339
+ if (!state) {
114340
+ state = { timer: null, running: null, rerun: false };
114341
+ states.set(questionId, state);
114342
+ }
114343
+ return state;
114344
+ }
114345
+ function run(questionId) {
114346
+ if (stopped)
114347
+ return;
114348
+ const state = stateFor(questionId);
114349
+ if (state.running) {
114350
+ state.rerun = true;
114351
+ return;
114352
+ }
114353
+ state.running = track(Promise.resolve().then(() => options.compute(questionId)).then(() => void 0, (err2) => warn({
114354
+ questionId,
114355
+ error: err2 instanceof Error ? err2.name : String(err2)
114356
+ }, "Derivative compute threw")).then(async () => {
114357
+ state.running = null;
114358
+ if (state.rerun) {
114359
+ state.rerun = false;
114360
+ await markStale(questionId);
114361
+ schedule(questionId, 0);
114362
+ } else if (!states.get(questionId)?.timer) {
114363
+ states.delete(questionId);
114364
+ }
114365
+ }));
114366
+ }
114367
+ function schedule(questionId, delayMs) {
114368
+ if (stopped)
114369
+ return;
114370
+ const state = stateFor(questionId);
114371
+ if (state.timer !== null)
114372
+ timers.clearTimeout(state.timer);
114373
+ state.timer = timers.setTimeout(() => {
114374
+ state.timer = null;
114375
+ run(questionId);
114376
+ }, delayMs);
114377
+ }
114378
+ async function markStale(questionId) {
114379
+ const registration = await options.store.get(questionId);
114380
+ if (!registration)
114381
+ return;
114382
+ if (registration.status === "ready" || registration.status === "failed") {
114383
+ await options.store.update(questionId, {
114384
+ status: "stale",
114385
+ updatedAt: now().toISOString()
114386
+ });
114387
+ }
114388
+ }
114389
+ return {
114390
+ markSourceChanged(scope, opts) {
114391
+ if (stopped)
114392
+ return;
114393
+ const lineage = new Set((opts?.lineageSources ?? []).map((id2) => id2.toLowerCase()));
114394
+ void track((async () => {
114395
+ const affected = await options.store.list({ sourceScope: scope });
114396
+ for (const registration of affected) {
114397
+ if (options.serverOwner && lineage.has(computeDataPointId(options.serverOwner, registration.derivedScope))) {
114398
+ continue;
114399
+ }
114400
+ await markStale(registration.questionId);
114401
+ schedule(registration.questionId, debounceMs);
114402
+ }
114403
+ })().catch((err2) => warn({ scope, error: err2 instanceof Error ? err2.name : String(err2) }, "Could not mark derivative questions stale")));
114404
+ },
114405
+ requestRecompute(questionId, opts) {
114406
+ if (stopped)
114407
+ return;
114408
+ void track(markStale(questionId).catch((err2) => warn({
114409
+ questionId,
114410
+ error: err2 instanceof Error ? err2.name : String(err2)
114411
+ }, "Could not mark derivative question stale")).then(() => schedule(questionId, opts?.immediate ? 0 : debounceMs)));
114412
+ },
114413
+ async whenIdle() {
114414
+ const waitForTimers = !options.timers;
114415
+ for (; ; ) {
114416
+ while (pending.size > 0) {
114417
+ await Promise.allSettled([...pending]);
114418
+ }
114419
+ const busy = [...states.values()].some((state) => state.running !== null || waitForTimers && state.timer !== null);
114420
+ if (!busy)
114421
+ return;
114422
+ await new Promise((resolve) => setTimeout(resolve, 5));
114423
+ }
114424
+ },
114425
+ stop() {
114426
+ stopped = true;
114427
+ for (const state of states.values()) {
114428
+ if (state.timer !== null)
114429
+ timers.clearTimeout(state.timer);
114430
+ state.timer = null;
114431
+ }
114432
+ },
114433
+ start() {
114434
+ if (!stopped)
114435
+ return;
114436
+ stopped = false;
114437
+ void track((async () => {
114438
+ for (const registration of await options.store.list()) {
114439
+ if (registration.status === "pending" || registration.status === "stale") {
114440
+ schedule(registration.questionId, 0);
114441
+ }
114442
+ }
114443
+ })().catch((err2) => warn({ error: err2 instanceof Error ? err2.name : String(err2) }, "Could not reschedule derivative questions")));
114444
+ }
114445
+ };
114446
+ }
114447
+
114448
+ // ../core/dist/derivatives/api.js
114449
+ var MAX_QUESTION_BODY_BYTES = 16 * 1024;
114450
+ function jsonResponse2(body, init) {
114451
+ const headers = new Headers(init?.headers);
114452
+ headers.set("Content-Type", "application/json");
114453
+ return new Response(JSON.stringify(body), { ...init, headers });
114454
+ }
114455
+ function errorResponse2(status2, errorCode, message) {
114456
+ return jsonResponse2({ error: { code: status2, errorCode, message } }, { status: status2 });
114457
+ }
114458
+ function stripBasePath2(pathname, basePath) {
114459
+ if (!basePath || basePath === "/")
114460
+ return pathname;
114461
+ if (pathname === basePath)
114462
+ return "/";
114463
+ if (pathname.startsWith(`${basePath}/`))
114464
+ return pathname.slice(basePath.length);
114465
+ return pathname;
114466
+ }
114467
+ async function authorizeOwnerOrWriter(deps, request2, scope) {
114468
+ if (deps.auth.authorizeWrite) {
114469
+ return await deps.auth.authorizeWrite({ request: request2, scope }) ?? void 0;
114470
+ }
114471
+ await deps.auth.authorizeOwner(request2);
114472
+ return void 0;
114473
+ }
114474
+ function sameBuilder(a10, b10) {
114475
+ return a10.toLowerCase() === b10.toLowerCase();
114476
+ }
114477
+ async function loadForCaller(deps, request2, store, questionId) {
114478
+ const registration = await store.get(questionId);
114479
+ if (!registration) {
114480
+ await deps.auth.authorizeOwner(request2);
114481
+ throw new DerivativeQuestionNotFoundError({ questionId });
114482
+ }
114483
+ const writer = await authorizeOwnerOrWriter(deps, request2, registration.derivedScope);
114484
+ if (writer) {
114485
+ const by2 = registration.registeredBy;
114486
+ if (by2.kind !== "builder" || !sameBuilder(by2.builder, writer.builder)) {
114487
+ await writer.releaseProof?.();
114488
+ throw new DerivativeQuestionNotFoundError({ questionId });
114489
+ }
114490
+ }
114491
+ return { registration, writer };
114492
+ }
114493
+ async function handlePersonalServerDerivativesRequest(request2, deps, options = {}) {
114494
+ try {
114495
+ const url2 = new URL(request2.url);
114496
+ const pathname = stripBasePath2(url2.pathname, options.basePath);
114497
+ const parts = pathname.split("/").filter(Boolean);
114498
+ if (parts[0] !== "questions" || parts.length > 3) {
114499
+ return errorResponse2(404, "NOT_FOUND", "Not found");
114500
+ }
114501
+ const compute = deps.compute;
114502
+ if (!compute)
114503
+ throw new DerivativeComputeUnavailableError();
114504
+ const { store, scheduler } = compute;
114505
+ const now = deps.now ?? (() => /* @__PURE__ */ new Date());
114506
+ if (parts.length === 1) {
114507
+ if (request2.method === "GET") {
114508
+ const derivedScope = url2.searchParams.get("derivedScope") ?? void 0;
114509
+ if (derivedScope) {
114510
+ const writer = await authorizeOwnerOrWriter(deps, request2, derivedScope);
114511
+ const registrations2 = await store.list({
114512
+ derivedScope,
114513
+ ...writer ? { builder: writer.builder } : {}
114514
+ });
114515
+ return jsonResponse2({
114516
+ questions: registrations2.map(questionRegistrationView)
114517
+ });
114518
+ }
114519
+ await deps.auth.authorizeOwner(request2);
114520
+ const registrations = await store.list();
114521
+ return jsonResponse2({
114522
+ questions: registrations.map(questionRegistrationView)
114523
+ });
114524
+ }
114525
+ if (request2.method === "POST") {
114526
+ const declared = Number(request2.headers.get("content-length") ?? "0");
114527
+ if (declared > MAX_QUESTION_BODY_BYTES) {
114528
+ throw new ContentTooLargeError({ max: MAX_QUESTION_BODY_BYTES });
114529
+ }
114530
+ const bodyBytes = new Uint8Array(await request2.clone().arrayBuffer());
114531
+ if (bodyBytes.byteLength > MAX_QUESTION_BODY_BYTES) {
114532
+ throw new ContentTooLargeError({ max: MAX_QUESTION_BODY_BYTES });
114533
+ }
114534
+ const parsed = await parseJsonObjectBody(request2.clone(), "Request body must be valid JSON");
114535
+ if (!parsed.ok) {
114536
+ return jsonResponse2(parsed.result.body, {
114537
+ status: parsed.result.status
114538
+ });
114539
+ }
114540
+ const rawScope = parsed.body.derivedScope;
114541
+ const scopeForAuth = typeof rawScope === "string" ? rawScope : "";
114542
+ const writer = await authorizeOwnerOrWriter(deps, request2, scopeForAuth);
114543
+ const registeredBy = writer ? {
114544
+ kind: "builder",
114545
+ builder: writer.builder,
114546
+ grantId: writer.grantId
114547
+ } : { kind: "owner" };
114548
+ let registration;
114549
+ try {
114550
+ if (writer) {
114551
+ const input = parseQuestionInput(parsed.body);
114552
+ const uncovered = uncoveredSourceScopes(input.sourceScopes, writer.grantScopes);
114553
+ if (uncovered.length > 0) {
114554
+ throw new DerivativeSourceNotGrantedError({ scopes: uncovered });
114555
+ }
114556
+ }
114557
+ registration = await createQuestionRegistration({
114558
+ body: parsed.body,
114559
+ registeredBy,
114560
+ store,
114561
+ questionId: deps.createQuestionId?.() ?? crypto.randomUUID(),
114562
+ now
114563
+ });
114564
+ } catch (err2) {
114565
+ await writer?.releaseProof?.();
114566
+ throw err2;
114567
+ }
114568
+ deps.logger?.info?.({
114569
+ questionId: registration.questionId,
114570
+ derivedScope: registration.derivedScope,
114571
+ sourceScopes: registration.sourceScopes,
114572
+ registeredBy: registeredBy.kind,
114573
+ ...writer ? { builder: writer.builder, grantId: writer.grantId } : {}
114574
+ }, "Derivative question registered");
114575
+ scheduler.requestRecompute(registration.questionId, {
114576
+ immediate: true
114577
+ });
114578
+ return jsonResponse2(questionRegistrationView(registration), {
114579
+ status: 201
114580
+ });
114581
+ }
114582
+ return errorResponse2(405, "METHOD_NOT_ALLOWED", "Method not allowed");
114583
+ }
114584
+ const questionId = decodeURIComponent(parts[1] ?? "");
114585
+ if (parts.length === 3) {
114586
+ if (parts[2] !== "recompute") {
114587
+ return errorResponse2(404, "NOT_FOUND", "Not found");
114588
+ }
114589
+ if (request2.method !== "POST") {
114590
+ return errorResponse2(405, "METHOD_NOT_ALLOWED", "Method not allowed");
114591
+ }
114592
+ const { registration } = await loadForCaller(deps, request2, store, questionId);
114593
+ scheduler.requestRecompute(questionId, { immediate: true });
114594
+ return jsonResponse2({
114595
+ questionId,
114596
+ status: registration.status === "pending" ? "pending" : "stale",
114597
+ derivedScope: registration.derivedScope
114598
+ }, { status: 202 });
114599
+ }
114600
+ if (request2.method === "GET") {
114601
+ const { registration } = await loadForCaller(deps, request2, store, questionId);
114602
+ return jsonResponse2(questionRegistrationView(registration));
114603
+ }
114604
+ if (request2.method === "DELETE") {
114605
+ const { registration } = await loadForCaller(deps, request2, store, questionId);
114606
+ await store.delete(questionId);
114607
+ deps.logger?.info?.({ questionId, derivedScope: registration.derivedScope }, "Derivative question deleted");
114608
+ return jsonResponse2({ questionId, deleted: true });
114609
+ }
114610
+ return errorResponse2(405, "METHOD_NOT_ALLOWED", "Method not allowed");
114611
+ } catch (err2) {
114612
+ if (err2 instanceof ProtocolError) {
114613
+ return jsonResponse2(err2.toJSON(), { status: err2.code });
114614
+ }
114615
+ return errorResponse2(500, "INTERNAL_ERROR", "Internal server error");
114616
+ }
114617
+ }
114618
+
112706
114619
  // ../core/dist/mcp/store.js
112707
114620
  function createInMemoryMcpOAuthAuthorizationStore() {
112708
114621
  const byId = /* @__PURE__ */ new Map();
@@ -112761,6 +114674,16 @@ var McpDataReadError = class extends Error {
112761
114674
  };
112762
114675
  function createMcpDataReadClient(options) {
112763
114676
  const basePath = options.basePath ?? "/v1/data";
114677
+ async function assertScopeReadable(scope, entry) {
114678
+ try {
114679
+ await assertScopeNotDeleted(options.dataApiDeps, scope, entry);
114680
+ } catch (err2) {
114681
+ if (err2 instanceof ProtocolError) {
114682
+ throw new McpDataReadError(err2.code, err2.toJSON());
114683
+ }
114684
+ throw err2;
114685
+ }
114686
+ }
112764
114687
  async function authorizeScopeRead(params) {
112765
114688
  const safeScope = encodeURIComponent(params.scope);
112766
114689
  const signingUri = `${basePath}/${safeScope}`;
@@ -112848,6 +114771,9 @@ function createMcpDataReadClient(options) {
112848
114771
  const entry = storage.findEntry({ scope });
112849
114772
  if (!entry)
112850
114773
  return null;
114774
+ if (await resolveReadDeletion(options.dataApiDeps, scope, entry)) {
114775
+ return null;
114776
+ }
112851
114777
  const hasBlocks = typeof storage.hasScopeBlocks === "function" ? await storage.hasScopeBlocks(scope, entry.collectedAt) : false;
112852
114778
  return {
112853
114779
  scope,
@@ -112876,6 +114802,7 @@ function createMcpDataReadClient(options) {
112876
114802
  message: `No data found for scope "${scope}"`
112877
114803
  });
112878
114804
  }
114805
+ await assertScopeReadable(scope, selectedEntry);
112879
114806
  const { request: request2, authResult } = await authorizeScopeRead({
112880
114807
  scope,
112881
114808
  grantId,
@@ -112933,6 +114860,7 @@ function createMcpDataReadClient(options) {
112933
114860
  message: `No data found for scope "${scope}"`
112934
114861
  });
112935
114862
  }
114863
+ await assertScopeReadable(scope, selectedEntry);
112936
114864
  const { request: request2, authResult } = await authorizeScopeRead({
112937
114865
  scope,
112938
114866
  grantId,
@@ -116596,15 +118524,15 @@ var makeIssue = (params) => {
116596
118524
  message: issueData.message
116597
118525
  };
116598
118526
  }
116599
- let errorMessage2 = "";
118527
+ let errorMessage3 = "";
116600
118528
  const maps = errorMaps.filter((m10) => !!m10).slice().reverse();
116601
118529
  for (const map2 of maps) {
116602
- errorMessage2 = map2(fullIssue, { data, defaultError: errorMessage2 }).message;
118530
+ errorMessage3 = map2(fullIssue, { data, defaultError: errorMessage3 }).message;
116603
118531
  }
116604
118532
  return {
116605
118533
  ...issueData,
116606
118534
  path: fullPath,
116607
- message: errorMessage2
118535
+ message: errorMessage3
116608
118536
  };
116609
118537
  };
116610
118538
  function addIssueToContext(ctx, issueData) {
@@ -121884,19 +123812,19 @@ var getRefs = (options) => {
121884
123812
  };
121885
123813
 
121886
123814
  // ../../node_modules/zod-to-json-schema/dist/esm/errorMessages.js
121887
- function addErrorMessage(res, key, errorMessage2, refs) {
123815
+ function addErrorMessage(res, key, errorMessage3, refs) {
121888
123816
  if (!refs?.errorMessages)
121889
123817
  return;
121890
- if (errorMessage2) {
123818
+ if (errorMessage3) {
121891
123819
  res.errorMessage = {
121892
123820
  ...res.errorMessage,
121893
- [key]: errorMessage2
123821
+ [key]: errorMessage3
121894
123822
  };
121895
123823
  }
121896
123824
  }
121897
- function setResponseValueAndErrors(res, key, value, errorMessage2, refs) {
123825
+ function setResponseValueAndErrors(res, key, value, errorMessage3, refs) {
121898
123826
  res[key] = value;
121899
- addErrorMessage(res, key, errorMessage2, refs);
123827
+ addErrorMessage(res, key, errorMessage3, refs);
121900
123828
  }
121901
123829
 
121902
123830
  // ../../node_modules/zod-to-json-schema/dist/esm/getRelativePath.js
@@ -123207,8 +125135,8 @@ var Protocol = class {
123207
125135
  if (queuedMessage.type === "response") {
123208
125136
  resolver(message);
123209
125137
  } else {
123210
- const errorMessage2 = message;
123211
- const error51 = new McpError(errorMessage2.error.code, errorMessage2.error.message, errorMessage2.error.data);
125138
+ const errorMessage3 = message;
125139
+ const error51 = new McpError(errorMessage3.error.code, errorMessage3.error.message, errorMessage3.error.data);
123212
125140
  resolver(error51);
123213
125141
  }
123214
125142
  } else {
@@ -123396,7 +125324,7 @@ var Protocol = class {
123396
125324
  const capturedTransport = this._transport;
123397
125325
  const relatedTaskId = request2.params?._meta?.[RELATED_TASK_META_KEY]?.taskId;
123398
125326
  if (handler === void 0) {
123399
- const errorResponse3 = {
125327
+ const errorResponse4 = {
123400
125328
  jsonrpc: "2.0",
123401
125329
  id: request2.id,
123402
125330
  error: {
@@ -123407,11 +125335,11 @@ var Protocol = class {
123407
125335
  if (relatedTaskId && this._taskMessageQueue) {
123408
125336
  this._enqueueTaskMessage(relatedTaskId, {
123409
125337
  type: "error",
123410
- message: errorResponse3,
125338
+ message: errorResponse4,
123411
125339
  timestamp: Date.now()
123412
125340
  }, capturedTransport?.sessionId).catch((error51) => this._onerror(new Error(`Failed to enqueue error response: ${error51}`)));
123413
125341
  } else {
123414
- capturedTransport?.send(errorResponse3).catch((error51) => this._onerror(new Error(`Failed to send an error response: ${error51}`)));
125342
+ capturedTransport?.send(errorResponse4).catch((error51) => this._onerror(new Error(`Failed to send an error response: ${error51}`)));
123415
125343
  }
123416
125344
  return;
123417
125345
  }
@@ -123481,7 +125409,7 @@ var Protocol = class {
123481
125409
  if (abortController.signal.aborted) {
123482
125410
  return;
123483
125411
  }
123484
- const errorResponse3 = {
125412
+ const errorResponse4 = {
123485
125413
  jsonrpc: "2.0",
123486
125414
  id: request2.id,
123487
125415
  error: {
@@ -123493,11 +125421,11 @@ var Protocol = class {
123493
125421
  if (relatedTaskId && this._taskMessageQueue) {
123494
125422
  await this._enqueueTaskMessage(relatedTaskId, {
123495
125423
  type: "error",
123496
- message: errorResponse3,
125424
+ message: errorResponse4,
123497
125425
  timestamp: Date.now()
123498
125426
  }, capturedTransport?.sessionId);
123499
125427
  } else {
123500
- await capturedTransport?.send(errorResponse3);
125428
+ await capturedTransport?.send(errorResponse4);
123501
125429
  }
123502
125430
  }).catch((error51) => this._onerror(new Error(`Failed to send response: ${error51}`))).finally(() => {
123503
125431
  if (this._requestHandlerAbortControllers.get(request2.id) === abortController) {
@@ -124508,23 +126436,23 @@ var Server = class extends Protocol {
124508
126436
  const wrappedHandler = async (request2, extra) => {
124509
126437
  const validatedRequest = safeParse3(CallToolRequestSchema, request2);
124510
126438
  if (!validatedRequest.success) {
124511
- const errorMessage2 = validatedRequest.error instanceof Error ? validatedRequest.error.message : String(validatedRequest.error);
124512
- throw new McpError(ErrorCode.InvalidParams, `Invalid tools/call request: ${errorMessage2}`);
126439
+ const errorMessage3 = validatedRequest.error instanceof Error ? validatedRequest.error.message : String(validatedRequest.error);
126440
+ throw new McpError(ErrorCode.InvalidParams, `Invalid tools/call request: ${errorMessage3}`);
124513
126441
  }
124514
126442
  const { params } = validatedRequest.data;
124515
126443
  const result = await Promise.resolve(handler(request2, extra));
124516
126444
  if (params.task) {
124517
126445
  const taskValidationResult = safeParse3(CreateTaskResultSchema, result);
124518
126446
  if (!taskValidationResult.success) {
124519
- const errorMessage2 = taskValidationResult.error instanceof Error ? taskValidationResult.error.message : String(taskValidationResult.error);
124520
- throw new McpError(ErrorCode.InvalidParams, `Invalid task creation result: ${errorMessage2}`);
126447
+ const errorMessage3 = taskValidationResult.error instanceof Error ? taskValidationResult.error.message : String(taskValidationResult.error);
126448
+ throw new McpError(ErrorCode.InvalidParams, `Invalid task creation result: ${errorMessage3}`);
124521
126449
  }
124522
126450
  return taskValidationResult.data;
124523
126451
  }
124524
126452
  const validationResult = safeParse3(CallToolResultSchema, result);
124525
126453
  if (!validationResult.success) {
124526
- const errorMessage2 = validationResult.error instanceof Error ? validationResult.error.message : String(validationResult.error);
124527
- throw new McpError(ErrorCode.InvalidParams, `Invalid tools/call result: ${errorMessage2}`);
126454
+ const errorMessage3 = validationResult.error instanceof Error ? validationResult.error.message : String(validationResult.error);
126455
+ throw new McpError(ErrorCode.InvalidParams, `Invalid tools/call result: ${errorMessage3}`);
124528
126456
  }
124529
126457
  return validationResult.data;
124530
126458
  };
@@ -125240,12 +127168,12 @@ var McpServer = class {
125240
127168
  * @param errorMessage - The error message.
125241
127169
  * @returns The tool error result.
125242
127170
  */
125243
- createToolError(errorMessage2) {
127171
+ createToolError(errorMessage3) {
125244
127172
  return {
125245
127173
  content: [
125246
127174
  {
125247
127175
  type: "text",
125248
- text: errorMessage2
127176
+ text: errorMessage3
125249
127177
  }
125250
127178
  ],
125251
127179
  isError: true
@@ -125263,8 +127191,8 @@ var McpServer = class {
125263
127191
  const parseResult = await safeParseAsync3(schemaToParse, args);
125264
127192
  if (!parseResult.success) {
125265
127193
  const error51 = "error" in parseResult ? parseResult.error : "Unknown error";
125266
- const errorMessage2 = getParseErrorMessage(error51);
125267
- throw new McpError(ErrorCode.InvalidParams, `Input validation error: Invalid arguments for tool ${toolName}: ${errorMessage2}`);
127194
+ const errorMessage3 = getParseErrorMessage(error51);
127195
+ throw new McpError(ErrorCode.InvalidParams, `Input validation error: Invalid arguments for tool ${toolName}: ${errorMessage3}`);
125268
127196
  }
125269
127197
  return parseResult.data;
125270
127198
  }
@@ -125288,8 +127216,8 @@ var McpServer = class {
125288
127216
  const parseResult = await safeParseAsync3(outputObj, result.structuredContent);
125289
127217
  if (!parseResult.success) {
125290
127218
  const error51 = "error" in parseResult ? parseResult.error : "Unknown error";
125291
- const errorMessage2 = getParseErrorMessage(error51);
125292
- throw new McpError(ErrorCode.InvalidParams, `Output validation error: Invalid structured content for tool ${toolName}: ${errorMessage2}`);
127219
+ const errorMessage3 = getParseErrorMessage(error51);
127220
+ throw new McpError(ErrorCode.InvalidParams, `Output validation error: Invalid structured content for tool ${toolName}: ${errorMessage3}`);
125293
127221
  }
125294
127222
  }
125295
127223
  /**
@@ -125501,8 +127429,8 @@ var McpServer = class {
125501
127429
  const parseResult = await safeParseAsync3(argsObj, request2.params.arguments);
125502
127430
  if (!parseResult.success) {
125503
127431
  const error51 = "error" in parseResult ? parseResult.error : "Unknown error";
125504
- const errorMessage2 = getParseErrorMessage(error51);
125505
- throw new McpError(ErrorCode.InvalidParams, `Invalid arguments for prompt ${request2.params.name}: ${errorMessage2}`);
127432
+ const errorMessage3 = getParseErrorMessage(error51);
127433
+ throw new McpError(ErrorCode.InvalidParams, `Invalid arguments for prompt ${request2.params.name}: ${errorMessage3}`);
125506
127434
  }
125507
127435
  const args = parseResult.data;
125508
127436
  const cb2 = prompt.callback;
@@ -127120,16 +129048,16 @@ async function collectDiagnosticsWithTimeout(recorder, options, timeoutMs = DIAG
127120
129048
  }
127121
129049
 
127122
129050
  // ../lite/dist/runtime.js
127123
- function jsonResponse2(body, init) {
129051
+ function jsonResponse3(body, init) {
127124
129052
  const headers = new Headers(init?.headers);
127125
129053
  headers.set("Content-Type", "application/json");
127126
129054
  return new Response(JSON.stringify(body), { ...init, headers });
127127
129055
  }
127128
129056
  function protocolErrorResponse2(err2) {
127129
- return jsonResponse2(err2.toJSON(), { status: err2.code });
129057
+ return jsonResponse3(err2.toJSON(), { status: err2.code });
127130
129058
  }
127131
- function errorResponse2(status2, errorCode, message) {
127132
- return jsonResponse2({
129059
+ function errorResponse3(status2, errorCode, message) {
129060
+ return jsonResponse3({
127133
129061
  error: {
127134
129062
  code: status2,
127135
129063
  errorCode,
@@ -127138,7 +129066,7 @@ function errorResponse2(status2, errorCode, message) {
127138
129066
  }, { status: status2 });
127139
129067
  }
127140
129068
  function mcpUnauthorized(origin, message = "MCP authorization required") {
127141
- return jsonResponse2({
129069
+ return jsonResponse3({
127142
129070
  error: {
127143
129071
  code: 401,
127144
129072
  errorCode: "MCP_AUTH_REQUIRED",
@@ -127187,7 +129115,7 @@ function redirectWithOAuthError(redirectUri, error51, description, state) {
127187
129115
  url2.searchParams.set("state", state);
127188
129116
  return Response.redirect(url2.toString(), 302);
127189
129117
  } catch {
127190
- return jsonResponse2({
129118
+ return jsonResponse3({
127191
129119
  error: error51,
127192
129120
  error_description: description,
127193
129121
  ...state ? { state } : {}
@@ -127393,11 +129321,11 @@ function createPsLiteRuntime(options) {
127393
129321
  if (err2 instanceof ProtocolError) {
127394
129322
  return protocolErrorResponse2(err2);
127395
129323
  }
127396
- return errorResponse2(500, "INTERNAL_ERROR", "Internal server error");
129324
+ return errorResponse3(500, "INTERNAL_ERROR", "Internal server error");
127397
129325
  }
127398
129326
  }
127399
129327
  function sendContractResult(result) {
127400
- return jsonResponse2(result.body, { status: result.status });
129328
+ return jsonResponse3(result.body, { status: result.status });
127401
129329
  }
127402
129330
  function ownerAddress() {
127403
129331
  return options.serverOwner ?? options.identity?.address;
@@ -127405,7 +129333,7 @@ function createPsLiteRuntime(options) {
127405
129333
  async function handleAuthDevice(request2, url2) {
127406
129334
  if (url2.pathname === "/auth/device") {
127407
129335
  if (request2.method !== "POST") {
127408
- return errorResponse2(405, "METHOD_NOT_ALLOWED", "Method not allowed");
129336
+ return errorResponse3(405, "METHOD_NOT_ALLOWED", "Method not allowed");
127409
129337
  }
127410
129338
  return sendContractResult(initiateDeviceSessionContract({
127411
129339
  sessionStore: deviceSessions,
@@ -127419,7 +129347,7 @@ function createPsLiteRuntime(options) {
127419
129347
  }
127420
129348
  if (url2.pathname === "/auth/device/poll") {
127421
129349
  if (request2.method !== "GET") {
127422
- return errorResponse2(405, "METHOD_NOT_ALLOWED", "Method not allowed");
129350
+ return errorResponse3(405, "METHOD_NOT_ALLOWED", "Method not allowed");
127423
129351
  }
127424
129352
  return sendContractResult(pollDeviceSessionContract({
127425
129353
  sessionStore: deviceSessions,
@@ -127431,11 +129359,11 @@ function createPsLiteRuntime(options) {
127431
129359
  if (url2.pathname === "/auth/device/approve") {
127432
129360
  const sessionId = url2.searchParams.get("session");
127433
129361
  if (!sessionId) {
127434
- return request2.method === "GET" ? new Response("Missing session parameter", { status: 400 }) : jsonResponse2({ error: { code: 400, message: "Missing session parameter" } }, { status: 400 });
129362
+ return request2.method === "GET" ? new Response("Missing session parameter", { status: 400 }) : jsonResponse3({ error: { code: 400, message: "Missing session parameter" } }, { status: 400 });
127435
129363
  }
127436
129364
  const session = deviceSessions.get(sessionId);
127437
129365
  if (!session) {
127438
- return request2.method === "GET" ? new Response("Session expired or invalid", { status: 404 }) : jsonResponse2({ error: { code: 404, message: "Session expired or invalid" } }, { status: 404 });
129366
+ return request2.method === "GET" ? new Response("Session expired or invalid", { status: 404 }) : jsonResponse3({ error: { code: 404, message: "Session expired or invalid" } }, { status: 404 });
127439
129367
  }
127440
129368
  if (request2.method === "GET") {
127441
129369
  return new Response("Device authorization pending", {
@@ -127443,10 +129371,10 @@ function createPsLiteRuntime(options) {
127443
129371
  });
127444
129372
  }
127445
129373
  if (request2.method !== "POST") {
127446
- return errorResponse2(405, "METHOD_NOT_ALLOWED", "Method not allowed");
129374
+ return errorResponse3(405, "METHOD_NOT_ALLOWED", "Method not allowed");
127447
129375
  }
127448
129376
  if (session.status === "approved") {
127449
- return jsonResponse2({ status: "already_approved" });
129377
+ return jsonResponse3({ status: "already_approved" });
127450
129378
  }
127451
129379
  await auth.authorizeOwner(request2);
127452
129380
  return sendContractResult(await approveDeviceSessionContract({
@@ -127462,7 +129390,7 @@ function createPsLiteRuntime(options) {
127462
129390
  if (request2.method === "DELETE") {
127463
129391
  const token2 = bearerToken(request2);
127464
129392
  if (!token2) {
127465
- return jsonResponse2({ error: { code: 401, message: "Missing Bearer token" } }, { status: 401 });
129393
+ return jsonResponse3({ error: { code: 401, message: "Missing Bearer token" } }, { status: 401 });
127466
129394
  }
127467
129395
  return sendContractResult(await revokeDeviceTokenContract({
127468
129396
  tokenStore,
@@ -127470,11 +129398,11 @@ function createPsLiteRuntime(options) {
127470
129398
  }));
127471
129399
  }
127472
129400
  if (request2.method !== "POST") {
127473
- return errorResponse2(405, "METHOD_NOT_ALLOWED", "Method not allowed");
129401
+ return errorResponse3(405, "METHOD_NOT_ALLOWED", "Method not allowed");
127474
129402
  }
127475
129403
  const token = bearerToken(request2);
127476
129404
  if (!options.accessToken || token !== options.accessToken) {
127477
- return jsonResponse2({
129405
+ return jsonResponse3({
127478
129406
  error: {
127479
129407
  code: 403,
127480
129408
  message: "Only control-plane tokens can provision Personal Server session tokens"
@@ -127485,10 +129413,10 @@ function createPsLiteRuntime(options) {
127485
129413
  try {
127486
129414
  body = await request2.json();
127487
129415
  } catch {
127488
- return jsonResponse2({ error: { code: 400, message: "Request body must be valid JSON" } }, { status: 400 });
129416
+ return jsonResponse3({ error: { code: 400, message: "Request body must be valid JSON" } }, { status: 400 });
127489
129417
  }
127490
129418
  if (!body.token || typeof body.token !== "string") {
127491
- return jsonResponse2({ error: { code: 400, message: "Missing token" } }, { status: 400 });
129419
+ return jsonResponse3({ error: { code: 400, message: "Missing token" } }, { status: 400 });
127492
129420
  }
127493
129421
  return sendContractResult(await provisionDeviceTokenContract({
127494
129422
  tokenStore,
@@ -127504,10 +129432,12 @@ function createPsLiteRuntime(options) {
127504
129432
  activate() {
127505
129433
  active = true;
127506
129434
  options.syncManager?.start?.();
129435
+ options.derivatives?.scheduler.start();
127507
129436
  },
127508
129437
  deactivate() {
127509
129438
  active = false;
127510
129439
  void options.syncManager?.stop?.();
129440
+ options.derivatives?.scheduler.stop();
127511
129441
  },
127512
129442
  isAvailable() {
127513
129443
  return active;
@@ -127541,7 +129471,7 @@ function createPsLiteRuntime(options) {
127541
129471
  accessLogs: accessLogReader.capabilities?.accessLogs ?? "custom",
127542
129472
  config: options.stateCapabilities?.config ?? (options.saveConfig ? "custom" : "indexeddb")
127543
129473
  };
127544
- return jsonResponse2({
129474
+ return jsonResponse3({
127545
129475
  status: active ? "healthy" : "unavailable",
127546
129476
  runtime: "ps-lite",
127547
129477
  storage: options.storage.kind,
@@ -127559,7 +129489,7 @@ function createPsLiteRuntime(options) {
127559
129489
  }
127560
129490
  if (url2.pathname === "/v1/diagnostics") {
127561
129491
  if (request2.method !== "GET") {
127562
- return errorResponse2(405, "METHOD_NOT_ALLOWED", "Method not allowed");
129492
+ return errorResponse3(405, "METHOD_NOT_ALLOWED", "Method not allowed");
127563
129493
  }
127564
129494
  try {
127565
129495
  await auth.authorizeOwner(request2);
@@ -127570,7 +129500,7 @@ function createPsLiteRuntime(options) {
127570
129500
  throw err2;
127571
129501
  }
127572
129502
  if (!options.diagnostics) {
127573
- return jsonResponse2({
129503
+ return jsonResponse3({
127574
129504
  error: {
127575
129505
  code: 404,
127576
129506
  errorCode: "DIAGNOSTICS_NOT_CONFIGURED",
@@ -127584,7 +129514,7 @@ function createPsLiteRuntime(options) {
127584
129514
  syncStatus: syncStatus2,
127585
129515
  storage: dataStorage
127586
129516
  });
127587
- return jsonResponse2(snapshot);
129517
+ return jsonResponse3(snapshot);
127588
129518
  }
127589
129519
  if (!active) {
127590
129520
  return unavailableResponse();
@@ -127598,6 +129528,7 @@ function createPsLiteRuntime(options) {
127598
129528
  accessLogWriter,
127599
129529
  readFulfillmentReporter: options.readFulfillmentReporter,
127600
129530
  syncManager: options.syncManager ?? null,
129531
+ scopeDeletions: options.scopeDeletions,
127601
129532
  now,
127602
129533
  createLogId,
127603
129534
  // x402 payment enforcement for builder reads. Only engages with a
@@ -127618,9 +129549,16 @@ function createPsLiteRuntime(options) {
127618
129549
  // serverOwner is absent the accessRecord is safely omitted.
127619
129550
  serverOwner: options.serverOwner,
127620
129551
  serverSigner: x402ServerSigner,
127621
- lineageGateway: options.lineageGateway
129552
+ lineageGateway: options.lineageGateway,
129553
+ // Recompute on refresh: a new local version marks every
129554
+ // question that reads the scope stale.
129555
+ onDataWritten: options.derivatives ? (event) => options.derivatives?.scheduler.markSourceChanged(event.scope, { lineageSources: event.lineageSources }) : void 0
127622
129556
  }, { basePath: dataPrefix });
127623
129557
  }
129558
+ const derivativesPrefix = "/v1/derivatives";
129559
+ if (url2.pathname.startsWith(`${derivativesPrefix}/`)) {
129560
+ return handlePersonalServerDerivativesRequest(request2, { auth, compute: options.derivatives ?? null, now }, { basePath: derivativesPrefix });
129561
+ }
127624
129562
  if (url2.pathname.startsWith("/auth/device")) {
127625
129563
  const response = await handleAuthDevice(request2, url2);
127626
129564
  if (response)
@@ -127709,7 +129647,7 @@ function createPsLiteRuntime(options) {
127709
129647
  if (mcpResponse)
127710
129648
  return mcpResponse;
127711
129649
  }
127712
- return errorResponse2(404, "NOT_FOUND", "Not found");
129650
+ return errorResponse3(404, "NOT_FOUND", "Not found");
127713
129651
  });
127714
129652
  }
127715
129653
  };
@@ -127720,22 +129658,22 @@ async function handleMcpRoute(input) {
127720
129658
  const ownerAuthorizationPrefix = "/v1/mcp/oauth/authorizations";
127721
129659
  if (pathname === "/.well-known/oauth-protected-resource/mcp") {
127722
129660
  if (!resolveMcpApprovalUrl(input.approvalUrl)) {
127723
- return errorResponse2(404, "MCP_OAUTH_NOT_CONFIGURED", "MCP OAuth is not configured");
129661
+ return errorResponse3(404, "MCP_OAUTH_NOT_CONFIGURED", "MCP OAuth is not configured");
127724
129662
  }
127725
- return jsonResponse2(protectedResourceMetadata(input.serverOrigin));
129663
+ return jsonResponse3(protectedResourceMetadata(input.serverOrigin));
127726
129664
  }
127727
129665
  if (pathname === "/.well-known/oauth-authorization-server") {
127728
129666
  if (!resolveMcpApprovalUrl(input.approvalUrl)) {
127729
- return errorResponse2(404, "MCP_OAUTH_NOT_CONFIGURED", "MCP OAuth is not configured");
129667
+ return errorResponse3(404, "MCP_OAUTH_NOT_CONFIGURED", "MCP OAuth is not configured");
127730
129668
  }
127731
- return jsonResponse2(authorizationServerMetadata(input.serverOrigin));
129669
+ return jsonResponse3(authorizationServerMetadata(input.serverOrigin));
127732
129670
  }
127733
129671
  if (pathname === "/mcp/oauth/register") {
127734
129672
  if (!resolveMcpApprovalUrl(input.approvalUrl)) {
127735
- return errorResponse2(404, "MCP_OAUTH_NOT_CONFIGURED", "MCP OAuth is not configured");
129673
+ return errorResponse3(404, "MCP_OAUTH_NOT_CONFIGURED", "MCP OAuth is not configured");
127736
129674
  }
127737
129675
  if (input.request.method !== "POST") {
127738
- return errorResponse2(405, "METHOD_NOT_ALLOWED", "Method not allowed");
129676
+ return errorResponse3(405, "METHOD_NOT_ALLOWED", "Method not allowed");
127739
129677
  }
127740
129678
  let body = {};
127741
129679
  try {
@@ -127743,7 +129681,7 @@ async function handleMcpRoute(input) {
127743
129681
  } catch {
127744
129682
  body = {};
127745
129683
  }
127746
- return jsonResponse2({
129684
+ return jsonResponse3({
127747
129685
  client_id: `mcp-client-${crypto.randomUUID()}`,
127748
129686
  client_name: body.client_name ?? "Claude",
127749
129687
  redirect_uris: Array.isArray(body.redirect_uris) ? body.redirect_uris : [],
@@ -127754,10 +129692,10 @@ async function handleMcpRoute(input) {
127754
129692
  }
127755
129693
  if (pathname === "/mcp/oauth/authorize") {
127756
129694
  if (!resolveMcpApprovalUrl(input.approvalUrl)) {
127757
- return errorResponse2(404, "MCP_OAUTH_NOT_CONFIGURED", "MCP OAuth is not configured");
129695
+ return errorResponse3(404, "MCP_OAUTH_NOT_CONFIGURED", "MCP OAuth is not configured");
127758
129696
  }
127759
129697
  if (input.request.method !== "GET") {
127760
- return errorResponse2(405, "METHOD_NOT_ALLOWED", "Method not allowed");
129698
+ return errorResponse3(405, "METHOD_NOT_ALLOWED", "Method not allowed");
127761
129699
  }
127762
129700
  const responseType = input.url.searchParams.get("response_type");
127763
129701
  const clientId = input.url.searchParams.get("client_id") ?? "";
@@ -127785,7 +129723,7 @@ async function handleMcpRoute(input) {
127785
129723
  });
127786
129724
  const approvalUrl = resolveMcpApprovalUrl(input.approvalUrl);
127787
129725
  if (!approvalUrl) {
127788
- return errorResponse2(500, "MCP_APPROVAL_URL_MISSING", "MCP OAuth approval URL is not configured");
129726
+ return errorResponse3(500, "MCP_APPROVAL_URL_MISSING", "MCP OAuth approval URL is not configured");
127789
129727
  }
127790
129728
  const approve = new URL(approvalUrl);
127791
129729
  approve.searchParams.set("mcp_authorization", created.authorizationId);
@@ -127798,14 +129736,14 @@ async function handleMcpRoute(input) {
127798
129736
  }
127799
129737
  if (pathname === "/mcp/oauth/token") {
127800
129738
  if (!resolveMcpApprovalUrl(input.approvalUrl)) {
127801
- return errorResponse2(404, "MCP_OAUTH_NOT_CONFIGURED", "MCP OAuth is not configured");
129739
+ return errorResponse3(404, "MCP_OAUTH_NOT_CONFIGURED", "MCP OAuth is not configured");
127802
129740
  }
127803
129741
  if (input.request.method !== "POST") {
127804
- return errorResponse2(405, "METHOD_NOT_ALLOWED", "Method not allowed");
129742
+ return errorResponse3(405, "METHOD_NOT_ALLOWED", "Method not allowed");
127805
129743
  }
127806
129744
  const body = await parseFormBody(input.request);
127807
129745
  if (body.get("grant_type") !== "authorization_code") {
127808
- return jsonResponse2({
129746
+ return jsonResponse3({
127809
129747
  error: "unsupported_grant_type",
127810
129748
  error_description: "Only authorization_code is supported"
127811
129749
  }, { status: 400 });
@@ -127821,13 +129759,13 @@ async function handleMcpRoute(input) {
127821
129759
  connectionStore: input.store,
127822
129760
  now: input.now
127823
129761
  });
127824
- return jsonResponse2({
129762
+ return jsonResponse3({
127825
129763
  access_token: token.accessToken,
127826
129764
  token_type: "Bearer",
127827
129765
  ...token.scope ? { scope: token.scope } : {}
127828
129766
  });
127829
129767
  } catch (err2) {
127830
- return jsonResponse2({
129768
+ return jsonResponse3({
127831
129769
  error: err2 instanceof McpOAuthAuthorizationError ? err2.code : "invalid_grant",
127832
129770
  error_description: err2 instanceof Error ? err2.message : String(err2)
127833
129771
  }, { status: 400 });
@@ -127845,28 +129783,28 @@ async function handleMcpRoute(input) {
127845
129783
  const tail = pathname.slice(ownerAuthorizationPrefix.length + 1);
127846
129784
  const [id2, action] = tail.split("/");
127847
129785
  if (!id2) {
127848
- return errorResponse2(404, "NOT_FOUND", "Not found");
129786
+ return errorResponse3(404, "NOT_FOUND", "Not found");
127849
129787
  }
127850
129788
  if (!action && input.request.method === "GET") {
127851
129789
  const record2 = await input.authorizationStore.getById(id2);
127852
129790
  if (!record2) {
127853
- return errorResponse2(404, "NOT_FOUND", "Authorization not found");
129791
+ return errorResponse3(404, "NOT_FOUND", "Authorization not found");
127854
129792
  }
127855
- return jsonResponse2(toMcpOAuthAuthorizationView(record2));
129793
+ return jsonResponse3(toMcpOAuthAuthorizationView(record2));
127856
129794
  }
127857
129795
  if (action === "approve") {
127858
129796
  if (input.request.method !== "POST") {
127859
- return errorResponse2(405, "METHOD_NOT_ALLOWED", "Method not allowed");
129797
+ return errorResponse3(405, "METHOD_NOT_ALLOWED", "Method not allowed");
127860
129798
  }
127861
129799
  let body = {};
127862
129800
  try {
127863
129801
  body = await input.request.json();
127864
129802
  } catch {
127865
- return errorResponse2(400, "INVALID_BODY", "Body must be JSON");
129803
+ return errorResponse3(400, "INVALID_BODY", "Body must be JSON");
127866
129804
  }
127867
129805
  if (Array.isArray(body.scopes) && body.scopes.length > 0) {
127868
129806
  if (!input.gateway || !input.gatewayConfig?.url) {
127869
- return errorResponse2(500, "SERVER_NOT_CONFIGURED", "Gateway config is not configured");
129807
+ return errorResponse3(500, "SERVER_NOT_CONFIGURED", "Gateway config is not configured");
127870
129808
  }
127871
129809
  try {
127872
129810
  const approved = await approveMcpOAuthAuthorizationWithScopes({
@@ -127884,16 +129822,16 @@ async function handleMcpRoute(input) {
127884
129822
  serverSigner: input.serverSigner,
127885
129823
  now: input.now
127886
129824
  });
127887
- return jsonResponse2({ redirectTo: approved.redirectTo });
129825
+ return jsonResponse3({ redirectTo: approved.redirectTo });
127888
129826
  } catch (err2) {
127889
129827
  if (err2 instanceof McpOAuthAuthorizationError) {
127890
- return errorResponse2(err2.status, err2.code, err2.message);
129828
+ return errorResponse3(err2.status, err2.code, err2.message);
127891
129829
  }
127892
129830
  throw err2;
127893
129831
  }
127894
129832
  }
127895
129833
  if (!Array.isArray(body.grants) || body.grants.length === 0) {
127896
- return errorResponse2(400, "GRANTS_REQUIRED", "Approve requires grants or scopes");
129834
+ return errorResponse3(400, "GRANTS_REQUIRED", "Approve requires grants or scopes");
127897
129835
  }
127898
129836
  try {
127899
129837
  const approved = await approveMcpOAuthAuthorization({ authorizationId: id2, grants: body.grants }, {
@@ -127901,15 +129839,15 @@ async function handleMcpRoute(input) {
127901
129839
  authorizationStore: input.authorizationStore,
127902
129840
  now: input.now
127903
129841
  });
127904
- return jsonResponse2({ redirectTo: approved.redirectTo });
129842
+ return jsonResponse3({ redirectTo: approved.redirectTo });
127905
129843
  } catch (err2) {
127906
129844
  if (err2 instanceof McpOAuthAuthorizationError) {
127907
- return errorResponse2(err2.status, err2.code, err2.message);
129845
+ return errorResponse3(err2.status, err2.code, err2.message);
127908
129846
  }
127909
129847
  throw err2;
127910
129848
  }
127911
129849
  }
127912
- return errorResponse2(404, "NOT_FOUND", "Not found");
129850
+ return errorResponse3(404, "NOT_FOUND", "Not found");
127913
129851
  }
127914
129852
  if (pathname === ownerPrefix || pathname.startsWith(`${ownerPrefix}/`)) {
127915
129853
  try {
@@ -127933,41 +129871,41 @@ async function handleMcpRoute(input) {
127933
129871
  publicOrigin: input.serverOrigin,
127934
129872
  now: input.now
127935
129873
  });
127936
- return jsonResponse2(created, { status: 201 });
129874
+ return jsonResponse3(created, { status: 201 });
127937
129875
  }
127938
129876
  if (input.request.method === "GET") {
127939
129877
  const records = await listMcpConnectionViews(input.store);
127940
- return jsonResponse2({ connections: records });
129878
+ return jsonResponse3({ connections: records });
127941
129879
  }
127942
- return errorResponse2(405, "METHOD_NOT_ALLOWED", "Method not allowed");
129880
+ return errorResponse3(405, "METHOD_NOT_ALLOWED", "Method not allowed");
127943
129881
  }
127944
129882
  const tail = pathname.slice(ownerPrefix.length + 1);
127945
129883
  const [id2, action] = tail.split("/");
127946
129884
  if (!id2) {
127947
- return errorResponse2(404, "NOT_FOUND", "Not found");
129885
+ return errorResponse3(404, "NOT_FOUND", "Not found");
127948
129886
  }
127949
129887
  if (action === "approve") {
127950
129888
  if (input.request.method !== "POST") {
127951
- return errorResponse2(405, "METHOD_NOT_ALLOWED", "Method not allowed");
129889
+ return errorResponse3(405, "METHOD_NOT_ALLOWED", "Method not allowed");
127952
129890
  }
127953
129891
  let body = {};
127954
129892
  try {
127955
129893
  body = await input.request.json();
127956
129894
  } catch {
127957
- return errorResponse2(400, "INVALID_BODY", "Body must be JSON");
129895
+ return errorResponse3(400, "INVALID_BODY", "Body must be JSON");
127958
129896
  }
127959
129897
  if (!Array.isArray(body.grants) || body.grants.length === 0) {
127960
- return errorResponse2(400, "GRANTS_REQUIRED", "Approve requires at least one grant \u2014 mint grants in the consent flow first");
129898
+ return errorResponse3(400, "GRANTS_REQUIRED", "Approve requires at least one grant \u2014 mint grants in the consent flow first");
127961
129899
  }
127962
129900
  try {
127963
129901
  const updated = await approveMcpConnection({ connectionId: id2, grants: body.grants }, { store: input.store, now: input.now });
127964
- return jsonResponse2(toMcpConnectionView(updated));
129902
+ return jsonResponse3(toMcpConnectionView(updated));
127965
129903
  } catch (err2) {
127966
129904
  if (err2 instanceof McpConnectionNotFoundError) {
127967
- return errorResponse2(404, "NOT_FOUND", err2.message);
129905
+ return errorResponse3(404, "NOT_FOUND", err2.message);
127968
129906
  }
127969
129907
  if (err2 instanceof McpConnectionStateError) {
127970
- return errorResponse2(409, "INVALID_STATE", err2.message);
129908
+ return errorResponse3(409, "INVALID_STATE", err2.message);
127971
129909
  }
127972
129910
  throw err2;
127973
129911
  }
@@ -127979,22 +129917,22 @@ async function handleMcpRoute(input) {
127979
129917
  store: input.store,
127980
129918
  now: input.now
127981
129919
  });
127982
- return jsonResponse2(toMcpConnectionView(updated));
129920
+ return jsonResponse3(toMcpConnectionView(updated));
127983
129921
  } catch (err2) {
127984
129922
  if (err2 instanceof McpConnectionNotFoundError) {
127985
- return errorResponse2(404, "NOT_FOUND", err2.message);
129923
+ return errorResponse3(404, "NOT_FOUND", err2.message);
127986
129924
  }
127987
129925
  throw err2;
127988
129926
  }
127989
129927
  }
127990
- return errorResponse2(405, "METHOD_NOT_ALLOWED", "Method not allowed");
129928
+ return errorResponse3(405, "METHOD_NOT_ALLOWED", "Method not allowed");
127991
129929
  }
127992
- return errorResponse2(404, "NOT_FOUND", "Not found");
129930
+ return errorResponse3(404, "NOT_FOUND", "Not found");
127993
129931
  }
127994
129932
  async function handleMcpToken(rawToken, options) {
127995
129933
  if (!rawToken) {
127996
129934
  if (!options.oauthChallenge) {
127997
- return errorResponse2(401, "INVALID_TOKEN", "Missing MCP connection token");
129935
+ return errorResponse3(401, "INVALID_TOKEN", "Missing MCP connection token");
127998
129936
  }
127999
129937
  return mcpUnauthorized(input.serverOrigin);
128000
129938
  }
@@ -128002,7 +129940,7 @@ async function handleMcpRoute(input) {
128002
129940
  const record2 = await input.store.getByTokenHash(tokenHash);
128003
129941
  if (!record2) {
128004
129942
  if (!options.oauthChallenge) {
128005
- return errorResponse2(401, "INVALID_TOKEN", "Unknown or revoked MCP connection");
129943
+ return errorResponse3(401, "INVALID_TOKEN", "Unknown or revoked MCP connection");
128006
129944
  }
128007
129945
  return mcpUnauthorized(input.serverOrigin, "Unknown or revoked MCP connection");
128008
129946
  }
@@ -128046,10 +129984,10 @@ async function handleMcpRoute(input) {
128046
129984
  throw err2;
128047
129985
  }
128048
129986
  if (input.request.method !== "GET") {
128049
- return errorResponse2(405, "METHOD_NOT_ALLOWED", "Method not allowed");
129987
+ return errorResponse3(405, "METHOD_NOT_ALLOWED", "Method not allowed");
128050
129988
  }
128051
129989
  const snapshot = input.activityRecorder ? input.activityRecorder.snapshot() : { events: [], running: 0, total: 0 };
128052
- return jsonResponse2(snapshot);
129990
+ return jsonResponse3(snapshot);
128053
129991
  }
128054
129992
  if (pathname === "/mcp") {
128055
129993
  return handleMcpToken(bearerToken(input.request), {
@@ -128060,7 +129998,7 @@ async function handleMcpRoute(input) {
128060
129998
  if (pathname.startsWith(mcpPrefix)) {
128061
129999
  const rawToken = decodeURIComponent(pathname.slice(mcpPrefix.length));
128062
130000
  if (!rawToken || rawToken.includes("/")) {
128063
- return errorResponse2(404, "NOT_FOUND", "Not found");
130001
+ return errorResponse3(404, "NOT_FOUND", "Not found");
128064
130002
  }
128065
130003
  return handleMcpToken(rawToken, { oauthChallenge: false });
128066
130004
  }
@@ -128074,6 +130012,45 @@ var OrphanedEntryError = class extends Error {
128074
130012
  this.name = "OrphanedEntryError";
128075
130013
  }
128076
130014
  };
130015
+ var DeletedScopeEntryError = class extends Error {
130016
+ constructor(path, deletedAt) {
130017
+ super(`Dropped local entry for a scope deleted at ${deletedAt}: ${path}`);
130018
+ this.name = "DeletedScopeEntryError";
130019
+ }
130020
+ };
130021
+ async function dropIfCoveredByDeletion(deps, entry, tombstone, deletedAt, uploaded) {
130022
+ const version4 = tombstoneVersion(tombstone);
130023
+ if (!isEntryCoveredByTombstone(entry, { version: version4 }))
130024
+ return;
130025
+ if (uploaded) {
130026
+ try {
130027
+ await deps.storageAdapter.delete(uploaded.url);
130028
+ } catch (err2) {
130029
+ const message = err2 instanceof Error ? err2.message : String(err2);
130030
+ if (deps.pendingBlobDeletions) {
130031
+ await deps.pendingBlobDeletions.add([
130032
+ { scope: entry.scope, version: String(entry.version) }
130033
+ ]);
130034
+ }
130035
+ deps.logger.warn({
130036
+ scope: entry.scope,
130037
+ url: uploaded.url,
130038
+ error: message,
130039
+ queuedForCleanup: Boolean(deps.pendingBlobDeletions)
130040
+ }, "Could not delete the ciphertext uploaded for an entry the tombstone covers");
130041
+ }
130042
+ }
130043
+ await deps.storage.deleteVersion(entry.scope, entry.collectedAt);
130044
+ deps.logger.warn({
130045
+ path: entry.path,
130046
+ scope: entry.scope,
130047
+ deletedAt,
130048
+ tombstoneVersion: version4,
130049
+ entryVersion: entry.version,
130050
+ afterTombstoneVersion: entry.afterTombstoneVersion ?? null
130051
+ }, "Dropped unsynced local entry: the gateway reports its scope as deleted");
130052
+ throw new DeletedScopeEntryError(entry.path, deletedAt);
130053
+ }
128077
130054
  function isMissingPayloadError(err2) {
128078
130055
  return err2 instanceof Error && err2.code === "ENOENT";
128079
130056
  }
@@ -128092,6 +130069,20 @@ async function uploadOne(deps, entry) {
128092
130069
  }
128093
130070
  throw err2;
128094
130071
  }
130072
+ let registerVersion = BigInt(entry.version);
130073
+ if (!entry.dataPointId && deps.dataPointFeed) {
130074
+ const remote = await deps.dataPointFeed.getDataPoint({
130075
+ ownerAddress: serverOwner,
130076
+ scope: entry.scope
130077
+ });
130078
+ const deletedAt = deletionTimestamp(remote);
130079
+ if (remote && deletedAt !== null) {
130080
+ await dropIfCoveredByDeletion(deps, entry, remote, deletedAt);
130081
+ const afterTombstone = BigInt(remote.expectedVersion) + 1n;
130082
+ if (afterTombstone > registerVersion)
130083
+ registerVersion = afterTombstone;
130084
+ }
130085
+ }
128095
130086
  const scopeKey = deriveScopeKey(masterKey, entry.scope);
128096
130087
  const scopeKeyHex = uint8ToHex(scopeKey);
128097
130088
  const plaintext = new TextEncoder().encode(JSON.stringify(envelope));
@@ -128102,7 +130093,7 @@ async function uploadOne(deps, entry) {
128102
130093
  collectedAt: entry.collectedAt,
128103
130094
  sizeBytes: encrypted.byteLength
128104
130095
  })));
128105
- const storageKey = `${entry.scope}/${entry.version}`;
130096
+ const storageKey = `${entry.scope}/${registerVersion}`;
128106
130097
  let url2 = await storageAdapter.upload(storageKey, encrypted);
128107
130098
  let dataPointId;
128108
130099
  if (entry.dataPointId) {
@@ -128152,11 +130143,23 @@ async function uploadOne(deps, entry) {
128152
130143
  return id2;
128153
130144
  };
128154
130145
  try {
128155
- dataPointId = await registerAt(BigInt(entry.version));
130146
+ dataPointId = await registerAt(registerVersion);
130147
+ if (registerVersion !== BigInt(entry.version)) {
130148
+ await storage.updateEntryVersion(entry.path, Number(registerVersion));
130149
+ }
128156
130150
  } catch (err2) {
128157
130151
  if (!isStaleVersionConflict(err2))
128158
130152
  throw err2;
128159
- const record2 = await gateway.getDataPoint(computeDataPointId(serverOwner, entry.scope));
130153
+ const record2 = deps.dataPointFeed ? await deps.dataPointFeed.getDataPoint({
130154
+ ownerAddress: serverOwner,
130155
+ scope: entry.scope
130156
+ }) : await gateway.getDataPoint(computeDataPointId(serverOwner, entry.scope));
130157
+ const conflictDeletedAt = record2 ? deletionTimestamp(record2) : null;
130158
+ if (record2 && conflictDeletedAt !== null) {
130159
+ await dropIfCoveredByDeletion(deps, entry, record2, conflictDeletedAt, {
130160
+ url: url2
130161
+ });
130162
+ }
128160
130163
  if (record2 && record2.dataHash.toLowerCase() === dataHash.toLowerCase()) {
128161
130164
  dataPointId = record2.id;
128162
130165
  const adoptedVersion = Number(record2.expectedVersion);
@@ -128182,6 +130185,7 @@ async function uploadOne(deps, entry) {
128182
130185
  }
128183
130186
  }
128184
130187
  await storage.updateDataPointId(entry.path, dataPointId);
130188
+ deps.scopeDeletions?.markLive(entry.scope);
128185
130189
  }
128186
130190
  logger.info({
128187
130191
  path: entry.path,
@@ -128201,7 +130205,7 @@ async function uploadAll(deps, options) {
128201
130205
  const result = await uploadOne(deps, entry);
128202
130206
  results.push(result);
128203
130207
  } catch (err2) {
128204
- if (err2 instanceof OrphanedEntryError) {
130208
+ if (err2 instanceof OrphanedEntryError || err2 instanceof DeletedScopeEntryError) {
128205
130209
  continue;
128206
130210
  }
128207
130211
  const error51 = err2;
@@ -128230,6 +130234,101 @@ function parseGatewayNextVersion(message) {
128230
130234
  return null;
128231
130235
  }
128232
130236
 
130237
+ // ../core/dist/sync/data-point-feed.js
130238
+ function createGatewayDataPointFeed(options) {
130239
+ const base = options.gatewayUrl.replace(/\/+$/, "");
130240
+ const fetchImpl = options.fetch ?? globalThis.fetch;
130241
+ const now = options.now ?? (() => /* @__PURE__ */ new Date());
130242
+ return {
130243
+ async listDataPointsByOwner(owner, cursor, listOptions) {
130244
+ const params = new URLSearchParams({ user: owner });
130245
+ if (cursor !== null)
130246
+ params.set("cursor", cursor);
130247
+ if (listOptions?.since)
130248
+ params.set("since", listOptions.since);
130249
+ if (listOptions?.limit !== void 0) {
130250
+ params.set("limit", String(listOptions.limit));
130251
+ }
130252
+ if (listOptions?.includeDeleted)
130253
+ params.set("includeDeleted", "true");
130254
+ const res = await fetchImpl(`${base}/v1/data?${params.toString()}`);
130255
+ if (!res.ok) {
130256
+ throw new Error(`Gateway error: ${res.status} ${res.statusText}`);
130257
+ }
130258
+ const envelope = await res.json();
130259
+ const nextCursor = envelope.pagination?.hasMore === false ? null : envelope.pagination?.nextCursor ?? null;
130260
+ const rows = envelope.data?.dataPoints ?? [];
130261
+ return {
130262
+ dataPoints: rows.map((row) => normalizeRecord(row)),
130263
+ cursor: nextCursor
130264
+ };
130265
+ },
130266
+ async getDataPoint(input) {
130267
+ const dataPointId = computeDataPointId(input.ownerAddress, input.scope);
130268
+ const res = await fetchImpl(`${base}/v1/data/${dataPointId}?includeDeleted=true`);
130269
+ if (res.status === 404)
130270
+ return null;
130271
+ if (res.status === 410) {
130272
+ const body2 = await res.json().catch(() => null);
130273
+ const echoed = unwrap(body2);
130274
+ const deletedAt = stringField(echoed, "deletedAt") ?? now().toISOString();
130275
+ return {
130276
+ id: dataPointId,
130277
+ ownerAddress: input.ownerAddress,
130278
+ scope: input.scope,
130279
+ dataHash: stringField(echoed, "dataHash") ?? TOMBSTONE_DATA_HASH,
130280
+ metadataHash: stringField(echoed, "metadataHash") ?? TOMBSTONE_METADATA_HASH,
130281
+ expectedVersion: stringField(echoed, "expectedVersion") ?? "0",
130282
+ addedAt: stringField(echoed, "addedAt") ?? deletedAt,
130283
+ deletedAt
130284
+ };
130285
+ }
130286
+ if (!res.ok) {
130287
+ throw new Error(`Gateway error: ${res.status} ${res.statusText}`);
130288
+ }
130289
+ const body = await res.json();
130290
+ return normalizeRecord(unwrap(body));
130291
+ }
130292
+ };
130293
+ }
130294
+ function unwrap(body) {
130295
+ if (typeof body !== "object" || body === null)
130296
+ return null;
130297
+ const record2 = body;
130298
+ if (typeof record2.data === "object" && record2.data !== null) {
130299
+ return record2.data;
130300
+ }
130301
+ return record2;
130302
+ }
130303
+ function stringField(record2, key) {
130304
+ const value = record2?.[key];
130305
+ return typeof value === "string" ? value : void 0;
130306
+ }
130307
+ function normalizeRecord(row) {
130308
+ const record2 = row ?? {};
130309
+ const deletedAt = record2.deletedAt;
130310
+ return {
130311
+ ...record2,
130312
+ deletedAt: typeof deletedAt === "string" ? deletedAt : null
130313
+ };
130314
+ }
130315
+ function feedFromGatewayClient(gateway) {
130316
+ return {
130317
+ async listDataPointsByOwner(owner, cursor, listOptions) {
130318
+ const { includeDeleted: _includeDeleted, ...sdkOptions } = listOptions ?? {};
130319
+ const result = Object.keys(sdkOptions).length > 0 ? await gateway.listDataPointsByOwner(owner, cursor, sdkOptions) : await gateway.listDataPointsByOwner(owner, cursor);
130320
+ return {
130321
+ dataPoints: result.dataPoints.map((row) => normalizeRecord(row)),
130322
+ cursor: result.cursor
130323
+ };
130324
+ },
130325
+ async getDataPoint(input) {
130326
+ const row = await gateway.getDataPoint(computeDataPointId(input.ownerAddress, input.scope));
130327
+ return row === null ? null : normalizeRecord(row);
130328
+ }
130329
+ };
130330
+ }
130331
+
128233
130332
  // ../core/dist/sync/issues.js
128234
130333
  var DETERMINISTIC_STAGES = /* @__PURE__ */ new Set([
128235
130334
  "openpgp_parse",
@@ -128546,6 +130645,24 @@ async function downloadOne(deps, record2) {
128546
130645
  }
128547
130646
  diagnostics?.onIndexEnd(record2.id, envelope.scope);
128548
130647
  logger.info({ dataPointId: record2.id, scope: envelope.scope, path: relativePath }, "Downloaded and indexed data point");
130648
+ if (deps.onDataPointIndexed) {
130649
+ try {
130650
+ let lineageSources;
130651
+ try {
130652
+ lineageSources = readStoredLineage(envelope.data)?.sources;
130653
+ } catch {
130654
+ }
130655
+ deps.onDataPointIndexed({
130656
+ scope: envelope.scope,
130657
+ dataPointId: record2.id,
130658
+ version: Number(record2.expectedVersion),
130659
+ collectedAt: envelope.collectedAt,
130660
+ lineageSources
130661
+ });
130662
+ } catch (err2) {
130663
+ logger.warn({ scope: envelope.scope, error: err2.message }, "onDataPointIndexed hook failed; data point already indexed");
130664
+ }
130665
+ }
128549
130666
  return {
128550
130667
  dataPointId: record2.id,
128551
130668
  scope: envelope.scope,
@@ -128567,10 +130684,43 @@ async function downloadAll(deps, options = {}) {
128567
130684
  if (options.fullReconcile || repairSummary.missingEnvelopeEntries > 0) {
128568
130685
  options.retryMemory?.onListingReset();
128569
130686
  }
128570
- const { dataPoints, cursor: nextCursor } = await gateway.listDataPointsByOwner(serverOwner, lastCursor);
130687
+ const feed = deps.dataPointFeed ?? feedFromGatewayClient(gateway);
130688
+ const { dataPoints, cursor: nextCursor } = await feed.listDataPointsByOwner(serverOwner, lastCursor, { includeDeleted: true });
130689
+ if (deps.scopeDeletions) {
130690
+ for (const dataPoint of dataPoints) {
130691
+ const deletedAt = deletionTimestamp(dataPoint);
130692
+ if (deletedAt !== null) {
130693
+ deps.scopeDeletions.markDeleted(dataPoint.scope, {
130694
+ deletedAt,
130695
+ version: tombstoneVersion(dataPoint)
130696
+ });
130697
+ } else {
130698
+ deps.scopeDeletions.markLive(dataPoint.scope);
130699
+ }
130700
+ }
130701
+ if (nextCursor === null) {
130702
+ deps.scopeDeletions.noteFeedSynced(void 0, {
130703
+ full: lastCursor === null
130704
+ });
130705
+ }
130706
+ }
128571
130707
  const results = [];
128572
130708
  let failed = false;
128573
130709
  for (const dataPoint of dataPoints) {
130710
+ const deletedAt = deletionTimestamp(dataPoint);
130711
+ if (deletedAt !== null) {
130712
+ try {
130713
+ await reconcileDeletedDataPoint(deps, dataPoint, deletedAt);
130714
+ } catch (err2) {
130715
+ logger.error({
130716
+ dataPointId: dataPoint.id,
130717
+ scope: dataPoint.scope,
130718
+ error: err2.message
130719
+ }, "Failed to reconcile deleted data point locally");
130720
+ failed = true;
130721
+ }
130722
+ continue;
130723
+ }
128574
130724
  const retryKey = downloadRetryKey(dataPoint);
128575
130725
  const decision = options.retryMemory?.decide(retryKey) ?? "attempt";
128576
130726
  if (decision === "give-up") {
@@ -128626,6 +130776,49 @@ async function downloadAll(deps, options = {}) {
128626
130776
  }
128627
130777
  return results;
128628
130778
  }
130779
+ async function reconcileDeletedDataPoint(deps, record2, deletedAt) {
130780
+ const tombstone = { version: tombstoneVersion(record2) };
130781
+ const tombstoned = tombstone.version === null ? null : BigInt(tombstone.version);
130782
+ const orphanKeys = [];
130783
+ const { storage, logger } = deps;
130784
+ const PAGE_SIZE = 500;
130785
+ const stale = [];
130786
+ let kept = 0;
130787
+ for (let offset = 0; ; offset += PAGE_SIZE) {
130788
+ const entries = storage.listVersions(record2.scope, {
130789
+ limit: PAGE_SIZE,
130790
+ offset
130791
+ });
130792
+ for (const entry of entries) {
130793
+ if (isEntryCoveredByTombstone(entry, tombstone)) {
130794
+ stale.push({ scope: entry.scope, collectedAt: entry.collectedAt });
130795
+ if (entry.dataPointId === null && (tombstoned === null || BigInt(entry.version) > tombstoned)) {
130796
+ orphanKeys.push({
130797
+ scope: entry.scope,
130798
+ version: String(entry.version)
130799
+ });
130800
+ }
130801
+ } else {
130802
+ kept += 1;
130803
+ }
130804
+ }
130805
+ if (entries.length < PAGE_SIZE)
130806
+ break;
130807
+ }
130808
+ let removed = 0;
130809
+ for (const version4 of stale) {
130810
+ if (await storage.deleteVersion(version4.scope, version4.collectedAt)) {
130811
+ removed += 1;
130812
+ }
130813
+ }
130814
+ if (orphanKeys.length > 0 && deps.pendingBlobDeletions) {
130815
+ await deps.pendingBlobDeletions.add(orphanKeys);
130816
+ }
130817
+ if (removed > 0 || kept > 0) {
130818
+ logger.info({ dataPointId: record2.id, scope: record2.scope, deletedAt, removed, kept }, "Reconciled gateway deletion against local index");
130819
+ }
130820
+ return { scope: record2.scope, deletedAt, removed, kept };
130821
+ }
128629
130822
  async function repairLocalMissingBlockSidecars(deps) {
128630
130823
  const { storage, logger, diagnostics } = deps;
128631
130824
  if (!storage.writeBlockManifest || !storage.hasScopeBlocks) {
@@ -128784,28 +130977,6 @@ async function repairMissingBlockSidecars(storage, logger, ctx, entry, diagnosti
128784
130977
  }
128785
130978
  }
128786
130979
 
128787
- // ../core/dist/sync/workers/delete.js
128788
- async function deleteScopeRemote(deps, scope) {
128789
- const { storage, logger } = deps;
128790
- let syncedVersions = 0;
128791
- const PAGE_SIZE = 1e3;
128792
- for (let offset = 0; ; offset += PAGE_SIZE) {
128793
- const entries = storage.listVersions(scope, { limit: PAGE_SIZE, offset });
128794
- syncedVersions += entries.filter((e10) => e10.dataPointId !== null).length;
128795
- if (entries.length < PAGE_SIZE)
128796
- break;
128797
- }
128798
- if (syncedVersions > 0) {
128799
- logger.warn({ scope, syncedVersions }, "Remote scope deletion is not supported on the DPv2 gateway (no de-registration endpoint); local data was removed but the remote data point(s) and ciphertext blob(s) remain");
128800
- }
128801
- return {
128802
- scope,
128803
- filesDeregistered: 0,
128804
- blobsDeleted: 0,
128805
- errors: []
128806
- };
128807
- }
128808
-
128809
130980
  // ../core/dist/sync/engine/sync-manager.js
128810
130981
  var MAX_ERRORS = 10;
128811
130982
  function createSyncManager(uploadDeps, downloadDeps, options) {
@@ -128823,12 +130994,28 @@ function createSyncManager(uploadDeps, downloadDeps, options) {
128823
130994
  let rerunRequested = false;
128824
130995
  let needsFullReconcile = true;
128825
130996
  const downloadRetryMemory = createDownloadRetryMemory();
130997
+ const dataPointFeed = downloadDeps.dataPointFeed ?? uploadDeps.dataPointFeed;
130998
+ const scopeDeletions = uploadDeps.scopeDeletions ?? downloadDeps.scopeDeletions;
130999
+ const workerUploadDeps = {
131000
+ ...uploadDeps,
131001
+ pendingBlobDeletions: uploadDeps.pendingBlobDeletions ?? options?.pendingBlobDeletions
131002
+ };
131003
+ const workerDownloadDeps = {
131004
+ ...downloadDeps,
131005
+ pendingBlobDeletions: downloadDeps.pendingBlobDeletions ?? options?.pendingBlobDeletions
131006
+ };
131007
+ let mutationQueue = Promise.resolve();
131008
+ function exclusive(operation) {
131009
+ const run = mutationQueue.then(operation, operation);
131010
+ mutationQueue = run.catch(() => void 0);
131011
+ return run;
131012
+ }
128826
131013
  async function runCycle() {
128827
131014
  if (cycleInFlight) {
128828
131015
  rerunRequested = true;
128829
131016
  return cycleInFlight;
128830
131017
  }
128831
- cycleInFlight = (async () => {
131018
+ cycleInFlight = exclusive(async () => {
128832
131019
  do {
128833
131020
  rerunRequested = false;
128834
131021
  const canRun = await (options?.canSync?.() ?? { ok: true });
@@ -128839,7 +131026,19 @@ function createSyncManager(uploadDeps, downloadDeps, options) {
128839
131026
  }
128840
131027
  blocked = null;
128841
131028
  try {
128842
- const uploadResults = await uploadAll(uploadDeps, {
131029
+ await retryPendingBlobDeletions({
131030
+ deleteData: options?.deleteData,
131031
+ pendingBlobDeletions: options?.pendingBlobDeletions,
131032
+ dataPointFeed,
131033
+ serverOwner: uploadDeps.serverOwner,
131034
+ storage: uploadDeps.storage,
131035
+ logger: uploadDeps.logger
131036
+ });
131037
+ } catch (err2) {
131038
+ uploadDeps.logger.warn({ error: err2.message }, "Pending blob deletion retry failed");
131039
+ }
131040
+ try {
131041
+ const uploadResults = await uploadAll(workerUploadDeps, {
128843
131042
  batchSize: uploadBatchSize,
128844
131043
  onError(entry, error51) {
128845
131044
  pushError({
@@ -128863,7 +131062,7 @@ function createSyncManager(uploadDeps, downloadDeps, options) {
128863
131062
  }
128864
131063
  try {
128865
131064
  const fullReconcile = needsFullReconcile;
128866
- const downloadResults = await downloadAll(downloadDeps, {
131065
+ const downloadResults = await downloadAll(workerDownloadDeps, {
128867
131066
  fullReconcile,
128868
131067
  retryMemory: downloadRetryMemory
128869
131068
  });
@@ -128885,7 +131084,7 @@ function createSyncManager(uploadDeps, downloadDeps, options) {
128885
131084
  }
128886
131085
  lastSync = (/* @__PURE__ */ new Date()).toISOString();
128887
131086
  } while (rerunRequested && isRunning);
128888
- })();
131087
+ });
128889
131088
  try {
128890
131089
  await cycleInFlight;
128891
131090
  } finally {
@@ -128974,13 +131173,291 @@ function createSyncManager(uploadDeps, downloadDeps, options) {
128974
131173
  uploadDeps.logger.debug("New data notification received");
128975
131174
  scheduleNotifiedCycle();
128976
131175
  },
128977
- async deleteScopeRemote(scope) {
128978
- await deleteScopeRemote(uploadDeps, scope);
131176
+ deleteScope(scope) {
131177
+ return exclusive(() => deleteScope({
131178
+ storage: uploadDeps.storage,
131179
+ serverOwner: uploadDeps.serverOwner,
131180
+ deleteData: options?.deleteData,
131181
+ pendingBlobDeletions: options?.pendingBlobDeletions,
131182
+ scopeDeletions,
131183
+ dataPointFeed,
131184
+ logger: uploadDeps.logger
131185
+ }, scope));
128979
131186
  }
128980
131187
  };
128981
131188
  return manager;
128982
131189
  }
128983
131190
 
131191
+ // ../core/dist/sync/delete-data-port.js
131192
+ function createGatewayDeleteDataPort(options) {
131193
+ const gatewayBase = options.gatewayUrl.replace(/\/+$/, "");
131194
+ const storageBase = options.storage.endpoint.replace(/\/+$/, "");
131195
+ const fetchImpl = options.fetch ?? globalThis.fetch;
131196
+ const owner = options.serverOwner.toLowerCase();
131197
+ async function sendTombstone(scope, dataPointId, version4) {
131198
+ const signature = await options.signer.signAddData({
131199
+ ownerAddress: options.serverOwner,
131200
+ scope,
131201
+ dataHash: TOMBSTONE_DATA_HASH,
131202
+ metadataHash: TOMBSTONE_METADATA_HASH,
131203
+ expectedVersion: version4
131204
+ });
131205
+ return fetchImpl(`${gatewayBase}/v1/data/${dataPointId}`, {
131206
+ method: "DELETE",
131207
+ headers: {
131208
+ "Content-Type": "application/json",
131209
+ Authorization: `Web3Signed ${signature}`
131210
+ },
131211
+ body: JSON.stringify({
131212
+ ownerAddress: options.serverOwner,
131213
+ scope,
131214
+ expectedVersion: String(version4),
131215
+ signature
131216
+ })
131217
+ });
131218
+ }
131219
+ return {
131220
+ async tombstone(scope) {
131221
+ const dataPointId = computeDataPointId(options.serverOwner, scope);
131222
+ const current = await options.dataPointFeed.getDataPoint({
131223
+ ownerAddress: options.serverOwner,
131224
+ scope
131225
+ });
131226
+ if (current === null) {
131227
+ return { status: "not-registered", dataPointId };
131228
+ }
131229
+ if (current.deletedAt !== null) {
131230
+ return {
131231
+ status: "already-deleted",
131232
+ dataPointId,
131233
+ version: current.expectedVersion,
131234
+ deletedAt: current.deletedAt
131235
+ };
131236
+ }
131237
+ let version4 = BigInt(current.expectedVersion) + 1n;
131238
+ let res = await sendTombstone(scope, dataPointId, version4);
131239
+ if (res.status === 409) {
131240
+ const body2 = await res.json().catch(() => null);
131241
+ const conflict = unwrap2(body2);
131242
+ const nextExplicit = integerField(conflict, "nextExpectedVersion") ?? integerField(body2, "nextExpectedVersion");
131243
+ const currentExpected = integerField(conflict, "currentExpectedVersion") ?? integerField(body2, "currentExpectedVersion");
131244
+ const legacyNext = parseGatewayNextVersion(detailFromBody(body2, res.statusText));
131245
+ const next = nextExplicit !== null ? nextExplicit : currentExpected !== null ? currentExpected + 1n : legacyNext !== null ? BigInt(legacyNext) : null;
131246
+ if (next === null) {
131247
+ throw new Error(`Gateway error: 409 ${detailFromBody(body2, res.statusText)}`);
131248
+ }
131249
+ version4 = next;
131250
+ res = await sendTombstone(scope, dataPointId, version4);
131251
+ }
131252
+ if (res.status === 404) {
131253
+ return { status: "not-registered", dataPointId };
131254
+ }
131255
+ if (res.status === 410) {
131256
+ const body2 = await res.json().catch(() => null);
131257
+ const echoed = unwrap2(body2);
131258
+ let winning = normalizeVersionString(stringField2(echoed, "expectedVersion"));
131259
+ let deletedAt = stringField2(echoed, "deletedAt");
131260
+ if (winning === null) {
131261
+ const reread = await options.dataPointFeed.getDataPoint({ ownerAddress: options.serverOwner, scope }).catch(() => null);
131262
+ if (reread && reread.deletedAt !== null) {
131263
+ winning = normalizeVersionString(reread.expectedVersion);
131264
+ deletedAt = deletedAt ?? reread.deletedAt;
131265
+ }
131266
+ }
131267
+ return {
131268
+ status: "already-deleted",
131269
+ dataPointId,
131270
+ version: winning,
131271
+ deletedAt
131272
+ };
131273
+ }
131274
+ if (!res.ok) {
131275
+ throw new Error(`Gateway error: ${res.status} ${await errorDetail(res)}`);
131276
+ }
131277
+ const body = await res.json().catch(() => null);
131278
+ const row = unwrap2(body);
131279
+ return {
131280
+ status: "tombstoned",
131281
+ dataPointId: stringField2(row, "dataPointId") ?? dataPointId,
131282
+ version: stringField2(row, "expectedVersion") ?? String(version4),
131283
+ deletedAt: stringField2(row, "deletedAt")
131284
+ };
131285
+ },
131286
+ async deleteBlobVersions(scope, versions) {
131287
+ const outcome = {
131288
+ deleted: [],
131289
+ missing: [],
131290
+ failed: []
131291
+ };
131292
+ for (const version4 of versions) {
131293
+ const path = `/v1/chains/${options.storage.chainId}/blobs/${owner}/${encodeURIComponent(scope)}/${encodeURIComponent(version4)}`;
131294
+ try {
131295
+ const authorization = await buildWeb3SignedHeader({
131296
+ signMessage: (message) => options.storage.signMessage(message),
131297
+ aud: storageBase,
131298
+ method: "DELETE",
131299
+ uri: path
131300
+ });
131301
+ const res = await fetchImpl(`${storageBase}${path}`, {
131302
+ method: "DELETE",
131303
+ headers: { authorization }
131304
+ });
131305
+ if (res.status === 404) {
131306
+ outcome.missing.push(version4);
131307
+ } else if (res.ok) {
131308
+ outcome.deleted.push(version4);
131309
+ } else {
131310
+ outcome.failed.push({
131311
+ version: version4,
131312
+ error: `vana-storage delete failed: ${res.status} ${res.statusText}`
131313
+ });
131314
+ }
131315
+ } catch (err2) {
131316
+ outcome.failed.push({
131317
+ version: version4,
131318
+ error: err2 instanceof Error ? err2.message : String(err2)
131319
+ });
131320
+ }
131321
+ }
131322
+ return outcome;
131323
+ }
131324
+ };
131325
+ }
131326
+ async function errorDetail(res) {
131327
+ const body = await res.json().catch(() => null);
131328
+ return detailFromBody(body, res.statusText);
131329
+ }
131330
+ function detailFromBody(body, fallback) {
131331
+ if (typeof body === "object" && body !== null) {
131332
+ const record2 = body;
131333
+ if (typeof record2.error === "string")
131334
+ return record2.error;
131335
+ if (typeof record2.message === "string")
131336
+ return record2.message;
131337
+ if (typeof record2.error === "object" && record2.error !== null) {
131338
+ const nested = record2.error;
131339
+ if (typeof nested.message === "string")
131340
+ return nested.message;
131341
+ }
131342
+ }
131343
+ return fallback;
131344
+ }
131345
+ function normalizeVersionString(value) {
131346
+ if (value === null || !/^\d+$/.test(value))
131347
+ return null;
131348
+ const parsed = BigInt(value);
131349
+ return parsed > 0n ? parsed.toString() : null;
131350
+ }
131351
+ function integerField(record2, key) {
131352
+ if (typeof record2 !== "object" || record2 === null)
131353
+ return null;
131354
+ const value = record2[key];
131355
+ if (typeof value === "number" && Number.isSafeInteger(value)) {
131356
+ return BigInt(value);
131357
+ }
131358
+ if (typeof value === "string" && /^\d+$/.test(value))
131359
+ return BigInt(value);
131360
+ return null;
131361
+ }
131362
+ function unwrap2(body) {
131363
+ if (typeof body !== "object" || body === null)
131364
+ return null;
131365
+ const record2 = body;
131366
+ if (typeof record2.data === "object" && record2.data !== null) {
131367
+ return record2.data;
131368
+ }
131369
+ return record2;
131370
+ }
131371
+ function stringField2(record2, key) {
131372
+ const value = record2?.[key];
131373
+ return typeof value === "string" ? value : null;
131374
+ }
131375
+
131376
+ // ../core/dist/sync/pending-blob-deletions.js
131377
+ function markerId(key) {
131378
+ const range = key.range ? `${key.range.from}-${key.range.to}` : "";
131379
+ return `${key.scope}\0${key.version ?? ""}\0${range}`;
131380
+ }
131381
+ function normalizeRange(value) {
131382
+ if (typeof value !== "object" || value === null)
131383
+ return void 0;
131384
+ const { from, to: to3 } = value;
131385
+ if (typeof from !== "string" || typeof to3 !== "string" || !/^\d+$/.test(from) || !/^\d+$/.test(to3) || BigInt(from) > BigInt(to3)) {
131386
+ return void 0;
131387
+ }
131388
+ return { from, to: to3 };
131389
+ }
131390
+ function normalizePendingBlobDeletions(stored) {
131391
+ if (!Array.isArray(stored))
131392
+ return [];
131393
+ const keys = [];
131394
+ for (const item of stored) {
131395
+ if (typeof item === "string") {
131396
+ keys.push({ scope: item, version: null });
131397
+ } else if (typeof item === "object" && item !== null && typeof item.scope === "string") {
131398
+ const version4 = item.version;
131399
+ const range = typeof version4 === "string" ? void 0 : normalizeRange(item.range);
131400
+ keys.push({
131401
+ scope: item.scope,
131402
+ version: typeof version4 === "string" ? version4 : null,
131403
+ ...range ? { range } : {}
131404
+ });
131405
+ }
131406
+ }
131407
+ return keys;
131408
+ }
131409
+ function createPendingBlobDeletionStore(kv) {
131410
+ let queue = Promise.resolve();
131411
+ function serialized(operation) {
131412
+ const run = queue.then(operation, operation);
131413
+ queue = run.catch(() => void 0);
131414
+ return run;
131415
+ }
131416
+ async function current() {
131417
+ return normalizePendingBlobDeletions(await kv.read());
131418
+ }
131419
+ return {
131420
+ list() {
131421
+ return serialized(current);
131422
+ },
131423
+ add(keys) {
131424
+ return serialized(async () => {
131425
+ if (keys.length === 0)
131426
+ return;
131427
+ const existing = await current();
131428
+ const known = new Set(existing.map(markerId));
131429
+ const next = [...existing];
131430
+ for (const key of keys) {
131431
+ const id2 = markerId(key);
131432
+ if (known.has(id2))
131433
+ continue;
131434
+ known.add(id2);
131435
+ next.push({
131436
+ scope: key.scope,
131437
+ version: key.version,
131438
+ ...key.range ? { range: { ...key.range } } : {}
131439
+ });
131440
+ }
131441
+ if (next.length === existing.length)
131442
+ return;
131443
+ await kv.write(next);
131444
+ });
131445
+ },
131446
+ remove(keys) {
131447
+ return serialized(async () => {
131448
+ if (keys.length === 0)
131449
+ return;
131450
+ const existing = await current();
131451
+ const gone = new Set(keys.map(markerId));
131452
+ const next = existing.filter((key) => !gone.has(markerId(key)));
131453
+ if (next.length === existing.length)
131454
+ return;
131455
+ await kv.write(next);
131456
+ });
131457
+ }
131458
+ };
131459
+ }
131460
+
128984
131461
  // ../core/dist/storage/adapters/sdk.js
128985
131462
  function createSdkStorageAdapter(providerOrFactory, options) {
128986
131463
  let cachedProvider;
@@ -129041,8 +131518,11 @@ function copyBytes(data) {
129041
131518
 
129042
131519
  // ../core/dist/storage/adapters/vana.js
129043
131520
  var DEFAULT_VANA_STORAGE_ENDPOINT = "https://storage.vana.org";
131521
+ function resolveVanaStorageEndpoint(config2) {
131522
+ return (config2.storage.config.vana?.apiUrl ?? DEFAULT_VANA_STORAGE_ENDPOINT).replace(/\/+$/, "");
131523
+ }
129044
131524
  function createVanaSyncStorageAdapter(params) {
129045
- const endpoint = (params.config.storage.config.vana?.apiUrl ?? DEFAULT_VANA_STORAGE_ENDPOINT).replace(/\/+$/, "");
131525
+ const endpoint = resolveVanaStorageEndpoint(params.config);
129046
131526
  const owner = params.serverOwner.toLowerCase();
129047
131527
  const chainId = params.config.gateway.chainId;
129048
131528
  return createSdkStorageAdapter(createVanaStorageProvider({
@@ -129097,6 +131577,7 @@ async function resolvePsLiteOwner(input) {
129097
131577
 
129098
131578
  // ../lite/dist/sync.js
129099
131579
  var SYNC_CURSOR_KEY = "sync-cursor-v1";
131580
+ var PENDING_BLOB_DELETIONS_KEY = "pending-blob-deletions-v1";
129100
131581
  function createBrowserLogger(logger) {
129101
131582
  const fallback = {
129102
131583
  info: console.info.bind(console),
@@ -129127,6 +131608,19 @@ function createPsLiteSyncCursor(stateStore) {
129127
131608
  }
129128
131609
  };
129129
131610
  }
131611
+ function createPsLitePendingBlobDeletionStore(stateStore) {
131612
+ return createPendingBlobDeletionStore({
131613
+ async read() {
131614
+ const state = await stateStore.get(PENDING_BLOB_DELETIONS_KEY);
131615
+ if (!state)
131616
+ return null;
131617
+ return normalizePendingBlobDeletions(state.keys ?? state.scopes);
131618
+ },
131619
+ async write(keys) {
131620
+ await stateStore.set(PENDING_BLOB_DELETIONS_KEY, { keys });
131621
+ }
131622
+ });
131623
+ }
129130
131624
  function buildDownloadDiagnosticsHook(recorder) {
129131
131625
  return {
129132
131626
  onDownloadStart(fileId) {
@@ -129212,6 +131706,20 @@ async function createPsLiteSyncManager(options) {
129212
131706
  });
129213
131707
  const cursor = createPsLiteSyncCursor(options.stateStore);
129214
131708
  const logger = createBrowserLogger(options.logger);
131709
+ const dataPointFeed = options.dataPointFeed ?? createGatewayDataPointFeed({ gatewayUrl: options.config.gateway.url });
131710
+ const scopeDeletions = options.scopeDeletions ?? createScopeDeletionTracker({ feed: dataPointFeed, serverOwner, logger });
131711
+ const deleteData = createGatewayDeleteDataPort({
131712
+ gatewayUrl: options.config.gateway.url,
131713
+ dataPointFeed,
131714
+ serverOwner,
131715
+ signer,
131716
+ storage: {
131717
+ endpoint: resolveVanaStorageEndpoint(options.config),
131718
+ chainId: options.config.gateway.chainId,
131719
+ signMessage: (message) => options.serverAccount.signMessage(message)
131720
+ }
131721
+ });
131722
+ const pendingBlobDeletions = createPsLitePendingBlobDeletionStore(options.stateStore);
129215
131723
  const downloadDiagnostics = options.diagnostics ? buildDownloadDiagnosticsHook(options.diagnostics) : void 0;
129216
131724
  const syncManager = createSyncManager({
129217
131725
  storage: options.storage,
@@ -129221,7 +131729,9 @@ async function createPsLiteSyncManager(options) {
129221
131729
  masterKey,
129222
131730
  serverOwner,
129223
131731
  logger,
129224
- lineageGateway: options.lineageGateway
131732
+ lineageGateway: options.lineageGateway,
131733
+ dataPointFeed,
131734
+ scopeDeletions
129225
131735
  }, {
129226
131736
  storage: options.storage,
129227
131737
  storageAdapter,
@@ -129230,8 +131740,13 @@ async function createPsLiteSyncManager(options) {
129230
131740
  masterKey,
129231
131741
  serverOwner,
129232
131742
  logger,
129233
- diagnostics: downloadDiagnostics
131743
+ diagnostics: downloadDiagnostics,
131744
+ dataPointFeed,
131745
+ scopeDeletions,
131746
+ onDataPointIndexed: options.onDataPointIndexed
129234
131747
  }, {
131748
+ deleteData,
131749
+ pendingBlobDeletions,
129235
131750
  async canSync() {
129236
131751
  try {
129237
131752
  const serverInfo = await gateway.getServer(options.serverAccount.address);
@@ -129253,7 +131768,59 @@ async function createPsLiteSyncManager(options) {
129253
131768
  }
129254
131769
  });
129255
131770
  syncManager.start();
129256
- return { syncManager, serverOwner };
131771
+ return { syncManager, serverOwner, dataPointFeed, scopeDeletions };
131772
+ }
131773
+
131774
+ // ../lite/dist/derivatives.js
131775
+ var QUESTIONS_KEY = "derivative-questions-v1";
131776
+ async function createPsLiteQuestionStore(stateStore) {
131777
+ const saved = await stateStore.get(QUESTIONS_KEY);
131778
+ const initial = saved?.version === 1 ? saved.questions : [];
131779
+ return createInMemoryQuestionStore({
131780
+ initial,
131781
+ onChange: (questions) => stateStore.set(QUESTIONS_KEY, {
131782
+ version: 1,
131783
+ questions
131784
+ })
131785
+ });
131786
+ }
131787
+ function psLiteInferenceConfigured(config2) {
131788
+ return config2.inference.baseUrl.replace(/\/+$/, "") !== DEFAULT_INFERENCE_BASE_URL.replace(/\/+$/, "");
131789
+ }
131790
+ function createPsLiteDerivativeCompute(options) {
131791
+ const provider = options.provider ?? createOpenAiCompatibleInferenceProvider({
131792
+ baseUrl: options.config.inference.baseUrl,
131793
+ model: options.config.inference.model
131794
+ });
131795
+ const logger = options.logger ? {
131796
+ info: (payload, message) => options.logger?.info(payload, message),
131797
+ warn: (payload, message) => options.logger?.warn(payload, message)
131798
+ } : void 0;
131799
+ const scheduler = createRecomputeScheduler({
131800
+ store: options.store,
131801
+ debounceMs: options.config.inference.recomputeDebounceMs,
131802
+ serverOwner: options.serverOwner,
131803
+ now: options.now,
131804
+ logger,
131805
+ compute: (questionId) => computeQuestion(questionId, {
131806
+ // A -> B -> C: a question reading this derived scope recomputes.
131807
+ onDerivedWritten: (event) => scheduler.markSourceChanged(event.scope, {
131808
+ lineageSources: event.lineageSources
131809
+ }),
131810
+ runtimeAvailability: options.runtimeAvailability,
131811
+ storage: options.storage,
131812
+ store: options.store,
131813
+ provider,
131814
+ serverOwner: options.serverOwner,
131815
+ maxSourceItems: options.config.inference.maxSourceItems,
131816
+ syncManager: options.syncManager?.() ?? null,
131817
+ scopeDeletions: options.scopeDeletions?.(),
131818
+ writePolicyPorts: options.writePolicyPorts,
131819
+ now: options.now,
131820
+ logger
131821
+ })
131822
+ });
131823
+ return { store: options.store, scheduler, provider };
129257
131824
  }
129258
131825
 
129259
131826
  // ../lite/dist/persistence.js
@@ -129319,8 +131886,32 @@ async function createIndexedDbPsLiteRuntime(options) {
129319
131886
  requestSigner: createRequestSigner(identity.account)
129320
131887
  });
129321
131888
  let syncManager = options.syncManager ?? null;
131889
+ let scopeDeletions = options.scopeDeletions;
131890
+ let runtimeRef = null;
131891
+ let derivatives = options.derivatives ?? null;
131892
+ if (derivatives === null && options.derivatives === void 0 && (options.inferenceProvider || psLiteInferenceConfigured(config2))) {
131893
+ derivatives = createPsLiteDerivativeCompute({
131894
+ config: config2,
131895
+ storage,
131896
+ store: await createPsLiteQuestionStore(stateStore),
131897
+ serverOwner,
131898
+ syncManager: () => syncManager,
131899
+ scopeDeletions: () => scopeDeletions,
131900
+ writePolicyPorts: {
131901
+ authSessionVerifier: gateway,
131902
+ grantVerifier: gateway
131903
+ },
131904
+ runtimeAvailability: {
131905
+ isAvailable: () => runtimeRef?.isAvailable() ?? Boolean(options.active)
131906
+ },
131907
+ provider: options.inferenceProvider,
131908
+ logger: options.logger
131909
+ });
131910
+ } else if (derivatives === null && options.derivatives === void 0) {
131911
+ options.logger?.warn({ baseUrl: config2.inference.baseUrl }, "Derivative compute disabled: inference.baseUrl is the direct-provider default; point it at the Vana inference relay");
131912
+ }
129322
131913
  if (!syncManager && config2.sync.enabled) {
129323
- syncManager = (await createPsLiteSyncManager({
131914
+ const sync = await createPsLiteSyncManager({
129324
131915
  config: config2,
129325
131916
  stateStore,
129326
131917
  storage,
@@ -129328,12 +131919,18 @@ async function createIndexedDbPsLiteRuntime(options) {
129328
131919
  ownerAddress: options.ownerAddress,
129329
131920
  serverAccount: identity.account,
129330
131921
  gateway,
131922
+ dataPointFeed: options.dataPointFeed,
131923
+ scopeDeletions,
129331
131924
  diagnostics,
129332
131925
  logger: options.logger,
129333
- lineageGateway
129334
- })).syncManager;
131926
+ lineageGateway,
131927
+ onDataPointIndexed: (event) => derivatives?.scheduler.markSourceChanged(event.scope, {
131928
+ lineageSources: event.lineageSources
131929
+ })
131930
+ });
131931
+ syncManager = sync.syncManager;
131932
+ scopeDeletions = sync.scopeDeletions;
129335
131933
  }
129336
- let runtimeRef = null;
129337
131934
  const auth = options.auth ?? createWeb3SignedPsLiteAuth({
129338
131935
  origin: () => options.runtimeOrigin ?? config2.server.origin,
129339
131936
  ownerAddress: serverOwner,
@@ -129360,8 +131957,10 @@ async function createIndexedDbPsLiteRuntime(options) {
129360
131957
  serverOwner,
129361
131958
  serverSigner,
129362
131959
  syncManager,
131960
+ scopeDeletions,
129363
131961
  diagnostics,
129364
131962
  lineageGateway,
131963
+ derivatives,
129365
131964
  saveConfig: async (nextConfig) => {
129366
131965
  const saved = await savePsLiteConfig(stateStore, nextConfig);
129367
131966
  Object.assign(config2, saved);
@@ -129384,7 +131983,8 @@ async function createIndexedDbPsLiteRuntime(options) {
129384
131983
  storage,
129385
131984
  tokenStore,
129386
131985
  accessLogStore,
129387
- syncManager
131986
+ syncManager,
131987
+ derivatives
129388
131988
  };
129389
131989
  }
129390
131990