@vincemakes/kiso-runtime 0.14.0 → 0.15.1
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 +5 -0
- package/dist/agent.js +1 -0
- package/dist/compose.js +6 -1
- package/dist/internal.d.ts +2 -0
- package/dist/internal.js +7 -0
- package/dist/provider/metadata.d.ts +56 -0
- package/dist/provider/metadata.js +82 -0
- package/dist/run.js +5 -1
- package/dist/session.d.ts +24 -4
- package/dist/session.js +63 -20
- package/dist/task-assessment.d.ts +28 -8
- package/dist/task-assessment.js +39 -7
- package/dist/trace/guard.js +9 -6
- package/dist/usage/canonical.d.ts +15 -0
- package/dist/usage/canonical.js +40 -9
- package/package.json +5 -5
package/dist/agent.d.ts
CHANGED
|
@@ -39,6 +39,10 @@ export interface AgentDefinition {
|
|
|
39
39
|
readonly provider?: "anthropic" | "openai-compat";
|
|
40
40
|
readonly apiKey?: string;
|
|
41
41
|
readonly baseUrl?: string;
|
|
42
|
+
/** PH-1c.1: opt-in Anthropic prompt caching (cache_control
|
|
43
|
+
* breakpoints) — OFF by default; the openai-compat path ignores it
|
|
44
|
+
* (that dialect's caching is server-automatic). Type-only additive. */
|
|
45
|
+
readonly promptCaching?: boolean;
|
|
42
46
|
readonly maxTurns?: number;
|
|
43
47
|
readonly maxTokens?: number;
|
|
44
48
|
readonly temperature?: number;
|
|
@@ -90,4 +94,5 @@ export declare function createAgent(definition: AgentDefinition): AgentRuntime;
|
|
|
90
94
|
export declare function buildAdapter(provider: "anthropic" | "openai-compat", opts?: {
|
|
91
95
|
readonly apiKey?: string;
|
|
92
96
|
readonly baseUrl?: string;
|
|
97
|
+
readonly promptCaching?: boolean;
|
|
93
98
|
}): Promise<Adapter>;
|
package/dist/agent.js
CHANGED
|
@@ -132,6 +132,7 @@ async function resolveAdapter(definition) {
|
|
|
132
132
|
return createAnthropicProvider({
|
|
133
133
|
...(definition.apiKey !== undefined ? { apiKey: definition.apiKey } : {}),
|
|
134
134
|
...(definition.baseUrl !== undefined ? { baseUrl: definition.baseUrl } : {}),
|
|
135
|
+
...(definition.promptCaching !== undefined ? { promptCaching: definition.promptCaching } : {}),
|
|
135
136
|
});
|
|
136
137
|
}
|
|
137
138
|
case "openai-compat": {
|
package/dist/compose.js
CHANGED
|
@@ -201,8 +201,13 @@ export function composeApprovalChain(extensions) {
|
|
|
201
201
|
try {
|
|
202
202
|
v = await Promise.resolve(policy.decide(payload, ctx));
|
|
203
203
|
}
|
|
204
|
-
catch {
|
|
204
|
+
catch (err) {
|
|
205
205
|
v = { action: "ask" }; // a throwing policy counts as ask — it speaks, never silently
|
|
206
|
+
// PH-1a (finding PH-F17): the degradation itself must speak
|
|
207
|
+
// too — a permanently-throwing policy used to manifest only
|
|
208
|
+
// as "why is it asking me about everything?". One stderr
|
|
209
|
+
// line, naming the extension; the verdict is unchanged.
|
|
210
|
+
console.error(`[kiso] approval policy from extension "${extension}" threw (${err instanceof Error ? err.message : String(err)}) — degraded to ask`);
|
|
206
211
|
}
|
|
207
212
|
if (v.action === "abstain")
|
|
208
213
|
continue; // no opinion — not a verdict
|
package/dist/internal.d.ts
CHANGED
package/dist/internal.js
CHANGED
|
@@ -23,3 +23,10 @@ export * from "./lock-adapter.js";
|
|
|
23
23
|
export * from "./ledger.js";
|
|
24
24
|
export * from "./extensions.js";
|
|
25
25
|
export * from "./trust.js";
|
|
26
|
+
// PH-1c: the model metadata registry (capabilities + dated pricing) —
|
|
27
|
+
// first-party consumers (the CLI window derivation) read it here; the
|
|
28
|
+
// curated root surface does not move.
|
|
29
|
+
export * from "./provider/metadata.js";
|
|
30
|
+
// PH-1c: the model-keyed cost derivation rides the same door (the root
|
|
31
|
+
// surface keeps only the frozen canonicalizeUsage).
|
|
32
|
+
export { canonicalizeUsageForModel } from "./usage/canonical.js";
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PH-1c — the model metadata table (findings PH-F14/PH-F15/PH-F16).
|
|
3
|
+
*
|
|
4
|
+
* Capabilities and pricing keyed by MODEL (+ optional endpoint), never
|
|
5
|
+
* by route: `anthropic`/`openai-compat` are protocol conventions — the
|
|
6
|
+
* same route serves models whose windows and rates have nothing in
|
|
7
|
+
* common, which is how the old route-keyed table priced an Anthropic
|
|
8
|
+
* run at DeepSeek's rates. The registry's one discipline: **unknown is
|
|
9
|
+
* null, everywhere** — an absent entry, an absent field, an unverified
|
|
10
|
+
* price all surface as null and no layer downstream may guess.
|
|
11
|
+
*
|
|
12
|
+
* Pricing is SEPARATE from capabilities (the review boundary): a price
|
|
13
|
+
* is a dated claim about someone else's billing page, so every entry
|
|
14
|
+
* carries `asOf` (the freeze date) and `source` (the page). A model we
|
|
15
|
+
* can name but not price stays `pricing: null` — the honest stamp the
|
|
16
|
+
* E2 nullable convention already defined.
|
|
17
|
+
*
|
|
18
|
+
* Lives under runtime/internal — the curated root surface (44 names)
|
|
19
|
+
* does not move; core is untouched.
|
|
20
|
+
*/
|
|
21
|
+
export interface ModelCapabilities {
|
|
22
|
+
/** tokens of context the model accepts; null = unknown. */
|
|
23
|
+
readonly contextWindow: number | null;
|
|
24
|
+
readonly maxOutputTokens: number | null;
|
|
25
|
+
/** "automatic" — the provider caches without request markup (DeepSeek,
|
|
26
|
+
* OpenAI); "explicit" — the request must place cache_control
|
|
27
|
+
* breakpoints (Anthropic); "none" — no caching; null = unknown. */
|
|
28
|
+
readonly promptCaching: "none" | "automatic" | "explicit" | null;
|
|
29
|
+
/** the model emits a reasoning stream (thinking); null = unknown. */
|
|
30
|
+
readonly reasoning: boolean | null;
|
|
31
|
+
}
|
|
32
|
+
export interface ModelPricing {
|
|
33
|
+
readonly inputPerM: number;
|
|
34
|
+
readonly outputPerM: number;
|
|
35
|
+
readonly cacheReadPerM: number;
|
|
36
|
+
readonly cacheWritePerM: number;
|
|
37
|
+
/** the date the rates were read — a price is a dated claim. */
|
|
38
|
+
readonly asOf: string;
|
|
39
|
+
/** the billing page the rates came from. */
|
|
40
|
+
readonly source: string;
|
|
41
|
+
}
|
|
42
|
+
export interface ModelMetadataEntry {
|
|
43
|
+
/** EXACT model id — v1 does no pattern matching. */
|
|
44
|
+
readonly model: string;
|
|
45
|
+
/** origin qualifier (e.g. "https://api.deepseek.com"): when present,
|
|
46
|
+
* the entry matches only requests aimed at that endpoint. */
|
|
47
|
+
readonly endpoint?: string;
|
|
48
|
+
readonly capabilities: ModelCapabilities;
|
|
49
|
+
readonly pricing: ModelPricing | null;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Look a model up. `endpoint` narrows: an entry WITH an endpoint only
|
|
53
|
+
* matches when the caller's endpoint origin equals it; an entry without
|
|
54
|
+
* one matches any endpoint. Unknown model → null, never a default.
|
|
55
|
+
*/
|
|
56
|
+
export declare function lookupModelMetadata(model: string, endpoint?: string): ModelMetadataEntry | null;
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PH-1c — the model metadata table (findings PH-F14/PH-F15/PH-F16).
|
|
3
|
+
*
|
|
4
|
+
* Capabilities and pricing keyed by MODEL (+ optional endpoint), never
|
|
5
|
+
* by route: `anthropic`/`openai-compat` are protocol conventions — the
|
|
6
|
+
* same route serves models whose windows and rates have nothing in
|
|
7
|
+
* common, which is how the old route-keyed table priced an Anthropic
|
|
8
|
+
* run at DeepSeek's rates. The registry's one discipline: **unknown is
|
|
9
|
+
* null, everywhere** — an absent entry, an absent field, an unverified
|
|
10
|
+
* price all surface as null and no layer downstream may guess.
|
|
11
|
+
*
|
|
12
|
+
* Pricing is SEPARATE from capabilities (the review boundary): a price
|
|
13
|
+
* is a dated claim about someone else's billing page, so every entry
|
|
14
|
+
* carries `asOf` (the freeze date) and `source` (the page). A model we
|
|
15
|
+
* can name but not price stays `pricing: null` — the honest stamp the
|
|
16
|
+
* E2 nullable convention already defined.
|
|
17
|
+
*
|
|
18
|
+
* Lives under runtime/internal — the curated root surface (44 names)
|
|
19
|
+
* does not move; core is untouched.
|
|
20
|
+
*/
|
|
21
|
+
const DEEPSEEK_PRICING = {
|
|
22
|
+
// The E2-frozen rates (pricing table v1, freeze date 2026-08-13),
|
|
23
|
+
// re-homed here with their provenance made explicit. The caveat
|
|
24
|
+
// carries forward verbatim: an approximation, not a bill.
|
|
25
|
+
inputPerM: 0.27,
|
|
26
|
+
outputPerM: 1.1,
|
|
27
|
+
cacheReadPerM: 0.027,
|
|
28
|
+
cacheWritePerM: 0,
|
|
29
|
+
asOf: "2026-08-13",
|
|
30
|
+
source: "https://api-docs.deepseek.com/quick_start/pricing",
|
|
31
|
+
};
|
|
32
|
+
/** The v1 table. Nulls outnumber numbers ON PURPOSE: only values with a
|
|
33
|
+
* named source enter; everything else waits for one. */
|
|
34
|
+
const ENTRIES = [
|
|
35
|
+
{
|
|
36
|
+
model: "deepseek-chat",
|
|
37
|
+
endpoint: "https://api.deepseek.com",
|
|
38
|
+
capabilities: { contextWindow: null, maxOutputTokens: null, promptCaching: "automatic", reasoning: false },
|
|
39
|
+
pricing: DEEPSEEK_PRICING,
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
model: "deepseek-reasoner",
|
|
43
|
+
endpoint: "https://api.deepseek.com",
|
|
44
|
+
capabilities: { contextWindow: null, maxOutputTokens: null, promptCaching: "automatic", reasoning: true },
|
|
45
|
+
pricing: DEEPSEEK_PRICING,
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
model: "claude-sonnet-5",
|
|
49
|
+
capabilities: { contextWindow: 200_000, maxOutputTokens: null, promptCaching: "explicit", reasoning: true },
|
|
50
|
+
// Priced only when the rates are read from the live billing page
|
|
51
|
+
// and dated — never copied from memory (the review's boundary ②).
|
|
52
|
+
pricing: null,
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
model: "gpt-4o",
|
|
56
|
+
capabilities: { contextWindow: 128_000, maxOutputTokens: null, promptCaching: "automatic", reasoning: false },
|
|
57
|
+
pricing: null,
|
|
58
|
+
},
|
|
59
|
+
];
|
|
60
|
+
/**
|
|
61
|
+
* Look a model up. `endpoint` narrows: an entry WITH an endpoint only
|
|
62
|
+
* matches when the caller's endpoint origin equals it; an entry without
|
|
63
|
+
* one matches any endpoint. Unknown model → null, never a default.
|
|
64
|
+
*/
|
|
65
|
+
export function lookupModelMetadata(model, endpoint) {
|
|
66
|
+
for (const entry of ENTRIES) {
|
|
67
|
+
if (entry.model !== model)
|
|
68
|
+
continue;
|
|
69
|
+
if (entry.endpoint !== undefined && endpoint !== undefined && entry.endpoint !== originOf(endpoint))
|
|
70
|
+
continue;
|
|
71
|
+
return entry;
|
|
72
|
+
}
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
function originOf(endpoint) {
|
|
76
|
+
try {
|
|
77
|
+
return new URL(endpoint).origin;
|
|
78
|
+
}
|
|
79
|
+
catch {
|
|
80
|
+
return endpoint;
|
|
81
|
+
}
|
|
82
|
+
}
|
package/dist/run.js
CHANGED
|
@@ -436,8 +436,12 @@ export class Run {
|
|
|
436
436
|
verdict = chainVerdict;
|
|
437
437
|
}
|
|
438
438
|
}
|
|
439
|
-
catch {
|
|
439
|
+
catch (err) {
|
|
440
440
|
verdict = { action: "ask" };
|
|
441
|
+
// PH-1a (finding PH-F17): same speaking degradation as the chain's
|
|
442
|
+
// per-policy catch — the recovery path has no extension name, so
|
|
443
|
+
// the chain is named as a whole.
|
|
444
|
+
console.error(`[kiso] approval chain threw during recovery (${err instanceof Error ? err.message : String(err)}) — degraded to ask`);
|
|
441
445
|
}
|
|
442
446
|
if (verdict === undefined && hooks?.onPreTool !== undefined) {
|
|
443
447
|
const decision = await abortable(Promise.resolve(hooks.onPreTool(payload, { sessionId: this.#session.id })), signal);
|
package/dist/session.d.ts
CHANGED
|
@@ -114,9 +114,25 @@ export declare class AgentSession {
|
|
|
114
114
|
* turns (dispatch's /model), never mid-run.
|
|
115
115
|
*/
|
|
116
116
|
setAdapter(adapter: Adapter): void;
|
|
117
|
+
/**
|
|
118
|
+
* PH-1a (finding PH-F8, P0): the ATOMIC model switch — adapter, model
|
|
119
|
+
* id, and provider route replace together, effective at the next run.
|
|
120
|
+
* setAdapter alone is for a same-binding adapter swap (the faux
|
|
121
|
+
* re-arm); a switch that changes WHAT model answers must come through
|
|
122
|
+
* here, or the UI claims one model while requests carry another.
|
|
123
|
+
* Omitting `provider` clears the route — an unknown binding is
|
|
124
|
+
* canonicalized under the honest "adapter" route (null-priced), never
|
|
125
|
+
* the stale one. (The context window joins the binding when per-model
|
|
126
|
+
* metadata exists — the PH-1c registry.)
|
|
127
|
+
*/
|
|
128
|
+
setModelBinding(binding: {
|
|
129
|
+
readonly adapter: Adapter;
|
|
130
|
+
readonly model: string;
|
|
131
|
+
readonly provider?: "anthropic" | "openai-compat";
|
|
132
|
+
}): void;
|
|
117
133
|
/** E2: the adapter identity ("anthropic" | "openai-compat") — the route
|
|
118
134
|
* key the canonical consumer (CLI usage, the trace block) keys on. The
|
|
119
|
-
* per-run tracer reads the SAME
|
|
135
|
+
* per-run tracer reads the SAME live binding; one source, one
|
|
120
136
|
* route — the CLI and the trace can never disagree. */
|
|
121
137
|
get provider(): "anthropic" | "openai-compat" | undefined;
|
|
122
138
|
/**
|
|
@@ -185,6 +201,7 @@ export declare class AgentSession {
|
|
|
185
201
|
signal?: AbortSignalLike;
|
|
186
202
|
onStart?: (info: CompactInfo) => void;
|
|
187
203
|
drop?: boolean;
|
|
204
|
+
focus?: string;
|
|
188
205
|
}): Promise<SummarizeResult | null>;
|
|
189
206
|
/**
|
|
190
207
|
* E6 — the run-start context policy (candidate A + the crux drop arm).
|
|
@@ -224,9 +241,12 @@ export declare class AgentSession {
|
|
|
224
241
|
uncertainExecutions(): import("./ledger.js").ExecutionRecord[];
|
|
225
242
|
/**
|
|
226
243
|
* TV-1A — assess the task claims and their evidence freshness over THIS
|
|
227
|
-
* session's durable log. The
|
|
228
|
-
* own `effects.
|
|
229
|
-
*
|
|
244
|
+
* session's durable log. The non-mutating set comes from the live tools'
|
|
245
|
+
* own `effects.precommitSafe` certificates (one direction of truth,
|
|
246
|
+
* never a second declaration) — the read-only+free+local contract, the
|
|
247
|
+
* only certificate that proves the world untouched. `concurrency:
|
|
248
|
+
* "shared"` is a SCHEDULING promise and never feeds this set (TV-1C —
|
|
249
|
+
* slow_touch is shared and writes). The evidence policy defaults to
|
|
230
250
|
* {"shell"} — the convention the task extension's own "make the LAST
|
|
231
251
|
* item a verification step" guidance produces.
|
|
232
252
|
*/
|
package/dist/session.js
CHANGED
|
@@ -36,7 +36,7 @@ import { assessTasks } from "./task-assessment.js";
|
|
|
36
36
|
const DEFAULT_EVIDENCE_TOOLS = new Set(["shell"]);
|
|
37
37
|
import { denialResult } from "@vincemakes/kiso-core";
|
|
38
38
|
import { DROP_PLACEHOLDER, estimateSummarySavings, KEEP_RECENT_ROUNDS, KEEP_TOKENS_DEFAULT, lastSummaryPoint, MAX_SUMMARY_FAILURES, policyTriggerFromWindow, serializeCovered, SUMMARY_MAX_OUTPUT, summarizeConversation, summaryBoundarySeq, } from "./summarize.js";
|
|
39
|
-
import {
|
|
39
|
+
import { canonicalizeUsageForModel } from "./usage/canonical.js";
|
|
40
40
|
import { appendFileSync, mkdirSync } from "node:fs";
|
|
41
41
|
import { join } from "node:path";
|
|
42
42
|
import { estimateTokens } from "@vincemakes/kiso-core";
|
|
@@ -72,9 +72,18 @@ export class AgentSession {
|
|
|
72
72
|
log;
|
|
73
73
|
#store;
|
|
74
74
|
// NOT readonly since 0.1.23: /model replaces it between runs (the
|
|
75
|
-
// constructor and
|
|
75
|
+
// constructor, setAdapter, and setModelBinding are the only writers).
|
|
76
76
|
#adapter;
|
|
77
77
|
#config;
|
|
78
|
+
// PH-1a (finding PH-F8, P0): the LIVE model binding. #config froze the
|
|
79
|
+
// startup model/provider, so a /model switch replaced the adapter while
|
|
80
|
+
// every later run kept sending the OLD model id and canonicalizing
|
|
81
|
+
// usage under the OLD route. These two travel WITH the adapter now —
|
|
82
|
+
// setModelBinding writes all three in one call; runs read them through
|
|
83
|
+
// #effectiveConfig at run construction (next-turn semantics, same as
|
|
84
|
+
// setAdapter always had).
|
|
85
|
+
#model;
|
|
86
|
+
#provider;
|
|
78
87
|
#pendingResolvers = new Map();
|
|
79
88
|
#uncertaintyResolvers = new Map();
|
|
80
89
|
#answered = new Set();
|
|
@@ -126,6 +135,16 @@ export class AgentSession {
|
|
|
126
135
|
}
|
|
127
136
|
const composedHooks = composeHooks(config.hooks, config.extensions ?? []);
|
|
128
137
|
this.#config = composedHooks === undefined ? config : { ...config, hooks: composedHooks };
|
|
138
|
+
this.#model = config.model;
|
|
139
|
+
this.#provider = config.provider;
|
|
140
|
+
}
|
|
141
|
+
/** The config a NEW run/resume/summary sees: the frozen startup config
|
|
142
|
+
* with the LIVE binding fields (model, provider) substituted. Built
|
|
143
|
+
* fresh per call so an in-flight run keeps the config it started with
|
|
144
|
+
* — the same boundary setAdapter has always drawn. */
|
|
145
|
+
#effectiveConfig() {
|
|
146
|
+
const { provider: _startup, ...rest } = this.#config;
|
|
147
|
+
return { ...rest, model: this.#model, ...(this.#provider !== undefined ? { provider: this.#provider } : {}) };
|
|
129
148
|
}
|
|
130
149
|
/** Write-ahead through the store; a rejected write POISONS the session
|
|
131
150
|
* (round 1/round 4): the in-memory log no longer matches the disk — whatever
|
|
@@ -170,12 +189,28 @@ export class AgentSession {
|
|
|
170
189
|
setAdapter(adapter) {
|
|
171
190
|
this.#adapter = adapter;
|
|
172
191
|
}
|
|
192
|
+
/**
|
|
193
|
+
* PH-1a (finding PH-F8, P0): the ATOMIC model switch — adapter, model
|
|
194
|
+
* id, and provider route replace together, effective at the next run.
|
|
195
|
+
* setAdapter alone is for a same-binding adapter swap (the faux
|
|
196
|
+
* re-arm); a switch that changes WHAT model answers must come through
|
|
197
|
+
* here, or the UI claims one model while requests carry another.
|
|
198
|
+
* Omitting `provider` clears the route — an unknown binding is
|
|
199
|
+
* canonicalized under the honest "adapter" route (null-priced), never
|
|
200
|
+
* the stale one. (The context window joins the binding when per-model
|
|
201
|
+
* metadata exists — the PH-1c registry.)
|
|
202
|
+
*/
|
|
203
|
+
setModelBinding(binding) {
|
|
204
|
+
this.#adapter = binding.adapter;
|
|
205
|
+
this.#model = binding.model;
|
|
206
|
+
this.#provider = binding.provider;
|
|
207
|
+
}
|
|
173
208
|
/** E2: the adapter identity ("anthropic" | "openai-compat") — the route
|
|
174
209
|
* key the canonical consumer (CLI usage, the trace block) keys on. The
|
|
175
|
-
* per-run tracer reads the SAME
|
|
210
|
+
* per-run tracer reads the SAME live binding; one source, one
|
|
176
211
|
* route — the CLI and the trace can never disagree. */
|
|
177
212
|
get provider() {
|
|
178
|
-
return this.#
|
|
213
|
+
return this.#provider;
|
|
179
214
|
}
|
|
180
215
|
/**
|
|
181
216
|
* TUI2-R3v2 ③ — ONE model request that belongs to no run (the
|
|
@@ -222,8 +257,8 @@ export class AgentSession {
|
|
|
222
257
|
root: this.#store.root,
|
|
223
258
|
sessionId: this.id,
|
|
224
259
|
runId,
|
|
225
|
-
provider: this.#
|
|
226
|
-
model: this.#
|
|
260
|
+
provider: this.#provider ?? "adapter",
|
|
261
|
+
model: this.#model,
|
|
227
262
|
adapterVersion: runtimeVersion(),
|
|
228
263
|
purpose: options.purpose,
|
|
229
264
|
// the manifest's seqRange pointers derive from the log, and a side
|
|
@@ -239,7 +274,7 @@ export class AgentSession {
|
|
|
239
274
|
let text = "";
|
|
240
275
|
try {
|
|
241
276
|
for await (const ev of guarded.stream({
|
|
242
|
-
model: this.#
|
|
277
|
+
model: this.#model,
|
|
243
278
|
messages: [{ role: "user", content: options.prompt }],
|
|
244
279
|
systemPrompt: options.systemPrompt,
|
|
245
280
|
...(options.maxTokens !== undefined ? { maxTokens: options.maxTokens } : {}),
|
|
@@ -257,7 +292,7 @@ export class AgentSession {
|
|
|
257
292
|
/** Run one user turn. Iterate to consume; `run.abort()` cancels. */
|
|
258
293
|
run(input, options) {
|
|
259
294
|
this.ensureHealthy();
|
|
260
|
-
return new Run(this.#store, this.#adapter, this.#
|
|
295
|
+
return new Run(this.#store, this.#adapter, this.#effectiveConfig(), this, input, options?.signal, false, options?.source);
|
|
261
296
|
}
|
|
262
297
|
/**
|
|
263
298
|
* Continue the interrupted run (Area 2): apply durable decisions,
|
|
@@ -267,7 +302,7 @@ export class AgentSession {
|
|
|
267
302
|
*/
|
|
268
303
|
resume() {
|
|
269
304
|
this.ensureHealthy();
|
|
270
|
-
return new Run(this.#store, this.#adapter, this.#
|
|
305
|
+
return new Run(this.#store, this.#adapter, this.#effectiveConfig(), this, undefined, undefined, true);
|
|
271
306
|
}
|
|
272
307
|
/**
|
|
273
308
|
* /compact (ADR-0044): compress the older conversation with a model
|
|
@@ -280,7 +315,11 @@ export class AgentSession {
|
|
|
280
315
|
* worth covering yet). Crash semantics: a crash BEFORE the persist is
|
|
281
316
|
* "nothing happened"; after it, a resume projects the compressed view.
|
|
282
317
|
*/
|
|
283
|
-
async summarize(
|
|
318
|
+
async summarize(
|
|
319
|
+
// R3a: `focus` — an optional steer for the summary call ("keep the
|
|
320
|
+
// auth details"). Rides the serialized input as ONE instruction
|
|
321
|
+
// line; absent = byte-identical to the pre-round call.
|
|
322
|
+
options = {}) {
|
|
284
323
|
this.ensureHealthy();
|
|
285
324
|
const keepRounds = options.keepRounds ?? KEEP_RECENT_ROUNDS;
|
|
286
325
|
// W18: the signal is observed at EVERY phase boundary — the cancel
|
|
@@ -301,7 +340,8 @@ export class AgentSession {
|
|
|
301
340
|
// messages still feed the pre-call token estimate and the savings
|
|
302
341
|
// figure (estimate-only, never the model input).
|
|
303
342
|
const covered = projectMessages(events.filter((e) => e.seq > prevPoint && e.seq <= boundary && e.type !== "summarized"));
|
|
304
|
-
const
|
|
343
|
+
const serialized0 = serializeCovered({ events, prevPoint, boundary });
|
|
344
|
+
const serializedInput = options.focus === undefined ? serialized0 : `Focus the summary on: ${options.focus}\n\n${serialized0}`;
|
|
305
345
|
// W18: the indicator's pre-call data — rounds + the token estimate
|
|
306
346
|
// are knowable BEFORE the adapter call; the summary itself is ONE
|
|
307
347
|
// call with no fraction (kiso never invents a percentage here).
|
|
@@ -322,7 +362,7 @@ export class AgentSession {
|
|
|
322
362
|
else {
|
|
323
363
|
const call = await summarizeConversation({
|
|
324
364
|
adapter: this.#adapter,
|
|
325
|
-
model: this.#
|
|
365
|
+
model: this.#model,
|
|
326
366
|
// E6 (a): ONE serialized user message — the DSML bug's
|
|
327
367
|
// raw-message array is structurally dead on this path.
|
|
328
368
|
messages: [{ role: "user", content: serializedInput }],
|
|
@@ -356,7 +396,7 @@ export class AgentSession {
|
|
|
356
396
|
// a degraded ledger costs one stderr line, never the summary.
|
|
357
397
|
if (usage !== null) {
|
|
358
398
|
try {
|
|
359
|
-
const canonical =
|
|
399
|
+
const canonical = canonicalizeUsageForModel(this.#model, undefined, this.#provider ?? "adapter", usage);
|
|
360
400
|
const line = JSON.stringify({ kind: "summary", canonical }) + "\n";
|
|
361
401
|
mkdirSync(join(this.#store.root, "traces"), { recursive: true });
|
|
362
402
|
appendFileSync(join(this.#store.root, "traces", `${this.id}.jsonl`), line);
|
|
@@ -535,20 +575,23 @@ export class AgentSession {
|
|
|
535
575
|
}
|
|
536
576
|
/**
|
|
537
577
|
* TV-1A — assess the task claims and their evidence freshness over THIS
|
|
538
|
-
* session's durable log. The
|
|
539
|
-
* own `effects.
|
|
540
|
-
*
|
|
578
|
+
* session's durable log. The non-mutating set comes from the live tools'
|
|
579
|
+
* own `effects.precommitSafe` certificates (one direction of truth,
|
|
580
|
+
* never a second declaration) — the read-only+free+local contract, the
|
|
581
|
+
* only certificate that proves the world untouched. `concurrency:
|
|
582
|
+
* "shared"` is a SCHEDULING promise and never feeds this set (TV-1C —
|
|
583
|
+
* slow_touch is shared and writes). The evidence policy defaults to
|
|
541
584
|
* {"shell"} — the convention the task extension's own "make the LAST
|
|
542
585
|
* item a verification step" guidance produces.
|
|
543
586
|
*/
|
|
544
587
|
assessTasks(opts) {
|
|
545
|
-
const
|
|
588
|
+
const nonMutatingTools = new Set();
|
|
546
589
|
for (const tool of this.#config.registry.list()) {
|
|
547
|
-
if (tool.effects?.
|
|
548
|
-
|
|
590
|
+
if (tool.effects?.precommitSafe === true)
|
|
591
|
+
nonMutatingTools.add(tool.name);
|
|
549
592
|
}
|
|
550
593
|
return assessTasks(this.log.all, {
|
|
551
|
-
|
|
594
|
+
nonMutatingTools,
|
|
552
595
|
evidenceTools: opts?.evidenceTools ?? DEFAULT_EVIDENCE_TOOLS,
|
|
553
596
|
});
|
|
554
597
|
}
|
|
@@ -9,13 +9,18 @@
|
|
|
9
9
|
*
|
|
10
10
|
* No new event kind, no core diff — the projection reads the existing
|
|
11
11
|
* durable vocabulary (`deriveRecoveryPlan` is the shape precedent). The
|
|
12
|
-
* classifiers are maximally conservative
|
|
13
|
-
*
|
|
12
|
+
* classifiers are maximally conservative; the exemptions are exactly
|
|
13
|
+
* three (TV-1C — no wider taxonomy exists or is wanted):
|
|
14
14
|
*
|
|
15
15
|
* - MUTATION marker: `tool_execution_started` (intent-to-effect) of any
|
|
16
|
-
* name NOT in `
|
|
17
|
-
* crash-window executions invalidate too, and pre-EC-1 overlap eras
|
|
18
|
-
* covered by the same rule. Absence of a certificate is a mutation.
|
|
16
|
+
* name NOT in `nonMutatingTools`. Started — not the receipt — so failed
|
|
17
|
+
* and crash-window executions invalidate too, and pre-EC-1 overlap eras
|
|
18
|
+
* are covered by the same rule. Absence of a certificate is a mutation.
|
|
19
|
+
* The exemption set is fed by `effects.precommitSafe` — the frozen
|
|
20
|
+
* read-only+free+local contract, the only certificate that PROVES the
|
|
21
|
+
* world untouched. `concurrency: "shared"` proves overlap-safety and
|
|
22
|
+
* nothing else (the TV-1C finding: slow_touch is shared AND writes) —
|
|
23
|
+
* it never feeds this set.
|
|
19
24
|
* ONE proven exception (TV-1B, grounded in WR-1A): an execution whose
|
|
20
25
|
* terminal receipt is failed(errorKind:"precondition") never counts —
|
|
21
26
|
* that kind's frozen contract is "work refused BEFORE it starts".
|
|
@@ -27,6 +32,19 @@
|
|
|
27
32
|
* - task_set itself never invalidates: recording the claim after the check
|
|
28
33
|
* is the natural arc (tests → mark done), and the claim-recording act
|
|
29
34
|
* mutates nothing the evidence observed.
|
|
35
|
+
* - VOIDED RANGES (TV-1C): a `model_output_abandoned` marker voids
|
|
36
|
+
* (voidFromSeq, seq] — the durable vocabulary's own "never happened",
|
|
37
|
+
* the same ranges the kernel projection skips. Every consumer here
|
|
38
|
+
* skips them uniformly: a voided echo is not a claim, a voided receipt
|
|
39
|
+
* is not evidence, a voided start is not a mutation. task_set waits
|
|
40
|
+
* for turn commit today (TT-1), so the scheduler already keeps claims
|
|
41
|
+
* out of voided turns — the projection encodes the admissibility rule
|
|
42
|
+
* itself instead of relying on that forever.
|
|
43
|
+
*
|
|
44
|
+
* `verified` means FRESH EVIDENCE UNDER THE CONFIGURED POLICY — never
|
|
45
|
+
* "objectively proven correct". Goal Truth is external (PE-1's
|
|
46
|
+
* evaluator); this projection only ever ranks the model's own claim
|
|
47
|
+
* against the trajectory's own receipts.
|
|
30
48
|
*/
|
|
31
49
|
import type { Event } from "@vincemakes/kiso-core";
|
|
32
50
|
/** One item of the model's plan, exactly as last claimed. */
|
|
@@ -64,9 +82,11 @@ export interface TaskAssessment {
|
|
|
64
82
|
* trajectory. Pure: same events, same options, same assessment.
|
|
65
83
|
*/
|
|
66
84
|
export declare function assessTasks(events: readonly Event[], opts?: {
|
|
67
|
-
/** Names whose executions never mutate
|
|
68
|
-
*
|
|
69
|
-
|
|
85
|
+
/** Names whose executions PROVABLY never mutate the observable
|
|
86
|
+
* world (from `effects.precommitSafe` certificates — read-only +
|
|
87
|
+
* free + local). `concurrency: "shared"` is NOT such a proof
|
|
88
|
+
* (TV-1C). Default ∅ — everything mutates. */
|
|
89
|
+
readonly nonMutatingTools?: ReadonlySet<string>;
|
|
70
90
|
/** Names whose successful receipts count as evidence. Default ∅ —
|
|
71
91
|
* nothing does. */
|
|
72
92
|
readonly evidenceTools?: ReadonlySet<string>;
|
package/dist/task-assessment.js
CHANGED
|
@@ -9,13 +9,18 @@
|
|
|
9
9
|
*
|
|
10
10
|
* No new event kind, no core diff — the projection reads the existing
|
|
11
11
|
* durable vocabulary (`deriveRecoveryPlan` is the shape precedent). The
|
|
12
|
-
* classifiers are maximally conservative
|
|
13
|
-
*
|
|
12
|
+
* classifiers are maximally conservative; the exemptions are exactly
|
|
13
|
+
* three (TV-1C — no wider taxonomy exists or is wanted):
|
|
14
14
|
*
|
|
15
15
|
* - MUTATION marker: `tool_execution_started` (intent-to-effect) of any
|
|
16
|
-
* name NOT in `
|
|
17
|
-
* crash-window executions invalidate too, and pre-EC-1 overlap eras
|
|
18
|
-
* covered by the same rule. Absence of a certificate is a mutation.
|
|
16
|
+
* name NOT in `nonMutatingTools`. Started — not the receipt — so failed
|
|
17
|
+
* and crash-window executions invalidate too, and pre-EC-1 overlap eras
|
|
18
|
+
* are covered by the same rule. Absence of a certificate is a mutation.
|
|
19
|
+
* The exemption set is fed by `effects.precommitSafe` — the frozen
|
|
20
|
+
* read-only+free+local contract, the only certificate that PROVES the
|
|
21
|
+
* world untouched. `concurrency: "shared"` proves overlap-safety and
|
|
22
|
+
* nothing else (the TV-1C finding: slow_touch is shared AND writes) —
|
|
23
|
+
* it never feeds this set.
|
|
19
24
|
* ONE proven exception (TV-1B, grounded in WR-1A): an execution whose
|
|
20
25
|
* terminal receipt is failed(errorKind:"precondition") never counts —
|
|
21
26
|
* that kind's frozen contract is "work refused BEFORE it starts".
|
|
@@ -27,6 +32,19 @@
|
|
|
27
32
|
* - task_set itself never invalidates: recording the claim after the check
|
|
28
33
|
* is the natural arc (tests → mark done), and the claim-recording act
|
|
29
34
|
* mutates nothing the evidence observed.
|
|
35
|
+
* - VOIDED RANGES (TV-1C): a `model_output_abandoned` marker voids
|
|
36
|
+
* (voidFromSeq, seq] — the durable vocabulary's own "never happened",
|
|
37
|
+
* the same ranges the kernel projection skips. Every consumer here
|
|
38
|
+
* skips them uniformly: a voided echo is not a claim, a voided receipt
|
|
39
|
+
* is not evidence, a voided start is not a mutation. task_set waits
|
|
40
|
+
* for turn commit today (TT-1), so the scheduler already keeps claims
|
|
41
|
+
* out of voided turns — the projection encodes the admissibility rule
|
|
42
|
+
* itself instead of relying on that forever.
|
|
43
|
+
*
|
|
44
|
+
* `verified` means FRESH EVIDENCE UNDER THE CONFIGURED POLICY — never
|
|
45
|
+
* "objectively proven correct". Goal Truth is external (PE-1's
|
|
46
|
+
* evaluator); this projection only ever ranks the model's own claim
|
|
47
|
+
* against the trajectory's own receipts.
|
|
30
48
|
*/
|
|
31
49
|
const TASK_TOOL = "task_set";
|
|
32
50
|
const STATUSES = new Set(["pending", "active", "done"]);
|
|
@@ -62,8 +80,18 @@ function parseEcho(content) {
|
|
|
62
80
|
* trajectory. Pure: same events, same options, same assessment.
|
|
63
81
|
*/
|
|
64
82
|
export function assessTasks(events, opts) {
|
|
65
|
-
const
|
|
83
|
+
const nonMutating = opts?.nonMutatingTools ?? new Set();
|
|
66
84
|
const evidenceNames = opts?.evidenceTools ?? new Set();
|
|
85
|
+
// TV-1C: PASS 0 — the voided ranges. (voidFromSeq, seq] of every
|
|
86
|
+
// `model_output_abandoned` marker is "never happened" for this whole
|
|
87
|
+
// projection, exactly as the kernel's own context projection treats
|
|
88
|
+
// it. Markers are rare; a linear range check is honest and enough.
|
|
89
|
+
const voidRanges = [];
|
|
90
|
+
for (const ev of events) {
|
|
91
|
+
if (ev.type === "model_output_abandoned")
|
|
92
|
+
voidRanges.push({ from: ev.voidFromSeq, to: ev.seq });
|
|
93
|
+
}
|
|
94
|
+
const voided = (seq) => voidRanges.some((r) => seq > r.from && seq <= r.to);
|
|
67
95
|
// TV-1B: PASS 1 — executions whose terminal receipt is a
|
|
68
96
|
// PRECONDITION failure are PROVEN no-mutation (WR-1A froze that
|
|
69
97
|
// contract: work refused before it starts). Everything else stays
|
|
@@ -72,6 +100,8 @@ export function assessTasks(events, opts) {
|
|
|
72
100
|
// Two passes, no temporal rollback state — same events, same verdict.
|
|
73
101
|
const provenNoMutation = new Set();
|
|
74
102
|
for (const ev of events) {
|
|
103
|
+
if (voided(ev.seq))
|
|
104
|
+
continue;
|
|
75
105
|
if (ev.type === "tool_execution_failed" && ev.errorKind === "precondition") {
|
|
76
106
|
provenNoMutation.add(ev.executionId);
|
|
77
107
|
}
|
|
@@ -84,9 +114,11 @@ export function assessTasks(events, opts) {
|
|
|
84
114
|
let lastEvidenceSeq = null;
|
|
85
115
|
let staleBySeq = null;
|
|
86
116
|
for (const ev of events) {
|
|
117
|
+
if (voided(ev.seq))
|
|
118
|
+
continue;
|
|
87
119
|
if (ev.type === "tool_execution_started") {
|
|
88
120
|
nameByExecution.set(ev.executionId, ev.name);
|
|
89
|
-
if (ev.name !== TASK_TOOL && !
|
|
121
|
+
if (ev.name !== TASK_TOOL && !nonMutating.has(ev.name) && !provenNoMutation.has(ev.executionId)) {
|
|
90
122
|
lastMutationSeq = ev.seq;
|
|
91
123
|
if (lastEvidenceSeq !== null && staleBySeq === null)
|
|
92
124
|
staleBySeq = ev.seq;
|
package/dist/trace/guard.js
CHANGED
|
@@ -25,7 +25,7 @@ import { randomUUID } from "node:crypto";
|
|
|
25
25
|
import { buildContextManifest, segmentHashes } from "./manifest.js";
|
|
26
26
|
import { cacheableHashes } from "./analyze.js";
|
|
27
27
|
import { hashContext, hashSystemPrompt, hashToolSpecs, stablePrefixFingerprint } from "./hash.js";
|
|
28
|
-
import { PRICING_TABLE_V1,
|
|
28
|
+
import { PRICING_TABLE_V1, canonicalizeUsageForModel } from "../usage/canonical.js";
|
|
29
29
|
import { buildRentLedger } from "./rent.js";
|
|
30
30
|
import { TRACE_SCHEMA_VERSION } from "./record.js";
|
|
31
31
|
import { TraceWriter } from "./writer.js";
|
|
@@ -201,11 +201,14 @@ export class RequestTracer {
|
|
|
201
201
|
record.cacheWrite = p.cacheWrite ?? null;
|
|
202
202
|
record.output = p.outputTokens ?? 0;
|
|
203
203
|
}
|
|
204
|
-
// E2 — the canonical block formalizes the same raw (the
|
|
205
|
-
//
|
|
206
|
-
//
|
|
207
|
-
//
|
|
208
|
-
|
|
204
|
+
// E2 — the canonical block formalizes the same raw (the validator
|
|
205
|
+
// pins block == quartet, so the two can never drift apart). Unknown
|
|
206
|
+
// usage canonicalizes to the "0 = unknown" convention, consistent
|
|
207
|
+
// with the quartet above. PH-1c: the PRICE keys on the record's own
|
|
208
|
+
// MODEL via the metadata registry (route-keyed pricing priced an
|
|
209
|
+
// Anthropic run at DeepSeek's rates); the convention still keys on
|
|
210
|
+
// the route. An unpriced model costs null — never a fallback rate.
|
|
211
|
+
record.canonical = canonicalizeUsageForModel(record.model, undefined, this.#provider, {
|
|
209
212
|
inputTokens: p.inputTokens,
|
|
210
213
|
outputTokens: p.outputTokens,
|
|
211
214
|
cacheRead: p.cacheRead,
|
|
@@ -111,6 +111,21 @@ export declare function priceFor(route: string, u: {
|
|
|
111
111
|
* builtin v1 table so the export's default behavior is the pinned one
|
|
112
112
|
* and a future injection never widens this frozen signature. */
|
|
113
113
|
export declare function canonicalizeUsage(route: string, raw: RawUsage, table?: PricingTable): CanonicalUsage;
|
|
114
|
+
/**
|
|
115
|
+
* PH-1c — the MODEL-KEYED cost path (findings PH-F14/PH-F16). The
|
|
116
|
+
* convention still keys on the ROUTE (a protocol property: fresh vs
|
|
117
|
+
* total input); the PRICE keys on the model+endpoint metadata entry —
|
|
118
|
+
* the route-keyed table is how an Anthropic run got priced at
|
|
119
|
+
* DeepSeek's rates. No metadata entry, or an entry with `pricing:
|
|
120
|
+
* null`, costs null — the honest stamp, never a fallback rate.
|
|
121
|
+
*
|
|
122
|
+
* A NEW function beside canonicalizeUsage, not a widened signature —
|
|
123
|
+
* the E2 ruling froze that one ("a future injection never widens this
|
|
124
|
+
* frozen signature"), and this is the future it reserved room for.
|
|
125
|
+
* Metadata-priced records stamp pricingTableId "metadata" (version 1);
|
|
126
|
+
* the (id, version) tuple ritual carries forward unchanged.
|
|
127
|
+
*/
|
|
128
|
+
export declare function canonicalizeUsageForModel(model: string, endpoint: string | undefined, route: string, raw: RawUsage): CanonicalUsage;
|
|
114
129
|
/** The canonical schema's invariants, machine-checked: closed key set;
|
|
115
130
|
* non-negative numbers; cacheWrite/reasoning null-or-number; the pinned
|
|
116
131
|
* sentence — cacheRead can never exceed total (= input + cacheRead +
|
package/dist/usage/canonical.js
CHANGED
|
@@ -27,6 +27,7 @@
|
|
|
27
27
|
* accounting boundary (R4 Case B, pure derivation; the union does not
|
|
28
28
|
* move). Raw is provider observation; canonical is the accounting truth.
|
|
29
29
|
*/
|
|
30
|
+
import { lookupModelMetadata } from "../provider/metadata.js";
|
|
30
31
|
/** The route → input-convention table (R2a). `input` semantics differ
|
|
31
32
|
* between the two routes; the convention is a property of the ROUTE, not
|
|
32
33
|
* the provider. Unknown routes fall back to "total" — exactly the
|
|
@@ -61,15 +62,14 @@ export function pricingTableFor(version) {
|
|
|
61
62
|
return table;
|
|
62
63
|
}
|
|
63
64
|
export function priceFor(route, u, table) {
|
|
64
|
-
//
|
|
65
|
-
//
|
|
66
|
-
//
|
|
67
|
-
//
|
|
68
|
-
//
|
|
69
|
-
//
|
|
70
|
-
//
|
|
71
|
-
const entry = table.entries[route]
|
|
72
|
-
(route in INPUT_CONVENTIONS ? undefined : table.entries["openai-compat"]);
|
|
65
|
+
// PH-1a (supersedes R5b-④c's mirror fallback): a route missing from
|
|
66
|
+
// the table is a HOLE → null, the explicit-absent stamp — REAL routes
|
|
67
|
+
// and unknown routes alike. The retired mirror fallback priced the
|
|
68
|
+
// "adapter" route (a directly-injected adapter, finding PH-F7) at the
|
|
69
|
+
// openai-compat entry: a fabricated cost on exactly the runs whose
|
|
70
|
+
// rates are unknown. null is the honest answer; the builtin table
|
|
71
|
+
// never backfills an injected table's hole either.
|
|
72
|
+
const entry = table.entries[route];
|
|
73
73
|
if (entry === undefined)
|
|
74
74
|
return null;
|
|
75
75
|
return ((u.input * entry.inputPerM +
|
|
@@ -107,6 +107,37 @@ export function canonicalizeUsage(route, raw, table = PRICING_TABLE_V1) {
|
|
|
107
107
|
pricingTableVersion: table.version,
|
|
108
108
|
};
|
|
109
109
|
}
|
|
110
|
+
/**
|
|
111
|
+
* PH-1c — the MODEL-KEYED cost path (findings PH-F14/PH-F16). The
|
|
112
|
+
* convention still keys on the ROUTE (a protocol property: fresh vs
|
|
113
|
+
* total input); the PRICE keys on the model+endpoint metadata entry —
|
|
114
|
+
* the route-keyed table is how an Anthropic run got priced at
|
|
115
|
+
* DeepSeek's rates. No metadata entry, or an entry with `pricing:
|
|
116
|
+
* null`, costs null — the honest stamp, never a fallback rate.
|
|
117
|
+
*
|
|
118
|
+
* A NEW function beside canonicalizeUsage, not a widened signature —
|
|
119
|
+
* the E2 ruling froze that one ("a future injection never widens this
|
|
120
|
+
* frozen signature"), and this is the future it reserved room for.
|
|
121
|
+
* Metadata-priced records stamp pricingTableId "metadata" (version 1);
|
|
122
|
+
* the (id, version) tuple ritual carries forward unchanged.
|
|
123
|
+
*/
|
|
124
|
+
export function canonicalizeUsageForModel(model, endpoint, route, raw) {
|
|
125
|
+
const base = canonicalizeUsage(route, raw, EMPTY_TABLE); // convention math only — EMPTY_TABLE prices nothing
|
|
126
|
+
const entry = lookupModelMetadata(model, endpoint);
|
|
127
|
+
const pricing = entry?.pricing ?? null;
|
|
128
|
+
if (pricing === null) {
|
|
129
|
+
return { ...base, costUsd: null, pricingTableId: "metadata", pricingTableVersion: 1 };
|
|
130
|
+
}
|
|
131
|
+
const costUsd = (base.input * pricing.inputPerM +
|
|
132
|
+
base.output * pricing.outputPerM +
|
|
133
|
+
base.cacheRead * pricing.cacheReadPerM +
|
|
134
|
+
(base.cacheWrite ?? 0) * pricing.cacheWritePerM) /
|
|
135
|
+
1e6;
|
|
136
|
+
return { ...base, costUsd, pricingTableId: "metadata", pricingTableVersion: 1 };
|
|
137
|
+
}
|
|
138
|
+
/** No entries at all — canonicalizeUsageForModel's convention-only pass
|
|
139
|
+
* through canonicalizeUsage; every price it could produce is null. */
|
|
140
|
+
const EMPTY_TABLE = { id: "metadata", version: 1, entries: {} };
|
|
110
141
|
// ── The validator ──────────────────────────────────────────────────────────
|
|
111
142
|
// Strict by design: closed keys (a misspelled field can never enter a
|
|
112
143
|
// ledger), non-negative numbers, the pinned sentence machine-checked, and
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vincemakes/kiso-runtime",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.15.1",
|
|
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",
|
|
@@ -25,11 +25,11 @@
|
|
|
25
25
|
"test": "vitest run"
|
|
26
26
|
},
|
|
27
27
|
"dependencies": {
|
|
28
|
-
"@vincemakes/kiso-core": "0.
|
|
28
|
+
"@vincemakes/kiso-core": "0.15.1"
|
|
29
29
|
},
|
|
30
30
|
"peerDependencies": {
|
|
31
|
-
"@vincemakes/kiso-provider-anthropic": "0.
|
|
32
|
-
"@vincemakes/kiso-provider-openai": "0.
|
|
31
|
+
"@vincemakes/kiso-provider-anthropic": "0.15.1",
|
|
32
|
+
"@vincemakes/kiso-provider-openai": "0.15.1"
|
|
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.
|
|
43
|
+
"@vincemakes/kiso-evals": "0.15.1",
|
|
44
44
|
"@types/node": "^26.1.2",
|
|
45
45
|
"typescript": "^5.7.2",
|
|
46
46
|
"vitest": "^3.0.0"
|