stratagate-dsh 0.2.33 → 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/dist/index.js CHANGED
@@ -3254,6 +3254,11 @@ var StrataGate = class _StrataGate {
3254
3254
  }
3255
3255
  return hits;
3256
3256
  }
3257
+ /**
3258
+ * Return decayed block views. Passing a threadId limits the result to that
3259
+ * conversation; omitting it intentionally returns every thread in this
3260
+ * StrataGate namespace (never another namespace).
3261
+ */
3257
3262
  getBlockContext(threadId) {
3258
3263
  const blocks = threadId === void 0 ? this.blocks : this.blocks.filter((block) => block.threadId === threadId);
3259
3264
  return blocks.map((block) => {
@@ -3474,6 +3479,16 @@ var StrataGate = class _StrataGate {
3474
3479
  }
3475
3480
  async searchGraphNodes(query, limit = 8) {
3476
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
+ ];
3477
3492
  const ranked = bm25Rank(candidates, query, (node) => weightedSearchTokens([
3478
3493
  [node.name, 6],
3479
3494
  [node.aliases.join(" "), 5],
@@ -3482,9 +3497,28 @@ var StrataGate = class _StrataGate {
3482
3497
  [node.currentState, 4],
3483
3498
  [node.facts.map((fact) => `${fact.key} ${Array.isArray(fact.value) ? fact.value.join(" ") : fact.value}`).join(" "), 4],
3484
3499
  [this.graphEdges.filter((edge) => edge.fromNodeId === node.id || edge.toNodeId === node.id).map(({ relation }) => relation).join(" "), 3]
3485
- ])).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)));
3486
3509
  if (searchTokens(query).length > 0 && ranked.length === 0) return [];
3487
- 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
+ });
3488
3522
  }
3489
3523
  requireGraphProjectionJob(id) {
3490
3524
  const job = this.graphProjectionJobs.get(id);
@@ -4713,7 +4747,7 @@ var StrataGateRuntime = class {
4713
4747
  return this.batch(session, results.map(({ event }) => ({
4714
4748
  ref: `event:${event.id}`,
4715
4749
  target: { eventIds: [event.id], elementIds: [] }
4716
- })), results);
4750
+ })), results.map(({ event, score }) => compactEvent(event, score)));
4717
4751
  }
4718
4752
  async searchElements(session, query, options = {}) {
4719
4753
  await this.flush();
@@ -4721,23 +4755,53 @@ var StrataGateRuntime = class {
4721
4755
  return this.batch(session, results.map((result) => ({
4722
4756
  ref: `element:${result.elementId}:fact:${result.id}`,
4723
4757
  target: { eventIds: [], elementIds: [result.elementId] }
4724
- })), 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
+ })));
4725
4770
  }
4726
- async searchRaw(session, query, limit) {
4771
+ async searchRaw(session, query, limit, scope = "namespace") {
4727
4772
  await this.flush();
4728
- const results = (await this.space(session)).searchRawMemory(query, limit);
4773
+ const memory = await this.space(session);
4774
+ const threadId = String(session.id);
4775
+ const results = memory.searchRawMemory(query, scope === "namespace" ? limit : Number.MAX_SAFE_INTEGER).filter((result) => scope === "namespace" || result.message.threadId === threadId || result.message.threadId === void 0).slice(0, limit);
4729
4776
  return this.batch(session, results.map((result, index) => ({
4730
4777
  ref: `raw:${result.blockId}:${result.message.id}:${index}`,
4731
4778
  target: { eventIds: [], elementIds: [] }
4732
- })), results);
4779
+ })), results.map(compactRawHit), { scope, namespace: this.namespaceFor(session), threadId });
4733
4780
  }
4734
- async blocks(session) {
4781
+ async blocks(session, scope = "session") {
4735
4782
  await this.flush();
4736
- const results = (await this.space(session)).getBlockContext(String(session.id));
4783
+ const memory = await this.space(session);
4784
+ const threadId = String(session.id);
4785
+ const snapshot = memory.exportSnapshot();
4786
+ const namespaceBlockCount = snapshot.blocks.length;
4787
+ const namespaceThreadIds = [...new Set(snapshot.blocks.map((block) => block.threadId).filter((value) => Boolean(value)))];
4788
+ const openTailCount = snapshot.openTail.filter((message) => scope === "namespace" || message.threadId === threadId || message.threadId === void 0).length;
4789
+ const results = scope === "namespace" ? memory.getBlockContext() : memory.getBlockContext().filter((block) => block.threadId === threadId || block.threadId === void 0);
4790
+ const emptyReason = results.length > 0 ? null : namespaceBlockCount === 0 ? openTailCount > 0 ? "open_tail_pending" : "no_blocks_in_namespace" : openTailCount > 0 ? "open_tail_pending" : "blocks_exist_in_other_threads";
4791
+ const status = {
4792
+ scope,
4793
+ namespace: this.namespaceFor(session),
4794
+ threadId,
4795
+ blockCount: results.length,
4796
+ namespaceBlockCount,
4797
+ namespaceThreadIds,
4798
+ openTailCount,
4799
+ emptyReason
4800
+ };
4737
4801
  return this.batch(session, results.map((result) => ({
4738
4802
  ref: `block:${result.id}:level:${result.level}`,
4739
4803
  target: { eventIds: [], elementIds: [] }
4740
- })), results);
4804
+ })), results, status);
4741
4805
  }
