@vincemakes/kiso-runtime 0.1.33 → 0.1.34

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.
@@ -58,6 +58,16 @@ export declare function lastSummaryPoint(events: readonly Event[]): number;
58
58
  * containing the LATEST one (still a turn boundary). A protected round
59
59
  * as the FIRST uncovered round leaves nothing before it to cover →
60
60
  * undefined (an honest "nothing to compact").
61
+ *
62
+ * P1 (0.1.42): the SAME pullback family now enforces the pairing
63
+ * invariant — a boundary NEVER splits a tool_call/tool_result pair. A
64
+ * mid-execution input leaves a covered call with a kept result, and the
65
+ * projection renders an orphaned tool message (a real provider 400 — the
66
+ * fresh2 family). The straddle pullback ITERATES to stability — every
67
+ * straddled pair in the shrinking range pulls the boundary before its
68
+ * round — while the protected pullback applies ONCE on the base range
69
+ * (the operative list is the LATEST echo — the old ⑥ semantics:
70
+ * superseded echoes stay coverable).
61
71
  */
62
72
  export declare function summaryBoundarySeq(events: readonly Event[], keepRounds?: number): number | undefined;
63
73
  /**
package/dist/summarize.js CHANGED
@@ -92,6 +92,16 @@ export function lastSummaryPoint(events) {
92
92
  * containing the LATEST one (still a turn boundary). A protected round
93
93
  * as the FIRST uncovered round leaves nothing before it to cover →
94
94
  * undefined (an honest "nothing to compact").
95
+ *
96
+ * P1 (0.1.42): the SAME pullback family now enforces the pairing
97
+ * invariant — a boundary NEVER splits a tool_call/tool_result pair. A
98
+ * mid-execution input leaves a covered call with a kept result, and the
99
+ * projection renders an orphaned tool message (a real provider 400 — the
100
+ * fresh2 family). The straddle pullback ITERATES to stability — every
101
+ * straddled pair in the shrinking range pulls the boundary before its
102
+ * round — while the protected pullback applies ONCE on the base range
103
+ * (the operative list is the LATEST echo — the old ⑥ semantics:
104
+ * superseded echoes stay coverable).
95
105
  */
96
106
  export function summaryBoundarySeq(events, keepRounds = KEEP_RECENT_ROUNDS) {
97
107
  const prevPoint = lastSummaryPoint(events);
@@ -102,16 +112,27 @@ export function summaryBoundarySeq(events, keepRounds = KEEP_RECENT_ROUNDS) {
102
112
  }
103
113
  if (uncoveredInputs.length <= keepRounds)
104
114
  return undefined;
105
- const base = uncoveredInputs[uncoveredInputs.length - keepRounds] - 1;
106
- const protectedRound = latestProtectedBoundary(events, prevPoint, base);
107
- if (protectedRound !== undefined) {
108
- if (protectedRound <= prevPoint)
115
+ const firstUncovered = uncoveredInputs[0];
116
+ let boundary = uncoveredInputs[uncoveredInputs.length - keepRounds] - 1;
117
+ // The protected pullback applies ONCE on the base range (⑥); the
118
+ // straddle pullback recomputes against the SHRINKING range below it.
119
+ const protectedBoundary = latestProtectedBoundary(events, prevPoint, boundary);
120
+ for (;;) {
121
+ const straddleBoundary = latestStraddleBoundary(events, prevPoint, boundary);
122
+ let pull = straddleBoundary;
123
+ if (protectedBoundary !== undefined) {
124
+ pull = pull === undefined ? protectedBoundary : Math.min(pull, protectedBoundary);
125
+ }
126
+ if (pull === undefined)
127
+ return boundary;
128
+ // Nothing before the first uncovered round (or before the previous
129
+ // summary point) is coverable — the honest "nothing to compact".
130
+ if (pull < firstUncovered || pull <= prevPoint)
109
131
  return undefined;
110
- return protectedRound;
132
+ if (pull >= boundary)
133
+ return boundary; // stable — the pull never advances
134
+ boundary = pull;
111
135
  }
112
- // The input at m - keepRounds opens the FIRST KEPT round; everything
113
- // before it (m - keepRounds ≥ 1 covered rounds) is summarizable.
114
- return base;
115
136
  }
116
137
  /**
117
138
  * ⑥: the boundary just before the round holding the LATEST do-not-compact
@@ -145,6 +166,37 @@ function latestProtectedBoundary(events, prevPoint, base) {
145
166
  return undefined;
146
167
  return inputSeq - 1;
147
168
  }
169
+ /**
170
+ * P1 (0.1.42): the boundary just before the round holding the LATEST
171
+ * tool_call_end in (prevPoint, cut] whose tool_result landed on the KEPT
172
+ * side of the cut — covering the call alone would project an orphaned
173
+ * tool message (the pairing invariant; the fresh2 400 family). Returns
174
+ * the pair's round-opening input minus one — still a turn boundary —
175
+ * or prevPoint when the round opened at or before the previous summary
176
+ * point (the caller's `pull <= prevPoint` guard turns that into the
177
+ * honest nothing-to-compact: the range holds no whole pair to keep, so
178
+ * the compact is refused) — or undefined when the range holds no
179
+ * straddled pair.
180
+ */
181
+ function latestStraddleBoundary(events, prevPoint, cut) {
182
+ let straddledCall = -1;
183
+ for (const ev of events) {
184
+ if (ev.type !== "tool_call_end" || ev.seq <= prevPoint || ev.seq > cut)
185
+ continue;
186
+ const keptResult = events.some((e) => e.type === "tool_result" && e.callId === ev.callId && e.seq > cut);
187
+ if (keptResult)
188
+ straddledCall = ev.seq;
189
+ }
190
+ if (straddledCall < 0)
191
+ return undefined;
192
+ // The pair's round opening: the last user_input before the call.
193
+ let inputSeq = -1;
194
+ for (const ev of events) {
195
+ if (ev.type === "user_input" && ev.seq > prevPoint && ev.seq < straddledCall)
196
+ inputSeq = ev.seq;
197
+ }
198
+ return inputSeq < 0 ? prevPoint : inputSeq - 1;
199
+ }
148
200
  /**
149
201
  * The NoticeCell's number: estimated tokens of the covered content minus
150
202
  * the summary's own — the same chars/4 proxy as estimateTokens (a stable
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vincemakes/kiso-runtime",
3
- "version": "0.1.33",
3
+ "version": "0.1.34",
4
4
  "description": "kiso runtime — durable multi-turn agent sessions: AgentDefinition, AgentRuntime, AgentSession, Run, append-only JSONL store.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -21,11 +21,11 @@
21
21
  "test": "vitest run"
22
22
  },
23
23
  "dependencies": {
24
- "@vincemakes/kiso-core": "0.1.32"
24
+ "@vincemakes/kiso-core": "0.1.33"
25
25
  },
26
26
  "peerDependencies": {
27
- "@vincemakes/kiso-provider-anthropic": "0.1.33",
28
- "@vincemakes/kiso-provider-openai": "0.1.33"
27
+ "@vincemakes/kiso-provider-anthropic": "0.1.34",
28
+ "@vincemakes/kiso-provider-openai": "0.1.34"
29
29
  },
30
30
  "peerDependenciesMeta": {
31
31
  "@vincemakes/kiso-provider-anthropic": {