@animalabs/context-manager 0.4.0 → 0.5.1

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 (128) hide show
  1. package/dist/src/adaptive/chunker.d.ts +70 -0
  2. package/dist/src/adaptive/chunker.d.ts.map +1 -0
  3. package/dist/src/adaptive/chunker.js +233 -0
  4. package/dist/src/adaptive/chunker.js.map +1 -0
  5. package/dist/src/adaptive/folding-strategy.d.ts +118 -0
  6. package/dist/src/adaptive/folding-strategy.d.ts.map +1 -0
  7. package/dist/src/adaptive/folding-strategy.js +12 -0
  8. package/dist/src/adaptive/folding-strategy.js.map +1 -0
  9. package/dist/src/adaptive/index.d.ts +17 -0
  10. package/dist/src/adaptive/index.d.ts.map +1 -0
  11. package/dist/src/adaptive/index.js +23 -0
  12. package/dist/src/adaptive/index.js.map +1 -0
  13. package/dist/src/adaptive/picker.d.ts +103 -0
  14. package/dist/src/adaptive/picker.d.ts.map +1 -0
  15. package/dist/src/adaptive/picker.js +357 -0
  16. package/dist/src/adaptive/picker.js.map +1 -0
  17. package/dist/src/adaptive/render.d.ts +39 -0
  18. package/dist/src/adaptive/render.d.ts.map +1 -0
  19. package/dist/src/adaptive/render.js +104 -0
  20. package/dist/src/adaptive/render.js.map +1 -0
  21. package/dist/src/adaptive/strategies/flat-profile.d.ts +19 -0
  22. package/dist/src/adaptive/strategies/flat-profile.d.ts.map +1 -0
  23. package/dist/src/adaptive/strategies/flat-profile.js +86 -0
  24. package/dist/src/adaptive/strategies/flat-profile.js.map +1 -0
  25. package/dist/src/adaptive/strategies/oldest-first.d.ts +19 -0
  26. package/dist/src/adaptive/strategies/oldest-first.d.ts.map +1 -0
  27. package/dist/src/adaptive/strategies/oldest-first.js +39 -0
  28. package/dist/src/adaptive/strategies/oldest-first.js.map +1 -0
  29. package/dist/src/context-manager.d.ts +5 -0
  30. package/dist/src/context-manager.d.ts.map +1 -1
  31. package/dist/src/context-manager.js +22 -0
  32. package/dist/src/context-manager.js.map +1 -1
  33. package/dist/src/index.d.ts +1 -1
  34. package/dist/src/index.d.ts.map +1 -1
  35. package/dist/src/index.js.map +1 -1
  36. package/dist/src/message-store.d.ts +36 -1
  37. package/dist/src/message-store.d.ts.map +1 -1
  38. package/dist/src/message-store.js +134 -8
  39. package/dist/src/message-store.js.map +1 -1
  40. package/dist/src/strategies/autobiographical.d.ts +217 -2
  41. package/dist/src/strategies/autobiographical.d.ts.map +1 -1
  42. package/dist/src/strategies/autobiographical.js +1525 -37
  43. package/dist/src/strategies/autobiographical.js.map +1 -1
  44. package/dist/src/strategies/index.d.ts +1 -1
  45. package/dist/src/strategies/index.d.ts.map +1 -1
  46. package/dist/src/strategies/index.js.map +1 -1
  47. package/dist/src/types/message.d.ts +38 -0
  48. package/dist/src/types/message.d.ts.map +1 -1
  49. package/dist/src/types/strategy.d.ts +106 -10
  50. package/dist/src/types/strategy.d.ts.map +1 -1
  51. package/dist/src/types/strategy.js +7 -0
  52. package/dist/src/types/strategy.js.map +1 -1
  53. package/dist/test/adaptive/branching.test.d.ts +20 -0
  54. package/dist/test/adaptive/branching.test.d.ts.map +1 -0
  55. package/dist/test/adaptive/branching.test.js +176 -0
  56. package/dist/test/adaptive/branching.test.js.map +1 -0
  57. package/dist/test/adaptive/chunker.test.d.ts +2 -0
  58. package/dist/test/adaptive/chunker.test.d.ts.map +1 -0
  59. package/dist/test/adaptive/chunker.test.js +120 -0
  60. package/dist/test/adaptive/chunker.test.js.map +1 -0
  61. package/dist/test/adaptive/deep-levels.test.d.ts +10 -0
  62. package/dist/test/adaptive/deep-levels.test.d.ts.map +1 -0
  63. package/dist/test/adaptive/deep-levels.test.js +167 -0
  64. package/dist/test/adaptive/deep-levels.test.js.map +1 -0
  65. package/dist/test/adaptive/doc-plus-chat.test.d.ts +10 -0
  66. package/dist/test/adaptive/doc-plus-chat.test.d.ts.map +1 -0
  67. package/dist/test/adaptive/doc-plus-chat.test.js +194 -0
  68. package/dist/test/adaptive/doc-plus-chat.test.js.map +1 -0
  69. package/dist/test/adaptive/hard-fail.test.d.ts +9 -0
  70. package/dist/test/adaptive/hard-fail.test.d.ts.map +1 -0
  71. package/dist/test/adaptive/hard-fail.test.js +154 -0
  72. package/dist/test/adaptive/hard-fail.test.js.map +1 -0
  73. package/dist/test/adaptive/harness.d.ts +109 -0
  74. package/dist/test/adaptive/harness.d.ts.map +1 -0
  75. package/dist/test/adaptive/harness.js +263 -0
  76. package/dist/test/adaptive/harness.js.map +1 -0
  77. package/dist/test/adaptive/ingestion.test.d.ts +13 -0
  78. package/dist/test/adaptive/ingestion.test.d.ts.map +1 -0
  79. package/dist/test/adaptive/ingestion.test.js +306 -0
  80. package/dist/test/adaptive/ingestion.test.js.map +1 -0
  81. package/dist/test/adaptive/integration.test.d.ts +12 -0
  82. package/dist/test/adaptive/integration.test.d.ts.map +1 -0
  83. package/dist/test/adaptive/integration.test.js +247 -0
  84. package/dist/test/adaptive/integration.test.js.map +1 -0
  85. package/dist/test/adaptive/long-chronicle.test.d.ts +13 -0
  86. package/dist/test/adaptive/long-chronicle.test.d.ts.map +1 -0
  87. package/dist/test/adaptive/long-chronicle.test.js +186 -0
  88. package/dist/test/adaptive/long-chronicle.test.js.map +1 -0
  89. package/dist/test/adaptive/persistence.test.d.ts +11 -0
  90. package/dist/test/adaptive/persistence.test.d.ts.map +1 -0
  91. package/dist/test/adaptive/persistence.test.js +200 -0
  92. package/dist/test/adaptive/persistence.test.js.map +1 -0
  93. package/dist/test/adaptive/picker.test.d.ts +2 -0
  94. package/dist/test/adaptive/picker.test.d.ts.map +1 -0
  95. package/dist/test/adaptive/picker.test.js +333 -0
  96. package/dist/test/adaptive/picker.test.js.map +1 -0
  97. package/dist/test/adaptive/render.test.d.ts +2 -0
  98. package/dist/test/adaptive/render.test.d.ts.map +1 -0
  99. package/dist/test/adaptive/render.test.js +109 -0
  100. package/dist/test/adaptive/render.test.js.map +1 -0
  101. package/dist/test/adaptive/shard-immutability.test.d.ts +14 -0
  102. package/dist/test/adaptive/shard-immutability.test.d.ts.map +1 -0
  103. package/dist/test/adaptive/shard-immutability.test.js +184 -0
  104. package/dist/test/adaptive/shard-immutability.test.js.map +1 -0
  105. package/dist/test/adaptive/strategy-integration.test.d.ts +9 -0
  106. package/dist/test/adaptive/strategy-integration.test.d.ts.map +1 -0
  107. package/dist/test/adaptive/strategy-integration.test.js +293 -0
  108. package/dist/test/adaptive/strategy-integration.test.js.map +1 -0
  109. package/dist/test/message-store-sequence.test.d.ts +25 -0
  110. package/dist/test/message-store-sequence.test.d.ts.map +1 -0
  111. package/dist/test/message-store-sequence.test.js +157 -0
  112. package/dist/test/message-store-sequence.test.js.map +1 -0
  113. package/dist/tsconfig.tsbuildinfo +1 -1
  114. package/package.json +1 -1
  115. package/src/adaptive/chunker.ts +323 -0
  116. package/src/adaptive/folding-strategy.ts +129 -0
  117. package/src/adaptive/index.ts +51 -0
  118. package/src/adaptive/picker.ts +442 -0
  119. package/src/adaptive/render.ts +115 -0
  120. package/src/adaptive/strategies/flat-profile.ts +96 -0
  121. package/src/adaptive/strategies/oldest-first.ts +48 -0
  122. package/src/context-manager.ts +32 -0
  123. package/src/index.ts +1 -1
  124. package/src/message-store.ts +147 -8
  125. package/src/strategies/autobiographical.ts +1635 -38
  126. package/src/strategies/index.ts +1 -1
  127. package/src/types/message.ts +43 -0
  128. package/src/types/strategy.ts +122 -10
