stratagate-dsh 0.2.34 → 0.2.35

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/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.2.35 - 2026-08-27
4
+
5
+ - Return compact Event, Knowledge Graph, and raw-memory search cards while keeping full details in expand tools.
6
+ - Rename tool-facing ranking output to `rankScore` and document that it is not confidence or factual accuracy.
7
+ - Filter relation-only Knowledge Graph matches, report matched fields, and preserve distinct same-name entity types.
8
+
3
9
  ## 0.2.34 - 2026-08-27
4
10
 
5
11
  - Add explicit `session` and `namespace` scopes to block and raw-memory retrieval.
package/README.md CHANGED
@@ -110,6 +110,13 @@ blocks, and `open_tail_pending` means matching turns exist but have not sealed y
110
110
  so a raw hit's `blockId` can be followed by `memory_get_blocks(scope=namespace)`
111
111
  or `memory_expand_block` without an unexplained visibility mismatch.
112
112
 
113
+ Search responses use compact cards by default. Event cards keep `id`, `title`, `summary`, source time,
114
+ status/scope, `sourceBlockId`, `batchId`, and `evidenceRefs`; graph cards keep `id`, `name`, type,
115
+ aliases, current state, status, and explainable `matchedFields`/`matchReason`; raw cards keep the
116
+ message id, `blockId`, role, turn range, and a bounded excerpt. Narrative, quotes, source message lists,
117
+ full graph facts/edges, and nearby raw messages are available through the corresponding expand tools.
118
+ `rankScore` is a BM25/RRF ordering metric only—it is not a probability, confidence, or factual-accuracy score.
119
+
113
120
  Legacy Element tool names remain available only for compatibility with existing installations.
114
121
 
115
122
  The prompt protocol requires assessment before relying on retrieved evidence. Search does not strengthen a memory. Non-empty `memory_record_use` submissions accept only evidence adopted by a sufficient assessment of the selected batch and use the DSH tool call id as an idempotency receipt. `batch_id` may be omitted for compatibility in strictly sequential flows, where it selects the latest batch; parallel or interleaved retrievals must pass it explicitly. Assessment responses list rejected refs and their reasons.
package/dist/index.js CHANGED
@@ -3479,6 +3479,16 @@ var StrataGate = class _StrataGate {
3479
3479
  }
3480
3480
  async searchGraphNodes(query, limit = 8) {
3481
3481
  const candidates = this.graphNodes.filter((node) => node.status === "active" || node.status === "disputed");
3482
+ const queryTokens = [...new Set(searchTokens(query))];
3483
+ const fieldValues = (node) => [
3484
+ ["name", node.name],
3485
+ ["aliases", node.aliases.join(" ")],
3486
+ ["tags", (node.tags ?? []).join(" ")],
3487
+ ["type", node.type],
3488
+ ["currentState", node.currentState],
3489
+ ["facts", node.facts.map((fact) => `${fact.key} ${Array.isArray(fact.value) ? fact.value.join(" ") : fact.value}`).join(" ")],
3490
+ ["relations", this.graphEdges.filter((edge) => edge.fromNodeId === node.id || edge.toNodeId === node.id).map(({ relation }) => relation).join(" ")]
3491
+ ];
3482
3492
  const ranked = bm25Rank(candidates, query, (node) => weightedSearchTokens([
3483
3493
  [node.name, 6],
3484
3494
  [node.aliases.join(" "), 5],
@@ -3487,9 +3497,28 @@ var StrataGate = class _StrataGate {
3487
3497
  [node.currentState, 4],
3488
3498
  [node.facts.map((fact) => `${fact.key} ${Array.isArray(fact.value) ? fact.value.join(" ") : fact.value}`).join(" "), 4],
3489
3499
  [this.graphEdges.filter((edge) => edge.fromNodeId === node.id || edge.toNodeId === node.id).map(({ relation }) => relation).join(" "), 3]
3490
- ])).slice(0, Math.max(1, Math.min(20, limit)));
3500
+ ])).filter(({ item }) => {
3501
+ const fields = fieldValues(item);
3502
+ const matches = fields.filter(([, value]) => {
3503
+ const haystack = new Set(searchTokens(value));
3504
+ return queryTokens.some((token) => haystack.has(token));
3505
+ }).map(([field]) => field);
3506
+ if (!matches.some((field) => field !== "relations")) return false;
3507
+ return matches.some((field) => field !== "relations");
3508
+ }).slice(0, Math.max(1, Math.min(20, limit)));
3491
3509
  if (searchTokens(query).length > 0 && ranked.length === 0) return [];
