@vincemakes/kiso-runtime 0.1.34 → 0.1.36

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/index.d.ts CHANGED
@@ -5,5 +5,6 @@ export * from "./recovery.js";
5
5
  export * from "./compose.js";
6
6
  export * from "./summarize.js";
7
7
  export * from "./store.js";
8
+ export * from "./ledger.js";
8
9
  export * from "./extensions.js";
9
10
  export * from "./trust.js";
package/dist/index.js CHANGED
@@ -5,5 +5,6 @@ export * from "./recovery.js";
5
5
  export * from "./compose.js";
6
6
  export * from "./summarize.js";
7
7
  export * from "./store.js";
8
+ export * from "./ledger.js";
8
9
  export * from "./extensions.js";
9
10
  export * from "./trust.js";
@@ -0,0 +1,45 @@
1
+ /**
2
+ * L2 — the execution ledger: exactly-once side effects from the event log.
3
+ *
4
+ * Every tool execution writes `tool_execution_started` before the handler
5
+ * and `tool_execution_succeeded` / `tool_execution_failed` after
6
+ * (kernel/loop.ts). From those events alone — no second store — this module
7
+ * answers the recovery questions:
8
+ *
9
+ * 1. What is the durable status of execution X? (`executionLedger`)
10
+ * 2. What is the latest execution of call Y? (`executionForCallId`)
11
+ *
12
+ * IDENTITY (Area 3): the ledger is keyed by `executionId` — a persistent,
13
+ * framework-generated id unique per log (one per started event). The
14
+ * provider's `callId` is correlation only and may repeat; two logical calls
15
+ * with identical (name, input) are two executions.
16
+ *
17
+ * Status derivation:
18
+ * started, no terminal event yet → "uncertain" (interrupted: human)
19
+ * succeeded → "succeeded" (confirmed, never re-run)
20
+ * failed (any) → "failed" (a complete receipt IS
21
+ * the outcome — ruling #12 / ADR-0038;
22
+ * safeToRetry stays on the event for
23
+ * history, it no longer feeds status)
24
+ * resolved "rerun" → "rerun" (human cleared it)
25
+ * resolved "abandoned" → "abandoned" (human killed it)
26
+ */
27
+ import type { Event } from "@vincemakes/kiso-core";
28
+ export type ExecutionStatus = "uncertain" | "succeeded" | "failed" | "rerun" | "abandoned";
29
+ export interface ExecutionRecord {
30
+ readonly executionId: string;
31
+ readonly callId: string;
32
+ readonly name: string;
33
+ readonly input: Readonly<Record<string, unknown>>;
34
+ readonly status: ExecutionStatus;
35
+ /** Present when `status` is "succeeded" — the durable result to replay. */
36
+ readonly result?: {
37
+ readonly content: string;
38
+ readonly isError: false;
39
+ };
40
+ readonly error?: string;
41
+ }
42
+ /** executionId → durable status, rebuilt purely from events (ADR-0002). */
43
+ export declare function executionLedger(events: readonly Event[]): Map<string, ExecutionRecord>;
44
+ /** The LATEST execution record for a provider call id (correlation only). */
45
+ export declare function executionForCallId(events: readonly Event[], callId: string): ExecutionRecord | undefined;
package/dist/ledger.js ADDED
@@ -0,0 +1,90 @@
1
+ /**
2
+ * L2 — the execution ledger: exactly-once side effects from the event log.
3
+ *
4
+ * Every tool execution writes `tool_execution_started` before the handler
5
+ * and `tool_execution_succeeded` / `tool_execution_failed` after
6
+ * (kernel/loop.ts). From those events alone — no second store — this module
7
+ * answers the recovery questions:
8
+ *
9
+ * 1. What is the durable status of execution X? (`executionLedger`)
10
+ * 2. What is the latest execution of call Y? (`executionForCallId`)
11
+ *
12
+ * IDENTITY (Area 3): the ledger is keyed by `executionId` — a persistent,
13
+ * framework-generated id unique per log (one per started event). The
14
+ * provider's `callId` is correlation only and may repeat; two logical calls
15
+ * with identical (name, input) are two executions.
16
+ *
17
+ * Status derivation:
18
+ * started, no terminal event yet → "uncertain" (interrupted: human)
19
+ * succeeded → "succeeded" (confirmed, never re-run)
20
+ * failed (any) → "failed" (a complete receipt IS
21
+ * the outcome — ruling #12 / ADR-0038;
22
+ * safeToRetry stays on the event for
23
+ * history, it no longer feeds status)
24
+ * resolved "rerun" → "rerun" (human cleared it)
25
+ * resolved "abandoned" → "abandoned" (human killed it)
26
+ */
27
+ /** executionId → durable status, rebuilt purely from events (ADR-0002). */
28
+ export function executionLedger(events) {
29
+ const ledger = new Map();
30
+ for (const ev of events) {
31
+ switch (ev.type) {
32
+ case "tool_execution_started":
33
+ ledger.set(ev.executionId, {
34
+ executionId: ev.executionId,
35
+ callId: ev.callId,
36
+ name: ev.name,
37
+ input: ev.input,
38
+ status: "uncertain",
39
+ });
40
+ break;
41
+ case "tool_execution_succeeded": {
42
+ const prior = ledger.get(ev.executionId);
43
+ if (prior) {
44
+ ledger.set(ev.executionId, { ...prior, status: "succeeded", result: ev.result });
45
+ }
46
+ break;
47
+ }
48
+ case "tool_execution_failed": {
49
+ const prior = ledger.get(ev.executionId);
50
+ if (prior) {
51
+ ledger.set(ev.executionId, {
52
+ ...prior,
53
+ // ruling #12 (ADR-0038): a complete receipt IS the outcome —
54
+ // failed is "failed", never "uncertain"; uncertainty
55
+ // belongs to the crash window alone (started, no receipt).
56
+ status: "failed",
57
+ ...(ev.error !== undefined ? { error: ev.error } : {}),
58
+ });
59
+ }
60
+ break;
61
+ }
62
+ case "tool_execution_resolved": {
63
+ const prior = ledger.get(ev.executionId);
64
+ if (prior) {
65
+ ledger.set(ev.executionId, {
66
+ ...prior,
67
+ status: ev.resolution === "rerun" ? "rerun" : "abandoned",
68
+ });
69
+ }
70
+ break;
71
+ }
72
+ default:
73
+ break;
74
+ }
75
+ }
76
+ return ledger;
77
+ }
78
+ /** The LATEST execution record for a provider call id (correlation only). */
79
+ export function executionForCallId(events, callId) {
80
+ const ledger = executionLedger(events);
81
+ let found;
82
+ for (const ev of events) {
83
+ if (ev.type !== "tool_execution_started")
84
+ continue;
85
+ if (ev.callId !== callId)
86
+ continue;
87
+ found = ledger.get(ev.executionId);
88
+ }
89
+ return found;
90
+ }
package/dist/run.js CHANGED
@@ -176,7 +176,7 @@ export class Run {
176
176
  if (ev.seq > baseSeq)
177
177
  await this.#session.persist(this.runId, ev);
178
178
  };