@@ -1,2 +1,2 @@
1
1
  export { PassthroughStrategy } from './passthrough.js';
2
- export { AutobiographicalStrategy } from './autobiographical.js';
2
+ export { AutobiographicalStrategy, type AutobiographicalProgressSnapshot } from './autobiographical.js';
@@ -47,6 +47,40 @@ export interface StoredMessage {
47
47
  timestamp: Date;
48
48
  /** IDs of messages that caused this one (from Chronicle causation) */
49
49
  causedBy?: MessageId[];
50
+
51
+ /**
52
+ * If this message is a shard of a larger logical message (chunked at
53
+ * ingestion because it exceeded `chunkThreshold`), this is the stable
54
+ * group id shared with sibling shards. Shards with the same id are
55
+ * concatenated into one API message at render time. Null/undefined
56
+ * for messages that fit in a single chunk. See
57
+ * `docs/adaptive-resolution-design.md` §3.6.
58
+ */
59
+ bodyGroupId?: string;
60
+
61
+ /**
62
+ * The shard's order within its bodyGroup, starting at 0. Only meaningful
63
+ * when `bodyGroupId` is set. Used to reassemble shards into byte-faithful
64
+ * order at render time.
65
+ */
66
+ shardIndex?: number;
67
+
68
+ /**
69
+ * Current display resolution for this chunk:
70
+ * - 0 = render raw content
71
+ * - k>0 = render the L_k summary that covers this chunk
72
+ *
73
+ * Set by the picker (or the agent in V2). Default 0. See
74
+ * `docs/adaptive-resolution-design.md` §3.3.
75
+ */
76
+ currentResolution?: number;
77
+
78
+ /**
79
+ * If true, the picker must not change `currentResolution` for this chunk.
80
+ * Set by `lockChunk()` (programmatic API) or, in V2, by the agent's
81
+ * `unfold` tool. Default false.
82
+ */
83
+ lockedByAgent?: boolean;
50
84
  }