3492
- return ranked.map(({ item: node, score }) => ({ node, score }));
3510
+ return ranked.map(({ item: node, score }) => {
3511
+ const matchedFields = fieldValues(node).filter(([, value]) => {
3512
+ const haystack = new Set(searchTokens(value));
3513
+ return queryTokens.some((token) => haystack.has(token));
3514
+ }).map(([field]) => field);
3515
+ return {
3516
+ node,
3517
+ score,
3518
+ matchedFields,
3519
+ matchReason: `Lexical match in ${matchedFields.join(", ") || "indexed fields"}; score is ranking-only.`
3520
+ };
3521
+ });
3493
3522
  }
3494
3523
  requireGraphProjectionJob(id) {
3495
3524
  const job = this.graphProjectionJobs.get(id);
@@ -4718,7 +4747,7 @@ var StrataGateRuntime = class {
4718
4747
  return this.batch(session, results.map(({ event }) => ({
4719
4748
  ref: `event:${event.id}`,
4720
4749
  target: { eventIds: [event.id], elementIds: [] }
4721
- })), results);
4750
+ })), results.map(({ event, score }) => compactEvent(event, score)));
4722
4751
  }
4723
4752
  async searchElements(session, query, options = {}) {
4724
4753
  await this.flush();
@@ -4726,7 +4755,18 @@ var StrataGateRuntime = class {
4726
4755
  return this.batch(session, results.map((result) => ({
4727
4756
  ref: `element:${result.elementId}:fact:${result.id}`,
4728
4757
  target: { eventIds: [], elementIds: [result.elementId] }
4729
- })), results);
4758
+ })), results.map((result) => ({
4759
+ id: result.id,
4760
+ elementId: result.elementId,
4761
+ name: result.name,
4762
+ type: result.type,
4763
+ factKey: result.fact.key,
4764
+ value: Array.isArray(result.fact.value) ? result.fact.value.join(", ") : result.fact.value,
4765
+ validFrom: result.fact.validFrom,
4766
+ validTo: result.fact.validTo,
4767
+ rankScore: result.score,
4768
+ scoreMeaning: "Ranking-only BM25/RRF score; not confidence or factual accuracy."
4769
+ })));
4730
4770
  }
4731
4771
  async searchRaw(session, query, limit, scope = "namespace") {
4732
4772
  await this.flush();
@@ -4736,7 +4776,7 @@ var StrataGateRuntime = class {
4736
4776
  return this.batch(session, results.map((result, index) => ({
4737
4777
  ref: `raw:${result.blockId}:${result.message.id}:${index}`,
4738
4778
  target: { eventIds: [], elementIds: [] }
4739
- })), results, { scope, namespace: this.namespaceFor(session), threadId });
4779
+ })), results.map(compactRawHit), { scope, namespace: this.namespaceFor(session), threadId });
4740
4780
  }
4741
4781
  async blocks(session, scope = "session") {
4742
4782
  await this.flush();
@@ -5244,7 +5284,7 @@ var StrataGateRuntime = class {
5244
5284
  return this.batch(session, results.map(({ node }) => ({
5245
5285
  ref: `graph-node:${node.id}`,
5246
5286
  target: { eventIds: node.sourceEventIds, elementIds: [] }
5247
- })), results);
5287
+ })), results.map(({ node, score, matchedFields, matchReason }) => compactGraphNode(node, score, matchedFields, matchReason)));
5248
5288
  }
5249
5289
  async expandGraphNode(session, id) {
5250
5290
  await this.flush();
@@ -5444,6 +5484,67 @@ function renderBlockSurfaceMessage(context) {
5444
5484
  context.content
5445
5485
  ].join("\n");
5446
5486
  }