179
- for await (const ev of this.#recover(log, signal, lastOpen.events)) {
179
+ for await (const ev of this.#recover(log, signal, lastOpen.events, approvalChain, this.#config.hooks)) {
180
180
  await persist(ev);
181
181
  yield ev;
182
182
  }
@@ -242,10 +242,211 @@ export class Run {
242
242
  * re-approved); a denial writes its tool result; a succeeded/failed
243
243
  * execution whose tool_result never landed is completed from the
244
244
  * receipt. Undecided requests pause and await approve().
245
+ *
246
+ * R-E 0.1.43 (Gap A): a committed turn's tool_call_end with no durable
247
+ * decision and no execution is UNDECIDED — recovery re-enters it into
248
+ * the approval pipeline before anything else (only a durable
249
+ * permission_decided authorizes an effect).
245
250
  */
246
- async *#recover(log, signal, scope) {
247
- const requests = scope.filter((e) => e.type === "permission_requested");
251
+ async *#recover(log, signal, scope, approvalChain, hooks) {
252
+ // ── Gap B: the tail draft is abandoned — never committed history ──
253
+ // "A model output suffix without a committed stop is an incomplete
254
+ // draft and must never become committed provider history." The
255
+ // resume appends the abandon marker FIRST: it voids the range after
256
+ // the last committed boundary (stop / user_input / terminal /
257
+ // compaction / summarized — or an earlier marker), so the projection
258
+ // excludes the draft and a call inside it is never executed (the
259
+ // boundary clause: the two Gaps divide at the stop). The audit bytes
260
+ // stay; a marker is kernel-exclusive (the AdapterEvent whitelist).
261
+ // Idempotent: an already-voided draft has a marker as its last
262
+ // boundary — the detection finds no output after it, and the
263
+ // recovered events (decided/started/result) are no draft.
264
+ const boundary = [...scope].reverse().find((e) => e.type === "stop" ||
265
+ e.type === "user_input" ||
266
+ e.type === "terminal" ||
267
+ e.type === "microcompacted" ||
268
+ e.type === "compacted" ||
269
+ e.type === "summarized" ||
270
+ e.type === "model_output_abandoned");
271
+ if (boundary !== undefined) {
272
+ // R-E 0.1.44 (verified against sentence 2): the draft detection
273
+ // stays text-only — a bare tool-call suffix [tool_call_end,
274
+ // permission_requested] with no text is the legal approval-panel
275
+ // pause (the Area 2 contract: the pending request binds and
276
+ // executes, pair-closed — the extensions-e2e gate). The finding's
277
+ // shapes all carry text, and the VOID SCOPE (what the marker
278
+ // covers) is the type filter at project.ts: model output dies
279
+ // with the draft, the framework's facts never do.
280
+ const draft = scope.some((e) => (e.type === "text_delta" || e.type === "thinking") && e.seq > boundary.seq);
281
+ if (draft) {
282
+ yield log.append({
283
+ type: "model_output_abandoned",
284
+ voidFromSeq: boundary.seq,
285
+ reason: "a model output suffix without a committed stop — abandoned on resume",
286
+ });
287
+ }
288
+ }
289
+ // The invocation's framework identity (R-E 0.1.43): the
290
+ // tool_call_end's seq — carried by new logs, derived by
291
+ // callId+proximity for old ones (the last such call before the seq).
292
+ const callSeqOf = (callId, before) => {
293
+ let seq;
294
+ for (const e of scope) {
295
+ if (e.type === "tool_call_end" && e.callId === callId && e.seq < before)
296
+ seq = e.seq;
297
+ }
298
+ return seq;
299
+ };
300
+ // ── Gap A: a committed turn's UNDECIDED invocation ────────────────
301
+ // A durable stop with a bare tool_call_end (no decision, no
302
+ // execution) re-enters the approval pipeline: the composed chain
303
+ // decides — allow → durable permission_decided (decidedBy
304
+ // faithfully) + the persisted execution; deny → decided + the
305
+ // denial result; ask/all-abstain → permission_requested (the
306
+ // requests pass below announces it and waits for the human). No
307
+ // guessing, no inheriting, no retro-authorization — re-decide.
308
+ // A durable POLICY verdict (E1: decidedBy set) newer than the call
309
+ // binds it — the chain never re-runs for a decided invocation.
310
+ const gapAsks = [];
311
+ for (const call of scope) {
312
+ if (call.type !== "tool_call_end")
313
+ continue;
314
+ // The boundary clause (the directive): a call whose turn has no
315
+ // legal stop is a DRAFT's call — Gap B voids it (never executed,
316
+ // never in the provider projection); this stage never touches it.
317
+ // The two Gaps divide at the stop; no mixing.
318
+ const turnEnd = scope.find((e) => e.type === "user_input" && e.seq > call.seq)?.seq ?? Number.POSITIVE_INFINITY;
319
+ const turnStop = scope.some((e) => e.type === "stop" && e.seq > call.seq && e.seq < turnEnd);
320
+ if (!turnStop)
321
+ continue;
322
+ // The requests pass below owns request-tracked invocations (it
323
+ // binds the stored request by decisionId, or pauses for the
324
+ // human) — Gap A must never re-decide over a stored
325
+ // permission_requested. "Only a durable permission_decided
326
+ // authorizes an effect": a pending request is not a decision.
327
+ const hasRequest = log.all.some((e) => e.type === "permission_requested" && e.callId === call.callId && e.seq > call.seq);
328
+ if (hasRequest)
329
+ continue;
330
+ const decided = log.all.find((e) => e.type === "permission_decided" && e.callId === call.callId && e.seq > call.seq && e.decidedBy !== undefined);
331
+ const hasExecution = log.all.some((e) => e.type === "tool_execution_started" && e.callId === call.callId && e.seq > call.seq);
332
+ const hasResult = log.all.some((e) => e.type === "tool_result" && e.callId === call.callId && e.seq > call.seq);
333
+ if (hasResult)
334
+ continue; // closed — nothing to fill
335
+ if (decided !== undefined) {
336
+ // E1: the durable verdict speaks for the call — apply it
337
+ // without re-running the chain.
338
+ if (signal.aborted)
339
+ return;
340
+ if (decided.decision === "approved" && !hasExecution) {
341
+ yield* this.#executePersisted(call.callId, call.name, call.input ?? {}, call.seq, signal);
342
+ }
343
+ else if (!hasResult) {
344
+ yield* this.#denialResult(call.callId, decided.reason ?? "denied by user", call.seq);
345
+ }
346
+ continue;
347
+ }
348
+ // UNDECIDED — re-enter the approval pipeline with the LIVE
349
+ // decision order (loop.ts decideCall): the composed chain
350
+ // first; only when no chain exists do the hooks' onPreTool
351
+ // speak (defer → ask, deny → deny, allow → allow); no policies
352
+ // at all → the kernel's default allow. Same semantics as the
353
+ // live path — a defer policy must not collapse into an
354
+ // auto-allow on resume. A throwing chain counts as ask: it
355
+ // speaks, never silently (the live parity).
356
+ const payload = { callId: call.callId, name: call.name, input: call.input ?? {} };
357
+ // The chain's PolicyCall carries name+input only — callId is the
358
+ // framework's, the hook's is the provider-facing ToolCallPayload.
359
+ const policyCall = { name: payload.name, input: payload.input };
360
+ let verdict;
361
+ try {
362
+ if (approvalChain !== undefined) {
363
+ const chainVerdict = await abortable(Promise.resolve(approvalChain.decide(policyCall, { signal, sessionId: this.#session.id })), signal);
364
+ if (chainVerdict === ABORTED)
365
+ return;
366
+ verdict = chainVerdict;
367
+ }
368
+ }
369
+ catch {
370
+ verdict = { action: "ask" };
371
+ }
372
+ if (verdict === undefined && hooks?.onPreTool !== undefined) {
373
+ const decision = await abortable(Promise.resolve(hooks.onPreTool(payload, { sessionId: this.#session.id })), signal);
374
+ if (decision === ABORTED)
375
+ return;
376
+ if (decision.action === "defer")
377
+ verdict = { action: "ask" };
378
+ else if (decision.action !== "allow")
379
+ verdict = { action: "deny", reason: decision.reason ?? "denied" };
380
+ else
381
+ verdict = { action: "allow" };
382
+ }
383
+ if (verdict === undefined)
384
+ verdict = { action: "allow" }; // no policies — the kernel's default allow
385
+ const decisionId = `d-${log.all.length + 1}`;
386
+ if (verdict.action === "allow") {
387
+ yield log.append({
388
+ type: "permission_decided",
389
+ decisionId,
390
+ callId: call.callId,
391
+ invocationSeq: call.seq,
392
+ decision: "approved",
393
+ ...("decidedBy" in verdict ? { decidedBy: verdict.decidedBy } : {}),
394
+ });
395
+ if (signal.aborted)
396
+ return;
397
+ yield* this.#executePersisted(call.callId, call.name, call.input ?? {}, call.seq, signal);
398
+ }
399
+ else if (verdict.action === "deny") {
400
+ yield log.append({
401
+ type: "permission_decided",
402
+ decisionId,
403
+ callId: call.callId,
404
+ invocationSeq: call.seq,
405
+ decision: "denied",
406
+ ...("reason" in verdict && verdict.reason !== undefined ? { reason: verdict.reason } : {}),
407
+ ...("decidedBy" in verdict ? { decidedBy: verdict.decidedBy } : {}),
408
+ });
409
+ yield* this.#denialResult(call.callId, ("reason" in verdict && verdict.reason) || "denied", call.seq);
410
+ }
411
+ else {
412
+ // ask / all-abstain — the requests pass below announces the
413
+ // stored request and waits for the human.
414
+ const appended = log.append({
415
+ type: "permission_requested",
416
+ decisionId,
417
+ callId: call.callId,
418
+ invocationSeq: call.seq,
419
+ name: call.name,
420
+ input: call.input ?? {},
421
+ });
422
+ gapAsks.push(appended);
423
+ yield appended;
424
+ }
425
+ }
426
+ const requests = [...scope.filter((e) => e.type === "permission_requested"), ...gapAsks];
248
427
  for (const pending of requests) {
428
+ // R-E 0.1.43: the writes below carry the invocation's framework
429
+ // identity — the request's own, or the callId+proximity
430
+ // fallback for old logs (the compat contract).
431
+ const invocationSeq = pending.invocationSeq ?? callSeqOf(pending.callId, pending.seq);
432
+ // R-E 0.1.44 (sentence 3): a stored request whose invocation is
433
+ // VOIDED is expired — never re-presented, never executed (the
434
+ // dead-run expiry precedent above). The void ranges come from
435
+ // log.all — the marker THIS recovery appended is included. The
436
+ // receipt stays in the audit; only the presentation is gone.
437
+ // An identity-less request (an old log where neither the field nor
438
+ // the callId+proximity fallback yields a seq) is never proven
439
+ // voided — it keeps the pre-0.1.44 behavior.
440
+ const voided = invocationSeq !== undefined &&
441
+ log.all.some((e) => e.type === "model_output_abandoned" && invocationSeq > e.voidFromSeq && invocationSeq <= e.seq);
442
+ if (voided) {
443
+ yield log.append({
444
+ type: "permission_expired",
445
+ decisionId: pending.decisionId,
446
+ reason: "the invocation was abandoned with an incomplete draft — never re-presented, never executed",
447
+ });
448
+ continue;
449
+ }
249
450
  const decided = log.all.find((e) => e.type === "permission_decided" && e.decisionId === pending.decisionId);
250
451
  // round 4: paired by events NEWER than the request — a historical
251
452
  // same-callId execution from an earlier run must not count as THIS
@@ -303,10 +504,10 @@ export class Run {
303
504
  });
304
505
  if (final.action === "allow") {
305
506
  if (!hasExecution)
306
- yield* this.#executePersisted(pending.callId, pending.name, pending.input, signal);
507
+ yield* this.#executePersisted(pending.callId, pending.name, pending.input, invocationSeq, signal);
307
508
  }
308
509
  else if (!hasResult) {
309
- yield* this.#denialResult(pending.callId, final.reason ?? "denied by user");
510
+ yield* this.#denialResult(pending.callId, final.reason ?? "denied by user", invocationSeq);
310
511
  }
311
512
  }
312
513
  else if (decided.decision === "approved") {
@@ -316,10 +517,10 @@ export class Run {
316
517
  if (signal.aborted)
317
518
  return;
318
519
  if (!hasExecution)
319
- yield* this.#executePersisted(pending.callId, pending.name, pending.input, signal);
520
+ yield* this.#executePersisted(pending.callId, pending.name, pending.input, invocationSeq, signal);
320
521
  }
321
522
  else if (!hasResult) {
322
- yield* this.#denialResult(pending.callId, decided.reason ?? "denied by user");
523
+ yield* this.#denialResult(pending.callId, decided.reason ?? "denied by user", invocationSeq);
323
524
  }
324
525
  }
325
526
  // Receipt repair: an execution that reached a terminal state but
@@ -385,7 +586,7 @@ export class Run {
385
586
  * the permission hook (it was decided) and the model (it was never
386
587
  * asked to re-issue). Full ledgered lifecycle.
387
588
  */
388
- async *#executePersisted(callId, name, input, signal) {
589
+ async *#executePersisted(callId, name, input, invocationSeq, signal) {
389
590
  const log = this.#session.log;
390
591
  const tool = this.#config.registry.get(name);
391
592
  const executionId = `ex-${log.lastSeq + 1}`;
@@ -393,7 +594,14 @@ export class Run {
393
594
  // not start the side effect (finding 3).
394
595
  if (signal.aborted)
395
596
  return;
396
- yield log.append({ type: "tool_execution_started", executionId, callId, name, input });
597
+ yield log.append({
598
+ type: "tool_execution_started",
599
+ executionId,
600
+ callId,
601
+ ...(invocationSeq !== undefined ? { invocationSeq } : {}),
602
+ name,
603
+ input,
604
+ });
397
605
  let result;
398
606
  if (tool === undefined) {
399
607
  result = { content: `Unknown tool: ${name}`, isError: true, errorKind: "invalid_input" };
@@ -432,6 +640,7 @@ export class Run {
432
640
  type: "tool_execution_failed",
433
641
  executionId,
434
642
  callId,
643
+ ...(invocationSeq !== undefined ? { invocationSeq } : {}),
435
644
  error: result.content,
436
645
  // P1-9: errorKind only exists on errors — runtime-guarded too.
437
646
  ...(result.isError && result.errorKind !== undefined ? { errorKind: result.errorKind } : {}),
@@ -444,6 +653,7 @@ export class Run {
444
653
  type: "tool_execution_succeeded",
445
654
  executionId,
446
655
  callId,
656
+ ...(invocationSeq !== undefined ? { invocationSeq } : {}),
447
657
  result: { content: result.content, isError: false },
448
658
  ...(result.tags !== undefined ? { tags: result.tags } : {}),
449
659
  });
@@ -451,6 +661,7 @@ export class Run {
451
661
  yield log.append({
452
662
  type: "tool_result",
453
663
  callId,
664
+ ...(invocationSeq !== undefined ? { invocationSeq } : {}),
454
665
  content: result.content,
455
666
  isError: result.isError,
456
667
  // P1-9: errorKind only exists on errors — runtime-guarded too.
@@ -465,11 +676,12 @@ export class Run {
465
676
  // alone. A retry passes the approval chain again.
466
677
  }
467
678
  /** The model-facing result of a durable denial — no execution happened. */
468
- async *#denialResult(callId, reason) {
679
+ async *#denialResult(callId, reason, invocationSeq) {
469
680
  const denial = denialResult(reason);
470
681
  yield this.#session.log.append({
471
682
  type: "tool_result",
472
683
  callId,
684
+ ...(invocationSeq !== undefined ? { invocationSeq } : {}),
473
685
  content: denial.content,
474
686
  isError: true,
475
687
  errorKind: denial.errorKind,
package/dist/session.d.ts CHANGED
@@ -145,7 +145,7 @@ export declare class AgentSession {
145
145
  */
146
146
  approve(decisionId: string, allow: boolean, reason?: string): Promise<void>;
147
147
  /** Executions that started but never reported a result (crash window). */
148
- uncertainExecutions(): import("@vincemakes/kiso-core").ExecutionRecord[];
148
+ uncertainExecutions(): import("./ledger.js").ExecutionRecord[];
149
149
  /**
150
150
  * The human's verdict on an interrupted execution, keyed by EXECUTION ID
151
151
  * (B group): "rerun" (the human says the side effect did NOT happen — the
package/dist/session.js CHANGED
@@ -27,7 +27,8 @@
27
27
  * support in recovery.ts, the E1/E2 composition helpers in compose.ts —
28
28
  * same package, same exports (index.ts re-exports all four).
29
29
  */
30
- import { EventLog, executionLedger, projectMessages, } from "@vincemakes/kiso-core";
30
+ import { EventLog, projectMessages, } from "@vincemakes/kiso-core";
31
+ import { executionLedger } from "./ledger.js";
31
32
  import { denialResult } from "@vincemakes/kiso-core";
32
33
  import { estimateSummarySavings, KEEP_RECENT_ROUNDS, lastSummaryPoint, summarizeConversation, summaryBoundarySeq, } from "./summarize.js";
33
34
  import { estimateTokens } from "@vincemakes/kiso-core";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vincemakes/kiso-runtime",
3
- "version": "0.1.34",
3
+ "version": "0.1.36",
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.33"
24
+ "@vincemakes/kiso-core": "0.1.35"
25
25
  },
26
26
  "peerDependencies": {
27
- "@vincemakes/kiso-provider-anthropic": "0.1.34",
28
- "@vincemakes/kiso-provider-openai": "0.1.34"
27
+ "@vincemakes/kiso-provider-anthropic": "0.1.36",
28
+ "@vincemakes/kiso-provider-openai": "0.1.36"
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.33",
39
+ "@vincemakes/kiso-evals": "0.1.36",
40
40
  "@types/node": "^26.1.2",
41
41
  "typescript": "^5.7.2",
42
42
  "vitest": "^3.0.0"