51
85
 
52
86
  /**
@@ -85,6 +119,15 @@ export interface StoredMessageInternal {
85
119
  metadata?: MessageMetadata;
86
120
  timestamp: number; // Unix timestamp for storage
87
121
  causedBy?: MessageId[];
122
+
123
+ /** See StoredMessage.bodyGroupId */
124
+ bodyGroupId?: string;
125
+ /** See StoredMessage.shardIndex */
126
+ shardIndex?: number;
127
+ /** See StoredMessage.currentResolution */
128
+ currentResolution?: number;
129
+ /** See StoredMessage.lockedByAgent */
130
+ lockedByAgent?: boolean;
88
131
  }
89
132
 
90
133
  /**
@@ -1,5 +1,5 @@
1
1
  import type { JsStore } from '@animalabs/chronicle';
2
- import type { Membrane } from '@animalabs/membrane';
2
+ import type { Membrane, ContentBlock } from '@animalabs/membrane';
3
3
  import type { StoredMessage, MessageId, Sequence } from './message.js';
4
4
  import type { ContextEntry, TokenBudget, PendingWork } from './context.js';
5
5
 
@@ -125,6 +125,41 @@ export interface ContextStrategy {
125
125
  log: ContextLogView,
126
126
  budget: TokenBudget
127
127
  ): ContextEntry[];
128
+
129
+ /**
130
+ * Optional: at ingestion time, decide whether to split this incoming
131
+ * message into multiple shards (because it's too large to be a single
132
+ * fold unit). If returned, the framework stores each shard as a separate
133
+ * StoredMessage with the shared `bodyGroupId` and per-shard `shardIndex`,
134
+ * and the render path will reassemble them into one API message at
135
+ * compile time.
136
+ *
137
+ * Return null or undefined to skip chunking (message stored as one record).
138
+ *
139
+ * See `docs/adaptive-resolution-design.md` §3.6.
140
+ */
141
+ chunkIngressMessage?(
142
+ participant: string,
143
+ content: ContentBlock[]
144
+ ): IngressChunkResult | null;
145
+ }
146
+
147
+ /**
148
+ * Result of a strategy's ingestion-time chunking decision.
149
+ */
150
+ export interface IngressChunkResult {
151
+ /** Stable id shared by all shards of this message. */
152
+ bodyGroupId: string;
153
+ /**
154
+ * The shards in source order. Each becomes a separate StoredMessage.
155
+ * Concatenating the shards' text content must reproduce the original
156
+ * message body byte-for-byte.
157
+ */
158
+ shards: Array<{
159
+ content: ContentBlock[];
160
+ /** Order within the bodyGroup, starting at 0. */
161
+ shardIndex: number;
162
+ }>;
128
163
  }
