@semiont/make-meaning 0.5.9 → 0.5.11

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,6 +1,6 @@
1
1
  import { FsJobQueue } from '@semiont/jobs';
2
2
  import { FilesystemViewStorage, resolveStorageUri, EventQuery, createEventStore } from '@semiont/event-sourcing';
3
- import { getPrimaryRepresentation, decodeRepresentation, getTargetSource, getBodySource, resourceId, getTargetSelector, getResourceEntityTypes, getResourceId, getTextPositionSelector, annotationId, errField, userId, generateUuid, getExactText, cloneToken, baseMediaType, isSupportedMediaType, capabilitiesOf, didToAgent, assembleAnnotation, jobId, entityType, baseUrl, busLog, BRIDGED_CHANNELS, burstBuffer, textExtractionOf, getPrimaryMediaType, extensionForMediaType, applyBodyOperations, findBodyItem } from '@semiont/core';
3
+ import { getPrimaryRepresentation, decodeRepresentation, getResourceEntityTypes, getResourceId, getTargetSource, resourceId, getBodySource, getTargetSelector, deriveViews, getTextPositionSelector, annotationId, errField, userId, generateUuid, getExactText, busRequest, cloneToken, baseMediaType, isSupportedMediaType, capabilitiesOf, didToAgent, assembleAnnotation, jobId, entityType, baseUrl, busLog, BRIDGED_CHANNELS, burstBuffer, textExtractionOf, getPrimaryMediaType, extensionForMediaType, applyBodyOperations, findBodyItem, softwareToAgent } from '@semiont/core';
4
4
  import { withActorSpan, recordBusEmit, withSpan, SpanKind, registerJobQueueProvider, registerVectorIndexSizeProvider } from '@semiont/observability';
5
5
  import { createInferenceClient } from '@semiont/inference';
6
6
  import { getGraphDatabase } from '@semiont/graph';
@@ -9,7 +9,6 @@ import { getEntityTypes, DEFAULT_ENTITY_TYPES } from '@semiont/ontology';
9
9
  import { promises } from 'fs';
10
10
  import * as path3 from 'path';
11
11
  import { chunkText } from '@semiont/vectors';
12
- import { busRequest } from '@semiont/sdk';
13
12
  import { createGzip, createGunzip } from 'zlib';
14
13
  import { pipeline, Readable } from 'stream';
15
14
  import { promisify } from 'util';
