stratagate-dsh 0.3.5 → 0.3.6

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/plugin.js CHANGED
@@ -169,14 +169,14 @@ function resultSummary(value) {
169
169
  }
170
170
  if (typeof value === "number" || typeof value === "boolean") return String(value);
171
171
  if (!value || typeof value !== "object") return "";
172
- const record2 = value;
173
- if (typeof record2.error === "string") return `failed: ${record2.error.replace(/\s+/g, " ").slice(0, 140)}`;
174
- if (typeof record2.summary === "string") return record2.summary.replace(/\s+/g, " ").slice(0, 160);
175
- if (typeof record2.message === "string") return record2.message.replace(/\s+/g, " ").slice(0, 160);
176
- const counts = Object.entries(record2).filter(([key, item]) => !["arguments", "params", "input", "request"].includes(key) && Array.isArray(item)).map(([key, item]) => `${key}: ${item.length}`);
172
+ const record3 = value;
173
+ if (typeof record3.error === "string") return `failed: ${record3.error.replace(/\s+/g, " ").slice(0, 140)}`;
174
+ if (typeof record3.summary === "string") return record3.summary.replace(/\s+/g, " ").slice(0, 160);
175
+ if (typeof record3.message === "string") return record3.message.replace(/\s+/g, " ").slice(0, 160);
176
+ const counts = Object.entries(record3).filter(([key2, item]) => !["arguments", "params", "input", "request"].includes(key2) && Array.isArray(item)).map(([key2, item]) => `${key2}: ${item.length}`);
177
177
  if (counts.length > 0) return counts.join(", ").slice(0, 160);
178
- if (record2.ok === true) return "completed";
179
- if (record2.ok === false) return "not completed";
178
+ if (record3.ok === true) return "completed";
179
+ if (record3.ok === false) return "not completed";
180
180
  return "structured result returned";
181
181
  }