4742
4806
  async expandBlock(session, id, target) {
4743
4807
  await this.flush();
@@ -5220,7 +5284,7 @@ var StrataGateRuntime = class {
5220
5284
  return this.batch(session, results.map(({ node }) => ({
5221
5285
  ref: `graph-node:${node.id}`,
5222
5286
  target: { eventIds: node.sourceEventIds, elementIds: [] }
5223
- })), results);
5287
+ })), results.map(({ node, score, matchedFields, matchReason }) => compactGraphNode(node, score, matchedFields, matchReason)));
5224
5288
  }
5225
5289
  async expandGraphNode(session, id) {
5226
5290
  await this.flush();
@@ -5274,7 +5338,7 @@ var StrataGateRuntime = class {
5274
5338
  const responses = this.models.takeSuccessfulResponses();
5275
5339
  if (responses.length > 0) await memory.recordSuccessfulModelResponses(responses);
5276
5340
  }
5277
- batch(session, evidence, results) {
5341
+ batch(session, evidence, results, metadata = {}) {
5278
5342
  const id = `batch_${++this.batchSequence}`;
5279
5343
  const refs = new Map(evidence.map(({ ref, target }) => [ref, target]));
5280
5344
  const key = String(session.id);
@@ -5285,7 +5349,7 @@ var StrataGateRuntime = class {
5285
5349
  }
5286
5350
  sessionBatches.set(id, { id, refs, status: "unresolved" });
5287
5351
  this.latestBatchIds.set(key, id);
5288
- return { batchId: id, evidenceRefs: [...refs.keys()], results };
5352
+ return { batchId: id, evidenceRefs: [...refs.keys()], results, ...metadata };
5289
5353
  }
5290
5354
  requireBatch(session, batchId, operation) {
5291
5355
  const key = String(session.id);
@@ -5420,6 +5484,67 @@ function renderBlockSurfaceMessage(context) {
5420
5484
  context.content
5421
5485
  ].join("\n");
5422
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
+ }
5423
5548
  function currentBlockSurfaceMessages(session) {
5424
5549
  const blocks = /* @__PURE__ */ new Map();
5425
5550
  if (!session.surface?.nodes) return blocks;
@@ -5564,7 +5689,7 @@ function sessionOf(exec) {
5564
5689
  function registerMemoryTools(ctx, runtime) {
5565
5690
  ctx.tools.register(defineTool({
5566
5691
  name: "memory_search_events",
5567
- 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.",
5568
5693
  parameters: {
5569
5694
  query: { type: "string", required: true, description: "What historical decision, event, preference, or outcome to find." },
5570
5695
  limit: { type: "integer", description: "Maximum results, 1-20." },
@@ -5582,7 +5707,7 @@ function registerMemoryTools(ctx, runtime) {
5582
5707
  }));
5583
5708
  ctx.tools.register(defineTool({
5584
5709
  name: "memory_search_graph",
5585
- 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.",
5586
5711
  parameters: {
5587
5712
  query: { type: "string", required: true },
5588
5713
  limit: { type: "integer", description: "Maximum results, 1-20." }
@@ -5599,7 +5724,7 @@ function registerMemoryTools(ctx, runtime) {
5599
5724
  }));
5600
5725
  ctx.tools.register(defineTool({
5601
5726
  name: "memory_search_elements",
5602
- 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.",
5603
5728
  parameters: {
5604
5729
  query: { type: "string", required: true },
5605
5730
  limit: { type: "integer" },
@@ -5615,20 +5740,23 @@ function registerMemoryTools(ctx, runtime) {
5615
5740
  }));
5616
5741
  ctx.tools.register(defineTool({
5617
5742
  name: "memory_search_raw",
5618
- description: "Search verbatim archived messages when summarized memories are insufficient. 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.",
5619
5744
  parameters: {
5620
5745
  query: { type: "string", required: true },
5621
- limit: { type: "integer" }
5746
+ limit: { type: "integer" },
5747
+ scope: { type: "string", enum: ["namespace", "session"], description: "Search range. Defaults to namespace for compatibility with historical raw search behavior." }
5622
5748
  },
5623
5749
  output: jsonOutput,
5624
- execute: async (args, exec) => runtime.searchRaw(sessionOf(exec), args.query, args.limit)
5750
+ execute: async (args, exec) => runtime.searchRaw(sessionOf(exec), args.query, args.limit, args.scope)
5625
5751
  }));
5626
5752
  ctx.tools.register(defineTool({
5627
5753
  name: "memory_get_blocks",
5628
- description: "List decayed conversation-block summaries and their current detail levels. Returns block evidence refs and a batchId for assessment.",
5629
- parameters: {},
5754
+ description: "List decayed conversation-block summaries and their current detail levels. Defaults to the active session only; use scope=namespace to inspect every thread in the current namespace. The response always reports scope, namespace, threadId, counts, and a machine-readable emptyReason when no blocks match.",
5755
+ parameters: {
5756
+ scope: { type: "string", enum: ["session", "namespace"], description: "Query range. Defaults to session to preserve existing isolation behavior." }
5757
+ },
5630
5758
  output: jsonOutput,
5631
- execute: async (_args, exec) => runtime.blocks(sessionOf(exec))
5759
+ execute: async (args, exec) => runtime.blocks(sessionOf(exec), args.scope)
5632
5760
  }));
5633
5761
  ctx.tools.register(defineTool({
5634
5762
  name: "memory_expand_block",