129
164
 
130
165
  /**
@@ -302,6 +337,19 @@ export interface AutobiographicalConfig {
302
337
  /** Token budget for L1 summaries in select() (default: 30000) */
303
338
  l1BudgetTokens?: number;
304
339
 
340
+ /**
341
+ * Cap on the total tokens of recall-pair prior-summary content
342
+ * included in each chunk compression request (default: 150000).
343
+ *
344
+ * Defends against the case where the unmerged frontier itself is
345
+ * large enough to overflow the API window. The cap walks summaries
346
+ * newest-first so proximate context survives; the kept set is then
347
+ * re-sorted chronologically. Set higher (or to Infinity) if you want
348
+ * to surface overflow via a 400 rather than silently drop oldest
349
+ * memories from the compression context.
350
+ */
351
+ compressionRecallBudgetTokens?: number;
352
+
305
353
  /**
306
354
  * When true (default), each selected summary emits as its own positioned
307
355
  * Q/A recall pair, sorted chronologically by source range. When false,
@@ -379,35 +427,91 @@ export interface AutobiographicalConfig {
379
427
  * that overflow is rare, and when it does happen you want to know.
380
428
  */
381
429
  enforceBudget?: boolean;
430
+
431
+ // --- Adaptive resolution (per docs/adaptive-resolution-design.md) ---
432
+
433
+ /**
434
+ * Enable picker-driven adaptive resolution. When true, `select()` uses
435
+ * the FoldingStrategy + Picker to choose per-message resolution under
436
+ * token-budget pressure rather than the threshold-driven `checkMergeThreshold`
437
+ * path. Default: false (existing hierarchical behavior preserved).
438
+ */
439
+ adaptiveResolution?: boolean;
440
+
441
+ /**
442
+ * Folding strategy name when adaptiveResolution is on. One of:
443
+ * 'flat-profile' (default) — level-equalizing
444
+ * 'oldest-first' — chronological
445
+ * Custom strategies can be plugged in by the host application.
446
+ */
447
+ foldingStrategy?: 'flat-profile' | 'oldest-first';
448
+
449
+ /**
450
+ * Slack ratio (hysteresis) for the picker. The picker folds until total
451
+ * tokens ≤ budget * (1 - slack), and stays quiet while between slack
452
+ * and budget. Default 0.1.
453
+ */
454
+ compressionSlackRatio?: number;
455
+
456
+ /**
457
+ * Enable bottom-up speculative pre-production of higher-level summaries.
458
+ * When a new L_k summary is produced, if N siblings exist that would share
459
+ * an L_{k+1} parent, the L_{k+1} is enqueued for production immediately
460
+ * (no picker request needed). Default true when adaptiveResolution is on.
461
+ */
462
+ speculativeProduction?: boolean;
382
463
  }
