@vincemakes/kiso-runtime 0.2.1 → 0.5.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.
package/dist/agent.d.ts CHANGED
@@ -55,6 +55,8 @@ export interface AgentDefinition {
55
55
  readonly microcompact?: {
56
56
  readonly thresholdTokens: number;
57
57
  };
58
+ /** E6: the session context policy (run-start actions, injection-side only). */
59
+ readonly contextPolicy?: import("./session.js").ContextPolicy;
58
60
  readonly maxRetries?: number;
59
61
  /** E1: loaded extensions — their tools merge into the registry (a name
60
62
  * collision with a built-in is a loud startup error), their hooks
package/dist/agent.js CHANGED
@@ -71,6 +71,7 @@ export class AgentRuntime {
71
71
  ...(this.#definition.temperature !== undefined ? { temperature: this.#definition.temperature } : {}),
72
72
  ...(this.#definition.compaction !== undefined ? { compaction: this.#definition.compaction } : {}),
73
73
  ...(this.#definition.microcompact !== undefined ? { microcompact: this.#definition.microcompact } : {}),
74
+ ...(this.#definition.contextPolicy !== undefined ? { contextPolicy: this.#definition.contextPolicy } : {}),
74
75
  ...(this.#definition.maxRetries !== undefined ? { maxRetries: this.#definition.maxRetries } : {}),
75
76
  ...(this.#definition.extensions !== undefined ? { extensions: this.#definition.extensions } : {}),
76
77
  };
package/dist/compose.d.ts CHANGED
@@ -3,7 +3,7 @@
3
3
  * from session.ts: the extension system-prompt appends, the extension
4
4
  * hook composition (the existing come first), and the loop's microcompact config lookup.
5
5
  */
6
- import type { ApprovalChain, HookHost, KisoExtension, ToolRegistry } from "@vincemakes/kiso-core";
6
+ import type { ApprovalChain, Event, HookHost, KisoExtension, ToolRegistry } from "@vincemakes/kiso-core";
7
7
  import type { SessionConfig } from "./session.js";
8
8
  /** The table, or "" when the registry is empty (no vocabulary, no tools). */
9
9
  export declare function composeToolTable(registry: ToolRegistry): string;
@@ -23,12 +23,17 @@ export declare function composeSystemPrompt(base: string | undefined, extensions
23
23
  */
24
24
  export declare function composeHooks(existing: HookHost | undefined, extensions: readonly KisoExtension[]): HookHost | undefined;
25
25
  /**
26
- * E2: the loop's microcompact config — the session's own microcompact wins;
27
- * otherwise the FIRST extension providing a compaction config supplies it.
28
- * An extension config without a threshold contributes nothing (a boundary
29
- * needs a threshold to ever fire).
26
+ * E2/E6: the loop's microcompact config — the session's own microcompact
27
+ * wins; otherwise the FIRST extension providing a compaction config
28
+ * supplies it. An extension config without a threshold contributes nothing
29
+ * (a boundary needs a threshold to ever fire).
30
+ * E6 (candidate B): the session's contextPolicy.microcompact override beats
31
+ * the session's own config (a tuned policy wins over the CLI default), and
32
+ * its minTurns no-fire guard — below that many completed user inputs the
33
+ * boundary config is OMITTED: a short task never pays a break it cannot
34
+ * amortize (the E5-F1 accounting).
30
35
  */
31
- export declare function microcompactFor(config: SessionConfig): {
36
+ export declare function microcompactFor(config: SessionConfig, log?: readonly Event[]): {
32
37
  readonly thresholdTokens: number;
33
38
  readonly keepResults?: number;
34
39
  } | undefined;
package/dist/compose.js CHANGED
@@ -135,14 +135,28 @@ export function composeHooks(existing, extensions) {
135
135
  return out;
136
136
  }
137
137
  /**
138
- * E2: the loop's microcompact config — the session's own microcompact wins;
139
- * otherwise the FIRST extension providing a compaction config supplies it.
140
- * An extension config without a threshold contributes nothing (a boundary
141
- * needs a threshold to ever fire).
138
+ * E2/E6: the loop's microcompact config — the session's own microcompact
139
+ * wins; otherwise the FIRST extension providing a compaction config
140
+ * supplies it. An extension config without a threshold contributes nothing
141
+ * (a boundary needs a threshold to ever fire).
142
+ * E6 (candidate B): the session's contextPolicy.microcompact override beats
143
+ * the session's own config (a tuned policy wins over the CLI default), and
144
+ * its minTurns no-fire guard — below that many completed user inputs the
145
+ * boundary config is OMITTED: a short task never pays a break it cannot
146
+ * amortize (the E5-F1 accounting).
142
147
  */
143
- export function microcompactFor(config) {
144
- if (config.microcompact !== undefined)
145
- return config.microcompact;
148
+ export function microcompactFor(config, log) {
149
+ const policy = config.contextPolicy?.microcompact;
150
+ const effective = policy !== undefined
151
+ ? { thresholdTokens: policy.thresholdTokens, ...(policy.keepResults !== undefined ? { keepResults: policy.keepResults } : {}) }
152
+ : config.microcompact;
153
+ if (policy?.minTurns !== undefined && log !== undefined) {
154
+ const userInputs = log.filter((e) => e.type === "user_input").length;
155
+ if (userInputs < policy.minTurns)
156
+ return undefined;
157
+ }
158
+ if (effective !== undefined)
159
+ return effective;
146
160
  for (const ext of config.extensions ?? []) {
147
161
  const c = ext.compaction;
148
162
  if (c !== undefined && c.thresholdTokens !== undefined) {
package/dist/index.d.ts CHANGED
@@ -19,7 +19,7 @@ export { AgentRuntime, AgentRuntime as Agent, createAgent } from "./agent.js";
19
19
  export type { AgentDefinition, PermissionPolicy, PermissionRule } from "./agent.js";
20
20
  export { AgentSession, AgentSession as Session } from "./session.js";
21
21
  export { PoisonedSessionError, ResumeBlockedError } from "./session.js";
22
- export type { ApprovalRequest, CompactInfo, SessionConfig, SummarizeResult } from "./session.js";
22
+ export type { ApprovalRequest, CompactInfo, ContextPolicy, SessionConfig, SummarizeResult } from "./session.js";
23
23
  export { Run } from "./run.js";
24
24
  export { SessionStore, StaleWriterError, StoreCorruptionError } from "./store.js";
25
25
  export type { Event, SessionMeta, StoreRecord } from "./store.js";
package/dist/run.js CHANGED
@@ -96,9 +96,11 @@ export class Run {
96
96
  });
97
97
  tracer.init();
98
98
  const signal = this.#externalSignal ? new MergedSignal(this.#abort.signal, this.#externalSignal) : this.#abort.signal;
99
- // E2: the session's own microcompact wins; otherwise the FIRST
100
- // extension providing a compaction config supplies it.
101
- const microcompact = microcompactFor(this.#config);
99
+ // E2/E6: the session's own microcompact wins; otherwise the FIRST
100
+ // extension providing a compaction config supplies it. E6: the
101
+ // contextPolicy override beats the session's own, and its minTurns
102
+ // no-fire guard may omit the config below the floor.
103
+ const microcompact = microcompactFor(this.#config, log.all);
102
104
  // E2: the session's own systemPrompt first, then every extension
103
105
  // append in LOAD order — deterministic (same extensions → same
104
106
  // prompt); no appends → byte-identical to the extension-less run.
@@ -228,6 +230,15 @@ export class Run {
228
230
  if (openRun !== undefined) {
229
231
  throw new Error(`session ${this.#session.id} still has an open run (${openRun}) — resume() it instead of starting a new run`);
230
232
  }
233
+ // E6 — the run-start context policy: BEFORE this run's
234
+ // user_input lands, the policy may persist one `summarized`
235
+ // fact through the existing summarize() path (the manual
236
+ // /compact affordance point). The boundary then rides the LAST
237
+ // recorded run, and this run's first request projects the
238
+ // compressed view. Fresh runs only — the recovery path's
239
+ // projection must never shift mid-recovery. A summary failure
240
+ // is swallowed inside (the turn proceeds uncompressed).
241
+ await this.#session.maybeApplyContextPolicy(signal);
231
242
  // 1. Durable first: the prompt enters the log and the store
232
243
  // before any model call — a crash here leaves a restorable
233
244
  // session. The prompt is also the first event the consumer
package/dist/session.d.ts CHANGED
@@ -127,9 +127,25 @@ export declare class AgentSession {
127
127
  */
128
128
  summarize(options?: {
129
129
  keepRounds?: number;
130
+ keepTokens?: number;
130
131
  signal?: AbortSignalLike;
131
132
  onStart?: (info: CompactInfo) => void;
133
+ drop?: boolean;
132
134
  }): Promise<SummarizeResult | null>;
135
+ /**
136
+ * E6 — the run-start context policy (candidate A + the crux drop arm).
137
+ * Called at the start of every FRESH run, BEFORE its user_input lands:
138
+ * when the policy is armed and the projected context crosses the
139
+ * trigger (and enough uncovered rounds exist — the keepRounds gate
140
+ * inside summarize), one `summarized` fact is persisted through the
141
+ * existing summarize() path. The boundary then rides the LAST recorded
142
+ * run and the run's first request projects the compressed view.
143
+ * Restraint: a short session never crosses the trigger — firing is a
144
+ * net loss by the E5-F1 accounting (a break that cannot amortize).
145
+ * Failure is swallowed: the compaction is an optimization — "nothing
146
+ * happened" must never break the user's turn.
147
+ */
148
+ maybeApplyContextPolicy(signal?: AbortSignalLike): Promise<void>;
133
149
  /**
134
150
  * Pauses that still await a human decision (durable, survives restart).
135
151
  * B group: a request whose RUN has terminated is DEAD — it is neither
@@ -182,6 +198,69 @@ export declare class AgentSession {
182
198
  flushPendingVerdicts(runId: string, log: EventLog): Promise<void>;
183
199
  dropResolver(decisionId: string): void;
184
200
  }
201
+ /**
202
+ * E6 — the session context policy (all optional; absent = the pre-E6
203
+ * behavior, zero change). The policy is INJECTION-side only: every action
204
+ * persists a durable fact (`summarized`, `microcompacted`) whose projection
205
+ * shrinks the SENT view — the durable log's bytes never change (the E5
206
+ * discipline). Actions land at RUN START, never mid-run (D5); the manual
207
+ * /compact affordance point.
208
+ */
209
+ export interface ContextPolicy {
210
+ /**
211
+ * A — the auto-summary: at run start, when the projected context
212
+ * crosses the trigger AND enough uncovered rounds exist, the
213
+ * existing summarize() path persists ONE `summarized` fact. The
214
+ * keepRounds gate is the amortization structure — a fire needs
215
+ * keepRounds+1 uncovered rounds, so each boundary is preceded by
216
+ * that much content and followed by the kept rounds' requests to
217
+ * amortize the break (the E5-F1 accounting).
218
+ *
219
+ * E6 (g): the trigger is EXACTLY ONE of triggerTokens (an absolute,
220
+ * the legacy override) or windowTokens (the product arming — the
221
+ * runtime computes window − POLICY_RESERVE; never a fixed low
222
+ * absolute). keepTokens floors the kept suffix (the (f) budget;
223
+ * the default KEEP_TOKENS_DEFAULT applies when absent). maxFailures
224
+ * sets the (h) circuit breaker (the default MAX_SUMMARY_FAILURES
225
+ * applies when absent).
226
+ */
227
+ readonly summary?: {
228
+ readonly triggerTokens?: number;
229
+ readonly windowTokens?: number;
230
+ readonly keepRounds?: number;
231
+ readonly keepTokens?: number;
232
+ readonly maxFailures?: number;
233
+ };
234
+ /**
235
+ * C — the crux-experiment drop arm (EXPERIMENT-ONLY, never a default):
236
+ * the same trigger persists the same-shaped fact with a fixed
237
+ * placeholder and NO model call — the covered turns leave the sent
238
+ * context at zero generation cost. When present, it REPLACES the
239
+ * summary mode (one conversation-layer compactor at a time). The
240
+ * adopted shape — if the crux evidence earns it — is a distinct
241
+ * `dropped` event family, not this placeholder text. Same trigger
242
+ * shapes, keep budget, and breaker as the summary arm.
243
+ */
244
+ readonly drop?: {
245
+ readonly triggerTokens?: number;
246
+ readonly windowTokens?: number;
247
+ readonly keepRounds?: number;
248
+ readonly keepTokens?: number;
249
+ readonly maxFailures?: number;
250
+ };
251
+ /**
252
+ * B — the session-aware microcompact: the threshold/keep-window
253
+ * override the session's own microcompact (a tuned policy wins over
254
+ * the CLI default). minTurns is the no-fire guard: below that many
255
+ * completed user inputs the boundary config is OMITTED from the loop
256
+ * — a short task never pays a break it cannot amortize.
257
+ */
258
+ readonly microcompact?: {
259
+ readonly thresholdTokens: number;
260
+ readonly keepResults?: number;
261
+ readonly minTurns?: number;
262
+ };
263
+ }
185
264
  export interface SessionConfig {
186
265
  readonly model: string;
187
266
  /** E1: the adapter identity ("anthropic" | "openai-compat") — trace
@@ -205,6 +284,8 @@ export interface SessionConfig {
205
284
  readonly microcompact?: {
206
285
  readonly thresholdTokens: number;
207
286
  };
287
+ /** E6: the session context policy (run-start actions, injection-side only). */
288
+ readonly contextPolicy?: ContextPolicy;
208
289
  readonly maxRetries?: number;
209
290
  /**
210
291
  * E1: loaded extensions — their tools join the registry (idempotently;
package/dist/session.js CHANGED
@@ -30,7 +30,10 @@
30
30
  import { EventLog, projectMessages, } from "@vincemakes/kiso-core";
31
31
  import { executionLedger } from "./ledger.js";
32
32
  import { denialResult } from "@vincemakes/kiso-core";
33
- import { estimateSummarySavings, KEEP_RECENT_ROUNDS, lastSummaryPoint, summarizeConversation, summaryBoundarySeq, } from "./summarize.js";
33
+ import { DROP_PLACEHOLDER, estimateSummarySavings, KEEP_RECENT_ROUNDS, KEEP_TOKENS_DEFAULT, lastSummaryPoint, MAX_SUMMARY_FAILURES, policyTriggerFromWindow, serializeCovered, SUMMARY_MAX_OUTPUT, summarizeConversation, summaryBoundarySeq, } from "./summarize.js";
34
+ import { canonicalizeUsage } from "./usage/canonical.js";
35
+ import { appendFileSync, mkdirSync } from "node:fs";
36
+ import { join } from "node:path";
34
37
  import { estimateTokens } from "@vincemakes/kiso-core";
35
38
  import { StaleWriterError } from "./store.js";
36
39
  import { composeHooks } from "./compose.js";
@@ -85,6 +88,9 @@ export class AgentSession {
85
88
  #pendingDurableApprovals = new Map();
86
89
  #pendingDurableUncertainties = new Map();
87
90
  #poisoned = null;
91
+ /** E6 (h): the circuit-breaker counter — consecutive auto-policy
92
+ * summary failures this session (a success resets it). */
93
+ #summaryFailures = 0;
88
94
  /** Permanently invalidate the session after a rejected disk write (round 1). */
89
95
  poison(reason) {
90
96
  if (this.#poisoned === null)
@@ -199,11 +205,17 @@ export class AgentSession {
199
205
  if (options.signal !== undefined && options.signal.aborted)
200
206
  throw cancelled();
201
207
  const events = this.log.all;
202
- const boundary = summaryBoundarySeq(events, keepRounds);
208
+ const boundary = summaryBoundarySeq(events, keepRounds, options.keepTokens);
203
209
  if (boundary === undefined)
204
210
  return null;
205
211
  const prevPoint = lastSummaryPoint(events);
212
+ // E6 (a): the summarizer's input is the covered range SERIALIZED to
213
+ // flat text — one guarded <conversation> user message — never the raw
214
+ // provider message array (the auto-T5-1 DSML garbage). The projected
215
+ // messages still feed the pre-call token estimate and the savings
216
+ // figure (estimate-only, never the model input).
206
217
  const covered = projectMessages(events.filter((e) => e.seq > prevPoint && e.seq <= boundary && e.type !== "summarized"));
218
+ const serializedInput = serializeCovered({ events, prevPoint, boundary });
207
219
  // W18: the indicator's pre-call data — rounds + the token estimate
208
220
  // are knowable BEFORE the adapter call; the summary itself is ONE
209
221
  // call with no fraction (kiso never invents a percentage here).
@@ -214,12 +226,30 @@ export class AgentSession {
214
226
  rounds: events.filter((e) => e.type === "user_input" && e.seq > prevPoint && e.seq <= boundary).length,
215
227
  tokens: estimateTokens(covered),
216
228
  });
217
- const summary = await summarizeConversation({
218
- adapter: this.#adapter,
219
- model: this.#config.model,
220
- messages: covered,
221
- ...(options.signal !== undefined ? { signal: options.signal } : {}),
222
- });
229
+ let summary;
230
+ let usage = null;
231
+ if (options.drop === true) {
232
+ // E6 — the crux drop arm: mechanical, no model call, the fixed
233
+ // placeholder replaces the covered range (experiment-only).
234
+ summary = DROP_PLACEHOLDER;
235
+ }
236
+ else {
237
+ const call = await summarizeConversation({
238
+ adapter: this.#adapter,
239
+ model: this.#config.model,
240
+ // E6 (a): ONE serialized user message — the DSML bug's
241
+ // raw-message array is structurally dead on this path.
242
+ messages: [{ role: "user", content: serializedInput }],
243
+ // E6 (g): the summary call ALWAYS carries the explicit
244
+ // output budget — 4,000 adapter maxTokens (a wire-level
245
+ // truncation is caught by the (b) required-section
246
+ // validation, never silently passed).
247
+ maxOutputTokens: SUMMARY_MAX_OUTPUT,
248
+ ...(options.signal !== undefined ? { signal: options.signal } : {}),
249
+ });
250
+ summary = call.text;
251
+ usage = call.usage;
252
+ }
223
253
  // The post-call boundary check: an abort that landed while the
224
254
  // adapter returned must NOT persist — "nothing happened".
225
255
  if (options.signal !== undefined && options.signal.aborted)
@@ -232,8 +262,86 @@ export class AgentSession {
232
262
  const records = this.#store.load(this.id);
233
263
  const runId = records.length > 0 ? records[records.length - 1].runId : "compact";
234
264
  await this.persist(runId, full);
265
+ // E6 — the honest accounting: the summary call's usage rides the
266
+ // trace ledger as a `kind: "summary"` line (the fifth ledger kind,
267
+ // observation-only — the request/run_end/crash vocabulary stands).
268
+ // The E5-era extraction could not see the call at all; both the
269
+ // manual /compact path and the auto policy land here. Soft-fail:
270
+ // a degraded ledger costs one stderr line, never the summary.
271
+ if (usage !== null) {
272
+ try {
273
+ const canonical = canonicalizeUsage(this.#config.provider ?? "adapter", usage);
274
+ const line = JSON.stringify({ kind: "summary", canonical }) + "\n";
275
+ mkdirSync(join(this.#store.root, "traces"), { recursive: true });
276
+ appendFileSync(join(this.#store.root, "traces", `${this.id}.jsonl`), line);
277
+ }
278
+ catch (err) {
279
+ console.error(`[kiso] summary usage ledger degraded (${err instanceof Error ? err.message : String(err)}); the summary call's cost is not recorded`);
280
+ }
281
+ }
235
282
  return { coversToSeq: boundary, summary, savedTokens: estimateSummarySavings(covered, summary) };
236
283
  }
284
+ /**
285
+ * E6 — the run-start context policy (candidate A + the crux drop arm).
286
+ * Called at the start of every FRESH run, BEFORE its user_input lands:
287
+ * when the policy is armed and the projected context crosses the
288
+ * trigger (and enough uncovered rounds exist — the keepRounds gate
289
+ * inside summarize), one `summarized` fact is persisted through the
290
+ * existing summarize() path. The boundary then rides the LAST recorded
291
+ * run and the run's first request projects the compressed view.
292
+ * Restraint: a short session never crosses the trigger — firing is a
293
+ * net loss by the E5-F1 accounting (a break that cannot amortize).
294
+ * Failure is swallowed: the compaction is an optimization — "nothing
295
+ * happened" must never break the user's turn.
296
+ */
297
+ async maybeApplyContextPolicy(signal) {
298
+ const policy = this.#config.contextPolicy;
299
+ if (policy === undefined)
300
+ return;
301
+ const mode = policy.drop ?? policy.summary;
302
+ if (mode === undefined)
303
+ return;
304
+ // E6 (g): the trigger is exactly one of triggerTokens (absolute)
305
+ // or windowTokens (window − POLICY_RESERVE, the product arming).
306
+ // The undefined guard is the belt: `projected <= undefined` is
307
+ // ALWAYS false, so a naive gate would fall THROUGH and fire
308
+ // unconditionally — an unresolved trigger must never fire.
309
+ const triggerTokens = mode.windowTokens !== undefined ? policyTriggerFromWindow(mode.windowTokens) : mode.triggerTokens;
310
+ if (triggerTokens === undefined)
311
+ return;
312
+ // E6 (h): the circuit breaker — after maxFailures consecutive
313
+ // summary failures the auto policy stands down for the rest of
314
+ // the session (a persistent failure — a broken provider, a
315
+ // hostile model — must never wedge the session into paying the
316
+ // summary call every run).
317
+ const maxFailures = mode.maxFailures ?? MAX_SUMMARY_FAILURES;
318
+ if (this.#summaryFailures >= maxFailures)
319
+ return;
320
+ if (estimateTokens(this.projected()) <= triggerTokens)
321
+ return;
322
+ try {
323
+ await this.summarize({
324
+ keepRounds: mode.keepRounds ?? KEEP_RECENT_ROUNDS,
325
+ // E6 (f): the keep budget is rounds AND tokens — the policy
326
+ // layer applies the 20k floor by default (small sessions are
327
+ // inert: the E5-F1 restraint, token-shaped).
328
+ keepTokens: mode.keepTokens ?? KEEP_TOKENS_DEFAULT,
329
+ ...(signal !== undefined ? { signal } : {}),
330
+ ...(policy.drop !== undefined ? { drop: true } : {}),
331
+ });
332
+ // A persisted fire resets the breaker — the failures were a
333
+ // transient blip, the budget starts fresh.
334
+ this.#summaryFailures = 0;
335
+ }
336
+ catch {
337
+ // the compaction failed — the session is unchanged and the run
338
+ // proceeds with the full context (the ADR-0044 "nothing
339
+ // happened" crash semantics, policy-shaped). The failure counts
340
+ // toward the breaker (both adapter failures and (b) rejections
341
+ // land here).
342
+ this.#summaryFailures += 1;
343
+ }
344
+ }
237
345
  // ── Phase D: approvals ───────────────────────────────────────────────
238
346
  /**
239
347
  * Pauses that still await a human decision (durable, survives restart).
@@ -16,28 +16,124 @@
16
16
  import type { AbortSignalLike, Adapter } from "@vincemakes/kiso-core";
17
17
  import type { Event } from "@vincemakes/kiso-core";
18
18
  import type { Message } from "@vincemakes/kiso-core";
19
+ import type { RawUsage } from "./usage/canonical.js";
19
20
  /** K (ADR-0044): the recent ROUNDS kept intact by /compact — a constant,
20
21
  * not a knob. The covered range ends just before the K-th most recent
21
22
  * round, so the model still reasons over the recent conversation. */
22
23
  export declare const KEEP_RECENT_ROUNDS = 4;
24
+ /** E6 (a) — the input-side DSML guard (the finding E6-F4/F5 follow-up):
25
+ * the guard sentence sits at the TOP of the system prompt (the BEFORE
26
+ * copy of the sandwich) AND again after the </conversation> block in the
27
+ * serialized input (the AFTER copy). The summarizer is a side-channel
28
+ * task — it must never continue the work, never touch tools, and only
29
+ * emit the summary text. */
30
+ export declare const SUMMARY_GUARD = "Only output the summary. Do not continue the conversation. Do not use any tools.";
31
+ /** The tool-result truncation ceiling in the serialized input: a huge
32
+ * result must not dominate the summary input, and the truncation is
33
+ * MARKED with the discarded character count — never silent. */
34
+ export declare const SUMMARY_RESULT_MAX_CHARS = 2000;
35
+ /**
36
+ * E6 (g) — the reserve arithmetic (the pre-registered numbers): the
37
+ * armed trigger is WINDOW − RESERVE, never a fixed low absolute (the
38
+ * e6probe's fixed 1300 fired 16-19× a session — the pathology the
39
+ * window math kills). The reserve is what ONE fire must buy back:
40
+ * the summary's own output budget (4,000), the kept-suffix token
41
+ * floor (20,000, item (f)), and the current run's in-flight context
42
+ * while the post-fire projection settles (8,000).
43
+ */
44
+ export declare const SUMMARY_MAX_OUTPUT = 4000;
45
+ export declare const KEEP_TOKENS_DEFAULT = 20000;
46
+ export declare const IN_FLIGHT_HEADROOM = 8000;
47
+ export declare const POLICY_RESERVE: number;
48
+ /** The reference context-window scale (the flash-family window); the
49
+ * env overrides. The default arming point is 120,000 − 32,000 =
50
+ * 88,000 — a post-fire projection (≥ 24k) can never re-cross it, so
51
+ * the session settles after one fire. */
52
+ export declare const DEFAULT_CONTEXT_WINDOW = 120000;
53
+ /** The armed trigger for a context window: window − POLICY_RESERVE. A
54
+ * window below the reserve arms a NEGATIVE trigger — the session
55
+ * never fires (the honest inert refusal: the window cannot hold even
56
+ * the post-fire projection, so the policy stays off, never clamped
57
+ * into pretending). */
58
+ export declare function policyTriggerFromWindow(windowTokens?: number): number;
59
+ /**
60
+ * E6 (h) — the circuit breaker: MAX_SUMMARY_FAILURES consecutive
61
+ * summary failures per session stand the auto policy down (no further
62
+ * auto-fire attempts; a success resets). Both adapter failures and the
63
+ * (b) validation rejections count — they throw through the policy's
64
+ * safe catch. A persistent summary failure (a broken provider, a
65
+ * hostile model) must never wedge the session into paying the call
66
+ * every run.
67
+ */
68
+ export declare const MAX_SUMMARY_FAILURES = 3;
69
+ /**
70
+ * E6 (a) — the covered range serialized to FLAT TEXT, one <conversation>
71
+ * block, role-labeled lines ([user]/[assistant]/[tool call name]/[tool
72
+ * result]), tool results truncated at SUMMARY_RESULT_MAX_CHARS with a
73
+ * "(… N more chars truncated)" marker, and the SUMMARY_GUARD sentence
74
+ * past the block's close. The model never sees the raw message array —
75
+ * the auto-T5-1 tool-call DSML garbage (the E6-F4/F5 signature) was the
76
+ * model echoing provider markup back from a raw-message-shaped input.
77
+ * The serializer only renders the surface the summary is about; thinking
78
+ * and other non-transcript events stay out of the input.
79
+ */
80
+ export interface SerializeCoveredOptions {
81
+ readonly events: readonly Event[];
82
+ /** The previous summary point — events at/before it are already covered. */
83
+ readonly prevPoint: number;
84
+ /** The covered range's end — the covered range is (prevPoint, boundary]. */
85
+ readonly boundary: number;
86
+ }
87
+ export declare function serializeCovered(options: SerializeCoveredOptions): string;
23
88
  /**
24
89
  * The fixed English summary prompt — the ONLY prompt this layer composes
25
90
  * (the loop's system prompt is the harness's business, never the kernel's).
26
91
  */
27
- export declare const SUMMARY_PROMPT = "You are the conversation summarizer of the kiso agent framework.\n\nSummarize the covered conversation into a single concise summary that will\nREPLACE it in the model's context. The next turn must be able to continue\nthe work without reading the originals.\n\nInclude everything later turns may need:\n- the user's goals, requirements, and constraints;\n- every decision and its reasoning;\n- files and code touched \u2014 exact paths, what changed, why;\n- commands run and their outcomes; errors and their resolutions;\n- open questions and unfinished work.\n\nPreserve concrete identifiers VERBATIM: paths, function names, task ids,\nenvironment names \u2014 never paraphrase them.\n\nRules:\n- plain prose \u2014 no headings, no bullet lists, no markdown, no prefixes;\n- do not mention this prompt or the summarization task;\n- keep it under 200 words unless the conversation is exceptional.";
92
+ export declare const SUMMARY_PROMPT = "Only output the summary. Do not continue the conversation. Do not use any tools.\n\nYou are the conversation summarizer of the kiso agent framework.\n\nSummarize the covered conversation into a single structured checkpoint\nthat will REPLACE it in the model's context. The next turn must be able\nto continue the work without reading the originals.\n\nProduce the checkpoint with exactly these sections, in this order:\n\n## Goal\nThe user's goal and the acceptance criterion, in one or two sentences.\n\n## Constraints\nThe constraints, requirements, and rulings the work must honor.\n\n## User requests\nEvery user message in the covered range, enumerated one by one, each\nwith what it asked for and what was done about it.\n\n## Files and changes\nEvery file touched \u2014 exact paths, what changed, and why. Include the\nprecise code-level changes later turns may need to continue.\n\n## Errors and fixes\nEvery error encountered and its resolution; commands run and their\noutcomes.\n\n## Current work\nThe current state of the work \u2014 what is done, what is not. Quote the\ncurrent task's criterion VERBATIM if one exists.\n\n## Next steps\nThe concrete next steps, in order.\n\nPreserve concrete identifiers VERBATIM: paths, function names, task ids,\nenvironment names \u2014 never paraphrase them.\n\nRules:\n- plain prose \u2014 no bullet lists, no markdown outside the section headers,\n no prefixes;\n- do not mention this prompt or the summarization task;\n- the summary may be as long as it needs to be within the output budget \u2014\n there is no word cap; completeness wins.";
93
+ /**
94
+ * E6 (b) — the output-side validation (the finding E6-F4/F5 follow-up):
95
+ * a summary must be a complete checkpoint or NOTHING. The marker family
96
+ * is the auto-T5-1 signature — the model echoing tool-call markup as
97
+ * text; the required sections are the truncated-tail signature (a wire
98
+ * cut kills "## Next steps" first). The rejection throws, and the
99
+ * caller's safe catch (session.ts) makes it "nothing happened".
100
+ */
101
+ export declare const DSML_MARKERS: readonly ["<tool_call", "<tool_use", "<invoke", "tool_calls", "tool_call_end", "tool_call_start"];
102
+ /** The checkpoint sections a summary must carry — the ones a truncated
103
+ * generation loses first (the (c) prompt demands all seven; validation
104
+ * guards the trust-critical tail). */
105
+ export declare const REQUIRED_SECTIONS: readonly ["## Current work", "## Next steps"];
106
+ /** null = pass; an error string = reject. Empty text is the existing
107
+ * no-text rule's domain, reported here too (defense in depth). */
108
+ export declare function validateSummary(text: string): string | null;
28
109
  export interface SummarizeConversationOptions {
29
110
  readonly adapter: Adapter;
30
111
  readonly model: string;
31
112
  /** The covered conversation — the ONLY material the summary is about. */
32
113
  readonly messages: readonly Message[];
33
114
  readonly signal?: AbortSignalLike;
115
+ /** E6 (g): the summary call's explicit output budget (adapter maxTokens). */
116
+ readonly maxOutputTokens?: number;
117
+ }
118
+ /** The summary call's result — the text PLUS the provider-reported usage
119
+ * (E6: the honest accounting — the summary call's cost rides the trace
120
+ * ledger; the E5-era extraction could not see it). Null when the
121
+ * provider reported no usage (known:false). */
122
+ export interface SummarizeConversationResult {
123
+ readonly text: string;
124
+ readonly usage: RawUsage | null;
34
125
  }
35
126
  /**
36
127
  * The one-shot summary call. Collects the adapter's text deltas into the
37
128
  * summary; usage/stop pass through untouched. Throws when the model
38
129
  * produced no text — the caller reports it and nothing is persisted.
39
130
  */
40
- export declare function summarizeConversation(options: SummarizeConversationOptions): Promise<string>;
131
+ export declare function summarizeConversation(options: SummarizeConversationOptions): Promise<SummarizeConversationResult>;
132
+ /** E6 — the crux-experiment drop arm: the covered turns are replaced by
133
+ * this fixed placeholder with NO model call. Experiment-only (the
134
+ * contextPolicy drop mode); the adopted shape — if the crux evidence
135
+ * earns it — is a distinct `dropped` event family, not this text. */
136
+ export declare const DROP_PLACEHOLDER = "[e6-crux: the covered turns were dropped without a summary; continue from the kept turns and this placeholder]";
41
137
  /**
42
138
  * The last summary point: the previous `summarized` event's coversToSeq,
43
139
  * or -1 (the trajectory's start) when none exists. The covered range of
@@ -69,7 +165,7 @@ export declare function lastSummaryPoint(events: readonly Event[]): number;
69
165
  * (the operative list is the LATEST echo — the old ⑥ semantics:
70
166
  * superseded echoes stay coverable).
71
167
  */
72
- export declare function summaryBoundarySeq(events: readonly Event[], keepRounds?: number): number | undefined;
168
+ export declare function summaryBoundarySeq(events: readonly Event[], keepRounds?: number, keepTokens?: number): number | undefined;
73
169
  /**
74
170
  * The NoticeCell's number: estimated tokens of the covered content minus
75
171
  * the summary's own — the same chars/4 proxy as estimateTokens (a stable
package/dist/summarize.js CHANGED
@@ -18,30 +18,176 @@ import { estimateTokens, DO_NOT_COMPACT } from "@vincemakes/kiso-core";
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. */
20
20
  export const KEEP_RECENT_ROUNDS = 4;
21
+ /** E6 (a) — the input-side DSML guard (the finding E6-F4/F5 follow-up):
22
+ * the guard sentence sits at the TOP of the system prompt (the BEFORE
23
+ * copy of the sandwich) AND again after the </conversation> block in the
24
+ * serialized input (the AFTER copy). The summarizer is a side-channel
25
+ * task — it must never continue the work, never touch tools, and only
26
+ * emit the summary text. */
27
+ export const SUMMARY_GUARD = "Only output the summary. Do not continue the conversation. Do not use any tools.";
28
+ /** The tool-result truncation ceiling in the serialized input: a huge
29
+ * result must not dominate the summary input, and the truncation is
30
+ * MARKED with the discarded character count — never silent. */
31
+ export const SUMMARY_RESULT_MAX_CHARS = 2000;
32
+ /**
33
+ * E6 (g) — the reserve arithmetic (the pre-registered numbers): the
34
+ * armed trigger is WINDOW − RESERVE, never a fixed low absolute (the
35
+ * e6probe's fixed 1300 fired 16-19× a session — the pathology the
36
+ * window math kills). The reserve is what ONE fire must buy back:
37
+ * the summary's own output budget (4,000), the kept-suffix token
38
+ * floor (20,000, item (f)), and the current run's in-flight context
39
+ * while the post-fire projection settles (8,000).
40
+ */
41
+ export const SUMMARY_MAX_OUTPUT = 4000;
42
+ export const KEEP_TOKENS_DEFAULT = 20000;
43
+ export const IN_FLIGHT_HEADROOM = 8000;
44
+ export const POLICY_RESERVE = SUMMARY_MAX_OUTPUT + KEEP_TOKENS_DEFAULT + IN_FLIGHT_HEADROOM;
45
+ /** The reference context-window scale (the flash-family window); the
46
+ * env overrides. The default arming point is 120,000 − 32,000 =
47
+ * 88,000 — a post-fire projection (≥ 24k) can never re-cross it, so
48
+ * the session settles after one fire. */
49
+ export const DEFAULT_CONTEXT_WINDOW = 120000;
50
+ /** The armed trigger for a context window: window − POLICY_RESERVE. A
51
+ * window below the reserve arms a NEGATIVE trigger — the session
52
+ * never fires (the honest inert refusal: the window cannot hold even
53
+ * the post-fire projection, so the policy stays off, never clamped
54
+ * into pretending). */
55
+ export function policyTriggerFromWindow(windowTokens = DEFAULT_CONTEXT_WINDOW) {
56
+ return windowTokens - POLICY_RESERVE;
57
+ }
58
+ /**
59
+ * E6 (h) — the circuit breaker: MAX_SUMMARY_FAILURES consecutive
60
+ * summary failures per session stand the auto policy down (no further
61
+ * auto-fire attempts; a success resets). Both adapter failures and the
62
+ * (b) validation rejections count — they throw through the policy's
63
+ * safe catch. A persistent summary failure (a broken provider, a
64
+ * hostile model) must never wedge the session into paying the call
65
+ * every run.
66
+ */
67
+ export const MAX_SUMMARY_FAILURES = 3;
68
+ export function serializeCovered(options) {
69
+ const { events, prevPoint, boundary } = options;
70
+ const lines = ["<conversation>"];
71
+ // E6 (d) (the order's R4): the old summary texts are RETAINED CONTEXT —
72
+ // the durable record of the earlier ranges. They render first, labeled
73
+ // do-not-re-summarize: the summarizer must know what the earlier
74
+ // summaries covered, but never fold them into the new checkpoint.
75
+ const retained = events.filter((e) => e.type === "summarized" && e.coversToSeq <= prevPoint);
76
+ if (retained.length > 0) {
77
+ lines.push("[retained context — do not re-summarize]");
78
+ for (const r of retained)
79
+ lines.push(`[summary covers to seq ${r.coversToSeq}] ${r.summary}`);
80
+ lines.push("[end retained context]");
81
+ }
82
+ for (const ev of events) {
83
+ if (ev.seq <= prevPoint || ev.seq > boundary || ev.type === "summarized")
84
+ continue;
85
+ switch (ev.type) {
86
+ case "user_input":
87
+ lines.push(`[user] ${ev.content}`);
88
+ break;
89
+ case "text_delta":
90
+ lines.push(`[assistant] ${ev.text}`);
91
+ break;
92
+ case "tool_call_end":
93
+ lines.push(`[tool call ${ev.name}] ${JSON.stringify(ev.input ?? null)}`);
94
+ break;
95
+ case "tool_result": {
96
+ const content = String(ev.content ?? "");
97
+ if (content.length > SUMMARY_RESULT_MAX_CHARS) {
98
+ const rest = content.length - SUMMARY_RESULT_MAX_CHARS;
99
+ lines.push(`[tool result] ${content.slice(0, SUMMARY_RESULT_MAX_CHARS)}… (${rest.toLocaleString("en-US")} more chars truncated)`);
100
+ }
101
+ else {
102
+ lines.push(`[tool result] ${content}`);
103
+ }
104
+ break;
105
+ }
106
+ default:
107
+ break; // thinking and the rest never enter the transcript surface
108
+ }
109
+ }
110
+ lines.push("</conversation>", "", SUMMARY_GUARD);
111
+ return lines.join("\n");
112
+ }
21
113
  /**
22
114
  * The fixed English summary prompt — the ONLY prompt this layer composes
23
115
  * (the loop's system prompt is the harness's business, never the kernel's).
24
116
  */
25
- export const SUMMARY_PROMPT = `You are the conversation summarizer of the kiso agent framework.
117
+ export const SUMMARY_PROMPT = `${SUMMARY_GUARD}
118
+
119
+ You are the conversation summarizer of the kiso agent framework.
120
+
121
+ Summarize the covered conversation into a single structured checkpoint
122
+ that will REPLACE it in the model's context. The next turn must be able
123
+ to continue the work without reading the originals.
124
+
125
+ Produce the checkpoint with exactly these sections, in this order:
126
+
127
+ ## Goal
128
+ The user's goal and the acceptance criterion, in one or two sentences.
129
+
130
+ ## Constraints
131
+ The constraints, requirements, and rulings the work must honor.
132
+
133
+ ## User requests
134
+ Every user message in the covered range, enumerated one by one, each
135
+ with what it asked for and what was done about it.
26
136
 
27
- Summarize the covered conversation into a single concise summary that will
28
- REPLACE it in the model's context. The next turn must be able to continue
29
- the work without reading the originals.
137
+ ## Files and changes
138
+ Every file touched exact paths, what changed, and why. Include the
139
+ precise code-level changes later turns may need to continue.
30
140
 
31
- Include everything later turns may need:
32
- - the user's goals, requirements, and constraints;
33
- - every decision and its reasoning;
34
- - files and code touched — exact paths, what changed, why;
35
- - commands run and their outcomes; errors and their resolutions;
36
- - open questions and unfinished work.
141
+ ## Errors and fixes
142
+ Every error encountered and its resolution; commands run and their
143
+ outcomes.
144
+
145
+ ## Current work
146
+ The current state of the work — what is done, what is not. Quote the
147
+ current task's criterion VERBATIM if one exists.
148
+
149
+ ## Next steps
150
+ The concrete next steps, in order.
37
151
 
38
152
  Preserve concrete identifiers VERBATIM: paths, function names, task ids,
39
153
  environment names — never paraphrase them.
40
154
 
41
155
  Rules:
42
- - plain prose — no headings, no bullet lists, no markdown, no prefixes;
156
+ - plain prose — no bullet lists, no markdown outside the section headers,
157
+ no prefixes;
43
158
  - do not mention this prompt or the summarization task;
44
- - keep it under 200 words unless the conversation is exceptional.`;
159
+ - the summary may be as long as it needs to be within the output budget
160
+ there is no word cap; completeness wins.`;
161
+ /**
162
+ * E6 (b) — the output-side validation (the finding E6-F4/F5 follow-up):
163
+ * a summary must be a complete checkpoint or NOTHING. The marker family
164
+ * is the auto-T5-1 signature — the model echoing tool-call markup as
165
+ * text; the required sections are the truncated-tail signature (a wire
166
+ * cut kills "## Next steps" first). The rejection throws, and the
167
+ * caller's safe catch (session.ts) makes it "nothing happened".
168
+ */
169
+ export const DSML_MARKERS = ["<tool_call", "<tool_use", "<invoke", "tool_calls", "tool_call_end", "tool_call_start"];
170
+ /** The checkpoint sections a summary must carry — the ones a truncated
171
+ * generation loses first (the (c) prompt demands all seven; validation
172
+ * guards the trust-critical tail). */
173
+ export const REQUIRED_SECTIONS = ["## Current work", "## Next steps"];
174
+ /** null = pass; an error string = reject. Empty text is the existing
175
+ * no-text rule's domain, reported here too (defense in depth). */
176
+ export function validateSummary(text) {
177
+ const trimmed = text.trim();
178
+ if (trimmed === "")
179
+ return "the summary is empty";
180
+ const lower = trimmed.toLowerCase();
181
+ for (const marker of DSML_MARKERS) {
182
+ if (lower.includes(marker))
183
+ return `the summary carries a tool-call marker (${marker}) — reject`;
184
+ }
185
+ for (const section of REQUIRED_SECTIONS) {
186
+ if (!trimmed.includes(section))
187
+ return `the summary is missing the required section ${section} — a truncated or incomplete checkpoint`;
188
+ }
189
+ return null;
190
+ }
45
191
  /**
46
192
  * The one-shot summary call. Collects the adapter's text deltas into the
47
193
  * summary; usage/stop pass through untouched. Throws when the model
@@ -50,21 +196,38 @@ Rules:
50
196
  export async function summarizeConversation(options) {
51
197
  const { adapter, model, messages } = options;
52
198
  let text = "";
199
+ let usage = null;
53
200
  for await (const ev of adapter.stream({
54
201
  model,
55
202
  messages,
56
203
  systemPrompt: SUMMARY_PROMPT,
57
204
  ...(options.signal !== undefined ? { signal: options.signal } : {}),
205
+ ...(options.maxOutputTokens !== undefined ? { maxTokens: options.maxOutputTokens } : {}),
58
206
  })) {
59
207
  if (ev.type === "text_delta")
60
208
  text += ev.text;
209
+ // The LAST usage event is the call's (a turn reports usage once).
210
+ if (ev.type === "usage" && ev.known) {
211
+ usage = { inputTokens: ev.inputTokens, outputTokens: ev.outputTokens, cacheRead: ev.cacheRead, cacheWrite: ev.cacheWrite };
212
+ }
61
213
  }
62
214
  const trimmed = text.trim();
63
215
  if (trimmed === "") {
64
216
  throw new Error("the summary call produced no text");
65
217
  }
66
- return trimmed;
218
+ // E6 (b): a non-checkpoint summary is an honest failure — throw, the
219
+ // caller reports it, nothing is persisted (the auto-T5-1 regression).
220
+ const invalid = validateSummary(trimmed);
221
+ if (invalid !== null) {
222
+ throw new Error(`the summary call produced an invalid summary: ${invalid}`);
223
+ }
224
+ return { text: trimmed, usage };
67
225
  }
226
+ /** E6 — the crux-experiment drop arm: the covered turns are replaced by
227
+ * this fixed placeholder with NO model call. Experiment-only (the
228
+ * contextPolicy drop mode); the adopted shape — if the crux evidence
229
+ * earns it — is a distinct `dropped` event family, not this text. */
230
+ export const DROP_PLACEHOLDER = "[e6-crux: the covered turns were dropped without a summary; continue from the kept turns and this placeholder]";
68
231
  /**
69
232
  * The last summary point: the previous `summarized` event's coversToSeq,
70
233
  * or -1 (the trajectory's start) when none exists. The covered range of
@@ -78,6 +241,23 @@ export function lastSummaryPoint(events) {
78
241
  }
79
242
  return prev;
80
243
  }
244
+ /** The chars/4 token proxy for a single EVENT (the same convention as
245
+ * estimateTokens, event-shaped — the (f) keep-floor walk needs the kept
246
+ * suffix's tokens without projecting it). */
247
+ function estimateEventTokens(ev) {
248
+ switch (ev.type) {
249
+ case "user_input":
250
+ return Math.ceil(ev.content.length / 4);
251
+ case "text_delta":
252
+ return Math.ceil(ev.text.length / 4);
253
+ case "tool_call_end":
254
+ return Math.ceil(JSON.stringify(ev.input ?? null).length / 4) + 20;
255
+ case "tool_result":
256
+ return Math.ceil(String(ev.content ?? "").length / 4);
257
+ default:
258
+ return 0;
259
+ }
260
+ }
81
261
  /**
82
262
  * The covered range's end: the seq of the event just before the
83
263
  * keepRounds-th most recent user_input AFTER the last summary point —
@@ -103,7 +283,7 @@ export function lastSummaryPoint(events) {
103
283
  * (the operative list is the LATEST echo — the old ⑥ semantics:
104
284
  * superseded echoes stay coverable).
105
285
  */
106
- export function summaryBoundarySeq(events, keepRounds = KEEP_RECENT_ROUNDS) {
286
+ export function summaryBoundarySeq(events, keepRounds = KEEP_RECENT_ROUNDS, keepTokens) {
107
287
  const prevPoint = lastSummaryPoint(events);
108
288
  const uncoveredInputs = [];
109
289
  for (const ev of events) {
@@ -114,6 +294,36 @@ export function summaryBoundarySeq(events, keepRounds = KEEP_RECENT_ROUNDS) {
114
294
  return undefined;
115
295
  const firstUncovered = uncoveredInputs[0];
116
296
  let boundary = uncoveredInputs[uncoveredInputs.length - keepRounds] - 1;
297
+ // E6 (f): the keep budget is rounds AND tokens. A kept suffix smaller
298
+ // than keepTokens is a break the session cannot amortize (the E5-F1
299
+ // accounting) — walk the boundary back (keep more) until the kept
300
+ // events clear the floor. The walk picks the smallest kept suffix
301
+ // meeting it: per-event cumulative tokens, one pass. A floor the whole
302
+ // uncovered range cannot meet → nothing to compact (the policy is
303
+ // inert on small sessions — the token-shaped restraint).
304
+ if (keepTokens !== undefined && keepTokens > 0) {
305
+ const prefixTokens = [0];
306
+ let total = 0;
307
+ for (const ev of events) {
308
+ total += estimateEventTokens(ev);
309
+ prefixTokens.push(total);
310
+ }
311
+ const keptTokens = (b) => total - prefixTokens[b + 1];
312
+ let floorBoundary;
313
+ for (let i = uncoveredInputs.length - 1; i >= 0; i--) {
314
+ const b = uncoveredInputs[i] - 1;
315
+ if (keptTokens(b) >= keepTokens) {
316
+ floorBoundary = b;
317
+ break;
318
+ }
319
+ }
320
+ // b < firstUncovered covers no whole round (or the empty residue) —
321
+ // the honest nothing-to-compact.
322
+ if (floorBoundary === undefined || floorBoundary < firstUncovered)
323
+ return undefined;
324
+ if (floorBoundary < boundary)
325
+ boundary = floorBoundary;
326
+ }
117
327
  // The protected pullback applies ONCE on the base range (⑥); the
118
328
  // straddle pullback recomputes against the SHRINKING range below it.
119
329
  const protectedBoundary = latestProtectedBoundary(events, prevPoint, boundary);
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@vincemakes/kiso-runtime",
3
- "version": "0.2.1",
4
- "description": "kiso runtime \u2014 durable multi-turn agent sessions: AgentDefinition, AgentRuntime, AgentSession, Run, append-only JSONL store.",
3
+ "version": "0.5.0",
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",
7
7
  "exports": {
@@ -25,11 +25,11 @@
25
25
  "test": "vitest run"
26
26
  },
27
27
  "dependencies": {
28
- "@vincemakes/kiso-core": "0.2.0"
28
+ "@vincemakes/kiso-core": "0.5.0"
29
29
  },
30
30
  "peerDependencies": {
31
- "@vincemakes/kiso-provider-anthropic": "0.2.0",
32
- "@vincemakes/kiso-provider-openai": "0.2.0"
31
+ "@vincemakes/kiso-provider-anthropic": "0.5.0",
32
+ "@vincemakes/kiso-provider-openai": "0.5.0"
33
33
  },
34
34
  "peerDependenciesMeta": {
35
35
  "@vincemakes/kiso-provider-anthropic": {
@@ -40,7 +40,7 @@
40
40
  }
41
41
  },
42
42
  "devDependencies": {
43
- "@vincemakes/kiso-evals": "0.2.0",
43
+ "@vincemakes/kiso-evals": "0.5.0",
44
44
  "@types/node": "^26.1.2",
45
45
  "typescript": "^5.7.2",
46
46
  "vitest": "^3.0.0"