@semiont/make-meaning 0.5.28 → 0.5.29

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,17 +1,16 @@
1
1
  import { STALL_THRESHOLD_MS, FsJobQueue } from '@semiont/jobs';
2
2
  import { FilesystemViewStorage, resolveStorageUri, EventQuery, createEventStore } from '@semiont/event-sourcing';
3
- import { getResourceEntityTypes, getResourceId, getTargetSource, resourceId, getPrimaryRepresentation, decodeRepresentation, getBodySource, getTargetSelector, deriveViews, getTextPositionSelector, annotationId, errField, userId, generateUuid, getExactText, busRequest, cloneToken, baseMediaType, isSupportedMediaType, capabilitiesOf, didToAgent, assembleAnnotation, isGenerationJobParams, jobId, entityType, baseUrl, busLog, BRIDGED_CHANNELS, burstBuffer, textExtractionOf, chunkText, getPrimaryMediaType, extensionForMediaType, applyBodyOperations, softwareToAgent } from '@semiont/core';
3
+ import { getResourceEntityTypes, getResourceId, getTargetSource, resourceId, decodeRepresentation, getBodySource, getStorageUri, getPrimaryRepresentation, getTargetSelector, deriveViews, getTextPositionSelector, annotationId, errField, userId, generateUuid, getExactText, busRequest, cloneToken, assembleAnnotation, applyBodyOperations, didToAgent, isGenerationJobParams, jobId, entityType, baseUrl, busLog, BRIDGED_CHANNELS, burstBuffer, textExtractionOf, chunkText, getPrimaryMediaType, isAnnotatable, softwareToAgent } from '@semiont/core';
4
4
  import { recordGatherDegrade, withActorSpan, recordBusEmit, withSpan, SpanKind, registerJobQueueProvider, registerVectorIndexSizeProvider } from '@semiont/observability';
5
5
  import { createInferenceClient } from '@semiont/inference';
6
6
  import { compareByRecencyThenId, getGraphDatabase } from '@semiont/graph';
7
- import { WorkingTreeStore, createAnchoredTextStore, deriveStorageUri, anchoredTextStoreOverTransport, EXTRACTORS, calculateChecksum } from '@semiont/content';
7
+ import { WorkingTreeStore, createAnchoredTextStore, RepresentationMissing, ChecksumMismatchError, EXTRACTORS, calculateChecksum } from '@semiont/content';
8
8
  import { getEntityTypes, DEFAULT_ENTITY_TYPES } from '@semiont/ontology';
9
9
  import { mergeByResource } from '@semiont/vectors';
10
10
  import { promises } from 'fs';
11
11
  import * as path3 from 'path';
12
- import { createGzip, createGunzip } from 'zlib';
13
- import { pipeline, Readable } from 'stream';
14
- import { promisify } from 'util';
12
+ import { createServer } from 'http';
13
+ import { pipeline } from 'stream/promises';
15
14
 
16
15
  var __create = Object.create;
17
16
  var __defProp = Object.defineProperty;
