@remnic/core 9.6.30 → 9.6.32

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.
Files changed (74) hide show
  1. package/dist/access-admin-ops-surface.d.ts +1 -0
  2. package/dist/access-boundary.d.ts +1 -0
  3. package/dist/access-cli.js +9 -9
  4. package/dist/access-http.d.ts +1 -0
  5. package/dist/access-identity-continuity-surface.d.ts +1 -0
  6. package/dist/access-lcm-surface.d.ts +1 -0
  7. package/dist/access-mcp.d.ts +1 -0
  8. package/dist/access-observe-write-surface.d.ts +1 -0
  9. package/dist/access-operations.d.ts +1 -0
  10. package/dist/access-recall-surface.d.ts +1 -0
  11. package/dist/access-service.d.ts +1 -0
  12. package/dist/access-surface-catalog.d.ts +1 -0
  13. package/dist/bootstrap.d.ts +1 -0
  14. package/dist/causal-behavior.d.ts +1 -0
  15. package/dist/{chunk-XGSOXFD4.js → chunk-6TCXIRP5.js} +2 -2
  16. package/dist/{chunk-7O2JL5RY.js → chunk-DEOYTFA6.js} +2 -2
  17. package/dist/{chunk-R26WO46Z.js → chunk-DGFU5MSX.js} +16 -1
  18. package/dist/chunk-DGFU5MSX.js.map +1 -0
  19. package/dist/{chunk-BFU7H32D.js → chunk-OH7QLI6W.js} +2 -2
  20. package/dist/{chunk-IRMLYBMU.js → chunk-T67LUYCR.js} +2 -2
  21. package/dist/{chunk-77UPM7P3.js → chunk-TVIVY7KU.js} +160 -61
  22. package/dist/chunk-TVIVY7KU.js.map +1 -0
  23. package/dist/{chunk-KCYE2MZM.js → chunk-UXPUUL3G.js} +29 -8
  24. package/dist/chunk-UXPUUL3G.js.map +1 -0
  25. package/dist/{chunk-3VSTTB74.js → chunk-WSOEYOHN.js} +7 -7
  26. package/dist/{chunk-5AJIDEFT.js → chunk-XFTPUXCE.js} +11 -7
  27. package/dist/chunk-XFTPUXCE.js.map +1 -0
  28. package/dist/cli.d.ts +1 -0
  29. package/dist/compounding/preference-consolidator.d.ts +1 -0
  30. package/dist/event-order-recall.d.ts +1 -0
  31. package/dist/event-order-recall.js +2 -2
  32. package/dist/evidence-pack.d.ts +16 -1
  33. package/dist/evidence-pack.js +1 -1
  34. package/dist/explicit-capture.d.ts +1 -0
  35. package/dist/explicit-cue-recall.d.ts +16 -1
  36. package/dist/explicit-cue-recall.js +2 -2
  37. package/dist/focused-list-recall.d.ts +1 -0
  38. package/dist/focused-list-recall.js +2 -2
  39. package/dist/index.d.ts +3 -2
  40. package/dist/index.js +9 -9
  41. package/dist/lcm/engine.d.ts +3 -0
  42. package/dist/lcm/engine.js +2 -2
  43. package/dist/lcm/index.d.ts +1 -1
  44. package/dist/lcm/index.js +5 -3
  45. package/dist/lcm/recall.d.ts +15 -1
  46. package/dist/lcm/recall.js +5 -3
  47. package/dist/lcm/tools.d.ts +1 -0
  48. package/dist/mcp-memory-inspector-app.d.ts +1 -0
  49. package/dist/orchestrator.d.ts +1 -0
  50. package/dist/orchestrator.js +9 -9
  51. package/dist/response-guidance-recall.d.ts +1 -0
  52. package/dist/response-guidance-recall.js +2 -2
  53. package/dist/targeted-fact-recall.d.ts +1 -0
  54. package/dist/targeted-fact-recall.js +2 -2
  55. package/package.json +2 -2
  56. package/src/evidence-pack.test.ts +66 -1
  57. package/src/evidence-pack.ts +39 -0
  58. package/src/explicit-cue-recall.ts +224 -67
  59. package/src/index.ts +3 -0
  60. package/src/lcm/engine.ts +17 -5
  61. package/src/lcm/index.ts +6 -1
  62. package/src/lcm/recall.ts +48 -5
  63. package/src/lcm-engine.test.ts +46 -0
  64. package/src/lcm-recall.test.ts +48 -1
  65. package/src/recall-trace-receipts.test.ts +380 -0
  66. package/dist/chunk-5AJIDEFT.js.map +0 -1
  67. package/dist/chunk-77UPM7P3.js.map +0 -1
  68. package/dist/chunk-KCYE2MZM.js.map +0 -1
  69. package/dist/chunk-R26WO46Z.js.map +0 -1
  70. /package/dist/{chunk-XGSOXFD4.js.map → chunk-6TCXIRP5.js.map} +0 -0
  71. /package/dist/{chunk-7O2JL5RY.js.map → chunk-DEOYTFA6.js.map} +0 -0
  72. /package/dist/{chunk-BFU7H32D.js.map → chunk-OH7QLI6W.js.map} +0 -0
  73. /package/dist/{chunk-IRMLYBMU.js.map → chunk-T67LUYCR.js.map} +0 -0
  74. /package/dist/{chunk-3VSTTB74.js.map → chunk-WSOEYOHN.js.map} +0 -0