5487
+ function compactTemporal(event) {
5488
+ const temporal = event.temporal;
5489
+ return Object.fromEntries(Object.entries({
5490
+ mentionedAt: temporal.mentionedAt,
5491
+ happenedStart: temporal.happenedStart,
5492
+ happenedEnd: temporal.happenedEnd,
5493
+ precision: temporal.precision,
5494
+ status: temporal.status,
5495
+ eventType: temporal.eventType
5496
+ }).filter(([, value]) => value !== void 0));
5497
+ }
5498
+ function compactText2(value, limit = 800) {
5499
+ return value.replace(/\s+/gu, " ").trim().slice(0, limit);
5500
+ }
5501
+ function compactEvent(event, score) {
5502
+ return {
5503
+ id: event.id,
5504
+ title: compactText2(event.title, 240),
5505
+ summary: compactText2(event.summary),
5506
+ sourceTime: event.temporal.happenedStart ?? event.temporal.mentionedAt ?? event.createdAt,
5507
+ temporal: compactTemporal(event),
5508
+ sourceBlockId: event.sourceBlockId,
5509
+ status: event.status,
5510
+ scope: event.scope,
5511
+ criticality: event.criticality,
5512
+ rankScore: score,
5513
+ scoreMeaning: "Ranking-only BM25/RRF score; not confidence, probability, or factual accuracy."
5514
+ };
5515
+ }
5516
+ function compactGraphNode(node, score, matchedFields, matchReason) {
5517
+ return {
5518
+ id: node.id,
5519
+ name: node.name,
5520
+ type: node.type,
5521
+ aliases: node.aliases.map((alias) => compactText2(alias, 160)),
5522
+ currentState: compactText2(node.currentState, 500),
5523
+ status: node.status,
5524
+ rankScore: score,
5525
+ ...matchedFields ? { matchedFields } : {},
5526
+ ...matchReason ? { matchReason } : {},
5527
+ scoreMeaning: "Ranking-only BM25/RRF score; not confidence, probability, or factual accuracy."
5528
+ };
5529
+ }
5530
+ function compactRawHit(result) {
5531
+ const message = {
5532
+ id: result.message.id,
5533
+ role: result.message.role,
5534
+ content: compactText2(result.message.content, 500),
5535
+ createdAt: result.message.createdAt,
5536
+ ...result.message.threadId ? { threadId: result.message.threadId } : {}
5537
+ };
5538
+ return {
5539
+ id: result.message.id,
5540
+ blockId: result.blockId,
5541
+ turnRange: result.turnRange,
5542
+ message,
5543
+ sourceTime: result.message.createdAt,
5544
+ ...result.message.threadId ? { threadId: result.message.threadId } : {},
5545
+ detailHint: "Use memory_expand_block with blockId for complete block/source details."
5546
+ };
5547
+ }
5447
5548
  function currentBlockSurfaceMessages(session) {
5448
5549
  const blocks = /* @__PURE__ */ new Map();
5449
5550
  if (!session.surface?.nodes) return blocks;
@@ -5588,7 +5689,7 @@ function sessionOf(exec) {
5588
5689
  function registerMemoryTools(ctx, runtime) {
5589
5690
  ctx.tools.register(defineTool({
5590
5691
  name: "memory_search_events",
5591
- description: "Search durable StrataGate event memories. Returns a batchId, evidenceRefs, and ranked event cards. Pass that batchId to memory_assess before relying on its evidence.",
5692
+ description: "Search durable StrataGate event memories. Returns a compact batch of event cards (id, title, summary, time, and evidence refs); call memory_expand_event for narrative/quotes/source messages. rankScore is BM25/RRF ordering only, never confidence or factual accuracy. Pass batchId to memory_assess before relying on evidence.",
5592
5693
  parameters: {
5593
5694
  query: { type: "string", required: true, description: "What historical decision, event, preference, or outcome to find." },
5594
5695
  limit: { type: "integer", description: "Maximum results, 1-20." },
@@ -5606,7 +5707,7 @@ function registerMemoryTools(ctx, runtime) {
5606
5707
  }));
5607
5708
  ctx.tools.register(defineTool({
5608
5709
  name: "memory_search_graph",
5609
- description: "Search the current Event-backed Knowledge Graph for people, projects, organizations, tools, places, facts, and relations. Returns an independently assessable retrieval batch.",
5710
+ description: "Search the current Event-backed Knowledge Graph for people, projects, organizations, tools, places, facts, and relations. Returns compact node cards with matchedFields/matchReason; call memory_expand_graph_node for complete facts and edges. rankScore is BM25/RRF ordering only, never confidence or factual accuracy. Results are independently assessable.",
5610
5711
  parameters: {
5611
5712
  query: { type: "string", required: true },
5612
5713
  limit: { type: "integer", description: "Maximum results, 1-20." }
@@ -5623,7 +5724,7 @@ function registerMemoryTools(ctx, runtime) {
5623
5724
  }));
5624
5725
  ctx.tools.register(defineTool({
5625
5726
  name: "memory_search_elements",
5626
- description: "Deprecated compatibility search for legacy Element-card data. Prefer memory_search_graph.",
5727
+ description: "Deprecated compatibility search for legacy Element-card data. Returns compact fact hits; rankScore is BM25/RRF ordering only, never confidence or factual accuracy. Prefer memory_search_graph.",
5627
5728
  parameters: {
5628
5729
  query: { type: "string", required: true },
5629
5730
  limit: { type: "integer" },
@@ -5639,7 +5740,7 @@ function registerMemoryTools(ctx, runtime) {
5639
5740
  }));
5640
5741
  ctx.tools.register(defineTool({
5641
5742
  name: "memory_search_raw",
5642
- description: "Search verbatim archived messages when summarized memories are insufficient. By default searches the whole current namespace; use scope=session to restrict results to the active thread. Returns raw evidence refs and a batchId for assessment.",
5743
+ description: "Search archived messages when summarized memories are insufficient. Returns compact raw hits (message id, blockId, excerpt, role, and time); use memory_expand_block with blockId for complete source details. By default searches the whole current namespace; use scope=session for the active thread. Returns evidence refs and batchId for assessment.",
5643
5744
  parameters: {
5644
5745
  query: { type: "string", required: true },
5645
5746
  limit: { type: "integer" },