@genesislcap/ai-assistant 15.5.0 → 15.6.0

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.
@@ -7505,6 +7505,17 @@
7505
7505
  "description": "Hard cap on tool-loop iterations.",
7506
7506
  "default": "maxToolIterations"
7507
7507
  },
7508
+ {
7509
+ "kind": "field",
7510
+ "name": "condenseBatchCalls",
7511
+ "type": {
7512
+ "text": "number"
7513
+ },
7514
+ "privacy": "private",
7515
+ "readonly": true,
7516
+ "description": "Model-calls per condensation batch; `1` collapses as soon as a trigger fires.",
7517
+ "default": "condenseBatchCalls"
7518
+ },
7508
7519
  {
7509
7520
  "kind": "field",
7510
7521
  "name": "sessionKey",
@@ -96,6 +96,16 @@ export interface ChatDriverConfig {
96
96
  maxToolIterations?: number;
97
97
  /** Hard cap on fold operations. Default `5`. */
98
98
  maxFoldOperations?: number;
99
+ /**
100
+ * Collapse `condenseWhen` payloads in batches of this many model-calls rather than as
101
+ * soon as each trigger fires. A positive integer; decimals are floored and anything
102
+ * below `1` becomes `1`. Default `1` — collapse immediately, the historical behaviour.
103
+ *
104
+ * Condensation rewrites history in place, so collapsing continuously breaks the
105
+ * provider's prompt cache on nearly every call of a re-read loop. Raising this trades
106
+ * up to that many calls' worth of extra context for one cache break per batch.
107
+ */
108
+ condenseBatchCalls?: number;
99
109
  /** Ring-buffer size for per-turn snapshots. Default `400`. */
100
110
  maxTurnSnapshots?: number;
101
111
  /** Session identity used to file meta events onto the shared debug-log timeline. */
@@ -419,6 +429,8 @@ export declare class ChatDriver extends EventTarget implements AiDriver {
419
429
  private unsubscribeRegistry?;
420
430
  /** Hard cap on tool-loop iterations. */
421
431
  private readonly maxToolIterations;
432
+ /** Model-calls per condensation batch; `1` collapses as soon as a trigger fires. */
433
+ private readonly condenseBatchCalls;
422
434
  /** Session identity used to file meta events onto the shared debug-log timeline. */
423
435
  private readonly sessionKey;
424
436
  /** Injected activity bus; defaults to a no-op off-browser (Node/tests/headless). */
@@ -1 +1 @@
1
- {"version":3,"file":"chat-driver.d.ts","sourceRoot":"","sources":["../../../../src/components/chat-driver/chat-driver.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAEV,kBAAkB,EAGlB,cAAc,EACd,gBAAgB,EAEhB,WAAW,EAGX,cAAc,EAKd,yBAAyB,EAEzB,qBAAqB,EAGtB,MAAM,4BAA4B,CAAC;AAMpC,OAAO,EAAE,KAAK,WAAW,EAAqB,MAAM,+BAA+B,CAAC;AACpF,OAAO,KAAK,EACV,WAAW,EAKX,iBAAiB,EAIjB,oBAAoB,EACpB,iBAAiB,EAElB,MAAM,qBAAqB,CAAC;AAgB7B,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,qCAAqC,CAAC;AAC3E,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,iCAAiC,CAAC;AAW1E,OAAO,KAAK,EAAE,QAAQ,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AA8CxE,wFAAwF;AACxF,eAAO,MAAM,yBAAyB,yBAAyB,CAAC;AAMhE;;;;GAIG;AACH,MAAM,MAAM,uBAAuB,GAAG,WAAW,CAAC,aAAa,CAAC,WAAW,CAAC,CAAC,CAAC;AAE9E;;;;;;;;;GASG;AACH,MAAM,WAAW,YAAY;IAC3B;;;;;;;OAOG;IACH,SAAS,EAAE,MAAM,CAAC;IAClB,uDAAuD;IACvD,SAAS,EAAE,MAAM,CAAC;IAClB,uDAAuD;IACvD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;;;;OAMG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;;;;;OAMG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,qGAAqG;IACrG,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB;;;;OAIG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;OAIG;IACH,UAAU,CAAC,EAAE,cAAc,CAAC;IAC5B;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,gGAAgG;IAChG,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB;AAQD;;;;;;;GAOG;AACH,MAAM,WAAW,gBAAgB;IAC/B,4EAA4E;IAC5E,YAAY,CAAC,EAAE,iBAAiB,CAAC;IACjC,iFAAiF;IACjF,eAAe,CAAC,EAAE,oBAAoB,CAAC;IACvC,2DAA2D;IAC3D,YAAY,CAAC,EAAE,iBAAiB,CAAC;IACjC,oDAAoD;IACpD,aAAa,CAAC,EAAE,WAAW,EAAE,CAAC;IAC9B,sDAAsD;IACtD,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,gDAAgD;IAChD,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,8DAA8D;IAC9D,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,oFAAoF;IACpF,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;;OAIG;IACH,WAAW,CAAC,EAAE,WAAW,CAAC;CAC3B;AAED;;;;;;;;;GASG;AACH,qBAAa,UAAW,SAAQ,WAAY,YAAW,QAAQ;IA4U3D,OAAO,CAAC,QAAQ,CAAC,gBAAgB;IA3UnC,OAAO,CAAC,OAAO,CAAqB;IACpC,OAAO,CAAC,IAAI,CAAS;IACrB,kFAAkF;IAClF,OAAO,CAAC,aAAa,CAAK;IAC1B,OAAO,CAAC,mBAAmB,CAUvB;IACJ;;;;;;;;OAQG;IACH,OAAO,CAAC,mBAAmB,CAA+C;IAE1E,OAAO,CAAC,YAAY,CAAC,CAAoB;IACzC;;;;OAIG;IACH,OAAO,CAAC,eAAe,CAAuB;IAC9C;;;;;OAKG;IACH,OAAO,CAAC,sBAAsB,CAAC,CAE2B;IAC1D;;;;;;;;OAQG;IACH,OAAO,CAAC,YAAY,CAAmB;IACvC;;;;OAIG;IACH,OAAO,CAAC,mBAAmB,CAAC,CAEsB;IAClD,OAAO,CAAC,aAAa,CAAC,CAAgB;IACtC,OAAO,CAAC,eAAe,CAAC,CAAS;IACjC;;;;OAIG;IACH,OAAO,CAAC,gBAAgB,CAAC,CAAS;IAClC,OAAO,CAAC,WAAW,CAAC,CAAoB;IACxC;;;;;OAKG;IACH,OAAO,CAAC,wBAAwB,CAAC,CAIjB;IAChB;;;OAGG;IACH,OAAO,CAAC,wBAAwB,CAAC,CAA4C;IAC7E;;;;;;OAMG;IACH,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAA+C;IAChF;;;;;;;OAOG;IACH,OAAO,CAAC,YAAY,CAAK;IACzB;;;;;OAKG;IACH,OAAO,CAAC,OAAO,CAAK;IACpB;;;;;;;;OAQG;IACH,OAAO,CAAC,iBAAiB,CAAK;IAC9B;;;;;;;OAOG;IACH,OAAO,CAAC,iBAAiB,CAAK;IAE9B,8EAA8E;IAC9E,OAAO,CAAC,SAAS,CAAwB;IACzC,8FAA8F;IAC9F,OAAO,CAAC,kBAAkB,CAAK;IAC/B,6FAA6F;IAC7F,OAAO,CAAC,2BAA2B,CAAK;IACxC;;;;OAIG;IACH,OAAO,CAAC,QAAQ,CAAC,sBAAsB,CAAqB;IAC5D;;;;;OAKG;IACH,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAqB;IACvD;;;;;;OAMG;IACH,OAAO,CAAC,QAAQ,CAAC,oBAAoB,CAAqB;IAC1D,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAS;IAE3C,qEAAqE;IACrE,OAAO,CAAC,YAAY,CAAuC;IAC3D;;;;;OAKG;IACH,OAAO,CAAC,kBAAkB,CAAkC;IAC5D;;;;;;OAMG;IACH,OAAO,CAAC,UAAU,CAAS;IAC3B;;;;OAIG;IACH,OAAO,CAAC,eAAe,CAAgD;IACvE;;;;;;;;OAQG;IACH,OAAO,CAAC,qBAAqB,CAAS;IACtC;;;;OAIG;IACH,OAAO,CAAC,aAAa,CAAsB;IAC3C;;;;;;;;;OASG;IACH,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAAyB;IAC7D;;;;OAIG;IACH,OAAO,CAAC,0BAA0B,CAAK;IACvC,+FAA+F;IAC/F,OAAO,CAAC,eAAe,CAAK;IAC5B,4EAA4E;IAC5E,OAAO,CAAC,gBAAgB,CAAC,CAAgB;IACzC,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAS;IAE1C;;;;;;OAMG;IACH,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAAyB;IAE7D;;;;;;OAMG;IACH,OAAO,CAAC,cAAc,CAAyB;IAC/C,0GAA0G;IAC1G,OAAO,CAAC,aAAa,CAAS;IAC9B,8EAA8E;IAC9E,OAAO,CAAC,uBAAuB,CAAC,CAAa;IAE7C;;;OAGG;IACH,OAAO,CAAC,mBAAmB,CAAC,CAAgB;IAC5C;;;OAGG;IACH,OAAO,CAAC,sBAAsB,CAAC,CAAmB;IAClD;;;;OAIG;IACH,OAAO,CAAC,qBAAqB,CAAC,CAAkB;IAChD;;;OAGG;IACH,OAAO,CAAC,sBAAsB,CAAC,CAAmB;IAClD;;;OAGG;IACH,OAAO,CAAC,sBAAsB,CAAC,CAAmB;IAClD;;;OAGG;IACH,OAAO,CAAC,yBAAyB,CAAC,CAAsB;IACxD;;;OAGG;IACH,OAAO,CAAC,eAAe,CAAC,CAAiB;IACzC;;;;;OAKG;IACH,OAAO,CAAC,sBAAsB,CAAC,CAAsB;IACrD;;;;OAIG;IACH,OAAO,CAAC,qBAAqB,CAAiC;IAC9D,iFAAiF;IACjF,OAAO,CAAC,wBAAwB,CAAC,CAAS;IAC1C,wFAAwF;IACxF,OAAO,CAAC,0BAA0B,CAAC,CAAS;IAC5C;;;;;OAKG;IACH,OAAO,CAAC,iBAAiB,CAAC,CAAS;IACnC;;;;OAIG;IACH,OAAO,CAAC,oBAAoB,CAAC,CAAiB;IAC9C;;;;;OAKG;IACH,OAAO,CAAC,mBAAmB,CAAoE;IAC/F;;;;;OAKG;IACH,OAAO,CAAC,mBAAmB,CAAC,CAAa;IAEzC,wCAAwC;IACxC,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAS;IAC3C,oFAAoF;IACpF,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,oFAAoF;IACpF,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAc;gBAGvB,gBAAgB,EAAE,kBAAkB,EACrD,MAAM,GAAE,gBAAqB;IAwD/B;;;;;;;;;;;;;;OAcG;IACH,OAAO,IAAI,IAAI;IAef;;;;;OAKG;IACH,MAAM,IAAI,IAAI;IAMd;;;OAGG;IACH,OAAO,CAAC,SAAS;IAcjB,oGAAoG;IACpG,OAAO,CAAC,OAAO;IAKf;;;;OAIG;IACH,OAAO,CAAC,mBAAmB;IAU3B;;;;;;;OAOG;IACH,OAAO,CAAC,QAAQ;IAIhB,4FAA4F;IAC5F,OAAO,CAAC,MAAM,CAAC,eAAe;IAI9B;;;;OAIG;IACH,OAAO,CAAC,MAAM,CAAC,aAAa;IAO5B;;;OAGG;IACH,UAAU,CAAC,MAAM,EAAE,WAAW,GAAG,IAAI;IA4DrC;;;;;;;OAOG;YACW,4BAA4B;IAwB1C;;;OAGG;IACH,qBAAqB,IAAI,MAAM;IAI/B;;;;;OAKG;IACH,OAAO,CAAC,qBAAqB;IAQ7B;;;;;OAKG;YACW,sBAAsB;IA+BpC;;;;;OAKG;YACW,wBAAwB;IAkBtC;;;;OAIG;YACW,gBAAgB;IAU9B;;;OAGG;IACH,qBAAqB,IAAI;QAAE,MAAM,EAAE,OAAO,CAAA;KAAE,GAAG,SAAS;IAIxD;;;;OAIG;IACH,cAAc,IAAI,IAAI;IAItB;;;;OAIG;IACH,kBAAkB,IAAI;QAAE,MAAM,EAAE,qBAAqB,CAAA;KAAE,GAAG,SAAS;IAInE;;;;;;;OAOG;IACH,OAAO,CAAC,YAAY;IAKpB;;;OAGG;IACH,wBAAwB,IAAI,OAAO;IAInC;;;;;;OAMG;IACH,gBAAgB,IAAI,aAAa,CAAC,YAAY,CAAC;IAI/C;;;;;;;OAOG;IACH,sBAAsB,IAAI,aAAa,CAAC,eAAe,CAAC;IAIxD;;;;;;;;;;;OAWG;IACH,OAAO,CAAC,wBAAwB;IA2BhC;;;;OAIG;IACH,OAAO,CAAC,kBAAkB;IAoC1B;;;OAGG;IACH,2BAA2B,CAAC,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,WAAW,EAAE,KAAK,WAAW,EAAE,GAAG,IAAI;IAIxF,UAAU,IAAI,aAAa,CAAC,WAAW,CAAC;IAIxC,aAAa,IAAI,SAAS,WAAW,EAAE;IAIvC,0DAA0D;IAC1D,kBAAkB,IAAI,MAAM,EAAE;IAIxB,cAAc,CAClB,OAAO,EAAE,WAAW,EAAE,EACtB,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,MAAM,EACb,YAAY,CAAC,EAAE,eAAe,EAAE,GAC/B,OAAO,CAAC,MAAM,EAAE,CAAC;IAiHpB;;;;OAIG;IACH,UAAU,IAAI,OAAO;IAIrB;;;;;;;;OAQG;IACG,OAAO,IAAI,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC;IAwE5C,MAAM,IAAI,OAAO;IAIjB;;;;;OAKG;IACI,2BAA2B,CAChC,EAAE,EAAE,CAAC,CAAC,EAAE,aAAa,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,EAAE,OAAO,CAAC,EAAE,yBAAyB,KAAK,OAAO,CAAC,CAAC,CAAC,GAC3F,IAAI;IAIP;;;;;;;;;;;;;;;;;;OAkBG;IACU,kBAAkB,CAAC,CAAC,EAC/B,aAAa,EAAE,MAAM,EACrB,IAAI,EAAE,GAAG,EACT,OAAO,CAAC,EAAE,yBAAyB,GAClC,OAAO,CAAC,CAAC,CAAC;IAmEb;;;OAGG;IACI,kBAAkB,CAAC,aAAa,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,GAAG,IAAI;IAyInE;;;;OAIG;IACI,qBAAqB,CAAC,aAAa,EAAE,MAAM,GAAG,kBAAkB,GAAG,SAAS;IAInF;;;;;;;OAOG;IACH,OAAO,CAAC,yBAAyB;IASjC;;;OAGG;IACI,WAAW,CAAC,QAAQ,EAAE,WAAW,EAAE,GAAG,IAAI;IAS3C,WAAW,CAAC,SAAS,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,cAAc,EAAE,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAmD/F;;;;;;;;;;;;OAYG;IACH,OAAO,CAAC,mBAAmB;IA8F3B;;;;;OAKG;YACW,cAAc;IA+M5B;;;OAGG;IACG,mBAAmB,CAAC,eAAe,CAAC,EAAE,WAAW,EAAE,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAiDrF,wFAAwF;IACxF,OAAO,CAAC,OAAO;IAKf;;;OAGG;IACH,OAAO,CAAC,kBAAkB;IAc1B;;;;;;;;;OASG;IACH,OAAO,CAAC,cAAc;IAMtB;;;OAGG;IACH,OAAO,CAAC,gBAAgB;IA+BxB,uFAAuF;IACvF,OAAO,CAAC,QAAQ;IAsChB,OAAO,CAAC,aAAa;IAQrB,8EAA8E;IAC9E,OAAO,CAAC,SAAS;IAWjB,mFAAmF;IACnF,OAAO,CAAC,2BAA2B;YAkCrB,WAAW;IAm0BzB,OAAO,CAAC,eAAe;CAoBxB"}
1
+ {"version":3,"file":"chat-driver.d.ts","sourceRoot":"","sources":["../../../../src/components/chat-driver/chat-driver.ts"],"names":[],"mappings":"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"}
@@ -64,6 +64,20 @@ export interface CondenseContext {
64
64
  * `phaseEnd` clock. True once `endPhase()` has been called since the call.
65
65
  */
66
66
  phaseEnded: (phaseEpoch: number) => boolean;
67
+ /**
68
+ * Collapse in batches of this many model-calls instead of continuously. A positive
69
+ * integer; decimals are floored and anything below `1` becomes `1`. Omit (or `1`) to
70
+ * collapse as soon as a trigger fires, which is the historical behaviour.
71
+ *
72
+ * Condensation rewrites a payload *in place*, so the model-bound history is not
73
+ * append-only and every prompt-cache entry written before the rewrite is invalidated
74
+ * from that position on. Collapsing the moment each trigger fires therefore breaks the
75
+ * cache on nearly every call of a re-read loop — the case condensation exists to
76
+ * contain. Batching holds the collapse set still between boundaries so the prefix is
77
+ * append-only in between, trading up to `batchCalls` calls' worth of extra context for
78
+ * one cache break per batch instead of one per call.
79
+ */
80
+ batchCalls?: number;
67
81
  }
68
82
  /**
69
83
  * Structured detail for a `context.condensed` meta-event. The index signature
@@ -1 +1 @@
1
- {"version":3,"file":"condense-history.d.ts","sourceRoot":"","sources":["../../../src/utils/condense-history.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,cAAc,EAAmB,MAAM,4BAA4B,CAAC;AAE/F;;;;;;;;;;;;;GAaG;AAEH;;;;GAIG;AACH,eAAO,MAAM,kBAAkB,OAAO,CAAC;AAEvC,uFAAuF;AACvF,eAAO,MAAM,kBAAkB,cAAc,CAAC;AAQ9C;;;;;GAKG;AACH,MAAM,WAAW,wBAAwB;IACvC,MAAM,EAAE,cAAc,CAAC;IACvB;;;;;OAKG;IACH,SAAS,EAAE,MAAM,CAAC;IAClB,kFAAkF;IAClF,IAAI,EAAE,MAAM,CAAC;IACb,+EAA+E;IAC/E,UAAU,EAAE,MAAM,CAAC;IACnB,gFAAgF;IAChF,UAAU,EAAE,MAAM,CAAC;IACnB,4EAA4E;IAC5E,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,gFAAgF;IAChF,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC5B;AAED;;;;GAIG;AACH,MAAM,WAAW,eAAe;IAC9B,2DAA2D;IAC3D,SAAS,EAAE,MAAM,CAAC;IAClB,yEAAyE;IACzE,IAAI,EAAE,MAAM,CAAC;IACb,4FAA4F;IAC5F,eAAe,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,OAAO,CAAC;IACjD;;;OAGG;IACH,UAAU,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,OAAO,CAAC;CAC7C;AAED;;;;GAIG;AACH,MAAM,WAAW,oBAAoB;IACnC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;IACvB,0DAA0D;IAC1D,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,GAAG,UAAU,CAAC;IAC5B,oEAAoE;IACpE,OAAO,EAAE,MAAM,CAAC;IAChB,sCAAsC;IACtC,OAAO,EAAE,MAAM,CAAC;IAChB,mFAAmF;IACnF,WAAW,EAAE,MAAM,CAAC;CACrB;AA8CD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AACH,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,WAAW,EAAE,EACtB,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,wBAAwB,CAAC,EAC/C,GAAG,EAAE,eAAe,EACpB,WAAW,EAAE,CAAC,MAAM,EAAE,oBAAoB,KAAK,IAAI,GAClD,WAAW,EAAE,CAyIf"}
1
+ {"version":3,"file":"condense-history.d.ts","sourceRoot":"","sources":["../../../src/utils/condense-history.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,cAAc,EAAmB,MAAM,4BAA4B,CAAC;AAE/F;;;;;;;;;;;;;GAaG;AAEH;;;;GAIG;AACH,eAAO,MAAM,kBAAkB,OAAO,CAAC;AAEvC,uFAAuF;AACvF,eAAO,MAAM,kBAAkB,cAAc,CAAC;AAQ9C;;;;;GAKG;AACH,MAAM,WAAW,wBAAwB;IACvC,MAAM,EAAE,cAAc,CAAC;IACvB;;;;;OAKG;IACH,SAAS,EAAE,MAAM,CAAC;IAClB,kFAAkF;IAClF,IAAI,EAAE,MAAM,CAAC;IACb,+EAA+E;IAC/E,UAAU,EAAE,MAAM,CAAC;IACnB,gFAAgF;IAChF,UAAU,EAAE,MAAM,CAAC;IACnB,4EAA4E;IAC5E,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,gFAAgF;IAChF,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC5B;AAED;;;;GAIG;AACH,MAAM,WAAW,eAAe;IAC9B,2DAA2D;IAC3D,SAAS,EAAE,MAAM,CAAC;IAClB,yEAAyE;IACzE,IAAI,EAAE,MAAM,CAAC;IACb,4FAA4F;IAC5F,eAAe,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,OAAO,CAAC;IACjD;;;OAGG;IACH,UAAU,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,OAAO,CAAC;IAC5C;;;;;;;;;;;;OAYG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;;;GAIG;AACH,MAAM,WAAW,oBAAoB;IACnC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;IACvB,0DAA0D;IAC1D,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,GAAG,UAAU,CAAC;IAC5B,oEAAoE;IACpE,OAAO,EAAE,MAAM,CAAC;IAChB,sCAAsC;IACtC,OAAO,EAAE,MAAM,CAAC;IAChB,mFAAmF;IACnF,WAAW,EAAE,MAAM,CAAC;CACrB;AA8CD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AACH,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,WAAW,EAAE,EACtB,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,wBAAwB,CAAC,EAC/C,GAAG,EAAE,eAAe,EACpB,WAAW,EAAE,CAAC,MAAM,EAAE,oBAAoB,KAAK,IAAI,GAClD,WAAW,EAAE,CAyJf"}
@@ -230,8 +230,9 @@ export class ChatDriver extends EventTarget {
230
230
  * picked up on the next turn.
231
231
  */
232
232
  this.resolvedStatusCache = new Map();
233
- const { toolHandlers = {}, toolDefinitions = [], systemPrompt, primerHistory, maxToolIterations = DEFAULT_MAX_TOOL_ITERATIONS, maxFoldOperations = DEFAULT_MAX_FOLD_OPERATIONS, maxTurnSnapshots = DEFAULT_MAX_TURN_SNAPSHOTS, sessionKey = '', activityBus = NOOP_ACTIVITY_BUS, } = config;
233
+ const { toolHandlers = {}, toolDefinitions = [], systemPrompt, primerHistory, maxToolIterations = DEFAULT_MAX_TOOL_ITERATIONS, maxFoldOperations = DEFAULT_MAX_FOLD_OPERATIONS, condenseBatchCalls = 1, maxTurnSnapshots = DEFAULT_MAX_TURN_SNAPSHOTS, sessionKey = '', activityBus = NOOP_ACTIVITY_BUS, } = config;
234
234
  this.maxToolIterations = maxToolIterations;
235
+ this.condenseBatchCalls = condenseBatchCalls;
235
236
  this.sessionKey = sessionKey;
236
237
  this.activityBus = activityBus;
237
238
  if (typeof toolHandlers === 'function') {
@@ -1824,6 +1825,7 @@ export class ChatDriver extends EventTarget {
1824
1825
  // A call's phase has ended once the app declared a later phase epoch
1825
1826
  // current via `endPhase()`.
1826
1827
  phaseEnded: (phaseEpoch) => phaseEpoch < this.currentPhaseEpoch,
1828
+ batchCalls: this.condenseBatchCalls,
1827
1829
  }, (detail) => recordMetaEvent(this.sessionKey, 'context.condensed', detail));
1828
1830
  const normalizedHistory = normalizeForProvider(condensedHistory);
1829
1831
  const historyForProvider = this.providerHistoryTransform
@@ -98,7 +98,7 @@ function estimateTokensSaved(origLen, stubLen) {
98
98
  * @param onCondensed - Invoked once per payload at its full→stub transition.
99
99
  */
100
100
  export function applyCondensation(history, policies, ctx, onCondensed) {
101
- var _a;
101
+ var _a, _b;
102
102
  if (policies.size === 0)
103
103
  return history;
104
104
  const triggersOf = (entry) => Array.isArray(entry.policy.on) ? entry.policy.on : [entry.policy.on];
@@ -106,15 +106,26 @@ export function applyCondensation(history, policies, ctx, onCondensed) {
106
106
  // lookup. For each `superseded` key the LAST call in history order survives.
107
107
  // The name lookup lets a tool message (which carries only an id) name its tool
108
108
  // in stubs and events.
109
+ // Batch boundary: the collapse set is resolved as of this model-call and stays put
110
+ // until the next boundary, so the prefix is append-only in between. `batchCalls` of 1
111
+ // (the default) makes this `ctx.modelCall` — i.e. no batching.
112
+ const batchCalls = Math.max(1, Math.floor((_a = ctx.batchCalls) !== null && _a !== void 0 ? _a : 1));
113
+ const asOf = batchCalls === 1 ? ctx.modelCall : Math.floor(ctx.modelCall / batchCalls) * batchCalls;
114
+ // A call made after the boundary is invisible to this pass in BOTH directions: it
115
+ // cannot supersede an older call (which would collapse the older payload early), and
116
+ // it cannot itself collapse (which would elide the very result just fetched). Unbatched,
117
+ // the boundary is the live clock and this gate is off entirely — a call registered on
118
+ // the current model-call is still eligible, as it always was.
119
+ const withinBatch = (entry) => batchCalls === 1 || entry.iteration <= asOf;
109
120
  const latestByKey = new Map();
110
121
  const nameById = new Map();
111
122
  for (const msg of history) {
112
- if (!((_a = msg.toolCalls) === null || _a === void 0 ? void 0 : _a.length))
123
+ if (!((_b = msg.toolCalls) === null || _b === void 0 ? void 0 : _b.length))
113
124
  continue;
114
125
  for (const tc of msg.toolCalls) {
115
126
  nameById.set(tc.id, tc.name);
116
127
  const entry = policies.get(tc.id);
117
- if (!entry)
128
+ if (!entry || !withinBatch(entry))
118
129
  continue;
119
130
  for (const t of triggersOf(entry)) {
120
131
  if (t.kind === 'superseded')
@@ -128,7 +139,7 @@ export function applyCondensation(history, policies, ctx, onCondensed) {
128
139
  // past the last allowed view: turns:1 → seen once, then gone. Monotonic
129
140
  // clock, so this holds across turn boundaries too.
130
141
  case 'age':
131
- return ctx.modelCall - entry.iteration > trig.turns;
142
+ return asOf - entry.iteration > trig.turns;
132
143
  case 'turnEnd':
133
144
  return ctx.turn > entry.turn;
134
145
  case 'agentEnd':
@@ -144,7 +155,9 @@ export function applyCondensation(history, policies, ctx, onCondensed) {
144
155
  };
145
156
  // The first listed trigger that fires — drives the collapse, the stub reason,
146
157
  // and the event label. `undefined` means the payload stays full.
147
- const firstFired = (toolCallId, entry) => triggersOf(entry).find((t) => triggerFires(toolCallId, entry, t));
158
+ const firstFired = (toolCallId, entry) => withinBatch(entry)
159
+ ? triggersOf(entry).find((t) => triggerFires(toolCallId, entry, t))
160
+ : undefined;
148
161
  return history.map((msg) => {
149
162
  var _a, _b, _c, _d;
150
163
  // Tool-call ARGS live on the assistant message.
@@ -27,6 +27,7 @@ const ctx = (o = {}) => {
27
27
  turn: (_b = o.turn) !== null && _b !== void 0 ? _b : 0,
28
28
  activationEnded: (_c = o.activationEnded) !== null && _c !== void 0 ? _c : (() => false),
29
29
  phaseEnded: (_d = o.phaseEnded) !== null && _d !== void 0 ? _d : (() => false),
30
+ batchCalls: o.batchCalls,
30
31
  });
31
32
  };
32
33
  const sink = () => {
@@ -336,3 +337,69 @@ suite('multi-trigger first-wins: the earliest-listed firing trigger is the reaso
336
337
  assert.is((_b = events.find((e) => e.toolCallId === 'm2')) === null || _b === void 0 ? void 0 : _b.trigger, 'turnEnd', 'the supersession survivor still collapses via the next trigger');
337
338
  });
338
339
  suite.run();
340
+ // ── batched collapse (GENC-1476) ───────────────────────────────────────────
341
+ // Condensation rewrites history in place, so collapsing the instant a trigger fires
342
+ // breaks the prompt cache on nearly every call of a re-read loop. Batching holds the
343
+ // collapse set still between boundaries.
344
+ const batch = createLogicSuite('applyCondensation batching');
345
+ /** A re-read loop: `n` reads of the same path, each registered one model-call apart. */
346
+ const reReadLoop = (n) => {
347
+ const history = [];
348
+ const policies = new Map();
349
+ for (let i = 1; i <= n; i += 1) {
350
+ history.push(asstCall(`r${i}`, 'vfs_read', { path: 'A.tsx' }), toolMsg(`r${i}`, big()));
351
+ policies.set(`r${i}`, reg({ on: { kind: 'superseded', by: 'A.tsx' }, response: 'pointer' }, { iteration: i }));
352
+ }
353
+ return { history, policies };
354
+ };
355
+ /** Result content of every tool message, in order. */
356
+ const results = (out) => out.filter((m) => m.role === 'tool').map((m) => { var _a, _b; return (_b = (_a = m.toolResult) === null || _a === void 0 ? void 0 : _a.content) !== null && _b !== void 0 ? _b : ''; });
357
+ batch('without batching every superseded read collapses immediately', () => {
358
+ const { history, policies } = reReadLoop(4);
359
+ const out = results(applyCondensation(history, policies, ctx({ modelCall: 4 }), sink().on));
360
+ // Only the newest survives; the other three are already stubs.
361
+ assert.is(out.filter((c) => c.startsWith('[')).length, 3);
362
+ assert.is(out[3].length, big().length);
363
+ });
364
+ batch('batching leaves the prefix untouched between boundaries', () => {
365
+ const { history, policies } = reReadLoop(4);
366
+ // Boundary is floor(4/10)*10 = 0, so no call is within the batch yet.
367
+ const out = results(applyCondensation(history, policies, ctx({ modelCall: 4, batchCalls: 10 }), sink().on));
368
+ assert.equal(out.map((c) => c.length), [big().length, big().length, big().length, big().length]);
369
+ });
370
+ batch('the batch boundary collapses the backlog in one step', () => {
371
+ const { history, policies } = reReadLoop(12);
372
+ const before = results(applyCondensation(history, policies, ctx({ modelCall: 9, batchCalls: 10 }), sink().on));
373
+ const after = results(applyCondensation(history, policies, ctx({ modelCall: 10, batchCalls: 10 }), sink().on));
374
+ assert.is(before.filter((c) => c.startsWith('[')).length, 0);
375
+ // At the boundary, reads 1-9 collapse together; 10-12 are past it and stay full.
376
+ assert.is(after.filter((c) => c.startsWith('[')).length, 9);
377
+ });
378
+ batch('a call past the boundary neither collapses nor supersedes an older one', () => {
379
+ // The regression this guards: excluding the new call as a SUPERSEDER but still letting
380
+ // it collapse would elide the very result the agent just fetched.
381
+ const { history, policies } = reReadLoop(2);
382
+ policies.set('r2', reg({ on: { kind: 'superseded', by: 'A.tsx' }, response: 'pointer' }, { iteration: 7 }));
383
+ const out = results(applyCondensation(history, policies, ctx({ modelCall: 7, batchCalls: 5 }), sink().on));
384
+ assert.is(out[1].length, big().length, 'the newest read must stay full');
385
+ assert.is(out[0].length, big().length, 'the older read must not collapse early either');
386
+ });
387
+ batch('age is measured against the boundary, not the live clock', () => {
388
+ const history = [asstCall('g1', 'grep', {}), toolMsg('g1', big())];
389
+ const policies = new Map([
390
+ ['g1', reg({ on: { kind: 'age', turns: 1 }, response: 'pointer' }, { iteration: 1 })],
391
+ ]);
392
+ // Live clock says age 4 > 1 (would collapse); the boundary says 0, so it holds.
393
+ const held = results(applyCondensation(history, policies, ctx({ modelCall: 5, batchCalls: 10 }), sink().on));
394
+ assert.is(held[0].length, big().length);
395
+ const collapsed = results(applyCondensation(history, policies, ctx({ modelCall: 10, batchCalls: 10 }), sink().on));
396
+ assert.ok(collapsed[0].startsWith('['));
397
+ });
398
+ batch('batchCalls of 1, 0 or absent all keep the historical behaviour', () => {
399
+ for (const batchCalls of [1, 0, undefined]) {
400
+ const { history, policies } = reReadLoop(3);
401
+ const out = results(applyCondensation(history, policies, ctx({ modelCall: 3, batchCalls }), sink().on));
402
+ assert.is(out.filter((c) => c.startsWith('[')).length, 2, `batchCalls=${batchCalls}`);
403
+ }
404
+ });
405
+ batch.run();
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@genesislcap/ai-assistant",
3
3
  "description": "Genesis AI Assistant micro-frontend",
4
- "version": "15.5.0",
4
+ "version": "15.6.0",
5
5
  "license": "SEE LICENSE IN license.txt",
6
6
  "main": "dist/esm/index.js",
7
7
  "types": "dist/ai-assistant.d.ts",
@@ -73,26 +73,26 @@
73
73
  }
74
74
  },
75
75
  "devDependencies": {
76
- "@genesislcap/foundation-testing": "15.5.0",
77
- "@genesislcap/genx": "15.5.0",
78
- "@genesislcap/rollup-builder": "15.5.0",
79
- "@genesislcap/ts-builder": "15.5.0",
80
- "@genesislcap/uvu-playwright-builder": "15.5.0",
81
- "@genesislcap/vite-builder": "15.5.0",
82
- "@genesislcap/webpack-builder": "15.5.0",
76
+ "@genesislcap/foundation-testing": "15.6.0",
77
+ "@genesislcap/genx": "15.6.0",
78
+ "@genesislcap/rollup-builder": "15.6.0",
79
+ "@genesislcap/ts-builder": "15.6.0",
80
+ "@genesislcap/uvu-playwright-builder": "15.6.0",
81
+ "@genesislcap/vite-builder": "15.6.0",
82
+ "@genesislcap/webpack-builder": "15.6.0",
83
83
  "@types/dompurify": "^3.0.5",
84
84
  "@types/marked": "^5.0.2",
85
85
  "esbuild": "0.25.12"
86
86
  },
87
87
  "dependencies": {
88
- "@genesislcap/foundation-ai": "15.5.0",
89
- "@genesislcap/foundation-logger": "15.5.0",
90
- "@genesislcap/foundation-notifications": "15.5.0",
91
- "@genesislcap/foundation-redux": "15.5.0",
92
- "@genesislcap/foundation-ui": "15.5.0",
93
- "@genesislcap/foundation-utils": "15.5.0",
94
- "@genesislcap/rapid-design-system": "15.5.0",
95
- "@genesislcap/web-core": "15.5.0",
88
+ "@genesislcap/foundation-ai": "15.6.0",
89
+ "@genesislcap/foundation-logger": "15.6.0",
90
+ "@genesislcap/foundation-notifications": "15.6.0",
91
+ "@genesislcap/foundation-redux": "15.6.0",
92
+ "@genesislcap/foundation-ui": "15.6.0",
93
+ "@genesislcap/foundation-utils": "15.6.0",
94
+ "@genesislcap/rapid-design-system": "15.6.0",
95
+ "@genesislcap/web-core": "15.6.0",
96
96
  "dompurify": "^3.3.1",
97
97
  "marked": "^17.0.3"
98
98
  },
@@ -105,5 +105,5 @@
105
105
  "access": "public"
106
106
  },
107
107
  "customElements": "dist/custom-elements.json",
108
- "gitHead": "be7121280e828345e79cca6a084b1025536813f2"
108
+ "gitHead": "dbf070bac8a4bfc2a178e335b1faffb130e8f761"
109
109
  }
@@ -218,6 +218,16 @@ export interface ChatDriverConfig {
218
218
  maxToolIterations?: number;
219
219
  /** Hard cap on fold operations. Default `5`. */
220
220
  maxFoldOperations?: number;
221
+ /**
222
+ * Collapse `condenseWhen` payloads in batches of this many model-calls rather than as
223
+ * soon as each trigger fires. A positive integer; decimals are floored and anything
224
+ * below `1` becomes `1`. Default `1` — collapse immediately, the historical behaviour.
225
+ *
226
+ * Condensation rewrites history in place, so collapsing continuously breaks the
227
+ * provider's prompt cache on nearly every call of a re-read loop. Raising this trades
228
+ * up to that many calls' worth of extra context for one cache break per batch.
229
+ */
230
+ condenseBatchCalls?: number;
221
231
  /** Ring-buffer size for per-turn snapshots. Default `400`. */
222
232
  maxTurnSnapshots?: number;
223
233
  /** Session identity used to file meta events onto the shared debug-log timeline. */
@@ -566,6 +576,8 @@ export class ChatDriver extends EventTarget implements AiDriver {
566
576
 
567
577
  /** Hard cap on tool-loop iterations. */
568
578
  private readonly maxToolIterations: number;
579
+ /** Model-calls per condensation batch; `1` collapses as soon as a trigger fires. */
580
+ private readonly condenseBatchCalls: number;
569
581
  /** Session identity used to file meta events onto the shared debug-log timeline. */
570
582
  private readonly sessionKey: string;
571
583
  /** Injected activity bus; defaults to a no-op off-browser (Node/tests/headless). */
@@ -583,11 +595,13 @@ export class ChatDriver extends EventTarget implements AiDriver {
583
595
  primerHistory,
584
596
  maxToolIterations = DEFAULT_MAX_TOOL_ITERATIONS,
585
597
  maxFoldOperations = DEFAULT_MAX_FOLD_OPERATIONS,
598
+ condenseBatchCalls = 1,
586
599
  maxTurnSnapshots = DEFAULT_MAX_TURN_SNAPSHOTS,
587
600
  sessionKey = '',
588
601
  activityBus = NOOP_ACTIVITY_BUS,
589
602
  } = config;
590
603
  this.maxToolIterations = maxToolIterations;
604
+ this.condenseBatchCalls = condenseBatchCalls;
591
605
  this.sessionKey = sessionKey;
592
606
  this.activityBus = activityBus;
593
607
  if (typeof toolHandlers === 'function') {
@@ -2335,6 +2349,7 @@ export class ChatDriver extends EventTarget implements AiDriver {
2335
2349
  // A call's phase has ended once the app declared a later phase epoch
2336
2350
  // current via `endPhase()`.
2337
2351
  phaseEnded: (phaseEpoch) => phaseEpoch < this.currentPhaseEpoch,
2352
+ batchCalls: this.condenseBatchCalls,
2338
2353
  },
2339
2354
  (detail) => recordMetaEvent(this.sessionKey, 'context.condensed', detail),
2340
2355
  );
@@ -44,12 +44,14 @@ const ctx = (
44
44
  turn?: number;
45
45
  activationEnded?: (a: number) => boolean;
46
46
  phaseEnded?: (e: number) => boolean;
47
+ batchCalls?: number;
47
48
  } = {},
48
49
  ): CondenseContext => ({
49
50
  modelCall: o.modelCall ?? 0,
50
51
  turn: o.turn ?? 0,
51
52
  activationEnded: o.activationEnded ?? (() => false),
52
53
  phaseEnded: o.phaseEnded ?? (() => false),
54
+ batchCalls: o.batchCalls,
53
55
  });
54
56
 
55
57
  const sink = () => {
@@ -434,3 +436,104 @@ suite('multi-trigger first-wins: the earliest-listed firing trigger is the reaso
434
436
  });
435
437
 
436
438
  suite.run();
439
+
440
+ // ── batched collapse (GENC-1476) ───────────────────────────────────────────
441
+ // Condensation rewrites history in place, so collapsing the instant a trigger fires
442
+ // breaks the prompt cache on nearly every call of a re-read loop. Batching holds the
443
+ // collapse set still between boundaries.
444
+
445
+ const batch = createLogicSuite('applyCondensation batching');
446
+
447
+ /** A re-read loop: `n` reads of the same path, each registered one model-call apart. */
448
+ const reReadLoop = (n: number) => {
449
+ const history: ChatMessage[] = [];
450
+ const policies = new Map<string, RegisteredCondensePolicy>();
451
+ for (let i = 1; i <= n; i += 1) {
452
+ history.push(asstCall(`r${i}`, 'vfs_read', { path: 'A.tsx' }), toolMsg(`r${i}`, big()));
453
+ policies.set(
454
+ `r${i}`,
455
+ reg({ on: { kind: 'superseded', by: 'A.tsx' }, response: 'pointer' }, { iteration: i }),
456
+ );
457
+ }
458
+ return { history, policies };
459
+ };
460
+
461
+ /** Result content of every tool message, in order. */
462
+ const results = (out: ChatMessage[]): string[] =>
463
+ out.filter((m) => m.role === 'tool').map((m) => m.toolResult?.content ?? '');
464
+
465
+ batch('without batching every superseded read collapses immediately', () => {
466
+ const { history, policies } = reReadLoop(4);
467
+ const out = results(applyCondensation(history, policies, ctx({ modelCall: 4 }), sink().on));
468
+ // Only the newest survives; the other three are already stubs.
469
+ assert.is(out.filter((c) => c.startsWith('[')).length, 3);
470
+ assert.is(out[3].length, big().length);
471
+ });
472
+
473
+ batch('batching leaves the prefix untouched between boundaries', () => {
474
+ const { history, policies } = reReadLoop(4);
475
+ // Boundary is floor(4/10)*10 = 0, so no call is within the batch yet.
476
+ const out = results(
477
+ applyCondensation(history, policies, ctx({ modelCall: 4, batchCalls: 10 }), sink().on),
478
+ );
479
+ assert.equal(
480
+ out.map((c) => c.length),
481
+ [big().length, big().length, big().length, big().length],
482
+ );
483
+ });
484
+
485
+ batch('the batch boundary collapses the backlog in one step', () => {
486
+ const { history, policies } = reReadLoop(12);
487
+ const before = results(
488
+ applyCondensation(history, policies, ctx({ modelCall: 9, batchCalls: 10 }), sink().on),
489
+ );
490
+ const after = results(
491
+ applyCondensation(history, policies, ctx({ modelCall: 10, batchCalls: 10 }), sink().on),
492
+ );
493
+ assert.is(before.filter((c) => c.startsWith('[')).length, 0);
494
+ // At the boundary, reads 1-9 collapse together; 10-12 are past it and stay full.
495
+ assert.is(after.filter((c) => c.startsWith('[')).length, 9);
496
+ });
497
+
498
+ batch('a call past the boundary neither collapses nor supersedes an older one', () => {
499
+ // The regression this guards: excluding the new call as a SUPERSEDER but still letting
500
+ // it collapse would elide the very result the agent just fetched.
501
+ const { history, policies } = reReadLoop(2);
502
+ policies.set(
503
+ 'r2',
504
+ reg({ on: { kind: 'superseded', by: 'A.tsx' }, response: 'pointer' }, { iteration: 7 }),
505
+ );
506
+ const out = results(
507
+ applyCondensation(history, policies, ctx({ modelCall: 7, batchCalls: 5 }), sink().on),
508
+ );
509
+ assert.is(out[1].length, big().length, 'the newest read must stay full');
510
+ assert.is(out[0].length, big().length, 'the older read must not collapse early either');
511
+ });
512
+
513
+ batch('age is measured against the boundary, not the live clock', () => {
514
+ const history = [asstCall('g1', 'grep', {}), toolMsg('g1', big())];
515
+ const policies = new Map([
516
+ ['g1', reg({ on: { kind: 'age', turns: 1 }, response: 'pointer' }, { iteration: 1 })],
517
+ ]);
518
+ // Live clock says age 4 > 1 (would collapse); the boundary says 0, so it holds.
519
+ const held = results(
520
+ applyCondensation(history, policies, ctx({ modelCall: 5, batchCalls: 10 }), sink().on),
521
+ );
522
+ assert.is(held[0].length, big().length);
523
+ const collapsed = results(
524
+ applyCondensation(history, policies, ctx({ modelCall: 10, batchCalls: 10 }), sink().on),
525
+ );
526
+ assert.ok(collapsed[0].startsWith('['));
527
+ });
528
+
529
+ batch('batchCalls of 1, 0 or absent all keep the historical behaviour', () => {
530
+ for (const batchCalls of [1, 0, undefined]) {
531
+ const { history, policies } = reReadLoop(3);
532
+ const out = results(
533
+ applyCondensation(history, policies, ctx({ modelCall: 3, batchCalls }), sink().on),
534
+ );
535
+ assert.is(out.filter((c) => c.startsWith('[')).length, 2, `batchCalls=${batchCalls}`);
536
+ }
537
+ });
538
+
539
+ batch.run();
@@ -75,6 +75,20 @@ export interface CondenseContext {
75
75
  * `phaseEnd` clock. True once `endPhase()` has been called since the call.
76
76
  */
77
77
  phaseEnded: (phaseEpoch: number) => boolean;
78
+ /**
79
+ * Collapse in batches of this many model-calls instead of continuously. A positive
80
+ * integer; decimals are floored and anything below `1` becomes `1`. Omit (or `1`) to
81
+ * collapse as soon as a trigger fires, which is the historical behaviour.
82
+ *
83
+ * Condensation rewrites a payload *in place*, so the model-bound history is not
84
+ * append-only and every prompt-cache entry written before the rewrite is invalidated
85
+ * from that position on. Collapsing the moment each trigger fires therefore breaks the
86
+ * cache on nearly every call of a re-read loop — the case condensation exists to
87
+ * contain. Batching holds the collapse set still between boundaries so the prefix is
88
+ * append-only in between, trading up to `batchCalls` calls' worth of extra context for
89
+ * one cache break per batch instead of one per call.
90
+ */
91
+ batchCalls?: number;
78
92
  }
79
93
 
80
94
  /**
@@ -193,6 +207,20 @@ export function applyCondensation(
193
207
  // lookup. For each `superseded` key the LAST call in history order survives.
194
208
  // The name lookup lets a tool message (which carries only an id) name its tool
195
209
  // in stubs and events.
210
+ // Batch boundary: the collapse set is resolved as of this model-call and stays put
211
+ // until the next boundary, so the prefix is append-only in between. `batchCalls` of 1
212
+ // (the default) makes this `ctx.modelCall` — i.e. no batching.
213
+ const batchCalls = Math.max(1, Math.floor(ctx.batchCalls ?? 1));
214
+ const asOf =
215
+ batchCalls === 1 ? ctx.modelCall : Math.floor(ctx.modelCall / batchCalls) * batchCalls;
216
+ // A call made after the boundary is invisible to this pass in BOTH directions: it
217
+ // cannot supersede an older call (which would collapse the older payload early), and
218
+ // it cannot itself collapse (which would elide the very result just fetched). Unbatched,
219
+ // the boundary is the live clock and this gate is off entirely — a call registered on
220
+ // the current model-call is still eligible, as it always was.
221
+ const withinBatch = (entry: RegisteredCondensePolicy): boolean =>
222
+ batchCalls === 1 || entry.iteration <= asOf;
223
+
196
224
  const latestByKey = new Map<string, string>();
197
225
  const nameById = new Map<string, string>();
198
226
  for (const msg of history) {
@@ -200,7 +228,7 @@ export function applyCondensation(
200
228
  for (const tc of msg.toolCalls) {
201
229
  nameById.set(tc.id, tc.name);
202
230
  const entry = policies.get(tc.id);
203
- if (!entry) continue;
231
+ if (!entry || !withinBatch(entry)) continue;
204
232
  for (const t of triggersOf(entry)) {
205
233
  if (t.kind === 'superseded') latestByKey.set(t.by, tc.id);
206
234
  }
@@ -217,7 +245,7 @@ export function applyCondensation(
217
245
  // past the last allowed view: turns:1 → seen once, then gone. Monotonic
218
246
  // clock, so this holds across turn boundaries too.
219
247
  case 'age':
220
- return ctx.modelCall - entry.iteration > trig.turns;
248
+ return asOf - entry.iteration > trig.turns;
221
249
  case 'turnEnd':
222
250
  return ctx.turn > entry.turn;
223
251
  case 'agentEnd':
@@ -238,7 +266,9 @@ export function applyCondensation(
238
266
  toolCallId: string,
239
267
  entry: RegisteredCondensePolicy,
240
268
  ): CondenseTrigger | undefined =>
241
- triggersOf(entry).find((t) => triggerFires(toolCallId, entry, t));
269
+ withinBatch(entry)
270
+ ? triggersOf(entry).find((t) => triggerFires(toolCallId, entry, t))
271
+ : undefined;
242
272
 
243
273
  return history.map((msg) => {
244
274
  // Tool-call ARGS live on the assistant message.