@@ -3569,7 +3568,7 @@ var require_firstValueFrom = __commonJS({
3569
3568
  exports.firstValueFrom = void 0;
3570
3569
  var EmptyError_1 = require_EmptyError();
3571
3570
  var Subscriber_1 = require_Subscriber();
3572
- function firstValueFrom5(source, config) {
3571
+ function firstValueFrom2(source, config) {
3573
3572
  var hasConfig = typeof config === "object";
3574
3573
  return new Promise(function(resolve2, reject) {
3575
3574
  var subscriber = new Subscriber_1.SafeSubscriber({
@@ -3589,7 +3588,7 @@ var require_firstValueFrom = __commonJS({
3589
3588
  source.subscribe(subscriber);
3590
3589
  });
3591
3590
  }
3592
- exports.firstValueFrom = firstValueFrom5;
3591
+ exports.firstValueFrom = firstValueFrom2;
3593
3592
  }
3594
3593
  });
3595
3594
 
@@ -3728,7 +3727,7 @@ var require_map = __commonJS({
3728
3727
  exports.map = void 0;
3729
3728
  var lift_1 = require_lift();
3730
3729
  var OperatorSubscriber_1 = require_OperatorSubscriber();
3731
- function map6(project, thisArg) {
3730
+ function map3(project, thisArg) {
3732
3731
  return lift_1.operate(function(source, subscriber) {
3733
3732
  var index = 0;
3734
3733
  source.subscribe(OperatorSubscriber_1.createOperatorSubscriber(subscriber, function(value) {
@@ -3736,7 +3735,7 @@ var require_map = __commonJS({
3736
3735
  }));
3737
3736
  });
3738
3737
  }
3739
- exports.map = map6;
3738
+ exports.map = map3;
3740
3739
  }
3741
3740
  });
3742
3741
 
@@ -4566,7 +4565,7 @@ var require_timer = __commonJS({
4566
4565
  var async_1 = require_async();
4567
4566
  var isScheduler_1 = require_isScheduler();
4568
4567
  var isDate_1 = require_isDate();
4569
- function timer5(dueTime, intervalOrScheduler, scheduler) {
4568
+ function timer2(dueTime, intervalOrScheduler, scheduler) {
4570
4569
  if (dueTime === void 0) {
4571
4570
  dueTime = 0;
4572
4571
  }
@@ -4599,7 +4598,7 @@ var require_timer = __commonJS({
4599
4598
  }, due);
4600
4599
  });
4601
4600
  }
4602
- exports.timer = timer5;
4601
+ exports.timer = timer2;
4603
4602
  }
4604
4603
  });
4605
4604
 
@@ -4789,7 +4788,7 @@ var require_race = __commonJS({
4789
4788
  var innerFrom_1 = require_innerFrom();
4790
4789
  var argsOrArgArray_1 = require_argsOrArgArray();
4791
4790
  var OperatorSubscriber_1 = require_OperatorSubscriber();
4792
- function race5() {
4791
+ function race2() {
4793
4792
  var sources = [];
4794
4793
  for (var _i = 0; _i < arguments.length; _i++) {
4795
4794
  sources[_i] = arguments[_i];
@@ -4797,7 +4796,7 @@ var require_race = __commonJS({
4797
4796
  sources = argsOrArgArray_1.argsOrArgArray(sources);
4798
4797
  return sources.length === 1 ? innerFrom_1.innerFrom(sources[0]) : new Observable_1.Observable(raceInit(sources));
4799
4798
  }
4800
- exports.race = race5;
4799
+ exports.race = race2;
4801
4800
  function raceInit(sources) {
4802
4801
  return function(subscriber) {
4803
4802
  var subscriptions = [];
@@ -5371,13 +5370,13 @@ var require_catchError = __commonJS({
5371
5370
  var innerFrom_1 = require_innerFrom();
5372
5371
  var OperatorSubscriber_1 = require_OperatorSubscriber();
5373
5372
  var lift_1 = require_lift();
5374
- function catchError(selector) {
5373
+ function catchError2(selector) {
5375
5374
  return lift_1.operate(function(source, subscriber) {
5376
5375
  var innerSub = null;
5377
5376
  var syncUnsub = false;
5378
5377
  var handledResult;
5379
5378
  innerSub = source.subscribe(OperatorSubscriber_1.createOperatorSubscriber(subscriber, void 0, void 0, function(err) {
5380
- handledResult = innerFrom_1.innerFrom(selector(err, catchError(selector)(source)));
5379
+ handledResult = innerFrom_1.innerFrom(selector(err, catchError2(selector)(source)));
5381
5380
  if (innerSub) {
5382
5381
  innerSub.unsubscribe();
5383
5382
  innerSub = null;
@@ -5393,7 +5392,7 @@ var require_catchError = __commonJS({
5393
5392
  }
5394
5393
  });
5395
5394
  }
5396
- exports.catchError = catchError;
5395
+ exports.catchError = catchError2;
5397
5396
  }
5398
5397
  });
5399
5398
 
@@ -5888,7 +5887,7 @@ var require_take = __commonJS({
5888
5887
  var empty_1 = require_empty();
5889
5888
  var lift_1 = require_lift();
5890
5889
  var OperatorSubscriber_1 = require_OperatorSubscriber();
5891
- function take3(count) {
5890
+ function take(count) {
5892
5891
  return count <= 0 ? function() {
5893
5892
  return empty_1.EMPTY;
5894
5893
  } : lift_1.operate(function(source, subscriber) {
@@ -5903,7 +5902,7 @@ var require_take = __commonJS({
5903
5902
  }));
5904
5903
  });
5905
5904
  }
5906
- exports.take = take3;
5905
+ exports.take = take;
5907
5906
  }
5908
5907
  });
5909
5908
 
@@ -9253,14 +9252,14 @@ var require_race2 = __commonJS({
9253
9252
  exports.race = void 0;
9254
9253
  var argsOrArgArray_1 = require_argsOrArgArray();
9255
9254
  var raceWith_1 = require_raceWith();
9256
- function race5() {
9255
+ function race2() {
9257
9256
  var args = [];
9258
9257
  for (var _i = 0; _i < arguments.length; _i++) {
9259
9258
  args[_i] = arguments[_i];
9260
9259
  }
9261
9260
  return raceWith_1.raceWith.apply(void 0, __spreadArray([], __read(argsOrArgArray_1.argsOrArgArray(args))));
9262
9261
  }
9263
- exports.race = race5;
9262
+ exports.race = race2;
9264
9263
  }
9265
9264
  });
9266
9265
 
@@ -9734,10 +9733,19 @@ function resolveActorInference(config, actor) {
9734
9733
  `No inference config found for actor '${actor}'. Set actors.${actor}.inference in your config.`
9735
9734
  );
9736
9735
  }
9736
+ function resolveWorkerInference(config, workerType) {
9737
+ const specific = config.workers?.[workerType];
9738
+ if (specific) return specific;
9739
+ const defaultWorker = config.workers?.default;
9740
+ if (defaultWorker) return defaultWorker;
9741
+ throw new Error(
9742
+ `No inference config found for worker '${workerType}'. Set workers.${workerType}.inference or workers.default.inference in your config.`
9743
+ );
9744
+ }
9737
9745
 
9738
9746
  // src/service.ts
9739
- var import_rxjs9 = __toESM(require_cjs());
9740
- var import_operators9 = __toESM(require_operators());
9747
+ var import_rxjs7 = __toESM(require_cjs());
9748
+ var import_operators7 = __toESM(require_operators());
9741
9749
 
9742
9750
  // src/graph/consumer.ts
9743
9751
  var import_rxjs = __toESM(require_cjs());
@@ -10056,7 +10064,7 @@ var GraphDBConsumer = class _GraphDBConsumer {
10056
10064
  {
10057
10065
  const rid = resourceId(event.resourceId);
10058
10066
  const doc = await graphDb.getResource(rid);
10059
- if (doc) {
10067
+ if (doc && !(doc.entityTypes || []).includes(event.payload.entityType)) {
10060
10068
  await graphDb.updateResource(rid, {
10061
10069
  entityTypes: [...doc.entityTypes || [], event.payload.entityType]
10062
10070
  });
@@ -10293,6 +10301,93 @@ var ResourceContext = class _ResourceContext {
10293
10301
  return void 0;
10294
10302
  }
10295
10303
  };
10304
+ var GraphContext = class {
10305
+ /**
10306
+ * Get all resources referencing this resource (backlinks)
10307
+ * Requires graph traversal - must use graph database
10308
+ */
10309
+ static async getBacklinks(resourceId10, kb) {
10310
+ return kb.graph.getResourceReferencedBy(resourceId10);
10311
+ }
10312
+ /**
10313
+ * Find shortest path between two resources
10314
+ * Requires graph traversal - must use graph database
10315
+ */
10316
+ static async findPath(fromResourceId, toResourceId, kb, maxDepth) {
10317
+ return kb.graph.findPath(fromResourceId, toResourceId, maxDepth);
10318
+ }
10319
+ /**
10320
+ * Get resource connections (graph edges)
10321
+ * Requires graph traversal - must use graph database
10322
+ */
10323
+ static async getResourceConnections(resourceId10, kb) {
10324
+ return kb.graph.getResourceConnections(resourceId10);
10325
+ }
10326
+ /**
10327
+ * Search resources by name (cross-resource query)
10328
+ * Requires full-text search - must use graph database
10329
+ */
10330
+ static async searchResources(query, kb, limit) {
10331
+ return kb.graph.searchResources(query, limit);
10332
+ }
10333
+ /**
10334
+ * Build the unified knowledge graph for a resource's neighborhood:
10335
+ * resources AND annotations as typed nodes, typed/directional edges.
10336
+ *
10337
+ * This is the single graph builder (CONTEXT-UNIFICATION D3) — both the
10338
+ * matcher (ranking) and the resource/viz path consume it. The flattened
10339
+ * signals the matcher reads today (`connections`, `citedBy`/count,
10340
+ * `siblingEntityTypes`, `bidirectional`) are all derivable from this:
10341
+ * - peer connections → resource nodes + main→peer edges carrying `bidirectional`
10342
+ * - inbound citations → citing-resource nodes + `citation` edges (citing→main),
10343
+ * so citedByCount = inbound citation-edge count
10344
+ * - annotations on the resource → `annotation` nodes + `annotation-of` edges,
10345
+ * so siblingEntityTypes = union of those nodes' entityTypes
10346
+ */
10347
+ static async buildKnowledgeGraph(resourceId10, kb) {
10348
+ const mainDoc = await kb.graph.getResource(resourceId10);
10349
+ if (!mainDoc) {
10350
+ throw new Error("Resource not found");
10351
+ }
10352
+ const mainId = String(resourceId10);
10353
+ const [connections, referencedBy, annotations] = await Promise.all([
10354
+ kb.graph.getResourceConnections(resourceId10),
10355
+ kb.graph.getResourceReferencedBy(resourceId10),
10356
+ kb.graph.getResourceAnnotations(resourceId10)
10357
+ ]);
10358
+ const nodes = [];
10359
+ const edges = [];
10360
+ const seen = /* @__PURE__ */ new Set();
10361
+ const addResourceNode = (id, label, entityTypes) => {
10362
+ if (!id || seen.has(id)) return;
10363
+ nodes.push({ id, type: "resource", label, entityTypes });
10364
+ seen.add(id);
10365
+ };
10366
+ addResourceNode(mainId, mainDoc.name, getResourceEntityTypes(mainDoc));
10367
+ for (const conn of connections) {
10368
+ const peerId = getResourceId(conn.targetResource);
10369
+ if (!peerId) continue;
10370
+ addResourceNode(peerId, conn.targetResource.name, getResourceEntityTypes(conn.targetResource));
10371
+ edges.push({ source: mainId, target: peerId, type: conn.relationshipType || "link", bidirectional: conn.bidirectional });
10372
+ }
10373
+ const citedSeen = /* @__PURE__ */ new Set();
10374
+ for (const ann of referencedBy) {
10375
+ const source = getTargetSource(ann.target);
10376
+ if (!source || source === String(resourceId10) || citedSeen.has(source)) continue;
10377
+ citedSeen.add(source);
10378
+ const view = await kb.views.get(resourceId(source));
10379
+ addResourceNode(source, view?.resource?.name ?? source, view?.resource ? getResourceEntityTypes(view.resource) : []);
10380
+ edges.push({ source, target: mainId, type: "citation" });
10381
+ }
10382
+ for (const ann of annotations) {
10383
+ if (!ann.id || seen.has(ann.id)) continue;
10384
+ nodes.push({ id: ann.id, type: "annotation", label: ann.motivation ?? "annotation", entityTypes: getEntityTypes(ann) });
10385
+ seen.add(ann.id);
10386
+ edges.push({ source: ann.id, target: mainId, type: "annotation-of" });
10387
+ }
10388
+ return { nodes, edges };
10389
+ }
10390
+ };
10296
10391
 
10297
10392
  // src/annotation-context.ts
10298
10393
  var AnnotationContext = class {
@@ -10406,42 +10501,21 @@ var AnnotationContext = class {
10406
10501
  };
10407
10502
  }
10408
10503
  }
10409
- logger?.debug("Building graph context", { resourceId: resourceId10 });
10410
- const [connections, referencedByAnnotations, entityTypeStats] = await Promise.all([
10411
- kb.graph.getResourceConnections(resourceId10),
10412
- kb.graph.getResourceReferencedBy(resourceId10),
10413
- kb.graph.getEntityTypeStats()
10414
- ]);
10415
- const citedByMap = /* @__PURE__ */ new Map();
10416
- for (const ann of referencedByAnnotations) {
10417
- const source = getTargetSource(ann.target);
10418
- if (source && source !== String(resourceId10)) {
10419
- const sourceResId = resourceId(source);
10420
- const sourceView2 = await kb.views.get(sourceResId);
10421
- if (sourceView2?.resource) {
10422
- citedByMap.set(source, sourceView2.resource.name);
10423
- }
10424
- }
10425
- }
10426
- const annotationEntityTypes = getEntityTypes(annotation);
10427
- const siblingEntityTypes = /* @__PURE__ */ new Set();
10428
- for (const ann of sourceView.annotations.annotations) {
10429
- if (ann.id !== annotationId5) {
10430
- for (const et of getEntityTypes(ann)) {
10431
- siblingEntityTypes.add(et);
10432
- }
10433
- }
10434
- }
10504
+ logger?.debug("Building knowledge graph", { resourceId: resourceId10 });
10505
+ const graph = await GraphContext.buildKnowledgeGraph(resourceId10, kb);
10506
+ const views = deriveViews(graph, String(resourceId10), annotationId5);
10507
+ const entityTypeStats = await kb.graph.getEntityTypeStats();
10435
10508
  const entityTypeFrequencies = {};
10436
10509
  for (const stat of entityTypeStats) {
10437
10510
  entityTypeFrequencies[stat.type] = stat.count;
10438
10511
  }
10512
+ const annotationEntityTypes = getEntityTypes(annotation);
10439
10513
  let inferredRelationshipSummary;
10440
10514
  if (inferenceClient && sourceContext) {
10441
10515
  try {
10442
- const connNames = connections.map((c) => c.targetResource.name).slice(0, 10);
10443
- const citedByNames = Array.from(citedByMap.values()).slice(0, 5);
10444
- const siblingTypes = Array.from(siblingEntityTypes).slice(0, 10);
10516
+ const connNames = views.connections.map((c) => c.resourceName).slice(0, 10);
10517
+ const citedByNames = views.citedBy.map((c) => c.resourceName).slice(0, 5);
10518
+ const siblingTypes = views.siblingEntityTypes.slice(0, 10);
10445
10519
  const parts = [];
10446
10520
  parts.push(`Passage: "${sourceContext.selected}"`);
10447
10521
  if (connNames.length > 0) parts.push(`Connected resources: ${connNames.join(", ")}`);
@@ -10459,27 +10533,6 @@ Summary:`;
10459
10533
  logger?.warn("Failed to generate inferred relationship summary", { error });
10460
10534
  }
10461
10535
  }
10462
- const graphContext = {
10463
- connections: connections.map((conn) => ({
10464
- resourceId: getResourceId(conn.targetResource) ?? "",
10465
- resourceName: conn.targetResource.name,
10466
- entityTypes: getResourceEntityTypes(conn.targetResource),
10467
- bidirectional: conn.bidirectional
10468
- })),
10469
- citedByCount: citedByMap.size,
10470
- citedBy: Array.from(citedByMap.entries()).map(([id, name]) => ({
10471
- resourceId: id,
10472
- resourceName: name
10473
- })),
10474
- siblingEntityTypes: Array.from(siblingEntityTypes),
10475
- entityTypeFrequencies,
10476
- ...inferredRelationshipSummary ? { inferredRelationshipSummary } : {}
10477
- };
10478
- logger?.debug("Built graph context", {
10479
- connections: connections.length,
10480
- citedByCount: citedByMap.size,
10481
- siblingEntityTypes: siblingEntityTypes.size
10482
- });
10483
10536
  let semanticContext;
10484
10537
  if (kb.vectors && embeddingProvider && sourceContext?.selected) {
10485
10538
  try {
@@ -10505,32 +10558,26 @@ Summary:`;
10505
10558
  logger?.warn("Semantic context search failed", { error });
10506
10559
  }
10507
10560
  }
10508
- const generationContext = {
10509
- annotation,
10510
- sourceResource: sourceDoc,
10561
+ const context = {
10562
+ focus: {
10563
+ kind: "annotation",
10564
+ annotation,
10565
+ sourceResource: sourceDoc,
10566
+ ...sourceContext ? { selected: { before: sourceContext.before || "", text: sourceContext.selected, after: sourceContext.after || "" } } : {},
10567
+ ...targetDoc ? { targetResource: targetDoc } : {},
10568
+ ...targetContext ? { targetContext } : {}
10569
+ },
10570
+ graph,
10511
10571
  metadata: {
10512
10572
  resourceType: "document",
10513
10573
  language: sourceDoc.language,
10514
- entityTypes: annotationEntityTypes
10574
+ entityTypes: annotationEntityTypes,
10575
+ entityTypeFrequencies
10515
10576
  },
10516
- graphContext,
10577
+ ...inferredRelationshipSummary ? { inferredRelationshipSummary } : {},
10517
10578
  ...semanticContext ? { semanticContext } : {}
10518
10579
  };
10519
- if (sourceContext) {
10520
- generationContext.sourceContext = {
10521
- before: sourceContext.before || "",
10522
- selected: sourceContext.selected,
10523
- after: sourceContext.after || ""
10524
- };
10525
- }
10526
- const response = {
10527
- annotation,
10528
- sourceResource: sourceDoc,
10529
- targetResource: targetDoc,
10530
- context: generationContext,
10531
- ...targetContext ? { targetContext } : {}
10532
- };
10533
- return response;
10580
+ return context;
10534
10581
  }
10535
10582
  /**
10536
10583
  * Get resource annotations from view storage (fast path)
@@ -10764,69 +10811,6 @@ Entity types: ${entityTypes.join(", ")}`;
10764
10811
  return inferenceClient.generateText(summaryPrompt, 500, 0.5);
10765
10812
  }
10766
10813
  };
10767
- var GraphContext = class {
10768
- /**
10769
- * Get all resources referencing this resource (backlinks)
10770
- * Requires graph traversal - must use graph database
10771
- */
10772
- static async getBacklinks(resourceId10, kb) {
10773
- return kb.graph.getResourceReferencedBy(resourceId10);
10774
- }
10775
- /**
10776
- * Find shortest path between two resources
10777
- * Requires graph traversal - must use graph database
10778
- */
10779
- static async findPath(fromResourceId, toResourceId, kb, maxDepth) {
10780
- return kb.graph.findPath(fromResourceId, toResourceId, maxDepth);
10781
- }
10782
- /**
10783
- * Get resource connections (graph edges)
10784
- * Requires graph traversal - must use graph database
10785
- */
10786
- static async getResourceConnections(resourceId10, kb) {
10787
- return kb.graph.getResourceConnections(resourceId10);
10788
- }
10789
- /**
10790
- * Search resources by name (cross-resource query)
10791
- * Requires full-text search - must use graph database
10792
- */
10793
- static async searchResources(query, kb, limit) {
10794
- return kb.graph.searchResources(query, limit);
10795
- }
10796
- /**
10797
- * Build graph representation with nodes and edges for a resource and its connections
10798
- * Retrieves connections from graph and builds visualization-ready structure
10799
- */
10800
- static async buildGraphRepresentation(resourceId10, maxRelated, kb) {
10801
- const mainDoc = await kb.graph.getResource(resourceId10);
10802
- if (!mainDoc) {
10803
- throw new Error("Resource not found");
10804
- }
10805
- const connections = await kb.graph.getResourceConnections(resourceId10);
10806
- const relatedDocs = connections.map((conn) => conn.targetResource).slice(0, maxRelated - 1);
10807
- const nodes = [
10808
- {
10809
- id: getResourceId(mainDoc),
10810
- type: "resource",
10811
- label: mainDoc.name,
10812
- metadata: { entityTypes: getResourceEntityTypes(mainDoc) }
10813
- },
10814
- ...relatedDocs.map((doc) => ({
10815
- id: getResourceId(doc),
10816
- type: "resource",
10817
- label: doc.name,
10818
- metadata: { entityTypes: getResourceEntityTypes(doc) }
10819
- }))
10820
- ].filter((node) => node.id !== void 0);
10821
- const edges = connections.slice(0, maxRelated - 1).map((conn) => ({
10822
- source: resourceId10,
10823
- target: getResourceId(conn.targetResource),
10824
- type: conn.relationshipType || "link",
10825
- metadata: {}
10826
- })).filter((edge) => edge.target !== void 0);
10827
- return { nodes, edges };
10828
- }
10829
- };
10830
10814
  var LLMContext = class {
10831
10815
  /**
10832
10816
  * Get comprehensive LLM context for a resource
@@ -10838,35 +10822,29 @@ var LLMContext = class {
10838
10822
  throw new Error("Resource not found");
10839
10823
  }
10840
10824
  const mainContent = options.includeContent ? await ResourceContext.getResourceContent(mainDoc, kb) : void 0;
10841
- const graph = await GraphContext.buildGraphRepresentation(
10842
- resourceId10,
10843
- options.maxResources,
10844
- kb
10845
- );
10846
- const relatedDocs = [];
10825
+ const graph = await GraphContext.buildKnowledgeGraph(resourceId10, kb);
10847
10826
  const resourceIdStr = resourceId10.toString();
10848
- for (const node of graph.nodes) {
10849
- if (node.id !== resourceIdStr) {
10850
- const relatedDoc = await ResourceContext.getResourceMetadata(resourceId(node.id), kb);
10851
- if (relatedDoc) {
10852
- relatedDocs.push(relatedDoc);
10853
- }
10827
+ const relatedDocs = [];
10828
+ const relatedNodes = graph.nodes.filter((node) => node.type === "resource" && node.id !== resourceIdStr).slice(0, Math.max(0, options.maxResources - 1));
10829
+ for (const node of relatedNodes) {
10830
+ const relatedDoc = await ResourceContext.getResourceMetadata(resourceId(node.id), kb);
10831
+ if (relatedDoc) {
10832
+ relatedDocs.push(relatedDoc);
10854
10833
  }
10855
10834
  }
10856
- const relatedResourcesContent = {};
10835
+ const relatedContent = {};
10857
10836
  if (options.includeContent) {
10858
10837
  await Promise.all(
10859
10838
  relatedDocs.map(async (doc) => {
10860
10839
  const docId = getResourceId(doc);
10861
10840
  if (!docId) return;
10862
- const content = await ResourceContext.getResourceContent(doc, kb);
10863
- if (content) {
10864
- relatedResourcesContent[docId] = content;
10841
+ const content2 = await ResourceContext.getResourceContent(doc, kb);
10842
+ if (content2) {
10843
+ relatedContent[docId] = content2;
10865
10844
  }
10866
10845
  })
10867
10846
  );
10868
10847
  }
10869
- const annotations = await AnnotationContext.getAllAnnotations(resourceId10, kb);
10870
10848
  const summary = options.includeSummary && mainContent ? await generateResourceSummary(
10871
10849
  mainDoc.name,
10872
10850
  mainContent,
@@ -10874,15 +10852,44 @@ var LLMContext = class {
10874
10852
  inferenceClient
10875
10853
  ) : void 0;
10876
10854
  const suggestedReferences = mainContent ? await generateReferenceSuggestions(mainContent, inferenceClient) : void 0;
10855
+ const content = {};
10856
+ if (mainContent) content.main = mainContent;
10857
+ if (options.includeContent) content.related = relatedContent;
10858
+ let semanticContext;
10859
+ if (kb.vectors) {
10860
+ const excludeEntityTypes = options.excludeEntityTypes ?? [];
10861
+ const matches = await kb.vectors.searchByResource(resourceId10, {
10862
+ limit: options.maxResources,
10863
+ scoreThreshold: 0.5,
10864
+ ...excludeEntityTypes.length ? { filter: { excludeEntityTypes } } : {}
10865
+ });
10866
+ if (matches.length > 0) {
10867
+ semanticContext = {
10868
+ similar: matches.map((m) => ({
10869
+ text: m.text,
10870
+ resourceId: m.resourceId,
10871
+ ...m.annotationId ? { annotationId: m.annotationId } : {},
10872
+ score: m.score,
10873
+ ...m.entityTypes ? { entityTypes: m.entityTypes } : {}
10874
+ })),
10875
+ ...excludeEntityTypes.length ? { excludedEntityTypes: excludeEntityTypes } : {}
10876
+ };
10877
+ }
10878
+ }
10877
10879
  return {
10878
- mainResource: mainDoc,
10879
- relatedResources: relatedDocs,
10880
- annotations,
10880
+ focus: {
10881
+ kind: "resource",
10882
+ resource: mainDoc,
10883
+ ...summary ? { summary } : {},
10884
+ ...suggestedReferences ? { suggestedReferences } : {},
10885
+ ...Object.keys(content).length > 0 ? { content } : {}
10886
+ },
10881
10887
  graph,
10882
- ...mainContent ? { mainResourceContent: mainContent } : {},
10883
- ...options.includeContent ? { relatedResourcesContent } : {},
10884
- ...summary ? { summary } : {},
10885
- ...suggestedReferences ? { suggestedReferences } : {}
10888
+ ...semanticContext ? { semanticContext } : {},
10889
+ metadata: {
10890
+ resourceType: "document",
10891
+ language: mainDoc.language
10892
+ }
10886
10893
  };
10887
10894
  }
10888
10895
  };
@@ -11042,8 +11049,13 @@ var Matcher = class {
11042
11049
  async handleSearch(event) {
11043
11050
  try {
11044
11051
  const context = event.context;
11045
- const selectedText = context.sourceContext?.selected ?? "";
11046
- const userHint = context.userHint ?? "";
11052
+ if (context.focus.kind !== "annotation") {
11053
+ throw new Error(`Matcher expected annotation focus, received '${context.focus.kind}'`);
11054
+ }
11055
+ const focus = context.focus;
11056
+ const mainResourceId = String(event.resourceId);
11057
+ const selectedText = focus.selected?.text ?? "";
11058
+ const userHint = focus.userHint ?? "";
11047
11059
  const searchTerm = [selectedText, userHint].filter(Boolean).join(" ");
11048
11060
  this.logger.debug("Searching for binding candidates", {
11049
11061
  referenceId: event.referenceId,
@@ -11054,6 +11066,8 @@ var Matcher = class {
11054
11066
  const scored = await this.contextDrivenSearch(
11055
11067
  searchTerm,
11056
11068
  context,
11069
+ focus,
11070
+ mainResourceId,
11057
11071
  event.useSemanticScoring
11058
11072
  );
11059
11073
  const limited = event.limit ? scored.slice(0, event.limit) : scored;
@@ -11089,9 +11103,10 @@ var Matcher = class {
11089
11103
  * - Name match quality (exact > prefix > contains)
11090
11104
  * - Recency (newer resources scored higher)
11091
11105
  */
11092
- async contextDrivenSearch(searchTerm, context, useSemanticScoring) {
11093
- const annotationEntityTypes = context.metadata?.entityTypes ?? [];
11094
- const connections = context.graphContext?.connections ?? [];
11106
+ async contextDrivenSearch(searchTerm, context, focus, mainResourceId, useSemanticScoring) {
11107
+ const annotationEntityTypes = context.metadata.entityTypes ?? [];
11108
+ const views = deriveViews(context.graph, mainResourceId);
11109
+ const connections = views.connections;
11095
11110
  const [nameMatches, entityTypeMatches, semanticMatches] = await Promise.all([
11096
11111
  this.kb.graph.searchResources(searchTerm),
11097
11112
  annotationEntityTypes.length > 0 ? this.kb.graph.listResources({ entityTypes: annotationEntityTypes, limit: 50 }).then((r) => r.resources) : Promise.resolve([]),
@@ -11136,7 +11151,7 @@ var Matcher = class {
11136
11151
  const bidirectionalIds = new Set(
11137
11152
  connections.filter((c) => c.bidirectional).map((c) => c.resourceId)
11138
11153
  );
11139
- const entityTypeFreqs = context.graphContext?.entityTypeFrequencies ?? {};
11154
+ const entityTypeFreqs = context.metadata.entityTypeFrequencies ?? {};
11140
11155
  const searchTermLower = searchTerm.toLowerCase();
11141
11156
  const scored = Array.from(candidateMap.values()).map(({ resource, sources }) => {
11142
11157
  const id = getResourceId(resource) ?? "";
@@ -11165,7 +11180,7 @@ var Matcher = class {
11165
11180
  score += 10;
11166
11181
  reasons.push("connected");
11167
11182
  }
11168
- const citedByCount = context.graphContext?.citedByCount ?? 0;
11183
+ const citedByCount = views.citedByCount;
11169
11184
  if (sources.has("neighborhood") && citedByCount > 0) {
11170
11185
  score += Math.min(citedByCount * 2, 15);
11171
11186
  }
@@ -11206,6 +11221,8 @@ var Matcher = class {
11206
11221
  const inferenceScores = await this.inferenceSemanticScore(
11207
11222
  searchTerm,
11208
11223
  context,
11224
+ focus,
11225
+ connections,
11209
11226
  scored.slice(0, 20)
11210
11227
  // Limit to top 20 candidates for cost
11211
11228
  );
@@ -11240,20 +11257,18 @@ var Matcher = class {
11240
11257
  *
11241
11258
  * @returns Map of resourceId → score (0-1)
11242
11259
  */
11243
- async inferenceSemanticScore(searchTerm, context, candidates) {
11244
- const passage = [context.sourceContext?.selected, context.userHint].filter(Boolean).join(" \u2014 ") || searchTerm;
11245
- const entityTypes = context.metadata?.entityTypes ?? [];
11246
- const graphConnections = context.graphContext?.connections;
11247
- const connections = graphConnections ?? [];
11260
+ async inferenceSemanticScore(searchTerm, context, focus, connections, candidates) {
11261
+ const passage = [focus.selected?.text, focus.userHint].filter(Boolean).join(" \u2014 ") || searchTerm;
11262
+ const entityTypes = context.metadata.entityTypes ?? [];
11248
11263
  const candidateLines = candidates.map((c, i) => {
11249
11264
  const id = getResourceId(c) ?? "";
11250
11265
  const cEntityTypes = getResourceEntityTypes(c);
11251
11266
  return `${i + 1}. "${c.name}" (id: ${id}, types: ${cEntityTypes.join(", ") || "none"})`;
11252
11267
  }).join("\n");
11253
11268
  const contextParts = [];
11254
- contextParts.push(`Annotation motivation: ${context.annotation.motivation}`);
11255
- contextParts.push(`Source resource: ${context.sourceResource.name}`);
11256
- const { motivation, body } = context.annotation;
11269
+ contextParts.push(`Annotation motivation: ${focus.annotation.motivation}`);
11270
+ contextParts.push(`Source resource: ${focus.sourceResource.name}`);
11271
+ const { motivation, body } = focus.annotation;
11257
11272
  if (motivation === "commenting" || motivation === "assessing") {
11258
11273
  const bodyItem = Array.isArray(body) ? body[0] : body;
11259
11274
  if (bodyItem && "value" in bodyItem && bodyItem.value) {
@@ -11266,8 +11281,8 @@ var Matcher = class {
11266
11281
  const connNames = connections.slice(0, 5).map((c) => c.resourceName);
11267
11282
  contextParts.push(`Connected resources: ${connNames.join(", ")}`);
11268
11283
  }
11269
- if (context.graphContext?.inferredRelationshipSummary) {
11270
- contextParts.push(`Relationship context: ${context.graphContext.inferredRelationshipSummary}`);
11284
+ if (context.inferredRelationshipSummary) {
11285
+ contextParts.push(`Relationship context: ${context.inferredRelationshipSummary}`);
11271
11286
  }
11272
11287
  const prompt = `Given this passage and context, score each candidate resource's semantic relevance on a scale of 0.0 to 1.0.
11273
11288
 
@@ -11339,14 +11354,59 @@ For each candidate, output a line with the number and score, like:
11339
11354
  // src/stower.ts
11340
11355
  var import_rxjs4 = __toESM(require_cjs());
11341
11356
  var import_operators4 = __toESM(require_operators());
11357
+ async function readEntityTypesProjection(project) {
11358
+ const entityTypesPath = path3.join(
11359
+ project.stateDir,
11360
+ "projections",
11361
+ "__system__",
11362
+ "entitytypes.json"
11363
+ );
11364
+ try {
11365
+ const content = await promises.readFile(entityTypesPath, "utf-8");
11366
+ const projection = JSON.parse(content);
11367
+ return projection.entityTypes || [];
11368
+ } catch (error) {
11369
+ if (error.code === "ENOENT") {
11370
+ return [];
11371
+ }
11372
+ throw error;
11373
+ }
11374
+ }
11375
+
11376
+ // src/views/projection-validators.ts
11377
+ function resolveTagSchema(tagSchemas, schemaId) {
11378
+ if (typeof schemaId !== "string" || !schemaId) {
11379
+ return { error: "tag-annotation requires schemaId" };
11380
+ }
11381
+ const schema = tagSchemas.find((s) => s.id === schemaId);
11382
+ if (!schema) {
11383
+ return { error: `Tag schema not registered: ${schemaId}` };
11384
+ }
11385
+ return { schema };
11386
+ }
11387
+ function validateEntityTypes(registered, requested) {
11388
+ if (!requested || requested.length === 0) {
11389
+ return { ok: true };
11390
+ }
11391
+ const set = new Set(registered);
11392
+ const unknown = requested.filter((t) => !set.has(t));
11393
+ return unknown.length > 0 ? { ok: false, unknown } : { ok: true };
11394
+ }
11395
+ function entityTypesNotRegisteredMessage(unknown) {
11396
+ return `Entity type not registered: ${unknown.join(", ")}`;
11397
+ }
11398
+
11399
+ // src/stower.ts
11342
11400
  var Stower = class {
11343
- constructor(kb, eventBus, logger) {
11401
+ constructor(kb, eventBus, project, logger) {
11344
11402
  this.kb = kb;
11345
11403
  this.eventBus = eventBus;
11404
+ this.project = project;
11346
11405
  this.logger = logger;
11347
11406
  }
11348
11407
  kb;
11349
11408
  eventBus;
11409
+ project;
11350
11410
  subscription = null;
11351
11411
  logger;
11352
11412
  async initialize() {
@@ -11411,26 +11471,10 @@ var Stower = class {
11411
11471
  generator: event.generator
11412
11472
  }
11413
11473
  });
11414
- const resource = {
11415
- "@context": "https://schema.org/",
11416
- "@id": rId,
11417
- name: event.name,
11418
- archived: false,
11419
- entityTypes: event.entityTypes || [],
11420
- storageUri: event.storageUri,
11421
- currentChecksum: checksum,
11422
- dateCreated: (/* @__PURE__ */ new Date()).toISOString(),
11423
- representations: [
11424
- {
11425
- mediaType: event.format,
11426
- checksum,
11427
- byteSize,
11428
- rel: "original",
11429
- language: event.language
11430
- }
11431
- ]
11432
- };
11433
- this.eventBus.get("yield:create-ok").next({ resourceId: rId, resource });
11474
+ this.eventBus.get("yield:create-ok").next({
11475
+ correlationId: event.correlationId,
11476
+ response: { resourceId: rId }
11477
+ });
11434
11478
  if (generatedFrom) {
11435
11479
  this.eventBus.get("mark:update-body").next({
11436
11480
  annotationId: generatedFrom.annotationId,
@@ -11456,6 +11500,7 @@ var Stower = class {
11456
11500
  } catch (error) {
11457
11501
  this.logger.error("Failed to create resource", { error: errField(error) });
11458
11502
  this.eventBus.get("yield:create-failed").next({
11503
+ correlationId: event.correlationId,
11459
11504
  message: error instanceof Error ? error.message : String(error)
11460
11505
  });
11461
11506
  }
@@ -11476,11 +11521,14 @@ var Stower = class {
11476
11521
  contentByteSize: event.byteSize
11477
11522
  }
11478
11523
  });
11479
- this.eventBus.get("yield:update-ok").next({ resourceId: event.resourceId });
11524
+ this.eventBus.get("yield:update-ok").next({
11525
+ correlationId: event.correlationId,
11526
+ response: { resourceId: event.resourceId }
11527
+ });
11480
11528
  } catch (error) {
11481
11529
  this.logger.error("Failed to update resource", { error: errField(error) });
11482
11530
  this.eventBus.get("yield:update-failed").next({
11483
- resourceId: event.resourceId,
11531
+ correlationId: event.correlationId,
11484
11532
  message: error instanceof Error ? error.message : String(error)
11485
11533
  });
11486
11534
  }
@@ -11513,7 +11561,6 @@ var Stower = class {
11513
11561
  toUri: event.toUri
11514
11562
  }
11515
11563
  });
11516
- this.eventBus.get("yield:move-ok").next({ resourceId: rId });
11517
11564
  } catch (error) {
11518
11565
  this.logger.error("Failed to move resource", { error: errField(error) });
11519
11566
  this.eventBus.get("yield:move-failed").next({
@@ -11561,10 +11608,11 @@ var Stower = class {
11561
11608
  version: 1,
11562
11609
  payload: { annotationId: annotationId(event.annotationId) }
11563
11610
  });
11564
- this.eventBus.get("mark:delete-ok").next({ annotationId: event.annotationId });
11611
+ this.eventBus.get("mark:delete-ok").next({ correlationId: event.correlationId, response: { annotationId: event.annotationId } });
11565
11612
  } catch (error) {
11566
11613
  this.logger.error("Failed to delete annotation", { error: errField(error) });
11567
11614
  this.eventBus.get("mark:delete-failed").next({
11615
+ correlationId: event.correlationId,
11568
11616
  message: error instanceof Error ? error.message : String(error)
11569
11617
  });
11570
11618
  }
@@ -11598,37 +11646,54 @@ var Stower = class {
11598
11646
  if (!event._userId) {
11599
11647
  throw new Error("mark:archive missing _userId (gateway injection)");
11600
11648
  }
11601
- if (event.storageUri) {
11602
- await this.kb.content.remove(event.storageUri, { keepFile: event.keepFile, noGit: event.noGit });
11649
+ try {
11650
+ if (event.storageUri) {
11651
+ await this.kb.content.remove(event.storageUri, { keepFile: event.keepFile, noGit: event.noGit });
11652
+ }
11653
+ await this.kb.eventStore.appendEvent({
11654
+ type: "mark:archived",
11655
+ resourceId: resourceId(event.resourceId),
11656
+ userId: userId(event._userId),
11657
+ version: 1,
11658
+ payload: { reason: void 0 }
11659
+ });
11660
+ this.eventBus.get("mark:archive-ok").next({ correlationId: event.correlationId });
11661
+ } catch (error) {
11662
+ this.logger.error("Failed to archive resource", { error: errField(error) });
11663
+ this.eventBus.get("mark:archive-failed").next({
11664
+ correlationId: event.correlationId,
11665
+ message: error instanceof Error ? error.message : String(error)
11666
+ });
11603
11667
  }
11604
- await this.kb.eventStore.appendEvent({
11605
- type: "mark:archived",
11606
- resourceId: resourceId(event.resourceId),
11607
- userId: userId(event._userId),
11608
- version: 1,
11609
- payload: { reason: void 0 }
11610
- });
11611
11668
  }
11612
11669
  async handleMarkUnarchive(event) {
11613
11670
  if (!event._userId) {
11614
11671
  throw new Error("mark:unarchive missing _userId (gateway injection)");
11615
11672
  }
11616
- if (event.storageUri) {
11617
- const absPath = this.kb.content.resolveUri(event.storageUri);
11618
- try {
11619
- await promises.access(absPath);
11620
- } catch {
11621
- this.logger.warn("Unarchive failed: file not found at storageUri", { storageUri: event.storageUri });
11622
- return;
11673
+ try {
11674
+ if (event.storageUri) {
11675
+ const absPath = this.kb.content.resolveUri(event.storageUri);
11676
+ try {
11677
+ await promises.access(absPath);
11678
+ } catch {
11679
+ throw new Error(`Cannot unarchive: file not found at ${event.storageUri}`);
11680
+ }
11623
11681
  }
11682
+ await this.kb.eventStore.appendEvent({
11683
+ type: "mark:unarchived",
11684
+ resourceId: resourceId(event.resourceId),
11685
+ userId: userId(event._userId),
11686
+ version: 1,
11687
+ payload: {}
11688
+ });
11689
+ this.eventBus.get("mark:unarchive-ok").next({ correlationId: event.correlationId });
11690
+ } catch (error) {
11691
+ this.logger.error("Failed to unarchive resource", { error: errField(error) });
11692
+ this.eventBus.get("mark:unarchive-failed").next({
11693
+ correlationId: event.correlationId,
11694
+ message: error instanceof Error ? error.message : String(error)
11695
+ });
11624
11696
  }
11625
- await this.kb.eventStore.appendEvent({
11626
- type: "mark:unarchived",
11627
- resourceId: resourceId(event.resourceId),
11628
- userId: userId(event._userId),
11629
- version: 1,
11630
- payload: {}
11631
- });
11632
11697
  }
11633
11698
  async handleAddEntityType(event) {
11634
11699
  if (!event._userId) {
@@ -11641,9 +11706,11 @@ var Stower = class {
11641
11706
  version: 1,
11642
11707
  payload: { entityType: event.tag }
11643
11708
  });
11709
+ this.eventBus.get("frame:entity-type-add-ok").next({ correlationId: event.correlationId });
11644
11710
  } catch (error) {
11645
11711
  this.logger.error("Failed to add entity type", { error: errField(error) });
11646
11712
  this.eventBus.get("frame:entity-type-add-failed").next({
11713
+ correlationId: event.correlationId,
11647
11714
  message: error instanceof Error ? error.message : String(error)
11648
11715
  });
11649
11716
  }
@@ -11659,9 +11726,11 @@ var Stower = class {
11659
11726
  version: 1,
11660
11727
  payload: { schema: event.schema }
11661
11728
  });
11729
+ this.eventBus.get("frame:tag-schema-add-ok").next({ correlationId: event.correlationId });
11662
11730
  } catch (error) {
11663
11731
  this.logger.error("Failed to add tag schema", { schemaId: event.schema?.id, error: errField(error) });
11664
11732
  this.eventBus.get("frame:tag-schema-add-failed").next({
11733
+ correlationId: event.correlationId,
11665
11734
  message: error instanceof Error ? error.message : String(error)
11666
11735
  });
11667
11736
  }
@@ -11673,22 +11742,38 @@ var Stower = class {
11673
11742
  const uid = userId(event._userId);
11674
11743
  const added = event.updatedEntityTypes.filter((et) => !event.currentEntityTypes.includes(et));
11675
11744
  const removed = event.currentEntityTypes.filter((et) => !event.updatedEntityTypes.includes(et));
11676
- for (const entityType2 of added) {
11677
- await this.kb.eventStore.appendEvent({
11678
- type: "mark:entity-tag-added",
11679
- resourceId: resourceId(event.resourceId),
11680
- userId: uid,
11681
- version: 1,
11682
- payload: { entityType: entityType2 }
11683
- });
11684
- }
11685
- for (const entityType2 of removed) {
11686
- await this.kb.eventStore.appendEvent({
11687
- type: "mark:entity-tag-removed",
11688
- resourceId: resourceId(event.resourceId),
11689
- userId: uid,
11690
- version: 1,
11691
- payload: { entityType: entityType2 }
11745
+ try {
11746
+ if (added.length > 0) {
11747
+ const registered = await readEntityTypesProjection(this.project);
11748
+ const result = validateEntityTypes(registered, added);
11749
+ if (!result.ok) {
11750
+ throw new Error(entityTypesNotRegisteredMessage(result.unknown));
11751
+ }
11752
+ }
11753
+ for (const entityType2 of added) {
11754
+ await this.kb.eventStore.appendEvent({
11755
+ type: "mark:entity-tag-added",
11756
+ resourceId: resourceId(event.resourceId),
11757
+ userId: uid,
11758
+ version: 1,
11759
+ payload: { entityType: entityType2 }
11760
+ });
11761
+ }
11762
+ for (const entityType2 of removed) {
11763
+ await this.kb.eventStore.appendEvent({
11764
+ type: "mark:entity-tag-removed",
11765
+ resourceId: resourceId(event.resourceId),
11766
+ userId: uid,
11767
+ version: 1,
11768
+ payload: { entityType: entityType2 }
11769
+ });
11770
+ }
11771
+ this.eventBus.get("mark:update-entity-types-ok").next({ correlationId: event.correlationId });
11772
+ } catch (error) {
11773
+ this.logger.error("Failed to update entity types", { error: errField(error) });
11774
+ this.eventBus.get("mark:update-entity-types-failed").next({
11775
+ correlationId: event.correlationId,
11776
+ message: error instanceof Error ? error.message : String(error)
11692
11777
  });
11693
11778
  }
11694
11779
  }
@@ -11752,24 +11837,6 @@ var Stower = class {
11752
11837
  // src/browser.ts
11753
11838
  var import_rxjs5 = __toESM(require_cjs());
11754
11839
  var import_operators5 = __toESM(require_operators());
11755
- async function readEntityTypesProjection(project) {
11756
- const entityTypesPath = path3.join(
11757
- project.stateDir,
11758
- "projections",
11759
- "__system__",
11760
- "entitytypes.json"
11761
- );
11762
- try {
11763
- const content = await promises.readFile(entityTypesPath, "utf-8");
11764
- const projection = JSON.parse(content);
11765
- return projection.entityTypes || [];
11766
- } catch (error) {
11767
- if (error.code === "ENOENT") {
11768
- return [];
11769
- }
11770
- throw error;
11771
- }
11772
- }
11773
11840
  async function readTagSchemasProjection(project) {
11774
11841
  const tagSchemasPath = path3.join(
11775
11842
  project.stateDir,
@@ -11800,20 +11867,66 @@ async function assembleResourceGraph(kb, resourceId10) {
11800
11867
  });
11801
11868
  return { resource: stored.resource, annotations, entityReferences };
11802
11869
  }
11870
+ var JOB_TYPES = [
11871
+ "reference-annotation",
11872
+ "highlight-annotation",
11873
+ "assessment-annotation",
11874
+ "comment-annotation",
11875
+ "tag-annotation",
11876
+ "generation"
11877
+ ];
11878
+ function deriveAgentRoster(config) {
11879
+ const domain = config.site?.domain;
11880
+ if (!domain) {
11881
+ throw new Error(
11882
+ "site.domain is required to mint agent DIDs \u2014 the directory must use the same canonical domain /api/tokens/agent mints worker DIDs from (no topology fallback)"
11883
+ );
11884
+ }
11885
+ const roster = /* @__PURE__ */ new Map();
11886
+ const admit = (inference, jobType) => {
11887
+ const key = `${inference.type}\0${inference.model}`;
11888
+ let entry = roster.get(key);
11889
+ if (!entry) {
11890
+ entry = {
11891
+ agent: softwareToAgent({ domain, provider: inference.type, model: inference.model }),
11892
+ servesJobTypes: []
11893
+ };
11894
+ roster.set(key, entry);
11895
+ }
11896
+ if (jobType) entry.servesJobTypes.push(jobType);
11897
+ };
11898
+ for (const jobType of JOB_TYPES) {
11899
+ try {
11900
+ admit(resolveWorkerInference(config, jobType), jobType);
11901
+ } catch {
11902
+ }
11903
+ }
11904
+ for (const actor of ["gatherer", "matcher"]) {
11905
+ try {
11906
+ admit(resolveActorInference(config, actor));
11907
+ } catch {
11908
+ }
11909
+ }
11910
+ return [...roster.values()].map(
11911
+ ({ agent, servesJobTypes }) => servesJobTypes.length ? { agent, servesJobTypes } : { agent }
11912
+ );
11913
+ }
11803
11914
 
11804
11915
  // src/browser.ts
11805
11916
  var Browser = class {
11806
- constructor(views, kb, eventBus, project, logger) {
11917
+ constructor(views, kb, eventBus, project, config, logger) {
11807
11918
  this.views = views;
11808
11919
  this.kb = kb;
11809
11920
  this.eventBus = eventBus;
11810
11921
  this.project = project;
11922
+ this.config = config;
11811
11923
  this.logger = logger;
11812
11924
  }
11813
11925
  views;
11814
11926
  kb;
11815
11927
  eventBus;
11816
11928
  project;
11929
+ config;
11817
11930
  subscriptions = [];
11818
11931
  logger;
11819
11932
  async initialize() {
@@ -11821,7 +11934,19 @@ var Browser = class {
11821
11934
  const errorHandler = (err) => this.logger.error("Browser pipeline error", { error: err });
11822
11935
  const pipe = (name, handler) => this.eventBus.get(name).pipe(
11823
11936
  (0, import_operators5.mergeMap)(
11824
- (event) => (0, import_rxjs5.from)(withActorSpan("browser", name, () => handler(event)))
11937
+ (event) => (0, import_rxjs5.from)(withActorSpan("browser", name, () => handler(event))).pipe(
11938
+ // Isolate per-event failures: a single handler throw must NOT tear down the
11939
+ // channel subscription for every future request — that's the browse:entity-types
11940
+ // wedge (.plans/bugs/browse-entity-types-never-responds.md). Handlers emit their
11941
+ // own *-failed reply; this is the structural backstop for any throw that escapes a
11942
+ // handler's try/catch — the channel survives, the offending request is logged.
11943
+ // (A per-channel *-failed can't be emitted from this generic helper without the
11944
+ // request→failure mapping — that's the Tier 1 operations registry.)
11945
+ (0, import_operators5.catchError)((error) => {
11946
+ this.logger.error(`browse handler threw on ${name}`, { error: errField(error) });
11947
+ return import_rxjs5.EMPTY;
11948
+ })
11949
+ )
11825
11950
  )
11826
11951
  );
11827
11952
  this.subscriptions.push(
@@ -11834,6 +11959,7 @@ var Browser = class {
11834
11959
  pipe("browse:referenced-by-requested", (e) => this.handleReferencedBy(e)).subscribe({ error: errorHandler }),
11835
11960
  pipe("browse:entity-types-requested", (e) => this.handleEntityTypes(e)).subscribe({ error: errorHandler }),
11836
11961
  pipe("browse:tag-schemas-requested", (e) => this.handleTagSchemas(e)).subscribe({ error: errorHandler }),
11962
+ pipe("browse:agents-requested", (e) => this.handleBrowseAgents(e)).subscribe({ error: errorHandler }),
11837
11963
  pipe("browse:directory-requested", (e) => this.handleBrowseDirectory(e)).subscribe({ error: errorHandler })
11838
11964
  );
11839
11965
  }
@@ -12082,6 +12208,21 @@ var Browser = class {
12082
12208
  });
12083
12209
  }
12084
12210
  }
12211
+ async handleBrowseAgents(event) {
12212
+ try {
12213
+ const agents = deriveAgentRoster(this.config);
12214
+ this.eventBus.get("browse:agents-result").next({
12215
+ correlationId: event.correlationId,
12216
+ response: { agents }
12217
+ });
12218
+ } catch (error) {
12219
+ this.logger.error("Agent roster derivation failed", { error: errField(error) });
12220
+ this.eventBus.get("browse:agents-failed").next({
12221
+ correlationId: event.correlationId,
12222
+ message: error instanceof Error ? error.message : String(error)
12223
+ });
12224
+ }
12225
+ }
12085
12226
  // ========================================================================
12086
12227
  // Filesystem read handler
12087
12228
  // ========================================================================
@@ -12206,49 +12347,48 @@ async function readAnnotationFromView(kb, resourceId10, annotationId5) {
12206
12347
  }
12207
12348
 
12208
12349
  // src/clone-token-manager.ts
12209
- var import_rxjs7 = __toESM(require_cjs());
12210
- var import_operators7 = __toESM(require_operators());
12211
-
12212
- // src/resource-operations.ts
12213
12350
  var import_rxjs6 = __toESM(require_cjs());
12214
12351
  var import_operators6 = __toESM(require_operators());
12352
+
12353
+ // src/bus-request-local.ts
12354
+ function asBusRequestPrimitive(eventBus) {
12355
+ return {
12356
+ emit(channel, payload) {
12357
+ eventBus.get(channel).next(payload);
12358
+ return Promise.resolve();
12359
+ },
12360
+ stream(channel) {
12361
+ return eventBus.get(channel).asObservable();
12362
+ }
12363
+ };
12364
+ }
12365
+
12366
+ // src/resource-operations.ts
12215
12367
  var ResourceOperations = class {
12216
12368
  /**
12217
12369
  * Create a new resource via EventBus → Stower
12218
12370
  */
12219
12371
  static async createResource(input, userId3, eventBus) {
12220
- const result$ = (0, import_rxjs6.race)(
12221
- eventBus.get("yield:create-ok").pipe(
12222
- (0, import_operators6.take)(1),
12223
- (0, import_operators6.map)((result) => ({ ok: true, result }))
12224
- ),
12225
- eventBus.get("yield:create-failed").pipe(
12226
- (0, import_operators6.take)(1),
12227
- (0, import_operators6.map)((failure) => ({ ok: false, error: new Error(failure.message) }))
12228
- ),
12229
- (0, import_rxjs6.timer)(3e4).pipe(
12230
- (0, import_operators6.map)(() => ({ ok: false, error: new Error("Resource creation timed out") }))
12231
- )
12372
+ const { resourceId: rId } = await busRequest(
12373
+ asBusRequestPrimitive(eventBus),
12374
+ "yield:create",
12375
+ {
12376
+ name: input.name,
12377
+ storageUri: input.storageUri,
12378
+ contentChecksum: input.contentChecksum,
12379
+ byteSize: input.byteSize,
12380
+ format: input.format,
12381
+ _userId: userId3,
12382
+ language: input.language,
12383
+ entityTypes: input.entityTypes,
12384
+ generatedFrom: input.generatedFrom,
12385
+ generationPrompt: input.generationPrompt,
12386
+ generator: input.generator,
12387
+ isDraft: input.isDraft
12388
+ },
12389
+ 3e4
12232
12390
  );
12233
- eventBus.get("yield:create").next({
12234
- name: input.name,
12235
- storageUri: input.storageUri,
12236
- contentChecksum: input.contentChecksum,
12237
- byteSize: input.byteSize,
12238
- format: input.format,
12239
- _userId: userId3,
12240
- language: input.language,
12241
- entityTypes: input.entityTypes,
12242
- generatedFrom: input.generatedFrom,
12243
- generationPrompt: input.generationPrompt,
12244
- generator: input.generator,
12245
- isDraft: input.isDraft
12246
- });
12247
- const outcome = await (0, import_rxjs6.firstValueFrom)(result$);
12248
- if (!outcome.ok) {
12249
- throw outcome.error;
12250
- }
12251
- return resourceId(outcome.result.resourceId);
12391
+ return resourceId(rId);
12252
12392
  }
12253
12393
  };
12254
12394
 
@@ -12268,13 +12408,13 @@ var CloneTokenManager = class {
12268
12408
  this.logger.info("CloneTokenManager actor initialized");
12269
12409
  const errorHandler = (err) => this.logger.error("CloneTokenManager pipeline error", { error: err });
12270
12410
  const generateToken$ = this.eventBus.get("yield:clone-token-requested").pipe(
12271
- (0, import_operators7.mergeMap)((event) => (0, import_rxjs7.from)(this.handleGenerateToken(event)))
12411
+ (0, import_operators6.mergeMap)((event) => (0, import_rxjs6.from)(this.handleGenerateToken(event)))
12272
12412
  );
12273
12413
  const getResource$ = this.eventBus.get("yield:clone-resource-requested").pipe(
12274
- (0, import_operators7.mergeMap)((event) => (0, import_rxjs7.from)(this.handleGetResource(event)))
12414
+ (0, import_operators6.mergeMap)((event) => (0, import_rxjs6.from)(this.handleGetResource(event)))
12275
12415
  );
12276
12416
  const createResource$ = this.eventBus.get("yield:clone-create").pipe(
12277
- (0, import_operators7.mergeMap)((event) => (0, import_rxjs7.from)(this.handleCreateResource(event)))
12417
+ (0, import_operators6.mergeMap)((event) => (0, import_rxjs6.from)(this.handleCreateResource(event)))
12278
12418
  );
12279
12419
  this.subscriptions.push(
12280
12420
  generateToken$.subscribe({ error: errorHandler }),
@@ -12448,10 +12588,6 @@ var CloneTokenManager = class {
12448
12588
  this.logger.info("CloneTokenManager actor stopped");
12449
12589
  }
12450
12590
  };
12451
-
12452
- // src/bootstrap/entity-types.ts
12453
- var import_rxjs8 = __toESM(require_cjs());
12454
- var import_operators8 = __toESM(require_operators());
12455
12591
  async function bootstrapEntityTypes(eventBus, eventStore, logger) {
12456
12592
  const systemEvents = await eventStore.log.getEvents(resourceId("__system__"));
12457
12593
  const existingTypes = new Set(
@@ -12466,16 +12602,12 @@ async function bootstrapEntityTypes(eventBus, eventStore, logger) {
12466
12602
  const SYSTEM_USER_ID = userId("00000000-0000-0000-0000-000000000000");
12467
12603
  for (const entityType2 of missing) {
12468
12604
  logger?.debug("Adding entity type via EventBus", { entityType: entityType2 });
12469
- const result$ = (0, import_rxjs8.race)(
12470
- eventBus.get("frame:entity-type-added").pipe((0, import_operators8.take)(1), (0, import_operators8.map)(() => ({ ok: true }))),
12471
- eventBus.get("frame:entity-type-add-failed").pipe((0, import_operators8.take)(1), (0, import_operators8.map)((f) => ({ ok: false, error: new Error(f.message) }))),
12472
- (0, import_rxjs8.timer)(1e4).pipe((0, import_operators8.map)(() => ({ ok: false, error: new Error(`Timeout adding entity type: ${entityType2}`) })))
12605
+ await busRequest(
12606
+ asBusRequestPrimitive(eventBus),
12607
+ "frame:add-entity-type",
12608
+ { tag: entityType2, _userId: SYSTEM_USER_ID },
12609
+ 1e4
12473
12610
  );
12474
- eventBus.get("frame:add-entity-type").next({ tag: entityType2, _userId: SYSTEM_USER_ID });
12475
- const outcome = await (0, import_rxjs8.firstValueFrom)(result$);
12476
- if (!outcome.ok) {
12477
- throw outcome.error;
12478
- }
12479
12611
  }
12480
12612
  logger?.info("Entity types bootstrap completed", { added: missing.length, total: DEFAULT_ENTITY_TYPES.length });
12481
12613
  }
@@ -12636,10 +12768,7 @@ function registerBindUpdateBodyHandler(eventBus, parentLogger) {
12636
12768
  if (!cid || !inflight.has(cid)) return;
12637
12769
  inflight.delete(cid);
12638
12770
  const annId = event.payload?.annotationId;
12639
- eventBus.get("bind:body-updated").next({
12640
- correlationId: cid,
12641
- annotationId: annId
12642
- });
12771
+ eventBus.get("bind:body-updated").next({ correlationId: cid });
12643
12772
  logger.info("Bind body-updated confirmed", { annotationId: annId, correlationId: cid });
12644
12773
  });
12645
12774
  eventBus.get("mark:body-update-failed").subscribe((event) => {
@@ -12654,31 +12783,6 @@ function registerBindUpdateBodyHandler(eventBus, parentLogger) {
12654
12783
  logger.warn("Bind body-update failed after forwarding", { correlationId: cid, message });
12655
12784
  });
12656
12785
  }
12657
-
12658
- // src/views/projection-validators.ts
12659
- function resolveTagSchema(tagSchemas, schemaId) {
12660
- if (typeof schemaId !== "string" || !schemaId) {
12661
- return { error: "tag-annotation requires schemaId" };
12662
- }
12663
- const schema = tagSchemas.find((s) => s.id === schemaId);
12664
- if (!schema) {
12665
- return { error: `Tag schema not registered: ${schemaId}` };
12666
- }
12667
- return { schema };
12668
- }
12669
- function validateEntityTypes(registered, requested) {
12670
- if (!requested || requested.length === 0) {
12671
- return { ok: true };
12672
- }
12673
- const set = new Set(registered);
12674
- const unknown = requested.filter((t) => !set.has(t));
12675
- return unknown.length > 0 ? { ok: false, unknown } : { ok: true };
12676
- }
12677
- function entityTypesNotRegisteredMessage(unknown) {
12678
- return `Entity type not registered: ${unknown.join(", ")}`;
12679
- }
12680
-
12681
- // src/handlers/job-commands.ts
12682
12786
  function parseDidUser(did) {
12683
12787
  const parts = did.split(":");
12684
12788
  const usersIdx = parts.indexOf("users");
@@ -12706,7 +12810,11 @@ function registerJobCommandHandlers(eventBus, jobQueue, project, parentLogger) {
12706
12810
  userDomain: user.domain,
12707
12811
  created: (/* @__PURE__ */ new Date()).toISOString(),
12708
12812
  retryCount: 0,
12709
- maxRetries: jobType === "generation" ? 3 : 1
12813
+ // Generation is non-idempotent a retry re-runs the LLM and produces
12814
+ // *different* content (not a replay) — and expensive. Surface the failure
12815
+ // to the caller rather than silently re-rolling. Detection jobs re-scan the
12816
+ // same content (≈idempotent) and keep one self-heal retry.
12817
+ maxRetries: jobType === "generation" ? 0 : 1
12710
12818
  },
12711
12819
  params: {
12712
12820
  resourceId: resourceId(resId),
@@ -12823,11 +12931,19 @@ function registerJobCommandHandlers(eventBus, jobQueue, project, parentLogger) {
12823
12931
  try {
12824
12932
  const cancelled = await jobQueue.cancelPendingJobs(event.jobType);
12825
12933
  logger.info("Cancel requested", { jobType: event.jobType, cancelled });
12934
+ eventBus.get("job:cancel-ok").next({
12935
+ correlationId: event.correlationId,
12936
+ response: { cancelled }
12937
+ });
12826
12938
  } catch (error) {
12827
12939
  logger.error("Failed to cancel pending jobs", {
12828
12940
  jobType: event.jobType,
12829
12941
  error: error.message
12830
12942
  });
12943
+ eventBus.get("job:cancel-failed").next({
12944
+ correlationId: event.correlationId,
12945
+ message: error.message
12946
+ });
12831
12947
  }
12832
12948
  });
12833
12949
  }
@@ -12847,7 +12963,7 @@ async function createJobQueue(project, eventBus, logger) {
12847
12963
  await jobQueue.initialize();
12848
12964
  registerJobQueueProvider(() => jobQueue.getStats());
12849
12965
  const jobStatusSubscription = eventBus.get("job:status-requested").pipe(
12850
- (0, import_operators9.mergeMap)((event) => (0, import_rxjs9.from)((async () => {
12966
+ (0, import_operators7.mergeMap)((event) => (0, import_rxjs7.from)((async () => {
12851
12967
  try {
12852
12968
  const job = await jobQueue.getJob(jobId(event.jobId));
12853
12969
  if (!job) {
@@ -12917,7 +13033,7 @@ async function createKnowledgeSystemFromConfig(project, config, eventBus, logger
12917
13033
  if (annotation === null) return event;
12918
13034
  return { ...event, annotation };
12919
13035
  });
12920
- const stower = new Stower(kb, eventBus, logger.child({ component: "stower" }));
13036
+ const stower = new Stower(kb, eventBus, project, logger.child({ component: "stower" }));
12921
13037
  await stower.initialize();
12922
13038
  await bootstrapEntityTypes(eventBus, eventStore, logger.child({ component: "entity-types-bootstrap" }));
12923
13039
  const gatherer = new Gatherer(
@@ -12936,7 +13052,7 @@ async function createKnowledgeSystemFromConfig(project, config, eventBus, logger
12936
13052
  embeddingProvider
12937
13053
  );
12938
13054
  await matcher.initialize();
12939
- const browser = new Browser(kb.views, kb, eventBus, project, logger.child({ component: "browser" }));
13055
+ const browser = new Browser(kb.views, kb, eventBus, project, config, logger.child({ component: "browser" }));
12940
13056
  await browser.initialize();
12941
13057
  const cloneTokenManager = new CloneTokenManager(kb, eventBus, logger.child({ component: "clone-token-manager" }));
12942
13058
  await cloneTokenManager.initialize();
@@ -12964,11 +13080,11 @@ async function startMakeMeaning(project, config, eventBus, logger, options) {
12964
13080
  }
12965
13081
 
12966
13082
  // src/local-transport.ts
12967
- var import_rxjs10 = __toESM(require_cjs());
13083
+ var import_rxjs8 = __toESM(require_cjs());
12968
13084
  var LocalTransport = class {
12969
13085
  baseUrl;
12970
13086
  state$;
12971
- errorsSubject = new import_rxjs10.Subject();
13087
+ errorsSubject = new import_rxjs8.Subject();
12972
13088
  /**
12973
13089
  * Stream of `SemiontError` instances surfaced from transport-mediated
12974
13090
  * round-trips (typed-wire methods on this transport that fail). The
@@ -12989,7 +13105,7 @@ var LocalTransport = class {
12989
13105
  this.bus = cfg.eventBus;
12990
13106
  this.userId = cfg.userId;
12991
13107
  this.baseUrl = cfg.baseUrl ?? baseUrl("local://in-process");
12992
- this.state$ = new import_rxjs10.BehaviorSubject("open");
13108
+ this.state$ = new import_rxjs8.BehaviorSubject("open");
12993
13109
  }
12994
13110
  // ── Bus primitives ──────────────────────────────────────────────────────
12995
13111
  async emit(channel, payload, resourceScope) {
@@ -13122,8 +13238,8 @@ var LocalContentTransport = class {
13122
13238
  };
13123
13239
 
13124
13240
  // src/smelter.ts
13125
- var import_rxjs11 = __toESM(require_cjs());
13126
- var import_operators10 = __toESM(require_operators());
13241
+ var import_rxjs9 = __toESM(require_cjs());
13242
+ var import_operators8 = __toESM(require_operators());
13127
13243
  function isWorkItem(input) {
13128
13244
  return input.type.startsWith("smelt:");
13129
13245
  }
@@ -13149,7 +13265,7 @@ var Smelter = class _Smelter {
13149
13265
  static RECONCILE_PAGE_SIZE = 200;
13150
13266
  /** Bound on concurrently in-flight reconcile work — a cold rebuild must not fan out unbounded embedding calls. */
13151
13267
  static RECONCILE_WAVE = 8;
13152
- eventSubject = new import_rxjs11.Subject();
13268
+ eventSubject = new import_rxjs9.Subject();
13153
13269
  sourceSubscription = null;
13154
13270
  pipelineSubscription = null;
13155
13271
  _eventsProcessed = 0;
@@ -13164,23 +13280,23 @@ var Smelter = class _Smelter {
13164
13280
  }
13165
13281
  initialize() {
13166
13282
  this.pipelineSubscription = this.eventSubject.pipe(
13167
- (0, import_operators10.groupBy)((e) => e.resourceId ?? "__unknown__"),
13168
- (0, import_operators10.mergeMap)(
13283
+ (0, import_operators8.groupBy)((e) => e.resourceId ?? "__unknown__"),
13284
+ (0, import_operators8.mergeMap)(
13169
13285
  (group) => group.pipe(
13170
13286
  burstBuffer({
13171
13287
  burstWindowMs: this.timing.burstWindowMs,
13172
13288
  maxBatchSize: this.timing.maxBatchSize,
13173
13289
  idleTimeoutMs: this.timing.idleTimeoutMs
13174
13290
  }),
13175
- (0, import_operators10.concatMap)((inputOrBatch) => {
13291
+ (0, import_operators8.concatMap)((inputOrBatch) => {
13176
13292
  if (Array.isArray(inputOrBatch)) {
13177
- return (0, import_rxjs11.from)(
13293
+ return (0, import_rxjs9.from)(
13178
13294
  withActorSpan("smelter", "batch", async () => {
13179
13295
  this._eventsProcessed += await this.processBatch(inputOrBatch);
13180
13296
  }, { "batch.size": inputOrBatch.length })
13181
13297
  );
13182
13298
  }
13183
- return (0, import_rxjs11.from)(
13299
+ return (0, import_rxjs9.from)(
13184
13300
  withActorSpan("smelter", inputOrBatch.type, async () => {
13185
13301
  const ok = await this.safeProcessEvent(inputOrBatch);
13186
13302
  if (isWorkItem(inputOrBatch)) this.noteWorkDone(1);
@@ -13339,6 +13455,23 @@ var Smelter = class _Smelter {
13339
13455
  return null;
13340
13456
  }
13341
13457
  }
13458
+ /**
13459
+ * Read a resource's current entity types from the materialized view — the
13460
+ * authoritative source, updated before the EventBus fires to consumers — so
13461
+ * its vectors carry the discriminator `searchResources` filters on (e.g.
13462
+ * exclude `['Question']`). One read on every embed path, mirroring how the
13463
+ * smelter already reads current content and annotations; a failed read
13464
+ * propagates to the pipeline's per-resource error handler (reconcile heals),
13465
+ * rather than silently stamping `[]` and letting the resource leak into recall.
13466
+ */
13467
+ async resolveEntityTypes(resourceId10) {
13468
+ const { resource } = await busRequest(
13469
+ this.bus,
13470
+ "browse:resource-requested",
13471
+ { resourceId: resourceId10 }
13472
+ );
13473
+ return getResourceEntityTypes(resource);
13474
+ }
13342
13475
  async embedResource(event, logMessage) {
13343
13476
  const rid = event.resourceId;
13344
13477
  if (!rid) return;
@@ -13346,13 +13479,14 @@ var Smelter = class _Smelter {
13346
13479
  if (!fetched) return;
13347
13480
  const chunks = chunkText(fetched.text, this.chunkingConfig);
13348
13481
  if (chunks.length === 0) return;
13482
+ const entityTypes = await this.resolveEntityTypes(rid);
13349
13483
  const embeddings = await this.embeddingProvider.embedBatch(chunks);
13350
13484
  const embeddingChunks = chunks.map((t, i) => ({
13351
13485
  chunkIndex: i,
13352
13486
  text: t,
13353
13487
  embedding: embeddings[i]
13354
13488
  }));
13355
- await this.vectorStore.upsertResourceVectors(resourceId(rid), embeddingChunks, fetched.checksum);
13489
+ await this.vectorStore.upsertResourceVectors(resourceId(rid), embeddingChunks, fetched.checksum, entityTypes);
13356
13490
  this.logger.info(logMessage, { resourceId: rid, chunks: chunks.length });
13357
13491
  }
13358
13492
  async handleResourceArchived(event) {
@@ -13403,19 +13537,20 @@ var Smelter = class _Smelter {
13403
13537
  if (!fetched) continue;
13404
13538
  const chunks = chunkText(fetched.text, this.chunkingConfig);
13405
13539
  if (chunks.length === 0) continue;
13406
- resourceData.push({ rid: resourceId(rid), chunks, checksum: fetched.checksum });
13540
+ const entityTypes = await this.resolveEntityTypes(rid);
13541
+ resourceData.push({ rid: resourceId(rid), chunks, checksum: fetched.checksum, entityTypes });
13407
13542
  allChunks.push(...chunks);
13408
13543
  }
13409
13544
  if (allChunks.length === 0) return events.length;
13410
13545
  const allEmbeddings = await this.embeddingProvider.embedBatch(allChunks);
13411
13546
  let offset = 0;
13412
- for (const { rid, chunks, checksum } of resourceData) {
13547
+ for (const { rid, chunks, checksum, entityTypes } of resourceData) {
13413
13548
  const embeddingChunks = chunks.map((t, i) => ({
13414
13549
  chunkIndex: i,
13415
13550
  text: t,
13416
13551
  embedding: allEmbeddings[offset + i]
13417
13552
  }));
13418
- await this.vectorStore.upsertResourceVectors(rid, embeddingChunks, checksum);
13553
+ await this.vectorStore.upsertResourceVectors(rid, embeddingChunks, checksum, entityTypes);
13419
13554
  this.logger.info("Batch-indexed resource", { resourceId: String(rid), chunks: chunks.length });
13420
13555
  offset += chunks.length;
13421
13556
  }
@@ -13522,9 +13657,7 @@ var Smelter = class _Smelter {
13522
13657
  const { annotations } = await busRequest(
13523
13658
  this.bus,
13524
13659
  "browse:annotations-requested",
13525
- { resourceId: rid },
13526
- "browse:annotations-result",
13527
- "browse:annotations-failed"
13660
+ { resourceId: rid }
13528
13661
  );
13529
13662
  for (const annotation of annotations) {
13530
13663
  const exactText = getExactText(getTargetSelector(annotation.target));
@@ -13582,9 +13715,7 @@ var Smelter = class _Smelter {
13582
13715
  const page = await busRequest(
13583
13716
  this.bus,
13584
13717
  "browse:resources-requested",
13585
- { archived: false, offset: all.length, limit: _Smelter.RECONCILE_PAGE_SIZE },
13586
- "browse:resources-result",
13587
- "browse:resources-failed"
13718
+ { archived: false, offset: all.length, limit: _Smelter.RECONCILE_PAGE_SIZE }
13588
13719
  );
13589
13720
  all.push(...page.resources);
13590
13721
  if (page.resources.length === 0 || all.length >= page.total) return all;
@@ -13593,8 +13724,8 @@ var Smelter = class _Smelter {
13593
13724
  };
13594
13725
 
13595
13726
  // src/smelter-actor-state-unit.ts
13596
- var import_rxjs12 = __toESM(require_cjs());
13597
- var import_operators11 = __toESM(require_operators());
13727
+ var import_rxjs10 = __toESM(require_cjs());
13728
+ var import_operators9 = __toESM(require_operators());
13598
13729
  var SMELTER_CHANNELS = [
13599
13730
  "yield:created",
13600
13731
  "yield:updated",
@@ -13606,10 +13737,10 @@ var SMELTER_CHANNELS = [
13606
13737
  function createSmelterActorStateUnit(options) {
13607
13738
  const { bus } = options;
13608
13739
  let started = false;
13609
- const events$ = (0, import_rxjs12.merge)(
13740
+ const events$ = (0, import_rxjs10.merge)(
13610
13741
  ...SMELTER_CHANNELS.map(
13611
13742
  (channel) => bus.on$(channel).pipe(
13612
- (0, import_operators11.map)((payload) => ({
13743
+ (0, import_operators9.map)((payload) => ({
13613
13744
  type: channel,
13614
13745
  resourceId: payload.resourceId,
13615
13746
  payload
@@ -13812,11 +13943,28 @@ function collectContentRefs(streamData) {
13812
13943
  return refs;
13813
13944
  }
13814
13945
 
13815
- // src/exchange/replay.ts
13816
- var import_rxjs13 = __toESM(require_cjs());
13817
- var import_operators12 = __toESM(require_operators());
13946
+ // src/exchange/await-reply.ts
13947
+ var import_rxjs11 = __toESM(require_cjs());
13948
+ var import_operators10 = __toESM(require_operators());
13949
+ function awaitReply(eventBus, command, payload, okChannel, failedChannel, timeoutMs) {
13950
+ const result$ = (0, import_rxjs11.race)(
13951
+ eventBus.get(okChannel).pipe((0, import_operators10.map)(() => void 0)),
13952
+ eventBus.get(failedChannel).pipe(
13953
+ (0, import_operators10.map)((e) => {
13954
+ throw new Error(e.message ?? `${command} failed`);
13955
+ })
13956
+ ),
13957
+ (0, import_rxjs11.timer)(timeoutMs).pipe(
13958
+ (0, import_operators10.map)(() => {
13959
+ throw new Error(`Timeout waiting for ${okChannel}`);
13960
+ })
13961
+ )
13962
+ );
13963
+ eventBus.get(command).next(payload);
13964
+ return (0, import_rxjs11.firstValueFrom)(result$);
13965
+ }
13818
13966
  var REPLAY_TIMEOUT_MS = 3e4;
13819
- async function replayEventStream(jsonl, eventBus, resolveBlob, contentStore, logger) {
13967
+ async function replayEventStream(jsonl, eventBus, eventStore, resolveBlob, contentStore, logger) {
13820
13968
  const lines = jsonl.trim().split("\n").filter((l) => l.length > 0);
13821
13969
  const storedEvents = lines.map((line) => JSON.parse(line));
13822
13970
  const stats = {
@@ -13826,12 +13974,12 @@ async function replayEventStream(jsonl, eventBus, resolveBlob, contentStore, log
13826
13974
  entityTypesAdded: 0
13827
13975
  };
13828
13976
  for (const stored of storedEvents) {
13829
- await replayEvent(stored, eventBus, resolveBlob, contentStore, stats, logger);
13977
+ await replayEvent(stored, eventBus, eventStore, resolveBlob, contentStore, stats, logger);
13830
13978
  stats.eventsReplayed++;
13831
13979
  }
13832
13980
  return { stats };
13833
13981
  }
13834
- async function replayEvent(event, eventBus, resolveBlob, contentStore, stats, logger) {
13982
+ async function replayEvent(event, eventBus, eventStore, resolveBlob, contentStore, stats, logger) {
13835
13983
  switch (event.type) {
13836
13984
  case "frame:entity-type-added":
13837
13985
  await replayEntityTypeAdded(event, eventBus, logger);
@@ -13859,7 +14007,7 @@ async function replayEvent(event, eventBus, resolveBlob, contentStore, stats, lo
13859
14007
  break;
13860
14008
  case "mark:entity-tag-added":
13861
14009
  case "mark:entity-tag-removed":
13862
- await replayEntityTagChange(event, eventBus, logger);
14010
+ await replayEntityTagChange(event, eventStore, logger);
13863
14011
  break;
13864
14012
  // Job events are transient — skip during replay
13865
14013
  case "job:started":
@@ -13878,20 +14026,12 @@ async function replayEvent(event, eventBus, resolveBlob, contentStore, stats, lo
13878
14026
  }
13879
14027
  }
13880
14028
  async function replayEntityTypeAdded(event, eventBus, logger) {
13881
- const result$ = (0, import_rxjs13.race)(
13882
- eventBus.get("frame:entity-type-added").pipe((0, import_operators12.map)(() => "ok")),
13883
- eventBus.get("frame:entity-type-add-failed").pipe((0, import_operators12.map)((e) => {
13884
- throw new Error(e.message);
13885
- })),
13886
- (0, import_rxjs13.timer)(REPLAY_TIMEOUT_MS).pipe((0, import_operators12.map)(() => {
13887
- throw new Error("Timeout waiting for frame:entity-type-added");
13888
- }))
14029
+ await busRequest(
14030
+ asBusRequestPrimitive(eventBus),
14031
+ "frame:add-entity-type",
14032
+ { tag: event.payload.entityType, _userId: event.userId },
14033
+ REPLAY_TIMEOUT_MS
13889
14034
  );
13890
- eventBus.get("frame:add-entity-type").next({
13891
- tag: event.payload.entityType,
13892
- _userId: event.userId
13893
- });
13894
- await (0, import_rxjs13.firstValueFrom)(result$);
13895
14035
  logger?.debug("Replayed entitytype.added", { entityType: event.payload.entityType });
13896
14036
  }
13897
14037
  async function replayResourceCreated(event, eventBus, resolveBlob, contentStore, logger) {
@@ -13903,84 +14043,70 @@ async function replayResourceCreated(event, eventBus, resolveBlob, contentStore,
13903
14043
  const base = baseMediaType(payload.format);
13904
14044
  const resolvedUri = payload.storageUri || deriveStorageUri(payload.name, isSupportedMediaType(base) ? base : "application/octet-stream");
13905
14045
  const stored = await contentStore.store(blob, resolvedUri);
13906
- const result$ = (0, import_rxjs13.race)(
13907
- eventBus.get("yield:create-ok").pipe((0, import_operators12.map)((r) => r)),
13908
- eventBus.get("yield:create-failed").pipe((0, import_operators12.map)((e) => {
13909
- throw new Error(e.message);
13910
- })),
13911
- (0, import_rxjs13.timer)(REPLAY_TIMEOUT_MS).pipe((0, import_operators12.map)(() => {
13912
- throw new Error("Timeout waiting for yield:create-ok");
13913
- }))
14046
+ await busRequest(
14047
+ asBusRequestPrimitive(eventBus),
14048
+ "yield:create",
14049
+ {
14050
+ name: payload.name,
14051
+ storageUri: resolvedUri,
14052
+ contentChecksum: stored.checksum,
14053
+ byteSize: stored.byteSize,
14054
+ format: payload.format,
14055
+ _userId: event.userId,
14056
+ language: payload.language,
14057
+ entityTypes: payload.entityTypes,
14058
+ isDraft: payload.isDraft,
14059
+ generatedFrom: payload.generatedFrom,
14060
+ generationPrompt: payload.generationPrompt
14061
+ },
14062
+ REPLAY_TIMEOUT_MS
13914
14063
  );
13915
- eventBus.get("yield:create").next({
13916
- name: payload.name,
13917
- storageUri: resolvedUri,
13918
- contentChecksum: stored.checksum,
13919
- byteSize: stored.byteSize,
13920
- format: payload.format,
13921
- _userId: event.userId,
13922
- language: payload.language,
13923
- entityTypes: payload.entityTypes,
13924
- isDraft: payload.isDraft,
13925
- generatedFrom: payload.generatedFrom,
13926
- generationPrompt: payload.generationPrompt
13927
- });
13928
- await (0, import_rxjs13.firstValueFrom)(result$);
13929
14064
  logger?.debug("Replayed resource.created", { name: payload.name });
13930
14065
  }
13931
14066
  async function replayAnnotationAdded(event, eventBus, logger) {
13932
- const result$ = (0, import_rxjs13.race)(
13933
- eventBus.get("mark:create-ok").pipe((0, import_operators12.map)(() => "ok")),
13934
- eventBus.get("mark:create-failed").pipe((0, import_operators12.map)((e) => {
13935
- throw new Error(e.message);
13936
- })),
13937
- (0, import_rxjs13.timer)(REPLAY_TIMEOUT_MS).pipe((0, import_operators12.map)(() => {
13938
- throw new Error("Timeout waiting for mark:create-ok");
13939
- }))
14067
+ await awaitReply(
14068
+ eventBus,
14069
+ "mark:create",
14070
+ {
14071
+ annotation: event.payload.annotation,
14072
+ _userId: event.userId,
14073
+ resourceId: event.resourceId
14074
+ },
14075
+ "mark:create-ok",
14076
+ "mark:create-failed",
14077
+ REPLAY_TIMEOUT_MS
13940
14078
  );
13941
- eventBus.get("mark:create").next({
13942
- annotation: event.payload.annotation,
13943
- _userId: event.userId,
13944
- resourceId: event.resourceId
13945
- });
13946
- await (0, import_rxjs13.firstValueFrom)(result$);
13947
14079
  logger?.debug("Replayed annotation.added", { annotationId: event.payload.annotation.id });
13948
14080
  }
13949
14081
  async function replayAnnotationBodyUpdated(event, eventBus, logger) {
13950
- const result$ = (0, import_rxjs13.race)(
13951
- eventBus.get("mark:body-updated").pipe((0, import_operators12.map)(() => "ok")),
13952
- eventBus.get("mark:body-update-failed").pipe((0, import_operators12.map)((e) => {
13953
- throw new Error(e.message);
13954
- })),
13955
- (0, import_rxjs13.timer)(REPLAY_TIMEOUT_MS).pipe((0, import_operators12.map)(() => {
13956
- throw new Error("Timeout waiting for mark:body-updated");
13957
- }))
14082
+ await awaitReply(
14083
+ eventBus,
14084
+ "mark:update-body",
14085
+ {
14086
+ annotationId: event.payload.annotationId,
14087
+ _userId: event.userId,
14088
+ resourceId: event.resourceId,
14089
+ operations: event.payload.operations
14090
+ },
14091
+ "mark:body-updated",
14092
+ "mark:body-update-failed",
14093
+ REPLAY_TIMEOUT_MS
13958
14094
  );
13959
- eventBus.get("mark:update-body").next({
13960
- annotationId: event.payload.annotationId,
13961
- _userId: event.userId,
13962
- resourceId: event.resourceId,
13963
- operations: event.payload.operations
13964
- });
13965
- await (0, import_rxjs13.firstValueFrom)(result$);
13966
14095
  logger?.debug("Replayed annotation.body.updated", { annotationId: event.payload.annotationId });
13967
14096
  }
13968
14097
  async function replayAnnotationRemoved(event, eventBus, logger) {
13969
- const result$ = (0, import_rxjs13.race)(
13970
- eventBus.get("mark:delete-ok").pipe((0, import_operators12.map)(() => "ok")),
13971
- eventBus.get("mark:delete-failed").pipe((0, import_operators12.map)((e) => {
13972
- throw new Error(e.message);
13973
- })),
13974
- (0, import_rxjs13.timer)(REPLAY_TIMEOUT_MS).pipe((0, import_operators12.map)(() => {
13975
- throw new Error("Timeout waiting for mark:delete-ok");
13976
- }))
14098
+ await awaitReply(
14099
+ eventBus,
14100
+ "mark:delete",
14101
+ {
14102
+ annotationId: event.payload.annotationId,
14103
+ _userId: event.userId,
14104
+ resourceId: event.resourceId
14105
+ },
14106
+ "mark:delete-ok",
14107
+ "mark:delete-failed",
14108
+ REPLAY_TIMEOUT_MS
13977
14109
  );
13978
- eventBus.get("mark:delete").next({
13979
- annotationId: event.payload.annotationId,
13980
- _userId: event.userId,
13981
- resourceId: event.resourceId
13982
- });
13983
- await (0, import_rxjs13.firstValueFrom)(result$);
13984
14110
  logger?.debug("Replayed annotation.removed", { annotationId: event.payload.annotationId });
13985
14111
  }
13986
14112
  async function replayResourceArchived(event, eventBus, logger) {
@@ -13997,25 +14123,18 @@ async function replayResourceUnarchived(event, eventBus, logger) {
13997
14123
  });
13998
14124
  logger?.debug("Replayed resource.unarchived", { resourceId: event.resourceId });
13999
14125
  }
14000
- async function replayEntityTagChange(event, eventBus, logger) {
14001
- const resourceId10 = event.resourceId;
14002
- const entityType2 = event.payload.entityType;
14126
+ async function replayEntityTagChange(event, eventStore, logger) {
14127
+ const base = {
14128
+ resourceId: event.resourceId,
14129
+ userId: event.userId,
14130
+ version: event.version
14131
+ };
14003
14132
  if (event.type === "mark:entity-tag-added") {
14004
- eventBus.get("mark:update-entity-types").next({
14005
- resourceId: resourceId10,
14006
- _userId: event.userId,
14007
- currentEntityTypes: [],
14008
- updatedEntityTypes: [entityType2]
14009
- });
14133
+ await eventStore.appendEvent({ ...base, type: "mark:entity-tag-added", payload: event.payload });
14010
14134
  } else {
14011
- eventBus.get("mark:update-entity-types").next({
14012
- resourceId: resourceId10,
14013
- _userId: event.userId,
14014
- currentEntityTypes: [entityType2],
14015
- updatedEntityTypes: []
14016
- });
14135
+ await eventStore.appendEvent({ ...base, type: "mark:entity-tag-removed", payload: event.payload });
14017
14136
  }
14018
- logger?.debug("Replayed entity tag change", { type: event.type, entityType: entityType2 });
14137
+ logger?.debug("Replayed entity tag change", { type: event.type, entityType: event.payload.entityType });
14019
14138
  }
14020
14139
 
14021
14140
  // src/exchange/backup-importer.ts
@@ -14034,7 +14153,7 @@ function buildBlobResolver(entries) {
14034
14153
  };
14035
14154
  }
14036
14155
  async function importBackup(archive, options) {
14037
- const { eventBus, contentStore, logger } = options;
14156
+ const { eventBus, eventStore, contentStore, logger } = options;
14038
14157
  const entries = /* @__PURE__ */ new Map();
14039
14158
  for await (const entry of readTarGz(archive)) {
14040
14159
  entries.set(entry.name, entry.data);
@@ -14062,6 +14181,7 @@ async function importBackup(archive, options) {
14062
14181
  const result = await replayEventStream(
14063
14182
  systemData.toString("utf8"),
14064
14183
  eventBus,
14184
+ eventStore,
14065
14185
  resolveBlob,
14066
14186
  contentStore,
14067
14187
  logger
@@ -14078,6 +14198,7 @@ async function importBackup(archive, options) {
14078
14198
  const result = await replayEventStream(
14079
14199
  eventData.toString("utf8"),
14080
14200
  eventBus,
14201
+ eventStore,
14081
14202
  resolveBlob,
14082
14203
  contentStore,
14083
14204
  logger
@@ -14263,10 +14384,6 @@ function collectContentRefsFromResource(resource, refs) {
14263
14384
  refs.set(resource.storageUri, mediaType);
14264
14385
  }
14265
14386
  }
14266
-
14267
- // src/exchange/linked-data-importer.ts
14268
- var import_rxjs14 = __toESM(require_cjs());
14269
- var import_operators13 = __toESM(require_operators());
14270
14387
  var IMPORT_TIMEOUT_MS = 3e4;
14271
14388
  function stripUriToId(uri) {
14272
14389
  if (!uri.includes("/")) return uri;
@@ -14368,20 +14485,12 @@ async function importLinkedData(archive, options) {
14368
14485
  };
14369
14486
  }
14370
14487
  async function addEntityType(entityType2, userId3, eventBus, logger) {
14371
- const result$ = (0, import_rxjs14.race)(
14372
- eventBus.get("frame:entity-type-added").pipe((0, import_operators13.map)(() => "ok")),
14373
- eventBus.get("frame:entity-type-add-failed").pipe((0, import_operators13.map)((e) => {
14374
- throw new Error(e.message);
14375
- })),
14376
- (0, import_rxjs14.timer)(IMPORT_TIMEOUT_MS).pipe((0, import_operators13.map)(() => {
14377
- throw new Error("Timeout waiting for frame:entity-type-added");
14378
- }))
14488
+ await busRequest(
14489
+ asBusRequestPrimitive(eventBus),
14490
+ "frame:add-entity-type",
14491
+ { tag: entityType2, _userId: userId3 },
14492
+ IMPORT_TIMEOUT_MS
14379
14493
  );
14380
- eventBus.get("frame:add-entity-type").next({
14381
- tag: entityType2,
14382
- _userId: userId3
14383
- });
14384
- await (0, import_rxjs14.firstValueFrom)(result$);
14385
14494
  logger?.debug("Added entity type", { entityType: entityType2 });
14386
14495
  }
14387
14496
  async function importResource(doc, userId3, eventBus, contentStore, resolveBlob, logger) {
@@ -14408,27 +14517,22 @@ async function importResource(doc, userId3, eventBus, contentStore, resolveBlob,
14408
14517
  const base = baseMediaType(format);
14409
14518
  const resolvedUri = deriveStorageUri(name, isSupportedMediaType(base) ? base : "application/octet-stream");
14410
14519
  const stored = await contentStore.store(blob, resolvedUri);
14411
- const createResult$ = (0, import_rxjs14.race)(
14412
- eventBus.get("yield:create-ok").pipe((0, import_operators13.map)((r) => r)),
14413
- eventBus.get("yield:create-failed").pipe((0, import_operators13.map)((e) => {
14414
- throw new Error(e.message);
14415
- })),
14416
- (0, import_rxjs14.timer)(IMPORT_TIMEOUT_MS).pipe((0, import_operators13.map)(() => {
14417
- throw new Error("Timeout waiting for yield:create-ok");
14418
- }))
14520
+ const { resourceId: createdId } = await busRequest(
14521
+ asBusRequestPrimitive(eventBus),
14522
+ "yield:create",
14523
+ {
14524
+ name,
14525
+ storageUri: resolvedUri,
14526
+ contentChecksum: stored.checksum,
14527
+ byteSize: stored.byteSize,
14528
+ format,
14529
+ _userId: userId3,
14530
+ language,
14531
+ entityTypes: entityTypes ?? []
14532
+ },
14533
+ IMPORT_TIMEOUT_MS
14419
14534
  );
14420
- eventBus.get("yield:create").next({
14421
- name,
14422
- storageUri: resolvedUri,
14423
- contentChecksum: stored.checksum,
14424
- byteSize: stored.byteSize,
14425
- format,
14426
- _userId: userId3,
14427
- language,
14428
- entityTypes: entityTypes ?? []
14429
- });
14430
- const created = await (0, import_rxjs14.firstValueFrom)(createResult$);
14431
- const resourceId10 = resourceId(created.resourceId);
14535
+ const resourceId10 = resourceId(createdId);
14432
14536
  logger?.debug("Created resource from JSON-LD", { name, resourceId: resourceId10 });
14433
14537
  let annotationsCreated = 0;
14434
14538
  if (annotations && annotations.length > 0) {
@@ -14440,21 +14544,18 @@ async function importResource(doc, userId3, eventBus, contentStore, resolveBlob,
14440
14544
  return { annotationsCreated };
14441
14545
  }
14442
14546
  async function createAnnotation(annotation, resourceId10, userId3, eventBus, logger) {
14443
- const result$ = (0, import_rxjs14.race)(
14444
- eventBus.get("mark:create-ok").pipe((0, import_operators13.map)(() => "ok")),
14445
- eventBus.get("mark:create-failed").pipe((0, import_operators13.map)((e) => {
14446
- throw new Error(e.message);
14447
- })),
14448
- (0, import_rxjs14.timer)(IMPORT_TIMEOUT_MS).pipe((0, import_operators13.map)(() => {
14449
- throw new Error("Timeout waiting for mark:create-ok");
14450
- }))
14547
+ await awaitReply(
14548
+ eventBus,
14549
+ "mark:create",
14550
+ {
14551
+ annotation: dehydrateAnnotation(annotation),
14552
+ _userId: userId3,
14553
+ resourceId: resourceId10
14554
+ },
14555
+ "mark:create-ok",
14556
+ "mark:create-failed",
14557
+ IMPORT_TIMEOUT_MS
14451
14558
  );
14452
- eventBus.get("mark:create").next({
14453
- annotation: dehydrateAnnotation(annotation),
14454
- _userId: userId3,
14455
- resourceId: resourceId10
14456
- });
14457
- await (0, import_rxjs14.firstValueFrom)(result$);
14458
14559
  logger?.debug("Created annotation", { annotationId: annotation.id });
14459
14560
  }
14460
14561
  var AnnotationOperations = class {