@vincemakes/kiso-runtime 0.2.0 → 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/run.js +24 -9
- package/dist/trace/guard.d.ts +6 -0
- package/dist/trace/guard.js +16 -0
- package/dist/trace/record.d.ts +27 -11
- package/dist/trace/record.js +33 -13
- package/dist/trace/rent.d.ts +81 -0
- package/dist/trace/rent.js +96 -0
- package/package.json +2 -2
package/dist/run.js
CHANGED
|
@@ -57,6 +57,29 @@ export class Run {
|
|
|
57
57
|
this.#session.ensureHealthy();
|
|
58
58
|
this.#session.beginRun(this);
|
|
59
59
|
const log = this.#session.log;
|
|
60
|
+
// The static prompt parts — computed ONCE, before the tracer, and
|
|
61
|
+
// reused for both the composed string (below) and the rent ledger
|
|
62
|
+
// (E3): a single evaluation keeps the model-visible byte stream
|
|
63
|
+
// byte-identical to the pre-E3 run (I6, trace-bytes.test.ts).
|
|
64
|
+
// 0.1.40 (R-C item 1): the tool substitution table — the ACTIVE tool
|
|
65
|
+
// set's vocabulary, snippets, and guidelines — sits BETWEEN the
|
|
66
|
+
// session's base prompt and the extension appends: generated
|
|
67
|
+
// machinery never outranks the deliberate extension text (the E2
|
|
68
|
+
// "append lands at the END" contract holds). "" when empty.
|
|
69
|
+
const toolTable = composeToolTable(this.#config.registry);
|
|
70
|
+
const basePrompt = toolTable === "" ? this.#config.systemPrompt
|
|
71
|
+
: this.#config.systemPrompt === undefined ? toolTable
|
|
72
|
+
: `${this.#config.systemPrompt}\n\n${toolTable}`;
|
|
73
|
+
// E3 — the ledger's parts: the base as CONFIGURED (what the CLI
|
|
74
|
+
// handed the runtime — the tool table is generated machinery, R3)
|
|
75
|
+
// and the extension appends in load order (R4 attribution). The
|
|
76
|
+
// composed string below is their result; the ledger counts the
|
|
77
|
+
// parts, observation-only. exactOptionalPropertyTypes: an absent
|
|
78
|
+
// surface is an absent key — never an explicit undefined (R9).
|
|
79
|
+
const rentParts = {
|
|
80
|
+
...(this.#config.systemPrompt !== undefined ? { base: this.#config.systemPrompt } : {}),
|
|
81
|
+
appends: (this.#config.extensions ?? []).flatMap((e) => e.systemPrompt?.append === undefined ? [] : [{ name: e.name, text: e.systemPrompt.append }]),
|
|
82
|
+
};
|
|
60
83
|
// E1 (1.2.0): the request tracer — the observation ledger. It
|
|
61
84
|
// sits at the adapter boundary; the model-visible byte stream is
|
|
62
85
|
// untouched (I6, trace-bytes.test.ts). Soft-fail: a degraded
|
|
@@ -69,21 +92,13 @@ export class Run {
|
|
|
69
92
|
model: this.#config.model,
|
|
70
93
|
adapterVersion: runtimeVersion(),
|
|
71
94
|
log: log.all,
|
|
95
|
+
rentParts,
|
|
72
96
|
});
|
|
73
97
|
tracer.init();
|
|
74
98
|
const signal = this.#externalSignal ? new MergedSignal(this.#abort.signal, this.#externalSignal) : this.#abort.signal;
|
|
75
99
|
// E2: the session's own microcompact wins; otherwise the FIRST
|
|
76
100
|
// extension providing a compaction config supplies it.
|
|
77
101
|
const microcompact = microcompactFor(this.#config);
|
|
78
|
-
// 0.1.40 (R-C item 1): the tool substitution table — the ACTIVE tool
|
|
79
|
-
// set's vocabulary, snippets, and guidelines — sits BETWEEN the
|
|
80
|
-
// session's base prompt and the extension appends: generated
|
|
81
|
-
// machinery never outranks the deliberate extension text (the E2
|
|
82
|
-
// "append lands at the END" contract holds). "" when empty.
|
|
83
|
-
const toolTable = composeToolTable(this.#config.registry);
|
|
84
|
-
const basePrompt = toolTable === "" ? this.#config.systemPrompt
|
|
85
|
-
: this.#config.systemPrompt === undefined ? toolTable
|
|
86
|
-
: `${this.#config.systemPrompt}\n\n${toolTable}`;
|
|
87
102
|
// E2: the session's own systemPrompt first, then every extension
|
|
88
103
|
// append in LOAD order — deterministic (same extensions → same
|
|
89
104
|
// prompt); no appends → byte-identical to the extension-less run.
|
package/dist/trace/guard.d.ts
CHANGED
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
* the honest nullable quartet is a schema bump, deferred.
|
|
23
23
|
*/
|
|
24
24
|
import type { Adapter, AdapterEvent, Event, StreamOptions } from "@vincemakes/kiso-core";
|
|
25
|
+
import { type RentParts } from "./rent.js";
|
|
25
26
|
export interface RequestTracerDeps {
|
|
26
27
|
root: string;
|
|
27
28
|
sessionId: string;
|
|
@@ -33,6 +34,11 @@ export interface RequestTracerDeps {
|
|
|
33
34
|
adapterVersion?: string | null;
|
|
34
35
|
/** The session log — the manifest's seqRange pointers derive from it. */
|
|
35
36
|
log: readonly Event[];
|
|
37
|
+
/** E3 — the rent ledger's inputs: the base prompt as configured and
|
|
38
|
+
* the per-extension appends in load order (the adapter's composed
|
|
39
|
+
* systemPrompt is their RESULT — the parts are what the ledger
|
|
40
|
+
* counts; the composed string itself is unchanged, I6). */
|
|
41
|
+
rentParts?: RentParts;
|
|
36
42
|
}
|
|
37
43
|
export declare class RequestTracer {
|
|
38
44
|
#private;
|
package/dist/trace/guard.js
CHANGED
|
@@ -26,6 +26,7 @@ import { buildContextManifest, segmentHashes } from "./manifest.js";
|
|
|
26
26
|
import { cacheableHashes } from "./analyze.js";
|
|
27
27
|
import { hashContext, hashSystemPrompt, hashToolSpecs, stablePrefixFingerprint } from "./hash.js";
|
|
28
28
|
import { PRICING_TABLE_V1, canonicalizeUsage } from "../usage/canonical.js";
|
|
29
|
+
import { buildRentLedger } from "./rent.js";
|
|
29
30
|
import { TRACE_SCHEMA_VERSION } from "./record.js";
|
|
30
31
|
import { TraceWriter } from "./writer.js";
|
|
31
32
|
export class RequestTracer {
|
|
@@ -34,6 +35,7 @@ export class RequestTracer {
|
|
|
34
35
|
#provider;
|
|
35
36
|
#runId;
|
|
36
37
|
#adapterVersion;
|
|
38
|
+
#rentParts;
|
|
37
39
|
#requestIndex = 0;
|
|
38
40
|
#contextHashCounts = new Map();
|
|
39
41
|
constructor(deps) {
|
|
@@ -42,6 +44,7 @@ export class RequestTracer {
|
|
|
42
44
|
this.#provider = deps.provider;
|
|
43
45
|
this.#runId = deps.runId;
|
|
44
46
|
this.#adapterVersion = deps.adapterVersion ?? null;
|
|
47
|
+
this.#rentParts = deps.rentParts;
|
|
45
48
|
}
|
|
46
49
|
init() {
|
|
47
50
|
this.#writer.init();
|
|
@@ -119,6 +122,18 @@ export class RequestTracer {
|
|
|
119
122
|
this.#contextHashCounts.set(contextHash, retryAttempt + 1);
|
|
120
123
|
const manifest = buildContextManifest({ log: this.#log, systemPrompt, tools, messages });
|
|
121
124
|
const hashes = segmentHashes(systemPrompt, tools, messages);
|
|
125
|
+
// E3 — the static rent ledger, one line per surface; the envelope
|
|
126
|
+
// derives from the request the guard already has (R5), the base and
|
|
127
|
+
// the appends from the threaded parts (R3/R4 — observation only).
|
|
128
|
+
// exactOptionalPropertyTypes: an absent surface is an absent key —
|
|
129
|
+
// never an explicit undefined (R9).
|
|
130
|
+
const rentParts = this.#rentParts;
|
|
131
|
+
const rent = buildRentLedger({
|
|
132
|
+
model: options.model,
|
|
133
|
+
...(rentParts?.base !== undefined ? { base: rentParts.base } : {}),
|
|
134
|
+
...(rentParts?.appends !== undefined ? { appends: rentParts.appends } : {}),
|
|
135
|
+
...(tools !== undefined ? { tools } : {}),
|
|
136
|
+
});
|
|
122
137
|
return {
|
|
123
138
|
schemaVersion: TRACE_SCHEMA_VERSION,
|
|
124
139
|
kind: "request",
|
|
@@ -156,6 +171,7 @@ export class RequestTracer {
|
|
|
156
171
|
pricingTableId: PRICING_TABLE_V1.id,
|
|
157
172
|
pricingTableVersion: PRICING_TABLE_V1.version,
|
|
158
173
|
},
|
|
174
|
+
rent,
|
|
159
175
|
latencyMs: 0,
|
|
160
176
|
ttftMs: 0,
|
|
161
177
|
toolCalls: [],
|
package/dist/trace/record.d.ts
CHANGED
|
@@ -19,15 +19,24 @@
|
|
|
19
19
|
* quartet stays as provider observation above it). The validators accept
|
|
20
20
|
* BOTH generations (R1d-1): a v1 sidecar has no canonical block and reads
|
|
21
21
|
* as defaults at every consumer — never a crash.
|
|
22
|
+
*
|
|
23
|
+
* E3 (0.2.1) — schemaVersion 3: the record gains the `rent` block — the
|
|
24
|
+
* static rent ledger, one line per surface (trace/rent.ts). The v3
|
|
25
|
+
* writers record it; v1/v2 sidecars keep reading as defaults (R1d-1,
|
|
26
|
+
* R2-1): no rent block = no rent lines = the zero-rent reading, never a
|
|
27
|
+
* crash.
|
|
22
28
|
*/
|
|
23
|
-
/** schemaVersion:
|
|
24
|
-
* shape,
|
|
25
|
-
* changes bump it (ADR-0051 §6
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
+
/** schemaVersion: 3 for 0.2.1 (the rent block). Version 1 = the 1.2.0
|
|
30
|
+
* shape, version 2 = the 1.3.0 shape; both kept for generation-compat
|
|
31
|
+
* reads (R1d-1, R2-1). Algorithm and shape changes bump it (ADR-0051 §6
|
|
32
|
+
* OUT-side versioning). */
|
|
33
|
+
export declare const TRACE_SCHEMA_VERSION = 3;
|
|
34
|
+
/** The versions a reader may meet in a ledger. v1 and v2 records are
|
|
35
|
+
* accepted (generation-compat) and read as defaults — no canonical
|
|
36
|
+
* block (v1), no rent block (v1, v2). */
|
|
29
37
|
export declare const TRACE_SCHEMA_VERSIONS: Readonly<Set<number>>;
|
|
30
38
|
import type { CanonicalUsage } from "../usage/canonical.js";
|
|
39
|
+
import { type RentLine } from "./rent.js";
|
|
31
40
|
export type Freshness = "fresh" | "cache_read" | "cache_write";
|
|
32
41
|
/** That is the complete set for 1.2.0. */
|
|
33
42
|
export type Outcome = "ok" | "provider_error" | "aborted";
|
|
@@ -43,7 +52,7 @@ export interface TraceSegment {
|
|
|
43
52
|
}
|
|
44
53
|
/** That is the complete set for 1.2.0. */
|
|
45
54
|
export interface TraceRecord {
|
|
46
|
-
schemaVersion:
|
|
55
|
+
schemaVersion: 3;
|
|
47
56
|
kind: "request";
|
|
48
57
|
requestId: string;
|
|
49
58
|
runId: string;
|
|
@@ -77,6 +86,11 @@ export interface TraceRecord {
|
|
|
77
86
|
* the validator pins the equality — and carries the cost from the
|
|
78
87
|
* versioned pricing table (every cost records its table version). */
|
|
79
88
|
canonical: CanonicalUsage;
|
|
89
|
+
/** E3 — the static rent ledger: one line per surface (system:base,
|
|
90
|
+
* system:ext:<name>, tool:<name>, envelope), counts never payloads —
|
|
91
|
+
* see trace/rent.ts. v3 requires the block; a v1/v2 sidecar has none
|
|
92
|
+
* and reads as the zero-rent ledger (R2-1). */
|
|
93
|
+
rent: RentLine[];
|
|
80
94
|
latencyMs: number;
|
|
81
95
|
ttftMs: number;
|
|
82
96
|
toolCalls: string[];
|
|
@@ -91,21 +105,21 @@ export interface TraceRecord {
|
|
|
91
105
|
}
|
|
92
106
|
/** That is the complete set for 1.2.0. */
|
|
93
107
|
export interface HeaderLine {
|
|
94
|
-
schemaVersion:
|
|
108
|
+
schemaVersion: 3;
|
|
95
109
|
kind: "header";
|
|
96
110
|
sessionId: string;
|
|
97
111
|
kisoVersion: string;
|
|
98
112
|
createdAt: number;
|
|
99
113
|
}
|
|
100
114
|
export interface RunEndLine {
|
|
101
|
-
schemaVersion:
|
|
115
|
+
schemaVersion: 3;
|
|
102
116
|
kind: "run_end";
|
|
103
117
|
runId: string;
|
|
104
118
|
ts: number;
|
|
105
119
|
lastRequestIndex: number;
|
|
106
120
|
}
|
|
107
121
|
export interface CrashLine {
|
|
108
|
-
schemaVersion:
|
|
122
|
+
schemaVersion: 3;
|
|
109
123
|
kind: "crash";
|
|
110
124
|
ts: number;
|
|
111
125
|
note: string;
|
|
@@ -122,7 +136,9 @@ export declare function hashSpecFor(version: number): HashSpec;
|
|
|
122
136
|
* canonical block and reads as defaults at every consumer. */
|
|
123
137
|
export declare const TRACE_RECORD_FIELDS_V1: readonly ["schemaVersion", "kind", "requestId", "runId", "requestIndex", "retryAttempt", "provider", "model", "adapterVersion", "systemPromptHash", "toolSchemaHash", "contextHash", "contextManifest", "segmentHashes", "stablePrefixFingerprint", "freshInput", "cacheRead", "cacheWrite", "output", "latencyMs", "ttftMs", "toolCalls", "outcome", "lineageLink", "ts"];
|
|
124
138
|
/** The 1.3.0 field set (schemaVersion 2) = the v1 set + `canonical`. */
|
|
125
|
-
export declare const
|
|
139
|
+
export declare const TRACE_RECORD_FIELDS_V2: readonly ["schemaVersion", "kind", "requestId", "runId", "requestIndex", "retryAttempt", "provider", "model", "adapterVersion", "systemPromptHash", "toolSchemaHash", "contextHash", "contextManifest", "segmentHashes", "stablePrefixFingerprint", "freshInput", "cacheRead", "cacheWrite", "output", "latencyMs", "ttftMs", "toolCalls", "outcome", "lineageLink", "ts", "canonical"];
|
|
140
|
+
/** The 0.2.1 field set (schemaVersion 3) = the v2 set + `rent`. */
|
|
141
|
+
export declare const TRACE_RECORD_FIELDS: readonly ["schemaVersion", "kind", "requestId", "runId", "requestIndex", "retryAttempt", "provider", "model", "adapterVersion", "systemPromptHash", "toolSchemaHash", "contextHash", "contextManifest", "segmentHashes", "stablePrefixFingerprint", "freshInput", "cacheRead", "cacheWrite", "output", "latencyMs", "ttftMs", "toolCalls", "outcome", "lineageLink", "ts", "canonical", "rent"];
|
|
126
142
|
export declare const TRACE_SEGMENT_FIELDS: readonly ["role", "seqRange", "estTokens", "freshness"];
|
|
127
143
|
export declare function validateTraceSegment(v: unknown): v is TraceSegment;
|
|
128
144
|
export declare function validateTraceRecord(v: unknown): v is TraceRecord;
|
package/dist/trace/record.js
CHANGED
|
@@ -19,18 +19,28 @@
|
|
|
19
19
|
* quartet stays as provider observation above it). The validators accept
|
|
20
20
|
* BOTH generations (R1d-1): a v1 sidecar has no canonical block and reads
|
|
21
21
|
* as defaults at every consumer — never a crash.
|
|
22
|
+
*
|
|
23
|
+
* E3 (0.2.1) — schemaVersion 3: the record gains the `rent` block — the
|
|
24
|
+
* static rent ledger, one line per surface (trace/rent.ts). The v3
|
|
25
|
+
* writers record it; v1/v2 sidecars keep reading as defaults (R1d-1,
|
|
26
|
+
* R2-1): no rent block = no rent lines = the zero-rent reading, never a
|
|
27
|
+
* crash.
|
|
22
28
|
*/
|
|
23
|
-
/** schemaVersion:
|
|
24
|
-
* shape,
|
|
25
|
-
* changes bump it (ADR-0051 §6
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
29
|
+
/** schemaVersion: 3 for 0.2.1 (the rent block). Version 1 = the 1.2.0
|
|
30
|
+
* shape, version 2 = the 1.3.0 shape; both kept for generation-compat
|
|
31
|
+
* reads (R1d-1, R2-1). Algorithm and shape changes bump it (ADR-0051 §6
|
|
32
|
+
* OUT-side versioning). */
|
|
33
|
+
export const TRACE_SCHEMA_VERSION = 3;
|
|
34
|
+
/** The versions a reader may meet in a ledger. v1 and v2 records are
|
|
35
|
+
* accepted (generation-compat) and read as defaults — no canonical
|
|
36
|
+
* block (v1), no rent block (v1, v2). */
|
|
37
|
+
export const TRACE_SCHEMA_VERSIONS = new Set([1, 2, TRACE_SCHEMA_VERSION]);
|
|
30
38
|
import { PRICING_TABLE_V1, priceFor, pricingTableFor, validateCanonicalUsage } from "../usage/canonical.js";
|
|
39
|
+
import { validateRentLine } from "./rent.js";
|
|
31
40
|
export const HASH_SPEC_BY_VERSION = {
|
|
32
41
|
1: { algorithm: "sha-256", output: "full-hex" },
|
|
33
42
|
2: { algorithm: "sha-256", output: "full-hex" }, // E2 — the algorithms do not change
|
|
43
|
+
3: { algorithm: "sha-256", output: "full-hex" }, // E3 — same algorithms, re-pinned (the E2 ritual)
|
|
34
44
|
};
|
|
35
45
|
export function hashSpecFor(version) {
|
|
36
46
|
const spec = HASH_SPEC_BY_VERSION[version];
|
|
@@ -72,7 +82,9 @@ export const TRACE_RECORD_FIELDS_V1 = [
|
|
|
72
82
|
"ts",
|
|
73
83
|
];
|
|
74
84
|
/** The 1.3.0 field set (schemaVersion 2) = the v1 set + `canonical`. */
|
|
75
|
-
export const
|
|
85
|
+
export const TRACE_RECORD_FIELDS_V2 = [...TRACE_RECORD_FIELDS_V1, "canonical"];
|
|
86
|
+
/** The 0.2.1 field set (schemaVersion 3) = the v2 set + `rent`. */
|
|
87
|
+
export const TRACE_RECORD_FIELDS = [...TRACE_RECORD_FIELDS_V2, "rent"];
|
|
76
88
|
export const TRACE_SEGMENT_FIELDS = ["role", "seqRange", "estTokens", "freshness"];
|
|
77
89
|
// ── Validators ────────────────────────────────────────────────────────────
|
|
78
90
|
// Strict by design: extra keys are rejected (the closed set), so a
|
|
@@ -119,12 +131,13 @@ export function validateTraceRecord(v) {
|
|
|
119
131
|
if (!isRecord(v))
|
|
120
132
|
return false;
|
|
121
133
|
const version = v.schemaVersion;
|
|
122
|
-
// generation-compat (R1d-1): a v1 sidecar has no canonical
|
|
123
|
-
//
|
|
124
|
-
//
|
|
125
|
-
|
|
134
|
+
// generation-compat (R1d-1, R2-1): a v1 sidecar has no canonical
|
|
135
|
+
// block, a v2 sidecar has no rent block — both read as defaults,
|
|
136
|
+
// accepted, never a crash; the current version is fully checked
|
|
137
|
+
// (shape + the canonical block + the rent ledger).
|
|
138
|
+
if (version !== 1 && version !== 2 && version !== TRACE_SCHEMA_VERSION)
|
|
126
139
|
return false;
|
|
127
|
-
const fields = version === 1 ? TRACE_RECORD_FIELDS_V1 : TRACE_RECORD_FIELDS;
|
|
140
|
+
const fields = version === 1 ? TRACE_RECORD_FIELDS_V1 : version === 2 ? TRACE_RECORD_FIELDS_V2 : TRACE_RECORD_FIELDS;
|
|
128
141
|
if (!hasClosedKeys(v, fields, ["lineageLink"]))
|
|
129
142
|
return false;
|
|
130
143
|
if (v.kind !== "request")
|
|
@@ -197,6 +210,13 @@ export function validateTraceRecord(v) {
|
|
|
197
210
|
return false;
|
|
198
211
|
}
|
|
199
212
|
}
|
|
213
|
+
if (version === TRACE_SCHEMA_VERSION) {
|
|
214
|
+
// the rent block: every line validates (closed fields, non-empty
|
|
215
|
+
// surface, non-negative integer chars, the estTokens == ceil(chars/4)
|
|
216
|
+
// cross-check — R6). v1/v2 sidecars have no block (R2-1).
|
|
217
|
+
if (!Array.isArray(v.rent) || !v.rent.every(validateRentLine))
|
|
218
|
+
return false;
|
|
219
|
+
}
|
|
200
220
|
if (!isNumber(v.ts))
|
|
201
221
|
return false;
|
|
202
222
|
return true;
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* E3 (0.2.1) — the rent ledger: every request's static model-side rent,
|
|
3
|
+
* one line per surface.
|
|
4
|
+
*
|
|
5
|
+
* The static rent is the surface the model pays REGARDLESS of the turn's
|
|
6
|
+
* content: the base system prompt, each extension's append, every tool's
|
|
7
|
+
* serialized spec, and the per-request envelope. One line per surface,
|
|
8
|
+
* four classes, measured in chars + the rounds' est-token convention
|
|
9
|
+
* (chars/4, ceil — the E1 script's formula, the work order's own words,
|
|
10
|
+
* R6):
|
|
11
|
+
*
|
|
12
|
+
* - system:base the session's own prompt as the CLI handed it
|
|
13
|
+
* (built-in constant + project instructions — the
|
|
14
|
+
* runtime cannot split those, R3). The runtime's
|
|
15
|
+
* generated tool table is machinery BETWEEN base and
|
|
16
|
+
* appends; it stays out of the ledger (counts live
|
|
17
|
+
* in tool:<name> lines and the base's own line).
|
|
18
|
+
* - system:ext:<name> one line per extension with a non-empty
|
|
19
|
+
* systemPrompt.append, measured on the append
|
|
20
|
+
* string, in load order.
|
|
21
|
+
* - tool:<name> one line per tool, measured on the exact
|
|
22
|
+
* serialized ToolSpec projection the adapters
|
|
23
|
+
* receive ({name, description, inputSchema} — the
|
|
24
|
+
* registry.toSpecs() shape, protocol/messages.ts).
|
|
25
|
+
* - envelope the per-request fixed overhead OUTSIDE the
|
|
26
|
+
* conversation payloads: the R5 skeleton
|
|
27
|
+
* JSON.stringify({model, messages: [], tools: []})
|
|
28
|
+
* — a function of the model string only, never of
|
|
29
|
+
* the payloads (the skeleton is the definition).
|
|
30
|
+
*
|
|
31
|
+
* R9: an absent surface is an absent line — an unconfigured mcp, a
|
|
32
|
+
* session with no project instructions: no line. The absence IS the
|
|
33
|
+
* ledger statement (the 0.1.45 diet-A precedent: not paid = no rent).
|
|
34
|
+
*
|
|
35
|
+
* Determinism (the reviewer-facing property): every line is derivable
|
|
36
|
+
* from components the reviewer can recompute — the exported prompt
|
|
37
|
+
* constant, the appends, the ToolSpec array, the request skeleton. The
|
|
38
|
+
* ledger stores COUNTS, never payloads (the seqRange thin-pointer
|
|
39
|
+
* discipline applied to rent).
|
|
40
|
+
*
|
|
41
|
+
* buildRentLedger is the SINGLE source: the R7 star gate drives a real
|
|
42
|
+
* session with the script's predicted composition and asserts the
|
|
43
|
+
* recorded ledger equals the prediction line for line.
|
|
44
|
+
*/
|
|
45
|
+
import type { ToolSpec } from "@vincemakes/kiso-core";
|
|
46
|
+
export interface RentLine {
|
|
47
|
+
/** "system:base" | "system:ext:<name>" | "tool:<name>" | "envelope" */
|
|
48
|
+
surface: string;
|
|
49
|
+
/** length of the serialized surface, measured, never a copy */
|
|
50
|
+
chars: number;
|
|
51
|
+
/** Math.ceil(chars / 4) — the E1 script convention, pinned (R6) */
|
|
52
|
+
estTokens: number;
|
|
53
|
+
}
|
|
54
|
+
/** The closed line set (the same gate discipline as the record fields). */
|
|
55
|
+
export declare const RENT_LINE_FIELDS: readonly ["surface", "chars", "estTokens"];
|
|
56
|
+
/** What the runtime knows about the static surface beyond what the
|
|
57
|
+
* adapter call itself carries: the base prompt as configured, and the
|
|
58
|
+
* per-extension appends in load order (the adapter's composed
|
|
59
|
+
* systemPrompt is the result — the parts are the ledger's inputs). */
|
|
60
|
+
export interface RentParts {
|
|
61
|
+
base?: string;
|
|
62
|
+
appends?: readonly {
|
|
63
|
+
name: string;
|
|
64
|
+
text: string;
|
|
65
|
+
}[];
|
|
66
|
+
}
|
|
67
|
+
export interface RentInput extends RentParts {
|
|
68
|
+
model: string;
|
|
69
|
+
tools?: readonly ToolSpec[];
|
|
70
|
+
}
|
|
71
|
+
/** The one ledger: system:base, then system:ext:* (load order), then
|
|
72
|
+
* tool:* (the array's order — the registry's toSpecs() order), then the
|
|
73
|
+
* envelope. Absent surfaces contribute no lines (R9). */
|
|
74
|
+
export declare function buildRentLedger(input: RentInput): RentLine[];
|
|
75
|
+
/** The line validator — the same strict discipline as the record's other
|
|
76
|
+
* closed sets: no key outside the spec, every spec'd key present,
|
|
77
|
+
* non-empty surface (R9: a line exists only for a surface that exists),
|
|
78
|
+
* non-negative integer chars, and the estTokens cross-check (R6). The
|
|
79
|
+
* schema pins the LINE, not the multiset — duplicate surfaces are the
|
|
80
|
+
* writer's business, never a schema crash. */
|
|
81
|
+
export declare function validateRentLine(v: unknown): v is RentLine;
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* E3 (0.2.1) — the rent ledger: every request's static model-side rent,
|
|
3
|
+
* one line per surface.
|
|
4
|
+
*
|
|
5
|
+
* The static rent is the surface the model pays REGARDLESS of the turn's
|
|
6
|
+
* content: the base system prompt, each extension's append, every tool's
|
|
7
|
+
* serialized spec, and the per-request envelope. One line per surface,
|
|
8
|
+
* four classes, measured in chars + the rounds' est-token convention
|
|
9
|
+
* (chars/4, ceil — the E1 script's formula, the work order's own words,
|
|
10
|
+
* R6):
|
|
11
|
+
*
|
|
12
|
+
* - system:base the session's own prompt as the CLI handed it
|
|
13
|
+
* (built-in constant + project instructions — the
|
|
14
|
+
* runtime cannot split those, R3). The runtime's
|
|
15
|
+
* generated tool table is machinery BETWEEN base and
|
|
16
|
+
* appends; it stays out of the ledger (counts live
|
|
17
|
+
* in tool:<name> lines and the base's own line).
|
|
18
|
+
* - system:ext:<name> one line per extension with a non-empty
|
|
19
|
+
* systemPrompt.append, measured on the append
|
|
20
|
+
* string, in load order.
|
|
21
|
+
* - tool:<name> one line per tool, measured on the exact
|
|
22
|
+
* serialized ToolSpec projection the adapters
|
|
23
|
+
* receive ({name, description, inputSchema} — the
|
|
24
|
+
* registry.toSpecs() shape, protocol/messages.ts).
|
|
25
|
+
* - envelope the per-request fixed overhead OUTSIDE the
|
|
26
|
+
* conversation payloads: the R5 skeleton
|
|
27
|
+
* JSON.stringify({model, messages: [], tools: []})
|
|
28
|
+
* — a function of the model string only, never of
|
|
29
|
+
* the payloads (the skeleton is the definition).
|
|
30
|
+
*
|
|
31
|
+
* R9: an absent surface is an absent line — an unconfigured mcp, a
|
|
32
|
+
* session with no project instructions: no line. The absence IS the
|
|
33
|
+
* ledger statement (the 0.1.45 diet-A precedent: not paid = no rent).
|
|
34
|
+
*
|
|
35
|
+
* Determinism (the reviewer-facing property): every line is derivable
|
|
36
|
+
* from components the reviewer can recompute — the exported prompt
|
|
37
|
+
* constant, the appends, the ToolSpec array, the request skeleton. The
|
|
38
|
+
* ledger stores COUNTS, never payloads (the seqRange thin-pointer
|
|
39
|
+
* discipline applied to rent).
|
|
40
|
+
*
|
|
41
|
+
* buildRentLedger is the SINGLE source: the R7 star gate drives a real
|
|
42
|
+
* session with the script's predicted composition and asserts the
|
|
43
|
+
* recorded ledger equals the prediction line for line.
|
|
44
|
+
*/
|
|
45
|
+
/** The closed line set (the same gate discipline as the record fields). */
|
|
46
|
+
export const RENT_LINE_FIELDS = ["surface", "chars", "estTokens"];
|
|
47
|
+
const line = (surface, text) => ({
|
|
48
|
+
surface,
|
|
49
|
+
chars: text.length,
|
|
50
|
+
estTokens: Math.ceil(text.length / 4),
|
|
51
|
+
});
|
|
52
|
+
/** The one ledger: system:base, then system:ext:* (load order), then
|
|
53
|
+
* tool:* (the array's order — the registry's toSpecs() order), then the
|
|
54
|
+
* envelope. Absent surfaces contribute no lines (R9). */
|
|
55
|
+
export function buildRentLedger(input) {
|
|
56
|
+
const lines = [];
|
|
57
|
+
if (input.base !== undefined)
|
|
58
|
+
lines.push(line("system:base", input.base));
|
|
59
|
+
for (const append of input.appends ?? [])
|
|
60
|
+
lines.push(line(`system:ext:${append.name}`, append.text));
|
|
61
|
+
for (const tool of input.tools ?? []) {
|
|
62
|
+
lines.push(line(`tool:${tool.name}`, JSON.stringify({ name: tool.name, description: tool.description, inputSchema: tool.inputSchema })));
|
|
63
|
+
}
|
|
64
|
+
// R5: the envelope is the request skeleton — a model-string function,
|
|
65
|
+
// never the payloads; the empty arrays are literal (the definition).
|
|
66
|
+
lines.push(line("envelope", JSON.stringify({ model: input.model, messages: [], tools: [] })));
|
|
67
|
+
return lines;
|
|
68
|
+
}
|
|
69
|
+
/** The line validator — the same strict discipline as the record's other
|
|
70
|
+
* closed sets: no key outside the spec, every spec'd key present,
|
|
71
|
+
* non-empty surface (R9: a line exists only for a surface that exists),
|
|
72
|
+
* non-negative integer chars, and the estTokens cross-check (R6). The
|
|
73
|
+
* schema pins the LINE, not the multiset — duplicate surfaces are the
|
|
74
|
+
* writer's business, never a schema crash. */
|
|
75
|
+
export function validateRentLine(v) {
|
|
76
|
+
if (typeof v !== "object" || v === null || Array.isArray(v))
|
|
77
|
+
return false;
|
|
78
|
+
const o = v;
|
|
79
|
+
const keys = Object.keys(o);
|
|
80
|
+
const fields = RENT_LINE_FIELDS;
|
|
81
|
+
if (keys.some((k) => !fields.includes(k)))
|
|
82
|
+
return false;
|
|
83
|
+
if (!fields.every((k) => k in o))
|
|
84
|
+
return false;
|
|
85
|
+
if (typeof o.surface !== "string" || o.surface.length === 0)
|
|
86
|
+
return false;
|
|
87
|
+
const chars = o.chars;
|
|
88
|
+
const estTokens = o.estTokens;
|
|
89
|
+
if (typeof chars !== "number" || !Number.isInteger(chars) || chars < 0)
|
|
90
|
+
return false;
|
|
91
|
+
if (typeof estTokens !== "number" || !Number.isInteger(estTokens) || estTokens < 0)
|
|
92
|
+
return false;
|
|
93
|
+
if (estTokens !== Math.ceil(chars / 4))
|
|
94
|
+
return false;
|
|
95
|
+
return true;
|
|
96
|
+
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vincemakes/kiso-runtime",
|
|
3
|
-
"version": "0.2.
|
|
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": {
|