383
464
 
384
465
  /**
385
466
  * Compression level in the hierarchical pyramid.
467
+ *
468
+ * Historically constrained to 1 | 2 | 3. As of the adaptive-resolution design
469
+ * (`docs/adaptive-resolution-design.md`), levels are unbounded: the picker
470
+ * can recursively produce L4, L5, ... as needed. The narrower literal type
471
+ * is kept as `LegacySummaryLevel` for code that still assumes the old shape.
472
+ */
473
+ export type SummaryLevel = number;
474
+
475
+ /**
476
+ * The narrow level type used by pre-adaptive-resolution code paths.
477
+ * Prefer `SummaryLevel` for new code.
386
478
  */
387
- export type SummaryLevel = 1 | 2 | 3;
479
+ export type LegacySummaryLevel = 1 | 2 | 3;
388
480
 
389
481
  /**
390
482
  * A summary entry in the hierarchical memory pyramid.
391
483
  * L1: compressed from raw message chunks.
392
- * L2: merged from mergeThreshold L1s.
393
- * L3: merged from mergeThreshold L2s.
484
+ * L_{k>1}: merged from mergeThreshold L_{k-1}s.
394
485
  */
395
486
  export interface SummaryEntry {
396
487
  /** Unique ID (e.g., "L1-0", "L2-3") */
397
488
  id: string;
398
- /** Compression level */
489
+ /** Compression level (1, 2, 3, ... — unbounded in the adaptive-resolution design) */
399
490
  level: SummaryLevel;
400
491
  /** The summary text */
401
492
  content: string;
402
- /** Estimated token count (content.length / 4) */
493
+ /** Estimated token count (content.length / 4 or tokenizer-cached) */
403
494
  tokens: number;
404
- /** Level of the sources: 0 = raw messages, 1 = L1s, 2 = L2s */
405
- sourceLevel: 0 | 1 | 2;
406
- /** IDs of source items (message IDs for L1, summary IDs for L2/L3) */
495
+ /**
496
+ * Level of the sources: 0 = raw messages, k = L_k summaries.
497
+ * Pre-adaptive code uses 0 | 1 | 2; new code may produce higher values.
498
+ */
499
+ sourceLevel: number;
500
+ /** IDs of source items (message IDs for L1, summary IDs for L_{k>1}) */
407
501
  sourceIds: string[];
408
502
  /** Range of original message IDs covered */
409
503
  sourceRange: { first: string; last: string };
410
- /** If merged into a higher-level summary, that summary's ID */
504
+ /**
505
+ * The L_{level+1} summary this one is a source for, if produced.
506
+ * Pure archive metadata in the adaptive-resolution design — display
507
+ * decisions live on per-chunk `currentResolution`, not here.
508
+ */
509
+ parentId?: string;
510
+ /**
511
+ * @deprecated Renamed to `parentId` in the adaptive-resolution design.
512
+ * Kept for read compatibility with chronicles produced by the old
513
+ * threshold-driven path. New writes should set `parentId` only.
514
+ */
411
515
  mergedInto?: string;
412
516
  /** Creation timestamp */
413
517
  created: number;
@@ -415,6 +519,14 @@ export interface SummaryEntry {
415
519
  phaseType?: string;
416
520
  }
417
521
 
522
+ /**
523
+ * Helper: read the parent pointer from a summary, accepting either the
524
+ * new `parentId` field or the deprecated `mergedInto` alias.
525
+ */
526
+ export function getSummaryParentId(s: SummaryEntry): string | undefined {
527
+ return s.parentId ?? s.mergedInto;
528
+ }
529
+
418
530
  /**
419
531
  * A range of messages protected from compression. Pins keep a span of raw
420
532
  * messages visible at their original position in the rendered context.