@vincemakes/kiso-runtime 0.1.28 → 0.1.29

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.
@@ -50,6 +50,14 @@ export declare function lastSummaryPoint(events: readonly Event[]): number;
50
50
  * a turn boundary by construction, so the projection's skip never splits
51
51
  * a message. Returns undefined when fewer than keepRounds+1 uncovered
52
52
  * rounds exist (nothing worth covering yet).
53
+ *
54
+ * ⑥ (todo round): a tool result tagged do-not-compact is DURABLE work
55
+ * memory (the todo_set echo) — the summary must never cover its round,
56
+ * or the model loses the current list. When the base boundary would
57
+ * cover such a result, the boundary pulls back to just before the round
58
+ * containing the LATEST one (still a turn boundary). A protected round
59
+ * as the FIRST uncovered round leaves nothing before it to cover →
60
+ * undefined (an honest "nothing to compact").
53
61
  */
54
62
  export declare function summaryBoundarySeq(events: readonly Event[], keepRounds?: number): number | undefined;
55
63
  /**
package/dist/summarize.js CHANGED
@@ -13,7 +13,7 @@
13
13
  * unchanged ("nothing happened"). Only the generated `summarized` event
14
14
  * lands on disk; the original events stay there forever.
15
15
  */
16
- import { estimateTokens } from "@vincemakes/kiso-core";
16
+ import { estimateTokens, DO_NOT_COMPACT } from "@vincemakes/kiso-core";
17
17
  /** K (ADR-0044): the recent ROUNDS kept intact by /compact — a constant,
18
18
  * not a knob. The covered range ends just before the K-th most recent
19
19
  * round, so the model still reasons over the recent conversation. */
@@ -84,6 +84,14 @@ export function lastSummaryPoint(events) {
84
84
  * a turn boundary by construction, so the projection's skip never splits
85
85
  * a message. Returns undefined when fewer than keepRounds+1 uncovered
86
86
  * rounds exist (nothing worth covering yet).
87
+ *
88
+ * ⑥ (todo round): a tool result tagged do-not-compact is DURABLE work
89
+ * memory (the todo_set echo) — the summary must never cover its round,
90
+ * or the model loses the current list. When the base boundary would
91
+ * cover such a result, the boundary pulls back to just before the round
92
+ * containing the LATEST one (still a turn boundary). A protected round
93
+ * as the FIRST uncovered round leaves nothing before it to cover →
94
+ * undefined (an honest "nothing to compact").
87
95
  */
88
96
  export function summaryBoundarySeq(events, keepRounds = KEEP_RECENT_ROUNDS) {
89
97
  const prevPoint = lastSummaryPoint(events);
@@ -94,9 +102,48 @@ export function summaryBoundarySeq(events, keepRounds = KEEP_RECENT_ROUNDS) {
94
102
  }
95
103
  if (uncoveredInputs.length <= keepRounds)
96
104
  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)
109
+ return undefined;
110
+ return protectedRound;
111
+ }
97
112
  // The input at m - keepRounds opens the FIRST KEPT round; everything
98
113
  // before it (m - keepRounds ≥ 1 covered rounds) is summarizable.
99
- return uncoveredInputs[uncoveredInputs.length - keepRounds] - 1;
114
+ return base;
115
+ }
116
+ /**
117
+ * ⑥: the boundary just before the round holding the LATEST do-not-compact
118
+ * tool result inside (prevPoint, base] — that round's opening user_input
119
+ * minus one, or undefined when the range holds no such result. The
120
+ * projection replaces by RANGE, so only the newest echo matters: older
121
+ * tagged echoes are superseded and may be covered.
122
+ */
123
+ function latestProtectedBoundary(events, prevPoint, base) {
124
+ let protectSeq = -1;
125
+ for (const ev of events) {
126
+ if (ev.type === "tool_result" &&
127
+ ev.seq > prevPoint &&
128
+ ev.seq <= base &&
129
+ (ev.tags ?? []).includes(DO_NOT_COMPACT)) {
130
+ protectSeq = ev.seq;
131
+ }
132
+ }
133
+ if (protectSeq < 0)
134
+ return undefined;
135
+ // The round's opening input: the last user_input before the result.
136
+ // The result's whole round is uncovered by construction (the previous
137
+ // compact ended at a turn boundary before its input), so the input is
138
+ // > prevPoint — the guard is the belt.
139
+ let inputSeq = -1;
140
+ for (const ev of events) {
141
+ if (ev.type === "user_input" && ev.seq > prevPoint && ev.seq < protectSeq)
142
+ inputSeq = ev.seq;
143
+ }
144
+ if (inputSeq < 0)
145
+ return undefined;
146
+ return inputSeq - 1;
100
147
  }
101
148
  /**
102
149
  * The NoticeCell's number: estimated tokens of the covered content minus
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vincemakes/kiso-runtime",
3
- "version": "0.1.28",
3
+ "version": "0.1.29",
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.28"
24
+ "@vincemakes/kiso-core": "0.1.29"
25
25
  },
26
26
  "peerDependencies": {
27
- "@vincemakes/kiso-provider-anthropic": "0.1.28",
28
- "@vincemakes/kiso-provider-openai": "0.1.28"
27
+ "@vincemakes/kiso-provider-anthropic": "0.1.29",
28
+ "@vincemakes/kiso-provider-openai": "0.1.29"
29
29
  },
30
30
  "peerDependenciesMeta": {
31
31
  "@vincemakes/kiso-provider-anthropic": {
@@ -36,7 +36,7 @@
36
36
  }
37
37
  },
38
38
  "devDependencies": {
39
- "@vincemakes/kiso-evals": "0.1.28",
39
+ "@vincemakes/kiso-evals": "0.1.29",
40
40
  "@types/node": "^26.1.2",
41
41
  "typescript": "^5.7.2",
42
42
  "vitest": "^3.0.0"