@vincemakes/kiso-runtime 0.40.6 → 0.40.7
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/trace/guard.d.ts +11 -0
- package/dist/trace/guard.js +31 -1
- package/dist/trace/record.d.ts +33 -8
- package/dist/trace/record.js +35 -7
- package/package.json +6 -6
package/dist/trace/guard.d.ts
CHANGED
|
@@ -23,6 +23,17 @@
|
|
|
23
23
|
*/
|
|
24
24
|
import type { Adapter, AdapterEvent, Event, StreamOptions } from "@vincemakes/kiso-core";
|
|
25
25
|
import { type RentParts } from "./rent.js";
|
|
26
|
+
import { type ProviderErrorNote } from "./record.js";
|
|
27
|
+
/** SMK0400-F1: what a provider error is allowed to leave in the ledger — a
|
|
28
|
+
* message capped, and with anything credential-shaped removed first: an
|
|
29
|
+
* `sk-`/`Bearer` token, an `api_key=`/`token:` value, and any unbroken run
|
|
30
|
+
* of 32+ key characters (a provider that echoes a masked or whole key
|
|
31
|
+
* back in its refusal must not get it written to disk). */
|
|
32
|
+
export declare function redactProviderMessage(message: string): string;
|
|
33
|
+
/** SMK0400-F1: the note a `provider_error` record carries. The adapters
|
|
34
|
+
* throw the structured error (code, message, status when there was one);
|
|
35
|
+
* anything else thrown is named by its error name. */
|
|
36
|
+
export declare function providerErrorNote(err: unknown): ProviderErrorNote;
|
|
26
37
|
export interface RequestTracerDeps {
|
|
27
38
|
root: string;
|
|
28
39
|
sessionId: string;
|
package/dist/trace/guard.js
CHANGED
|
@@ -27,7 +27,29 @@ import { cacheableHashes } from "./analyze.js";
|
|
|
27
27
|
import { hashContext, hashSystemPrompt, hashToolSpecs, stablePrefixFingerprint } from "./hash.js";
|
|
28
28
|
import { PRICING_TABLE_V1, canonicalizeUsageForModel } from "../usage/canonical.js";
|
|
29
29
|
import { buildRentLedger } from "./rent.js";
|
|
30
|
-
import { TRACE_SCHEMA_VERSION } from "./record.js";
|
|
30
|
+
import { PROVIDER_ERROR_MESSAGE_MAX, TRACE_SCHEMA_VERSION } from "./record.js";
|
|
31
|
+
/** SMK0400-F1: what a provider error is allowed to leave in the ledger — a
|
|
32
|
+
* message capped, and with anything credential-shaped removed first: an
|
|
33
|
+
* `sk-`/`Bearer` token, an `api_key=`/`token:` value, and any unbroken run
|
|
34
|
+
* of 32+ key characters (a provider that echoes a masked or whole key
|
|
35
|
+
* back in its refusal must not get it written to disk). */
|
|
36
|
+
export function redactProviderMessage(message) {
|
|
37
|
+
return message
|
|
38
|
+
.replace(/\b(Bearer|Basic)\s+[A-Za-z0-9._~+/=-]+/gi, "$1 <redacted>")
|
|
39
|
+
.replace(/\b(api[_-]?key|access[_-]?token|token|secret|password|authorization)(["']?\s*[:=]\s*["']?)[^\s"',;]+/gi, "$1$2<redacted>")
|
|
40
|
+
.replace(/\bsk-[A-Za-z0-9_-]{6,}/g, "sk-<redacted>")
|
|
41
|
+
.replace(/[A-Za-z0-9_-]{32,}/g, "<redacted>")
|
|
42
|
+
.slice(0, PROVIDER_ERROR_MESSAGE_MAX);
|
|
43
|
+
}
|
|
44
|
+
/** SMK0400-F1: the note a `provider_error` record carries. The adapters
|
|
45
|
+
* throw the structured error (code, message, status when there was one);
|
|
46
|
+
* anything else thrown is named by its error name. */
|
|
47
|
+
export function providerErrorNote(err) {
|
|
48
|
+
const e = (typeof err === "object" && err !== null ? err : {});
|
|
49
|
+
const code = typeof e.code === "string" && e.code !== "" ? e.code : typeof e.name === "string" && e.name !== "" ? e.name : "error";
|
|
50
|
+
const message = typeof e.message === "string" ? e.message : String(err);
|
|
51
|
+
return { code, ...(typeof e.status === "number" && Number.isInteger(e.status) ? { status: e.status } : {}), message: redactProviderMessage(message) };
|
|
52
|
+
}
|
|
31
53
|
import { TraceWriter } from "./writer.js";
|
|
32
54
|
export class RequestTracer {
|
|
33
55
|
#writer;
|
|
@@ -81,6 +103,8 @@ export class RequestTracer {
|
|
|
81
103
|
let reasoningTokens;
|
|
82
104
|
const toolCalls = [];
|
|
83
105
|
let outcome = "ok";
|
|
106
|
+
// SMK0400-F1: what the provider said, when the outcome is its error
|
|
107
|
+
let providerError;
|
|
84
108
|
try {
|
|
85
109
|
for await (const ev of upstream) {
|
|
86
110
|
// the null check must be the ONLY guard — a 0-initialized
|
|
@@ -109,6 +133,8 @@ export class RequestTracer {
|
|
|
109
133
|
}
|
|
110
134
|
catch (err) {
|
|
111
135
|
outcome = this.#classifyOutcome(err, options);
|
|
136
|
+
if (outcome === "provider_error")
|
|
137
|
+
providerError = providerErrorNote(err);
|
|
112
138
|
throw err;
|
|
113
139
|
}
|
|
114
140
|
finally {
|
|
@@ -125,6 +151,7 @@ export class RequestTracer {
|
|
|
125
151
|
usageKnown,
|
|
126
152
|
servedModel,
|
|
127
153
|
reasoningTokens,
|
|
154
|
+
providerError,
|
|
128
155
|
});
|
|
129
156
|
}
|
|
130
157
|
}
|
|
@@ -239,6 +266,9 @@ export class RequestTracer {
|
|
|
239
266
|
// the agreement the reconciliation exists to test.
|
|
240
267
|
if (p.servedModel !== null)
|
|
241
268
|
record.servedModel = p.servedModel;
|
|
269
|
+
// SMK0400-F1: on a provider_error record only; absent otherwise
|
|
270
|
+
if (p.providerError !== undefined)
|
|
271
|
+
record.providerError = p.providerError;
|
|
242
272
|
if (p.usageKnown) {
|
|
243
273
|
record.freshInput =
|
|
244
274
|
this.#provider === "anthropic"
|
package/dist/trace/record.d.ts
CHANGED
|
@@ -68,13 +68,22 @@
|
|
|
68
68
|
* as requested". That default is the whole defect: a field filled in with
|
|
69
69
|
* the request manufactures the agreement the reconciliation exists to
|
|
70
70
|
* test.
|
|
71
|
+
*
|
|
72
|
+
* SMK0400-F1 (0.40.7) — schemaVersion 7: a `provider_error` record gains
|
|
73
|
+
* `providerError` — the error's code, the HTTP status when there was one,
|
|
74
|
+
* and the provider's message (capped, and redacted of anything
|
|
75
|
+
* credential-shaped). Until now the record said only `provider_error`, so
|
|
76
|
+
* a failed request could not be named after the fact (the 0.40.0 smoke's
|
|
77
|
+
* two failures). OPTIONAL: absent on every other outcome, and on every
|
|
78
|
+
* record a v1–v6 writer produced.
|
|
71
79
|
*/
|
|
72
|
-
/** schemaVersion:
|
|
80
|
+
/** schemaVersion: 7 for the `providerError` statement (SMK0400-F1);
|
|
81
|
+
* 6 was the `servedModel` statement (TRACE-F1);
|
|
73
82
|
* 5 was the `usageKnown` marker (F33-1). Version
|
|
74
83
|
* 1 = the 1.2.0 shape, 2 = the 1.3.0 shape, 3 = the 0.2.1 shape; all
|
|
75
84
|
* kept for generation-compat reads (R1d-1, R2-1). Algorithm and shape
|
|
76
85
|
* changes bump it (ADR-0051 §6 OUT-side versioning). */
|
|
77
|
-
export declare const TRACE_SCHEMA_VERSION =
|
|
86
|
+
export declare const TRACE_SCHEMA_VERSION = 7;
|
|
78
87
|
/** The versions a reader may meet in a ledger. v1 and v2 records are
|
|
79
88
|
* accepted (generation-compat) and read as defaults — no canonical
|
|
80
89
|
* block (v1), no rent block (v1, v2). */
|
|
@@ -111,7 +120,7 @@ export interface TraceSegment {
|
|
|
111
120
|
}
|
|
112
121
|
/** That is the complete set for 1.2.0. */
|
|
113
122
|
export interface TraceRecord {
|
|
114
|
-
schemaVersion:
|
|
123
|
+
schemaVersion: 7;
|
|
115
124
|
kind: "request";
|
|
116
125
|
requestId: string;
|
|
117
126
|
runId: string;
|
|
@@ -178,25 +187,29 @@ export interface TraceRecord {
|
|
|
178
187
|
* four days. The requested id stays in `model`; these are two facts,
|
|
179
188
|
* never merged. */
|
|
180
189
|
servedModel?: string;
|
|
190
|
+
/** SMK0400-F1 (v7) — on a `provider_error` record: what the provider
|
|
191
|
+
* said. `message` is capped at PROVIDER_ERROR_MESSAGE_MAX characters and
|
|
192
|
+
* redacted of credential-shaped strings before it is written. */
|
|
193
|
+
providerError?: ProviderErrorNote;
|
|
181
194
|
ts: number;
|
|
182
195
|
}
|
|
183
196
|
/** That is the complete set for 1.2.0. */
|
|
184
197
|
export interface HeaderLine {
|
|
185
|
-
schemaVersion:
|
|
198
|
+
schemaVersion: 7;
|
|
186
199
|
kind: "header";
|
|
187
200
|
sessionId: string;
|
|
188
201
|
kisoVersion: string;
|
|
189
202
|
createdAt: number;
|
|
190
203
|
}
|
|
191
204
|
export interface RunEndLine {
|
|
192
|
-
schemaVersion:
|
|
205
|
+
schemaVersion: 7;
|
|
193
206
|
kind: "run_end";
|
|
194
207
|
runId: string;
|
|
195
208
|
ts: number;
|
|
196
209
|
lastRequestIndex: number;
|
|
197
210
|
}
|
|
198
211
|
export interface CrashLine {
|
|
199
|
-
schemaVersion:
|
|
212
|
+
schemaVersion: 7;
|
|
200
213
|
kind: "crash";
|
|
201
214
|
ts: number;
|
|
202
215
|
note: string;
|
|
@@ -229,9 +242,21 @@ export declare const TRACE_RECORD_FIELDS_V5: readonly ["schemaVersion", "kind",
|
|
|
229
242
|
/** TRACE-F1 (v6): the model the SERVER said it served. A v5 ledger has none
|
|
230
243
|
* on any record, which reads as "nobody asked the server" — the true
|
|
231
244
|
* statement about a ledger written before the adapters read the field. */
|
|
232
|
-
export declare const
|
|
245
|
+
export declare const TRACE_RECORD_FIELDS_V6: 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", "purpose", "usageKnown", "servedModel"];
|
|
246
|
+
/** SMK0400-F1 (v7): what the provider said on a `provider_error` record. */
|
|
247
|
+
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", "purpose", "usageKnown", "servedModel", "providerError"];
|
|
233
248
|
/** The fields the closed-set check does not require to be present. */
|
|
234
|
-
export declare const TRACE_RECORD_OPTIONAL: readonly ["purpose", "usageKnown", "servedModel"];
|
|
249
|
+
export declare const TRACE_RECORD_OPTIONAL: readonly ["purpose", "usageKnown", "servedModel", "providerError"];
|
|
250
|
+
/** SMK0400-F1: the shape of `providerError`. */
|
|
251
|
+
export interface ProviderErrorNote {
|
|
252
|
+
/** the structured error's code (`rate_limit`, `network`, …), or the
|
|
253
|
+
* thrown error's name when it carried none */
|
|
254
|
+
code: string;
|
|
255
|
+
/** the HTTP status, when the failure had one */
|
|
256
|
+
status?: number;
|
|
257
|
+
message: string;
|
|
258
|
+
}
|
|
259
|
+
export declare const PROVIDER_ERROR_MESSAGE_MAX = 300;
|
|
235
260
|
export declare const TRACE_SEGMENT_FIELDS: readonly ["role", "seqRange", "estTokens", "freshness"];
|
|
236
261
|
export declare function validateTraceSegment(v: unknown): v is TraceSegment;
|
|
237
262
|
export declare function validateTraceRecord(v: unknown): v is TraceRecord;
|
package/dist/trace/record.js
CHANGED
|
@@ -68,13 +68,22 @@
|
|
|
68
68
|
* as requested". That default is the whole defect: a field filled in with
|
|
69
69
|
* the request manufactures the agreement the reconciliation exists to
|
|
70
70
|
* test.
|
|
71
|
+
*
|
|
72
|
+
* SMK0400-F1 (0.40.7) — schemaVersion 7: a `provider_error` record gains
|
|
73
|
+
* `providerError` — the error's code, the HTTP status when there was one,
|
|
74
|
+
* and the provider's message (capped, and redacted of anything
|
|
75
|
+
* credential-shaped). Until now the record said only `provider_error`, so
|
|
76
|
+
* a failed request could not be named after the fact (the 0.40.0 smoke's
|
|
77
|
+
* two failures). OPTIONAL: absent on every other outcome, and on every
|
|
78
|
+
* record a v1–v6 writer produced.
|
|
71
79
|
*/
|
|
72
|
-
/** schemaVersion:
|
|
80
|
+
/** schemaVersion: 7 for the `providerError` statement (SMK0400-F1);
|
|
81
|
+
* 6 was the `servedModel` statement (TRACE-F1);
|
|
73
82
|
* 5 was the `usageKnown` marker (F33-1). Version
|
|
74
83
|
* 1 = the 1.2.0 shape, 2 = the 1.3.0 shape, 3 = the 0.2.1 shape; all
|
|
75
84
|
* kept for generation-compat reads (R1d-1, R2-1). Algorithm and shape
|
|
76
85
|
* changes bump it (ADR-0051 §6 OUT-side versioning). */
|
|
77
|
-
export const TRACE_SCHEMA_VERSION =
|
|
86
|
+
export const TRACE_SCHEMA_VERSION = 7;
|
|
78
87
|
/** The versions a reader may meet in a ledger. v1 and v2 records are
|
|
79
88
|
* accepted (generation-compat) and read as defaults — no canonical
|
|
80
89
|
* block (v1), no rent block (v1, v2). */
|
|
@@ -93,7 +102,7 @@ export const TRACE_SCHEMA_VERSION = 6;
|
|
|
93
102
|
* spec. Three lists to extend is the point: a bump that forgets one is a
|
|
94
103
|
* bump that fails loudly rather than a reader that quietly narrows.
|
|
95
104
|
*/
|
|
96
|
-
export const TRACE_SCHEMA_VERSIONS = new Set([1, 2, 3, 4, 5, 6]);
|
|
105
|
+
export const TRACE_SCHEMA_VERSIONS = new Set([1, 2, 3, 4, 5, 6, 7]);
|
|
97
106
|
import { PRICING_TABLE_V1, priceFor, pricingTableFor, validateCanonicalUsage } from "../usage/canonical.js";
|
|
98
107
|
import { validateRentLine } from "./rent.js";
|
|
99
108
|
export const HASH_SPEC_BY_VERSION = {
|
|
@@ -103,6 +112,7 @@ export const HASH_SPEC_BY_VERSION = {
|
|
|
103
112
|
4: { algorithm: "sha-256", output: "full-hex" }, // TUI2-R3v2 — `purpose` is a marker, not an input to any hash; re-pinned by the same ritual
|
|
104
113
|
5: { algorithm: "sha-256", output: "full-hex" }, // F33-1 — `usageKnown` is a marker, not an input to any hash; re-pinned by the same ritual
|
|
105
114
|
6: { algorithm: "sha-256", output: "full-hex" }, // TRACE-F1 — `servedModel` is the server's statement, not an input to any hash; re-pinned by the same ritual
|
|
115
|
+
7: { algorithm: "sha-256", output: "full-hex" }, // SMK0400-F1 — `providerError` is the provider's statement, not an input to any hash; re-pinned by the same ritual
|
|
106
116
|
};
|
|
107
117
|
export function hashSpecFor(version) {
|
|
108
118
|
const spec = HASH_SPEC_BY_VERSION[version];
|
|
@@ -160,9 +170,12 @@ export const TRACE_RECORD_FIELDS_V5 = [...TRACE_RECORD_FIELDS_V4, "usageKnown"];
|
|
|
160
170
|
/** TRACE-F1 (v6): the model the SERVER said it served. A v5 ledger has none
|
|
161
171
|
* on any record, which reads as "nobody asked the server" — the true
|
|
162
172
|
* statement about a ledger written before the adapters read the field. */
|
|
163
|
-
export const
|
|
173
|
+
export const TRACE_RECORD_FIELDS_V6 = [...TRACE_RECORD_FIELDS_V5, "servedModel"];
|
|
174
|
+
/** SMK0400-F1 (v7): what the provider said on a `provider_error` record. */
|
|
175
|
+
export const TRACE_RECORD_FIELDS = [...TRACE_RECORD_FIELDS_V6, "providerError"];
|
|
164
176
|
/** The fields the closed-set check does not require to be present. */
|
|
165
|
-
export const TRACE_RECORD_OPTIONAL = ["purpose", "usageKnown", "servedModel"];
|
|
177
|
+
export const TRACE_RECORD_OPTIONAL = ["purpose", "usageKnown", "servedModel", "providerError"];
|
|
178
|
+
export const PROVIDER_ERROR_MESSAGE_MAX = 300;
|
|
166
179
|
export const TRACE_SEGMENT_FIELDS = ["role", "seqRange", "estTokens", "freshness"];
|
|
167
180
|
// ── Validators ────────────────────────────────────────────────────────────
|
|
168
181
|
// Strict by design: extra keys are rejected (the closed set), so a
|
|
@@ -232,7 +245,9 @@ export function validateTraceRecord(v) {
|
|
|
232
245
|
? TRACE_RECORD_FIELDS_V4
|
|
233
246
|
: version === 5
|
|
234
247
|
? TRACE_RECORD_FIELDS_V5
|
|
235
|
-
:
|
|
248
|
+
: version === 6
|
|
249
|
+
? TRACE_RECORD_FIELDS_V6
|
|
250
|
+
: TRACE_RECORD_FIELDS;
|
|
236
251
|
if (!hasClosedKeys(v, fields, ["lineageLink", ...TRACE_RECORD_OPTIONAL]))
|
|
237
252
|
return false;
|
|
238
253
|
if (v.purpose !== undefined && (typeof v.purpose !== "string" || v.purpose === ""))
|
|
@@ -248,6 +263,17 @@ export function validateTraceRecord(v) {
|
|
|
248
263
|
// honest "the server said nothing".
|
|
249
264
|
if (v.servedModel !== undefined && (typeof v.servedModel !== "string" || v.servedModel === ""))
|
|
250
265
|
return false;
|
|
266
|
+
// SMK0400-F1: closed keys; a non-empty code; an integer status when
|
|
267
|
+
// stated; the message a string within the cap (the writer caps it)
|
|
268
|
+
if (v.providerError !== undefined) {
|
|
269
|
+
const e = v.providerError;
|
|
270
|
+
if (!isRecord(e) || !hasClosedKeys(e, ["code", "message", "status"], ["status"]))
|
|
271
|
+
return false;
|
|
272
|
+
if (typeof e.code !== "string" || e.code === "" || typeof e.message !== "string" || e.message.length > PROVIDER_ERROR_MESSAGE_MAX)
|
|
273
|
+
return false;
|
|
274
|
+
if (e.status !== undefined && !Number.isInteger(e.status))
|
|
275
|
+
return false;
|
|
276
|
+
}
|
|
251
277
|
if (v.kind !== "request")
|
|
252
278
|
return false;
|
|
253
279
|
if (typeof v.requestId !== "string" || typeof v.runId !== "string")
|
|
@@ -318,7 +344,9 @@ export function validateTraceRecord(v) {
|
|
|
318
344
|
return false;
|
|
319
345
|
}
|
|
320
346
|
}
|
|
321
|
-
|
|
347
|
+
// SMK0400-F1: `>= 6`, not `=== TRACE_SCHEMA_VERSION` — the bump to 7
|
|
348
|
+
// must not stop checking the rent lines of the v6 records already written
|
|
349
|
+
if (version >= 6) {
|
|
322
350
|
// the rent block: every line validates (closed fields, non-empty
|
|
323
351
|
// surface, non-negative integer chars, the estTokens == ceil(chars/4)
|
|
324
352
|
// cross-check — R6). v1/v2 sidecars have no block (R2-1).
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vincemakes/kiso-runtime",
|
|
3
|
-
"version": "0.40.
|
|
3
|
+
"version": "0.40.7",
|
|
4
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",
|
|
@@ -25,12 +25,12 @@
|
|
|
25
25
|
"test": "vitest run"
|
|
26
26
|
},
|
|
27
27
|
"dependencies": {
|
|
28
|
-
"@vincemakes/kiso-core": "0.40.
|
|
28
|
+
"@vincemakes/kiso-core": "0.40.7"
|
|
29
29
|
},
|
|
30
30
|
"peerDependencies": {
|
|
31
|
-
"@vincemakes/kiso-provider-anthropic": "0.40.
|
|
32
|
-
"@vincemakes/kiso-provider-openai": "0.40.
|
|
33
|
-
"@vincemakes/kiso-provider-openai-responses": "0.40.
|
|
31
|
+
"@vincemakes/kiso-provider-anthropic": "0.40.7",
|
|
32
|
+
"@vincemakes/kiso-provider-openai": "0.40.7",
|
|
33
|
+
"@vincemakes/kiso-provider-openai-responses": "0.40.7"
|
|
34
34
|
},
|
|
35
35
|
"peerDependenciesMeta": {
|
|
36
36
|
"@vincemakes/kiso-provider-anthropic": {
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
}
|
|
45
45
|
},
|
|
46
46
|
"devDependencies": {
|
|
47
|
-
"@vincemakes/kiso-evals": "0.40.
|
|
47
|
+
"@vincemakes/kiso-evals": "0.40.7",
|
|
48
48
|
"@types/node": "^26.1.2",
|
|
49
49
|
"typescript": "^5.7.2",
|
|
50
50
|
"vitest": "^3.0.0"
|