@vincemakes/kiso-runtime 0.1.38 → 0.2.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 +1 -0
- package/dist/agent.js +2 -0
- package/dist/index.d.ts +32 -11
- package/dist/index.js +41 -11
- package/dist/internal.d.ts +25 -0
- package/dist/internal.js +25 -0
- package/dist/lock-adapter.js +46 -1
- package/dist/run.js +40 -5
- package/dist/session.d.ts +9 -0
- package/dist/session.js +8 -0
- package/dist/trace/analyze.d.ts +35 -0
- package/dist/trace/analyze.js +51 -0
- package/dist/trace/guard.d.ts +52 -0
- package/dist/trace/guard.js +224 -0
- package/dist/trace/hash.d.ts +22 -0
- package/dist/trace/hash.js +34 -0
- package/dist/trace/manifest.d.ts +36 -0
- package/dist/trace/manifest.js +100 -0
- package/dist/trace/record.d.ts +145 -0
- package/dist/trace/record.js +257 -0
- package/dist/trace/rent.d.ts +81 -0
- package/dist/trace/rent.js +96 -0
- package/dist/trace/writer.d.ts +43 -0
- package/dist/trace/writer.js +154 -0
- package/dist/usage/canonical.d.ts +123 -0
- package/dist/usage/canonical.js +152 -0
- package/package.json +10 -6
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* E2 (1.3.0) — Canonical Usage: the reconciled usage schema (proposal §1
|
|
3
|
+
* as ruled 2026-08-13; R1a-R5 all adopted).
|
|
4
|
+
*
|
|
5
|
+
* THE PINNED SENTENCE (R1a): one canonical meaning for `input`, everywhere
|
|
6
|
+
* in kiso, forever: `input` is FRESH-ONLY — the tokens NOT served from
|
|
7
|
+
* cache. `total = input + cacheRead + cacheWrite` is the derived quantity,
|
|
8
|
+
* never a reported one. A cache ratio is always `cacheRead / total` — the
|
|
9
|
+
* numerator can never exceed the denominator. The >100% disease is
|
|
10
|
+
* structurally impossible under the canonical schema.
|
|
11
|
+
*
|
|
12
|
+
* The mapping keys on the ROUTE, not the provider (R2a): DeepSeek serves
|
|
13
|
+
* both routes and reports input fresh-only on one (anthropic-compat) and
|
|
14
|
+
* TOTAL on the other (openai-compat) — the SAME canonical meaning falls
|
|
15
|
+
* out of the route key (the dual-endpoint closure). The route string is
|
|
16
|
+
* the config adapter identity the guard already holds (`"anthropic" |
|
|
17
|
+
* "openai-compat"`).
|
|
18
|
+
*
|
|
19
|
+
* Cost comes from the versioned pricing table — every cost carries the
|
|
20
|
+
* table version it was computed with (R1c). The built-in v1 table prices
|
|
21
|
+
* both routes at DeepSeek's published rates; the caveat sentence from the
|
|
22
|
+
* bench README carries forward verbatim ("an approximation, not a bill").
|
|
23
|
+
* The commercial table is the product's Phase 10 (the table is injectable;
|
|
24
|
+
* the version travels with every cost).
|
|
25
|
+
*
|
|
26
|
+
* The frozen usage union stays provider-raw — this module derives at the
|
|
27
|
+
* accounting boundary (R4 Case B, pure derivation; the union does not
|
|
28
|
+
* move). Raw is provider observation; canonical is the accounting truth.
|
|
29
|
+
*/
|
|
30
|
+
/** The union's usage shape (provider-raw — core protocol `events.Usage`). */
|
|
31
|
+
export interface RawUsage {
|
|
32
|
+
readonly inputTokens: number | null;
|
|
33
|
+
readonly outputTokens: number | null;
|
|
34
|
+
readonly cacheRead: number | null;
|
|
35
|
+
readonly cacheWrite: number | null;
|
|
36
|
+
}
|
|
37
|
+
/** The ONE canonical usage record. Every field has exactly one meaning
|
|
38
|
+
* (proposal §1.2). wall/ttft live in the trace record (latencyMs/ttftMs)
|
|
39
|
+
* — one source, never duplicated here. */
|
|
40
|
+
export interface CanonicalUsage {
|
|
41
|
+
/** FRESH-ONLY — the pinned sentence above. total = input + cacheRead +
|
|
42
|
+
* cacheWrite is the derived quantity, never a reported one. */
|
|
43
|
+
readonly input: number;
|
|
44
|
+
readonly output: number;
|
|
45
|
+
readonly cacheRead: number;
|
|
46
|
+
/** null = the provider reports none (openai-compat honestly does). */
|
|
47
|
+
readonly cacheWrite: number | null;
|
|
48
|
+
/** null = no reasoning split reported (reserved — every provider today). */
|
|
49
|
+
readonly reasoning: number | null;
|
|
50
|
+
/** USD from the pricing table below — null is the R5b-④c ABSENT stamp:
|
|
51
|
+
* the table has no rate for this route (an injected table's hole is
|
|
52
|
+
* explicit absent, never backfilled from the builtin table). The
|
|
53
|
+
* builtin v1 table covers both real routes, so the null branch is
|
|
54
|
+
* unreachable today — but the type must be able to express it.
|
|
55
|
+
* Never reported without its (pricingTableId, pricingTableVersion)
|
|
56
|
+
* tuple — the billing-basis ground. */
|
|
57
|
+
readonly costUsd: number | null;
|
|
58
|
+
/** The table the cost was computed with (R5b-④b): the (id, version)
|
|
59
|
+
* two-tuple stamp — cross-table version reconciliation and billing
|
|
60
|
+
* disputes answerable. "builtin" = the pinned in-repo table below;
|
|
61
|
+
* injected tables carry their own id. */
|
|
62
|
+
readonly pricingTableId: string;
|
|
63
|
+
readonly pricingTableVersion: number;
|
|
64
|
+
}
|
|
65
|
+
/** The versioned pricing table — a rate change is a version bump, never an
|
|
66
|
+
* edit; every cost records the version it was computed with (R1c). The
|
|
67
|
+
* id is the table's identity (R5b-④b): "builtin" for the pinned in-repo
|
|
68
|
+
* table, anything else for an injected commercial table. */
|
|
69
|
+
export interface PricingTable {
|
|
70
|
+
readonly id: string;
|
|
71
|
+
readonly version: number;
|
|
72
|
+
readonly entries: Readonly<Record<string, PricingEntry>>;
|
|
73
|
+
}
|
|
74
|
+
export interface PricingEntry {
|
|
75
|
+
/** USD per 1M FRESH input tokens. */
|
|
76
|
+
readonly inputPerM: number;
|
|
77
|
+
readonly outputPerM: number;
|
|
78
|
+
readonly cacheReadPerM: number;
|
|
79
|
+
/** USD per 1M cache-creation tokens. 0 = the provider does not price
|
|
80
|
+
* cache writes separately (DeepSeek's automatic caching). */
|
|
81
|
+
readonly cacheWritePerM: number;
|
|
82
|
+
}
|
|
83
|
+
/** The route → input-convention table (R2a). `input` semantics differ
|
|
84
|
+
* between the two routes; the convention is a property of the ROUTE, not
|
|
85
|
+
* the provider. Unknown routes fall back to "total" — exactly the
|
|
86
|
+
* incumbent guard behavior (the guard's else-branch: non-anthropic =
|
|
87
|
+
* total), preserved by construction. */
|
|
88
|
+
export declare const INPUT_CONVENTIONS: Readonly<Record<string, "fresh" | "total">>;
|
|
89
|
+
/** Pricing table v1. Freeze date 2026-08-13 (the E2 ruling). Rates:
|
|
90
|
+
* DeepSeek's published rates (https://api-docs.deepseek.com/quick_start/pricing),
|
|
91
|
+
* the 0.1 cache-hit ratio the bench has used since the 0.1.23 round. The
|
|
92
|
+
* caveat sentence carries forward verbatim (bench README): "an
|
|
93
|
+
* approximation, not a bill." */
|
|
94
|
+
export declare const PRICING_TABLE_V1: PricingTable;
|
|
95
|
+
export declare const PRICING_TABLES: Readonly<Record<number, PricingTable>>;
|
|
96
|
+
/** The table pinned for a version; throws when none is pinned (a cost
|
|
97
|
+
* recorded against an unpinned version is a ledger corruption). */
|
|
98
|
+
export declare function pricingTableFor(version: number): PricingTable;
|
|
99
|
+
export declare function priceFor(route: string, u: {
|
|
100
|
+
input: number;
|
|
101
|
+
output: number;
|
|
102
|
+
cacheRead: number;
|
|
103
|
+
cacheWrite: number | null;
|
|
104
|
+
}, table: PricingTable): number | null;
|
|
105
|
+
/** Raw → canonical (the accounting boundary). Behavior-preserving against
|
|
106
|
+
* the guard's incumbent per-provider branch (guard.ts settle): anthropic
|
|
107
|
+
* input is fresh as-is; every other route subtracts cacheRead.
|
|
108
|
+
*
|
|
109
|
+
* The trailing `table` parameter is the injection slot (R5b-④a): the
|
|
110
|
+
* R5a-1-commercial table rides it on day one, defaulting to the pinned
|
|
111
|
+
* builtin v1 table so the export's default behavior is the pinned one
|
|
112
|
+
* and a future injection never widens this frozen signature. */
|
|
113
|
+
export declare function canonicalizeUsage(route: string, raw: RawUsage, table?: PricingTable): CanonicalUsage;
|
|
114
|
+
/** The canonical schema's invariants, machine-checked: closed key set;
|
|
115
|
+
* non-negative numbers; cacheWrite/reasoning null-or-number; the pinned
|
|
116
|
+
* sentence — cacheRead can never exceed total (= input + cacheRead +
|
|
117
|
+
* cacheWrite) — structurally true for non-negatives but pinned against
|
|
118
|
+
* future edits; costUsd null-or-number (R5b-④c — null is the injected
|
|
119
|
+
* table's absent stamp); the (pricingTableId, pricingTableVersion)
|
|
120
|
+
* tuple, with the registry pin scoped to the builtin id (R5b-④b) — an
|
|
121
|
+
* injected table's version is the injector's accounting, the ledger
|
|
122
|
+
* cannot know tables it does not carry. */
|
|
123
|
+
export declare function validateCanonicalUsage(v: unknown): v is CanonicalUsage;
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* E2 (1.3.0) — Canonical Usage: the reconciled usage schema (proposal §1
|
|
3
|
+
* as ruled 2026-08-13; R1a-R5 all adopted).
|
|
4
|
+
*
|
|
5
|
+
* THE PINNED SENTENCE (R1a): one canonical meaning for `input`, everywhere
|
|
6
|
+
* in kiso, forever: `input` is FRESH-ONLY — the tokens NOT served from
|
|
7
|
+
* cache. `total = input + cacheRead + cacheWrite` is the derived quantity,
|
|
8
|
+
* never a reported one. A cache ratio is always `cacheRead / total` — the
|
|
9
|
+
* numerator can never exceed the denominator. The >100% disease is
|
|
10
|
+
* structurally impossible under the canonical schema.
|
|
11
|
+
*
|
|
12
|
+
* The mapping keys on the ROUTE, not the provider (R2a): DeepSeek serves
|
|
13
|
+
* both routes and reports input fresh-only on one (anthropic-compat) and
|
|
14
|
+
* TOTAL on the other (openai-compat) — the SAME canonical meaning falls
|
|
15
|
+
* out of the route key (the dual-endpoint closure). The route string is
|
|
16
|
+
* the config adapter identity the guard already holds (`"anthropic" |
|
|
17
|
+
* "openai-compat"`).
|
|
18
|
+
*
|
|
19
|
+
* Cost comes from the versioned pricing table — every cost carries the
|
|
20
|
+
* table version it was computed with (R1c). The built-in v1 table prices
|
|
21
|
+
* both routes at DeepSeek's published rates; the caveat sentence from the
|
|
22
|
+
* bench README carries forward verbatim ("an approximation, not a bill").
|
|
23
|
+
* The commercial table is the product's Phase 10 (the table is injectable;
|
|
24
|
+
* the version travels with every cost).
|
|
25
|
+
*
|
|
26
|
+
* The frozen usage union stays provider-raw — this module derives at the
|
|
27
|
+
* accounting boundary (R4 Case B, pure derivation; the union does not
|
|
28
|
+
* move). Raw is provider observation; canonical is the accounting truth.
|
|
29
|
+
*/
|
|
30
|
+
/** The route → input-convention table (R2a). `input` semantics differ
|
|
31
|
+
* between the two routes; the convention is a property of the ROUTE, not
|
|
32
|
+
* the provider. Unknown routes fall back to "total" — exactly the
|
|
33
|
+
* incumbent guard behavior (the guard's else-branch: non-anthropic =
|
|
34
|
+
* total), preserved by construction. */
|
|
35
|
+
export const INPUT_CONVENTIONS = {
|
|
36
|
+
anthropic: "fresh", // input_tokens excludes the cached prefix
|
|
37
|
+
"openai-compat": "total", // prompt_tokens includes the cached prefix
|
|
38
|
+
};
|
|
39
|
+
/** Pricing table v1. Freeze date 2026-08-13 (the E2 ruling). Rates:
|
|
40
|
+
* DeepSeek's published rates (https://api-docs.deepseek.com/quick_start/pricing),
|
|
41
|
+
* the 0.1 cache-hit ratio the bench has used since the 0.1.23 round. The
|
|
42
|
+
* caveat sentence carries forward verbatim (bench README): "an
|
|
43
|
+
* approximation, not a bill." */
|
|
44
|
+
export const PRICING_TABLE_V1 = {
|
|
45
|
+
id: "builtin",
|
|
46
|
+
version: 1,
|
|
47
|
+
entries: {
|
|
48
|
+
anthropic: { inputPerM: 0.27, outputPerM: 1.1, cacheReadPerM: 0.027, cacheWritePerM: 0 },
|
|
49
|
+
"openai-compat": { inputPerM: 0.27, outputPerM: 1.1, cacheReadPerM: 0.027, cacheWritePerM: 0 },
|
|
50
|
+
},
|
|
51
|
+
};
|
|
52
|
+
export const PRICING_TABLES = {
|
|
53
|
+
1: PRICING_TABLE_V1,
|
|
54
|
+
};
|
|
55
|
+
/** The table pinned for a version; throws when none is pinned (a cost
|
|
56
|
+
* recorded against an unpinned version is a ledger corruption). */
|
|
57
|
+
export function pricingTableFor(version) {
|
|
58
|
+
const table = PRICING_TABLES[version];
|
|
59
|
+
if (table === undefined)
|
|
60
|
+
throw new Error(`no pricing table pinned for version ${version}`);
|
|
61
|
+
return table;
|
|
62
|
+
}
|
|
63
|
+
export function priceFor(route, u, table) {
|
|
64
|
+
// The R5b-④c semantics: a REAL route (an INPUT_CONVENTIONS key — the
|
|
65
|
+
// routes the table is expected to price) missing from the table is a
|
|
66
|
+
// HOLE → null, the explicit-absent stamp. The builtin table never
|
|
67
|
+
// backfills an injected table's hole. Unknown routes keep the legacy
|
|
68
|
+
// within-table mirror fallback (the total-convention entry, matching
|
|
69
|
+
// the convention fallback above — one table, one fallback, no drift);
|
|
70
|
+
// a table without even the mirror entry yields null, never a crash.
|
|
71
|
+
const entry = table.entries[route] ??
|
|
72
|
+
(route in INPUT_CONVENTIONS ? undefined : table.entries["openai-compat"]);
|
|
73
|
+
if (entry === undefined)
|
|
74
|
+
return null;
|
|
75
|
+
return ((u.input * entry.inputPerM +
|
|
76
|
+
u.output * entry.outputPerM +
|
|
77
|
+
u.cacheRead * entry.cacheReadPerM +
|
|
78
|
+
(u.cacheWrite ?? 0) * entry.cacheWritePerM) /
|
|
79
|
+
1e6);
|
|
80
|
+
}
|
|
81
|
+
/** Raw → canonical (the accounting boundary). Behavior-preserving against
|
|
82
|
+
* the guard's incumbent per-provider branch (guard.ts settle): anthropic
|
|
83
|
+
* input is fresh as-is; every other route subtracts cacheRead.
|
|
84
|
+
*
|
|
85
|
+
* The trailing `table` parameter is the injection slot (R5b-④a): the
|
|
86
|
+
* R5a-1-commercial table rides it on day one, defaulting to the pinned
|
|
87
|
+
* builtin v1 table so the export's default behavior is the pinned one
|
|
88
|
+
* and a future injection never widens this frozen signature. */
|
|
89
|
+
export function canonicalizeUsage(route, raw, table = PRICING_TABLE_V1) {
|
|
90
|
+
const convention = INPUT_CONVENTIONS[route] ?? "total";
|
|
91
|
+
const input = raw.inputTokens === null
|
|
92
|
+
? 0
|
|
93
|
+
: convention === "fresh"
|
|
94
|
+
? raw.inputTokens
|
|
95
|
+
: Math.max(0, raw.inputTokens - (raw.cacheRead ?? 0));
|
|
96
|
+
const cacheRead = raw.cacheRead ?? 0;
|
|
97
|
+
const cacheWrite = raw.cacheWrite ?? null;
|
|
98
|
+
const output = raw.outputTokens ?? 0;
|
|
99
|
+
return {
|
|
100
|
+
input,
|
|
101
|
+
output,
|
|
102
|
+
cacheRead,
|
|
103
|
+
cacheWrite,
|
|
104
|
+
reasoning: null, // reserved — no provider reports a split today
|
|
105
|
+
costUsd: priceFor(route, { input, output, cacheRead, cacheWrite }, table),
|
|
106
|
+
pricingTableId: table.id,
|
|
107
|
+
pricingTableVersion: table.version,
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
// ── The validator ──────────────────────────────────────────────────────────
|
|
111
|
+
// Strict by design: closed keys (a misspelled field can never enter a
|
|
112
|
+
// ledger), non-negative numbers, the pinned sentence machine-checked, and
|
|
113
|
+
// a pinned table version. Cost CONSISTENCY (costUsd recomputed from the
|
|
114
|
+
// components × the version's table) needs the ROUTE — it lives in the
|
|
115
|
+
// trace record; the trace-level validator cross-checks it there (T3).
|
|
116
|
+
const isRecord = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
|
|
117
|
+
const CANONICAL_FIELDS = ["input", "output", "cacheRead", "cacheWrite", "reasoning", "costUsd", "pricingTableId", "pricingTableVersion"];
|
|
118
|
+
const isNonNegNum = (v) => typeof v === "number" && Number.isFinite(v) && v >= 0;
|
|
119
|
+
/** The canonical schema's invariants, machine-checked: closed key set;
|
|
120
|
+
* non-negative numbers; cacheWrite/reasoning null-or-number; the pinned
|
|
121
|
+
* sentence — cacheRead can never exceed total (= input + cacheRead +
|
|
122
|
+
* cacheWrite) — structurally true for non-negatives but pinned against
|
|
123
|
+
* future edits; costUsd null-or-number (R5b-④c — null is the injected
|
|
124
|
+
* table's absent stamp); the (pricingTableId, pricingTableVersion)
|
|
125
|
+
* tuple, with the registry pin scoped to the builtin id (R5b-④b) — an
|
|
126
|
+
* injected table's version is the injector's accounting, the ledger
|
|
127
|
+
* cannot know tables it does not carry. */
|
|
128
|
+
export function validateCanonicalUsage(v) {
|
|
129
|
+
if (!isRecord(v))
|
|
130
|
+
return false;
|
|
131
|
+
const keys = Object.keys(v);
|
|
132
|
+
if (keys.length !== CANONICAL_FIELDS.length || keys.some((k) => !CANONICAL_FIELDS.includes(k)))
|
|
133
|
+
return false;
|
|
134
|
+
if (!isNonNegNum(v.input) || !isNonNegNum(v.output) || !isNonNegNum(v.cacheRead))
|
|
135
|
+
return false;
|
|
136
|
+
if (v.cacheWrite !== null && !isNonNegNum(v.cacheWrite))
|
|
137
|
+
return false;
|
|
138
|
+
if (v.reasoning !== null && !isNonNegNum(v.reasoning))
|
|
139
|
+
return false;
|
|
140
|
+
if (v.costUsd !== null && !isNonNegNum(v.costUsd))
|
|
141
|
+
return false;
|
|
142
|
+
if (typeof v.pricingTableId !== "string" || v.pricingTableId.length === 0)
|
|
143
|
+
return false;
|
|
144
|
+
if (typeof v.pricingTableVersion !== "number" || !Number.isInteger(v.pricingTableVersion) || v.pricingTableVersion <= 0)
|
|
145
|
+
return false;
|
|
146
|
+
if (v.pricingTableId === PRICING_TABLE_V1.id && !(v.pricingTableVersion in PRICING_TABLES))
|
|
147
|
+
return false;
|
|
148
|
+
const total = v.input + v.cacheRead + (v.cacheWrite ?? 0);
|
|
149
|
+
if (v.cacheRead > total)
|
|
150
|
+
return false; // the pinned sentence
|
|
151
|
+
return true;
|
|
152
|
+
}
|
package/package.json
CHANGED
|
@@ -1,13 +1,17 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vincemakes/kiso-runtime",
|
|
3
|
-
"version": "0.1
|
|
4
|
-
"description": "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.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"exports": {
|
|
8
8
|
".": {
|
|
9
9
|
"types": "./dist/index.d.ts",
|
|
10
10
|
"default": "./dist/index.js"
|
|
11
|
+
},
|
|
12
|
+
"./internal": {
|
|
13
|
+
"types": "./dist/internal.d.ts",
|
|
14
|
+
"default": "./dist/internal.js"
|
|
11
15
|
}
|
|
12
16
|
},
|
|
13
17
|
"files": [
|
|
@@ -21,11 +25,11 @@
|
|
|
21
25
|
"test": "vitest run"
|
|
22
26
|
},
|
|
23
27
|
"dependencies": {
|
|
24
|
-
"@vincemakes/kiso-core": "0.
|
|
28
|
+
"@vincemakes/kiso-core": "0.2.0"
|
|
25
29
|
},
|
|
26
30
|
"peerDependencies": {
|
|
27
|
-
"@vincemakes/kiso-provider-anthropic": "0.
|
|
28
|
-
"@vincemakes/kiso-provider-openai": "0.
|
|
31
|
+
"@vincemakes/kiso-provider-anthropic": "0.2.0",
|
|
32
|
+
"@vincemakes/kiso-provider-openai": "0.2.0"
|
|
29
33
|
},
|
|
30
34
|
"peerDependenciesMeta": {
|
|
31
35
|
"@vincemakes/kiso-provider-anthropic": {
|
|
@@ -36,7 +40,7 @@
|
|
|
36
40
|
}
|
|
37
41
|
},
|
|
38
42
|
"devDependencies": {
|
|
39
|
-
"@vincemakes/kiso-evals": "0.
|
|
43
|
+
"@vincemakes/kiso-evals": "0.2.0",
|
|
40
44
|
"@types/node": "^26.1.2",
|
|
41
45
|
"typescript": "^5.7.2",
|
|
42
46
|
"vitest": "^3.0.0"
|