@@ -1,4 +1,7 @@
1
- import { buildEvidencePack } from "./evidence-pack.js";
1
+ import {
2
+ buildEvidencePack,
3
+ type EvidencePackSelectionReceipt,
4
+ } from "./evidence-pack.js";
2
5
  import {
3
6
  gatherAcrossReadSessions,
4
7
  resolveLcmReadSessionIds,
@@ -61,6 +64,8 @@ export interface ExplicitCueRecallOptions {
61
64
  includeBenchmarkAnchorCues?: boolean;
62
65
  includeContentLexicalCues?: boolean;
63
66
  includeStructuredPlanCues?: boolean;
67
+ /** Observe selected evidence blocks without changing rendered output. */
68
+ onEvidenceSelected?: (receipt: EvidencePackSelectionReceipt) => void;
64
69
  }
65
70
 
66
71
  export interface TrajectoryAnalysisRecallOptions {
@@ -68,6 +73,18 @@ export interface TrajectoryAnalysisRecallOptions {
68
73
  sessionId?: string;
69
74
  query: string;
70
75
  maxChars: number;
76
+ /** Observe only emitted lines; unavailable lineage is never guessed. */
77
+ onLineSelected?: (receipt: TrajectoryAnalysisLineReceipt) => void;
78
+ }
79
+
80
+ export interface TrajectoryAnalysisLineReceipt {
81
+ /** Half-open offsets within the returned trajectory section. */
82
+ lineStart: number;
83
+ lineEnd: number;
84
+ /** Whether the source row set is structurally exact or unavailable. */
85
+ lineageStatus: "exact" | "unavailable";
86
+ actionArchiveRowIds: number[];
87
+ observationArchiveRowIds: number[];
71
88
  }
72
89
 
73
90
  export type ExplicitTurnReference = {
@@ -455,6 +472,9 @@ export async function buildExplicitCueRecallSection(
455
472
  options.maxItemChars,
456
473
  DEFAULT_MAX_ITEM_CHARS,
457
474
  ),
475
+ ...(options.onEvidenceSelected
476
+ ? { onSelection: options.onEvidenceSelected }
477
+ : {}),
458
478
  });
459
479
  }
460
480
 
@@ -519,7 +539,32 @@ export async function buildTrajectoryAnalysisRecallSection(
519
539
  }
520
540
 
521
541
  const clipped = truncateTrajectoryAnalysisLines(lines, bodyBudget);
522
- return clipped.length === 0 ? "" : `${header}\n${clipped.join("\n")}`;
542
+ if (clipped.length === 0) return "";
543
+ const text = `${header}\n${clipped.map((line) => line.text).join("\n")}`;
544
+ if (options.onLineSelected) {
545
+ let lineStart = header.length + 1;
546
+ for (const line of clipped) {
547
+ const lineEnd = lineStart + line.text.length;
548
+ options.onLineSelected({
549
+ lineStart,
550
+ lineEnd,
551
+ lineageStatus: line.lineageStatus,
552
+ actionArchiveRowIds: [...line.actionArchiveRowIds],
553
+ observationArchiveRowIds: [...line.observationArchiveRowIds],
554
+ });
555
+ lineStart = lineEnd + 1;
556
+ }
557
+ }
558
+ return text;
559
+ }
560
+
561
+ function uniqueSortedArchiveRowIds(
562
+ values: readonly (number | undefined)[],
563
+ ): number[] {
564
+ return [...new Set(values.filter(
565
+ (value): value is number =>
566
+ typeof value === "number" && Number.isSafeInteger(value) && value > 0,
567
+ ))].sort((left, right) => left - right);
523
568
  }
524
569
 