182
182
  function serializedValue(value) {
@@ -190,15 +190,15 @@ function serializedValue(value) {
190
190
  function sourceFieldName(value) {
191
191
  return DERIVATION_SOURCE_FIELD.test(value.replace(/[\s_-]+/gu, "").toLocaleLowerCase());
192
192
  }
193
- function omitLargeSourceFields(value, key = "", seen = /* @__PURE__ */ new WeakSet()) {
193
+ function omitLargeSourceFields(value, key2 = "", seen = /* @__PURE__ */ new WeakSet()) {
194
194
  if (typeof value === "string") {
195
- if (!sourceFieldName(key) || value.length <= DERIVATION_SOURCE_FIELD_MIN_CHARS) return value;
196
- return `[${key || "source"} omitted: ${value.length} chars; full value remains in L5]`;
195
+ if (!sourceFieldName(key2) || value.length <= DERIVATION_SOURCE_FIELD_MIN_CHARS) return value;
196
+ return `[${key2 || "source"} omitted: ${value.length} chars; full value remains in L5]`;
197
197
  }
198
198
  if (!value || typeof value !== "object") return value;
199
199
  if (seen.has(value)) return "[circular value omitted; full value remains in L5]";
200
200
  seen.add(value);
201
- if (Array.isArray(value)) return value.map((item) => omitLargeSourceFields(item, key, seen));
201
+ if (Array.isArray(value)) return value.map((item) => omitLargeSourceFields(item, key2, seen));
202
202
  return Object.fromEntries(Object.entries(value).map(([nestedKey, nestedValue]) => [nestedKey, omitLargeSourceFields(nestedValue, nestedKey, seen)]));
203
203
  }
204
204
  function compactDerivationValue(value, maxChars, label) {
@@ -261,14 +261,14 @@ function summarizeToolJson(value) {
261
261
  item.forEach(visit);
262
262
  return;
263
263
  }
264
- const record2 = item;
265
- if (typeof record2.name === "string" && ("arguments" in record2 || "parameters" in record2)) names.add(record2.name);
266
- if (typeof record2.tool === "string") names.add(record2.tool);
267
- if (record2.function && typeof record2.function === "object") {
268
- const name2 = record2.function.name;
264
+ const record3 = item;
265
+ if (typeof record3.name === "string" && ("arguments" in record3 || "parameters" in record3)) names.add(record3.name);
266
+ if (typeof record3.tool === "string") names.add(record3.tool);
267
+ if (record3.function && typeof record3.function === "object") {
268
+ const name2 = record3.function.name;
269
269
  if (typeof name2 === "string") names.add(name2);
270
270
  }
271
- Object.values(record2).forEach(visit);
271
+ Object.values(record3).forEach(visit);
272
272
  };
273
273
  visit(parsed);
274
274
  if (names.size === 0) return null;
@@ -358,9 +358,9 @@ function condenseTranscript(messages) {
358
358
  ];
359
359
  const rendered = parts.map((part) => {
360
360
  if (!part.pasteCandidate) return part.text;
361
- const key = normalizedParagraph(part.text);
362
- if (!seen.has(key)) {
363
- seen.add(key);
361
+ const key2 = normalizedParagraph(part.text);
362
+ if (!seen.has(key2)) {
363
+ seen.add(key2);
364
364
  return part.text;
365
365
  }
366
366
  return REPEATED_PASTE_MARKER;
@@ -483,12 +483,12 @@ function renderCurrentState(facts) {
483
483
  return facts.map((fact) => `${factText(fact)}${fact.status === "disputed" ? " [disputed]" : ""}`).join("\n");
484
484
  }
485
485
  var EXPOSED_EVENT_STATUSES = /* @__PURE__ */ new Set(["active", "superseded"]);
486
- function exposedSources(ids, eventById) {
487
- return unique((ids ?? []).filter((id) => EXPOSED_EVENT_STATUSES.has(eventById.get(id)?.status ?? "forgotten")));
486
+ function exposedSources(ids2, eventById) {
487
+ return unique((ids2 ?? []).filter((id) => EXPOSED_EVENT_STATUSES.has(eventById.get(id)?.status ?? "forgotten")));
488
488
  }
489
- function metadataEntries(entries, allowedValues, eventById) {
489
+ function metadataEntries(entries2, allowedValues, eventById) {
490
490
  const allowed = new Set(allowedValues.map((value) => normalizeSearchText(value)));
491
- return (entries ?? []).flatMap((entry) => {
491
+ return (entries2 ?? []).flatMap((entry) => {
492
492
  const value = typeof entry.value === "string" ? entry.value.trim() : "";
493
493
  const sourceEventIds = exposedSources(entry.sourceEventIds, eventById);
494
494
  return value && allowed.has(normalizeSearchText(value)) && sourceEventIds.length > 0 ? [{ value, sourceEventIds }] : [];
@@ -518,21 +518,21 @@ function effectiveMetadata(node, eventById) {
518
518
  function effectiveGraphNodeView(node, edges, events) {
519
519
  if (node.status !== "active" && node.status !== "disputed") return null;
520
520
  const eventById = new Map(events.map((event) => [event.id, event]));
521
- const sources2 = (ids, statuses) => unique((ids ?? []).filter((id) => {
521
+ const sources2 = (ids2, statuses) => unique((ids2 ?? []).filter((id) => {
522
522
  const event = eventById.get(id);
523
523
  return event !== void 0 && statuses.has(event.status);
524
524
  }));
525
525
  const activeStatuses = /* @__PURE__ */ new Set(["active"]);
526
526
  const visibleStatuses = /* @__PURE__ */ new Set(["active", "superseded"]);
527
- const splitRecord = (record2) => {
528
- if (record2.status === "archived") return {};
529
- const activeSources = sources2(record2.sourceEventIds, activeStatuses);
530
- const visibleSources = sources2(record2.sourceEventIds, visibleStatuses);
531
- if ((record2.status === "active" || record2.status === "disputed") && activeSources.length > 0) {
532
- return { current: { ...record2, sourceEventIds: activeSources } };
527
+ const splitRecord = (record3) => {
528
+ if (record3.status === "archived") return {};
529
+ const activeSources = sources2(record3.sourceEventIds, activeStatuses);
530
+ const visibleSources = sources2(record3.sourceEventIds, visibleStatuses);
531
+ if ((record3.status === "active" || record3.status === "disputed") && activeSources.length > 0) {
532
+ return { current: { ...record3, sourceEventIds: activeSources } };
533
533
  }
534
- if (visibleSources.length > 0 && record2.status === "superseded") {
535
- return { historical: { ...record2, sourceEventIds: visibleSources } };
534
+ if (visibleSources.length > 0 && record3.status === "superseded") {
535
+ return { historical: { ...record3, sourceEventIds: visibleSources } };
536
536
  }
537
537
  return {};
538
538
  };
@@ -568,14 +568,14 @@ function effectiveGraphNodeView(node, edges, events) {
568
568
  };
569
569
  }
570
570
  function boundEffectiveGraphNodeView(view, eventIds, options = {}) {
571
- const boundRecord = (record2) => {
572
- const sourceEventIds = record2.sourceEventIds.filter((id) => eventIds.has(id));
573
- return sourceEventIds.length > 0 ? { ...record2, sourceEventIds } : null;
571
+ const boundRecord = (record3) => {
572
+ const sourceEventIds = record3.sourceEventIds.filter((id) => eventIds.has(id));
573
+ return sourceEventIds.length > 0 ? { ...record3, sourceEventIds } : null;
574
574
  };
575
- const currentFacts = view.currentFacts.flatMap((record2) => boundRecord(record2) ?? []);
576
- const historicalFacts = view.historicalFacts.flatMap((record2) => boundRecord(record2) ?? []);
577
- const currentEdges = view.currentEdges.flatMap((record2) => boundRecord(record2) ?? []);
578
- const historicalEdges = view.historicalEdges.flatMap((record2) => boundRecord(record2) ?? []);
575
+ const currentFacts = view.currentFacts.flatMap((record3) => boundRecord(record3) ?? []);
576
+ const historicalFacts = view.historicalFacts.flatMap((record3) => boundRecord(record3) ?? []);
577
+ const currentEdges = view.currentEdges.flatMap((record3) => boundRecord(record3) ?? []);
578
+ const historicalEdges = view.historicalEdges.flatMap((record3) => boundRecord(record3) ?? []);
579
579
  const metadata = view.node.metadataProvenance ? (() => {
580
580
  const name2 = (view.node.metadataProvenance?.name ?? []).filter((id) => eventIds.has(id));
581
581
  const aliases = (view.node.metadataProvenance?.aliases ?? []).flatMap((entry) => {
@@ -630,16 +630,16 @@ function strings(value, limit = 32) {
630
630
  function confidence(value) {
631
631
  return typeof value === "number" && Number.isFinite(value) ? Math.max(0, Math.min(1, value)) : 0.8;
632
632
  }
633
- function chronology(events, ids, fallback) {
634
- return ids.flatMap((id) => events.find((event) => event.id === id) ?? []).map((event) => event.temporal.happenedStart ?? event.temporal.happenedEnd ?? event.temporal.mentionedAt ?? event.createdAt).sort().at(-1) ?? fallback;
633
+ function chronology(events, ids2, fallback) {
634
+ return ids2.flatMap((id) => events.find((event) => event.id === id) ?? []).map((event) => event.temporal.happenedStart ?? event.temporal.happenedEnd ?? event.temporal.mentionedAt ?? event.createdAt).sort().at(-1) ?? fallback;
635
635
  }
636
636
  function entityMatchKind(node, proposal, eventById) {
637
637
  if (node.type !== proposal.type) return null;
638
- const key = (value) => normalizeSearchText(value).replace(/[\s_-]+/g, "");
638
+ const key2 = (value) => normalizeSearchText(value).replace(/[\s_-]+/g, "");
639
639
  const metadata = effectiveMetadata(node, eventById);
640
- const proposalNames = new Set([proposal.name, ...proposal.aliases ?? []].map(key));
641
- if (proposalNames.has(key(metadata.name))) return "canonical-name";
642
- if (metadata.aliases.some((value) => proposalNames.has(key(value)))) return "alias";
640
+ const proposalNames = new Set([proposal.name, ...proposal.aliases ?? []].map(key2));
641
+ if (proposalNames.has(key2(metadata.name))) return "canonical-name";
642
+ if (metadata.aliases.some((value) => proposalNames.has(key2(value)))) return "alias";
643
643
  return null;
644
644
  }
645
645
  function applyGraphProjection(options) {
@@ -652,9 +652,9 @@ function applyGraphProjection(options) {
652
652
  const requested = strings(value, 64);
653
653
  return requested.length > 0 && requested.every((id) => options.allowedEventIds.has(id) && EXPOSED_EVENT_STATUSES.has(eventById.get(id)?.status ?? "forgotten")) ? requested : [];
654
654
  };
655
- const activeSources = (ids) => ids.filter((id) => eventById.get(id)?.status === "active");
656
- const metadataEntriesFromProjection = (entries, fallbackValues) => {
657
- const byValue = new Map((entries ?? []).map((entry) => [normalizeSearchText(entry.value), entry]));
655
+ const activeSources = (ids2) => ids2.filter((id) => eventById.get(id)?.status === "active");
656
+ const metadataEntriesFromProjection = (entries2, fallbackValues) => {
657
+ const byValue = new Map((entries2 ?? []).map((entry) => [normalizeSearchText(entry.value), entry]));
658
658
  return fallbackValues.flatMap((value) => {
659
659
  const entry = byValue.get(normalizeSearchText(value));
660
660
  const sourceEventIds = validSources(entry?.sourceEventIds);
@@ -748,26 +748,26 @@ function applyGraphProjection(options) {
748
748
  ...Array.isArray(proposal.facts) ? proposal.facts : []
749
749
  ];
750
750
  for (const rawFact of facts) {
751
- const key = text(rawFact.key, 160);
751
+ const key2 = text(rawFact.key, 160);
752
752
  const value = Array.isArray(rawFact.value) ? strings(rawFact.value, 40) : text(rawFact.value, 1200);
753
- if (!key || (Array.isArray(value) ? value.length === 0 : !value)) continue;
753
+ if (!key2 || (Array.isArray(value) ? value.length === 0 : !value)) continue;
754
754
  const factSources = "sourceEventIds" in rawFact ? validSources(rawFact.sourceEventIds) : sources2;
755
755
  if (factSources.length === 0) {
756
- warnings.push(`Dropped fact "${key}" because it lacks valid exposable Event provenance.`);
756
+ warnings.push(`Dropped fact "${key2}" because it lacks valid exposable Event provenance.`);
757
757
  continue;
758
758
  }
759
759
  if ((node.status === "active" || node.status === "disputed") && activeSources(factSources).length === 0) {
760
- warnings.push(`Dropped current fact "${key}" because it has no active Event provenance.`);
760
+ warnings.push(`Dropped current fact "${key2}" because it has no active Event provenance.`);
761
761
  continue;
762
762
  }
763
- for (const old of node.facts.filter((fact2) => fact2.status === "active" && fact2.key === key)) {
763
+ for (const old of node.facts.filter((fact2) => fact2.status === "active" && fact2.key === key2)) {
764
764
  old.status = "superseded";
765
765
  if (!old.validTo) old.validTo = validFrom;
766
766
  old.updatedAt = options.now;
767
767
  }
768
768
  const fact = {
769
769
  id: options.idFactory("gfact"),
770
- key,
770
+ key: key2,
771
771
  value,
772
772
  status: node.status,
773
773
  validFrom,
@@ -1519,9 +1519,9 @@ function parseTemporal(value) {
1519
1519
  const raw = record(value);
1520
1520
  if (!raw) return void 0;
1521
1521
  const temporal = {};
1522
- for (const key of ["mentionedAt", "happenedStart", "happenedEnd", "originalText"]) {
1523
- const parsed = optionalString(raw[key], 200);
1524
- if (parsed) temporal[key] = parsed;
1522
+ for (const key2 of ["mentionedAt", "happenedStart", "happenedEnd", "originalText"]) {
1523
+ const parsed = optionalString(raw[key2], 200);
1524
+ if (parsed) temporal[key2] = parsed;
1525
1525
  }
1526
1526
  const precision = oneOf(raw.precision, ["instant", "day", "month", "year", "range", "unknown"]);
1527
1527
  const basis = oneOf(raw.basis, ["explicit", "relative", "inferred", "unknown"]);
@@ -1909,8 +1909,8 @@ function normalizeSnapshot(value) {
1909
1909
  throw new TypeError("Invalid StrataGate snapshot: blockDecayLambda must be a non-negative finite number");
1910
1910
  }
1911
1911
  if (!Array.isArray(snapshot.externalMemoryImportJobs)) snapshot.externalMemoryImportJobs = [];
1912
- for (const key of ["openTail", "blocks", "summaryJobs", "events", "agentEvents", "graphNodes", "graphEdges", "graphProjectionJobs", "elements", "extractionJobs", "elementProjectionJobs", "usageReceipts", "ingestionReceipts", "externalMemoryImportJobs"]) {
1913
- if (!Array.isArray(snapshot[key])) throw new TypeError(`Invalid StrataGate snapshot: ${key} must be an array`);
1912
+ for (const key2 of ["openTail", "blocks", "summaryJobs", "events", "agentEvents", "graphNodes", "graphEdges", "graphProjectionJobs", "elements", "extractionJobs", "elementProjectionJobs", "usageReceipts", "ingestionReceipts", "externalMemoryImportJobs"]) {
1913
+ if (!Array.isArray(snapshot[key2])) throw new TypeError(`Invalid StrataGate snapshot: ${key2} must be an array`);
1914
1914
  }
1915
1915
  if (!Array.isArray(snapshot.successfulModelResponses)) snapshot.successfulModelResponses = [];
1916
1916
  for (const job of snapshot.graphProjectionJobs) {
@@ -2000,7 +2000,7 @@ function renderElementState(facts, includeHistorical = false) {
2000
2000
  lines.push(`${fact.key}: ${values.join(", ")}`);
2001
2001
  }
2002
2002
  }
2003
- for (const [key, values] of sets) lines.push(`${key}: ${values.join(", ")}`);
2003
+ for (const [key2, values] of sets) lines.push(`${key2}: ${values.join(", ")}`);
2004
2004
  return lines.join("\n");
2005
2005
  }
2006
2006
  function applyElementChanges(options) {
@@ -2008,7 +2008,7 @@ function applyElementChanges(options) {
2008
2008
  for (const rawChange of options.changes) {
2009
2009
  const name2 = compactText(rawChange.element?.name, 160);
2010
2010
  const type = rawChange.element?.type;
2011
- const key = compactText(rawChange.key, 160);
2011
+ const key2 = compactText(rawChange.key, 160);
2012
2012
  const mode = rawChange.mode;
2013
2013
  const operation = rawChange.operation;
2014
2014
  const requestedSourceEventIds = stringList(rawChange.sourceEventIds, 24);
@@ -2016,7 +2016,7 @@ function applyElementChanges(options) {
2016
2016
  const rawValue = rawChange.value;
2017
2017
  const value = Array.isArray(rawValue) ? stringList(rawValue, 40) : compactText(rawValue, 1200);
2018
2018
  const operationMatchesMode = mode === "state" && operation === "set_state" || mode === "set" && operation === "add_set_item" || mode === "relation" && operation === "set_relation";
2019
- if (!name2 || !ELEMENT_TYPES.has(type) || !key || !FACT_MODES.has(mode) || !operationMatchesMode || sourceEventIds.length === 0 || sourceEventIds.length !== requestedSourceEventIds.length || (Array.isArray(value) ? value.length === 0 : !value)) continue;
2019
+ if (!name2 || !ELEMENT_TYPES.has(type) || !key2 || !FACT_MODES.has(mode) || !operationMatchesMode || sourceEventIds.length === 0 || sourceEventIds.length !== requestedSourceEventIds.length || (Array.isArray(value) ? value.length === 0 : !value)) continue;
2020
2020
  const aliases = stringList(rawChange.element?.aliases, 20, 160).filter((alias) => normalizeSearchText(alias) !== normalizeSearchText(name2));
2021
2021
  const knownNames = new Set([name2, ...aliases].map(normalizeSearchText));
2022
2022
  let element = options.elements.find((candidate) => candidate.type === type && [candidate.name, ...candidate.aliases].some((candidateName) => knownNames.has(normalizeSearchText(candidateName))));
@@ -2049,18 +2049,18 @@ function applyElementChanges(options) {
2049
2049
  const validFrom = compactText(rawChange.validFrom, 80) || sourceEvents.map(eventChronology).sort().at(-1) || options.now;
2050
2050
  const validTo = compactText(rawChange.validTo, 80) || void 0;
2051
2051
  if (mode !== "set") {
2052
- for (const fact of element.facts.filter((candidate) => candidate.status === "active" && candidate.key === key && candidate.mode === mode)) {
2052
+ for (const fact of element.facts.filter((candidate) => candidate.status === "active" && candidate.key === key2 && candidate.mode === mode)) {
2053
2053
  fact.status = "superseded";
2054
2054
  if (!fact.validTo) fact.validTo = validFrom;
2055
2055
  fact.updatedAt = options.now;
2056
2056
  }
2057
2057
  }
2058
- const existingSetValues = new Set(element.facts.filter((fact) => fact.status === "active" && fact.mode === "set" && fact.key === key).flatMap((fact) => Array.isArray(fact.value) ? fact.value : [fact.value]).map(normalizeSearchText));
2058
+ const existingSetValues = new Set(element.facts.filter((fact) => fact.status === "active" && fact.mode === "set" && fact.key === key2).flatMap((fact) => Array.isArray(fact.value) ? fact.value : [fact.value]).map(normalizeSearchText));
2059
2059
  const factValue = mode === "set" ? (Array.isArray(value) ? value : [value]).filter((item) => !existingSetValues.has(normalizeSearchText(item))) : value;
2060
2060
  if (mode !== "set" || factValue.length > 0) {
2061
2061
  const fact = {
2062
2062
  id: options.idFactory("fact"),
2063
- key,
2063
+ key: key2,
2064
2064
  mode,
2065
2065
  value: factValue,
2066
2066
  ...validFrom ? { validFrom } : {},
@@ -2550,11 +2550,11 @@ var SqliteStorage = class {
2550
2550
  }
2551
2551
  async load(namespace) {
2552
2552
  this.assertOpen();
2553
- const key = nonEmptyNamespace(namespace);
2553
+ const key2 = nonEmptyNamespace(namespace);
2554
2554
  const space = this.database.prepare(`
2555
2555
  SELECT schema_version, revision, current_turn, block_turn_size, block_decay_lambda
2556
2556
  FROM memory_spaces WHERE namespace = ?
2557
- `).get(key);
2557
+ `).get(key2);
2558
2558
  if (!space) return null;
2559
2559
  if (space.schema_version !== STRATAGATE_STORAGE_SCHEMA_VERSION) {
2560
2560
  throw new Error(`Unsupported stored StrataGate schema: ${space.schema_version}`);
@@ -2562,7 +2562,7 @@ var SqliteStorage = class {
2562
2562
  const messageRows = this.database.prepare(`
2563
2563
  SELECT id, block_id, thread_id, position, role, content, created_at, tool_calls_json
2564
2564
  FROM messages WHERE namespace = ? ORDER BY block_id, position
2565
- `).all(key);
2565
+ `).all(key2);
2566
2566
  const openTail = [];
2567
2567
  const messagesByBlock = /* @__PURE__ */ new Map();
2568
2568
  for (const row of messageRows) {
@@ -2583,7 +2583,7 @@ var SqliteStorage = class {
2583
2583
  }
2584
2584
  const blockRows = this.database.prepare(`
2585
2585
  SELECT * FROM blocks WHERE namespace = ? ORDER BY sequence
2586
- `).all(key);
2586
+ `).all(key2);
2587
2587
  const blocks = blockRows.map((row) => ({
2588
2588
  id: row.id,
2589
2589
  ...row.thread_id ? { threadId: row.thread_id } : {},
@@ -2611,7 +2611,7 @@ var SqliteStorage = class {
2611
2611
  const summaryJobs = this.database.prepare(`
2612
2612
  SELECT block_id, status, attempts, last_error, next_retry_at, updated_at
2613
2613
  FROM block_summary_jobs WHERE namespace = ? ORDER BY block_id
2614
- `).all(key).map((row) => ({
2614
+ `).all(key2).map((row) => ({
2615
2615
  blockId: row.block_id,
2616
2616
  status: row.status,
2617
2617
  attempts: row.attempts,
@@ -2622,34 +2622,34 @@ var SqliteStorage = class {
2622
2622
  const sourceRows = this.database.prepare(`
2623
2623
  SELECT event_id, message_id, position FROM event_sources
2624
2624
  WHERE namespace = ? ORDER BY event_id, position
2625
- `).all(key);
2625
+ `).all(key2);
2626
2626
  const sourcesByEvent = /* @__PURE__ */ new Map();
2627
2627
  for (const row of sourceRows) {
2628
- const ids = sourcesByEvent.get(row.event_id) ?? [];
2629
- ids.push(row.message_id);
2630
- sourcesByEvent.set(row.event_id, ids);
2628
+ const ids2 = sourcesByEvent.get(row.event_id) ?? [];
2629
+ ids2.push(row.message_id);
2630
+ sourcesByEvent.set(row.event_id, ids2);
2631
2631
  }
2632
2632
  const eventRows = this.database.prepare(`
2633
2633
  SELECT * FROM events WHERE namespace = ? ORDER BY position
2634
- `).all(key);
2634
+ `).all(key2);
2635
2635
  const events = mapEventRows(eventRows, sourcesByEvent, "events");
2636
2636
  const agentSourceRows = this.database.prepare(`
2637
2637
  SELECT event_id, message_id, position FROM agent_event_sources
2638
2638
  WHERE namespace = ? ORDER BY event_id, position
2639
- `).all(key);
2639
+ `).all(key2);
2640
2640
  const agentSourcesByEvent = /* @__PURE__ */ new Map();
2641
2641
  for (const row of agentSourceRows) {
2642
- const ids = agentSourcesByEvent.get(row.event_id) ?? [];
2643
- ids.push(row.message_id);
2644
- agentSourcesByEvent.set(row.event_id, ids);
2642
+ const ids2 = agentSourcesByEvent.get(row.event_id) ?? [];
2643
+ ids2.push(row.message_id);
2644
+ agentSourcesByEvent.set(row.event_id, ids2);
2645
2645
  }
2646
2646
  const agentEventRows = this.database.prepare(`
2647
2647
  SELECT * FROM agent_events WHERE namespace = ? ORDER BY position
2648
- `).all(key);
2648
+ `).all(key2);
2649
2649
  const agentEvents = mapEventRows(agentEventRows, agentSourcesByEvent, "agent_events");
2650
2650
  const hasEventMetadata = this.database.prepare("SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = 'event_metadata'").get();
2651
2651
  if (hasEventMetadata) {
2652
- const metadataRows = this.database.prepare("SELECT event_id, catalog_hints_json, extractor_version FROM event_metadata WHERE namespace = ?").all(key);
2652
+ const metadataRows = this.database.prepare("SELECT event_id, catalog_hints_json, extractor_version FROM event_metadata WHERE namespace = ?").all(key2);
2653
2653
  const byId = new Map([...events, ...agentEvents].map((event) => [event.id, event]));
2654
2654
  for (const row of metadataRows) {
2655
2655
  const event = byId.get(row.event_id);
@@ -2662,26 +2662,26 @@ var SqliteStorage = class {
2662
2662
  const elementSourceRows = this.database.prepare(`
2663
2663
  SELECT element_id, event_id, position FROM element_sources
2664
2664
  WHERE namespace = ? ORDER BY element_id, position
2665
- `).all(key);
2665
+ `).all(key2);
2666
2666
  const sourcesByElement = /* @__PURE__ */ new Map();
2667
2667
  for (const row of elementSourceRows) {
2668
- const ids = sourcesByElement.get(row.element_id) ?? [];
2669
- ids.push(row.event_id);
2670
- sourcesByElement.set(row.element_id, ids);
2668
+ const ids2 = sourcesByElement.get(row.element_id) ?? [];
2669
+ ids2.push(row.event_id);
2670
+ sourcesByElement.set(row.element_id, ids2);
2671
2671
  }
2672
2672
  const elementFactSourceRows = this.database.prepare(`
2673
2673
  SELECT fact_id, event_id, position FROM element_fact_sources
2674
2674
  WHERE namespace = ? ORDER BY fact_id, position
2675
- `).all(key);
2675
+ `).all(key2);
2676
2676
  const sourcesByFact = /* @__PURE__ */ new Map();
2677
2677
  for (const row of elementFactSourceRows) {
2678
- const ids = sourcesByFact.get(row.fact_id) ?? [];
2679
- ids.push(row.event_id);
2680
- sourcesByFact.set(row.fact_id, ids);
2678
+ const ids2 = sourcesByFact.get(row.fact_id) ?? [];
2679
+ ids2.push(row.event_id);
2680
+ sourcesByFact.set(row.fact_id, ids2);
2681
2681
  }
2682
2682
  const elementFactRows = this.database.prepare(`
2683
2683
  SELECT * FROM element_facts WHERE namespace = ? ORDER BY element_id, position
2684
- `).all(key);
2684
+ `).all(key2);
2685
2685
  const factsByElement = /* @__PURE__ */ new Map();
2686
2686
  for (const row of elementFactRows) {
2687
2687
  const facts = factsByElement.get(row.element_id) ?? [];
@@ -2702,7 +2702,7 @@ var SqliteStorage = class {
2702
2702
  }
2703
2703
  const elementRows = this.database.prepare(`
2704
2704
  SELECT * FROM elements WHERE namespace = ? ORDER BY position
2705
- `).all(key);
2705
+ `).all(key2);
2706
2706
  const messagesByEvent = new Map(events.map((event) => [event.id, event.sourceMessageIds]));
2707
2707
  const elements = elementRows.map((row) => {
2708
2708
  const sourceEventIds = sourcesByElement.get(row.id) ?? [];
@@ -2730,7 +2730,7 @@ var SqliteStorage = class {
2730
2730
  const extractionJobs = this.database.prepare(`
2731
2731
  SELECT block_id, status, attempts, last_error, next_retry_at, updated_at
2732
2732
  FROM extraction_jobs WHERE namespace = ? ORDER BY block_id
2733
- `).all(key).map((row) => ({
2733
+ `).all(key2).map((row) => ({
2734
2734
  blockId: row.block_id,
2735
2735
  status: row.status,
2736
2736
  attempts: row.attempts,
@@ -2741,7 +2741,7 @@ var SqliteStorage = class {
2741
2741
  const elementProjectionJobs = this.database.prepare(`
2742
2742
  SELECT id, source_event_ids_json, status, attempts, element_ids_json, reason, last_error, created_at, updated_at
2743
2743
  FROM element_projection_jobs WHERE namespace = ? ORDER BY created_at, id
2744
- `).all(key).map((row) => ({
2744
+ `).all(key2).map((row) => ({
2745
2745
  id: row.id,
2746
2746
  sourceEventIds: parseJson(row.source_event_ids_json, "element_projection_jobs.source_event_ids_json"),
2747
2747
  status: row.status,
@@ -2755,7 +2755,7 @@ var SqliteStorage = class {
2755
2755
  const successfulModelResponses = this.database.prepare(`
2756
2756
  SELECT id, kind, response, created_at
2757
2757
  FROM model_response_history WHERE namespace = ? ORDER BY created_at, id
2758
- `).all(key).map((row) => ({
2758
+ `).all(key2).map((row) => ({
2759
2759
  id: row.id,
2760
2760
  kind: row.kind,
2761
2761
  response: row.response,
@@ -2764,7 +2764,7 @@ var SqliteStorage = class {
2764
2764
  const usageReceipts = this.database.prepare(`
2765
2765
  SELECT receipt_id, event_ids_json, element_ids_json, audit_json, created_at
2766
2766
  FROM usage_receipts WHERE namespace = ? ORDER BY created_at, receipt_id
2767
- `).all(key).map((row) => {
2767
+ `).all(key2).map((row) => {
2768
2768
  const audit2 = parseJson(row.audit_json, "usage_receipts.audit_json");
2769
2769
  return {
2770
2770
  id: row.receipt_id,
@@ -2777,22 +2777,22 @@ var SqliteStorage = class {
2777
2777
  const ingestionReceipts = this.database.prepare(`
2778
2778
  SELECT receipt_id, created_at
2779
2779
  FROM ingestion_receipts WHERE namespace = ? ORDER BY created_at, receipt_id
2780
- `).all(key).map((row) => ({
2780
+ `).all(key2).map((row) => ({
2781
2781
  id: row.receipt_id,
2782
2782
  createdAt: row.created_at
2783
2783
  }));
2784
2784
  const externalMemoryImportJobs = this.database.prepare(`
2785
2785
  SELECT id, payload_json FROM external_memory_import_jobs
2786
2786
  WHERE namespace = ? ORDER BY created_at, id
2787
- `).all(key).map((row) => parseJson(row.payload_json, "external_memory_import_jobs.payload_json"));
2787
+ `).all(key2).map((row) => parseJson(row.payload_json, "external_memory_import_jobs.payload_json"));
2788
2788
  const graphState = this.database.prepare(`
2789
2789
  SELECT nodes_json, edges_json, jobs_json FROM graph_state WHERE namespace = ?
2790
- `).get(key);
2790
+ `).get(key2);
2791
2791
  const graphNodes = graphState ? parseJson(graphState.nodes_json, "graph_state.nodes_json") : [];
2792
2792
  const graphEdges = graphState ? parseJson(graphState.edges_json, "graph_state.edges_json") : [];
2793
2793
  const graphProjectionJobs = graphState ? parseJson(graphState.jobs_json, "graph_state.jobs_json") : [];
2794
2794
  const hasTopics = this.database.prepare("SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = 'memory_topic_state'").get();
2795
- const topicState = hasTopics ? this.database.prepare("SELECT state_json FROM memory_topic_state WHERE namespace = ?").get(key) : void 0;
2795
+ const topicState = hasTopics ? this.database.prepare("SELECT state_json FROM memory_topic_state WHERE namespace = ?").get(key2) : void 0;
2796
2796
  const snapshot = {
2797
2797
  schemaVersion: STRATAGATE_STORAGE_SCHEMA_VERSION,
2798
2798
  currentTurn: space.current_turn,
@@ -2823,8 +2823,8 @@ var SqliteStorage = class {
2823
2823
  if (!Number.isSafeInteger(expectedRevision) || expectedRevision < 0) {
2824
2824
  throw new TypeError("expectedRevision must be a non-negative integer");
2825
2825
  }
2826
- const key = nonEmptyNamespace(namespace);
2827
- return this.immediateTransaction(() => this.persistSnapshot(key, snapshot, expectedRevision, rawMessageIndexDelta));
2826
+ const key2 = nonEmptyNamespace(namespace);
2827
+ return this.immediateTransaction(() => this.persistSnapshot(key2, snapshot, expectedRevision, rawMessageIndexDelta));
2828
2828
  }
2829
2829
  async close() {
2830
2830
  if (this.closed) return;
@@ -2945,7 +2945,7 @@ var SqliteStorage = class {
2945
2945
  searchRawMessageIds(namespace, tokens, limit, threadId, includeUnthreaded = false) {
2946
2946
  this.assertOpen();
2947
2947
  if (!this.rawSearchFtsAvailable || tokens.length === 0) return null;
2948
- const key = nonEmptyNamespace(namespace);
2948
+ const key2 = nonEmptyNamespace(namespace);
2949
2949
  const boundedLimit = Math.max(1, Math.min(5e3, Math.floor(limit)));
2950
2950
  const match = tokens.map(encodeRawSearchToken).filter(Boolean).map((token) => `"${token}"`).join(" OR ");
2951
2951
  if (!match) return [];
@@ -2960,7 +2960,7 @@ var SqliteStorage = class {
2960
2960
  ${threadId === void 0 ? "" : includeUnthreaded ? "AND (m.thread_id = ? OR m.thread_id IS NULL)" : "AND m.thread_id = ?"}
2961
2961
  ORDER BY rank
2962
2962
  LIMIT ?
2963
- `).all(...threadId === void 0 ? [match, key, boundedLimit] : [match, key, threadId, boundedLimit]);
2963
+ `).all(...threadId === void 0 ? [match, key2, boundedLimit] : [match, key2, threadId, boundedLimit]);
2964
2964
  return rows.map(({ message_id }) => message_id);
2965
2965
  } catch {
2966
2966
  this.rawSearchFtsAvailable = false;
@@ -3761,6 +3761,7 @@ var TOPIC_SAFE_REASONS = /* @__PURE__ */ new Set([
3761
3761
  "Topic projection must contain at most 8 overview paragraphs per chapter per batch.",
3762
3762
  "Topic projection must return 1-12 topics.",
3763
3763
  "Topic projection omitted batch events.",
3764
+ "Topic retry must replace invalid overview without removing entries.",
3764
3765
  "Topic projection source version changed; stale result rejected.",
3765
3766
  "Topic sections omitted batch events.",
3766
3767
  "Truncated topic sources permit navigation scope only, not factual overview.",
@@ -3776,6 +3777,10 @@ var TOPIC_SAFE_REASONS = /* @__PURE__ */ new Set([
3776
3777
  "overview must contain 0-8 source-grounded entries",
3777
3778
  "overview.sourceEventIds must contain unique, supplied Event ids (at most 12)",
3778
3779
  "overview.text must be nonempty and at most 600 characters",
3780
+ "overview.text must be a string",
3781
+ "overview.text is empty",
3782
+ "overview.text contains only whitespace",
3783
+ "overview.text exceeds the character limit",
3779
3784
  "overview.title must be nonempty and at most 80 characters",
3780
3785
  "section backfill must update only its existing chapter",
3781
3786
  "section.sourceEventIds must contain unique, supplied Event ids",
@@ -3811,7 +3816,7 @@ function safeTopicDiagnostics(value) {
3811
3816
  category: input.category,
3812
3817
  reason: typeof input.reason === "string" && TOPIC_SAFE_REASONS.has(input.reason) ? input.reason : "topic worker failed"
3813
3818
  };
3814
- for (const key of [
3819
+ for (const key2 of [
3815
3820
  "eventCount",
3816
3821
  "candidateTopicCount",
3817
3822
  "estimatedInputTokens",
@@ -3819,10 +3824,16 @@ function safeTopicDiagnostics(value) {
3819
3824
  "maxOutputTokens",
3820
3825
  "modelCalls",
3821
3826
  "attempt",
3822
- "splitDepth"
3827
+ "splitDepth",
3828
+ "actualCharacters",
3829
+ "minCharacters",
3830
+ "maxCharacters"
3823
3831
  ]) {
3824
- const number = input[key];
3825
- if (typeof number === "number" && Number.isSafeInteger(number) && number >= 0) result[key] = number;
3832
+ const number = input[key2];
3833
+ if (typeof number === "number" && Number.isSafeInteger(number) && number >= 0) result[key2] = number;
3834
+ }
3835
+ if (typeof input.fieldPath === "string" && /^topics\[\d{1,5}\]\.overview\[\d{1,5}\]\.text$/.test(input.fieldPath)) {
3836
+ result.fieldPath = input.fieldPath;
3826
3837
  }
3827
3838
  if (["stop", "max-tokens", "error", "aborted", "tool-calls", "unknown"].includes(String(input.finishReason))) {
3828
3839
  result.finishReason = input.finishReason;
@@ -3842,16 +3853,146 @@ var TopicProjectionError = class extends Error {
3842
3853
  this.diagnostics = diagnostics;
3843
3854
  }
3844
3855
  };
3856
+ function validateTopicOverviewText(value, fieldPath, category = "validation-failed") {
3857
+ const count = typeof value === "string" ? Array.from(value).length : void 0;
3858
+ const metadata = {
3859
+ fieldPath,
3860
+ minCharacters: 1,
3861
+ maxCharacters: 600,
3862
+ ...count === void 0 ? {} : { actualCharacters: count }
3863
+ };
3864
+ const reason = typeof value !== "string" ? "overview.text must be a string" : value.length === 0 ? "overview.text is empty" : !value.trim() ? "overview.text contains only whitespace" : count > 600 ? "overview.text exceeds the character limit" : null;
3865
+ if (reason) throw new TopicProjectionError(category, reason, metadata);
3866
+ }
3867
+
3868
+ // packages/core/src/topic-overview-repair.ts
3869
+ var kinds = ["history", "decision", "change", "open-question", "scope"];
3870
+ var record2 = (value) => value && typeof value === "object" ? value : {};
3871
+ var ids = (value) => Array.isArray(value) ? [...new Set(value.filter((id) => typeof id === "string"))].sort() : [];
3872
+ var key = (row) => JSON.stringify([row.kind ?? null, row.sourceEventIds]);
3873
+ var validText = (value) => typeof value === "string" && !!value.trim() && Array.from(value).length <= 600;
3874
+ function uniqueTopicOverviewEntries(parts) {
3875
+ const seen = /* @__PURE__ */ new Set();
3876
+ return parts.filter((part) => {
3877
+ const row = record2(part);
3878
+ const identity = JSON.stringify([row.kind, row.title ?? null, row.text, ids(row.sourceEventIds)]);
3879
+ if (seen.has(identity)) return false;
3880
+ seen.add(identity);
3881
+ return true;
3882
+ });
3883
+ }
3884
+ function entries(value) {
3885
+ const topics = record2(value).topics;
3886
+ return Array.isArray(topics) ? topics.flatMap((topic) => {
3887
+ const overview2 = record2(topic).overview;
3888
+ return Array.isArray(overview2) ? uniqueTopicOverviewEntries(overview2).map(record2) : [];
3889
+ }) : [];
3890
+ }
3891
+ var repairsByError = /* @__PURE__ */ new WeakMap();
3892
+ function carryTopicOverviewRepairs(error, requirements) {
3893
+ if (requirements.length) repairsByError.set(error, structuredClone([...requirements]));
3894
+ return error;
3895
+ }
3896
+ function topicOverviewRepairsFromError(error) {
3897
+ return error instanceof TopicProjectionError ? structuredClone(repairsByError.get(error) ?? []) : [];
3898
+ }
3899
+ function topicOverviewRepairsForHistoricalFailure(diagnostics) {
3900
+ return diagnostics && ["schema-invalid", "validation-failed"].includes(diagnostics.category) && [
3901
+ "overview.text must be a string",
3902
+ "overview.text is empty",
3903
+ "overview.text contains only whitespace",
3904
+ "overview.text exceeds the character limit",
3905
+ "overview.text must be nonempty and at most 600 characters"
3906
+ ].includes(diagnostics.reason) ? [{ sourceEventIds: [], minimumEntries: 1 }] : [];
3907
+ }
3908
+ var TopicOverviewRepairGuard = class {
3909
+ required = [];
3910
+ constructor(requirements = []) {
3911
+ this.merge(requirements);
3912
+ }
3913
+ get requirements() {
3914
+ return structuredClone(this.required);
3915
+ }
3916
+ merge(requirements) {
3917
+ const merged = new Map(this.required.map((row) => [key(row), row]));
3918
+ for (const requirement of requirements) {
3919
+ const row = {
3920
+ ...kinds.includes(requirement.kind ?? "") ? { kind: requirement.kind } : {},
3921
+ sourceEventIds: ids(requirement.sourceEventIds),
3922
+ minimumEntries: requirement.minimumEntries
3923
+ };
3924
+ if (!Number.isSafeInteger(row.minimumEntries) || row.minimumEntries < 1) continue;
3925
+ const previous = merged.get(key(row));
3926
+ if (!previous || previous.minimumEntries < row.minimumEntries) merged.set(key(row), row);
3927
+ }
3928
+ this.required = [...merged.values()];
3929
+ }
3930
+ observe(value, context) {
3931
+ const parts = entries(value);
3932
+ if (!parts.some((part) => !validText(part.text))) return;
3933
+ const rawTopics = record2(value).topics;
3934
+ const rows = rawTopics.flatMap((topic) => {
3935
+ const item = record2(topic), overview2 = item.overview;
3936
+ const candidate = context.existingTopics.find(({ id }) => id === item.topicId);
3937
+ const allowed = /* @__PURE__ */ new Set([...context.events.map(({ id }) => id), ...candidate?.sourceEventIds ?? []]);
3938
+ return Array.isArray(overview2) ? uniqueTopicOverviewEntries(overview2).map((entry) => {
3939
+ const part = record2(entry);
3940
+ const row = {
3941
+ ...kinds.includes(String(part.kind)) ? { kind: part.kind } : {},
3942
+ sourceEventIds: ids(part.sourceEventIds).filter((id) => allowed.has(id)),
3943
+ minimumEntries: 1
3944
+ };
3945
+ return { row, invalid: !validText(part.text) };
3946
+ }) : [];
3947
+ });
3948
+ const counts = /* @__PURE__ */ new Map();
3949
+ for (const { row, invalid } of rows) {
3950
+ if (!invalid) continue;
3951
+ const count = rows.filter(({ row: original }) => (row.kind === void 0 || row.kind === original.kind) && (!row.sourceEventIds.length || JSON.stringify(row.sourceEventIds) === JSON.stringify(original.sourceEventIds))).length;
3952
+ counts.set(key(row), { ...row, minimumEntries: count });
3953
+ }
3954
+ if (rows.some(({ row, invalid }) => invalid && (row.kind === void 0 || !row.sourceEventIds.length))) {
3955
+ const total = { sourceEventIds: [], minimumEntries: parts.length };
3956
+ counts.set(key(total), total);
3957
+ }
3958
+ this.merge([...counts.values()]);
3959
+ }
3960
+ assert(value) {
3961
+ if (!this.required.length) return;
3962
+ const actual = uniqueTopicOverviewEntries(entries(value)).filter((part) => validText(part.text)).map((part) => ({
3963
+ kind: part.kind,
3964
+ sourceEventIds: ids(part.sourceEventIds)
3965
+ }));
3966
+ for (const row of this.required) {
3967
+ const count = actual.filter((part) => (row.kind === void 0 || row.kind === part.kind) && (!row.sourceEventIds.length || JSON.stringify(row.sourceEventIds) === JSON.stringify(part.sourceEventIds))).length;
3968
+ if (count < row.minimumEntries) {
3969
+ throw new TopicProjectionError("validation-failed", "Topic retry must replace invalid overview without removing entries.");
3970
+ }
3971
+ }
3972
+ }
3973
+ };
3974
+ function partitionTopicOverviewRepairs(requirements, parentIds, childIds) {
3975
+ if (!requirements?.length) return void 0;
3976
+ const parent = new Set(parentIds), child = new Set(childIds);
3977
+ const rows = requirements.flatMap((row) => {
3978
+ const batchIds = row.sourceEventIds.filter((id) => parent.has(id));
3979
+ if (batchIds.length && !batchIds.some((id) => child.has(id))) return [];
3980
+ return [{ ...row, sourceEventIds: row.sourceEventIds.filter((id) => !parent.has(id) || child.has(id)) }];
3981
+ });
3982
+ const guard = new TopicOverviewRepairGuard(rows);
3983
+ return guard.requirements.length ? guard.requirements : void 0;
3984
+ }
3845
3985
 
3846
3986
  // packages/core/src/topics.ts
3847
3987
  var MEMORY_TOPIC_PROJECTOR_VERSION = 3;
3848
3988
  var TOPIC_BATCH_LIMIT = 12;
3849
3989
  var TOPIC_CANDIDATE_LIMIT = 12;
3850
3990
  var TOPIC_MAX_ATTEMPTS = 3;
3991
+ var TOPIC_RECOVERY_VERSION = 1;
3851
3992
  var TOPIC_LEASE_MS = 5 * 6e4;
3852
3993
  function canonical(value) {
3853
3994
  if (Array.isArray(value)) return value.map(canonical);
3854
- if (value && typeof value === "object") return Object.fromEntries(Object.entries(value).sort(([a], [b]) => a.localeCompare(b)).map(([key, item]) => [key, canonical(item)]));
3995
+ if (value && typeof value === "object") return Object.fromEntries(Object.entries(value).sort(([a], [b]) => a.localeCompare(b)).map(([key2, item]) => [key2, canonical(item)]));
3855
3996
  return value;
3856
3997
  }
3857
3998
  function digest(value) {
@@ -3866,7 +4007,7 @@ function topicTemporal(event) {
3866
4007
  return temporal;
3867
4008
  }
3868
4009
  var visible = (event) => event.status === "active" || event.status === "superseded";
3869
- var unique2 = (ids) => [...new Set(ids)];
4010
+ var unique2 = (ids2) => [...new Set(ids2)];
3870
4011
  var bounded = (value, size) => [...value].slice(0, size).join("");
3871
4012
  var SECTION_TITLES = {
3872
4013
  history: "\u53D1\u5C55\u8109\u7EDC",
@@ -3888,10 +4029,10 @@ function memoryTopicMembershipSections(topic) {
3888
4029
  const sections = /* @__PURE__ */ new Map();
3889
4030
  for (const part of topic.overview) {
3890
4031
  const title = memoryTopicSectionTitle(part);
3891
- const key = normalizedLabel(title);
3892
- const section = sections.get(key) ?? { title, sourceEventIds: [] };
4032
+ const key2 = normalizedLabel(title);
4033
+ const section = sections.get(key2) ?? { title, sourceEventIds: [] };
3893
4034
  section.sourceEventIds = unique2([...section.sourceEventIds, ...part.sourceEventIds]);
3894
- sections.set(key, section);
4035
+ sections.set(key2, section);
3895
4036
  }
3896
4037
  return [...sections.values()];
3897
4038
  }
@@ -3910,22 +4051,26 @@ function sourceVersions(sources2) {
3910
4051
  function jobInputsCurrent(job, versions) {
3911
4052
  return job.projectorVersion === MEMORY_TOPIC_PROJECTOR_VERSION && Object.entries({ ...job.sourceVersions, ...job.dependencyVersions }).every(([id, version]) => versions.get(id) === version);
3912
4053
  }
4054
+ function memoryTopicUnfinishedJobEventIds(job, state, versions) {
4055
+ if (job.superseded || job.status === "completed" || !jobInputsCurrent(job, versions)) return [];
4056
+ return job.sourceEventIds.filter((id) => versions.has(id) && job.sourceVersions[id] === versions.get(id) && (job.sectionBackfillTopicId ? state.sectionBackfill?.[job.sectionBackfillTopicId]?.[id] === versions.get(id) : state.projectedVersions[id] !== versions.get(id)));
4057
+ }
3913
4058
  function topicView(topic, events, versions) {
3914
- const ids = topic.sourceEventIds.filter((id) => events.has(id));
3915
- if (ids.length === 0) return null;
3916
- const valid = !topic.invalidated && topic.projectorVersion === MEMORY_TOPIC_PROJECTOR_VERSION && ids.length === topic.sourceEventIds.length && Object.entries(topic.dependencyVersions ?? topic.sourceVersions).every(([id, version]) => versions.get(id) === version);
4059
+ const ids2 = topic.sourceEventIds.filter((id) => events.has(id));
4060
+ if (ids2.length === 0) return null;
4061
+ const valid = !topic.invalidated && topic.projectorVersion === MEMORY_TOPIC_PROJECTOR_VERSION && ids2.length === topic.sourceEventIds.length && Object.entries(topic.dependencyVersions ?? topic.sourceVersions).every(([id, version]) => versions.get(id) === version);
3917
4062
  const overview2 = valid ? structuredClone(topic.overview) : [];
3918
4063
  const summarized = new Set(overview2.flatMap((part) => part.sourceEventIds));
3919
4064
  return {
3920
4065
  id: topic.id,
3921
4066
  title: valid ? topic.title : "\u5F85\u66F4\u65B0\u8BB0\u5FC6\u4E3B\u9898",
3922
4067
  description: valid ? topic.description : "\u6982\u89C8\u7B49\u5F85\u91CD\u65B0\u6574\u7406\uFF0C\u8BF7\u67E5\u770B\u5173\u8054\u4E8B\u4EF6\u3002",
3923
- sourceEventIds: ids,
4068
+ sourceEventIds: ids2,
3924
4069
  overview: overview2,
3925
4070
  sections: valid ? memoryTopicMembershipSections(topic) : [],
3926
4071
  createdAt: topic.createdAt,
3927
4072
  updatedAt: topic.updatedAt,
3928
- coverage: { totalEvents: ids.length, summarizedEvents: summarized.size, omittedEvents: ids.length - summarized.size },
4073
+ coverage: { totalEvents: ids2.length, summarizedEvents: summarized.size, omittedEvents: ids2.length - summarized.size },
3929
4074
  ...!valid ? { isFallback: true } : {}
3930
4075
  };
3931
4076
  }
@@ -3933,9 +4078,9 @@ function compactEvent(event) {
3933
4078
  const full = { ...structuredClone(event), temporal: structuredClone(topicTemporal(event)), quotes: [], sourceMessageIds: [] };
3934
4079
  if (JSON.stringify(full).length <= 1e4) return full;
3935
4080
  const temporal = structuredClone(topicTemporal(event));
3936
- for (const [key, value] of Object.entries(temporal)) {
3937
- if (typeof value === "string") temporal[key] = bounded(value, key === "originalText" ? 200 : 120);
3938
- if (Array.isArray(value)) temporal[key] = value.slice(0, 12).map((item) => bounded(String(item), 120));
4081
+ for (const [key2, value] of Object.entries(temporal)) {
4082
+ if (typeof value === "string") temporal[key2] = bounded(value, key2 === "originalText" ? 200 : 120);
4083
+ if (Array.isArray(value)) temporal[key2] = value.slice(0, 12).map((item) => bounded(String(item), 120));
3939
4084
  }
3940
4085
  return {
3941
4086
  ...structuredClone(event),
@@ -3990,7 +4135,7 @@ var MemoryTopicDirectory = class _MemoryTopicDirectory {
3990
4135
  return this.state.bootstrap ? structuredClone(this.state.bootstrap) : null;
3991
4136
  }
3992
4137
  needsInitialization() {
3993
- return this.state.bootstrap?.projectorVersion !== MEMORY_TOPIC_PROJECTOR_VERSION || this.state.sectionMembershipVersion !== 1;
4138
+ return this.state.bootstrap?.projectorVersion !== MEMORY_TOPIC_PROJECTOR_VERSION || this.state.sectionMembershipVersion !== 1 || (this.state.recoveryVersion ?? 0) < TOPIC_RECOVERY_VERSION;
3994
4139
  }
3995
4140
  initializeBootstrap(events, now) {
3996
4141
  this.synchronize(events, now);
@@ -3999,12 +4144,13 @@ var MemoryTopicDirectory = class _MemoryTopicDirectory {
3999
4144
  const missing = {};
4000
4145
  for (const topic of this.state.topics.filter((topic2) => !topic2.invalidated)) {
4001
4146
  const assigned = new Set(memoryTopicMembershipSections(topic).flatMap((section) => section.sourceEventIds));
4002
- const ids = topic.sourceEventIds.filter((id) => !assigned.has(id) && versions.has(id) && this.state.projectedVersions[id] === versions.get(id));
4003
- if (ids.length) missing[topic.id] = Object.fromEntries(ids.map((id) => [id, versions.get(id)]));
4147
+ const ids2 = topic.sourceEventIds.filter((id) => !assigned.has(id) && versions.has(id) && this.state.projectedVersions[id] === versions.get(id));
4148
+ if (ids2.length) missing[topic.id] = Object.fromEntries(ids2.map((id) => [id, versions.get(id)]));
4004
4149
  }
4005
4150
  this.state.sectionBackfill = missing;
4006
4151
  this.state.sectionMembershipVersion = 1;
4007
4152
  }
4153
+ this.recoverFailures(events, now);
4008
4154
  if (this.state.bootstrap?.projectorVersion === MEMORY_TOPIC_PROJECTOR_VERSION) return;
4009
4155
  this.state.bootstrap = {
4010
4156
  projectorVersion: MEMORY_TOPIC_PROJECTOR_VERSION,
@@ -4016,6 +4162,24 @@ var MemoryTopicDirectory = class _MemoryTopicDirectory {
4016
4162
  };
4017
4163
  this.synchronize(events, now);
4018
4164
  }
4165
+ recoverFailures(events, now) {
4166
+ if ((this.state.recoveryVersion ?? 0) >= TOPIC_RECOVERY_VERSION) return;
4167
+ this.state.recoveryVersion = TOPIC_RECOVERY_VERSION;
4168
+ const versions = sourceVersions(new Map(events.filter(visible).map((event) => [event.id, event])));
4169
+ for (const previous of [...this.state.jobs]) {
4170
+ if (previous.superseded || previous.status !== "failed" || previous.attempts < TOPIC_MAX_ATTEMPTS || previous.nextRetryAt !== null || !jobInputsCurrent(previous, versions)) continue;
4171
+ const work = memoryTopicUnfinishedJobEventIds(previous, this.state, versions);
4172
+ if (!work.length) continue;
4173
+ const ids2 = work.filter((id) => !this.state.jobs.some((job) => job.id !== previous.id && !job.superseded && jobInputsCurrent(job, versions) && job.sectionBackfillTopicId === previous.sectionBackfillTopicId && job.sourceVersions[id] === versions.get(id) && (job.status === "pending" || job.status === "running" || job.status === "failed" && job.attempts < TOPIC_MAX_ATTEMPTS && job.nextRetryAt !== null)));
4174
+ if (!ids2.length) {
4175
+ previous.superseded = true;
4176
+ previous.updatedAt = now;
4177
+ continue;
4178
+ }
4179
+ this.requeue(previous, ids2, versions, now);
4180
+ }
4181
+ this.synchronize(events, now);
4182
+ }
4019
4183
  synchronize(events, now) {
4020
4184
  const sources2 = new Map(events.filter(visible).map((event) => [event.id, event]));
4021
4185
  const versions = sourceVersions(sources2);
@@ -4074,13 +4238,24 @@ var MemoryTopicDirectory = class _MemoryTopicDirectory {
4074
4238
  for (const [id, version] of Object.entries(pending)) if (!topic || !topic.sourceEventIds.includes(id) || versions.get(id) !== version || assigned.has(id)) delete pending[id];
4075
4239
  if (!Object.keys(pending).length) delete this.state.sectionBackfill[topicId];
4076
4240
  }
4241
+ for (const job of this.state.jobs) if (job.status === "failed" && !job.superseded && jobInputsCurrent(job, versions) && !memoryTopicUnfinishedJobEventIds(job, this.state, versions).length) {
4242
+ job.superseded = true;
4243
+ job.context = null;
4244
+ job.nextRetryAt = null;
4245
+ job.leaseUntil = null;
4246
+ job.updatedAt = now;
4247
+ }
4077
4248
  const bootstrap = this.state.bootstrap;
4078
4249
  if (bootstrap && bootstrap.projectorVersion === MEMORY_TOPIC_PROJECTOR_VERSION) {
4079
4250
  const outstanding = Object.entries(bootstrap.sourceVersions).filter(([id, version]) => versions.get(id) === version && this.state.projectedVersions[id] !== version);
4080
- bootstrap.failedEvents = outstanding.filter(([id, version]) => this.state.jobs.some((job) => !job.superseded && job.status === "failed" && job.attempts >= TOPIC_MAX_ATTEMPTS && job.sourceVersions[id] === version && jobInputsCurrent(job, versions))).length;
4081
- if (outstanding.length === bootstrap.failedEvents && bootstrap.status !== "completed") {
4251
+ bootstrap.failedEvents = outstanding.filter(([id, version]) => this.state.jobs.some((job) => !job.superseded && job.sectionBackfillTopicId === void 0 && job.status === "failed" && job.attempts >= TOPIC_MAX_ATTEMPTS && job.sourceVersions[id] === version && jobInputsCurrent(job, versions))).length;
4252
+ if (outstanding.length === 0) {
4253
+ if (bootstrap.status !== "completed") bootstrap.completedAt = now;
4082
4254
  bootstrap.status = "completed";
4083
- bootstrap.completedAt = now;
4255
+ } else {
4256
+ bootstrap.completedAt = null;
4257
+ if (outstanding.length === bootstrap.failedEvents) bootstrap.status = "failed";
4258
+ else if (bootstrap.status === "completed" || bootstrap.status === "failed") bootstrap.status = "running";
4084
4259
  }
4085
4260
  }
4086
4261
  }
@@ -4123,8 +4298,8 @@ var MemoryTopicDirectory = class _MemoryTopicDirectory {
4123
4298
  const bootstrap = this.state.bootstrap;
4124
4299
  return sectionBackfillTopicId !== void 0 || this.state.rebuildVersions?.[id] === version || !bootstrap || bootstrap.projectorVersion !== MEMORY_TOPIC_PROJECTOR_VERSION || bootstrap.status !== "completed" && bootstrap.sourceVersions[id] === version;
4125
4300
  }
4126
- matchesMode(ids, versions, mode, sectionBackfillTopicId) {
4127
- return mode === "all" || ids.some((id) => this.isBootstrap(id, versions.get(id), sectionBackfillTopicId) === (mode === "bootstrap"));
4301
+ matchesMode(ids2, versions, mode, sectionBackfillTopicId) {
4302
+ return mode === "all" || ids2.some((id) => this.isBootstrap(id, versions.get(id), sectionBackfillTopicId) === (mode === "bootstrap"));
4128
4303
  }
4129
4304
  hasPending(events, now, mode = "all") {
4130
4305
  const sources2 = new Map(events.filter(visible).map((event) => [event.id, event]));
@@ -4254,6 +4429,7 @@ var MemoryTopicDirectory = class _MemoryTopicDirectory {
4254
4429
  }
4255
4430
  job.context = {
4256
4431
  jobId: job.id,
4432
+ ...job.overviewRepairRequirements?.length ? { overviewRepairRequirements: structuredClone(job.overviewRepairRequirements) } : {},
4257
4433
  events: batch.map(compactEvent),
4258
4434
  existingTopics,
4259
4435
  truncatedEventIds,
@@ -4279,6 +4455,17 @@ var MemoryTopicDirectory = class _MemoryTopicDirectory {
4279
4455
  return structuredClone(job.context);
4280
4456
  }
4281
4457
  complete(id, result, events, now) {
4458
+ const job = this.requireJob(id);
4459
+ const repairs = new TopicOverviewRepairGuard(job.overviewRepairRequirements);
4460
+ if (job.context) repairs.observe(result, job.context);
4461
+ try {
4462
+ return this.completeValidated(id, result, events, now, repairs);
4463
+ } catch (error) {
4464
+ if (error instanceof TopicProjectionError) throw carryTopicOverviewRepairs(error, repairs.requirements);
4465
+ throw error;
4466
+ }
4467
+ }
4468
+ completeValidated(id, result, events, now, repairs) {
4282
4469
  const job = this.requireJob(id);
4283
4470
  if (job.status === "completed") return { topicIds: [...job.topicIds] };
4284
4471
  if (job.status !== "running" || !job.context) throw new Error(`Topic projection ${id} is not running`);
@@ -4299,13 +4486,13 @@ var MemoryTopicDirectory = class _MemoryTopicDirectory {
4299
4486
  const touched = /* @__PURE__ */ new Set();
4300
4487
  const labels = /* @__PURE__ */ new Set();
4301
4488
  const proposals = [];
4302
- const checkIds = (ids, label) => {
4303
- if (!Array.isArray(ids) || ids.length === 0 || unique2(ids).length !== ids.length || ids.some((eventId) => typeof eventId !== "string" || !allowed.has(eventId))) throw new TopicProjectionError("validation-failed", `Invalid topic ${label} source events.`);
4304
- return ids;
4489
+ const checkIds = (ids2, label) => {
4490
+ if (!Array.isArray(ids2) || ids2.length === 0 || unique2(ids2).length !== ids2.length || ids2.some((eventId) => typeof eventId !== "string" || !allowed.has(eventId))) throw new TopicProjectionError("validation-failed", `Invalid topic ${label} source events.`);
4491
+ return ids2;
4305
4492
  };
4306
- for (const proposal of result.topics) {
4307
- const ids = checkIds(proposal.sourceEventIds, "membership");
4308
- const matching = this.state.topics.filter((topic) => candidates.has(topic.id) && topic.sourceEventIds.some((eventId) => ids.includes(eventId)));
4493
+ for (const [topicIndex, proposal] of result.topics.entries()) {
4494
+ const ids2 = checkIds(proposal.sourceEventIds, "membership");
4495
+ const matching = this.state.topics.filter((topic) => candidates.has(topic.id) && topic.sourceEventIds.some((eventId) => ids2.includes(eventId)));
4309
4496
  const inferred = matching.length === 1 && result.topics.filter((other) => Array.isArray(other.sourceEventIds) && matching[0].sourceEventIds.some((eventId) => other.sourceEventIds.includes(eventId))).length === 1 ? matching[0] : void 0;
4310
4497
  const label = normalizedLabel;
4311
4498
  const sameLabel = typeof proposal.title === "string" ? this.state.topics.filter((topic) => !topic.invalidated && label(topic.title) === label(proposal.title)) : [];
@@ -4317,25 +4504,26 @@ var MemoryTopicDirectory = class _MemoryTopicDirectory {
4317
4504
  throw new TopicProjectionError("validation-failed", "Invalid duplicate chapter label conflicts with an existing topic.");
4318
4505
  }
4319
4506
  if (typeof proposal.title === "string") {
4320
- const key = label(proposal.title);
4321
- if (labels.has(key)) throw new TopicProjectionError("validation-failed", "Invalid duplicate chapter label in one projection.");
4322
- labels.add(key);
4507
+ const key2 = label(proposal.title);
4508
+ if (labels.has(key2)) throw new TopicProjectionError("validation-failed", "Invalid duplicate chapter label in one projection.");
4509
+ labels.add(key2);
4323
4510
  }
4324
4511
  if (proposal.topicId && (!existing || !candidates.has(proposal.topicId))) throw new TopicProjectionError("validation-failed", "Unknown or unexposed topic id.");
4325
4512
  const topicId = existing?.id ?? `topic_${randomUUID()}`;
4326
4513
  if (touched.has(topicId)) throw new TopicProjectionError("validation-failed", "A topic may be updated only once per projection.");
4327
4514
  touched.add(topicId);
4328
4515
  if (typeof proposal.title !== "string" || !proposal.title.trim() || [...proposal.title].length > 120 || typeof proposal.description !== "string" || !proposal.description.trim() || [...proposal.description].length > 400) throw new TopicProjectionError("validation-failed", "Invalid topic title or description.");
4329
- for (const eventId of ids) if (batchIds.has(eventId)) covered.add(eventId);
4516
+ for (const eventId of ids2) if (batchIds.has(eventId)) covered.add(eventId);
4330
4517
  if (!Array.isArray(proposal.overview) || proposal.overview.length > 8) throw new TopicProjectionError("validation-failed", "Topic projection must contain at most 8 overview paragraphs per chapter per batch.");
4331
- for (const part of proposal.overview) {
4518
+ for (const [overviewIndex, part] of proposal.overview.entries()) {
4332
4519
  if (part.title !== void 0 && (typeof part.title !== "string" || !part.title.trim() || [...part.title].length > 80)) throw new TopicProjectionError("validation-failed", "Invalid topic overview title.");
4333
- if (!["history", "decision", "change", "open-question", "scope"].includes(part.kind) || typeof part.text !== "string" || !part.text.trim() || [...part.text].length > 600) throw new TopicProjectionError("validation-failed", "Invalid topic overview section.");
4520
+ if (!["history", "decision", "change", "open-question", "scope"].includes(part.kind)) throw new TopicProjectionError("validation-failed", "Invalid topic overview section.");
4521
+ validateTopicOverviewText(part.text, `topics[${topicIndex}].overview[${overviewIndex}].text`);
4334
4522
  checkIds(part.sourceEventIds, "overview");
4335
4523
  if (part.kind !== "scope" && part.sourceEventIds.some((eventId) => job.context.truncatedEventIds?.includes(eventId))) {
4336
4524
  throw new TopicProjectionError("validation-failed", "Truncated topic sources permit navigation scope only, not factual overview.");
4337
4525
  }
4338
- if (part.sourceEventIds.length > 12 || part.sourceEventIds.some((eventId) => !ids.includes(eventId))) throw new TopicProjectionError("validation-failed", "Overview references must belong to returned topic membership.");
4526
+ if (part.sourceEventIds.length > 12 || part.sourceEventIds.some((eventId) => !ids2.includes(eventId))) throw new TopicProjectionError("validation-failed", "Overview references must belong to returned topic membership.");
4339
4527
  if (part.sourceEventIds.some((eventId) => !batchIds.has(eventId)) && !candidates.get(topicId)?.overview.some((old) => digest(old) === digest(part))) throw new TopicProjectionError("validation-failed", "Old overview evidence must be preserved verbatim; new sections need batch events.");
4340
4528
  }
4341
4529
  if (proposal.sections !== void 0 && !Array.isArray(proposal.sections)) throw new TopicProjectionError("validation-failed", "Invalid topic sections.");
@@ -4345,11 +4533,11 @@ var MemoryTopicDirectory = class _MemoryTopicDirectory {
4345
4533
  if (!section || typeof section.title !== "string" || !section.title.trim() || [...section.title].length > 80 || sectionLabels.has(label(section.title))) throw new TopicProjectionError("validation-failed", "Invalid or duplicate topic section title.");
4346
4534
  sectionLabels.add(label(section.title));
4347
4535
  checkIds(section.sourceEventIds, "section");
4348
- if (section.sourceEventIds.some((eventId) => !ids.includes(eventId))) throw new TopicProjectionError("validation-failed", "Section references must belong to returned topic membership.");
4536
+ if (section.sourceEventIds.some((eventId) => !ids2.includes(eventId))) throw new TopicProjectionError("validation-failed", "Section references must belong to returned topic membership.");
4349
4537
  if (section.sourceEventIds.some((eventId) => !batchIds.has(eventId)) && !memoryTopicMembershipSections(existing ?? { overview: [] }).some((old) => label(old.title) === label(section.title) && section.sourceEventIds.filter((eventId) => !batchIds.has(eventId)).every((eventId) => old.sourceEventIds.includes(eventId)))) throw new TopicProjectionError("validation-failed", "Old section membership must be preserved; reassignment needs batch events.");
4350
4538
  }
4351
- if (proposal.sections !== void 0 && ids.some((eventId) => batchIds.has(eventId) && !assignments.some((section) => section.sourceEventIds.includes(eventId)))) throw new TopicProjectionError("validation-failed", "Topic sections omitted batch events.");
4352
- const membership = unique2([...(existing?.sourceEventIds ?? []).filter((eventId) => sources2.has(eventId)), ...ids]);
4539
+ if (proposal.sections !== void 0 && ids2.some((eventId) => batchIds.has(eventId) && !assignments.some((section) => section.sourceEventIds.includes(eventId)))) throw new TopicProjectionError("validation-failed", "Topic sections omitted batch events.");
4540
+ const membership = unique2([...(existing?.sourceEventIds ?? []).filter((eventId) => sources2.has(eventId)), ...ids2]);
4353
4541
  const assignedIds = new Set(proposal.sections === void 0 ? [] : assignments.flatMap((section) => section.sourceEventIds).filter((eventId) => batchIds.has(eventId)));
4354
4542
  const sections = existing && !existing.invalidated ? memoryTopicMembershipSections(existing) : [];
4355
4543
  const memberIds = new Set(membership);
@@ -4367,7 +4555,7 @@ var MemoryTopicDirectory = class _MemoryTopicDirectory {
4367
4555
  throw new TopicProjectionError("validation-failed", "New topic overview must match a final topic section.");
4368
4556
  }
4369
4557
  }
4370
- const overview2 = unique2(proposal.overview.filter((part) => memoryTopicOverviewMatchesSection(part, titles)).map((part) => JSON.stringify(part))).map((part) => JSON.parse(part));
4558
+ const overview2 = uniqueTopicOverviewEntries(proposal.overview.filter((part) => memoryTopicOverviewMatchesSection(part, titles))).map((part) => structuredClone(part));
4371
4559
  for (const part of inherited) if (memoryTopicOverviewMatchesSection(part, titles) && !overview2.some((current) => digest(current) === digest(part))) overview2.push(structuredClone(part));
4372
4560
  proposals.push({
4373
4561
  id: topicId,
@@ -4385,6 +4573,8 @@ var MemoryTopicDirectory = class _MemoryTopicDirectory {
4385
4573
  });
4386
4574
  }
4387
4575
  if (job.sourceEventIds.some((eventId) => !covered.has(eventId))) throw new TopicProjectionError("validation-failed", "every supplied batch Event must be assigned to a topic");
4576
+ repairs.assert(result);
4577
+ repairs.assert({ topics: proposals });
4388
4578
  for (const topic of proposals) {
4389
4579
  const index = this.state.topics.findIndex((current) => current.id === topic.id);
4390
4580
  if (index < 0) this.state.topics.push(topic);
@@ -4400,6 +4590,7 @@ var MemoryTopicDirectory = class _MemoryTopicDirectory {
4400
4590
  if (this.state.rebuildVersions?.[eventId] === version) delete this.state.rebuildVersions[eventId];
4401
4591
  }
4402
4592
  this.state.topics = this.state.topics.filter((topic) => !topic.invalidated || topic.sourceEventIds.some((eventId) => sources2.has(eventId) && this.state.projectedVersions[eventId] !== versions.get(eventId)));
4593
+ job.overviewRepairRequirements = void 0;
4403
4594
  job.status = "completed";
4404
4595
  job.topicIds = proposals.map((topic) => topic.id);
4405
4596
  job.context = null;
@@ -4415,6 +4606,10 @@ var MemoryTopicDirectory = class _MemoryTopicDirectory {
4415
4606
  const job = this.state.jobs.find((candidate) => candidate.id === id);
4416
4607
  if (!job || job.status !== "running") return;
4417
4608
  const diagnostic = error instanceof TopicProjectionError ? safeTopicDiagnostics(error.diagnostics) : void 0;
4609
+ const repairs = new TopicOverviewRepairGuard(job.overviewRepairRequirements);
4610
+ const observed = topicOverviewRepairsFromError(error);
4611
+ repairs.merge(observed.length ? observed : topicOverviewRepairsForHistoricalFailure(diagnostic));
4612
+ job.overviewRepairRequirements = repairs.requirements.length ? repairs.requirements : void 0;
4418
4613
  const message = error instanceof Error ? error.message : String(error);
4419
4614
  const category = diagnostic?.category ?? (/timeout|timed out/i.test(message) ? "timeout" : /source.*changed|stale|not running/i.test(message) ? "source-changed" : "worker-failed");
4420
4615
  job.diagnostics = safeTopicDiagnostics({
@@ -4434,12 +4629,13 @@ var MemoryTopicDirectory = class _MemoryTopicDirectory {
4434
4629
  job.updatedAt = now;
4435
4630
  if (category === "max-tokens" && diagnostic?.finishReason === "max-tokens" && job.sourceEventIds.length > 1 && (job.splitDepth ?? 0) < 4) {
4436
4631
  const middle = Math.ceil(job.sourceEventIds.length / 2);
4437
- for (const ids of [job.sourceEventIds.slice(0, middle), job.sourceEventIds.slice(middle)]) {
4632
+ for (const ids2 of [job.sourceEventIds.slice(0, middle), job.sourceEventIds.slice(middle)]) {
4438
4633
  this.state.jobs.push({
4439
4634
  ...structuredClone(job),
4440
4635
  id: `tproj_${randomUUID()}`,
4441
- sourceEventIds: ids,
4442
- sourceVersions: Object.fromEntries(ids.map((id2) => [id2, job.sourceVersions[id2]])),
4636
+ sourceEventIds: ids2,
4637
+ sourceVersions: Object.fromEntries(ids2.map((id2) => [id2, job.sourceVersions[id2]])),
4638
+ overviewRepairRequirements: partitionTopicOverviewRepairs(job.overviewRepairRequirements, job.sourceEventIds, ids2),
4443
4639
  dependencyVersions: {},
4444
4640
  candidateVersions: {},
4445
4641
  status: "pending",
@@ -4463,15 +4659,12 @@ var MemoryTopicDirectory = class _MemoryTopicDirectory {
4463
4659
  if (!job) throw new Error(`Unknown topic projection: ${id}`);
4464
4660
  return job;
4465
4661
  }
4466
- retry(id, events, now) {
4467
- const previous = this.requireJob(id);
4468
- const sources2 = new Map(events.filter(visible).map((event) => [event.id, event]));
4469
- const versions = sourceVersions(sources2);
4470
- if (previous.superseded || previous.status !== "failed" || previous.attempts < TOPIC_MAX_ATTEMPTS || previous.nextRetryAt !== null || !jobInputsCurrent(previous, versions) || previous.sourceEventIds.some((eventId) => this.state.projectedVersions[eventId] === versions.get(eventId) && (!previous.sectionBackfillTopicId || this.state.sectionBackfill?.[previous.sectionBackfillTopicId]?.[eventId] !== versions.get(eventId)))) {
4471
- throw new Error("Topic retry conflict: failure is no longer current or retryable.");
4472
- }
4662
+ requeue(previous, ids2, versions, now) {
4473
4663
  const job = {
4474
4664
  ...structuredClone(previous),
4665
+ sourceEventIds: [...ids2],
4666
+ overviewRepairRequirements: partitionTopicOverviewRepairs(previous.overviewRepairRequirements ?? topicOverviewRepairsForHistoricalFailure(safeTopicDiagnostics(previous.diagnostics)), previous.sourceEventIds, ids2),
4667
+ sourceVersions: Object.fromEntries(ids2.map((id) => [id, previous.sourceVersions[id]])),
4475
4668
  id: `tproj_${randomUUID()}`,
4476
4669
  status: "pending",
4477
4670
  attempts: 0,
@@ -4496,6 +4689,17 @@ var MemoryTopicDirectory = class _MemoryTopicDirectory {
4496
4689
  bootstrap.status = "pending";
4497
4690
  bootstrap.completedAt = null;
4498
4691
  }
4692
+ return job;
4693
+ }
4694
+ retry(id, events, now) {
4695
+ const previous = this.requireJob(id);
4696
+ const sources2 = new Map(events.filter(visible).map((event) => [event.id, event]));
4697
+ const versions = sourceVersions(sources2);
4698
+ const ids2 = memoryTopicUnfinishedJobEventIds(previous, this.state, versions);
4699
+ if (previous.superseded || previous.status !== "failed" || previous.attempts < TOPIC_MAX_ATTEMPTS || previous.nextRetryAt !== null || !jobInputsCurrent(previous, versions) || !ids2.length) {
4700
+ throw new Error("Topic retry conflict: failure is no longer current or retryable.");
4701
+ }
4702
+ const job = this.requeue(previous, ids2, versions, now);
4499
4703
  this.synchronize(events, now);
4500
4704
  return { jobId: job.id, status: "pending" };
4501
4705
  }
@@ -4579,8 +4783,8 @@ function restrictExtractionRelations(temporal, allowedIds) {
4579
4783
  }
4580
4784
  for (const field of ["beforeEventIds", "afterEventIds", "supersedesEventIds", "conflictsWithEventIds", "relatedEventIds"]) {
4581
4785
  const values = temporal[field];
4582
- const ids = Array.isArray(values) ? [...new Set(values.filter((id) => typeof id === "string" && allowedIds.has(id)))] : [];
4583
- if (ids.length > 0) result[field] = ids;
4786
+ const ids2 = Array.isArray(values) ? [...new Set(values.filter((id) => typeof id === "string" && allowedIds.has(id)))] : [];
4787
+ if (ids2.length > 0) result[field] = ids2;
4584
4788
  else delete result[field];
4585
4789
  }
4586
4790
  return result;
@@ -4889,13 +5093,13 @@ var StrataGate = class _StrataGate {
4889
5093
  listSummaryJobs() {
4890
5094
  return [...this.summaryJobs.values()];
4891
5095
  }
4892
- runManualRetry(key, operation) {
4893
- const existing = this.manualRetryRuns.get(key);
5096
+ runManualRetry(key2, operation) {
5097
+ const existing = this.manualRetryRuns.get(key2);
4894
5098
  if (existing) return existing;
4895
5099
  const run = operation().finally(() => {
4896
- if (this.manualRetryRuns.get(key) === run) this.manualRetryRuns.delete(key);
5100
+ if (this.manualRetryRuns.get(key2) === run) this.manualRetryRuns.delete(key2);
4897
5101
  });
4898
- this.manualRetryRuns.set(key, run);
5102
+ this.manualRetryRuns.set(key2, run);
4899
5103
  return run;
4900
5104
  }
4901
5105
  /** Give one failed Block Summary job a fresh, user-requested retry budget. */
@@ -5451,9 +5655,9 @@ var StrataGate = class _StrataGate {
5451
5655
  decisions.push(audit2);
5452
5656
  }
5453
5657
  if (addedEvents.length > 0) {
5454
- const ids = addedEvents.map(({ id }) => id);
5455
- this.queueElementProjection(ids);
5456
- this.queueGraphProjection(ids, 2e3);
5658
+ const ids2 = addedEvents.map(({ id }) => id);
5659
+ this.queueElementProjection(ids2);
5660
+ this.queueGraphProjection(ids2, 2e3);
5457
5661
  }
5458
5662
  return {
5459
5663
  sourceBlockId: source.id,
@@ -6425,8 +6629,8 @@ var StrataGate = class _StrataGate {
6425
6629
  const matchDetails = (node) => {
6426
6630
  const view = viewById.get(node.id);
6427
6631
  const queryParts = normalizeSearchText(query).split(/[^\p{Letter}\p{Number}_]+/gu).filter(Boolean);
6428
- const explicitFactKeys = dedupe([...view.currentFacts, ...view.historicalFacts].map(({ key }) => key)).filter((key) => queryParts.includes(normalizeSearchText(key)));
6429
- const knownFactKeyTokens = new Set(explicitFactKeys.flatMap((key) => [...tokenSet(key)]));
6632
+ const explicitFactKeys = dedupe([...view.currentFacts, ...view.historicalFacts].map(({ key: key2 }) => key2)).filter((key2) => queryParts.includes(normalizeSearchText(key2)));
6633
+ const knownFactKeyTokens = new Set(explicitFactKeys.flatMap((key2) => [...tokenSet(key2)]));
6430
6634
  const queryKeyTokens = queryTokens.filter((token) => knownFactKeyTokens.has(token));
6431
6635
  const valueQueryTokens = queryTokens.filter((token) => !queryKeyTokens.includes(token));
6432
6636
  const meaningfulTokens = queryTokens;
@@ -6528,13 +6732,13 @@ var StrataGate = class _StrataGate {
6528
6732
  ...details.currentEdges.flatMap(({ sourceEventIds }) => sourceEventIds),
6529
6733
  ...details.historicalEdges.flatMap(({ sourceEventIds }) => sourceEventIds)
6530
6734
  ]);
6531
- const historicalKeys = new Set(details.historicalFacts.map(({ key }) => key));
6735
+ const historicalKeys = new Set(details.historicalFacts.map(({ key: key2 }) => key2));
6532
6736
  const historicalRelations = new Set(details.historicalEdges.map(({ relation }) => relation));
6533
6737
  const currentContextEventIds = dedupe([
6534
- ...details.view.currentFacts.filter(({ key }) => historicalKeys.has(key)).flatMap(({ sourceEventIds }) => sourceEventIds),
6738
+ ...details.view.currentFacts.filter(({ key: key2 }) => historicalKeys.has(key2)).flatMap(({ sourceEventIds }) => sourceEventIds),
6535
6739
  ...details.view.currentEdges.filter(({ relation }) => historicalRelations.has(relation)).flatMap(({ sourceEventIds }) => sourceEventIds)
6536
6740
  ]);
6537
- const currentContextFacts = details.view.currentFacts.filter(({ key }) => historicalKeys.has(key));
6741
+ const currentContextFacts = details.view.currentFacts.filter(({ key: key2 }) => historicalKeys.has(key2));
6538
6742
  const currentContextEdges = details.view.currentEdges.filter(({ relation }) => historicalRelations.has(relation));
6539
6743
  const nameEventIds = node.metadataProvenance?.name ?? [];
6540
6744
  const metadataCandidates = dedupe([...details.matchedMetadataEventIds, ...nameEventIds]);
@@ -6561,9 +6765,9 @@ var StrataGate = class _StrataGate {
6561
6765
  const boundedView = boundEffectiveGraphNodeView(details.view, evidenceIds, {
6562
6766
  preserveLegacyMetadata: legacyMetadataNotExpanded
6563
6767
  });
6564
- const bounded2 = (record2) => {
6565
- const sourceEventIds = record2.sourceEventIds.filter((id) => evidenceIds.has(id));
6566
- return sourceEventIds.length > 0 ? { ...record2, sourceEventIds } : null;
6768
+ const bounded2 = (record3) => {
6769
+ const sourceEventIds = record3.sourceEventIds.filter((id) => evidenceIds.has(id));
6770
+ return sourceEventIds.length > 0 ? { ...record3, sourceEventIds } : null;
6567
6771
  };
6568
6772
  const matchType = details.currentHit && details.historicalHit ? "both" : details.historicalHit ? "historical" : "current";
6569
6773
  return {
@@ -6572,10 +6776,10 @@ var StrataGate = class _StrataGate {
6572
6776
  matchedFields: details.matchedFields,
6573
6777
  matchReason: `Meaningful ${matchType} match in ${details.matchedFields.join(", ")}; relation text is ranking-only.`,
6574
6778
  matchType,
6575
- currentFacts: dedupe([...details.currentFacts, ...currentContextFacts]).flatMap((record2) => bounded2(record2) ?? []),
6576
- historicalFacts: details.historicalFacts.flatMap((record2) => bounded2(record2) ?? []),
6577
- currentEdges: dedupe([...details.currentEdges, ...currentContextEdges]).flatMap((record2) => bounded2(record2) ?? []),
6578
- historicalEdges: details.historicalEdges.flatMap((record2) => bounded2(record2) ?? []),
6779
+ currentFacts: dedupe([...details.currentFacts, ...currentContextFacts]).flatMap((record3) => bounded2(record3) ?? []),
6780
+ historicalFacts: details.historicalFacts.flatMap((record3) => bounded2(record3) ?? []),
6781
+ currentEdges: dedupe([...details.currentEdges, ...currentContextEdges]).flatMap((record3) => bounded2(record3) ?? []),
6782
+ historicalEdges: details.historicalEdges.flatMap((record3) => bounded2(record3) ?? []),
6579
6783
  provenanceEventIds,
6580
6784
  ...legacyMetadataNotExpanded ? { metadataEvidenceStatus: "not_expanded" } : {},
6581
6785
  timeline: graphTimeline(provenanceEventIds, this.listAllEvents())
@@ -6591,12 +6795,12 @@ var StrataGate = class _StrataGate {
6591
6795
  if (!this.graphProjector) return null;
6592
6796
  const completed = new Set([...this.graphProjectionJobs.values()].filter((job2) => job2.projectorVersion === KNOWLEDGE_GRAPH_PROJECTOR_VERSION && job2.status === "completed").flatMap((job2) => job2.sourceEventIds));
6593
6797
  const queued = new Set([...this.graphProjectionJobs.values()].filter((job2) => job2.projectorVersion === KNOWLEDGE_GRAPH_PROJECTOR_VERSION && job2.status !== "completed").flatMap((job2) => job2.sourceEventIds));
6594
- const ids = [...new Set(sourceEventIds.filter((id) => this.findEvent(id) && !completed.has(id) && !queued.has(id)))];
6595
- if (ids.length === 0) return null;
6798
+ const ids2 = [...new Set(sourceEventIds.filter((id) => this.findEvent(id) && !completed.has(id) && !queued.has(id)))];
6799
+ if (ids2.length === 0) return null;
6596
6800
  const now = toUtc8Iso(this.now());
6597
6801
  const job = {
6598
6802
  id: this.graphIdFactory("gproj"),
6599
- sourceEventIds: ids,
6803
+ sourceEventIds: ids2,
6600
6804
  projectorVersion: KNOWLEDGE_GRAPH_PROJECTOR_VERSION,
6601
6805
  status: "pending",
6602
6806
  attempts: 0,
@@ -6639,12 +6843,12 @@ var StrataGate = class _StrataGate {
6639
6843
  }
6640
6844
  queueElementProjection(sourceEventIds) {
6641
6845
  if (this.disableElementProjection) return null;
6642
- const ids = [...new Set(sourceEventIds.filter((id) => Boolean(this.findEvent(id))))];
6643
- if (ids.length === 0) return null;
6846
+ const ids2 = [...new Set(sourceEventIds.filter((id) => Boolean(this.findEvent(id))))];
6847
+ if (ids2.length === 0) return null;
6644
6848
  const now = toUtc8Iso(this.now());
6645
6849
  const job = {
6646
6850
  id: this.elementIdFactory("proj"),
6647
- sourceEventIds: ids,
6851
+ sourceEventIds: ids2,
6648
6852
  status: "pending",
6649
6853
  attempts: 0,
6650
6854
  elementIds: [],
@@ -6667,8 +6871,8 @@ var StrataGate = class _StrataGate {
6667
6871
  this.pendingRawUpserts.set(message.id, structuredClone(message));
6668
6872
  }
6669
6873
  }
6670
- markRawMessagesDeleted(ids) {
6671
- for (const id of ids) {
6874
+ markRawMessagesDeleted(ids2) {
6875
+ for (const id of ids2) {
6672
6876
  this.pendingRawUpserts.delete(id);
6673
6877
  this.pendingRawDeletes.add(id);
6674
6878
  this.rawMessageLookup.delete(id);
@@ -6962,9 +7166,9 @@ var StrataGate = class _StrataGate {
6962
7166
  ...event.temporal ? { temporal: restrictExtractionRelations(event.temporal, allowedIds) } : {}
6963
7167
  })) : [];
6964
7168
  if (extracted.length > 0) {
6965
- const ids = extracted.map(({ id }) => id);
6966
- this.queueElementProjection(ids);
6967
- this.queueGraphProjection(ids, 1e3);
7169
+ const ids2 = extracted.map(({ id }) => id);
7170
+ this.queueElementProjection(ids2);
7171
+ this.queueGraphProjection(ids2, 1e3);
6968
7172
  }
6969
7173
  const job = this.extractionJobs.get(target.id);
6970
7174
  if (!job) throw new Error(`Missing extraction job for block: ${target.id}`);
@@ -7381,7 +7585,7 @@ function isObject(value) {
7381
7585
  return value !== null && typeof value === "object" && !Array.isArray(value);
7382
7586
  }
7383
7587
  function hasRequiredKeys(value, requiredKeys) {
7384
- return requiredKeys.every((key) => Object.prototype.hasOwnProperty.call(value, key));
7588
+ return requiredKeys.every((key2) => Object.prototype.hasOwnProperty.call(value, key2));
7385
7589
  }
7386
7590
  function balancedValueEnd(value, start) {
7387
7591
  const opening = value[start];
@@ -7633,13 +7837,13 @@ function normalizeGraphNameProvenance(value) {
7633
7837
  for (const candidate of result.nodes) {
7634
7838
  const node = object(candidate);
7635
7839
  const metadata = object(node.metadataProvenance);
7636
- const entries = metadata.name;
7637
- if (typeof node.name !== "string" || !Array.isArray(entries) || entries.length === 0) continue;
7638
- if (!entries.every((candidateEntry) => {
7840
+ const entries2 = metadata.name;
7841
+ if (typeof node.name !== "string" || !Array.isArray(entries2) || entries2.length === 0) continue;
7842
+ if (!entries2.every((candidateEntry) => {
7639
7843
  const entry = object(candidateEntry);
7640
- return entry.value === node.name && Object.keys(entry).every((key) => key === "value" || key === "sourceEventIds") && Array.isArray(entry.sourceEventIds) && entry.sourceEventIds.length > 0 && entry.sourceEventIds.every((id) => typeof id === "string");
7844
+ return entry.value === node.name && Object.keys(entry).every((key2) => key2 === "value" || key2 === "sourceEventIds") && Array.isArray(entry.sourceEventIds) && entry.sourceEventIds.length > 0 && entry.sourceEventIds.every((id) => typeof id === "string");
7641
7845
  })) continue;
7642
- metadata.name = [...new Set(entries.flatMap((entry) => entry.sourceEventIds))];
7846
+ metadata.name = [...new Set(entries2.flatMap((entry) => entry.sourceEventIds))];
7643
7847
  }
7644
7848
  return value;
7645
7849
  }
@@ -7731,7 +7935,8 @@ Topic \u662F\u957F\u671F\u7684\u5927\u7AE0\u8282\uFF0C\u6309\u9879\u76EE\u3001\u
7731
7935
  \u5BFC\u822A\u5C42\u7EA7\u662F\u7AE0 \u2192 \u8282 \u2192 Event\u3002Event \u4FDD\u6301\u5177\u4F53\u3001\u72EC\u7ACB\uFF1B\u8282\u5FC5\u987B\u80FD\u6301\u7EED\u5BB9\u7EB3\u540C\u7C7B\u4E8B\u4EF6\u3002\u5355\u4E2A bug\u3001\u7248\u672C\u53F7\u3001\u67D0\u5929\u8FDB\u5C55\u3001\u4E00\u6B21\u5B89\u88C5\u6216\u4E00\u4E2A\u4FEE\u590D\u65B9\u6848\u4E0D\u80FD\u5355\u72EC\u6210\u8282\uFF0C\u4E5F\u4E0D\u8981\u7167\u6284 Event \u6807\u9898\u4F5C\u4E3A\u8282\u6807\u9898\u3002\u4F8B\u5982\u201C0.2.4 \u63D0\u53D6\u6839\u56E0\u66B4\u9732\u8FC7\u7A0B\u201D\u201C0.2.6 \u63D0\u53D6\u4ECD 0 \u4EA7\u51FA\u201D\u201C0.2.7 \u4FEE\u590D\u65B9\u6848\u201D\u201C\u5C01\u5757\u5F02\u5E38\u5F85\u6392\u67E5\u201D\u90FD\u8FDB\u5165\u201C\u7F3A\u9677\u6392\u67E5\u4E0E\u4FEE\u590D\u201D\uFF1B\u201C\u4ECA\u65E5\u7248\u672C\u8FED\u4EE3 0.2.1\u21920.2.7\u201D\u201C0.2.8 \u5B89\u88C5\u4E0E\u9A8C\u8BC1\u201D\u8FDB\u5165\u201C\u7248\u672C\u53D1\u5E03\u4E0E\u5B89\u88C5\u201D\u3002\u5177\u4F53\u7248\u672C\u3001\u65F6\u95F4\u3001\u6839\u56E0\u3001\u5904\u7F6E\u8FC7\u7A0B\u548C\u72B6\u6001\u653E\u5728\u8282\u5185\u6982\u8981\u53CA\u539F Event \u4E2D\u3002\u201C\u8BA1\u5212\u4FEE\u590D\u201D\u201C\u4FEE\u590D\u4E2D\u201D\u201C\u5DF2\u4FEE\u590D\u201D\u5C5E\u4E8E\u540C\u4E00\u7C7B\u522B\u7684\u4E0D\u540C\u72B6\u6001\uFF0C\u4E0D\u53E6\u8D77\u8282\u3002\u540C\u4E00\u7C7B\u522B\u53EF\u4EE5\u6709\u591A\u4E2A\u6709\u6765\u6E90\u7684\u6BB5\u843D\uFF0C\u4E0D\u8981\u4E3A\u6BB5\u843D\u53E6\u8D77\u6807\u9898\u3002
7732
7936
  \u5148\u627E\u672C\u7AE0\u5DF2\u6709\u7C7B\u522B\uFF1B\u5B83\u80FD\u5BB9\u7EB3\u65F6\u5FC5\u987B\u590D\u7528\u3002\u65B0\u7AE0\u5148\u5F52\u7EB3\u5C11\u91CF\u957F\u671F\u7C7B\u522B\uFF0C\u4E0D\u628A\u6BCF\u4E2A Event \u5404\u5EFA\u4E00\u8282\uFF1B\u540E\u7EED\u6279\u6B21\u6CBF\u7528\u8FD9\u4E9B\u7C7B\u522B\uFF0C\u6BCF\u6279\u901A\u5E38\u53EA\u65B0\u589E 0-2 \u4E2A\u8282\u3002\u7C7B\u522B\u6570\u91CF\u7531\u771F\u5B9E\u5185\u5BB9\u51B3\u5B9A\uFF0C\u4E0D\u8BBE\u786C\u8282\u6570\u4E0A\u9650\uFF0C\u4E5F\u4E0D\u8981\u628A\u65E0\u5173\u7C7B\u522B\u786C\u585E\u8FDB\u201C\u5176\u4ED6\u201D\u3002\u4EE5\u4E0A\u662F\u9879\u76EE\u7AE0\u7684\u4F8B\u5B50\uFF0C\u4E0D\u662F\u5168\u5C40\u56FA\u5B9A\u5206\u7C7B\u8868\uFF1B\u6C42\u804C\u7AE0\u53EF\u7528\u201C\u6295\u9012\u4E0E\u9762\u8BD5\u201D\u201C\u804C\u4E1A\u9009\u62E9\u201D\u201C\u5165\u804C\u4E0E\u9002\u5E94\u201D\uFF0C\u751F\u6D3B\u7AE0\u4F7F\u7528\u5176\u81EA\u8EAB\u9002\u5408\u7684\u957F\u671F\u7C7B\u522B\u3002\u7ADE\u54C1\u5206\u6790\u3001\u5B9E\u9A8C\u8BC4\u6D4B\u3001\u5B89\u5168\u4E0E\u9690\u79C1\u7B49\u5185\u5BB9\u9700\u8981\u65F6\u5404\u5F52\u5165\u76F8\u5E94\u7C7B\u522B\uFF0C\u4E0D\u7EDF\u4E00\u585E\u8FDB\u4FEE bug\u3002
7733
7937
 
7734
- title \u662F\u53EF\u8BC6\u522B\u7684\u4E3B\u9898\u540D\u79F0\uFF1Bdescription \u7528\u4E00\u53E5\u8BDD\u8BF4\u660E\u8FD9\u91CC\u5B58\u6709\u54EA\u4E9B\u8D44\u6599\uFF0C\u4F8B\u5982\u201C\u5305\u542B\u90E8\u7F72\u9009\u578B\u3001\u8FC1\u79FB\u7ECF\u8FC7\u548C\u9057\u7559\u95EE\u9898\u201D\uFF0C\u4E0D\u8981\u628A\u5386\u53F2\u7ED3\u8BBA\u5199\u6210\u5F53\u524D\u4E8B\u5B9E\u3002overview \u7684 kind \u4EC5\u5141\u8BB8 scope\uFF08\u80CC\u666F\u6216\u8986\u76D6\u8303\u56F4\uFF09\u3001history\uFF08\u5386\u53F2\u8FDB\u5C55\uFF09\u3001decision\uFF08\u5F53\u65F6\u7684\u51B3\u5B9A\uFF09\u3001change\uFF08\u6709\u4F9D\u636E\u7684\u53D8\u5316\uFF09\u3001open-question\uFF08\u5F85\u786E\u8BA4\u4E8B\u9879\uFF09\u3002\u4FDD\u7559\u660E\u786E\u65F6\u70B9\u3001\u8FC7\u53BB\u5F0F\u3001\u8BA1\u5212\u3001\u53D6\u6D88\u3001\u4E89\u8BAE\u548C\u4E0D\u786E\u5B9A\u6027\uFF1B\u6CA1\u6709\u65B0\u7684\u652F\u6301\u4E0D\u80FD\u628A\u65E7\u4E8B\u5B9E\u5347\u683C\u4E3A\u5F53\u524D\u7ED3\u8BBA\uFF0C\u4E0D\u80FD\u628A\u8BA1\u5212\u5199\u6210\u5DF2\u5B8C\u6210\u3001\u5EFA\u8BAE\u5199\u6210\u5DF2\u51B3\u5B9A\u3001\u63A8\u6D4B\u5199\u6210\u539F\u56E0\u3002\u51B3\u5B9A\u66FE\u7ECF\u6210\u7ACB\u4E0D\u7B49\u4E8E\u73B0\u5728\u4ECD\u6709\u6548\u3002\u76F8\u5173\u4E8B\u4EF6\u77DB\u76FE\u65F6\u4FDD\u7559\u77DB\u76FE\u548C\u5F85\u786E\u8BA4\u72B6\u6001\u3002
7938
+ title \u662F\u53EF\u8BC6\u522B\u7684\u4E3B\u9898\u540D\u79F0\uFF1Bdescription \u7528\u4E00\u53E5\u8BDD\u8BF4\u660E\u8FD9\u91CC\u5B58\u6709\u54EA\u4E9B\u8D44\u6599\uFF0C\u4F8B\u5982\u201C\u5305\u542B\u90E8\u7F72\u9009\u578B\u3001\u8FC1\u79FB\u7ECF\u8FC7\u548C\u9057\u7559\u95EE\u9898\u201D\uFF0C\u4E0D\u8981\u628A\u5386\u53F2\u7ED3\u8BBA\u5199\u6210\u5F53\u524D\u4E8B\u5B9E\u3002overviewRepairRequirements \u5982\u5B58\u5728\uFF0C\u8868\u793A\u6B64\u524D\u4E0D\u5408\u683C\u6982\u8981\u5FC5\u987B\u63D0\u4F9B\u5408\u6CD5\u66FF\u4EE3\uFF1A\u4FDD\u7559\u5BF9\u5E94 kind\u3001\u6765\u6E90\u7F16\u53F7\u548C\u81F3\u5C11 minimumEntries \u6761\uFF1B\u4E0D\u5F97\u5220\u9664\u6BB5\u843D\u3001\u6E05\u7A7A overview\u3001\u6362\u6389\u6765\u6E90\u6216\u964D\u4F4E\u4E8B\u5B9E\u6027\u8D28\u6765\u7ED5\u8FC7\u6821\u9A8C\u3002\u9996\u6B21\u6CA1\u6709\u4FEE\u590D\u7EA6\u675F\u65F6\uFF0C\u6B63\u5E38\u7684\u7A7A overview \u4ECD\u5408\u6CD5\u3002
7939
+ overview \u7684 kind \u4EC5\u5141\u8BB8 scope\uFF08\u80CC\u666F\u6216\u8986\u76D6\u8303\u56F4\uFF09\u3001history\uFF08\u5386\u53F2\u8FDB\u5C55\uFF09\u3001decision\uFF08\u5F53\u65F6\u7684\u51B3\u5B9A\uFF09\u3001change\uFF08\u6709\u4F9D\u636E\u7684\u53D8\u5316\uFF09\u3001open-question\uFF08\u5F85\u786E\u8BA4\u4E8B\u9879\uFF09\u3002\u4FDD\u7559\u660E\u786E\u65F6\u70B9\u3001\u8FC7\u53BB\u5F0F\u3001\u8BA1\u5212\u3001\u53D6\u6D88\u3001\u4E89\u8BAE\u548C\u4E0D\u786E\u5B9A\u6027\uFF1B\u6CA1\u6709\u65B0\u7684\u652F\u6301\u4E0D\u80FD\u628A\u65E7\u4E8B\u5B9E\u5347\u683C\u4E3A\u5F53\u524D\u7ED3\u8BBA\uFF0C\u4E0D\u80FD\u628A\u8BA1\u5212\u5199\u6210\u5DF2\u5B8C\u6210\u3001\u5EFA\u8BAE\u5199\u6210\u5DF2\u51B3\u5B9A\u3001\u63A8\u6D4B\u5199\u6210\u539F\u56E0\u3002\u51B3\u5B9A\u66FE\u7ECF\u6210\u7ACB\u4E0D\u7B49\u4E8E\u73B0\u5728\u4ECD\u6709\u6548\u3002\u76F8\u5173\u4E8B\u4EF6\u77DB\u76FE\u65F6\u4FDD\u7559\u77DB\u76FE\u548C\u5F85\u786E\u8BA4\u72B6\u6001\u3002
7735
7940
 
7736
7941
  sections \u72EC\u7ACB\u4FDD\u5B58\u4E8B\u4EF6\u7684\u5C0F\u8282\u5F52\u5C5E\uFF0C\u4E0E overview \u7684\u6765\u6E90\u5F15\u7528\u5206\u5F00\u3002\u6BCF\u4E2A\u5F52\u5165\u672C\u7AE0\u7684\u672C\u6279\u4E8B\u4EF6\u5FC5\u987B\u81F3\u5C11\u5206\u914D\u5230\u4E00\u4E2A sections \u5C0F\u8282\uFF0C\u5373\u4F7F\u603B\u89C8\u6CA1\u6709\u5F15\u7528\u5B83\u3002sections \u53EA\u5199\u672C\u6279\u4E8B\u4EF6\u7F16\u53F7\uFF1B\u65E7\u5F52\u5C5E\u7531\u5B58\u50A8\u5C42\u7EE7\u627F\u3002\u4F18\u5148\u9010\u5B57\u590D\u7528 existingTopics.sectionTitles \u4E2D\u9002\u5408\u7684\u6807\u9898\uFF1B\u5C0F\u8282\u6CBF\u7528\u4E0A\u8FF0\u957F\u671F\u7C7B\u522B\u89C4\u5219\u3002\u65B0 overview \u7684\u5C55\u793A\u6807\u9898\u5FC5\u987B\u5339\u914D\u672C\u8F6E sections.title \u6216\u91CD\u5206\u7C7B\u540E\u4ECD\u5B58\u5728\u7684\u5DF2\u6709\u5C0F\u8282\u6807\u9898\uFF0C\u4E0D\u80FD\u9760\u603B\u89C8\u5355\u72EC\u521B\u5EFA\u5C0F\u8282\uFF1B\u7701\u7565 title \u65F6\uFF0C\u5176 kind \u7684\u9ED8\u8BA4\u5C55\u793A\u6807\u9898\u4E5F\u5FC5\u987B\u6709\u5BF9\u5E94\u7684\u6700\u7EC8\u5C0F\u8282\u3002\u82E5\u91CD\u5206\u7C7B\u79FB\u8D70\u65E7\u5C0F\u8282\u7684\u5168\u90E8\u4E8B\u4EF6\uFF0C\u5B58\u50A8\u5C42\u4E22\u5F03\u8BE5\u8282\u7684\u65E7\u603B\u89C8\uFF0C\u5373\u4F7F\u672C\u8F6E\u539F\u6837\u56DE\u4F20\u4E5F\u4E0D\u4F1A\u4FDD\u7559\u3002overview.title \u53EA\u51B3\u5B9A\u6982\u62EC\u6587\u5B57\u663E\u793A\u5728\u54EA\u4E2A\u5C0F\u8282\uFF0C\u4E0D\u51B3\u5B9A\u4E8B\u4EF6\u5F52\u5C5E\uFF1B\u65E0\u9700\u4E3A\u6BCF\u6761\u4E8B\u4EF6\u5199\u603B\u89C8\uFF0C\u4E5F\u4E0D\u8981\u4E3A\u4E86\u8986\u76D6\u76EE\u5F55\u800C\u865A\u589E\u6765\u6E90\u5F15\u7528\u3002
7737
7942
  \u82E5\u8F93\u5165\u542B sectionBackfillTopicId\uFF0C\u8FD9\u662F\u5347\u7EA7\u540E\u7684\u65E7\u4E8B\u4EF6\u8865\u5F52\u7C7B\uFF1A\u53EA\u8FD4\u56DE\u8BE5 topicId \u7684\u4E00\u4E2A\u7AE0\u8282\uFF0C\u4FDD\u7559\u539F\u7AE0\u540D\u548C\u63CF\u8FF0\uFF0C\u4E3A\u5168\u90E8\u672C\u6279\u4E8B\u4EF6\u5199\u5165\u9002\u5F53\u7684\u5C0F\u8282\uFF1B\u4E0D\u5F97\u6362\u7AE0\u6216\u65B0\u5EFA\u7AE0\u3002\u5DF2\u6709\u5C0F\u8282\u6807\u9898\u53EF\u80FD\u53EA\u5C55\u793A\u524D 120 \u4E2A\uFF0C\u4F18\u5148\u590D\u7528\u9002\u5408\u7684\u5DF2\u6709\u6807\u9898\u3002\u6B64\u4EFB\u52A1\u65E0\u9700\u91CD\u5199\u603B\u89C8\uFF0Coverview \u53EF\u4EE5\u4E3A\u7A7A\uFF0C\u5B58\u50A8\u5C42\u4FDD\u7559\u6709\u6548\u65E7\u603B\u89C8\u3002
@@ -7901,6 +8106,7 @@ function topicProjectionPayload(context, outputTokenBudget) {
7901
8106
  ...context.sectionBackfillTopicId ? { sectionBackfillTopicId: context.sectionBackfillTopicId } : {},
7902
8107
  evidenceCompleteness: "bounded-navigation-cards; retrieve Event and original-source evidence before relying on factual content",
7903
8108
  outputTokenBudget,
8109
+ ...context.overviewRepairRequirements?.length ? { overviewRepairRequirements: context.overviewRepairRequirements } : {},
7904
8110
  events,
7905
8111
  truncatedEventIds: context.truncatedEventIds ?? [],
7906
8112
  existingTopics: existingTopics.map((topic) => ({
@@ -7928,7 +8134,7 @@ function parseTopicProjection(value, context) {
7928
8134
  throw new TopicProjectionError("validation-failed", message);
7929
8135
  };
7930
8136
  const raw = object(value);
7931
- if (Object.keys(raw).some((key) => key !== "topics") || !Array.isArray(raw.topics) || raw.topics.length > 12) {
8137
+ if (Object.keys(raw).some((key2) => key2 !== "topics") || !Array.isArray(raw.topics) || raw.topics.length > 12) {
7932
8138
  fail("expected only topics, with at most 12 entries");
7933
8139
  }
7934
8140
  const eventIds = new Set(context.events.map(({ id }) => id));
@@ -7948,13 +8154,13 @@ function parseTopicProjection(value, context) {
7948
8154
  return value2;
7949
8155
  };
7950
8156
  const rawTopics = raw.topics;
7951
- const topics = rawTopics.map((candidate) => {
8157
+ const topics = rawTopics.map((candidate, topicIndex) => {
7952
8158
  const item = object(candidate);
7953
8159
  const title = boundedText(item.title, "topic.title", 120);
7954
8160
  const label = title.normalize("NFKC").replace(/\s+/g, " ").toLowerCase();
7955
8161
  if (usedLabels.has(label)) fail("duplicate chapter label; combine its batch assignments");
7956
8162
  usedLabels.add(label);
7957
- if (Object.keys(item).some((key) => !["topicId", "title", "description", "sourceEventIds", "overview", "sections"].includes(key))) fail("unexpected topic field");
8163
+ if (Object.keys(item).some((key2) => !["topicId", "title", "description", "sourceEventIds", "overview", "sections"].includes(key2))) fail("unexpected topic field");
7958
8164
  const topicId = item.topicId === void 0 ? void 0 : boundedText(item.topicId, "topicId", 200);
7959
8165
  if (context.sectionBackfillTopicId && (rawTopics.length !== 1 || topicId !== context.sectionBackfillTopicId)) {
7960
8166
  fail("section backfill must update only its existing chapter");
@@ -7970,11 +8176,11 @@ function parseTopicProjection(value, context) {
7970
8176
  const sectionLabels = /* @__PURE__ */ new Set();
7971
8177
  const sections = item.sections.map((candidateSection) => {
7972
8178
  const section = object(candidateSection);
7973
- if (Object.keys(section).some((key2) => !["title", "sourceEventIds"].includes(key2))) fail("unexpected section field");
8179
+ if (Object.keys(section).some((key3) => !["title", "sourceEventIds"].includes(key3))) fail("unexpected section field");
7974
8180
  const title2 = boundedText(section.title, "section.title", 80);
7975
- const key = title2.normalize("NFKC").replace(/\s+/g, " ").toLowerCase();
7976
- if (sectionLabels.has(key)) fail("duplicate section title");
7977
- sectionLabels.add(key);
8181
+ const key2 = title2.normalize("NFKC").replace(/\s+/g, " ").toLowerCase();
8182
+ if (sectionLabels.has(key2)) fail("duplicate section title");
8183
+ sectionLabels.add(key2);
7978
8184
  return { title: title2, sourceEventIds: sourceIds(section.sourceEventIds, "section.sourceEventIds", new Set(sources2.filter((id) => eventIds.has(id)))) };
7979
8185
  });
7980
8186
  const assigned = new Set(sections.flatMap((section) => section.sourceEventIds));
@@ -7984,12 +8190,13 @@ function parseTopicProjection(value, context) {
7984
8190
  ...sections.map((section) => section.title),
7985
8191
  ...existing?.sectionTitles ?? memoryTopicMembershipSections(existing ?? { overview: [] }).map((section) => section.title)
7986
8192
  ];
7987
- const overview2 = item.overview.map((candidateEntry) => {
8193
+ const overview2 = item.overview.map((candidateEntry, overviewIndex) => {
7988
8194
  const entry = object(candidateEntry);
7989
- if (Object.keys(entry).some((key) => !["kind", "title", "text", "sourceEventIds"].includes(key))) fail("unexpected overview field");
8195
+ if (Object.keys(entry).some((key2) => !["kind", "title", "text", "sourceEventIds"].includes(key2))) fail("unexpected overview field");
7990
8196
  const sectionTitle = entry.title === void 0 ? void 0 : boundedText(entry.title, "overview.title", 80);
7991
8197
  if (!TOPIC_OVERVIEW_KINDS.includes(entry.kind)) fail("unknown overview kind");
7992
- const paragraph = boundedText(entry.text, "overview.text", 600);
8198
+ validateTopicOverviewText(entry.text, `topics[${topicIndex}].overview[${overviewIndex}].text`);
8199
+ const paragraph = entry.text;
7993
8200
  const paragraphSources = sourceIds(entry.sourceEventIds, "overview.sourceEventIds", new Set(sources2), 12);
7994
8201
  if (entry.kind !== "scope" && paragraphSources.some((id) => truncatedIds.has(id))) fail("truncated Event evidence may only support scope entries");
7995
8202
  const preserved = existing?.overview.some((old) => old.kind === entry.kind && old.title === sectionTitle && old.text === paragraph && old.sourceEventIds.length === paragraphSources.length && old.sourceEventIds.every((id) => paragraphSources.includes(id)));
@@ -8131,10 +8338,10 @@ Use project scope for repository decisions, user scope for stable preferences/id
8131
8338
  }).filter((event) => event !== null);
8132
8339
  const unique3 = /* @__PURE__ */ new Map();
8133
8340
  for (const event of candidates) {
8134
- const key = exactKeys.get(event);
8135
- const prior = unique3.get(key);
8341
+ const key2 = exactKeys.get(event);
8342
+ const prior = unique3.get(key2);
8136
8343
  if (!prior) {
8137
- unique3.set(key, event);
8344
+ unique3.set(key2, event);
8138
8345
  continue;
8139
8346
  }
8140
8347
  prior.sourceMessageIds = [.../* @__PURE__ */ new Set([...prior.sourceMessageIds, ...event.sourceMessageIds])];
@@ -8259,6 +8466,7 @@ Use project scope for repository decisions, user scope for stable preferences/id
8259
8466
  };
8260
8467
  topicProjector = async (context) => {
8261
8468
  const { payload, shownContext } = topicProjectionPayload(context, TOPIC_OUTPUT_TOKEN_BUDGET);
8469
+ const repairs = new TopicOverviewRepairGuard(context.overviewRepairRequirements);
8262
8470
  try {
8263
8471
  const raw = await this.callStructured(
8264
8472
  "topicProjector",
@@ -8266,16 +8474,18 @@ Use project scope for repository decisions, user scope for stable preferences/id
8266
8474
  payload,
8267
8475
  (value) => {
8268
8476
  parseTopicProjection(value, shownContext);
8477
+ repairs.assert(value);
8269
8478
  },
8270
- { eventCount: shownContext.events.length, candidateTopicCount: shownContext.existingTopics.length }
8479
+ { eventCount: shownContext.events.length, candidateTopicCount: shownContext.existingTopics.length },
8480
+ { guard: repairs, context: shownContext }
8271
8481
  );
8272
8482
  return parseTopicProjection(raw, shownContext);
8273
8483
  } catch (error) {
8274
- if (error instanceof TopicProjectionError) throw error;
8484
+ if (error instanceof TopicProjectionError) throw carryTopicOverviewRepairs(error, repairs.requirements);
8275
8485
  if (error instanceof Error && /^StrataGate structured model task timed out after \d+ms$/.test(error.message)) {
8276
- throw new TopicProjectionError("timeout", "structured model task timed out");
8486
+ throw carryTopicOverviewRepairs(new TopicProjectionError("timeout", "structured model task timed out"), repairs.requirements);
8277
8487
  }
8278
- throw new TopicProjectionError("provider-failed", "provider or route error");
8488
+ throw carryTopicOverviewRepairs(new TopicProjectionError("provider-failed", "provider or route error"), repairs.requirements);
8279
8489
  }
8280
8490
  };
8281
8491
  externalMemoryDecider = async (context) => {
@@ -8327,7 +8537,7 @@ Use project scope for repository decisions, user scope for stable preferences/id
8327
8537
  }
8328
8538
  return proposed;
8329
8539
  }
8330
- async callStructured(kind, system, payload, validate, topicMetadata) {
8540
+ async callStructured(kind, system, payload, validate, topicMetadata, topicRepair) {
8331
8541
  const execution = this.sessions.getStore();
8332
8542
  if (!execution) throw new Error("StrataGate model callback ran without an execution context");
8333
8543
  const baseRoute = this.resolveRoute(execution.session);
@@ -8345,11 +8555,13 @@ Use project scope for repository decisions, user scope for stable preferences/id
8345
8555
  let attemptsUsed = 0;
8346
8556
  let noAdapterRetried = false;
8347
8557
  let graphRetryFeedback = "";
8558
+ let topicRetryFeedback = "";
8348
8559
  let offFallback = false;
8349
8560
  for (let attempt = 1; attempt <= JSON_RESPONSE_ATTEMPTS; attempt += 1) {
8350
8561
  attemptsUsed = attempt;
8562
+ const requestPayload = topicRepair?.guard.requirements.length ? { ...payload, overviewRepairRequirements: topicRepair.guard.requirements } : payload;
8351
8563
  const message = createUserMessage({
8352
- content: [{ type: "text", text: JSON.stringify(payload) }],
8564
+ content: [{ type: "text", text: JSON.stringify(requestPayload) }],
8353
8565
  source: dshMessageSource()
8354
8566
  });
8355
8567
  const assembler = new BlockAssembler();
@@ -8359,7 +8571,7 @@ Use project scope for repository decisions, user scope for stable preferences/id
8359
8571
  messages: [message],
8360
8572
  system: attempt === 1 ? system : `${system}
8361
8573
 
8362
- ${JSON_RETRY_INSTRUCTION}${graphRetryFeedback}`,
8574
+ ${JSON_RETRY_INSTRUCTION}${graphRetryFeedback}${topicRetryFeedback}`,
8363
8575
  tools: [{
8364
8576
  name: STRUCTURED_TOOLS[kind].name,
8365
8577
  description: STRUCTURED_TOOLS[kind].description,
@@ -8383,7 +8595,7 @@ ${JSON_RETRY_INSTRUCTION}${graphRetryFeedback}`,
8383
8595
  }
8384
8596
  if (window !== void 0) request.maxTokens = Math.min(TOPIC_OUTPUT_TOKEN_BUDGET, Math.floor(window - inputTokens - 1024));
8385
8597
  request.messages = [{ ...message, content: [{ type: "text", text: JSON.stringify({
8386
- ...payload,
8598
+ ...requestPayload,
8387
8599
  outputTokenBudget: request.maxTokens
8388
8600
  }) }] }];
8389
8601
  inputTokens = estimateInput();
@@ -8474,10 +8686,20 @@ ${JSON_RETRY_INSTRUCTION}${graphRetryFeedback}`,
8474
8686
  );
8475
8687
  }
8476
8688
  }
8689
+ topicRepair?.guard.observe(parsed, topicRepair.context);
8477
8690
  if (kind === "graphProjector") parsed = normalizeGraphNameProvenance(parsed);
8478
8691
  const violations = validateArgs(STRUCTURED_TOOLS[kind].parameters, parsed);
8479
8692
  if (violations.length > 0) {
8480
- if (isTopic) throw topicError("schema-invalid", "structured topic schema mismatch");
8693
+ if (isTopic) {
8694
+ const topics = object(parsed).topics;
8695
+ if (Array.isArray(topics)) topics.forEach((topic, topicIndex) => {
8696
+ const overview2 = object(topic).overview;
8697
+ if (Array.isArray(overview2)) overview2.forEach((part, overviewIndex) => {
8698
+ validateTopicOverviewText(object(part).text, `topics[${topicIndex}].overview[${overviewIndex}].text`, "schema-invalid");
8699
+ });
8700
+ });
8701
+ throw topicError("schema-invalid", "structured topic schema mismatch");
8702
+ }
8481
8703
  if (kind === "graphProjector") {
8482
8704
  const paths = violations.flatMap((violation) => violation.match(/nodes\[\d+\]\.metadataProvenance\.name(?:\[\d+\])?/g) ?? []);
8483
8705
  graphRetryFeedback = paths.length > 0 ? `
@@ -8500,7 +8722,7 @@ Correct these canonical-name provenance fields: ${[...new Set(paths)].slice(0, 2
8500
8722
  try {
8501
8723
  validate?.(parsed);
8502
8724
  } catch (error) {
8503
- if (isTopic) throw topicError("validation-failed", error instanceof TopicProjectionError ? error.diagnostics.reason : "topic semantic validation failed");
8725
+ if (isTopic) throw error instanceof TopicProjectionError ? new TopicProjectionError(error.diagnostics.category, error.diagnostics.reason, { ...topicDiagnostics, ...error.diagnostics }) : topicError("validation-failed", "topic semantic validation failed");
8504
8726
  throw new ModelJsonResponseError(
8505
8727
  `StrataGate ${expectedTool} arguments were invalid: ${error instanceof Error ? error.message : String(error)}`,
8506
8728
  { cause: error, response: responseForError }
@@ -8518,10 +8740,17 @@ Correct these canonical-name provenance fields: ${[...new Set(paths)].slice(0, 2
8518
8740
  return parsed;
8519
8741
  } catch (error) {
8520
8742
  if (!(error instanceof ModelJsonResponseError) && !(isTopic && error instanceof TopicProjectionError)) throw error;
8743
+ let retryError = error;
8744
+ if (isTopic && error instanceof TopicProjectionError) {
8745
+ const safe = new TopicProjectionError(error.diagnostics.category, error.diagnostics.reason, { ...topicDiagnostics, ...error.diagnostics });
8746
+ topicRetryFeedback = `
8747
+ Correct the previous Topic output using these validation errors: ${JSON.stringify(safe.diagnostics)}. Return the complete result; preserve evidence, references and Event assignments. Do not truncate, delete or fabricate content to bypass validation.`;
8748
+ retryError = safe;
8749
+ }
8521
8750
  lastError = finish.kind === "max-tokens" ? new ModelJsonResponseError(
8522
8751
  `StrataGate ${STRUCTURED_TOOLS[kind].name} call was truncated before valid arguments`,
8523
- { cause: error, response: responseForError }
8524
- ) : error;
8752
+ { cause: retryError, response: responseForError }
8753
+ ) : retryError;
8525
8754
  if ((kind === "graphProjector" || kind === "topicProjector") && finish.kind === "max-tokens") break;
8526
8755
  if (attempt < JSON_RESPONSE_ATTEMPTS) {
8527
8756
  this.ctx.logger.warn(`stratagate-memory model returned an invalid structured tool call; retrying (${attempt}/${JSON_RESPONSE_ATTEMPTS})`);
@@ -8536,18 +8765,18 @@ Correct these canonical-name provenance fields: ${[...new Set(paths)].slice(0, 2
8536
8765
  );
8537
8766
  }
8538
8767
  async shouldUseOff(route) {
8539
- const key = `${route.provider}\0${route.model}`;
8540
- const cached = this.offCapabilities.get(key);
8768
+ const key2 = `${route.provider}\0${route.model}`;
8769
+ const cached = this.offCapabilities.get(key2);
8541
8770
  if (cached) {
8542
8771
  if (cached === "unsupported" && this.currentStructuredReasoningEffort() === "force-off") {
8543
- this.warnOffFallbackOnce(key, `${route.provider}/${route.model} does not support reasoningEffort=off; using the model default`);
8772
+ this.warnOffFallbackOnce(key2, `${route.provider}/${route.model} does not support reasoningEffort=off; using the model default`);
8544
8773
  }
8545
8774
  return cached === "supported";
8546
8775
  }
8547
8776
  if (typeof this.ctx.llm.resolveModelInfo !== "function") {
8548
- this.offCapabilities.set(key, "unsupported");
8777
+ this.offCapabilities.set(key2, "unsupported");
8549
8778
  if (this.currentStructuredReasoningEffort() === "force-off") {
8550
- this.warnOffFallbackOnce(key, `${route.provider}/${route.model} capabilities are unavailable; using the model default`);
8779
+ this.warnOffFallbackOnce(key2, `${route.provider}/${route.model} capabilities are unavailable; using the model default`);
8551
8780
  }
8552
8781
  return false;
8553
8782
  }
@@ -8565,18 +8794,18 @@ Correct these canonical-name provenance fields: ${[...new Set(paths)].slice(0, 2
8565
8794
  timer.unref?.();
8566
8795
  })
8567
8796
  ]);
8568
- if (Number.isFinite(info.context?.contextWindow) && info.context.contextWindow > 0) this.contextWindows.set(key, info.context.contextWindow);
8797
+ if (Number.isFinite(info.context?.contextWindow) && info.context.contextWindow > 0) this.contextWindows.set(key2, info.context.contextWindow);
8569
8798
  if (!info.reasoning) return this.currentStructuredReasoningEffort() === "force-off";
8570
8799
  const supported = info.reasoning.efforts.some(({ id }) => String(id) === "off");
8571
- this.offCapabilities.set(key, supported ? "supported" : "unsupported");
8800
+ this.offCapabilities.set(key2, supported ? "supported" : "unsupported");
8572
8801
  if (!supported && this.currentStructuredReasoningEffort() === "force-off") {
8573
- this.warnOffFallbackOnce(key, `${route.provider}/${route.model} does not support reasoningEffort=off; using the model default`);
8802
+ this.warnOffFallbackOnce(key2, `${route.provider}/${route.model} does not support reasoningEffort=off; using the model default`);
8574
8803
  }
8575
8804
  return supported;
8576
8805
  } catch {
8577
- this.offCapabilities.set(key, "unsupported");
8806
+ this.offCapabilities.set(key2, "unsupported");
8578
8807
  if (this.currentStructuredReasoningEffort() === "force-off") {
8579
- this.warnOffFallbackOnce(key, `${route.provider}/${route.model} capability lookup failed; using the model default`);
8808
+ this.warnOffFallbackOnce(key2, `${route.provider}/${route.model} capability lookup failed; using the model default`);
8580
8809
  }
8581
8810
  return false;
8582
8811
  } finally {
@@ -8752,15 +8981,15 @@ var DshMetadataStore = class {
8752
8981
  setLastFeedbackPromptAt(value) {
8753
8982
  this.setSettingValue("lastFeedbackPromptAt", value);
8754
8983
  }
8755
- setSetting(key, value) {
8756
- this.setSettingValue(key, String(value));
8984
+ setSetting(key2, value) {
8985
+ this.setSettingValue(key2, String(value));
8757
8986
  }
8758
- setSettingValue(key, value) {
8987
+ setSettingValue(key2, value) {
8759
8988
  this.database.prepare(`
8760
8989
  INSERT INTO stratagate_dsh_settings (key, value, updated_at)
8761
8990
  VALUES (?, ?, ?)
8762
8991
  ON CONFLICT (key) DO UPDATE SET value = excluded.value, updated_at = excluded.updated_at
8763
- `).run(key, value, (/* @__PURE__ */ new Date()).toISOString());
8992
+ `).run(key2, value, (/* @__PURE__ */ new Date()).toISOString());
8764
8993
  }
8765
8994
  workspaceName(namespace) {
8766
8995
  const row = this.database.prepare("SELECT display_name FROM stratagate_dsh_workspaces WHERE namespace = ?").get(namespace);
@@ -8803,6 +9032,7 @@ import { dirname, resolve } from "node:path";
8803
9032
  import { DatabaseSync as DatabaseSync3 } from "node:sqlite";
8804
9033
  import { createUserMessage as createUserMessage2 } from "@deepseek-ai/dsh-llm";
8805
9034
  import { openNativePath } from "@deepseek-ai/dsh-native-command";
9035
+ import { validateArgs as validateArgs2 } from "@deepseek-ai/dsh-tools";
8806
9036
 
8807
9037
  // src/fold.ts
8808
9038
  function renderBlocks(blocks) {
@@ -8936,10 +9166,10 @@ function memoryTopicSections(topic) {
8936
9166
  });
8937
9167
  const sections = /* @__PURE__ */ new Map();
8938
9168
  for (const membership of memoryTopicMembershipSections(topic)) {
8939
- const key = memoryTopicSectionKey(membership.title);
8940
- const section = sections.get(key) ?? { key, title: membership.title, paragraphs: [], sourceEventIds: [] };
9169
+ const key2 = memoryTopicSectionKey(membership.title);
9170
+ const section = sections.get(key2) ?? { key: key2, title: membership.title, paragraphs: [], sourceEventIds: [] };
8941
9171
  section.sourceEventIds.push(...membership.sourceEventIds.filter((id) => memberPositions.has(id)));
8942
- sections.set(key, section);
9172
+ sections.set(key2, section);
8943
9173
  }
8944
9174
  for (const part of topic.overview) {
8945
9175
  const title = memoryTopicSectionTitle(part);
@@ -9008,7 +9238,7 @@ function sortMemoryTopics(topics) {
9008
9238
  }
9009
9239
  function topicNavigation(topics, events) {
9010
9240
  const byId = new Map(events.map((event) => [event.id, event]));
9011
- const entries = sortMemoryTopics(topics).map((topic) => ({
9241
+ const entries2 = sortMemoryTopics(topics).map((topic) => ({
9012
9242
  id: topic.id,
9013
9243
  title: topic.title,
9014
9244
  description: topic.description,
@@ -9019,24 +9249,24 @@ function topicNavigation(topics, events) {
9019
9249
  }));
9020
9250
  const categories2 = CATEGORIES.map((category) => ({
9021
9251
  ...category,
9022
- count: entries.filter((entry) => entry.category === category.id).length
9252
+ count: entries2.filter((entry) => entry.category === category.id).length
9023
9253
  })).filter(({ count }) => count > 0);
9024
- return { entries, categories: categories2 };
9254
+ return { entries: entries2, categories: categories2 };
9025
9255
  }
9026
9256
  function topicPage(topics, events, options = {}) {
9027
9257
  const navigation = topicNavigation(topics, events);
9028
9258
  if (options.category && !CATEGORIES.some(({ id }) => id === options.category)) throw new TypeError("Unknown topic category");
9029
- let entries = options.category ? navigation.entries.filter(({ category }) => category === options.category) : navigation.entries;
9259
+ let entries2 = options.category ? navigation.entries.filter(({ category }) => category === options.category) : navigation.entries;
9030
9260
  const query = options.query?.trim().slice(0, 500);
9031
- if (query) entries = bm25Rank(entries, query, (entry) => weightedSearchTokens([[entry.title, 4], [entry.description, 2]])).map(({ item }) => item);
9261
+ if (query) entries2 = bm25Rank(entries2, query, (entry) => weightedSearchTokens([[entry.title, 4], [entry.description, 2]])).map(({ item }) => item);
9032
9262
  const offset = Number.isFinite(options.offset) ? Math.max(0, Math.floor(options.offset)) : 0;
9033
9263
  const limit = Number.isFinite(options.limit) ? Math.max(1, Math.min(20, Math.floor(options.limit))) : 12;
9034
- const page = entries.slice(offset, offset + limit);
9264
+ const page = entries2.slice(offset, offset + limit);
9035
9265
  return {
9036
9266
  navigationOnly: true,
9037
- total: entries.length,
9267
+ total: entries2.length,
9038
9268
  offset,
9039
- nextOffset: offset + page.length < entries.length ? offset + page.length : null,
9269
+ nextOffset: offset + page.length < entries2.length ? offset + page.length : null,
9040
9270
  categories: navigation.categories,
9041
9271
  topics: page
9042
9272
  };
@@ -9045,11 +9275,11 @@ function lineText(value, maximum) {
9045
9275
  return value.replace(/\s+/g, " ").trim().slice(0, maximum);
9046
9276
  }
9047
9277
  function renderMemoryDirectory(topics, events) {
9048
- const { entries, categories: categories2 } = topicNavigation(topics, events);
9049
- if (entries.length === 0) return "";
9278
+ const { entries: entries2, categories: categories2 } = topicNavigation(topics, events);
9279
+ if (entries2.length === 0) return "";
9050
9280
  const header = "[StrataGate \u8BB0\u5FC6\u76EE\u5F55]\n\u4EC5\u4F9B\u5BFC\u822A\uFF0C\u4E0D\u662F\u4E8B\u5B9E\u8BC1\u636E\u3002\u9700\u8981\u4E3B\u9898\u8109\u7EDC\u65F6\u7528 memory_expand_topic\uFF1B\u9700\u8981\u4E8B\u5B9E\u8BC1\u636E\u53EF\u76F4\u63A5 memory_search_events(topic_id)\uFF0C\u4E5F\u53EF\u76F4\u63A5\u641C\u7D22\u4E8B\u4EF6/\u56FE\u8C31\u3002";
9051
9281
  const byId = new Map(topics.map((topic) => [topic.id, topic]));
9052
- const chapters = entries.map((entry) => ({
9282
+ const chapters = entries2.map((entry) => ({
9053
9283
  ...entry,
9054
9284
  line: `- ${entry.id}\uFF1A${lineText(entry.title, 64)}${entry.description ? `\uFF1B${lineText(entry.description, 48)}` : ""}`,
9055
9285
  // Reuse the book's stable section order, including summary-free and legacy sections.
@@ -9061,7 +9291,7 @@ ${selected2.flatMap(({ line, sections }) => [line, ...sections]).join("\n")}`.tr
9061
9291
  if (estimateTokens(complete) <= MEMORY_DIRECTORY_TOKEN_BUDGET) return complete;
9062
9292
  const branches = categories2.map(({ id, label, count }) => `${id}\uFF08${label} ${count}\uFF09`).join("\uFF1B");
9063
9293
  const navigation = `${header}
9064
- \u5171 ${entries.length} \u9879\uFF1B\u5206\u7C7B\uFF1A${branches}\u3002\u76EE\u5F55\u5DF2\u88C1\u526A\uFF1B\u5B8C\u6574\u76EE\u5F55\u7528 memory_list_topics(category, offset) \u5206\u9875\uFF1Bquery \u53EF\u67E5\u4E3B\u9898\uFF0C\u5C0F\u8282\u7528 memory_expand_topic(id)\u3002`;
9294
+ \u5171 ${entries2.length} \u9879\uFF1B\u5206\u7C7B\uFF1A${branches}\u3002\u76EE\u5F55\u5DF2\u88C1\u526A\uFF1B\u5B8C\u6574\u76EE\u5F55\u7528 memory_list_topics(category, offset) \u5206\u9875\uFF1Bquery \u53EF\u67E5\u4E3B\u9898\uFF0C\u5C0F\u8282\u7528 memory_expand_topic(id)\u3002`;
9065
9295
  let selected = chapters.map((chapter) => ({ ...chapter, sections: [] }));
9066
9296
  if (estimateTokens(render(navigation, selected)) > MEMORY_DIRECTORY_TOKEN_BUDGET) {
9067
9297
  selected = [];
@@ -9077,7 +9307,7 @@ ${selected2.flatMap(({ line, sections }) => [line, ...sections]).join("\n")}`.tr
9077
9307
  for (const category of categories2) admit(chapters.find((chapter) => chapter.category === category.id));
9078
9308
  for (const chapter of chapters) if (!selected.some(({ id }) => id === chapter.id)) admit(chapter);
9079
9309
  }
9080
- const order = new Map(entries.map(({ id }, index) => [id, index]));
9310
+ const order = new Map(entries2.map(({ id }, index) => [id, index]));
9081
9311
  selected.sort((a, b) => order.get(a.id) - order.get(b.id));
9082
9312
  const sectionLines = new Map(chapters.map(({ id, sections }) => [id, sections]));
9083
9313
  const exhausted = /* @__PURE__ */ new Set();
@@ -9159,13 +9389,13 @@ var FEEDBACK_MARKDOWN_HEADINGS = {
9159
9389
  };
9160
9390
  function patchFeedbackMarkdown(markdown, input) {
9161
9391
  let result = markdown.trim();
9162
- for (const [key, heading] of Object.entries(FEEDBACK_MARKDOWN_HEADINGS)) {
9163
- if (!Object.prototype.hasOwnProperty.call(input, key)) continue;
9164
- const raw = input[key];
9392
+ for (const [key2, heading] of Object.entries(FEEDBACK_MARKDOWN_HEADINGS)) {
9393
+ if (!Object.prototype.hasOwnProperty.call(input, key2)) continue;
9394
+ const raw = input[key2];
9165
9395
  const text3 = Array.isArray(raw) ? raw.map((item, index) => {
9166
9396
  const value = feedbackText(item, 2e3);
9167
9397
  return value ? `${index + 1}. ${value}` : "";
9168
- }).filter(Boolean).join("\n") : feedbackText(raw, key === "description" ? 2e4 : key === "expected" || key === "actual" ? 1e4 : 2e4);
9398
+ }).filter(Boolean).join("\n") : feedbackText(raw, key2 === "description" ? 2e4 : key2 === "expected" || key2 === "actual" ? 1e4 : 2e4);
9169
9399
  const marker = `## ${heading}`;
9170
9400
  const lines = result.split("\n");
9171
9401
  let fence = null;
@@ -9207,9 +9437,9 @@ ${text3}`;
9207
9437
  return result.trim();
9208
9438
  }
9209
9439
  function normalizeFeedbackDraft(input, previous) {
9210
- const has = (key) => Object.prototype.hasOwnProperty.call(input, key);
9440
+ const has = (key2) => Object.prototype.hasOwnProperty.call(input, key2);
9211
9441
  const replacesStructuredBody = has("bodyMarkdown");
9212
- const updatesStructuredBody = ["description", "reproduction", "expected", "actual", "errorContext"].some((key) => has(key));
9442
+ const updatesStructuredBody = ["description", "reproduction", "expected", "actual", "errorContext"].some((key2) => has(key2));
9213
9443
  const reproduction = replacesStructuredBody ? [] : has("reproduction") ? (Array.isArray(input.reproduction) ? input.reproduction : []).map((value) => feedbackText(value, 2e3)).filter(Boolean).slice(0, 20) : previous?.reproduction ?? [];
9214
9444
  const bodyMarkdown = has("bodyMarkdown") ? feedbackText(input.bodyMarkdown, 5e4) : updatesStructuredBody && previous?.bodyMarkdown ? patchFeedbackMarkdown(previous.bodyMarkdown, input) : previous?.bodyMarkdown;
9215
9445
  return {
@@ -9392,7 +9622,7 @@ var StrataGateRuntime = class {
9392
9622
  ...this.derivationRuns.keys(),
9393
9623
  ...this.migrationTimers.keys(),
9394
9624
  ...this.migrationRuns.keys()
9395
- ].some((key) => key.startsWith(`${namespace}\0`) || key === namespace);
9625
+ ].some((key2) => key2.startsWith(`${namespace}\0`) || key2 === namespace);
9396
9626
  if (hasActiveSessionWork) return;
9397
9627
  if (!this.models.isReady()) return;
9398
9628
  const resumed = await this.models.runDetached(
@@ -9491,24 +9721,24 @@ var StrataGateRuntime = class {
9491
9721
  if (!this.config.ingestSubagents && session.header.origin === "subagent") return;
9492
9722
  const turn = this.folder.accept(session, event);
9493
9723
  if (!turn) return;
9494
- const key = String(session.id);
9495
- this.sessionsById.set(key, session);
9496
- const queued = this.pendingTurns.get(key) ?? [];
9724
+ const key2 = String(session.id);
9725
+ this.sessionsById.set(key2, session);
9726
+ const queued = this.pendingTurns.get(key2) ?? [];
9497
9727
  queued.push(turn);
9498
- this.pendingTurns.set(key, queued);
9728
+ this.pendingTurns.set(key2, queued);
9499
9729
  this.scheduleDrain(session);
9500
9730
  }
9501
9731
  /** Serialize drains per session while allowing unrelated sessions to settle independently. */
9502
9732
  enqueueDrain(session) {
9503
- const key = String(session.id);
9504
- const prior = this.drainTails.get(key) ?? Promise.resolve();
9733
+ const key2 = String(session.id);
9734
+ const prior = this.drainTails.get(key2) ?? Promise.resolve();
9505
9735
  const next = prior.catch(() => {
9506
9736
  }).then(() => this.drain(session));
9507
- this.drainTails.set(key, next);
9737
+ this.drainTails.set(key2, next);
9508
9738
  void next.then(() => {
9509
- if ((this.pendingTurns.get(key)?.length ?? 0) === 0) this.drainErrors.delete(key);
9739
+ if ((this.pendingTurns.get(key2)?.length ?? 0) === 0) this.drainErrors.delete(key2);
9510
9740
  }, (error) => {
9511
- this.drainErrors.set(key, error);
9741
+ this.drainErrors.set(key2, error);
9512
9742
  this.notePluginError(session, error);
9513
9743
  this.onIngestError(error);
9514
9744
  });
@@ -9516,46 +9746,46 @@ var StrataGateRuntime = class {
9516
9746
  }
9517
9747
  /** Batch short bursts and retry failed drains with bounded exponential backoff. */
9518
9748
  scheduleDrain(session, overrideDelayMs) {
9519
- const key = String(session.id);
9749
+ const key2 = String(session.id);
9520
9750
  if (this.closed) return;
9521
- const queued = this.pendingTurns.get(key)?.length ?? 0;
9751
+ const queued = this.pendingTurns.get(key2)?.length ?? 0;
9522
9752
  if (queued === 0) return;
9523
- const delay = overrideDelayMs ?? (queued >= DRAIN_THRESHOLD ? DRAIN_EAGER_MS : Math.min(this.drainBackoffMs.get(key) ?? DRAIN_BASE_BACKOFF_MS, DRAIN_MAX_BACKOFF_MS));
9524
- const pending = this.drainTimers.get(key);
9753
+ const delay = overrideDelayMs ?? (queued >= DRAIN_THRESHOLD ? DRAIN_EAGER_MS : Math.min(this.drainBackoffMs.get(key2) ?? DRAIN_BASE_BACKOFF_MS, DRAIN_MAX_BACKOFF_MS));
9754
+ const pending = this.drainTimers.get(key2);
9525
9755
  if (pending) {
9526
9756
  if (delay >= DRAIN_BASE_BACKOFF_MS) return;
9527
9757
  clearTimeout(pending);
9528
- this.drainTimers.delete(key);
9758
+ this.drainTimers.delete(key2);
9529
9759
  }
9530
9760
  const timer = setTimeout(() => {
9531
- this.drainTimers.delete(key);
9761
+ this.drainTimers.delete(key2);
9532
9762
  if (this.closed) return;
9533
9763
  void this.enqueueDrain(session).then(() => {
9534
- this.drainBackoffMs.delete(key);
9535
- if ((this.pendingTurns.get(key)?.length ?? 0) > 0) {
9764
+ this.drainBackoffMs.delete(key2);
9765
+ if ((this.pendingTurns.get(key2)?.length ?? 0) > 0) {
9536
9766
  this.scheduleDrain(session, DRAIN_BASE_BACKOFF_MS);
9537
9767
  }
9538
9768
  }, () => {
9539
- const backoff = this.drainBackoffMs.get(key) ?? DRAIN_BASE_BACKOFF_MS;
9540
- this.drainBackoffMs.set(key, Math.min(backoff * 2, DRAIN_MAX_BACKOFF_MS));
9541
- const pendingRetry = this.drainTimers.get(key);
9769
+ const backoff = this.drainBackoffMs.get(key2) ?? DRAIN_BASE_BACKOFF_MS;
9770
+ this.drainBackoffMs.set(key2, Math.min(backoff * 2, DRAIN_MAX_BACKOFF_MS));
9771
+ const pendingRetry = this.drainTimers.get(key2);
9542
9772
  if (pendingRetry) clearTimeout(pendingRetry);
9543
- this.drainTimers.delete(key);
9773
+ this.drainTimers.delete(key2);
9544
9774
  this.scheduleDrain(session);
9545
9775
  });
9546
9776
  }, delay);
9547
9777
  timer.unref?.();
9548
- this.drainTimers.set(key, timer);
9778
+ this.drainTimers.set(key2, timer);
9549
9779
  }
9550
9780
  /**
9551
9781
  * Persist one detached batch. On failure, the failed and unvisited turns are
9552
9782
  * restored ahead of turns that arrived meanwhile, preserving original order.
9553
9783
  */
9554
9784
  async drain(session) {
9555
- const key = String(session.id);
9556
- const turns = this.pendingTurns.get(key);
9785
+ const key2 = String(session.id);
9786
+ const turns = this.pendingTurns.get(key2);
9557
9787
  if (!turns || turns.length === 0) return;
9558
- this.pendingTurns.delete(key);
9788
+ this.pendingTurns.delete(key2);
9559
9789
  let completed = 0;
9560
9790
  let memory;
9561
9791
  try {
@@ -9568,7 +9798,7 @@ var StrataGateRuntime = class {
9568
9798
  } catch (error) {
9569
9799
  const remaining = turns.slice(completed);
9570
9800
  if (remaining.length > 0) {
9571
- this.pendingTurns.set(key, [...remaining, ...this.pendingTurns.get(key) ?? []]);
9801
+ this.pendingTurns.set(key2, [...remaining, ...this.pendingTurns.get(key2) ?? []]);
9572
9802
  }
9573
9803
  throw error;
9574
9804
  } finally {
@@ -9803,7 +10033,7 @@ var StrataGateRuntime = class {
9803
10033
  };
9804
10034
  }
9805
10035
  async recordUse(session, receiptId, evidenceRefs, batchId) {
9806
- const key = String(session.id);
10036
+ const key2 = String(session.id);
9807
10037
  const batch = this.requireBatch(session, batchId, "memory_record_use");
9808
10038
  if (batch.status !== "unresolved") {
9809
10039
  throw new Error(this.batchError(
@@ -9882,9 +10112,9 @@ var StrataGateRuntime = class {
9882
10112
  eventIds: [...eventIds],
9883
10113
  elementIds: [...elementIds]
9884
10114
  }, {
9885
- receiptId: `dsh:${key}:tool:${receiptId}`,
10115
+ receiptId: `dsh:${key2}:tool:${receiptId}`,
9886
10116
  audit: {
9887
- sessionId: key,
10117
+ sessionId: key2,
9888
10118
  ...turn === void 0 ? {} : { turn },
9889
10119
  batchId: batch.id,
9890
10120
  evidenceRefs: selectedRefs,
@@ -10048,9 +10278,9 @@ var StrataGateRuntime = class {
10048
10278
  const contexts = new Map(memory.getBlockContext(threadId).map((context) => [context.id, context]));
10049
10279
  const existing = currentBlockSurfaceMessages(session);
10050
10280
  let changed = false;
10051
- for (const [key, { blockId }] of pending) {
10281
+ for (const [key2, { blockId }] of pending) {
10052
10282
  if (existing.has(blockId)) {
10053
- this.pendingSurfaceSync.delete(key);
10283
+ this.pendingSurfaceSync.delete(key2);
10054
10284
  continue;
10055
10285
  }
10056
10286
  const block = memory.listBlocks().find((candidate) => candidate.id === blockId && candidate.threadId === threadId);
@@ -10058,7 +10288,7 @@ var StrataGateRuntime = class {
10058
10288
  if (!block || block.processingStatus !== "ready" || !context) continue;
10059
10289
  try {
10060
10290
  changed = this.replaceSealedSurface(session, block, context, dshTurnAtBlockEnd(session, block)) || changed;
10061
- this.pendingSurfaceSync.delete(key);
10291
+ this.pendingSurfaceSync.delete(key2);
10062
10292
  } catch (error) {
10063
10293
  this.onIngestError(error);
10064
10294
  }
@@ -10068,11 +10298,11 @@ var StrataGateRuntime = class {
10068
10298
  // Keep the ingestion error for callers that explicitly require a flushed run.
10069
10299
  async settleIngestion() {
10070
10300
  do {
10071
- for (const [key, session] of this.sessionsById) {
10072
- if ((this.pendingTurns.get(key)?.length ?? 0) === 0) continue;
10073
- const timer = this.drainTimers.get(key);
10301
+ for (const [key2, session] of this.sessionsById) {
10302
+ if ((this.pendingTurns.get(key2)?.length ?? 0) === 0) continue;
10303
+ const timer = this.drainTimers.get(key2);
10074
10304
  if (timer) clearTimeout(timer);
10075
- this.drainTimers.delete(key);
10305
+ this.drainTimers.delete(key2);
10076
10306
  this.enqueueDrain(session);
10077
10307
  }
10078
10308
  await Promise.allSettled(this.drainTails.values());
@@ -10135,16 +10365,16 @@ var StrataGateRuntime = class {
10135
10365
  };
10136
10366
  }
10137
10367
  adminFeedbackDraft(namespace) {
10138
- const key = namespace.trim();
10139
- if (!key) throw new TypeError("StrataGate feedback namespace must not be empty");
10140
- return { namespace: key, draft: this.loadFeedbackDraft(key) };
10368
+ const key2 = namespace.trim();
10369
+ if (!key2) throw new TypeError("StrataGate feedback namespace must not be empty");
10370
+ return { namespace: key2, draft: this.loadFeedbackDraft(key2) };
10141
10371
  }
10142
10372
  adminSaveFeedbackDraft(namespace, input) {
10143
- const key = namespace.trim();
10144
- if (!key) throw new TypeError("StrataGate feedback namespace must not be empty");
10145
- const draft = normalizeFeedbackDraft(input, this.loadFeedbackDraft(key));
10146
- this.saveFeedbackDraft(key, draft);
10147
- return { namespace: key, draft };
10373
+ const key2 = namespace.trim();
10374
+ if (!key2) throw new TypeError("StrataGate feedback namespace must not be empty");
10375
+ const draft = normalizeFeedbackDraft(input, this.loadFeedbackDraft(key2));
10376
+ this.saveFeedbackDraft(key2, draft);
10377
+ return { namespace: key2, draft };
10148
10378
  }
10149
10379
  /**
10150
10380
  * Record one agent-authored fact through the core long-term Event pipeline.
@@ -10312,7 +10542,7 @@ var StrataGateRuntime = class {
10312
10542
  if (this.config.database === ":memory:" || !existsSync(this.config.database)) return [];
10313
10543
  const storage = new SqliteStorage({ filename: this.config.database, readonly: true });
10314
10544
  try {
10315
- const entries = [];
10545
+ const entries2 = [];
10316
10546
  for (const { namespace, revision } of storage.listNamespaceRevisions()) {
10317
10547
  const opening = this.spaces.get(namespace);
10318
10548
  if (opening) {
@@ -10322,25 +10552,25 @@ var StrataGateRuntime = class {
10322
10552
  const cached2 = this.adminSnapshotCache.get(namespace);
10323
10553
  const entry2 = cached2?.revision === currentRevision ? cached2 : { namespace, revision: currentRevision, snapshot: memory.exportSnapshot() };
10324
10554
  this.adminSnapshotCache.set(namespace, entry2);
10325
- entries.push(entry2);
10555
+ entries2.push(entry2);
10326
10556
  continue;
10327
10557
  }
10328
10558
  const cached = this.adminSnapshotCache.get(namespace);
10329
10559
  if (cached?.revision === revision) {
10330
- entries.push(cached);
10560
+ entries2.push(cached);
10331
10561
  continue;
10332
10562
  }
10333
10563
  const loaded = await storage.load(namespace);
10334
10564
  if (!loaded) continue;
10335
10565
  const entry = { namespace, revision: loaded.revision, snapshot: loaded.snapshot };
10336
10566
  this.adminSnapshotCache.set(namespace, entry);
10337
- entries.push(entry);
10567
+ entries2.push(entry);
10338
10568
  }
10339
- const activeNamespaces = new Set(entries.map(({ namespace }) => namespace));
10569
+ const activeNamespaces = new Set(entries2.map(({ namespace }) => namespace));
10340
10570
  for (const namespace of this.adminSnapshotCache.keys()) {
10341
10571
  if (!activeNamespaces.has(namespace)) this.adminSnapshotCache.delete(namespace);
10342
10572
  }
10343
- return entries;
10573
+ return entries2;
10344
10574
  } finally {
10345
10575
  await storage.close();
10346
10576
  }
@@ -10371,30 +10601,30 @@ var StrataGateRuntime = class {
10371
10601
  }
10372
10602
  }
10373
10603
  async adminSnapshot(namespace) {
10374
- const key = namespace.trim();
10375
- if (!key) throw new TypeError("StrataGate admin namespace must not be empty");
10604
+ const key2 = namespace.trim();
10605
+ if (!key2) throw new TypeError("StrataGate admin namespace must not be empty");
10376
10606
  if (this.config.database === ":memory:" || !existsSync(this.config.database)) return null;
10377
- const opening = this.spaces.get(key);
10607
+ const opening = this.spaces.get(key2);
10378
10608
  if (opening) {
10379
10609
  const memory = await opening;
10380
- await this.refreshExternalImportMemory(key, memory);
10610
+ await this.refreshExternalImportMemory(key2, memory);
10381
10611
  const revision = memory.storageRevision;
10382
- const cached = this.adminSnapshotCache.get(key);
10612
+ const cached = this.adminSnapshotCache.get(key2);
10383
10613
  if (cached?.revision === revision) return cached.snapshot;
10384
- const entry = { namespace: key, revision, snapshot: memory.exportSnapshot() };
10385
- this.adminSnapshotCache.set(key, entry);
10614
+ const entry = { namespace: key2, revision, snapshot: memory.exportSnapshot() };
10615
+ this.adminSnapshotCache.set(key2, entry);
10386
10616
  return entry.snapshot;
10387
10617
  }
10388
10618
  const storage = new SqliteStorage({ filename: this.config.database, readonly: true });
10389
10619
  try {
10390
- const head = storage.listNamespaceRevisions().find(({ namespace: namespace2 }) => namespace2 === key);
10620
+ const head = storage.listNamespaceRevisions().find(({ namespace: namespace2 }) => namespace2 === key2);
10391
10621
  if (!head) return null;
10392
- const cached = this.adminSnapshotCache.get(key);
10622
+ const cached = this.adminSnapshotCache.get(key2);
10393
10623
  if (cached?.revision === head.revision) return cached.snapshot;
10394
- const loaded = await storage.load(key);
10624
+ const loaded = await storage.load(key2);
10395
10625
  if (!loaded) return null;
10396
- const entry = { namespace: key, revision: loaded.revision, snapshot: loaded.snapshot };
10397
- this.adminSnapshotCache.set(key, entry);
10626
+ const entry = { namespace: key2, revision: loaded.revision, snapshot: loaded.snapshot };
10627
+ this.adminSnapshotCache.set(key2, entry);
10398
10628
  return entry.snapshot;
10399
10629
  } finally {
10400
10630
  await storage.close();
@@ -10523,44 +10753,44 @@ var StrataGateRuntime = class {
10523
10753
  }
10524
10754
  /** Create a durable analysis job and return before model-backed work begins. */
10525
10755
  async adminPreviewExternalMemory(namespace, text3) {
10526
- const key = namespace.trim();
10527
- if (!key) throw new TypeError("StrataGate admin namespace must not be empty");
10756
+ const key2 = namespace.trim();
10757
+ if (!key2) throw new TypeError("StrataGate admin namespace must not be empty");
10528
10758
  if (!text3.trim()) throw new TypeError("External memory text must not be empty");
10529
10759
  await this.flush();
10530
- const { memory, owned } = await this.openAdminMemory(key);
10760
+ const { memory, owned } = await this.openAdminMemory(key2);
10531
10761
  try {
10532
- await this.refreshExternalImportMemory(key, memory);
10762
+ await this.refreshExternalImportMemory(key2, memory);
10533
10763
  const job = await memory.createExternalMemoryImportJob(text3);
10534
- this.scheduleExternalMemoryImport(key, job.id);
10764
+ this.scheduleExternalMemoryImport(key2, job.id);
10535
10765
  return this.externalImportView(job);
10536
10766
  } finally {
10537
10767
  if (owned) await memory.close();
10538
10768
  }
10539
10769
  }
10540
10770
  async adminExternalMemoryStatus(namespace, jobId) {
10541
- const key = namespace.trim();
10542
- if (!key) throw new TypeError("StrataGate admin namespace must not be empty");
10543
- const { memory, owned } = await this.openAdminMemory(key);
10771
+ const key2 = namespace.trim();
10772
+ if (!key2) throw new TypeError("StrataGate admin namespace must not be empty");
10773
+ const { memory, owned } = await this.openAdminMemory(key2);
10544
10774
  try {
10545
- await this.refreshExternalImportMemory(key, memory);
10775
+ await this.refreshExternalImportMemory(key2, memory);
10546
10776
  const jobs = memory.listExternalMemoryImportJobs();
10547
10777
  const job = jobId ? jobs.find(({ id }) => id === jobId) : [...jobs].sort((left, right) => right.createdAt.localeCompare(left.createdAt))[0];
10548
10778
  if (!job) return { job: null };
10549
- if (job.status === "extracting" || job.status === "processing") this.scheduleExternalMemoryImport(key, job.id);
10779
+ if (job.status === "extracting" || job.status === "processing") this.scheduleExternalMemoryImport(key2, job.id);
10550
10780
  return this.externalImportView(job);
10551
10781
  } finally {
10552
10782
  if (owned) await memory.close();
10553
10783
  }
10554
10784
  }
10555
10785
  async adminRetryExternalMemory(namespace, jobId) {
10556
- const key = namespace.trim();
10786
+ const key2 = namespace.trim();
10557
10787
  let lastConflict;
10558
10788
  for (let attempt = 0; attempt < 4; attempt += 1) {
10559
- const { memory, owned } = await this.openAdminMemory(key);
10789
+ const { memory, owned } = await this.openAdminMemory(key2);
10560
10790
  try {
10561
- await this.refreshExternalImportMemory(key, memory);
10791
+ await this.refreshExternalImportMemory(key2, memory);
10562
10792
  const job = await memory.retryExternalMemoryImportJob(jobId);
10563
- this.scheduleExternalMemoryImport(key, job.id);
10793
+ this.scheduleExternalMemoryImport(key2, job.id);
10564
10794
  return this.externalImportView(job);
10565
10795
  } catch (error) {
10566
10796
  if (!(error instanceof StorageConflictError)) throw error;
@@ -10573,10 +10803,10 @@ var StrataGateRuntime = class {
10573
10803
  throw lastConflict;
10574
10804
  }
10575
10805
  async adminCommitExternalMemory(namespace, jobId, choices) {
10576
- const key = namespace.trim();
10577
- const { memory, owned } = await this.openAdminMemory(key);
10806
+ const key2 = namespace.trim();
10807
+ const { memory, owned } = await this.openAdminMemory(key2);
10578
10808
  try {
10579
- await this.refreshExternalImportMemory(key, memory);
10809
+ await this.refreshExternalImportMemory(key2, memory);
10580
10810
  const job = memory.getExternalMemoryImportJob(jobId);
10581
10811
  if (!job) throw new Error("\u627E\u4E0D\u5230\u5BFC\u5165\u4EFB\u52A1");
10582
10812
  if (job.status !== "ready" && job.status !== "awaiting_confirmation") {
@@ -10617,10 +10847,10 @@ var StrataGateRuntime = class {
10617
10847
  }
10618
10848
  }
10619
10849
  async adminUndoExternalMemory(namespace, sourceBlockId) {
10620
- const key = namespace.trim();
10621
- const { memory, owned } = await this.openAdminMemory(key);
10850
+ const key2 = namespace.trim();
10851
+ const { memory, owned } = await this.openAdminMemory(key2);
10622
10852
  try {
10623
- await this.refreshExternalImportMemory(key, memory);
10853
+ await this.refreshExternalImportMemory(key2, memory);
10624
10854
  const result = await memory.undoExternalMemoryImport(sourceBlockId);
10625
10855
  const job = memory.listExternalMemoryImportJobs().find((candidate) => candidate.sourceBlockId === sourceBlockId);
10626
10856
  if (job) await memory.markExternalMemoryImportUndone(job.id);
@@ -10702,19 +10932,19 @@ var StrataGateRuntime = class {
10702
10932
  return { opened: true, path };
10703
10933
  }
10704
10934
  async adminExpandBlock(namespace, id, target) {
10705
- const key = namespace.trim();
10706
- if (!key) throw new TypeError("StrataGate admin namespace must not be empty");
10935
+ const key2 = namespace.trim();
10936
+ if (!key2) throw new TypeError("StrataGate admin namespace must not be empty");
10707
10937
  const update = this.settingsTail.catch(() => {
10708
10938
  }).then(async () => {
10709
10939
  await this.flush();
10710
- const active = this.spaces.get(key);
10940
+ const active = this.spaces.get(key2);
10711
10941
  if (active) return (await active).expandBlock(id, target, "user");
10712
10942
  if (this.config.database === ":memory:" || !existsSync(this.config.database)) {
10713
- throw new Error(`Unknown StrataGate namespace: ${key}`);
10943
+ throw new Error(`Unknown StrataGate namespace: ${key2}`);
10714
10944
  }
10715
10945
  const memory = await StrataGate.open({
10716
10946
  database: this.config.database,
10717
- namespace: key,
10947
+ namespace: key2,
10718
10948
  blockTurnSize: this.blockTurnSize,
10719
10949
  blockDecayLambda: this.blockDecayLambda,
10720
10950
  summarizer: this.models.summarizer,
@@ -10734,20 +10964,20 @@ var StrataGateRuntime = class {
10734
10964
  return update;
10735
10965
  }
10736
10966
  async adminRetryJob(namespace, kind, id) {
10737
- const key = namespace.trim();
10967
+ const key2 = namespace.trim();
10738
10968
  const jobId = id.trim();
10739
- if (!key) throw new TypeError("StrataGate admin namespace must not be empty");
10969
+ if (!key2) throw new TypeError("StrataGate admin namespace must not be empty");
10740
10970
  if (!jobId) throw new TypeError("StrataGate job id must not be empty");
10741
- if (kind === "topic-projection") return this.adminRetryTopicProjection(key, jobId);
10742
- const retryKey = `${key}\0${kind}\0${jobId}`;
10971
+ if (kind === "topic-projection") return this.adminRetryTopicProjection(key2, jobId);
10972
+ const retryKey = `${key2}\0${kind}\0${jobId}`;
10743
10973
  const existing = this.adminJobRetryRuns.get(retryKey);
10744
10974
  if (existing) return existing;
10745
10975
  const update = this.settingsTail.catch(() => {
10746
10976
  }).then(async () => {
10747
10977
  await this.flush();
10748
- const { memory, owned } = await this.openAdminMemory(key, { derivation: true });
10978
+ const { memory, owned } = await this.openAdminMemory(key2, { derivation: true });
10749
10979
  try {
10750
- await this.refreshExternalImportMemory(key, memory);
10980
+ await this.refreshExternalImportMemory(key2, memory);
10751
10981
  const graphJob = kind === "graph-projection" ? memory.listGraphProjectionJobs().find((candidate) => candidate.id === jobId) : void 0;
10752
10982
  const blockId = kind === "graph-projection" ? graphJob?.sourceEventIds.flatMap((eventId) => memory.listAllEvents().find(({ id: eventCandidateId }) => eventCandidateId === eventId)?.sourceBlockId ?? [])[0] : jobId;
10753
10983
  const block = blockId ? memory.listBlocks().find((candidate) => candidate.id === blockId) : void 0;
@@ -10763,12 +10993,12 @@ var StrataGateRuntime = class {
10763
10993
  if (!this.models.isReady(session)) {
10764
10994
  throw new Error("StrataGate model adapter is not ready; retry after DSH finishes registering adapters");
10765
10995
  }
10766
- const result = session && this.namespaceFor(session) === key ? await this.models.run(session, retry) : await this.models.runDetached(threadId, retry);
10996
+ const result = session && this.namespaceFor(session) === key2 ? await this.models.run(session, retry) : await this.models.runDetached(threadId, retry);
10767
10997
  await this.persistSuccessfulResponses(memory);
10768
10998
  const currentBlock = blockId ? memory.listBlocks().find(({ id: candidateId }) => candidateId === blockId) : void 0;
10769
10999
  if (kind !== "graph-projection" && currentBlock?.processingStatus === "ready" && currentBlock.threadId) {
10770
- this.pendingSurfaceSync.set(`${key}\0${currentBlock.id}`, { namespace: key, blockId: currentBlock.id });
10771
- if (session && this.namespaceFor(session) === key) await this.syncPendingRetrySurface(session, memory);
11000
+ this.pendingSurfaceSync.set(`${key2}\0${currentBlock.id}`, { namespace: key2, blockId: currentBlock.id });
11001
+ if (session && this.namespaceFor(session) === key2) await this.syncPendingRetrySurface(session, memory);
10772
11002
  }
10773
11003
  const job = kind === "block-summary" ? memory.listSummaryJobs().find(({ blockId: candidateId }) => candidateId === jobId) : kind === "event-extraction" ? memory.listExtractionJobs().find(({ blockId: candidateId }) => candidateId === jobId) : memory.listGraphProjectionJobs().find(({ id: candidateId }) => candidateId === jobId);
10774
11004
  const succeeded = kind === "graph-projection" ? job?.status === "completed" : job?.status === "succeeded" || job?.status === "skipped";
@@ -10783,7 +11013,7 @@ var StrataGateRuntime = class {
10783
11013
  result,
10784
11014
  processingStatus: currentBlock?.processingStatus ?? null,
10785
11015
  ready: currentBlock?.processingStatus === "ready",
10786
- surfaceUpdated: !blockId || !this.pendingSurfaceSync.has(`${key}\0${blockId}`)
11016
+ surfaceUpdated: !blockId || !this.pendingSurfaceSync.has(`${key2}\0${blockId}`)
10787
11017
  };
10788
11018
  } finally {
10789
11019
  if (owned) await memory.close();
@@ -10799,14 +11029,14 @@ var StrataGateRuntime = class {
10799
11029
  return tracked;
10800
11030
  }
10801
11031
  async adminRetryTopicProjection(namespace, id) {
10802
- const key = namespace.trim();
11032
+ const key2 = namespace.trim();
10803
11033
  const jobId = id.trim();
10804
- if (!key || !jobId) throw new TypeError("Topic retry requires namespace and job id");
11034
+ if (!key2 || !jobId) throw new TypeError("Topic retry requires namespace and job id");
10805
11035
  const update = this.settingsTail.catch(() => {
10806
11036
  }).then(async () => {
10807
- const { memory, owned } = await this.openAdminMemory(key, { derivation: true });
11037
+ const { memory, owned } = await this.openAdminMemory(key2, { derivation: true });
10808
11038
  try {
10809
- return await this.retryTopicWrite(key, memory, () => memory.retryTopicProjection(jobId));
11039
+ return await this.retryTopicWrite(key2, memory, () => memory.retryTopicProjection(jobId));
10810
11040
  } finally {
10811
11041
  if (owned) await memory.close();
10812
11042
  }
@@ -10983,8 +11213,8 @@ var StrataGateRuntime = class {
10983
11213
  }
10984
11214
  scheduleBlockDerivation(session, memory) {
10985
11215
  const threadId = String(session.id);
10986
- const key = `${this.namespaceFor(session)}\0${threadId}`;
10987
- if (this.closed || !this.models.isReady(session) || this.derivationTimers.has(key) || this.derivationRuns.has(key)) return;
11216
+ const key2 = `${this.namespaceFor(session)}\0${threadId}`;
11217
+ if (this.closed || !this.models.isReady(session) || this.derivationTimers.has(key2) || this.derivationRuns.has(key2)) return;
10988
11218
  const pendingBlockIds = new Set(memory.listBlocks().filter((block) => block.threadId === threadId && block.processingStatus === "pending").map((block) => block.id));
10989
11219
  if (pendingBlockIds.size === 0) return;
10990
11220
  const retryTimes = [
@@ -10994,7 +11224,7 @@ var StrataGateRuntime = class {
10994
11224
  if (retryTimes.length === 0) return;
10995
11225
  const delay = Math.max(0, Math.min(...retryTimes) - Date.now());
10996
11226
  const timer = setTimeout(() => {
10997
- this.derivationTimers.delete(key);
11227
+ this.derivationTimers.delete(key2);
10998
11228
  if (this.closed || !this.models.isReady(session)) return;
10999
11229
  const failedBefore = this.failedCoreJobs(memory);
11000
11230
  const run = this.models.run(session, () => memory.resumePendingWork({ threadId })).then(async (resumed) => {
@@ -11021,13 +11251,13 @@ var StrataGateRuntime = class {
11021
11251
  this.notePluginError(session, error);
11022
11252
  this.onIngestError(error);
11023
11253
  }).finally(() => {
11024
- this.derivationRuns.delete(key);
11254
+ this.derivationRuns.delete(key2);
11025
11255
  this.scheduleBlockDerivation(session, memory);
11026
11256
  });
11027
- this.derivationRuns.set(key, run);
11257
+ this.derivationRuns.set(key2, run);
11028
11258
  }, delay);
11029
11259
  timer.unref?.();
11030
- this.derivationTimers.set(key, timer);
11260
+ this.derivationTimers.set(key2, timer);
11031
11261
  }
11032
11262
  scheduleGraphMigration(session, memory) {
11033
11263
  const namespace = this.namespaceFor(session);
@@ -11112,23 +11342,28 @@ var StrataGateRuntime = class {
11112
11342
  if (responses.length > 0) await memory.recordSuccessfulModelResponses(responses);
11113
11343
  }
11114
11344
  batch(session, evidence, results, metadata = {}) {
11115
- const sequence = ++this.batchSequence;
11345
+ const sequence = this.batchSequence + 1;
11116
11346
  const id = `batch_${sequence}`;
11117
11347
  const refs = new Map(evidence.map(({ ref, target }) => [ref, target]));
11118
- const key = String(session.id);
11119
- let sessionBatches = this.batches.get(key);
11348
+ const output = { batchId: id, evidenceRefs: [...refs.keys()], results, ...metadata };
11349
+ if (validateArgs2({ retrieval: { type: "json", required: true } }, { retrieval: output }).length) {
11350
+ throw new TypeError("StrataGate retrieval output is not lossless JSON");
11351
+ }
11352
+ this.batchSequence = sequence;
11353
+ const key2 = String(session.id);
11354
+ let sessionBatches = this.batches.get(key2);
11120
11355
  if (!sessionBatches) {
11121
11356
  sessionBatches = /* @__PURE__ */ new Map();
11122
- this.batches.set(key, sessionBatches);
11357
+ this.batches.set(key2, sessionBatches);
11123
11358
  }
11124
11359
  sessionBatches.set(id, { id, sequence, refs, status: "unresolved" });
11125
- this.latestBatchIds.set(key, id);
11126
- return { batchId: id, evidenceRefs: [...refs.keys()], results, ...metadata };
11360
+ this.latestBatchIds.set(key2, id);
11361
+ return output;
11127
11362
  }
11128
11363
  requireBatch(session, batchId, operation) {
11129
- const key = String(session.id);
11130
- const selectedId = batchId?.trim() || this.latestBatchIds.get(key);
11131
- const sessionBatches = this.batches.get(key);
11364
+ const key2 = String(session.id);
11365
+ const selectedId = batchId?.trim() || this.latestBatchIds.get(key2);
11366
+ const sessionBatches = this.batches.get(key2);
11132
11367
  const batch = selectedId ? sessionBatches?.get(selectedId) : void 0;
11133
11368
  if (batch) return batch;
11134
11369
  const availableBatchIds = [...sessionBatches?.keys() ?? []];
@@ -11136,7 +11371,7 @@ var StrataGateRuntime = class {
11136
11371
  const requested = batchId?.trim() ? `Unknown retrieval batch: ${batchId.trim()}.` : "No StrataGate retrieval batch exists for this session.";
11137
11372
  throw new Error([
11138
11373
  `${operation} could not select a retrieval batch. ${requested}`,
11139
- `Latest batch: ${this.latestBatchIds.get(key) ?? "none"}.`,
11374
+ `Latest batch: ${this.latestBatchIds.get(key2) ?? "none"}.`,
11140
11375
  `Unresolved batches: ${JSON.stringify(unresolvedBatchIds)}.`,
11141
11376
  `Available batches: ${JSON.stringify(availableBatchIds)}.`
11142
11377
  ].join(" "));
@@ -11147,10 +11382,10 @@ var StrataGateRuntime = class {
11147
11382
  return [...sessionBatches.values()].filter(({ status }) => status === "unresolved").map(({ id }) => id);
11148
11383
  }
11149
11384
  batchError(session, batch, summary, invalidEvidenceRefs = []) {
11150
- const key = String(session.id);
11385
+ const key2 = String(session.id);
11151
11386
  const assessmentStatus = batch.assessment?.verdict ?? "not_assessed";
11152
- const latestBatch = this.latestBatchIds.get(key);
11153
- const latestState = latestBatch ? this.batches.get(key)?.get(latestBatch) : void 0;
11387
+ const latestBatch = this.latestBatchIds.get(key2);
11388
+ const latestState = latestBatch ? this.batches.get(key2)?.get(latestBatch) : void 0;
11154
11389
  return [
11155
11390
  summary,
11156
11391
  `Invalid evidence refs: ${JSON.stringify(invalidEvidenceRefs)}.`,
@@ -11394,7 +11629,7 @@ function compactEvent2(event, score) {
11394
11629
  summary: compactText2(event.summary),
11395
11630
  sourceTime: event.temporal.happenedStart ?? event.temporal.mentionedAt ?? event.createdAt,
11396
11631
  temporal: compactTemporal(event),
11397
- sourceBlockId: event.sourceBlockId,
11632
+ ...event.sourceBlockId !== void 0 ? { sourceBlockId: event.sourceBlockId } : {},
11398
11633
  status: event.status,
11399
11634
  scope: event.scope,
11400
11635
  criticality: event.criticality,
@@ -11539,9 +11774,9 @@ Events:
11539
11774
  metadataEvidenceStatus: result.metadataEvidenceStatus,
11540
11775
  matchType: result.matchType,
11541
11776
  currentState: node.currentState,
11542
- facts: node.facts.map(({ key, value, status, validFrom, validTo }) => ({ key, value, status, validFrom, validTo })),
11777
+ facts: node.facts.map(({ key: key2, value, status, validFrom, validTo }) => ({ key: key2, value, status, validFrom, validTo })),
11543
11778
  currentRelations: result.currentEdges?.map(({ relation, status }) => ({ relation, status })),
11544
- historicalMatches: result.historicalFacts?.map(({ key, value, status, validFrom, validTo }) => ({ key, value, status, validFrom, validTo })),
11779
+ historicalMatches: result.historicalFacts?.map(({ key: key2, value, status, validFrom, validTo }) => ({ key: key2, value, status, validFrom, validTo })),
11545
11780
  historicalRelations: result.historicalEdges?.map(({ relation, status, validFrom, validTo }) => ({ relation, status, validFrom, validTo })),
11546
11781
  timeline: result.timeline
11547
11782
  });
@@ -11601,7 +11836,7 @@ function registerMemoryTools(ctx, runtime) {
11601
11836
  },
11602
11837
  output: jsonOutput,
11603
11838
  execute: async (args, exec) => {
11604
- if (Object.keys(args).some((key) => key !== "field" && key !== "value")) throw new TypeError("Unknown Profile update argument");
11839
+ if (Object.keys(args).some((key2) => key2 !== "field" && key2 !== "value")) throw new TypeError("Unknown Profile update argument");
11605
11840
  sessionOf(exec);
11606
11841
  return runtime.updatePersistentProfileFromTool(args.field, args.value);
11607
11842
  }
@@ -11815,9 +12050,9 @@ var LEIDEN_RESOLUTION = 0.55;
11815
12050
  function pairKey(left, right) {
11816
12051
  return left < right ? `${left}\0${right}` : `${right}\0${left}`;
11817
12052
  }
11818
- function pairIds(key) {
11819
- const separator = key.indexOf("\0");
11820
- return [key.slice(0, separator), key.slice(separator + 1)];
12053
+ function pairIds(key2) {
12054
+ const separator = key2.indexOf("\0");
12055
+ return [key2.slice(0, separator), key2.slice(separator + 1)];
11821
12056
  }
11822
12057
  function semanticTags(node) {
11823
12058
  return Array.isArray(node.tags) ? [...new Set(node.tags.map((tag) => tag.trim()).filter(Boolean))] : [];
@@ -11848,8 +12083,8 @@ function clusterKnowledgeGraph(rawNodes, rawEdges) {
11848
12083
  const structuralWeights = /* @__PURE__ */ new Map();
11849
12084
  const addStructuralWeight = (left, right, weight) => {
11850
12085
  if (left === right || !nodeMap.has(left) || !nodeMap.has(right) || weight <= 0) return;
11851
- const key = pairKey(left, right);
11852
- structuralWeights.set(key, (structuralWeights.get(key) ?? 0) + weight);
12086
+ const key2 = pairKey(left, right);
12087
+ structuralWeights.set(key2, (structuralWeights.get(key2) ?? 0) + weight);
11853
12088
  };
11854
12089
  for (const edge of rawEdges.filter((edge2) => edge2.status === "active")) {
11855
12090
  addStructuralWeight(edge.fromNodeId, edge.toNodeId, 3 * Math.max(0.25, Number(edge.confidence) || 0.8));
@@ -11869,8 +12104,8 @@ function clusterKnowledgeGraph(rawNodes, rawEdges) {
11869
12104
  const graph = createGraph({ multigraph: false });
11870
12105
  for (const node of nodes) graph.addNode(node.id);
11871
12106
  const weightedDegree = new Map(nodes.map((node) => [node.id, 0]));
11872
- for (const [key, structuralWeight] of structuralWeights) {
11873
- const [leftId, rightId] = pairIds(key);
12107
+ for (const [key2, structuralWeight] of structuralWeights) {
12108
+ const [leftId, rightId] = pairIds(key2);
11874
12109
  const left = nodeMap.get(leftId);
11875
12110
  const right = nodeMap.get(rightId);
11876
12111
  const leftTags = new Set(semanticTags(left));
@@ -11895,9 +12130,9 @@ function clusterKnowledgeGraph(rawNodes, rawEdges) {
11895
12130
  const connectedGroups = [];
11896
12131
  const isolatedIds = [];
11897
12132
  for (const community of partition.getCommunities().values()) {
11898
- const ids = community.map(String).sort();
11899
- const connected = ids.filter((id) => (weightedDegree.get(id) ?? 0) > 0);
11900
- const isolated = ids.filter((id) => (weightedDegree.get(id) ?? 0) === 0);
12133
+ const ids2 = community.map(String).sort();
12134
+ const connected = ids2.filter((id) => (weightedDegree.get(id) ?? 0) > 0);
12135
+ const isolated = ids2.filter((id) => (weightedDegree.get(id) ?? 0) === 0);
11901
12136
  if (connected.length > 0) connectedGroups.push(connected);
11902
12137
  isolatedIds.push(...isolated);
11903
12138
  }
@@ -11929,7 +12164,7 @@ function currentPluginVersion() {
11929
12164
  return "unknown";
11930
12165
  }
11931
12166
  var STRATAGATE_DSH_VERSION = currentPluginVersion();
11932
- var TOPIC_DIRECTORY_VIEW_VERSION = 5;
12167
+ var TOPIC_DIRECTORY_VIEW_VERSION = 7;
11933
12168
  function graphProjectionIsProcessing(job) {
11934
12169
  return derivationJobState(job, false) === "processing" || derivationJobState(job, false) === "retryable";
11935
12170
  }
@@ -11984,7 +12219,7 @@ function redactValue(value) {
11984
12219
  if (typeof value === "string") return redact(value);
11985
12220
  if (Array.isArray(value)) return value.map(redactValue);
11986
12221
  if (value && typeof value === "object") {
11987
- return Object.fromEntries(Object.entries(value).map(([key, item]) => [key, redactValue(item)]));
12222
+ return Object.fromEntries(Object.entries(value).map(([key2, item]) => [key2, redactValue(item)]));
11988
12223
  }
11989
12224
  return value;
11990
12225
  }
@@ -11993,17 +12228,17 @@ function redactedMessage(message, blockId) {
11993
12228
  const common = { ...base, content: redact(message.content), blockId };
11994
12229
  return toolCalls ? { ...common, toolCalls: redactValue(toolCalls) } : common;
11995
12230
  }
11996
- function sourceMessages(snapshot, ids) {
12231
+ function sourceMessages(snapshot, ids2) {
11997
12232
  const output = [];
11998
12233
  for (const block of snapshot.blocks) {
11999
12234
  for (const message of block.l5Raw) {
12000
- if (!ids || ids.has(message.id)) {
12235
+ if (!ids2 || ids2.has(message.id)) {
12001
12236
  output.push(redactedMessage(message, block.id));
12002
12237
  }
12003
12238
  }
12004
12239
  }
12005
12240
  for (const message of snapshot.openTail) {
12006
- if (!ids || ids.has(message.id)) {
12241
+ if (!ids2 || ids2.has(message.id)) {
12007
12242
  output.push(redactedMessage(message, null));
12008
12243
  }
12009
12244
  }
@@ -12249,15 +12484,15 @@ function topicDirectoryProjection(snapshot, agentMemoryWeight) {
12249
12484
  const failures = state.jobs.flatMap((job) => {
12250
12485
  if (job.superseded || job.projectorVersion !== MEMORY_TOPIC_PROJECTOR_VERSION || job.status !== "failed" || job.attempts < TOPIC_MAX_ATTEMPTS || !Object.entries({ ...job.sourceVersions, ...job.dependencyVersions }).every(([id, version]) => versions.get(id) === version)) return [];
12251
12486
  const diagnostics = safeTopicDiagnostics(job.diagnostics);
12252
- const ids = job.sourceEventIds.filter((id) => {
12253
- const key = relationKey(job.sectionBackfillTopicId, id, job.sourceVersions[id]);
12254
- if (!outstandingWork.has(key)) return false;
12255
- failedWork.add(key);
12487
+ const ids2 = memoryTopicUnfinishedJobEventIds(job, state, versions).filter((id) => {
12488
+ const key2 = relationKey(job.sectionBackfillTopicId, id, job.sourceVersions[id]);
12489
+ if (!outstandingWork.has(key2)) return false;
12490
+ failedWork.add(key2);
12256
12491
  return true;
12257
12492
  });
12258
- return ids.length > 0 ? [{
12493
+ return ids2.length > 0 ? [{
12259
12494
  jobId: job.id,
12260
- eventIds: ids,
12495
+ eventIds: ids2,
12261
12496
  attempts: job.attempts,
12262
12497
  // Topic failures store reason codes, never raw model output.
12263
12498
  diagnostics,
@@ -12268,7 +12503,7 @@ function topicDirectoryProjection(snapshot, agentMemoryWeight) {
12268
12503
  const total = (/* @__PURE__ */ new Set([...history.map(([id]) => id), ...backfillRelations.map(({ id }) => id)])).size;
12269
12504
  const failedRelations = backfillRelations.filter(({ topicId, id, version }) => failedWork.has(relationKey(topicId, id, version))).length;
12270
12505
  const bootstrap = frozen ? {
12271
- status: backfillRelations.length > 0 && outstandingWork.size === failedWork.size ? "failed" : Object.keys(state.rebuildVersions ?? {}).length > 0 || backfillRelations.length > 0 ? outstandingWork.size === failedWork.size ? "completed" : "running" : frozen.status,
12506
+ status: outstandingWork.size === 0 ? "completed" : outstandingWork.size === failedWork.size ? "failed" : frozen.status === "pending" ? "pending" : "running",
12272
12507
  total,
12273
12508
  completed: total - outstanding.size,
12274
12509
  failedEvents: new Set(failures.flatMap(({ eventIds }) => eventIds)).size,
@@ -12337,8 +12572,8 @@ function topicEventPageNumber(url, name2, fallback) {
12337
12572
  }
12338
12573
  async function topicEvents(runtime, url) {
12339
12574
  const allowed = /* @__PURE__ */ new Set(["namespace", "topicId", "sectionKey", "offset", "limit", "expectedRevision"]);
12340
- for (const key of url.searchParams.keys()) {
12341
- if (!allowed.has(key) || url.searchParams.getAll(key).length !== 1) {
12575
+ for (const key2 of url.searchParams.keys()) {
12576
+ if (!allowed.has(key2) || url.searchParams.getAll(key2).length !== 1) {
12342
12577
  throw new AdminHttpError(400, "Unknown or repeated topic Event parameter");
12343
12578
  }
12344
12579
  }
@@ -12365,29 +12600,29 @@ async function topicEvents(runtime, url) {
12365
12600
  revision: data.revision
12366
12601
  });
12367
12602
  }
12368
- let ids;
12603
+ let ids2;
12369
12604
  if (topicId === "pending") {
12370
- if (sectionKey === null) ids = projection.pending.map(({ id }) => id);
12605
+ if (sectionKey === null) ids2 = projection.pending.map(({ id }) => id);
12371
12606
  else if (sectionKey.startsWith("failure:")) {
12372
12607
  const failure = projection.failures.find(({ jobId }) => jobId === sectionKey.slice("failure:".length));
12373
12608
  if (!failure) throw new AdminHttpError(404, "Unknown or unavailable Topic failure");
12374
- ids = failure.eventIds;
12609
+ ids2 = failure.eventIds;
12375
12610
  } else throw new AdminHttpError(404, "Unknown or unavailable pending Topic section");
12376
12611
  } else {
12377
12612
  const topic = projection.chapters.find(({ id }) => id === topicId);
12378
12613
  if (!topic) throw new AdminHttpError(404, "Unknown or unavailable memory topic");
12379
- if (sectionKey === null) ids = topic.sourceEventIds;
12614
+ if (sectionKey === null) ids2 = topic.sourceEventIds;
12380
12615
  else if (sectionKey === "uncovered") {
12381
12616
  const covered = new Set(memoryTopicSections(topic).flatMap((section) => section.sourceEventIds));
12382
- ids = topic.sourceEventIds.filter((id) => !covered.has(id));
12617
+ ids2 = topic.sourceEventIds.filter((id) => !covered.has(id));
12383
12618
  } else {
12384
12619
  const section = memoryTopicSections(topic).find((candidate) => candidate.key === sectionKey);
12385
12620
  if (!section) throw new AdminHttpError(404, "Unknown or unavailable memory topic section");
12386
- ids = section.sourceEventIds;
12621
+ ids2 = section.sourceEventIds;
12387
12622
  }
12388
12623
  }
12389
12624
  const byId = new Map(projection.visibleEvents.map((event) => [event.id, event]));
12390
- const sources2 = [...new Set(ids)].flatMap((id) => byId.get(id) ?? []);
12625
+ const sources2 = [...new Set(ids2)].flatMap((id) => byId.get(id) ?? []);
12391
12626
  const page = sources2.slice(offset, offset + limit);
12392
12627
  return {
12393
12628
  namespace,
@@ -12402,13 +12637,13 @@ async function topicEvents(runtime, url) {
12402
12637
  };
12403
12638
  }
12404
12639
  async function overview(runtime, cachedEntries) {
12405
- const entries = cachedEntries ?? await Promise.all((await runtime.adminNamespaces()).map(async (namespace) => ({
12640
+ const entries2 = cachedEntries ?? await Promise.all((await runtime.adminNamespaces()).map(async (namespace) => ({
12406
12641
  namespace,
12407
12642
  revision: 0,
12408
12643
  snapshot: await runtime.adminSnapshot(namespace)
12409
12644
  })));
12410
12645
  const rows = [];
12411
- for (const { namespace, snapshot } of entries) {
12646
+ for (const { namespace, snapshot } of entries2) {
12412
12647
  if (!snapshot) continue;
12413
12648
  const recovered = recoverSnapshotView(snapshot);
12414
12649
  const blockDetails = (blockIds) => [...new Set(blockIds)].flatMap((blockId) => {
@@ -12477,7 +12712,12 @@ async function overview(runtime, cachedEntries) {
12477
12712
  sourceEventIds: job.sourceEventIds
12478
12713
  };
12479
12714
  };
12480
- const topicJobs = (snapshot.memoryTopicState?.jobs ?? []).filter((job) => !job.superseded);
12715
+ const topicEvents2 = [...snapshot.events, ...(runtime.adminAgentMemoryRetrievalWeight?.() ?? 1) > 0 ? snapshot.agentEvents : []].filter((event) => event.status === "active" || event.status === "superseded");
12716
+ const topicDirectory2 = new MemoryTopicDirectory();
12717
+ topicDirectory2.restore(snapshot.memoryTopicState);
12718
+ const topicState = topicDirectory2.snapshot(topicEvents2);
12719
+ const topicVersions = new Map(topicEvents2.map((event) => [event.id, memoryTopicEventFingerprint(event)]));
12720
+ const topicJobs = topicState.jobs.filter((job) => !job.superseded && (job.status === "completed" || memoryTopicUnfinishedJobEventIds(job, topicState, topicVersions).length > 0));
12481
12721
  const describeTopicJob = (job) => {
12482
12722
  const diagnostics = safeTopicDiagnostics(job.diagnostics);
12483
12723
  const lastError = diagnostics ? `${diagnostics.category}: ${diagnostics.reason}` : ["timeout", "source-changed", "invalid-output", "worker-failed"].includes(job.lastError ?? "") ? job.lastError : "worker-failed";
@@ -12492,7 +12732,7 @@ async function overview(runtime, cachedEntries) {
12492
12732
  lastError,
12493
12733
  lastErrorFull: lastError,
12494
12734
  diagnostics,
12495
- eventCount: job.sourceEventIds.length,
12735
+ eventCount: job.status === "completed" ? job.sourceEventIds.length : memoryTopicUnfinishedJobEventIds(job, topicState, topicVersions).length,
12496
12736
  blockIds: [],
12497
12737
  threadIds: [],
12498
12738
  blockDetails: []
@@ -12796,12 +13036,12 @@ function recoverSnapshotView(snapshot) {
12796
13036
  if (turn !== null) receiptTurns.set(receiptTurnKey(threadId, receipt.createdAt), turn);
12797
13037
  const currentActivity = receiptActivity.get(threadId);
12798
13038
  if (!currentActivity || receipt.createdAt > currentActivity) receiptActivity.set(threadId, receipt.createdAt);
12799
- const key = timestampKey(receipt.createdAt);
12800
- const candidates = receiptCandidates.get(key) ?? /* @__PURE__ */ new Set();
13039
+ const key2 = timestampKey(receipt.createdAt);
13040
+ const candidates = receiptCandidates.get(key2) ?? /* @__PURE__ */ new Set();
12801
13041
  candidates.add(threadId);
12802
- receiptCandidates.set(key, candidates);
13042
+ receiptCandidates.set(key2, candidates);
12803
13043
  }
12804
- const exactThreadAt = new Map([...receiptCandidates].filter(([, ids]) => ids.size === 1).map(([createdAt, ids]) => [createdAt, [...ids][0]]));
13044
+ const exactThreadAt = new Map([...receiptCandidates].filter(([, ids2]) => ids2.size === 1).map(([createdAt, ids2]) => [createdAt, [...ids2][0]]));
12805
13045
  const recoverMessages = (messages) => {
12806
13046
  let precedingThreadId = null;
12807
13047
  return messages.map((message) => {
@@ -12822,11 +13062,11 @@ function recoverSnapshotView(snapshot) {
12822
13062
  messages.push(item.message);
12823
13063
  groups.set(item.threadId, messages);
12824
13064
  }
12825
- const entries = [...groups];
12826
- for (const [threadId, messages] of entries) {
12827
- const virtual = !source.threadId && (entries.length > 1 || threadId !== LEGACY_THREAD_ID);
13065
+ const entries2 = [...groups];
13066
+ for (const [threadId, messages] of entries2) {
13067
+ const virtual = !source.threadId && (entries2.length > 1 || threadId !== LEGACY_THREAD_ID);
12828
13068
  blocks.push({
12829
- id: entries.length > 1 ? `virtual:${source.id}:${encodeURIComponent(threadId)}` : source.id,
13069
+ id: entries2.length > 1 ? `virtual:${source.id}:${encodeURIComponent(threadId)}` : source.id,
12830
13070
  source,
12831
13071
  threadId,
12832
13072
  messages,
@@ -12875,12 +13115,12 @@ function virtualBlockLayers(block) {
12875
13115
  ]);
12876
13116
  }
12877
13117
  function conversationRows(snapshot, view = recoverSnapshotView(snapshot)) {
12878
- const ids = /* @__PURE__ */ new Set([
13118
+ const ids2 = /* @__PURE__ */ new Set([
12879
13119
  ...view.blocks.map((block) => block.threadId),
12880
13120
  ...view.openMessages.map(({ threadId }) => threadId),
12881
13121
  ...view.receiptThreads.values()
12882
13122
  ]);
12883
- return [...ids].map((id) => {
13123
+ return [...ids2].map((id) => {
12884
13124
  const blocks = view.blocks.filter((block) => block.threadId === id);
12885
13125
  const messages = [
12886
13126
  ...blocks.flatMap((block) => block.messages),
@@ -13079,12 +13319,12 @@ async function sources(runtime, url) {
13079
13319
  const blockId = url.searchParams.get("blockId");
13080
13320
  let events = [];
13081
13321
  let elements = [];
13082
- let ids = /* @__PURE__ */ new Set();
13322
+ let ids2 = /* @__PURE__ */ new Set();
13083
13323
  if (eventId) {
13084
13324
  const event = snapshot.events.find(({ id }) => id === eventId) ?? ((runtime.adminAgentMemoryRetrievalWeight?.() ?? 1) > 0 ? snapshot.agentEvents.find(({ id, status }) => id === eventId && (status === "active" || status === "superseded")) : void 0);
13085
13325
  if (!event) throw new AdminHttpError(404, `Unknown event: ${eventId}`);
13086
13326
  events = [event];
13087
- ids = new Set(event.sourceMessageIds);
13327
+ ids2 = new Set(event.sourceMessageIds);
13088
13328
  } else if (nodeId) {
13089
13329
  const rawNode = snapshot.graphNodes.find(({ id }) => id === nodeId);
13090
13330
  if (!rawNode) throw new AdminHttpError(404, `Unknown graph node: ${nodeId}`);
@@ -13106,7 +13346,7 @@ async function sources(runtime, url) {
13106
13346
  ...edges.flatMap(({ sourceEventIds }) => sourceEventIds)
13107
13347
  ]);
13108
13348
  events = snapshot.events.filter(({ id }) => eventIds.has(id));
13109
- ids = new Set(events.flatMap(({ sourceMessageIds }) => sourceMessageIds));
13349
+ ids2 = new Set(events.flatMap(({ sourceMessageIds }) => sourceMessageIds));
13110
13350
  return {
13111
13351
  namespace,
13112
13352
  node: view.node,
@@ -13117,28 +13357,28 @@ async function sources(runtime, url) {
13117
13357
  currentEdges: view.currentEdges,
13118
13358
  historicalEdges: view.historicalEdges,
13119
13359
  events: events.map((event) => eventSummary(event)),
13120
- messages: sourceMessages(snapshot, ids)
13360
+ messages: sourceMessages(snapshot, ids2)
13121
13361
  };
13122
13362
  } else if (elementId) {
13123
13363
  const element = snapshot.elements.find(({ id }) => id === elementId);
13124
13364
  if (!element) throw new AdminHttpError(404, `Unknown element: ${elementId}`);
13125
13365
  elements = [element];
13126
13366
  events = snapshot.events.filter(({ id }) => element.sourceEventIds.includes(id));
13127
- ids = new Set(events.flatMap(({ sourceMessageIds }) => sourceMessageIds));
13367
+ ids2 = new Set(events.flatMap(({ sourceMessageIds }) => sourceMessageIds));
13128
13368
  } else if (blockId) {
13129
13369
  const displayBlock = recoverSnapshotView(snapshot).blocks.find(({ id }) => id === blockId);
13130
13370
  const block = displayBlock?.source ?? snapshot.blocks.find(({ id }) => id === blockId);
13131
13371
  if (!block) throw new AdminHttpError(404, `Unknown block: ${blockId}`);
13132
13372
  const messages = displayBlock?.messages ?? block.l5Raw;
13133
- ids = new Set(messages.map(({ id }) => id));
13134
- events = snapshot.events.filter((event) => event.sourceBlockId === block.id && (!displayBlock?.virtual || event.sourceMessageIds.some((id) => ids.has(id))));
13373
+ ids2 = new Set(messages.map(({ id }) => id));
13374
+ events = snapshot.events.filter((event) => event.sourceBlockId === block.id && (!displayBlock?.virtual || event.sourceMessageIds.some((id) => ids2.has(id))));
13135
13375
  const eventIds = new Set(events.map(({ id }) => id));
13136
13376
  elements = snapshot.elements.filter(({ sourceEventIds }) => sourceEventIds.some((id) => eventIds.has(id)));
13137
13377
  return {
13138
13378
  namespace,
13139
13379
  events: events.map((event) => eventSummary(event)),
13140
13380
  elements: elements.map(elementSummary),
13141
- messages: sourceMessages(snapshot, ids),
13381
+ messages: sourceMessages(snapshot, ids2),
13142
13382
  layers: displayBlock ? virtualBlockLayers(displayBlock) : blockLayers(block),
13143
13383
  virtual: displayBlock?.virtual ?? false
13144
13384
  };
@@ -13153,7 +13393,7 @@ async function sources(runtime, url) {
13153
13393
  } : {},
13154
13394
  elements: elements.map(elementSummary),
13155
13395
  blocks: events.map((event) => snapshot.blocks.find(({ id }) => id === event.sourceBlockId)).filter((block) => Boolean(block)).map((block) => ({ id: block.id, title: block.l0Title, createdAt: block.createdAt })),
13156
- messages: sourceMessages(snapshot, ids)
13396
+ messages: sourceMessages(snapshot, ids2)
13157
13397
  };
13158
13398
  }
13159
13399
  async function expandBlock(runtime, url) {
@@ -13251,20 +13491,20 @@ async function audit(runtime, url) {
13251
13491
  }
13252
13492
  function requestHeader(req, name2) {
13253
13493
  const headers = req.headers ?? {};
13254
- const key = Object.keys(headers).find((candidate) => candidate.toLocaleLowerCase() === name2.toLocaleLowerCase());
13255
- const value = key ? headers[key] : void 0;
13494
+ const key2 = Object.keys(headers).find((candidate) => candidate.toLocaleLowerCase() === name2.toLocaleLowerCase());
13495
+ const value = key2 ? headers[key2] : void 0;
13256
13496
  return Array.isArray(value) ? value.join(", ") : value ?? "";
13257
13497
  }
13258
13498
  async function dashboard(runtime, url, ifNoneMatch) {
13259
- const entries = await runtime.adminSnapshotEntries();
13499
+ const entries2 = await runtime.adminSnapshotEntries();
13260
13500
  const requestedNamespace = url.searchParams.get("namespace")?.trim() ?? "";
13261
- const selected = entries.find(({ namespace }) => namespace === requestedNamespace) ?? entries[0];
13501
+ const selected = entries2.find(({ namespace }) => namespace === requestedNamespace) ?? entries2[0];
13262
13502
  const threadId = url.searchParams.get("threadId")?.trim() ?? "";
13263
- const revisionKey = entries.map(({ namespace, revision }) => `${namespace}:${revision}`).join("|");
13503
+ const revisionKey = entries2.map(({ namespace, revision }) => `${namespace}:${revision}`).join("|");
13264
13504
  const agentMemoryWeight = runtime.adminAgentMemoryRetrievalWeight?.() ?? 1;
13265
13505
  const etag = `"${createHash4("sha256").update(`${STRATAGATE_DSH_VERSION}\0${TOPIC_DIRECTORY_VIEW_VERSION}\0${revisionKey}\0${selected?.namespace ?? ""}\0${threadId}\0${agentMemoryWeight}`).digest("base64url").slice(0, 24)}"`;
13266
13506
  if (ifNoneMatch.split(",").map((value) => value.trim()).includes(etag)) return { etag, notModified: true };
13267
- const overviewValue = await overview(runtime, entries);
13507
+ const overviewValue = await overview(runtime, entries2);
13268
13508
  if (!selected) {
13269
13509
  return { etag, notModified: false, body: { namespace: null, overview: overviewValue, data: null, processing: false } };
13270
13510
  }
@@ -13402,8 +13642,8 @@ function isCompleteAnswer(data) {
13402
13642
  if (!data.message.content.some((block) => block.type === "text" && block.text.trim().length > 0)) return false;
13403
13643
  const stream = data.stream ?? [];
13404
13644
  for (let index = stream.length - 1; index >= 0; index--) {
13405
- const record2 = stream[index];
13406
- if (record2.type === "chunk" && record2.chunk.type === "finish") return record2.chunk.reason.kind === "stop";
13645
+ const record3 = stream[index];
13646
+ if (record3.type === "chunk" && record3.chunk.type === "finish") return record3.chunk.reason.kind === "stop";
13407
13647
  }
13408
13648
  return true;
13409
13649
  }