@@ -3568,7 +3567,7 @@ var require_firstValueFrom = __commonJS({
3568
3567
  exports.firstValueFrom = void 0;
3569
3568
  var EmptyError_1 = require_EmptyError();
3570
3569
  var Subscriber_1 = require_Subscriber();
3571
- function firstValueFrom2(source, config) {
3570
+ function firstValueFrom(source, config) {
3572
3571
  var hasConfig = typeof config === "object";
3573
3572
  return new Promise(function(resolve2, reject) {
3574
3573
  var subscriber = new Subscriber_1.SafeSubscriber({
@@ -3588,7 +3587,7 @@ var require_firstValueFrom = __commonJS({
3588
3587
  source.subscribe(subscriber);
3589
3588
  });
3590
3589
  }
3591
- exports.firstValueFrom = firstValueFrom2;
3590
+ exports.firstValueFrom = firstValueFrom;
3592
3591
  }
3593
3592
  });
3594
3593
 
@@ -3727,7 +3726,7 @@ var require_map = __commonJS({
3727
3726
  exports.map = void 0;
3728
3727
  var lift_1 = require_lift();
3729
3728
  var OperatorSubscriber_1 = require_OperatorSubscriber();
3730
- function map3(project, thisArg) {
3729
+ function map2(project, thisArg) {
3731
3730
  return lift_1.operate(function(source, subscriber) {
3732
3731
  var index = 0;
3733
3732
  source.subscribe(OperatorSubscriber_1.createOperatorSubscriber(subscriber, function(value) {
@@ -3735,7 +3734,7 @@ var require_map = __commonJS({
3735
3734
  }));
3736
3735
  });
3737
3736
  }
3738
- exports.map = map3;
3737
+ exports.map = map2;
3739
3738
  }
3740
3739
  });
3741
3740
 
@@ -4565,7 +4564,7 @@ var require_timer = __commonJS({
4565
4564
  var async_1 = require_async();
4566
4565
  var isScheduler_1 = require_isScheduler();
4567
4566
  var isDate_1 = require_isDate();
4568
- function timer2(dueTime, intervalOrScheduler, scheduler) {
4567
+ function timer(dueTime, intervalOrScheduler, scheduler) {
4569
4568
  if (dueTime === void 0) {
4570
4569
  dueTime = 0;
4571
4570
  }
@@ -4598,7 +4597,7 @@ var require_timer = __commonJS({
4598
4597
  }, due);
4599
4598
  });
4600
4599
  }
4601
- exports.timer = timer2;
4600
+ exports.timer = timer;
4602
4601
  }
4603
4602
  });
4604
4603
 
@@ -4788,7 +4787,7 @@ var require_race = __commonJS({
4788
4787
  var innerFrom_1 = require_innerFrom();
4789
4788
  var argsOrArgArray_1 = require_argsOrArgArray();
4790
4789
  var OperatorSubscriber_1 = require_OperatorSubscriber();
4791
- function race2() {
4790
+ function race() {
4792
4791
  var sources = [];
4793
4792
  for (var _i = 0; _i < arguments.length; _i++) {
4794
4793
  sources[_i] = arguments[_i];
@@ -4796,7 +4795,7 @@ var require_race = __commonJS({
4796
4795
  sources = argsOrArgArray_1.argsOrArgArray(sources);
4797
4796
  return sources.length === 1 ? innerFrom_1.innerFrom(sources[0]) : new Observable_1.Observable(raceInit(sources));
4798
4797
  }
4799
- exports.race = race2;
4798
+ exports.race = race;
4800
4799
  function raceInit(sources) {
4801
4800
  return function(subscriber) {
4802
4801
  var subscriptions = [];
@@ -9252,14 +9251,14 @@ var require_race2 = __commonJS({
9252
9251
  exports.race = void 0;
9253
9252
  var argsOrArgArray_1 = require_argsOrArgArray();
9254
9253
  var raceWith_1 = require_raceWith();
9255
- function race2() {
9254
+ function race() {
9256
9255
  var args = [];
9257
9256
  for (var _i = 0; _i < arguments.length; _i++) {
9258
9257
  args[_i] = arguments[_i];
9259
9258
  }
9260
9259
  return raceWith_1.raceWith.apply(void 0, __spreadArray([], __read(argsOrArgArray_1.argsOrArgArray(args))));
9261
9260
  }
9262
- exports.race = race2;
9261
+ exports.race = race;
9263
9262
  }
9264
9263
  });
9265
9264
 
@@ -9726,6 +9725,45 @@ var require_operators = __commonJS({
9726
9725
  });
9727
9726
 
9728
9727
  // src/config.ts
9728
+ function requireKBName(config) {
9729
+ const name = config.kb?.name;
9730
+ if (!name) {
9731
+ throw new Error(
9732
+ "[kb] name is missing from the environment config. The launcher stages the KB's committed identity into each service's config (SINGLE-KB-MOUNT D4); without it this service cannot locate the state tree."
9733
+ );
9734
+ }
9735
+ return name;
9736
+ }
9737
+ function makeMeaningConfigFrom(config) {
9738
+ const meta = config._metadata;
9739
+ const gather = meta?.gather;
9740
+ if (!gather) {
9741
+ throw new Error("make-meaning gather config missing \u2014 load config via loadEnvironmentConfig (the TOML loader owns the settleTimeoutMs default)");
9742
+ }
9743
+ const search = meta?.search;
9744
+ if (!search) {
9745
+ throw new Error("make-meaning search config missing \u2014 load config via loadEnvironmentConfig (the TOML loader owns the semanticFloor default)");
9746
+ }
9747
+ return {
9748
+ gather,
9749
+ search,
9750
+ services: {
9751
+ // vectors/embedding are required on both sides (MANDATORY-EMBEDDING P3):
9752
+ // core's ServicesConfig requires the pair, so a config missing either
9753
+ // already refused at the TOML loader — nothing to re-check here.
9754
+ graph: config.services.graph,
9755
+ vectors: config.services.vectors,
9756
+ embedding: config.services.embedding,
9757
+ archivist: config.services.archivist
9758
+ },
9759
+ // The KB's canonical identity — the agent roster mints DIDs from this,
9760
+ // the SAME value /api/tokens/agent uses (agent-did-host-skew fix). The
9761
+ // value, not JWTService, so make-meaning stays gateway-agnostic.
9762
+ ...config.site?.domain ? { site: { domain: config.site.domain } } : {},
9763
+ actors: meta?.actors,
9764
+ workers: meta?.workers
9765
+ };
9766
+ }
9729
9767
  function resolveActorInference(config, actor) {
9730
9768
  const specific = config.actors?.[actor];
9731
9769
  if (specific) return specific;
@@ -9746,6 +9784,21 @@ function resolveWorkerInference(config, workerType) {
9746
9784
  // src/service.ts
9747
9785
  var import_rxjs7 = __toESM(require_cjs());
9748
9786
  var import_operators6 = __toESM(require_operators());
9787
+ function representationSource(resource) {
9788
+ const primary = getPrimaryRepresentation(resource);
9789
+ if (!primary?.storageUri) return null;
9790
+ return {
9791
+ storageUri: primary.storageUri,
9792
+ mediaType: primary.mediaType
9793
+ };
9794
+ }
9795
+ async function resolveRepresentation(deps, resourceId10) {
9796
+ const view = await deps.views.get(resourceId10);
9797
+ if (!view?.resource) throw new RepresentationMissing(String(resourceId10), "resource");
9798
+ const source = representationSource(view.resource);
9799
+ if (!source) throw new RepresentationMissing(String(resourceId10), "representation");
9800
+ return { stream: deps.content.retrieveStream(source.storageUri), mediaType: source.mediaType };
9801
+ }
9749
9802
 
9750
9803
  // src/weave-progress.ts
9751
9804
  var WeaveProgressTimeout = class extends Error {
@@ -9883,6 +9936,19 @@ function createSmeltProgress(eventBus) {
9883
9936
  }
9884
9937
 
9885
9938
  // src/knowledge-base.ts
9939
+ function workingTreeContentReads(views, content) {
9940
+ return {
9941
+ getBinary: async (resourceId10) => {
9942
+ const { stream, mediaType } = await resolveRepresentation({ views, content }, resourceId10);
9943
+ const chunks = [];
9944
+ for await (const chunk of stream) chunks.push(chunk);
9945
+ const buf = Buffer.concat(chunks);
9946
+ const data = new ArrayBuffer(buf.byteLength);
9947
+ new Uint8Array(data).set(buf);
9948
+ return { data, contentType: mediaType };
9949
+ }
9950
+ };
9951
+ }
9886
9952
  async function createKnowledgeBase(eventStore, project, graphDb, eventBus, logger, options) {
9887
9953
  const views = new FilesystemViewStorage(project, logger.child({ component: "view-storage" }));
9888
9954
  const content = new WorkingTreeStore(
@@ -9915,27 +9981,6 @@ var PROJECTION_LAG_BACKOFF_MS = [25, 50, 100, 200];
9915
9981
  var PROJECTION_BARRIER_TIMEOUT_MS = 500;
9916
9982
  var GRAPH_BARRIER_BUDGET_MS = PROJECTION_BARRIER_TIMEOUT_MS + PROJECTION_LAG_BACKOFF_MS.reduce((a, b) => a + b, 0);
9917
9983
  var GraphContext = class {
9918
- /**
9919
- * Get all resources referencing this resource (backlinks)
9920
- * Requires graph traversal - must use graph database
9921
- */
9922
- static async getBacklinks(resourceId10, kb) {
9923
- return kb.graph.getResourceReferencedBy(resourceId10);
9924
- }
9925
- /**
9926
- * Find shortest path between two resources
9927
- * Requires graph traversal - must use graph database
9928
- */
9929
- static async findPath(fromResourceId, toResourceId, kb, maxDepth) {
9930
- return kb.graph.findPath(fromResourceId, toResourceId, maxDepth);
9931
- }
9932
- /**
9933
- * Get resource connections (graph edges)
9934
- * Requires graph traversal - must use graph database
9935
- */
9936
- static async getResourceConnections(resourceId10, kb) {
9937
- return kb.graph.getResourceConnections(resourceId10);
9938
- }
9939
9984
  /**
9940
9985
  * Build the unified knowledge graph for a resource's neighborhood:
9941
9986
  * resources AND annotations as typed nodes, typed/directional edges.
@@ -10168,10 +10213,10 @@ var ResourceContext = class _ResourceContext {
10168
10213
  return Promise.all(
10169
10214
  resources.map(async (doc) => {
10170
10215
  try {
10171
- if (doc.storageUri) {
10172
- const contentBuffer = await kb.content.retrieve(doc.storageUri);
10173
- const primaryRep = getPrimaryRepresentation(doc);
10174
- const contentPreview = decodeRepresentation(contentBuffer, primaryRep?.mediaType ?? "text/plain").slice(0, 200);
10216
+ const source = representationSource(doc);
10217
+ if (source) {
10218
+ const contentBuffer = await kb.content.retrieve(source.storageUri);
10219
+ const contentPreview = decodeRepresentation(contentBuffer, source.mediaType).slice(0, 200);
10175
10220
  return { ...doc, content: contentPreview };
10176
10221
  }
10177
10222
  return { ...doc, content: "" };
@@ -10183,13 +10228,17 @@ var ResourceContext = class _ResourceContext {
10183
10228
  }
10184
10229
  /**
10185
10230
  * Get full content for a resource
10186
- * Retrieves and decodes the primary representation
10231
+ * Retrieves and decodes the primary representation. ResourceId-keyed
10232
+ * (EXTRACT-LIBRARIAN P3, D-CONTENT b): the descriptor's `storageUri` is the
10233
+ * has-content signal; the fetch itself goes by id, so the standalone
10234
+ * Librarian serves it over the transport.
10187
10235
  */
10188
10236
  static async getResourceContent(resource, kb) {
10189
- if (resource.storageUri) {
10190
- const contentBuffer = await kb.content.retrieve(resource.storageUri);
10191
- const primaryRep = getPrimaryRepresentation(resource);
10192
- return decodeRepresentation(contentBuffer, primaryRep?.mediaType ?? "text/plain");
10237
+ const id = getResourceId(resource);
10238
+ const source = representationSource(resource);
10239
+ if (source && id) {
10240
+ const { data, contentType } = await kb.content.getBinary(resourceId(id));
10241
+ return decodeRepresentation(Buffer.from(data), contentType);
10193
10242
  }
10194
10243
  return void 0;
10195
10244
  }
@@ -10256,12 +10305,12 @@ var AnnotationContext = class {
10256
10305
  }
10257
10306
  let sourceContext;
10258
10307
  if (includeSourceContext) {
10259
- if (!sourceDoc.storageUri) {
10308
+ if (!getStorageUri(sourceDoc)) {
10260
10309
  throw new Error("Source content not found: no storageUri");
10261
10310
  }
10262
10311
  const primaryRep = getPrimaryRepresentation(sourceDoc);
10263
- const sourceContent = await kb.content.retrieve(sourceDoc.storageUri);
10264
- const contentStr = decodeRepresentation(sourceContent, primaryRep?.mediaType ?? "text/plain");
10312
+ const { data: sourceContent } = await kb.content.getBinary(resourceId10);
10313
+ const contentStr = decodeRepresentation(Buffer.from(sourceContent), primaryRep?.mediaType ?? "text/plain");
10265
10314
  const targetSelectorRaw = getTargetSelector(annotation.target);
10266
10315
  const targetSelector = Array.isArray(targetSelectorRaw) ? targetSelectorRaw[0] : targetSelectorRaw;
10267
10316
  logger?.debug("Target selector", { type: targetSelector?.type });
@@ -10297,10 +10346,10 @@ var AnnotationContext = class {
10297
10346
  }
10298
10347
  let targetContext;
10299
10348
  if (includeTargetContext && targetDoc) {
10300
- if (targetDoc.storageUri) {
10349
+ if (getStorageUri(targetDoc) && bodySource) {
10301
10350
  const targetRep = getPrimaryRepresentation(targetDoc);
10302
- const targetContent = await kb.content.retrieve(targetDoc.storageUri);
10303
- const contentStr = decodeRepresentation(targetContent, targetRep?.mediaType ?? "text/plain");
10351
+ const { data: targetContent } = await kb.content.getBinary(resourceId(bodySource));
10352
+ const contentStr = decodeRepresentation(Buffer.from(targetContent), targetRep?.mediaType ?? "text/plain");
10304
10353
  targetContext = {
10305
10354
  content: contentStr.slice(0, contextWindow * 2),
10306
10355
  summary: inferenceClient ? await generateResourceSummary(targetDoc.name, contentStr, getResourceEntityTypes(targetDoc), inferenceClient) : void 0
@@ -10582,15 +10631,18 @@ Summary:`;
10582
10631
  };
10583
10632
  }
10584
10633
  /**
10585
- * Get resource content as string
10634
+ * Get resource content as string. ResourceId-keyed (D-CONTENT b): the
10635
+ * primary representation's `storageUri` stays the has-content signal, the
10636
+ * fetch goes by id.
10586
10637
  */
10587
10638
  static async getResourceContent(resource, content) {
10588
- if (!resource.storageUri) {
10639
+ const id = getResourceId(resource);
10640
+ if (!getStorageUri(resource) || !id) {
10589
10641
  throw new Error("Resource content not found: no storageUri");
10590
10642
  }
10591
10643
  const primaryRep = getPrimaryRepresentation(resource);
10592
- const buf = await content.retrieve(resource.storageUri);
10593
- return decodeRepresentation(buf, primaryRep?.mediaType ?? "text/plain");
10644
+ const { data } = await content.getBinary(resourceId(id));
10645
+ return decodeRepresentation(Buffer.from(data), primaryRep?.mediaType ?? "text/plain");
10594
10646
  }
10595
10647
  /**
10596
10648
  * Extract annotation context from resource content
@@ -10745,15 +10797,15 @@ var LLMContext = class {
10745
10797
 
10746
10798
  // src/gatherer.ts
10747
10799
  var Gatherer = class {
10748
- constructor(kb, eventBus, inferenceClient, settleTimeoutMs, logger, embeddingProvider) {
10749
- this.kb = kb;
10800
+ constructor(stores, eventBus, inferenceClient, settleTimeoutMs, logger, embeddingProvider) {
10801
+ this.stores = stores;
10750
10802
  this.eventBus = eventBus;
10751
10803
  this.inferenceClient = inferenceClient;
10752
10804
  this.settleTimeoutMs = settleTimeoutMs;
10753
10805
  this.embeddingProvider = embeddingProvider;
10754
10806
  this.logger = logger;
10755
10807
  }
10756
- kb;
10808
+ stores;
10757
10809
  eventBus;
10758
10810
  inferenceClient;
10759
10811
  settleTimeoutMs;
@@ -10800,7 +10852,7 @@ var Gatherer = class {
10800
10852
  const response = await AnnotationContext.buildLLMContext(
10801
10853
  annotationId(event.annotationId),
10802
10854
  resourceId(event.resourceId),
10803
- this.kb,
10855
+ this.stores,
10804
10856
  this.embeddingProvider,
10805
10857
  event.options ?? {},
10806
10858
  this.inferenceClient,
@@ -10831,7 +10883,7 @@ var Gatherer = class {
10831
10883
  const result = await LLMContext.getResourceContext(
10832
10884
  resourceId(event.resourceId),
10833
10885
  event.options,
10834
- this.kb,
10886
+ this.stores,
10835
10887
  this.inferenceClient,
10836
10888
  this.settleTimeoutMs,
10837
10889
  this.logger
@@ -10857,7 +10909,7 @@ var Gatherer = class {
10857
10909
  return AnnotationContext.generateAnnotationSummary(
10858
10910
  annotationId5,
10859
10911
  resourceId10,
10860
- this.kb,
10912
+ this.stores,
10861
10913
  this.inferenceClient
10862
10914
  );
10863
10915
  }
@@ -10874,14 +10926,14 @@ var Gatherer = class {
10874
10926
  var import_rxjs2 = __toESM(require_cjs());
10875
10927
  var import_operators2 = __toESM(require_operators());
10876
10928
  var Matcher = class {
10877
- constructor(kb, eventBus, logger, inferenceClient, embeddingProvider) {
10878
- this.kb = kb;
10929
+ constructor(stores, eventBus, logger, inferenceClient, embeddingProvider) {
10930
+ this.stores = stores;
10879
10931
  this.eventBus = eventBus;
10880
10932
  this.inferenceClient = inferenceClient;
10881
10933
  this.embeddingProvider = embeddingProvider;
10882
10934
  this.logger = logger;
10883
10935
  }
10884
- kb;
10936
+ stores;
10885
10937
  eventBus;
10886
10938
  inferenceClient;
10887
10939
  embeddingProvider;
@@ -10961,13 +11013,13 @@ var Matcher = class {
10961
11013
  const views = deriveViews(context.graph, mainResourceId);
10962
11014
  const connections = views.connections;
10963
11015
  const [nameMatches, entityTypeMatches, semanticMatches] = await Promise.all([
10964
- this.kb.graph.listResources({ search: searchTerm, limit: 20 }).then((r) => r.resources),
10965
- annotationEntityTypes.length > 0 ? this.kb.graph.listResources({ entityTypes: annotationEntityTypes, limit: 50 }).then((r) => r.resources) : Promise.resolve([]),
11016
+ this.stores.graph.listResources({ search: searchTerm, limit: 20 }).then((r) => r.resources),
11017
+ annotationEntityTypes.length > 0 ? this.stores.graph.listResources({ entityTypes: annotationEntityTypes, limit: 50 }).then((r) => r.resources) : Promise.resolve([]),
10966
11018
  // 4. Semantic match — vector similarity search (if vectors configured)
10967
11019
  this.searchVectors(searchTerm)
10968
11020
  ]);
10969
11021
  const neighborResolved = await Promise.all(
10970
- connections.map((conn) => resourceWithViewGrace(this.kb, resourceId(conn.resourceId)))
11022
+ connections.map((conn) => resourceWithViewGrace(this.stores, resourceId(conn.resourceId)))
10971
11023
  );
10972
11024
  const laggedNeighbors = neighborResolved.filter((r) => r.laggedBehindView).length;
10973
11025
  if (laggedNeighbors > 0) {
@@ -10994,7 +11046,7 @@ var Matcher = class {
10994
11046
  let laggedSemantic = 0;
10995
11047
  for (const sm of semanticMatches) {
10996
11048
  semanticScores.set(sm.resourceId, sm.score);
10997
- const { resource, laggedBehindView } = await resourceWithViewGrace(this.kb, resourceId(sm.resourceId));
11049
+ const { resource, laggedBehindView } = await resourceWithViewGrace(this.stores, resourceId(sm.resourceId));
10998
11050
  if (laggedBehindView) laggedSemantic++;
10999
11051
  if (resource) addCandidate(resource, "semantic");
11000
11052
  }
@@ -11190,7 +11242,7 @@ For each candidate, output a line with the number and score, like:
11190
11242
  if (!searchTerm.trim()) return [];
11191
11243
  try {
11192
11244
  const embedding = await this.embeddingProvider.embed(searchTerm);
11193
- const results = await this.kb.vectors.searchResources(embedding, {
11245
+ const results = await this.stores.vectors.searchResources(embedding, {
11194
11246
  limit: 40,
11195
11247
  scoreThreshold: 0.4
11196
11248
  });
@@ -11215,9 +11267,9 @@ For each candidate, output a line with the number and score, like:
11215
11267
  // src/stower.ts
11216
11268
  var import_rxjs3 = __toESM(require_cjs());
11217
11269
  var import_operators3 = __toESM(require_operators());
11218
- async function readEntityTypesProjection(project) {
11270
+ async function readEntityTypesProjection(state) {
11219
11271
  const entityTypesPath = path3.join(
11220
- project.stateDir,
11272
+ state.stateDir,
11221
11273
  "projections",
11222
11274
  "__system__",
11223
11275
  "entitytypes.json"
@@ -11259,13 +11311,13 @@ function entityTypesNotRegisteredMessage(unknown) {
11259
11311
 
11260
11312
  // src/stower.ts
11261
11313
  var Stower = class {
11262
- constructor(kb, eventBus, project, logger) {
11263
- this.kb = kb;
11314
+ constructor(stores, eventBus, project, logger) {
11315
+ this.stores = stores;
11264
11316
  this.eventBus = eventBus;
11265
11317
  this.project = project;
11266
11318
  this.logger = logger;
11267
11319
  }
11268
- kb;
11320
+ stores;
11269
11321
  eventBus;
11270
11322
  project;
11271
11323
  subscription = null;
@@ -11279,6 +11331,7 @@ var Stower = class {
11279
11331
  );
11280
11332
  this.subscription = (0, import_rxjs3.merge)(
11281
11333
  pipe("yield:create", (e) => this.handleYieldCreate(e)),
11334
+ pipe("yield:clone-persist", (e) => this.handleYieldClonePersist(e)),
11282
11335
  pipe("yield:update", (e) => this.handleYieldUpdate(e)),
11283
11336
  pipe("yield:mv", (e) => this.handleYieldMv(e)),
11284
11337
  pipe("mark:create", (e) => this.handleMarkCreate(e)),
@@ -11306,14 +11359,14 @@ var Stower = class {
11306
11359
  const uid = userId(event._userId);
11307
11360
  try {
11308
11361
  const rId = resourceId(generateUuid());
11309
- const stored = await this.kb.content.register(event.storageUri, event.contentChecksum, { noGit: event.noGit });
11362
+ const stored = await this.stores.content.register(event.storageUri, event.contentChecksum, { noGit: event.noGit });
11310
11363
  const checksum = stored.checksum;
11311
11364
  const byteSize = event.byteSize;
11312
11365
  const generatedFrom = event.generatedFrom?.resourceId && event.generatedFrom?.annotationId ? {
11313
11366
  resourceId: resourceId(event.generatedFrom.resourceId),
11314
11367
  annotationId: annotationId(event.generatedFrom.annotationId)
11315
11368
  } : void 0;
11316
- await this.kb.eventStore.appendEvent({
11369
+ await this.stores.eventStore.appendEvent({
11317
11370
  type: "yield:created",
11318
11371
  resourceId: rId,
11319
11372
  userId: uid,
@@ -11366,13 +11419,66 @@ var Stower = class {
11366
11419
  });
11367
11420
  }
11368
11421
  }
11422
+ /**
11423
+ * Persist a clone (the CloneTokenManager's second half).
11424
+ *
11425
+ * A clone is its own domain fact, not a creation with an extra field: it
11426
+ * NAMES the resource it came from, and `ResourceClonedPayload` requires
11427
+ * that name. So it gets its own command and appends its own event, rather
11428
+ * than an optional parent turning `yield:created` into something else at
11429
+ * runtime.
11430
+ *
11431
+ * The token was already validated and the source's entity types already
11432
+ * read by the CloneTokenManager — the one party that can do either. What
11433
+ * happens here is what only the Stower may do: register the bytes and
11434
+ * append the event (single-writer, GATEWAY.md D4b).
11435
+ *
11436
+ * Generated resources do NOT come through here. Their provenance is
11437
+ * `generatedFrom` on `yield:created`, which is a different relation: a
11438
+ * generated resource is derived from a source, a clone IS a copy of one.
11439
+ */
11440
+ async handleYieldClonePersist(event) {
11441
+ if (!event._userId) {
11442
+ throw new Error("yield:clone-persist missing _userId (gateway injection)");
11443
+ }
11444
+ try {
11445
+ const rId = resourceId(generateUuid());
11446
+ const stored = await this.stores.content.register(event.storageUri, event.contentChecksum, { noGit: event.noGit });
11447
+ await this.stores.eventStore.appendEvent({
11448
+ type: "yield:cloned",
11449
+ resourceId: rId,
11450
+ userId: userId(event._userId),
11451
+ version: 1,
11452
+ payload: {
11453
+ name: event.name,
11454
+ format: event.format,
11455
+ contentChecksum: stored.checksum,
11456
+ contentByteSize: event.byteSize,
11457
+ storageUri: event.storageUri,
11458
+ parentResourceId: event.parentResourceId,
11459
+ entityTypes: event.entityTypes || [],
11460
+ language: event.language || void 0
11461
+ }
11462
+ });
11463
+ this.eventBus.get("yield:clone-persist-ok").next({
11464
+ correlationId: event.correlationId,
11465
+ response: { resourceId: rId }
11466
+ });
11467
+ } catch (error) {
11468
+ this.logger.error("Failed to persist clone", { error: errField(error) });
11469
+ this.eventBus.get("yield:clone-persist-failed").next({
11470
+ correlationId: event.correlationId,
11471
+ message: error instanceof Error ? error.message : String(error)
11472
+ });
11473
+ }
11474
+ }
11369
11475
  async handleYieldUpdate(event) {
11370
11476
  if (!event._userId) {
11371
11477
  throw new Error("yield:update missing _userId (gateway injection)");
11372
11478
  }
11373
11479
  try {
11374
- await this.kb.content.register(event.storageUri, event.contentChecksum, { noGit: event.noGit });
11375
- await this.kb.eventStore.appendEvent({
11480
+ await this.stores.content.register(event.storageUri, event.contentChecksum, { noGit: event.noGit });
11481
+ await this.stores.eventStore.appendEvent({
11376
11482
  type: "yield:updated",
11377
11483
  resourceId: resourceId(event.resourceId),
11378
11484
  userId: userId(event._userId),
@@ -11397,7 +11503,7 @@ var Stower = class {
11397
11503
  async handleYieldMv(event) {
11398
11504
  let rId;
11399
11505
  try {
11400
- const resolved = await resolveStorageUri(this.kb.projectionsDir, event.fromUri);
11506
+ const resolved = await resolveStorageUri(this.project.projectionsDir, event.fromUri);
11401
11507
  rId = resolved;
11402
11508
  } catch (error) {
11403
11509
  this.logger.error("Failed to resolve resource for move", { fromUri: event.fromUri, error });
@@ -11411,8 +11517,8 @@ var Stower = class {
11411
11517
  throw new Error("yield:mv missing _userId (gateway injection)");
11412
11518
  }
11413
11519
  try {
11414
- await this.kb.content.move(event.fromUri, event.toUri, { noGit: event.noGit });
11415
- await this.kb.eventStore.appendEvent({
11520
+ await this.stores.content.move(event.fromUri, event.toUri, { noGit: event.noGit });
11521
+ await this.stores.eventStore.appendEvent({
11416
11522
  type: "yield:moved",
11417
11523
  resourceId: rId,
11418
11524
  userId: userId(event._userId),
@@ -11436,7 +11542,7 @@ var Stower = class {
11436
11542
  }
11437
11543
  try {
11438
11544
  this.logger.debug("Stowing annotation", { annotationId: event.annotation.id });
11439
- await this.kb.eventStore.appendEvent(
11545
+ await this.stores.eventStore.appendEvent(
11440
11546
  {
11441
11547
  type: "mark:added",
11442
11548
  resourceId: resourceId(event.resourceId),
@@ -11462,7 +11568,7 @@ var Stower = class {
11462
11568
  throw new Error("mark:delete missing resourceId");
11463
11569
  }
11464
11570
  try {
11465
- await this.kb.eventStore.appendEvent({
11571
+ await this.stores.eventStore.appendEvent({
11466
11572
  type: "mark:removed",
11467
11573
  resourceId: resourceId(event.resourceId),
11468
11574
  userId: userId(event._userId),
@@ -11483,7 +11589,7 @@ var Stower = class {
11483
11589
  throw new Error("mark:update-body missing _userId (gateway injection)");
11484
11590
  }
11485
11591
  try {
11486
- await this.kb.eventStore.appendEvent(
11592
+ await this.stores.eventStore.appendEvent(
11487
11593
  {
11488
11594
  type: "mark:body-updated",
11489
11595
  resourceId: resourceId(event.resourceId),
@@ -11509,9 +11615,9 @@ var Stower = class {
11509
11615
  }
11510
11616
  try {
11511
11617
  if (event.storageUri) {
11512
- await this.kb.content.remove(event.storageUri, { keepFile: event.keepFile, noGit: event.noGit });
11618
+ await this.stores.content.remove(event.storageUri, { keepFile: event.keepFile, noGit: event.noGit });
11513
11619
  }
11514
- await this.kb.eventStore.appendEvent({
11620
+ await this.stores.eventStore.appendEvent({
11515
11621
  type: "mark:archived",
11516
11622
  resourceId: resourceId(event.resourceId),
11517
11623
  userId: userId(event._userId),
@@ -11533,14 +11639,14 @@ var Stower = class {
11533
11639
  }
11534
11640
  try {
11535
11641
  if (event.storageUri) {
11536
- const absPath = this.kb.content.resolveUri(event.storageUri);
11642
+ const absPath = this.stores.content.resolveUri(event.storageUri);
11537
11643
  try {
11538
11644
  await promises.access(absPath);
11539
11645
  } catch {
11540
11646
  throw new Error(`Cannot unarchive: file not found at ${event.storageUri}`);
11541
11647
  }
11542
11648
  }
11543
- await this.kb.eventStore.appendEvent({
11649
+ await this.stores.eventStore.appendEvent({
11544
11650
  type: "mark:unarchived",
11545
11651
  resourceId: resourceId(event.resourceId),
11546
11652
  userId: userId(event._userId),
@@ -11561,7 +11667,7 @@ var Stower = class {
11561
11667
  throw new Error("frame:add-entity-type missing _userId (gateway injection)");
11562
11668
  }
11563
11669
  try {
11564
- await this.kb.eventStore.appendEvent({
11670
+ await this.stores.eventStore.appendEvent({
11565
11671
  type: "frame:entity-type-added",
11566
11672
  userId: userId(event._userId),
11567
11673
  version: 1,
@@ -11581,7 +11687,7 @@ var Stower = class {
11581
11687
  throw new Error("frame:add-tag-schema missing _userId (gateway injection)");
11582
11688
  }
11583
11689
  try {
11584
- await this.kb.eventStore.appendEvent({
11690
+ await this.stores.eventStore.appendEvent({
11585
11691
  type: "frame:tag-schema-added",
11586
11692
  userId: userId(event._userId),
11587
11693
  version: 1,
@@ -11612,7 +11718,7 @@ var Stower = class {
11612
11718
  }
11613
11719
  }
11614
11720
  for (const entityType2 of added) {
11615
- await this.kb.eventStore.appendEvent({
11721
+ await this.stores.eventStore.appendEvent({
11616
11722
  type: "mark:entity-tag-added",
11617
11723
  resourceId: resourceId(event.resourceId),
11618
11724
  userId: uid,
@@ -11621,7 +11727,7 @@ var Stower = class {
11621
11727
  });
11622
11728
  }
11623
11729
  for (const entityType2 of removed) {
11624
- await this.kb.eventStore.appendEvent({
11730
+ await this.stores.eventStore.appendEvent({
11625
11731
  type: "mark:entity-tag-removed",
11626
11732
  resourceId: resourceId(event.resourceId),
11627
11733
  userId: uid,
@@ -11642,7 +11748,7 @@ var Stower = class {
11642
11748
  if (!event._userId) {
11643
11749
  throw new Error("job:start missing _userId (gateway injection)");
11644
11750
  }
11645
- await this.kb.eventStore.appendEvent({
11751
+ await this.stores.eventStore.appendEvent({
11646
11752
  type: "job:started",
11647
11753
  resourceId: resourceId(event.resourceId),
11648
11754
  userId: userId(event._userId),
@@ -11658,7 +11764,7 @@ var Stower = class {
11658
11764
  if (!event._userId) {
11659
11765
  throw new Error("job:complete missing _userId (gateway injection)");
11660
11766
  }
11661
- await this.kb.eventStore.appendEvent({
11767
+ await this.stores.eventStore.appendEvent({
11662
11768
  type: "job:completed",
11663
11769
  resourceId: resourceId(event.resourceId),
11664
11770
  userId: userId(event._userId),
@@ -11675,7 +11781,7 @@ var Stower = class {
11675
11781
  if (!event._userId) {
11676
11782
  throw new Error("job:fail missing _userId (gateway injection)");
11677
11783
  }
11678
- await this.kb.eventStore.appendEvent({
11784
+ await this.stores.eventStore.appendEvent({
11679
11785
  type: "job:failed",
11680
11786
  resourceId: resourceId(event.resourceId),
11681
11787
  userId: userId(event._userId),
@@ -11713,9 +11819,9 @@ async function readAnchoredText(kb, resourceId10) {
11713
11819
  throw error;
11714
11820
  }
11715
11821
  }
11716
- async function readTagSchemasProjection(project) {
11822
+ async function readTagSchemasProjection(state) {
11717
11823
  const tagSchemasPath = path3.join(
11718
- project.stateDir,
11824
+ state.stateDir,
11719
11825
  "projections",
11720
11826
  "__system__",
11721
11827
  "tagschemas.json"
@@ -11801,8 +11907,7 @@ function deriveAgentRoster(config) {
11801
11907
 
11802
11908
  // src/browser.ts
11803
11909
  var Browser = class {
11804
- constructor(views, kb, eventBus, project, config, limitsDiscovery, embeddingProvider, logger) {
11805
- this.views = views;
11910
+ constructor(kb, eventBus, project, config, limitsDiscovery, embeddingProvider, logger) {
11806
11911
  this.kb = kb;
11807
11912
  this.eventBus = eventBus;
11808
11913
  this.project = project;
@@ -11811,7 +11916,6 @@ var Browser = class {
11811
11916
  this.embeddingProvider = embeddingProvider;
11812
11917
  this.logger = logger;
11813
11918
  }
11814
- views;
11815
11919
  kb;
11816
11920
  eventBus;
11817
11921
  project;
@@ -11843,6 +11947,7 @@ var Browser = class {
11843
11947
  this.subscriptions.push(
11844
11948
  pipe("browse:resource-requested", (e) => this.handleBrowseResource(e)).subscribe({ error: errorHandler }),
11845
11949
  pipe("browse:anchored-text-requested", (e) => this.handleAnchoredText(e)).subscribe({ error: errorHandler }),
11950
+ pipe("browse:anchored-text-by-checksum-requested", (e) => this.handleAnchoredTextByChecksum(e)).subscribe({ error: errorHandler }),
11846
11951
  pipe("browse:resources-requested", (e) => this.handleBrowseResources(e)).subscribe({ error: errorHandler }),
11847
11952
  pipe("browse:annotations-requested", (e) => this.handleBrowseAnnotations(e)).subscribe({ error: errorHandler }),
11848
11953
  pipe("browse:annotation-requested", (e) => this.handleBrowseAnnotation(e)).subscribe({ error: errorHandler }),
@@ -11886,6 +11991,30 @@ var Browser = class {
11886
11991
  });
11887
11992
  }
11888
11993
  }
11994
+ /**
11995
+ * Serve the checksum-addressed consult (ANCHORED-TEXT-TO-SMELTER P2, D2) —
11996
+ * the detection workers' read-through cache. The caller computed the key
11997
+ * from bytes it already holds, so unlike the resource-addressed read there
11998
+ * is nothing to resolve and no settle barrier: with no resourceId there is
11999
+ * no content generation to wait on, and a miss means "extract it yourself".
12000
+ * A hit — success or decline — is served whole, so the caller runs neither
12001
+ * parser nor engine. Read-only over the wire: the Smelter is the sole
12002
+ * writer and never answers here.
12003
+ */
12004
+ async handleAnchoredTextByChecksum(event) {
12005
+ try {
12006
+ this.eventBus.get("browse:anchored-text-by-checksum-result").next({
12007
+ correlationId: event.correlationId,
12008
+ response: await this.kb.anchoredText.read(event.checksum)
12009
+ });
12010
+ } catch (error) {
12011
+ this.logger.error("Browse anchored text by checksum failed", { checksum: event.checksum, error: errField(error) });
12012
+ this.eventBus.get("browse:anchored-text-by-checksum-failed").next({
12013
+ correlationId: event.correlationId,
12014
+ message: error instanceof Error ? error.message : String(error)
12015
+ });
12016
+ }
12017
+ }
11889
12018
  async handleBrowseResource(event) {
11890
12019
  try {
11891
12020
  const response = await assembleResourceGraph(this.kb, resourceId(event.resourceId));
@@ -12183,10 +12312,13 @@ var Browser = class {
12183
12312
  return;
12184
12313
  }
12185
12314
  const visible = dirents.filter((d) => d.name !== ".semiont" && !d.name.startsWith("."));
12186
- const allViews = await this.views.getAll();
12315
+ const allViews = await this.kb.views.getAll();
12187
12316
  const prefix = `file://${resolved}`;
12188
12317
  const viewsByUri = new Map(
12189
- allViews.filter((v) => v.resource.storageUri?.startsWith(prefix + "/") || v.resource.storageUri?.startsWith(prefix + path3.sep)).map((v) => [v.resource.storageUri, v])
12318
+ allViews.filter((v) => {
12319
+ const uri = getStorageUri(v.resource);
12320
+ return uri?.startsWith(prefix + "/") || uri?.startsWith(prefix + path3.sep);
12321
+ }).map((v) => [getStorageUri(v.resource), v])
12190
12322
  );
12191
12323
  const entries = [];
12192
12324
  for (const dirent of visible) {
@@ -12275,13 +12407,13 @@ function createLimitsDiscovery(config, logger, options) {
12275
12407
  }
12276
12408
  }
12277
12409
  const consult = async (pair, client) => {
12278
- let timer2;
12410
+ let timer;
12279
12411
  try {
12280
12412
  const discovery = client.limits();
12281
12413
  const raced = await Promise.race([
12282
12414
  discovery,
12283
12415
  new Promise((resolve2) => {
12284
- timer2 = setTimeout(() => resolve2(void 0), budgetMs);
12416
+ timer = setTimeout(() => resolve2(void 0), budgetMs);
12285
12417
  })
12286
12418
  ]);
12287
12419
  if (raced === void 0) {
@@ -12298,7 +12430,7 @@ function createLimitsDiscovery(config, logger, options) {
12298
12430
  });
12299
12431
  return void 0;
12300
12432
  } finally {
12301
- clearTimeout(timer2);
12433
+ clearTimeout(timer);
12302
12434
  }
12303
12435
  };
12304
12436
  return {
@@ -12334,6 +12466,15 @@ async function readAnnotationFromView(kb, resourceId10, annotationId5) {
12334
12466
  const allAnnotations = await AnnotationContext.getAllAnnotations(resourceId10, kb);
12335
12467
  return allAnnotations.find((a) => a.id === annotationId5) ?? null;
12336
12468
  }
12469
+ function wireEnrichment(eventStore, kb) {
12470
+ eventStore.setEnrichEvent(async (event, resourceId10) => {
12471
+ const annId = eventAnnotationId(event);
12472
+ if (annId === null) return event;
12473
+ const annotation = await readAnnotationFromView(kb, resourceId10, annId);
12474
+ if (annotation === null) return event;
12475
+ return { ...event, annotation };
12476
+ });
12477
+ }
12337
12478
 
12338
12479
  // src/clone-token-manager.ts
12339
12480
  var import_rxjs6 = __toESM(require_cjs());
@@ -12385,16 +12526,67 @@ var ResourceOperations = class {
12385
12526
  );
12386
12527
  return resourceId(rId);
12387
12528
  }
12529
+ /**
12530
+ * Persist a CLONE via EventBus → Stower.
12531
+ *
12532
+ * Separate from `createResource` because a clone is a separate fact: it
12533
+ * names its parent, and `yield:cloned` requires that name. Callers reach
12534
+ * this only after the CloneTokenManager has validated the token — it is the
12535
+ * inner half of the flow, not a public entry point.
12536
+ */
12537
+ static async persistClone(input, userId3, eventBus) {
12538
+ const { resourceId: rId } = await busRequest(
12539
+ asBusRequestPrimitive(eventBus),
12540
+ "yield:clone-persist",
12541
+ {
12542
+ name: input.name,
12543
+ storageUri: input.storageUri,
12544
+ contentChecksum: input.contentChecksum,
12545
+ byteSize: input.byteSize,
12546
+ format: input.format,
12547
+ parentResourceId: input.parentResourceId,
12548
+ entityTypes: input.entityTypes,
12549
+ language: input.language,
12550
+ _userId: userId3
12551
+ },
12552
+ 3e4
12553
+ );
12554
+ return resourceId(rId);
12555
+ }
12556
+ /**
12557
+ * Create a resource from a clone token via EventBus → CloneTokenManager.
12558
+ * The bytes are already stored (the gateway's upload path, `noGit` — the
12559
+ * Archivist's register does the one `git add`, D4b); the command carries
12560
+ * storage coordinates only (EXTRACT-ARCHIVIST P3, D4a).
12561
+ */
12562
+ static async createFromCloneToken(input, userId3, eventBus) {
12563
+ const { resourceId: rId } = await busRequest(
12564
+ asBusRequestPrimitive(eventBus),
12565
+ "yield:clone-create",
12566
+ {
12567
+ token: input.token,
12568
+ name: input.name,
12569
+ storageUri: input.storageUri,
12570
+ contentChecksum: input.contentChecksum,
12571
+ byteSize: input.byteSize,
12572
+ format: input.format,
12573
+ archiveOriginal: input.archiveOriginal,
12574
+ _userId: userId3
12575
+ },
12576
+ 3e4
12577
+ );
12578
+ return resourceId(rId);
12579
+ }
12388
12580
  };
12389
12581
 
12390
12582
  // src/clone-token-manager.ts
12391
12583
  var CloneTokenManager = class {
12392
- constructor(kb, eventBus, logger) {
12393
- this.kb = kb;
12584
+ constructor(stores, eventBus, logger) {
12585
+ this.stores = stores;
12394
12586
  this.eventBus = eventBus;
12395
12587
  this.logger = logger;
12396
12588
  }
12397
- kb;
12589
+ stores;
12398
12590
  eventBus;
12399
12591
  subscriptions = [];
12400
12592
  logger;
@@ -12419,7 +12611,7 @@ var CloneTokenManager = class {
12419
12611
  }
12420
12612
  async handleGenerateToken(event) {
12421
12613
  try {
12422
- const resource = await ResourceContext.getResourceMetadata(resourceId(event.resourceId), this.kb);
12614
+ const resource = await ResourceContext.getResourceMetadata(resourceId(event.resourceId), this.stores);
12423
12615
  if (!resource) {
12424
12616
  this.eventBus.get("yield:clone-token-failed").next({
12425
12617
  correlationId: event.correlationId,
@@ -12427,7 +12619,8 @@ var CloneTokenManager = class {
12427
12619
  });
12428
12620
  return;
12429
12621
  }
12430
- if (!resource.storageUri) {
12622
+ const storageUri = getStorageUri(resource);
12623
+ if (!storageUri) {
12431
12624
  this.eventBus.get("yield:clone-token-failed").next({
12432
12625
  correlationId: event.correlationId,
12433
12626
  message: "Resource content not found"
@@ -12435,7 +12628,7 @@ var CloneTokenManager = class {
12435
12628
  return;
12436
12629
  }
12437
12630
  try {
12438
- await this.kb.content.retrieve(resource.storageUri);
12631
+ await promises.access(this.stores.content.resolveUri(storageUri));
12439
12632
  } catch {
12440
12633
  this.eventBus.get("yield:clone-token-failed").next({
12441
12634
  correlationId: event.correlationId,
@@ -12482,7 +12675,7 @@ var CloneTokenManager = class {
12482
12675
  });
12483
12676
  return;
12484
12677
  }
12485
- const sourceResource = await ResourceContext.getResourceMetadata(tokenData.resourceId, this.kb);
12678
+ const sourceResource = await ResourceContext.getResourceMetadata(tokenData.resourceId, this.stores);
12486
12679
  if (!sourceResource) {
12487
12680
  this.eventBus.get("yield:clone-resource-failed").next({
12488
12681
  correlationId: event.correlationId,
@@ -12531,7 +12724,7 @@ var CloneTokenManager = class {
12531
12724
  });
12532
12725
  return;
12533
12726
  }
12534
- const sourceDoc = await ResourceContext.getResourceMetadata(tokenData.resourceId, this.kb);
12727
+ const sourceDoc = await ResourceContext.getResourceMetadata(tokenData.resourceId, this.stores);
12535
12728
  if (!sourceDoc) {
12536
12729
  this.eventBus.get("yield:clone-create-failed").next({
12537
12730
  correlationId: event.correlationId,
@@ -12539,17 +12732,14 @@ var CloneTokenManager = class {
12539
12732
  });
12540
12733
  return;
12541
12734
  }
12542
- const base = baseMediaType(getPrimaryRepresentation(sourceDoc)?.mediaType ?? "text/plain");
12543
- const format = isSupportedMediaType(base) && capabilitiesOf(base)?.authorable ? base : "text/plain";
12544
- const resolvedUri = deriveStorageUri(event.name, format);
12545
- const stored = await this.kb.content.store(Buffer.from(event.content), resolvedUri);
12546
- const newResourceId = await ResourceOperations.createResource(
12735
+ const newResourceId = await ResourceOperations.persistClone(
12547
12736
  {
12548
12737
  name: event.name,
12549
- storageUri: resolvedUri,
12550
- contentChecksum: stored.checksum,
12551
- byteSize: stored.byteSize,
12552
- format,
12738
+ storageUri: event.storageUri,
12739
+ contentChecksum: event.contentChecksum,
12740
+ byteSize: event.byteSize,
12741
+ format: event.format,
12742
+ parentResourceId: String(tokenData.resourceId),
12553
12743
  entityTypes: getResourceEntityTypes(sourceDoc)
12554
12744
  },
12555
12745
  userId(event._userId),
@@ -12617,42 +12807,115 @@ async function stopKnowledgeSystem(ks) {
12617
12807
  ks.kb.weaveProgress.dispose();
12618
12808
  await ks.kb.graph.disconnect();
12619
12809
  }
12620
- function registerAnnotationAssemblyHandler(eventBus, parentLogger) {
12810
+ async function assertAnnotatableTarget(kb, target) {
12811
+ const view = await kb.views.get(resourceId(target));
12812
+ const mediaType = getPrimaryRepresentation(view?.resource)?.mediaType;
12813
+ if (!mediaType || !isAnnotatable(mediaType)) {
12814
+ throw new Error(`"${mediaType ?? "unknown"}" cannot be annotated`);
12815
+ }
12816
+ }
12817
+ var AnnotationOperations = class {
12818
+ /**
12819
+ * Create a new annotation via EventBus → Stower
12820
+ */
12821
+ static async createAnnotation(request, userId3, creator, eventBus, kb) {
12822
+ await assertAnnotatableTarget(kb, request.target.source);
12823
+ const { annotation } = assembleAnnotation(request, creator);
12824
+ const resId = resourceId(request.target.source);
12825
+ eventBus.get("mark:create").next({
12826
+ annotation,
12827
+ _userId: userId3,
12828
+ resourceId: resId
12829
+ });
12830
+ return { annotation };
12831
+ }
12832
+ /**
12833
+ * Update annotation body via EventBus → Stower
12834
+ */
12835
+ static async updateAnnotationBody(id, request, userId3, eventBus, kb) {
12836
+ const resId = resourceId(request.resourceId);
12837
+ const annotation = await AnnotationContext.getAnnotation(
12838
+ annotationId(id),
12839
+ resId,
12840
+ kb
12841
+ );
12842
+ if (!annotation) {
12843
+ throw new Error("Annotation not found");
12844
+ }
12845
+ eventBus.get("mark:update-body").next({
12846
+ annotationId: annotationId(id),
12847
+ _userId: userId3,
12848
+ resourceId: resId,
12849
+ operations: request.operations
12850
+ });
12851
+ const updatedBody = applyBodyOperations(annotation.body, request.operations);
12852
+ return {
12853
+ annotation: {
12854
+ ...annotation,
12855
+ body: updatedBody
12856
+ }
12857
+ };
12858
+ }
12859
+ /**
12860
+ * Delete an annotation via EventBus → Stower
12861
+ */
12862
+ static async deleteAnnotation(id, resourceIdStr, userId3, eventBus, kb, logger) {
12863
+ const resId = resourceId(resourceIdStr);
12864
+ const projection = await AnnotationContext.getResourceAnnotations(resId, kb);
12865
+ const annotation = projection.annotations.find((a) => a.id === id);
12866
+ if (!annotation) {
12867
+ throw new Error("Annotation not found in resource");
12868
+ }
12869
+ logger?.debug("Removing annotation via EventBus", { annotationId: id });
12870
+ eventBus.get("mark:delete").next({
12871
+ annotationId: annotationId(id),
12872
+ _userId: userId3,
12873
+ resourceId: resId
12874
+ });
12875
+ logger?.debug("Annotation delete event emitted");
12876
+ }
12877
+ };
12878
+
12879
+ // src/handlers/annotation-assembly.ts
12880
+ function registerAnnotationAssemblyHandler(eventBus, kb, parentLogger) {
12621
12881
  const logger = parentLogger.child({ component: "annotation-assembly" });
12622
12882
  const inflight = /* @__PURE__ */ new Map();
12623
12883
  eventBus.get("mark:create-request").subscribe((command) => {
12624
- const { correlationId, resourceId: resId, request, _userId } = command;
12625
- const cid = correlationId;
12626
- try {
12627
- if (!_userId || typeof _userId !== "string") {
12628
- throw new Error("_userId is required (injected by bus gateway)");
12629
- }
12630
- if (!cid) {
12631
- throw new Error("correlationId is required on mark:create-request");
12884
+ void (async () => {
12885
+ const { correlationId, resourceId: resId, request, _userId } = command;
12886
+ const cid = correlationId;
12887
+ try {
12888
+ if (!_userId || typeof _userId !== "string") {
12889
+ throw new Error("_userId is required (injected by bus gateway)");
12890
+ }
12891
+ if (!cid) {
12892
+ throw new Error("correlationId is required on mark:create-request");
12893
+ }
12894
+ await assertAnnotatableTarget(kb, resId);
12895
+ const agent = didToAgent(_userId);
12896
+ const { annotation } = assembleAnnotation(request, agent);
12897
+ inflight.set(cid, { annotationId: annotation.id });
12898
+ eventBus.get("mark:create").next({
12899
+ correlationId: cid,
12900
+ annotation,
12901
+ _userId,
12902
+ resourceId: resourceId(resId)
12903
+ });
12904
+ logger.info("Annotation assembled, awaiting persistence", {
12905
+ annotationId: annotation.id,
12906
+ correlationId: cid
12907
+ });
12908
+ } catch (error) {
12909
+ logger.warn("mark:create-request failed during assembly", {
12910
+ correlationId: cid,
12911
+ error: error.message
12912
+ });
12913
+ eventBus.get("mark:create-failed").next({
12914
+ correlationId: cid,
12915
+ message: error.message
12916
+ });
12632
12917
  }
12633
- const agent = didToAgent(_userId);
12634
- const { annotation } = assembleAnnotation(request, agent);
12635
- inflight.set(cid, { annotationId: annotation.id });
12636
- eventBus.get("mark:create").next({
12637
- correlationId: cid,
12638
- annotation,
12639
- _userId,
12640
- resourceId: resourceId(resId)
12641
- });
12642
- logger.info("Annotation assembled, awaiting persistence", {
12643
- annotationId: annotation.id,
12644
- correlationId: cid
12645
- });
12646
- } catch (error) {
12647
- logger.warn("mark:create-request failed during assembly", {
12648
- correlationId: cid,
12649
- error: error.message
12650
- });
12651
- eventBus.get("mark:create-failed").next({
12652
- correlationId: cid,
12653
- message: error.message
12654
- });
12655
- }
12918
+ })();
12656
12919
  });
12657
12920
  eventBus.get("mark:added").subscribe((event) => {
12658
12921
  const cid = event.metadata?.correlationId;
@@ -12672,7 +12935,7 @@ function registerAnnotationAssemblyHandler(eventBus, parentLogger) {
12672
12935
  inflight.delete(cid);
12673
12936
  });
12674
12937
  }
12675
- function registerAnnotationLookupHandlers(eventBus, kb, gatherer, parentLogger) {
12938
+ function registerAnnotationContextHandler(eventBus, kb, parentLogger) {
12676
12939
  const logger = parentLogger.child({ component: "annotation-lookups" });
12677
12940
  eventBus.get("browse:annotation-context-requested").subscribe(async (command) => {
12678
12941
  const { correlationId } = command;
@@ -12700,6 +12963,9 @@ function registerAnnotationLookupHandlers(eventBus, kb, gatherer, parentLogger)
12700
12963
  });
12701
12964
  }
12702
12965
  });
12966
+ }
12967
+ function registerGatherSummaryHandler(eventBus, gatherer, parentLogger) {
12968
+ const logger = parentLogger.child({ component: "annotation-lookups" });
12703
12969
  eventBus.get("gather:summary-requested").subscribe(async (command) => {
12704
12970
  const { correlationId } = command;
12705
12971
  const annId = command.annotationId;
@@ -12785,7 +13051,7 @@ function parseDidUser(did) {
12785
13051
  const email = decodeURIComponent(parts.slice(usersIdx + 1).join(":"));
12786
13052
  return { userId: did, email, domain };
12787
13053
  }
12788
- function registerJobCommandHandlers(eventBus, jobQueue, project, parentLogger) {
13054
+ function registerJobCommandHandlers(eventBus, jobQueue, state, parentLogger) {
12789
13055
  const logger = parentLogger.child({ component: "job-commands" });
12790
13056
  eventBus.get("job:create").subscribe(async (command) => {
12791
13057
  const { correlationId, jobType, resourceId: resId, params, _userId } = command;
@@ -12850,7 +13116,7 @@ function registerJobCommandHandlers(eventBus, jobQueue, project, parentLogger) {
12850
13116
  };
12851
13117
  const jobParams = job.params;
12852
13118
  if ((jobType === "reference-annotation" || jobType === "generation") && Array.isArray(jobParams.entityTypes) && jobParams.entityTypes.length > 0) {
12853
- const registered = await readEntityTypesProjection(project);
13119
+ const registered = await readEntityTypesProjection(state);
12854
13120
  const result = validateEntityTypes(registered, jobParams.entityTypes);
12855
13121
  if (!result.ok) {
12856
13122
  throw new Error(entityTypesNotRegisteredMessage(result.unknown));
@@ -12860,7 +13126,7 @@ function registerJobCommandHandlers(eventBus, jobQueue, project, parentLogger) {
12860
13126
  jobParams.entityTypes = jobParams.entityTypes.map((et) => entityType(et));
12861
13127
  }
12862
13128
  if (jobType === "tag-annotation") {
12863
- const schemas = await readTagSchemasProjection(project);
13129
+ const schemas = await readTagSchemasProjection(state);
12864
13130
  const result = resolveTagSchema(schemas, jobParams.schemaId);
12865
13131
  if (result.error !== void 0) {
12866
13132
  throw new Error(result.error);
@@ -12976,17 +13242,27 @@ function registerJobCommandHandlers(eventBus, jobQueue, project, parentLogger) {
12976
13242
  }
12977
13243
 
12978
13244
  // src/handlers/index.ts
12979
- function registerBusHandlers(eventBus, knowledgeSystem, jobQueue, project, logger) {
12980
- registerAnnotationAssemblyHandler(eventBus, logger);
12981
- registerAnnotationLookupHandlers(eventBus, knowledgeSystem.kb, knowledgeSystem.gatherer, logger);
13245
+ function registerBusHandlers(eventBus, knowledgeSystem, jobQueue, state, logger) {
13246
+ const { kb } = knowledgeSystem;
13247
+ registerAnnotationAssemblyHandler(eventBus, kb, logger);
13248
+ registerAnnotationContextHandler(
13249
+ eventBus,
13250
+ { views: kb.views, content: workingTreeContentReads(kb.views, kb.content) },
13251
+ logger
13252
+ );
13253
+ registerGatherSummaryHandler(eventBus, knowledgeSystem.gatherer, logger);
13254
+ registerBindUpdateBodyHandler(eventBus, logger);
13255
+ registerJobCommandHandlers(eventBus, jobQueue, state, logger);
13256
+ }
13257
+ function registerGatewayBusHandlers(eventBus, jobQueue, state, logger) {
12982
13258
  registerBindUpdateBodyHandler(eventBus, logger);
12983
- registerJobCommandHandlers(eventBus, jobQueue, project, logger);
13259
+ registerJobCommandHandlers(eventBus, jobQueue, state, logger);
12984
13260
  }
12985
13261
 
12986
13262
  // src/service.ts
12987
- async function createJobQueue(project, eventBus, logger) {
13263
+ async function createJobQueue(state, eventBus, logger) {
12988
13264
  const jobQueueLogger = logger.child({ component: "job-queue" });
12989
- const jobQueue = new FsJobQueue(project, jobQueueLogger, eventBus);
13265
+ const jobQueue = new FsJobQueue(state, jobQueueLogger, eventBus);
12990
13266
  await jobQueue.initialize();
12991
13267
  registerJobQueueProvider(() => jobQueue.getStats());
12992
13268
  const jobStatusSubscription = eventBus.get("job:status-requested").pipe(
@@ -13026,12 +13302,12 @@ async function createJobQueue(project, eventBus, logger) {
13026
13302
  }
13027
13303
  var STARTUP_CONNECT_TIMEOUT_MS = 6e4;
13028
13304
  async function withStartupTimeout(what, work) {
13029
- let timer2;
13305
+ let timer;
13030
13306
  try {
13031
13307
  return await Promise.race([
13032
13308
  work,
13033
13309
  new Promise((_resolve, reject) => {
13034
- timer2 = setTimeout(
13310
+ timer = setTimeout(
13035
13311
  () => reject(
13036
13312
  new Error(
13037
13313
  `${what} did not become available within ${STARTUP_CONNECT_TIMEOUT_MS / 1e3}s. Exiting so the container restart policy can retry \u2014 it is normal for a dependency to be slow when every service restarts at once.`
@@ -13042,10 +13318,10 @@ async function withStartupTimeout(what, work) {
13042
13318
  })
13043
13319
  ]);
13044
13320
  } finally {
13045
- if (timer2 !== void 0) clearTimeout(timer2);
13321
+ if (timer !== void 0) clearTimeout(timer);
13046
13322
  }
13047
13323
  }
13048
- async function createKnowledgeSystemFromConfig(project, config, eventBus, logger, skipRebuild) {
13324
+ async function connectStores(project, config, eventBus, logger, skipRebuild) {
13049
13325
  const graphConfig = config.services.graph;
13050
13326
  logger.info("Connecting to graph database", { type: graphConfig.type });
13051
13327
  const graphDb = await withStartupTimeout("Graph database", getGraphDatabase(graphConfig));
@@ -13088,18 +13364,18 @@ async function createKnowledgeSystemFromConfig(project, config, eventBus, logger
13088
13364
  vectorStore,
13089
13365
  skipRebuild
13090
13366
  });
13091
- eventStore.setEnrichEvent(async (event, resourceId10) => {
13092
- const annId = eventAnnotationId(event);
13093
- if (annId === null) return event;
13094
- const annotation = await readAnnotationFromView(kb, resourceId10, annId);
13095
- if (annotation === null) return event;
13096
- return { ...event, annotation };
13097
- });
13367
+ return { kb, eventStore, embeddingProvider };
13368
+ }
13369
+ async function createKnowledgeSystemFromConfig(project, config, eventBus, logger, skipRebuild) {
13370
+ const { kb, eventStore, embeddingProvider } = await connectStores(project, config, eventBus, logger, skipRebuild ?? false);
13371
+ wireEnrichment(eventStore, kb);
13098
13372
  const stower = new Stower(kb, eventBus, project, logger.child({ component: "stower" }));
13099
13373
  await stower.initialize();
13100
13374
  await bootstrapEntityTypes(eventBus, eventStore, logger.child({ component: "entity-types-bootstrap" }));
13101
13375
  const gatherer = new Gatherer(
13102
- kb,
13376
+ // The content capability is ResourceId-keyed (D-CONTENT b); in-process
13377
+ // it wraps this root's own working tree behind the transport shape.
13378
+ { ...kb, content: workingTreeContentReads(kb.views, kb.content) },
13103
13379
  eventBus,
13104
13380
  createInferenceClient(resolveActorInference(config, "gatherer"), logger.child({ component: "inference-client-gatherer" })),
13105
13381
  config.gather.settleTimeoutMs,
@@ -13116,14 +13392,14 @@ async function createKnowledgeSystemFromConfig(project, config, eventBus, logger
13116
13392
  );
13117
13393
  await matcher.initialize();
13118
13394
  const limitsDiscovery = createLimitsDiscovery(config, logger.child({ component: "limits-discovery" }));
13119
- const browser = new Browser(kb.views, kb, eventBus, project, config, limitsDiscovery, embeddingProvider, logger.child({ component: "browser" }));
13395
+ const browser = new Browser(kb, eventBus, project, config, limitsDiscovery, embeddingProvider, logger.child({ component: "browser" }));
13120
13396
  await browser.initialize();
13121
13397
  const cloneTokenManager = new CloneTokenManager(kb, eventBus, logger.child({ component: "clone-token-manager" }));
13122
13398
  await cloneTokenManager.initialize();
13123
13399
  const ks = { kb, stower, gatherer, matcher, browser, cloneTokenManager, stop: () => stopKnowledgeSystem(ks) };
13124
13400
  return ks;
13125
13401
  }
13126
- async function startMakeMeaning(project, config, eventBus, logger, options) {
13402
+ function assertMakeMeaningConfig(config) {
13127
13403
  if (!config.services?.graph) {
13128
13404
  throw new Error("services.graph is required for make-meaning service");
13129
13405
  }
@@ -13135,6 +13411,9 @@ async function startMakeMeaning(project, config, eventBus, logger, options) {
13135
13411
  `gather.settleTimeoutMs (${config.gather.settleTimeoutMs}ms) plus the graph barrier budget (${GRAPH_BARRIER_BUDGET_MS}ms) must nest inside the job-worker stall watchdog (${STALL_THRESHOLD_MS}ms) \u2014 lower settleTimeoutMs (A4)`
13136
13412
  );
13137
13413
  }
13414
+ }
13415
+ async function startMakeMeaning(project, config, eventBus, logger, options) {
13416
+ assertMakeMeaningConfig(config);
13138
13417
  const skipRebuild = options?.skipRebuild ?? process.env.SEMIONT_SKIP_REBUILD === "true";
13139
13418
  const { jobQueue, jobStatusSubscription } = await createJobQueue(project, eventBus, logger);
13140
13419
  const knowledgeSystem = await createKnowledgeSystemFromConfig(project, config, eventBus, logger, skipRebuild);
@@ -13151,6 +13430,106 @@ async function startMakeMeaning(project, config, eventBus, logger, options) {
13151
13430
  }
13152
13431
  };
13153
13432
  }
13433
+ async function startMakeMeaningGateway(state, config, eventBus, logger) {
13434
+ assertMakeMeaningConfig(config);
13435
+ const { jobQueue, jobStatusSubscription } = await createJobQueue(state, eventBus, logger);
13436
+ registerGatewayBusHandlers(eventBus, jobQueue, state, logger);
13437
+ return {
13438
+ jobQueue,
13439
+ state,
13440
+ stop: async () => {
13441
+ logger.info("Stopping gateway make-meaning");
13442
+ jobStatusSubscription.unsubscribe();
13443
+ logger.info("Gateway make-meaning stopped");
13444
+ }
13445
+ };
13446
+ }
13447
+ var json = (res, status, body) => {
13448
+ res.writeHead(status, { "Content-Type": "application/json" });
13449
+ res.end(JSON.stringify(body));
13450
+ };
13451
+ function createArchivistServer(deps) {
13452
+ const { events, content, views, workerSecret, health, branch, logger } = deps;
13453
+ const authorized = (req, res) => {
13454
+ if (!workerSecret) {
13455
+ json(res, 503, { error: "disabled: no SEMIONT_WORKER_SECRET configured" });
13456
+ return false;
13457
+ }
13458
+ if (req.headers.authorization !== `Bearer ${workerSecret}`) {
13459
+ json(res, 401, { error: "unauthorized" });
13460
+ return false;
13461
+ }
13462
+ return true;
13463
+ };
13464
+ return createServer((req, res) => {
13465
+ const url = new URL(req.url ?? "/", "http://archivist");
13466
+ if (req.method === "GET" && url.pathname === "/health") {
13467
+ json(res, 200, health());
13468
+ return;
13469
+ }
13470
+ if (req.method === "GET" && url.pathname === "/kb/branch") {
13471
+ if (!authorized(req, res)) return;
13472
+ json(res, 200, { branch: branch() });
13473
+ return;
13474
+ }
13475
+ if (req.method === "GET" && url.pathname.startsWith("/events/")) {
13476
+ if (!authorized(req, res)) return;
13477
+ const rawId = decodeURIComponent(url.pathname.slice("/events/".length));
13478
+ const rawFrom = url.searchParams.get("fromSequence");
13479
+ const fromSequence = rawFrom === null ? NaN : Number(rawFrom);
13480
+ if (!rawId || !Number.isInteger(fromSequence) || fromSequence < 1) {
13481
+ json(res, 400, { error: "resourceId path segment and integer fromSequence >= 1 are required" });
13482
+ return;
13483
+ }
13484
+ events.queryEvents(resourceId(rawId), { fromSequence }).then((replay) => json(res, 200, { events: replay })).catch((error) => {
13485
+ logger.error("D1 read path failed", { resourceId: rawId, fromSequence, error: errField(error) });
13486
+ json(res, 500, { error: "event read failed" });
13487
+ });
13488
+ return;
13489
+ }
13490
+ const contentMatch = req.method === "GET" && /^\/resources\/(.+)\/content$/.exec(url.pathname);
13491
+ if (contentMatch) {
13492
+ if (!authorized(req, res)) return;
13493
+ const rid = decodeURIComponent(contentMatch[1]);
13494
+ resolveRepresentation({ views, content }, resourceId(rid)).then(async ({ stream, mediaType }) => {
13495
+ res.writeHead(200, { "Content-Type": mediaType });
13496
+ await pipeline(stream, res);
13497
+ }).catch((error) => {
13498
+ if (error instanceof RepresentationMissing) {
13499
+ json(res, 404, { error: error.message, reason: error.reason });
13500
+ return;
13501
+ }
13502
+ logger.error("Content read failed", { resourceId: rid, error: errField(error) });
13503
+ if (res.headersSent) res.destroy();
13504
+ else json(res, 500, { error: "content read failed" });
13505
+ });
13506
+ return;
13507
+ }
13508
+ if (req.method === "PUT" && url.pathname.startsWith("/content/")) {
13509
+ if (!authorized(req, res)) return;
13510
+ const storageUri = decodeURIComponent(url.pathname.slice("/content/".length));
13511
+ if (!storageUri) {
13512
+ json(res, 400, { error: "storageUri path segment is required" });
13513
+ return;
13514
+ }
13515
+ const expectedChecksum = url.searchParams.get("checksum");
13516
+ content.store(req, storageUri, {
13517
+ noGit: true,
13518
+ ...expectedChecksum !== null ? { expectedChecksum } : {}
13519
+ }).then((stored) => json(res, 200, stored)).catch((error) => {
13520
+ if (error instanceof ChecksumMismatchError) {
13521
+ json(res, 409, { error: "checksum mismatch: body does not match the checksum the caller supplied" });
13522
+ return;
13523
+ }
13524
+ logger.error("Content write failed", { storageUri, error: errField(error) });
13525
+ json(res, 500, { error: "content write failed" });
13526
+ });
13527
+ return;
13528
+ }
13529
+ res.writeHead(404);
13530
+ res.end();
13531
+ });
13532
+ }
13154
13533
 
13155
13534
  // src/local-transport.ts
13156
13535
  var import_rxjs8 = __toESM(require_cjs());
@@ -13232,7 +13611,7 @@ var LocalTransport = class {
13232
13611
  }
13233
13612
  }
13234
13613
  // LocalTransport implements `ITransport` only. It does not implement
13235
- // `IBackendOperations` — a SemiontClient built over LocalTransport has
13614
+ // `IGatewayOperations` — a SemiontClient built over LocalTransport has
13236
13615
  // no `client.auth` / `client.admin` namespaces by design (no
13237
13616
  // credentials, no admin routes, no exchange machinery in-process).
13238
13617
  // ── Lifecycle ───────────────────────────────────────────────────────────
@@ -13248,10 +13627,10 @@ var LocalTransport = class {
13248
13627
  }
13249
13628
  };
13250
13629
  var LocalContentTransport = class {
13251
- constructor(ks) {
13252
- this.ks = ks;
13630
+ constructor(kb) {
13631
+ this.kb = kb;
13253
13632
  }
13254
- ks;
13633
+ kb;
13255
13634
  async putBinary(_request, _options) {
13256
13635
  throw new Error(
13257
13636
  "LocalContentTransport does not support putBinary() \u2014 create resources via bus emits (mark/yield namespaces) in local mode"
@@ -13266,12 +13645,12 @@ var LocalContentTransport = class {
13266
13645
  */
13267
13646
  async putAnchoredText(checksum, outcome, _options) {
13268
13647
  busLog("PUT", "anchored-text", { checksum });
13269
- await this.ks.kb.anchoredText.write(checksum, outcome);
13648
+ await this.kb.anchoredText.write(checksum, outcome);
13270
13649
  }
13271
13650
  /** The stored outcome, or null when nothing has derived one — the common case. */
13272
13651
  async getAnchoredText(resourceId10, _options) {
13273
13652
  busLog("GET", "anchored-text", { resourceId: resourceId10 });
13274
- return readAnchoredText(this.ks.kb, resourceId10);
13653
+ return readAnchoredText(this.kb, resourceId10);
13275
13654
  }
13276
13655
  /**
13277
13656
  * The cache-consult read (PERSIST-ANCHORS P2c), straight from the store —
@@ -13280,7 +13659,7 @@ var LocalContentTransport = class {
13280
13659
  */
13281
13660
  async getAnchoredTextByChecksum(checksum, _options) {
13282
13661
  busLog("GET", "anchored-text-by-checksum", { checksum });
13283
- return this.ks.kb.anchoredText.read(checksum);
13662
+ return this.kb.anchoredText.read(checksum);
13284
13663
  }
13285
13664
  /**
13286
13665
  * The store's would-hit keys, straight from the store — planning data for
@@ -13289,7 +13668,7 @@ var LocalContentTransport = class {
13289
13668
  */
13290
13669
  async listAnchoredTextKeys(_options) {
13291
13670
  busLog("GET", "anchored-text-keys", {});
13292
- return this.ks.kb.anchoredText.list();
13671
+ return this.kb.anchoredText.list();
13293
13672
  }
13294
13673
  async getBinary(resourceId10, _options) {
13295
13674
  busLog("GET", "content", { resourceId: resourceId10 });
@@ -13320,16 +13699,14 @@ var LocalContentTransport = class {
13320
13699
  }
13321
13700
  );
13322
13701
  }
13323
- async loadBinary(resourceId10) {
13324
- const view = await this.ks.kb.views.get(resourceId10);
13325
- if (!view) throw new Error(`Resource not found: ${resourceId10}`);
13326
- const rep = getPrimaryRepresentation(view.resource);
13327
- if (!rep?.storageUri) {
13328
- throw new Error(`Resource ${resourceId10} has no representation with a storageUri`);
13329
- }
13330
- const buf = await this.ks.kb.content.retrieve(rep.storageUri);
13331
- const data = buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength);
13332
- return { data, contentType: rep.mediaType };
13702
+ /**
13703
+ * The same resolution the wire path serves, buffered — local and hosted
13704
+ * modes must answer identically, which they did not before
13705
+ * SINGLE-KB-MOUNT P3: this resolved through `representations[].storageUri`,
13706
+ * a field `ViewMaterializer` never writes, so every binary read here threw.
13707
+ */
13708
+ loadBinary(resourceId10) {
13709
+ return workingTreeContentReads(this.kb.views, this.kb.content).getBinary(resourceId10);
13333
13710
  }
13334
13711
  /**
13335
13712
  * Assemble the resource's JSON-LD graph in-process from the KB — the local
@@ -13337,7 +13714,7 @@ var LocalContentTransport = class {
13337
13714
  * getBinary; SIMPLER-JSON-LD.md decision 7). Local mode has no auth.
13338
13715
  */
13339
13716
  async getResourceGraph(resourceId10, _options) {
13340
- const graph = await assembleResourceGraph(this.ks.kb, resourceId10);
13717
+ const graph = await assembleResourceGraph(this.kb, resourceId10);
13341
13718
  if (!graph) throw new Error(`Resource not found: ${resourceId10}`);
13342
13719
  return graph;
13343
13720
  }
@@ -13382,23 +13759,24 @@ function isWorkItem(input) {
13382
13759
  return WORK_ITEM_TYPES.has(input.type);
13383
13760
  }
13384
13761
  var Smelter = class _Smelter {
13385
- constructor(events$, rebuildAnchors$, vectorStore, embeddingProvider, content, bus, chunkingConfig, timing, logger) {
13762
+ constructor(events$, rebuildAnchors$, vectorStore, embeddingProvider, content, anchoredStore, bus, chunkingConfig, timing, logger) {
13386
13763
  this.events$ = events$;
13387
13764
  this.rebuildAnchors$ = rebuildAnchors$;
13388
13765
  this.vectorStore = vectorStore;
13389
13766
  this.embeddingProvider = embeddingProvider;
13390
13767
  this.content = content;
13768
+ this.anchoredStore = anchoredStore;
13391
13769
  this.bus = bus;
13392
13770
  this.chunkingConfig = chunkingConfig;
13393
13771
  this.timing = timing;
13394
13772
  this.logger = logger;
13395
- this.anchoredStore = anchoredTextStoreOverTransport(content, logger.child({ component: "anchored-text-cache" }));
13396
13773
  }
13397
13774
  events$;
13398
13775
  rebuildAnchors$;
13399
13776
  vectorStore;
13400
13777
  embeddingProvider;
13401
13778
  content;
13779
+ anchoredStore;
13402
13780
  bus;
13403
13781
  chunkingConfig;
13404
13782
  timing;
@@ -13422,7 +13800,6 @@ var Smelter = class _Smelter {
13422
13800
  * OCR pass.
13423
13801
  */
13424
13802
  drainChain = Promise.resolve();
13425
- anchoredStore;
13426
13803
  get eventsProcessed() {
13427
13804
  return this._eventsProcessed;
13428
13805
  }
@@ -13652,7 +14029,7 @@ var Smelter = class _Smelter {
13652
14029
  });
13653
14030
  return;
13654
14031
  }
13655
- await this.content.putAnchoredText(checksum, { ...extracted, items: extracted.items });
14032
+ await this.anchoredStore.write(checksum, { ...extracted, items: extracted.items });
13656
14033
  this.logger.info("Re-anchored resource", { resourceId: rid, checksum, items: extracted.items.length });
13657
14034
  }
13658
14035
  async handleResourcePurge(event) {
@@ -13939,7 +14316,7 @@ var Smelter = class _Smelter {
13939
14316
  // per resource (PERSIST-ANCHORS P0). Keys are resource ids today;
13940
14317
  // P1 rekeys the store by content checksum and this lookup moves
13941
14318
  // with it.
13942
- this.content.listAnchoredTextKeys().then((keys) => new Set(keys))
14319
+ this.anchoredStore.list().then((keys) => new Set(keys))
13943
14320
  ]);
13944
14321
  const resources = await this.listAllResources();
13945
14322
  this.logger.info("Reconcile started", {
@@ -14176,864 +14553,7 @@ function createSmelterActorStateUnit(options) {
14176
14553
  }
14177
14554
  };
14178
14555
  }
14179
- var pipelineAsync = promisify(pipeline);
14180
- var BLOCK_SIZE = 512;
14181
- function createTarHeader(name, size) {
14182
- const header = Buffer.alloc(BLOCK_SIZE, 0);
14183
- header.write(name.slice(0, 100), 0, 100, "utf8");
14184
- header.write("0000644\0", 100, 8, "utf8");
14185
- header.write("0000000\0", 108, 8, "utf8");
14186
- header.write("0000000\0", 116, 8, "utf8");
14187
- header.write(size.toString(8).padStart(11, "0") + "\0", 124, 12, "utf8");
14188
- const mtime = Math.floor(Date.now() / 1e3);
14189
- header.write(mtime.toString(8).padStart(11, "0") + "\0", 136, 12, "utf8");
14190
- header.write(" ", 148, 8, "utf8");
14191
- header.write("0", 156, 1, "utf8");
14192
- header.write("ustar\0", 257, 6, "utf8");
14193
- header.write("00", 263, 2, "utf8");
14194
- let checksum = 0;
14195
- for (let i = 0; i < BLOCK_SIZE; i++) {
14196
- checksum += header[i];
14197
- }
14198
- header.write(checksum.toString(8).padStart(6, "0") + "\0 ", 148, 8, "utf8");
14199
- return header;
14200
- }
14201
- function paddingBytes(size) {
14202
- const remainder = size % BLOCK_SIZE;
14203
- return remainder === 0 ? 0 : BLOCK_SIZE - remainder;
14204
- }
14205
- async function writeTarGz(entries, output) {
14206
- const gzip = createGzip();
14207
- const tarStream = new Readable({ read() {
14208
- } });
14209
- const pipePromise = pipelineAsync(tarStream, gzip, output);
14210
- for await (const entry of entries) {
14211
- const header = createTarHeader(entry.name, entry.data.length);
14212
- tarStream.push(header);
14213
- tarStream.push(entry.data);
14214
- const pad = paddingBytes(entry.data.length);
14215
- if (pad > 0) {
14216
- tarStream.push(Buffer.alloc(pad, 0));
14217
- }
14218
- }
14219
- tarStream.push(Buffer.alloc(BLOCK_SIZE * 2, 0));
14220
- tarStream.push(null);
14221
- await pipePromise;
14222
- }
14223
- async function decompressStream(input) {
14224
- const gunzip = createGunzip();
14225
- const chunks = [];
14226
- return new Promise((resolve2, reject) => {
14227
- gunzip.on("data", (chunk) => chunks.push(chunk));
14228
- gunzip.on("end", () => resolve2(Buffer.concat(chunks)));
14229
- gunzip.on("error", reject);
14230
- input.on("error", reject);
14231
- input.pipe(gunzip);
14232
- });
14233
- }
14234
- function* parseTarEntries(decompressed) {
14235
- let offset = 0;
14236
- while (offset + BLOCK_SIZE <= decompressed.length) {
14237
- const header = decompressed.subarray(offset, offset + BLOCK_SIZE);
14238
- if (header.every((b) => b === 0)) break;
14239
- const nameEnd = header.indexOf(0, 0);
14240
- const name = header.subarray(0, Math.min(nameEnd, 100)).toString("utf8");
14241
- const sizeStr = header.subarray(124, 135).toString("utf8").trim();
14242
- const size = parseInt(sizeStr, 8);
14243
- offset += BLOCK_SIZE;
14244
- const data = decompressed.subarray(offset, offset + size);
14245
- offset += size;
14246
- offset += paddingBytes(size);
14247
- yield { name, size, data };
14248
- }
14249
- }
14250
- async function* readTarGz(input) {
14251
- const decompressed = await decompressStream(input);
14252
- yield* parseTarEntries(decompressed);
14253
- }
14254
-
14255
- // src/exchange/manifest.ts
14256
- var BACKUP_FORMAT = "semiont-backup";
14257
- var FORMAT_VERSION = 1;
14258
- var LINKED_DATA_FORMAT = "semiont-linked-data";
14259
- function isLinkedDataManifest(obj) {
14260
- return typeof obj === "object" && obj !== null && obj["semiont:format"] === LINKED_DATA_FORMAT;
14261
- }
14262
- function isBackupManifest(obj) {
14263
- return typeof obj === "object" && obj !== null && obj.format === BACKUP_FORMAT;
14264
- }
14265
- function validateManifestVersion(version) {
14266
- if (version > FORMAT_VERSION) {
14267
- throw new Error(
14268
- `Unsupported format version ${version}. This tool supports version ${FORMAT_VERSION}.`
14269
- );
14270
- }
14271
- }
14272
-
14273
- // src/exchange/backup-exporter.ts
14274
- var SYSTEM_STREAM = "__system__";
14275
- async function exportBackup(options, output) {
14276
- const { eventStore, content, sourceUrl, logger } = options;
14277
- const resourceIds = await eventStore.log.storage.getAllResourceIds();
14278
- logger?.info("Backup export: enumerating streams", { resourceCount: resourceIds.length });
14279
- const allStreamIds = [SYSTEM_STREAM, ...resourceIds];
14280
- const streamData = /* @__PURE__ */ new Map();
14281
- let totalEvents = 0;
14282
- for (const id of allStreamIds) {
14283
- const events = await eventStore.log.getEvents(id);
14284
- if (events.length > 0) {
14285
- streamData.set(id, events);
14286
- totalEvents += events.length;
14287
- }
14288
- }
14289
- const contentRefs = collectContentRefs(streamData);
14290
- logger?.info("Backup export: collected content refs", {
14291
- streams: streamData.size,
14292
- events: totalEvents,
14293
- blobs: contentRefs.size
14294
- });
14295
- const contentBlobs = /* @__PURE__ */ new Map();
14296
- let totalContentBytes = 0;
14297
- for (const [storageUri, mediaType] of contentRefs) {
14298
- const data = await content.retrieve(storageUri);
14299
- const ext = extensionForMediaType(mediaType);
14300
- contentBlobs.set(storageUri, { data, ext });
14301
- totalContentBytes += data.length;
14302
- }
14303
- const streamSummaries = [];
14304
- for (const [streamId, events] of streamData) {
14305
- streamSummaries.push({
14306
- stream: streamId,
14307
- eventCount: events.length
14308
- });
14309
- }
14310
- const manifestHeader = {
14311
- format: BACKUP_FORMAT,
14312
- version: FORMAT_VERSION,
14313
- exportedAt: (/* @__PURE__ */ new Date()).toISOString(),
14314
- sourceUrl,
14315
- stats: {
14316
- streams: streamData.size,
14317
- events: totalEvents,
14318
- blobs: contentBlobs.size,
14319
- contentBytes: totalContentBytes
14320
- }
14321
- };
14322
- async function* generateEntries() {
14323
- const manifestLines = [
14324
- JSON.stringify(manifestHeader),
14325
- ...streamSummaries.map((s) => JSON.stringify(s))
14326
- ].join("\n") + "\n";
14327
- yield { name: ".semiont/manifest.jsonl", data: Buffer.from(manifestLines, "utf8") };
14328
- for (const [streamId, events] of streamData) {
14329
- const fileName = streamId === SYSTEM_STREAM ? ".semiont/events/__system__.jsonl" : `.semiont/events/${streamId}.jsonl`;
14330
- const jsonl = events.map((e) => JSON.stringify(e)).join("\n") + "\n";
14331
- yield { name: fileName, data: Buffer.from(jsonl, "utf8") };
14332
- }
14333
- for (const [storageUri, { data, ext }] of contentBlobs) {
14334
- const blobPath = storageUri.startsWith("file://") ? storageUri.slice(7) : storageUri;
14335
- yield { name: `${blobPath}${ext}`, data };
14336
- }
14337
- }
14338
- await writeTarGz(generateEntries(), output);
14339
- logger?.info("Backup export complete", {
14340
- streams: streamData.size,
14341
- events: totalEvents,
14342
- blobs: contentBlobs.size,
14343
- contentBytes: totalContentBytes
14344
- });
14345
- return manifestHeader;
14346
- }
14347
- function collectContentRefs(streamData) {
14348
- const refs = /* @__PURE__ */ new Map();
14349
- for (const [, events] of streamData) {
14350
- for (const stored of events) {
14351
- if (stored.type === "yield:created") {
14352
- const payload = stored.payload;
14353
- if (payload.storageUri && payload.format) {
14354
- refs.set(payload.storageUri, payload.format);
14355
- }
14356
- }
14357
- }
14358
- }
14359
- return refs;
14360
- }
14361
-
14362
- // src/exchange/await-reply.ts
14363
- var import_rxjs11 = __toESM(require_cjs());
14364
- var import_operators9 = __toESM(require_operators());
14365
- function awaitReply(eventBus, command, payload, okChannel, failedChannel, timeoutMs) {
14366
- const result$ = (0, import_rxjs11.race)(
14367
- eventBus.get(okChannel).pipe((0, import_operators9.map)(() => void 0)),
14368
- eventBus.get(failedChannel).pipe(
14369
- (0, import_operators9.map)((e) => {
14370
- throw new Error(e.message ?? `${command} failed`);
14371
- })
14372
- ),
14373
- (0, import_rxjs11.timer)(timeoutMs).pipe(
14374
- (0, import_operators9.map)(() => {
14375
- throw new Error(`Timeout waiting for ${okChannel}`);
14376
- })
14377
- )
14378
- );
14379
- eventBus.get(command).next(payload);
14380
- return (0, import_rxjs11.firstValueFrom)(result$);
14381
- }
14382
- var REPLAY_TIMEOUT_MS = 3e4;
14383
- async function replayEventStream(jsonl, eventBus, eventStore, resolveBlob, contentStore, logger) {
14384
- const lines = jsonl.trim().split("\n").filter((l) => l.length > 0);
14385
- const storedEvents = lines.map((line) => JSON.parse(line));
14386
- const stats = {
14387
- eventsReplayed: 0,
14388
- resourcesCreated: 0,
14389
- annotationsCreated: 0,
14390
- entityTypesAdded: 0
14391
- };
14392
- for (const stored of storedEvents) {
14393
- await replayEvent(stored, eventBus, eventStore, resolveBlob, contentStore, stats, logger);
14394
- stats.eventsReplayed++;
14395
- }
14396
- return { stats };
14397
- }
14398
- async function replayEvent(event, eventBus, eventStore, resolveBlob, contentStore, stats, logger) {
14399
- switch (event.type) {
14400
- case "frame:entity-type-added":
14401
- await replayEntityTypeAdded(event, eventBus, logger);
14402
- stats.entityTypesAdded++;
14403
- break;
14404
- case "yield:created":
14405
- await replayResourceCreated(event, eventBus, resolveBlob, contentStore, logger);
14406
- stats.resourcesCreated++;
14407
- break;
14408
- case "mark:added":
14409
- await replayAnnotationAdded(event, eventBus, logger);
14410
- stats.annotationsCreated++;
14411
- break;
14412
- case "mark:body-updated":
14413
- await replayAnnotationBodyUpdated(event, eventBus, logger);
14414
- break;
14415
- case "mark:removed":
14416
- await replayAnnotationRemoved(event, eventBus, logger);
14417
- break;
14418
- case "mark:archived":
14419
- await replayResourceArchived(event, eventBus, logger);
14420
- break;
14421
- case "mark:unarchived":
14422
- await replayResourceUnarchived(event, eventBus, logger);
14423
- break;
14424
- case "mark:entity-tag-added":
14425
- case "mark:entity-tag-removed":
14426
- await replayEntityTagChange(event, eventStore, logger);
14427
- break;
14428
- // Job events are transient — skip during replay
14429
- case "job:started":
14430
- case "job:completed":
14431
- case "job:failed":
14432
- logger?.debug("Skipping job event during replay", { type: event.type });
14433
- break;
14434
- // Representation events — content is already stored via yield:created replay
14435
- case "yield:representation-added":
14436
- case "yield:representation-removed":
14437
- logger?.debug("Skipping representation event during replay", { type: event.type });
14438
- break;
14439
- default:
14440
- logger?.warn("Unknown event type during replay", { type: event.type });
14441
- }
14442
- }
14443
- async function replayEntityTypeAdded(event, eventBus, logger) {
14444
- await busRequest(
14445
- asBusRequestPrimitive(eventBus),
14446
- "frame:add-entity-type",
14447
- { tag: event.payload.entityType, _userId: event.userId },
14448
- REPLAY_TIMEOUT_MS
14449
- );
14450
- logger?.debug("Replayed entitytype.added", { entityType: event.payload.entityType });
14451
- }
14452
- async function replayResourceCreated(event, eventBus, resolveBlob, contentStore, logger) {
14453
- const { payload } = event;
14454
- const blob = resolveBlob(payload.contentChecksum);
14455
- if (!blob) {
14456
- throw new Error(`Missing content blob for checksum ${payload.contentChecksum}`);
14457
- }
14458
- const base = baseMediaType(payload.format);
14459
- const resolvedUri = payload.storageUri || deriveStorageUri(payload.name, isSupportedMediaType(base) ? base : "application/octet-stream");
14460
- const stored = await contentStore.store(blob, resolvedUri);
14461
- await busRequest(
14462
- asBusRequestPrimitive(eventBus),
14463
- "yield:create",
14464
- {
14465
- name: payload.name,
14466
- storageUri: resolvedUri,
14467
- contentChecksum: stored.checksum,
14468
- byteSize: stored.byteSize,
14469
- format: payload.format,
14470
- _userId: event.userId,
14471
- language: payload.language,
14472
- entityTypes: payload.entityTypes,
14473
- isDraft: payload.isDraft,
14474
- generatedFrom: payload.generatedFrom,
14475
- generationPrompt: payload.generationPrompt
14476
- },
14477
- REPLAY_TIMEOUT_MS
14478
- );
14479
- logger?.debug("Replayed resource.created", { name: payload.name });
14480
- }
14481
- async function replayAnnotationAdded(event, eventBus, logger) {
14482
- await awaitReply(
14483
- eventBus,
14484
- "mark:create",
14485
- {
14486
- annotation: event.payload.annotation,
14487
- _userId: event.userId,
14488
- resourceId: event.resourceId
14489
- },
14490
- "mark:create-ok",
14491
- "mark:create-failed",
14492
- REPLAY_TIMEOUT_MS
14493
- );
14494
- logger?.debug("Replayed annotation.added", { annotationId: event.payload.annotation.id });
14495
- }
14496
- async function replayAnnotationBodyUpdated(event, eventBus, logger) {
14497
- await awaitReply(
14498
- eventBus,
14499
- "mark:update-body",
14500
- {
14501
- annotationId: event.payload.annotationId,
14502
- _userId: event.userId,
14503
- resourceId: event.resourceId,
14504
- operations: event.payload.operations
14505
- },
14506
- "mark:body-updated",
14507
- "mark:body-update-failed",
14508
- REPLAY_TIMEOUT_MS
14509
- );
14510
- logger?.debug("Replayed annotation.body.updated", { annotationId: event.payload.annotationId });
14511
- }
14512
- async function replayAnnotationRemoved(event, eventBus, logger) {
14513
- await awaitReply(
14514
- eventBus,
14515
- "mark:delete",
14516
- {
14517
- annotationId: event.payload.annotationId,
14518
- _userId: event.userId,
14519
- resourceId: event.resourceId
14520
- },
14521
- "mark:delete-ok",
14522
- "mark:delete-failed",
14523
- REPLAY_TIMEOUT_MS
14524
- );
14525
- logger?.debug("Replayed annotation.removed", { annotationId: event.payload.annotationId });
14526
- }
14527
- async function replayResourceArchived(event, eventBus, logger) {
14528
- eventBus.get("mark:archive").next({
14529
- _userId: event.userId,
14530
- resourceId: event.resourceId
14531
- });
14532
- logger?.debug("Replayed resource.archived", { resourceId: event.resourceId });
14533
- }
14534
- async function replayResourceUnarchived(event, eventBus, logger) {
14535
- eventBus.get("mark:unarchive").next({
14536
- _userId: event.userId,
14537
- resourceId: event.resourceId
14538
- });
14539
- logger?.debug("Replayed resource.unarchived", { resourceId: event.resourceId });
14540
- }
14541
- async function replayEntityTagChange(event, eventStore, logger) {
14542
- const base = {
14543
- resourceId: event.resourceId,
14544
- userId: event.userId,
14545
- version: event.version
14546
- };
14547
- if (event.type === "mark:entity-tag-added") {
14548
- await eventStore.appendEvent({ ...base, type: "mark:entity-tag-added", payload: event.payload });
14549
- } else {
14550
- await eventStore.appendEvent({ ...base, type: "mark:entity-tag-removed", payload: event.payload });
14551
- }
14552
- logger?.debug("Replayed entity tag change", { type: event.type, entityType: event.payload.entityType });
14553
- }
14554
-
14555
- // src/exchange/backup-importer.ts
14556
- function buildBlobResolver(entries) {
14557
- const checksumIndex = /* @__PURE__ */ new Map();
14558
- for (const name of entries.keys()) {
14559
- if (!name.startsWith(".semiont/")) {
14560
- const dotIndex = name.lastIndexOf(".");
14561
- const checksum = dotIndex >= 0 ? name.slice(0, dotIndex) : name;
14562
- checksumIndex.set(checksum, name);
14563
- }
14564
- }
14565
- return (checksum) => {
14566
- const entryName = checksumIndex.get(checksum);
14567
- return entryName ? entries.get(entryName) : void 0;
14568
- };
14569
- }
14570
- async function importBackup(archive, options) {
14571
- const { eventBus, eventStore, contentStore, logger } = options;
14572
- const entries = /* @__PURE__ */ new Map();
14573
- for await (const entry of readTarGz(archive)) {
14574
- entries.set(entry.name, entry.data);
14575
- }
14576
- const manifestData = entries.get(".semiont/manifest.jsonl");
14577
- if (!manifestData) {
14578
- throw new Error("Invalid backup: missing .semiont/manifest.jsonl");
14579
- }
14580
- const manifestLines = manifestData.toString("utf8").trim().split("\n");
14581
- const header = JSON.parse(manifestLines[0]);
14582
- if (!isBackupManifest(header)) {
14583
- throw new Error(`Invalid backup: expected format "${BACKUP_FORMAT}", got "${header.format}"`);
14584
- }
14585
- validateManifestVersion(header.version);
14586
- const streamSummaries = manifestLines.slice(1).map((line) => JSON.parse(line));
14587
- logger?.info("Backup import: parsed manifest", {
14588
- streams: header.stats.streams,
14589
- events: header.stats.events,
14590
- blobs: header.stats.blobs
14591
- });
14592
- const resolveBlob = buildBlobResolver(entries);
14593
- const systemData = entries.get(".semiont/events/__system__.jsonl");
14594
- let stats = { eventsReplayed: 0, resourcesCreated: 0, annotationsCreated: 0, entityTypesAdded: 0 };
14595
- if (systemData) {
14596
- const result = await replayEventStream(
14597
- systemData.toString("utf8"),
14598
- eventBus,
14599
- eventStore,
14600
- resolveBlob,
14601
- contentStore,
14602
- logger
14603
- );
14604
- stats = mergeStats(stats, result.stats);
14605
- }
14606
- for (const summary of streamSummaries) {
14607
- if (summary.stream === "__system__") continue;
14608
- const eventData = entries.get(`.semiont/events/${summary.stream}.jsonl`);
14609
- if (!eventData) {
14610
- logger?.warn("Backup import: missing event stream", { stream: summary.stream });
14611
- continue;
14612
- }
14613
- const result = await replayEventStream(
14614
- eventData.toString("utf8"),
14615
- eventBus,
14616
- eventStore,
14617
- resolveBlob,
14618
- contentStore,
14619
- logger
14620
- );
14621
- stats = mergeStats(stats, result.stats);
14622
- }
14623
- logger?.info("Backup import complete", { ...stats });
14624
- return { manifest: header, stats };
14625
- }
14626
- function mergeStats(a, b) {
14627
- return {
14628
- eventsReplayed: a.eventsReplayed + b.eventsReplayed,
14629
- resourcesCreated: a.resourcesCreated + b.resourcesCreated,
14630
- annotationsCreated: a.annotationsCreated + b.annotationsCreated,
14631
- entityTypesAdded: a.entityTypesAdded + b.entityTypesAdded
14632
- };
14633
- }
14634
- var SEMIONT_CONTEXT = [
14635
- "https://schema.org/",
14636
- "http://www.w3.org/ns/anno.jsonld",
14637
- {
14638
- "semiont": "https://semiont.org/vocab/",
14639
- "entityTypes": "semiont:entityTypes",
14640
- "archived": "semiont:archived",
14641
- "representations": { "@id": "semiont:representations", "@container": "@set" },
14642
- "annotations": { "@id": "semiont:annotations", "@container": "@set" }
14643
- }
14644
- ];
14645
- var MANIFEST_CONTEXT = {
14646
- "semiont": "https://semiont.org/vocab/",
14647
- "schema": "https://schema.org/",
14648
- "dct": "http://purl.org/dc/terms/",
14649
- "prov": "http://www.w3.org/ns/prov#",
14650
- "void": "http://rdfs.org/ns/void#"
14651
- };
14652
- function hydrateAnnotation(annotation, baseUrl) {
14653
- const hydrated = { ...annotation };
14654
- if (hydrated.id && !hydrated.id.startsWith("http")) {
14655
- hydrated.id = `${baseUrl}/annotations/${hydrated.id}`;
14656
- }
14657
- if (typeof hydrated.target === "string") {
14658
- if (!hydrated.target.startsWith("http")) {
14659
- hydrated.target = `${baseUrl}/resources/${hydrated.target}`;
14660
- }
14661
- } else if (hydrated.target && typeof hydrated.target === "object") {
14662
- const target = { ...hydrated.target };
14663
- if (target.source && !target.source.startsWith("http")) {
14664
- target.source = `${baseUrl}/resources/${target.source}`;
14665
- }
14666
- hydrated.target = target;
14667
- }
14668
- hydrated.body = hydrateBody(hydrated.body, baseUrl);
14669
- return hydrated;
14670
- }
14671
- function hydrateBody(body, baseUrl) {
14672
- if (Array.isArray(body)) {
14673
- return body.map((b) => hydrateBodyItem(b, baseUrl));
14674
- }
14675
- return hydrateBodyItem(body, baseUrl);
14676
- }
14677
- function hydrateBodyItem(item, baseUrl) {
14678
- if (item && typeof item === "object" && "source" in item) {
14679
- const source = item.source;
14680
- if (typeof source === "string" && !source.startsWith("http")) {
14681
- return { ...item, source: `${baseUrl}/resources/${source}` };
14682
- }
14683
- }
14684
- return item;
14685
- }
14686
- async function exportLinkedData(options, output) {
14687
- const { views, content, sourceUrl, entityTypes, includeArchived, logger } = options;
14688
- const allViews = await views.getAll();
14689
- const resourceViews = includeArchived ? allViews : allViews.filter((v) => !v.resource.archived);
14690
- logger?.info("Linked data export: enumerating resources", { count: resourceViews.length });
14691
- const contentRefs = /* @__PURE__ */ new Map();
14692
- for (const view of resourceViews) {
14693
- collectContentRefsFromResource(view.resource, contentRefs);
14694
- }
14695
- const contentBlobs = /* @__PURE__ */ new Map();
14696
- for (const [storageUri, mediaType] of contentRefs) {
14697
- try {
14698
- const data = await content.retrieve(storageUri);
14699
- const ext = extensionForMediaType(mediaType);
14700
- contentBlobs.set(storageUri, { data, ext });
14701
- } catch (err) {
14702
- logger?.warn("Failed to retrieve content blob", { storageUri, error: String(err) });
14703
- }
14704
- }
14705
- const manifest = {
14706
- "@context": MANIFEST_CONTEXT,
14707
- "@type": "void:Dataset",
14708
- "semiont:format": LINKED_DATA_FORMAT,
14709
- "semiont:version": FORMAT_VERSION,
14710
- "dct:created": (/* @__PURE__ */ new Date()).toISOString(),
14711
- "prov:wasGeneratedBy": {
14712
- "@type": "prov:Activity",
14713
- "prov:used": sourceUrl
14714
- },
14715
- "semiont:entityTypes": entityTypes,
14716
- "void:entities": resourceViews.length
14717
- };
14718
- async function* generateEntries() {
14719
- yield {
14720
- name: ".semiont/manifest.jsonld",
14721
- data: Buffer.from(JSON.stringify(manifest, null, 2), "utf8")
14722
- };
14723
- for (const view of resourceViews) {
14724
- const resourceId10 = view.resource["@id"];
14725
- const jsonld = buildResourceJsonLd(view.resource, view.annotations.annotations, sourceUrl);
14726
- yield {
14727
- name: `.semiont/resources/${resourceId10}.jsonld`,
14728
- data: Buffer.from(JSON.stringify(jsonld, null, 2), "utf8")
14729
- };
14730
- }
14731
- for (const [storageUri, { data, ext }] of contentBlobs) {
14732
- const blobPath = storageUri.startsWith("file://") ? storageUri.slice(7) : storageUri;
14733
- yield { name: `${blobPath}${ext}`, data };
14734
- }
14735
- }
14736
- await writeTarGz(generateEntries(), output);
14737
- logger?.info("Linked data export complete", {
14738
- resources: resourceViews.length,
14739
- blobs: contentBlobs.size
14740
- });
14741
- return manifest;
14742
- }
14743
- function buildResourceJsonLd(resource, annotations, sourceUrl) {
14744
- const resourceId10 = resource["@id"];
14745
- const resourceUri = resourceId10.startsWith("http") ? resourceId10 : `${sourceUrl}/resources/${resourceId10}`;
14746
- const doc = {
14747
- "@context": SEMIONT_CONTEXT,
14748
- "@id": resourceUri,
14749
- "@type": resource["@type"] ?? "DigitalDocument",
14750
- "name": resource.name
14751
- };
14752
- if (resource.dateCreated) doc["dateCreated"] = resource.dateCreated;
14753
- if (resource.dateModified) doc["dateModified"] = resource.dateModified;
14754
- if (resource.description) doc["description"] = resource.description;
14755
- const reps = normalizeRepresentations(resource.representations);
14756
- if (reps.length > 0) {
14757
- const primary = reps[0];
14758
- if (primary.language) doc["inLanguage"] = primary.language;
14759
- if (primary.mediaType) doc["encodingFormat"] = primary.mediaType;
14760
- }
14761
- if (resource.entityTypes && resource.entityTypes.length > 0) doc["entityTypes"] = resource.entityTypes;
14762
- if (resource.archived) doc["archived"] = resource.archived;
14763
- if (resource.wasDerivedFrom) doc["wasDerivedFrom"] = resource.wasDerivedFrom;
14764
- if (resource.wasAttributedTo) doc["wasAttributedTo"] = resource.wasAttributedTo;
14765
- if (resource.sameAs && resource.sameAs.length > 0) doc["sameAs"] = resource.sameAs;
14766
- if (resource.isPartOf && resource.isPartOf.length > 0) doc["isPartOf"] = resource.isPartOf;
14767
- if (resource.hasPart && resource.hasPart.length > 0) doc["hasPart"] = resource.hasPart;
14768
- if (reps.length > 0) {
14769
- doc["representations"] = reps.map((rep) => {
14770
- const mediaObj = {
14771
- "@type": "schema:MediaObject",
14772
- "encodingFormat": rep.mediaType
14773
- };
14774
- if (rep.byteSize !== void 0) mediaObj["contentSize"] = rep.byteSize;
14775
- if (rep.checksum) {
14776
- const rawChecksum = rep.checksum.startsWith("sha256:") ? rep.checksum.slice(7) : rep.checksum;
14777
- mediaObj["sha256"] = rawChecksum;
14778
- const ext = extensionForMediaType(rep.mediaType);
14779
- mediaObj["name"] = `${rawChecksum}${ext}`;
14780
- }
14781
- if (rep.language) mediaObj["inLanguage"] = rep.language;
14782
- return mediaObj;
14783
- });
14784
- }
14785
- if (annotations.length > 0) {
14786
- doc["annotations"] = annotations.map((ann) => hydrateAnnotation(ann, sourceUrl));
14787
- }
14788
- return doc;
14789
- }
14790
- function normalizeRepresentations(reps) {
14791
- if (!reps) return [];
14792
- if (Array.isArray(reps)) return reps;
14793
- return [reps];
14794
- }
14795
- function collectContentRefsFromResource(resource, refs) {
14796
- if (resource.storageUri) {
14797
- const reps = normalizeRepresentations(resource.representations);
14798
- const mediaType = reps[0]?.mediaType ?? "application/octet-stream";
14799
- refs.set(resource.storageUri, mediaType);
14800
- }
14801
- }
14802
- var IMPORT_TIMEOUT_MS = 3e4;
14803
- function stripUriToId(uri) {
14804
- if (!uri.includes("/")) return uri;
14805
- const lastSlash = uri.lastIndexOf("/");
14806
- return uri.slice(lastSlash + 1);
14807
- }
14808
- function dehydrateAnnotation(annotation) {
14809
- const dehydrated = { ...annotation };
14810
- if (dehydrated.id) {
14811
- dehydrated.id = annotationId(stripUriToId(dehydrated.id));
14812
- }
14813
- if (typeof dehydrated.target === "string") {
14814
- dehydrated.target = stripUriToId(dehydrated.target);
14815
- } else if (dehydrated.target && typeof dehydrated.target === "object") {
14816
- const target = { ...dehydrated.target };
14817
- if (target.source) {
14818
- target.source = stripUriToId(target.source);
14819
- }
14820
- dehydrated.target = target;
14821
- }
14822
- dehydrated.body = dehydrateBody(dehydrated.body);
14823
- return dehydrated;
14824
- }
14825
- function dehydrateBody(body) {
14826
- if (Array.isArray(body)) {
14827
- return body.map((b) => dehydrateBodyItem(b));
14828
- }
14829
- return dehydrateBodyItem(body);
14830
- }
14831
- function dehydrateBodyItem(item) {
14832
- if (item && typeof item === "object" && "source" in item) {
14833
- const source = item.source;
14834
- if (typeof source === "string" && source.includes("/")) {
14835
- return { ...item, source: stripUriToId(source) };
14836
- }
14837
- }
14838
- return item;
14839
- }
14840
- function buildBlobResolver2(entries) {
14841
- const checksumIndex = /* @__PURE__ */ new Map();
14842
- for (const name of entries.keys()) {
14843
- if (!name.startsWith(".semiont/")) {
14844
- const dotIndex = name.lastIndexOf(".");
14845
- const checksum = dotIndex >= 0 ? name.slice(0, dotIndex) : name;
14846
- checksumIndex.set(checksum, name);
14847
- }
14848
- }
14849
- return (checksum) => {
14850
- const entryName = checksumIndex.get(checksum);
14851
- return entryName ? entries.get(entryName) : void 0;
14852
- };
14853
- }
14854
- async function importLinkedData(archive, options) {
14855
- const { eventBus, contentStore, userId: userId3, logger } = options;
14856
- const entries = /* @__PURE__ */ new Map();
14857
- for await (const entry of readTarGz(archive)) {
14858
- entries.set(entry.name, entry.data);
14859
- }
14860
- const manifestData = entries.get(".semiont/manifest.jsonld");
14861
- if (!manifestData) {
14862
- throw new Error("Invalid linked data archive: missing .semiont/manifest.jsonld");
14863
- }
14864
- const manifest = JSON.parse(manifestData.toString("utf8"));
14865
- if (!isLinkedDataManifest(manifest)) {
14866
- throw new Error(
14867
- `Invalid linked data archive: expected format "${LINKED_DATA_FORMAT}", got "${manifest["semiont:format"]}"`
14868
- );
14869
- }
14870
- validateManifestVersion(manifest["semiont:version"]);
14871
- logger?.info("Linked data import: parsed manifest", {
14872
- entityTypes: manifest["semiont:entityTypes"].length,
14873
- resources: manifest["void:entities"]
14874
- });
14875
- const resolveBlob = buildBlobResolver2(entries);
14876
- let entityTypesAdded = 0;
14877
- for (const entityType2 of manifest["semiont:entityTypes"]) {
14878
- await addEntityType(entityType2, userId3, eventBus, logger);
14879
- entityTypesAdded++;
14880
- }
14881
- const resourceEntries = [...entries.keys()].filter((name) => name.startsWith(".semiont/resources/") && name.endsWith(".jsonld")).sort();
14882
- let resourcesCreated = 0;
14883
- let annotationsCreated = 0;
14884
- for (const entryName of resourceEntries) {
14885
- const resourceDoc = JSON.parse(entries.get(entryName).toString("utf8"));
14886
- const result = await importResource(resourceDoc, userId3, eventBus, contentStore, resolveBlob, logger);
14887
- resourcesCreated++;
14888
- annotationsCreated += result.annotationsCreated;
14889
- }
14890
- logger?.info("Linked data import complete", {
14891
- resourcesCreated,
14892
- annotationsCreated,
14893
- entityTypesAdded
14894
- });
14895
- return {
14896
- manifest,
14897
- resourcesCreated,
14898
- annotationsCreated,
14899
- entityTypesAdded
14900
- };
14901
- }
14902
- async function addEntityType(entityType2, userId3, eventBus, logger) {
14903
- await busRequest(
14904
- asBusRequestPrimitive(eventBus),
14905
- "frame:add-entity-type",
14906
- { tag: entityType2, _userId: userId3 },
14907
- IMPORT_TIMEOUT_MS
14908
- );
14909
- logger?.debug("Added entity type", { entityType: entityType2 });
14910
- }
14911
- async function importResource(doc, userId3, eventBus, contentStore, resolveBlob, logger) {
14912
- const name = doc["name"];
14913
- const representations = doc["representations"];
14914
- const annotations = doc["annotations"];
14915
- const entityTypes = doc["entityTypes"];
14916
- let format = "text/markdown";
14917
- let language;
14918
- let contentChecksum;
14919
- if (representations && representations.length > 0) {
14920
- const primary = representations[0];
14921
- if (primary["encodingFormat"]) format = primary["encodingFormat"];
14922
- if (primary["inLanguage"]) language = primary["inLanguage"];
14923
- if (primary["sha256"]) contentChecksum = primary["sha256"];
14924
- }
14925
- if (!contentChecksum) {
14926
- throw new Error(`Resource "${name}" has no content checksum in representations`);
14927
- }
14928
- const blob = resolveBlob(contentChecksum);
14929
- if (!blob) {
14930
- throw new Error(`Missing content blob for checksum ${contentChecksum} (resource "${name}")`);
14931
- }
14932
- const base = baseMediaType(format);
14933
- const resolvedUri = deriveStorageUri(name, isSupportedMediaType(base) ? base : "application/octet-stream");
14934
- const stored = await contentStore.store(blob, resolvedUri);
14935
- const { resourceId: createdId } = await busRequest(
14936
- asBusRequestPrimitive(eventBus),
14937
- "yield:create",
14938
- {
14939
- name,
14940
- storageUri: resolvedUri,
14941
- contentChecksum: stored.checksum,
14942
- byteSize: stored.byteSize,
14943
- format,
14944
- _userId: userId3,
14945
- language,
14946
- entityTypes: entityTypes ?? []
14947
- },
14948
- IMPORT_TIMEOUT_MS
14949
- );
14950
- const resourceId10 = resourceId(createdId);
14951
- logger?.debug("Created resource from JSON-LD", { name, resourceId: resourceId10 });
14952
- let annotationsCreated = 0;
14953
- if (annotations && annotations.length > 0) {
14954
- for (const annotation of annotations) {
14955
- await createAnnotation(annotation, resourceId10, userId3, eventBus, logger);
14956
- annotationsCreated++;
14957
- }
14958
- }
14959
- return { annotationsCreated };
14960
- }
14961
- async function createAnnotation(annotation, resourceId10, userId3, eventBus, logger) {
14962
- await awaitReply(
14963
- eventBus,
14964
- "mark:create",
14965
- {
14966
- annotation: dehydrateAnnotation(annotation),
14967
- _userId: userId3,
14968
- resourceId: resourceId10
14969
- },
14970
- "mark:create-ok",
14971
- "mark:create-failed",
14972
- IMPORT_TIMEOUT_MS
14973
- );
14974
- logger?.debug("Created annotation", { annotationId: annotation.id });
14975
- }
14976
- var AnnotationOperations = class {
14977
- /**
14978
- * Create a new annotation via EventBus → Stower
14979
- */
14980
- static async createAnnotation(request, userId3, creator, eventBus) {
14981
- const { annotation } = assembleAnnotation(request, creator);
14982
- const resId = resourceId(request.target.source);
14983
- eventBus.get("mark:create").next({
14984
- annotation,
14985
- _userId: userId3,
14986
- resourceId: resId
14987
- });
14988
- return { annotation };
14989
- }
14990
- /**
14991
- * Update annotation body via EventBus → Stower
14992
- */
14993
- static async updateAnnotationBody(id, request, userId3, eventBus, kb) {
14994
- const resId = resourceId(request.resourceId);
14995
- const annotation = await AnnotationContext.getAnnotation(
14996
- annotationId(id),
14997
- resId,
14998
- kb
14999
- );
15000
- if (!annotation) {
15001
- throw new Error("Annotation not found");
15002
- }
15003
- eventBus.get("mark:update-body").next({
15004
- annotationId: annotationId(id),
15005
- _userId: userId3,
15006
- resourceId: resId,
15007
- operations: request.operations
15008
- });
15009
- const updatedBody = applyBodyOperations(annotation.body, request.operations);
15010
- return {
15011
- annotation: {
15012
- ...annotation,
15013
- body: updatedBody
15014
- }
15015
- };
15016
- }
15017
- /**
15018
- * Delete an annotation via EventBus → Stower
15019
- */
15020
- static async deleteAnnotation(id, resourceIdStr, userId3, eventBus, kb, logger) {
15021
- const resId = resourceId(resourceIdStr);
15022
- const projection = await AnnotationContext.getResourceAnnotations(resId, kb);
15023
- const annotation = projection.annotations.find((a) => a.id === id);
15024
- if (!annotation) {
15025
- throw new Error("Annotation not found in resource");
15026
- }
15027
- logger?.debug("Removing annotation via EventBus", { annotationId: id });
15028
- eventBus.get("mark:delete").next({
15029
- annotationId: annotationId(id),
15030
- _userId: userId3,
15031
- resourceId: resId
15032
- });
15033
- logger?.debug("Annotation delete event emitted");
15034
- }
15035
- };
15036
14556
 
15037
- export { AnnotationContext, AnnotationOperations, BACKUP_FORMAT, Browser, CloneTokenManager, FORMAT_VERSION, Gatherer, GraphContext, LLMContext, LocalContentTransport, LocalTransport, Matcher, ResourceContext, ResourceOperations, Smelter, Stower, asBusRequestPrimitive, bootstrapEntityTypes, createKnowledgeBase, createSmelterActorStateUnit, exportBackup, exportLinkedData, generateReferenceSuggestions, generateResourceSummary, importBackup, importLinkedData, isBackupManifest, readEntityTypesProjection, registerAnnotationAssemblyHandler, registerAnnotationLookupHandlers, registerBindUpdateBodyHandler, registerBusHandlers, registerJobCommandHandlers, startMakeMeaning, stopKnowledgeSystem, validateManifestVersion };
14557
+ export { AnnotationContext, AnnotationOperations, Browser, CloneTokenManager, Gatherer, GraphContext, LLMContext, LocalContentTransport, LocalTransport, Matcher, ResourceContext, ResourceOperations, Smelter, Stower, asBusRequestPrimitive, bootstrapEntityTypes, createArchivistServer, createKnowledgeBase, createSmelterActorStateUnit, generateReferenceSuggestions, generateResourceSummary, makeMeaningConfigFrom, readEntityTypesProjection, registerAnnotationAssemblyHandler, registerAnnotationContextHandler, registerBindUpdateBodyHandler, registerBusHandlers, registerGatherSummaryHandler, registerJobCommandHandlers, requireKBName, startMakeMeaning, startMakeMeaningGateway, stopKnowledgeSystem };
15038
14558
  //# sourceMappingURL=index.js.map
15039
14559
  //# sourceMappingURL=index.js.map