525
570
  async function collectTurnReferenceEvidence(options: {
@@ -1130,6 +1175,65 @@ interface LabeledTrajectoryStep {
1130
1175
  observationArchiveRowId?: number;
1131
1176
  }
1132
1177
 
1178
+ interface TrajectoryLineSource {
1179
+ lineageStatus: "exact" | "unavailable";
1180
+ actionArchiveRowIds: number[];
1181
+ observationArchiveRowIds: number[];
1182
+ }
1183
+
1184
+ interface SelectedTrajectoryLine extends TrajectoryLineSource {
1185
+ text: string;
1186
+ }
1187
+
1188
+ class TrajectoryAnalysisLines extends Array<string> {
1189
+ private readonly sources = new Map<number, TrajectoryLineSource>();
1190
+
1191
+ pushWithSources(
1192
+ text: string,
1193
+ actionSteps: readonly LabeledTrajectoryStep[] = [],
1194
+ observationSteps: readonly LabeledTrajectoryStep[] = [],
1195
+ ): number {
1196
+ const index = this.length;
1197
+ const length = super.push(text);
1198
+ const hasUnavailableSource =
1199
+ actionSteps.some((step) =>
1200
+ lcmArchiveRowId({ id: step.actionArchiveRowId }) === undefined
1201
+ ) ||
1202
+ observationSteps.some((step) =>
1203
+ lcmArchiveRowId({ id: step.observationArchiveRowId }) === undefined
1204
+ );
1205
+ this.sources.set(index, {
1206
+ lineageStatus: hasUnavailableSource ? "unavailable" : "exact",
1207
+ actionArchiveRowIds: hasUnavailableSource
1208
+ ? []
1209
+ : uniqueSortedArchiveRowIds(
1210
+ actionSteps.map((step) => step.actionArchiveRowId),
1211
+ ),
1212
+ observationArchiveRowIds: hasUnavailableSource
1213
+ ? []
1214
+ : uniqueSortedArchiveRowIds(
1215
+ observationSteps.map((step) => step.observationArchiveRowId),
1216
+ ),
1217
+ });
1218
+ return length;
1219
+ }
1220
+
1221
+ sourceAt(index: number): TrajectoryLineSource {
1222
+ const source = this.sources.get(index);
1223
+ return source
1224
+ ? {
1225
+ lineageStatus: source.lineageStatus,
1226
+ actionArchiveRowIds: [...source.actionArchiveRowIds],
1227
+ observationArchiveRowIds: [...source.observationArchiveRowIds],
1228
+ }
1229
+ : {
1230
+ lineageStatus: "unavailable",
1231
+ actionArchiveRowIds: [],
1232
+ observationArchiveRowIds: [],
1233
+ };
1234
+ }
1235
+ }
1236
+
1133
1237
  interface TrajectoryBounds {
1134
1238
  start: number;
1135
1239
  end: number;
@@ -1315,12 +1419,13 @@ function buildTrajectoryAnalysisLines(
1315
1419
  query: string,
1316
1420
  trajectory: readonly LabeledTrajectoryStep[],
1317
1421
  bounds: TrajectoryBounds,
1318
- ): string[] {
1422
+ ): TrajectoryAnalysisLines {
1319
1423
  const normalized = normalizeTrajectoryQuery(query);
1320
1424
  const explicitReferences = collectExplicitTurnReferences(query);
1321
- const lines: string[] = [
1425
+ const lines = new TrajectoryAnalysisLines();
1426
+ lines.pushWithSources(
1322
1427
  `Analyzed labeled action/observation transcript window: steps ${bounds.start}-${bounds.end} (${bounds.reason}).`,
1323
- ];
1428
+ );
1324
1429
  const hasQuotedObservationPair = extractQuotedObservations(query).length >= 2;
1325
1430
  const transition = findObservationTransition(query, trajectory);
1326
1431
  const entities = extractNumberedEntities(query);
@@ -1340,8 +1445,14 @@ function buildTrajectoryAnalysisLines(
1340
1445
  /\bwhat\s+will\s+be\s+the\s+resulting\b/.test(normalized);
1341
1446
 
1342
1447
  if (transition) {
1343
- lines.push(
1448
+ const fromStep = trajectory.find((step) => step.step === transition.fromStep);
1449
+ const toStep = trajectory.find((step) => step.step === transition.toStep);
1450
+ lines.pushWithSources(
1344
1451
  `Matched quoted observations: Observation ${transition.fromStep} -> Observation ${transition.toStep}.`,
1452
+ [],
1453
+ [fromStep, toStep].filter(
1454
+ (step): step is LabeledTrajectoryStep => step !== undefined,
1455
+ ),
1345
1456
  );
1346
1457
  appendActionSequenceSummary(
1347
1458
  lines,
@@ -1424,11 +1535,11 @@ function buildTrajectoryAnalysisLines(
1424
1535
  }
1425
1536
  }
1426
1537
 
1427
- return lines.length === 1 ? [] : lines;
1538
+ return lines.length === 1 ? new TrajectoryAnalysisLines() : lines;
1428
1539
  }
1429
1540
 
1430
1541
  function appendReferencedTrajectoryLines(
1431
- lines: string[],
1542
+ lines: TrajectoryAnalysisLines,
1432
1543
  trajectory: readonly LabeledTrajectoryStep[],
1433
1544
  references: readonly ExplicitTurnReference[],
1434
1545
  options: { includeObservations: boolean; heading: string },
@@ -1442,19 +1553,23 @@ function appendReferencedTrajectoryLines(
1442
1553
  return;
1443
1554
  }
1444
1555
 
1445
- lines.push(options.heading);
1556
+ lines.pushWithSources(options.heading);
1446
1557
  for (const step of window) {
1447
1558
  if (step.action) {
1448
- lines.push(`[Action ${step.step}]: ${step.action}`);
1559
+ lines.pushWithSources(`[Action ${step.step}]: ${step.action}`, [step]);
1449
1560
  }
1450
1561
  if (options.includeObservations && step.observation) {
1451
- lines.push(`[Observation ${step.step}]: ${oneLine(step.observation)}`);
1562
+ lines.pushWithSources(
1563
+ `[Observation ${step.step}]: ${oneLine(step.observation)}`,
1564
+ [],
1565
+ [step],
1566
+ );
1452
1567
  }
1453
1568
  }
1454
1569
  }
1455
1570
 
1456
1571
  function appendActionRangeLines(
1457
- lines: string[],
1572
+ lines: TrajectoryAnalysisLines,
1458
1573
  trajectory: readonly LabeledTrajectoryStep[],
1459
1574
  start: number,
1460
1575
  end: number,
@@ -1470,19 +1585,23 @@ function appendActionRangeLines(
1470
1585
  if (window.length === 0) {
1471
1586
  return;
1472
1587
  }
1473
- lines.push(options.heading);
1588
+ lines.pushWithSources(options.heading);
1474
1589
  for (const step of window) {
1475
1590
  if (step.action) {
1476
- lines.push(`[Action ${step.step}]: ${step.action}`);
1591
+ lines.pushWithSources(`[Action ${step.step}]: ${step.action}`, [step]);
1477
1592
  }
1478
1593
  if (options.includeObservations && step.observation) {
1479
- lines.push(`[Observation ${step.step}]: ${oneLine(step.observation)}`);
1594
+ lines.pushWithSources(
1595
+ `[Observation ${step.step}]: ${oneLine(step.observation)}`,
1596
+ [],
1597
+ [step],
1598
+ );
1480
1599
  }
1481
1600
  }
1482
1601
  }
1483
1602
 
1484
1603
  function appendActionSequenceSummary(
1485
- lines: string[],
1604
+ lines: TrajectoryAnalysisLines,
1486
1605
  trajectory: readonly LabeledTrajectoryStep[],
1487
1606
  start: number,
1488
1607
  end: number,
@@ -1490,17 +1609,18 @@ function appendActionSequenceSummary(
1490
1609
  ): void {
1491
1610
  const low = Math.min(start, end);
1492
1611
  const high = Math.max(start, end);
1493
- const actions = trajectory
1494
- .filter((step) => step.step >= low && step.step <= high && step.action)
1612
+ const actionSteps = trajectory
1613
+ .filter((step) => step.step >= low && step.step <= high && step.action);
1614
+ const actions = actionSteps
1495
1615
  .map((step) => `step ${step.step}: ${step.action}`);
1496
1616
  if (actions.length === 0) {
1497
1617
  return;
1498
1618
  }
1499
- lines.push(`${heading} ${actions.join("; ")}.`);
1619
+ lines.pushWithSources(`${heading} ${actions.join("; ")}.`, actionSteps);
1500
1620
  }
1501
1621
 
1502
1622
  function appendResultingObservationLine(
1503
- lines: string[],
1623
+ lines: TrajectoryAnalysisLines,
1504
1624
  trajectory: readonly LabeledTrajectoryStep[],
1505
1625
  stepNumber: number,
1506
1626
  ): void {
@@ -1508,11 +1628,15 @@ function appendResultingObservationLine(
1508
1628
  if (!step?.observation) {
1509
1629
  return;
1510
1630
  }
1511
- lines.push(`Resulting observation after Action ${step.step}: ${oneLine(step.observation)}`);
1631
+ lines.pushWithSources(
1632
+ `Resulting observation after Action ${step.step}: ${oneLine(step.observation)}`,
1633
+ [],
1634
+ [step],
1635
+ );
1512
1636
  }
1513
1637
 
1514
1638
  function appendSpatialTrajectoryInferenceLines(
1515
- lines: string[],
1639
+ lines: TrajectoryAnalysisLines,
1516
1640
  query: string,
1517
1641
  trajectory: readonly LabeledTrajectoryStep[],
1518
1642
  bounds: TrajectoryBounds,
@@ -1536,23 +1660,23 @@ function appendSpatialTrajectoryInferenceLines(
1536
1660
  const window = spatialTrajectoryWindowForQuery(query, trajectory, bounds);
1537
1661
  const counterfactualContactLines = collectCounterfactualContactLines(query, trajectory, normalized);
1538
1662
  if (counterfactualContactLines.length > 0) {
1539
- lines.push("Counterfactual contact cues:");
1663
+ lines.pushWithSources("Counterfactual contact cues:");
1540
1664
  lines.push(...counterfactualContactLines);
1541
1665
  }
1542
1666
 
1543
1667
  const selfReversingLines = collectSelfReversingProgressLines(query, window, normalized);
1544
1668
  if (selfReversingLines.length > 0) {
1545
- lines.push("Self-reversing sequence cues:");
1669
+ lines.pushWithSources("Self-reversing sequence cues:");
1546
1670
  lines.push(...selfReversingLines);
1547
1671
  }
1548
1672
  const onlyEffectiveActionLines = collectOnlyEffectiveActionLines(window, normalized);
1549
1673
  if (onlyEffectiveActionLines.length > 0) {
1550
- lines.push("Only-effective action cues:");
1674
+ lines.pushWithSources("Only-effective action cues:");
1551
1675
  lines.push(...onlyEffectiveActionLines);
1552
1676
  }
1553
1677
  const pushedPhraseGroupLines = collectPushedPhraseGroupShiftLines(query, window, normalized);
1554
1678
  if (pushedPhraseGroupLines.length > 0) {
1555
- lines.push("Pushed phrase-group shift cues:");
1679
+ lines.pushWithSources("Pushed phrase-group shift cues:");
1556
1680
  lines.push(...pushedPhraseGroupLines);
1557
1681
  }
1558
1682
  const suppressMovementProgressEvidence = onlyEffectiveActionLines.length > 0 ||
@@ -1566,7 +1690,7 @@ function appendSpatialTrajectoryInferenceLines(
1566
1690
  ? []
1567
1691
  : collectActionMovementSummaryLines(query, window, bounds);
1568
1692
  if (actionMovementLines.length > 0) {
1569
- lines.push("Action movement summary cues:");
1693
+ lines.pushWithSources("Action movement summary cues:");
1570
1694
  lines.push(...actionMovementLines);
1571
1695
  }
1572
1696
 
@@ -1574,7 +1698,7 @@ function appendSpatialTrajectoryInferenceLines(
1574
1698
  ? []
1575
1699
  : collectMovementDeltaLines(query, window);
1576
1700
  if (movementLines.length > 0) {
1577
- lines.push("Relative-position movement cues:");
1701
+ lines.pushWithSources("Relative-position movement cues:");
1578
1702
  lines.push(...movementLines);
1579
1703
  }
1580
1704
 
@@ -1585,37 +1709,37 @@ function appendSpatialTrajectoryInferenceLines(
1585
1709
  normalized,
1586
1710
  );
1587
1711
  if (objectAlignmentLines.length > 0) {
1588
- lines.push("Object alignment cues:");
1712
+ lines.pushWithSources("Object alignment cues:");
1589
1713
  lines.push(...objectAlignmentLines);
1590
1714
  }
1591
1715
 
1592
1716
  const alternativeActionLines = collectAlternativeActionLines(query, trajectory);
1593
1717
  if (alternativeActionLines.length > 0) {
1594
- lines.push("Counterfactual action cues:");
1718
+ lines.pushWithSources("Counterfactual action cues:");
1595
1719
  lines.push(...alternativeActionLines);
1596
1720
  }
1597
1721
 
1598
1722
  const adjacentRuleSetupLines = collectAdjacentRuleSetupLines(window, normalized);
1599
1723
  if (adjacentRuleSetupLines.length > 0) {
1600
- lines.push("Adjacent rule-block setup cues:");
1724
+ lines.pushWithSources("Adjacent rule-block setup cues:");
1601
1725
  lines.push(...adjacentRuleSetupLines);
1602
1726
  }
1603
1727
 
1604
1728
  const blockedMoveLines = collectBlockedMoveLines(query, window, normalized);
1605
1729
  if (blockedMoveLines.length > 0) {
1606
- lines.push("Blocked-move cues:");
1730
+ lines.pushWithSources("Blocked-move cues:");
1607
1731
  lines.push(...blockedMoveLines);
1608
1732
  }
1609
1733
 
1610
1734
  const failedEscapeLines = collectFailedEscapeLines(query, trajectory, normalized);
1611
1735
  if (failedEscapeLines.length > 0) {
1612
- lines.push("Failed-move escape cues:");
1736
+ lines.pushWithSources("Failed-move escape cues:");
1613
1737
  lines.push(...failedEscapeLines);
1614
1738
  }
1615
1739
 
1616
1740
  const sameRelativeTextPushLines = collectSameRelativeTextPushLines(window, normalized);
1617
1741
  if (sameRelativeTextPushLines.length > 0) {
1618
- lines.push("Same-relative text-push cues:");
1742
+ lines.pushWithSources("Same-relative text-push cues:");
1619
1743
  lines.push(...sameRelativeTextPushLines);
1620
1744
  }
1621
1745
 
@@ -1623,43 +1747,43 @@ function appendSpatialTrajectoryInferenceLines(
1623
1747
  ? []
1624
1748
  : collectFailedContactBoundaryLines(window, normalized);
1625
1749
  if (failedContactLines.length > 0) {
1626
- lines.push("Failed-push boundary cues:");
1750
+ lines.pushWithSources("Failed-push boundary cues:");
1627
1751
  lines.push(...failedContactLines);
1628
1752
  }
1629
1753
 
1630
1754
  const transformationLines = collectTemporaryRuleTransformationLines(window, normalized);
1631
1755
  if (transformationLines.length > 0) {
1632
- lines.push("Temporary transformation cues:");
1756
+ lines.pushWithSources("Temporary transformation cues:");
1633
1757
  lines.push(...transformationLines);
1634
1758
  }
1635
1759
 
1636
1760
  const wholeConfigurationShiftLines = collectWholeConfigurationShiftLines(window, normalized);
1637
1761
  if (wholeConfigurationShiftLines.length > 0) {
1638
- lines.push("Whole-configuration shift cues:");
1762
+ lines.pushWithSources("Whole-configuration shift cues:");
1639
1763
  lines.push(...wholeConfigurationShiftLines);
1640
1764
  }
1641
1765
 
1642
1766
  const ruleInterventionLines = collectRuleInterventionStrategyLines(window, normalized);
1643
1767
  if (ruleInterventionLines.length > 0) {
1644
- lines.push("Rule-intervention strategy cues:");
1768
+ lines.pushWithSources("Rule-intervention strategy cues:");
1645
1769
  lines.push(...ruleInterventionLines);
1646
1770
  }
1647
1771
 
1648
1772
  const missingPushTargetLines = collectMissingPushTargetLines(window, normalized);
1649
1773
  if (missingPushTargetLines.length > 0) {
1650
- lines.push("Missing-interaction cues:");
1774
+ lines.pushWithSources("Missing-interaction cues:");
1651
1775
  lines.push(...missingPushTargetLines);
1652
1776
  }
1653
1777
 
1654
1778
  const rulePhraseAlignmentLines = collectRulePhraseAlignmentLines(window, normalized);
1655
1779
  if (rulePhraseAlignmentLines.length > 0) {
1656
- lines.push("Rule-phrase alignment cues:");
1780
+ lines.pushWithSources("Rule-phrase alignment cues:");
1657
1781
  lines.push(...rulePhraseAlignmentLines);
1658
1782
  }
1659
1783
 
1660
1784
  const controlRuleInteractionLines = collectControlRuleInteractionLines(window, normalized);
1661
1785
  if (controlRuleInteractionLines.length > 0) {
1662
- lines.push("Control-rule interaction cues:");
1786
+ lines.pushWithSources("Control-rule interaction cues:");
1663
1787
  lines.push(...controlRuleInteractionLines);
1664
1788
  }
1665
1789
 
@@ -1667,13 +1791,13 @@ function appendSpatialTrajectoryInferenceLines(
1667
1791
  ? []
1668
1792
  : collectRuleTextPositionLines(window, normalized);
1669
1793
  if (ruleTextPositionLines.length > 0) {
1670
- lines.push("Rule-text positioning cues:");
1794
+ lines.pushWithSources("Rule-text positioning cues:");
1671
1795
  lines.push(...ruleTextPositionLines);
1672
1796
  }
1673
1797
 
1674
1798
  const ruleLines = collectRuleStateLines(window, normalized);
1675
1799
  if (ruleLines.length > 0) {
1676
- lines.push("Rule-state cues:");
1800
+ lines.pushWithSources("Rule-state cues:");
1677
1801
  lines.push(...ruleLines);
1678
1802
  }
1679
1803
  }
@@ -3466,12 +3590,15 @@ function uniqueLines(lines: readonly string[]): string[] {
3466
3590
  }
3467
3591
 
3468
3592
  function appendActionFrequencyLines(
3469
- lines: string[],
3593
+ lines: TrajectoryAnalysisLines,
3470
3594
  trajectory: readonly LabeledTrajectoryStep[],
3471
3595
  bounds: TrajectoryBounds,
3472
3596
  ): void {
3597
+ const actionSteps = boundedTrajectory(trajectory, bounds).filter(
3598
+ (step) => step.action !== undefined,
3599
+ );
3473
3600
  const counts = new Map<string, number>();
3474
- for (const step of boundedTrajectory(trajectory, bounds)) {
3601
+ for (const step of actionSteps) {
3475
3602
  if (!step.action) continue;
3476
3603
  const verb = normalizeActionVerb(step.action);
3477
3604
  if (!verb) continue;
@@ -3483,15 +3610,16 @@ function appendActionFrequencyLines(
3483
3610
  const frequencies = [...counts.entries()].sort((left, right) =>
3484
3611
  right[1] - left[1] || left[0].localeCompare(right[0]),
3485
3612
  );
3486
- lines.push(
3613
+ lines.pushWithSources(
3487
3614
  `Action frequency through requested window: ${frequencies
3488
3615
  .map(([verb, count]) => `${verb}=${count}`)
3489
3616
  .join(", ")}.`,
3617
+ actionSteps,
3490
3618
  );
3491
3619
  }
3492
3620
 
3493
3621
  function appendInventoryChangeLines(
3494
- lines: string[],
3622
+ lines: TrajectoryAnalysisLines,
3495
3623
  trajectory: readonly LabeledTrajectoryStep[],
3496
3624
  bounds: TrajectoryBounds,
3497
3625
  ): void {
@@ -3507,16 +3635,24 @@ function appendInventoryChangeLines(
3507
3635
  if (changes.length === 0) {
3508
3636
  return;
3509
3637
  }
3510
- lines.push("Inventory changes from action transcript:");
3638
+ const changeSteps = changes
3639
+ .map((change) => trajectory.find((step) => step.step === change.step))
3640
+ .filter((step): step is LabeledTrajectoryStep => step !== undefined);
3641
+ lines.pushWithSources("Inventory changes from action transcript:");
3511
3642
  if (changes.length > 5) {
3512
- lines.push(
3643
+ lines.pushWithSources(
3513
3644
  `First five inventory changes: ${formatInventoryChangeSummary(changes.slice(0, 5))}.`,
3645
+ changeSteps.slice(0, 5),
3514
3646
  );
3515
- lines.push(
3647
+ lines.pushWithSources(
3516
3648
  `Complete inventory changes: ${formatInventoryChangeSummary(changes)}.`,
3649
+ changeSteps,
3517
3650
  );
3518
3651
  }
3519
3652
  for (const change of changes) {
3653
+ // The rendered change is stateful: whether a place/move removes an item
3654
+ // depends on earlier take actions in `held`. Until the parser returns that
3655
+ // full dependency chain, do not publish a misleading partial row receipt.
3520
3656
  lines.push(`[Action ${change.step}]: ${change.action} => ${change.change}`);
3521
3657
  }
3522
3658
  }
@@ -3543,7 +3679,7 @@ function summarizeInventoryChange(change: string): string {
3543
3679
  }
3544
3680
 
3545
3681
  function appendContainerStateChangeLines(
3546
- lines: string[],
3682
+ lines: TrajectoryAnalysisLines,
3547
3683
  trajectory: readonly LabeledTrajectoryStep[],
3548
3684
  bounds: TrajectoryBounds,
3549
3685
  ): void {
@@ -3551,14 +3687,18 @@ function appendContainerStateChangeLines(
3551
3687
  if (changes.length === 0) {
3552
3688
  return;
3553
3689
  }
3554
- lines.push("Container open/close state changes:");
3690
+ lines.pushWithSources("Container open/close state changes:");
3555
3691
  for (const change of changes) {
3556
- lines.push(`[Action ${change.step}]: ${change.action} => ${change.entity} ${change.state}`);
3692
+ const step = trajectory.find((candidate) => candidate.step === change.step);
3693
+ lines.pushWithSources(
3694
+ `[Action ${change.step}]: ${change.action} => ${change.entity} ${change.state}`,
3695
+ step ? [step] : [],
3696
+ );
3557
3697
  }
3558
3698
  }
3559
3699
 
3560
3700
  function appendEntityTimelineLines(
3561
- lines: string[],
3701
+ lines: TrajectoryAnalysisLines,
3562
3702
  trajectory: readonly LabeledTrajectoryStep[],
3563
3703
  bounds: TrajectoryBounds,
3564
3704
  entities: readonly string[],
@@ -3594,28 +3734,37 @@ function appendEntityTimelineLines(
3594
3734
  continue;
3595
3735
  }
3596
3736
 
3597
- lines.push(`Timeline for ${entity}:`);
3737
+ lines.pushWithSources(`Timeline for ${entity}:`);
3598
3738
  for (const step of directActions) {
3599
- lines.push(`[Action ${step.step}]: ${step.action}`);
3739
+ lines.pushWithSources(`[Action ${step.step}]: ${step.action}`, [step]);
3600
3740
  }
3601
3741
  if (objectTransfers.length > 0) {
3602
- lines.push(`Object transfers involving ${entity}:`);
3742
+ lines.pushWithSources(`Object transfers involving ${entity}:`);
3603
3743
  for (const transfer of objectTransfers) {
3604
- lines.push(`[Action ${transfer.step}]: ${transfer.action} => ${formatContainerTransfer(transfer)}`);
3744
+ const step = trajectory.find((candidate) => candidate.step === transfer.step);
3745
+ lines.pushWithSources(
3746
+ `[Action ${transfer.step}]: ${transfer.action} => ${formatContainerTransfer(transfer)}`,
3747
+ step ? [step] : [],
3748
+ );
3605
3749
  }
3606
3750
  }
3607
3751
  const inferredLocation = inferEntityLocation(trajectory, bounds, entity);
3608
3752
  if (inferredLocation) {
3609
- lines.push(
3610
- `Inferred ${entity} location at step ${bounds.end}: ${inferredLocation}.`,
3753
+ lines.pushWithSources(
3754
+ `Inferred ${entity} location at step ${bounds.end}: ${inferredLocation.location}.`,
3755
+ [inferredLocation.sourceStep],
3611
3756
  );
3612
3757
  }
3613
3758
  if (stateChanges.length > 0) {
3614
3759
  const latest = stateChanges[stateChanges.length - 1]!;
3615
- lines.push(
3760
+ const stateSteps = stateChanges
3761
+ .map((change) => trajectory.find((step) => step.step === change.step))
3762
+ .filter((step): step is LabeledTrajectoryStep => step !== undefined);
3763
+ lines.pushWithSources(
3616
3764
  `Latest ${entity} state at step ${bounds.end}: ${latest.state}; state changes: ${stateChanges
3617
3765
  .map((change) => `${change.step}:${change.state}`)
3618
3766
  .join(", ")}.`,
3767
+ stateSteps,
3619
3768
  );
3620
3769
  }
3621
3770
  }
@@ -3966,15 +4115,17 @@ function inferEntityLocation(
3966
4115
  trajectory: readonly LabeledTrajectoryStep[],
3967
4116
  bounds: TrajectoryBounds,
3968
4117
  entity: string,
3969
- ): string | undefined {
4118
+ ): { location: string; sourceStep: LabeledTrajectoryStep } | undefined {
3970
4119
  const normalizedEntity = normalizeEntity(entity);
3971
4120
  let location: string | undefined;
4121
+ let sourceStep: LabeledTrajectoryStep | undefined;
3972
4122
  for (const step of boundedTrajectory(trajectory, bounds)) {
3973
4123
  if (!step.action) continue;
3974
4124
  const action = step.action.trim();
3975
4125
  const take = /^take\s+(.+?)\s+from\s+(.+)$/i.exec(action);
3976
4126
  if (take && normalizeEntity(take[1]!) === normalizedEntity) {
3977
4127
  location = "inventory";
4128
+ sourceStep = step;
3978
4129
  continue;
3979
4130
  }
3980
4131
  const move = /^(?:move|put|place|insert)\s+(.+?)\s+(?:to|in|into|on)\s+(.+)$/i.exec(
@@ -3982,9 +4133,10 @@ function inferEntityLocation(
3982
4133
  );
3983
4134
  if (move && normalizeEntity(move[1]!) === normalizedEntity) {
3984
4135
  location = move[2]!.trim();
4136
+ sourceStep = step;
3985
4137
  }
3986
4138
  }
3987
- return location;
4139
+ return location && sourceStep ? { location, sourceStep } : undefined;
3988
4140
  }
3989
4141
 
3990
4142
  function normalizeActionVerb(action: string): string | undefined {
@@ -4028,10 +4180,15 @@ export function normalizeTurnExpansionEnd(stats: {
4028
4180
  return Math.max(messageCountEnd, Math.floor(stats.maxTurnIndex));
4029
4181
  }
4030
4182
 
4031
- function truncateTrajectoryAnalysisLines(lines: string[], maxChars: number): string[] {
4032
- const result: string[] = [];
4183
+ function truncateTrajectoryAnalysisLines(
4184
+ lines: TrajectoryAnalysisLines,
4185
+ maxChars: number,
4186
+ ): SelectedTrajectoryLine[] {
4187
+ const result: SelectedTrajectoryLine[] = [];
4033
4188
  let used = 0;
4034
- for (const line of lines.slice(0, TRAJECTORY_ANALYSIS_MAX_LINES)) {
4189
+ const limit = Math.min(lines.length, TRAJECTORY_ANALYSIS_MAX_LINES);
4190
+ for (let index = 0; index < limit; index += 1) {
4191
+ const line = lines[index]!;
4035
4192
  const separator = result.length === 0 ? 0 : 1;
4036
4193
  const remaining = maxChars - used - separator;
4037
4194
  if (remaining <= 0) {
@@ -4045,7 +4202,7 @@ function truncateTrajectoryAnalysisLines(lines: string[], maxChars: number): str
4045
4202
  if (clipped.length === 0) {
4046
4203
  break;
4047
4204
  }
4048
- result.push(clipped);
4205
+ result.push({ text: clipped, ...lines.sourceAt(index) });
4049
4206
  used += separator + clipped.length;
4050
4207
  }
4051
4208
  return result;
package/src/index.ts CHANGED
@@ -58,6 +58,8 @@ export {
58
58
  buildEvidencePack,
59
59
  type EvidencePackItem,
60
60
  type EvidencePackOptions,
61
+ type EvidencePackSelectedItem,
62
+ type EvidencePackSelectionReceipt,
61
63
  } from "./evidence-pack.js";
62
64
  export {
63
65
  buildExplicitCueRecallSection,
@@ -74,6 +76,7 @@ export {
74
76
  type ExplicitCueRecallOptions,
75
77
  type ExplicitTurnReference,
76
78
  type TrajectoryAnalysisRecallOptions,
79
+ type TrajectoryAnalysisLineReceipt,
77
80
  } from "./explicit-cue-recall.js";
78
81
  export {
79
82
  buildTargetedFactRecallSection,