@vincemakes/kiso-core 0.15.12 → 0.16.0
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/kernel/loop.d.ts +14 -1
- package/dist/kernel/loop.js +53 -0
- package/dist/kernel/project.js +11 -0
- package/dist/protocol/adapter.d.ts +7 -0
- package/dist/protocol/events.d.ts +33 -0
- package/dist/protocol/events.js +28 -1
- package/dist/protocol/messages.d.ts +7 -0
- package/package.json +2 -2
package/dist/kernel/loop.d.ts
CHANGED
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
* re-stream that duplicates output or tool calls.
|
|
34
34
|
*/
|
|
35
35
|
import { type Adapter, type AbortSignalLike } from "../protocol/adapter.js";
|
|
36
|
-
import type { Event, StructuredError } from "../protocol/events.js";
|
|
36
|
+
import type { ContinuationScope, Event, StructuredError } from "../protocol/events.js";
|
|
37
37
|
import type { ApprovalChain } from "../protocol/extension.js";
|
|
38
38
|
import { EventLog } from "./event-log.js";
|
|
39
39
|
import type { EventInput } from "./event-log.js";
|
|
@@ -53,6 +53,19 @@ export interface LoopConfig {
|
|
|
53
53
|
readonly mode?: string;
|
|
54
54
|
readonly maxTurns?: number;
|
|
55
55
|
readonly maxRetries?: number;
|
|
56
|
+
/**
|
|
57
|
+
* MG-1 (ADR-0051 Amendment 5): the run's continuation scope — the
|
|
58
|
+
* kernel stamps it onto a committed stop's envelope (adapters cannot
|
|
59
|
+
* forge scope). Absent = an unscoped run (SDK-injected or faux
|
|
60
|
+
* adapters): adapter-emitted continuation is STRIPPED at the commit.
|
|
61
|
+
*/
|
|
62
|
+
readonly continuationScope?: ContinuationScope;
|
|
63
|
+
/** XP-1: the RESOLVED reasoning wire values — passed to the adapter
|
|
64
|
+
* verbatim; absent = provider defaults (the byte anchor). */
|
|
65
|
+
readonly reasoning?: {
|
|
66
|
+
readonly thinking?: "adaptive" | "enabled" | "disabled";
|
|
67
|
+
readonly effort?: string;
|
|
68
|
+
};
|
|
56
69
|
/**
|
|
57
70
|
* Seed history. When a `log` is provided, the log IS the truth and this
|
|
58
71
|
* is only used if the log is empty. See ADR-0002 / kernel/project.ts.
|
package/dist/kernel/loop.js
CHANGED
|
@@ -527,6 +527,7 @@ export async function* loop(config) {
|
|
|
527
527
|
const stream = config.adapter.stream({
|
|
528
528
|
model: config.model,
|
|
529
529
|
messages,
|
|
530
|
+
...(config.reasoning !== undefined ? { reasoning: config.reasoning } : {}),
|
|
530
531
|
...(config.systemPrompt !== undefined ? { systemPrompt: config.systemPrompt } : {}),
|
|
531
532
|
tools: registry.toSpecs(),
|
|
532
533
|
...(config.maxTokens !== undefined ? { maxTokens: config.maxTokens } : {}),
|
|
@@ -727,6 +728,22 @@ export async function* loop(config) {
|
|
|
727
728
|
break;
|
|
728
729
|
}
|
|
729
730
|
}
|
|
731
|
+
// ── MG-1 (A5): the trust boundary's half of the envelope ─────────
|
|
732
|
+
// Stamp, strip, cap — BEFORE the commit decision, so a hard-cap
|
|
733
|
+
// violation voids the turn (no durable stop persists that is known
|
|
734
|
+
// unable to continue correctly; the F4b abandon voids its draft).
|
|
735
|
+
if (voided === null && heldStop !== null && heldStop.continuation !== undefined) {
|
|
736
|
+
const prepared = prepareContinuation(heldStop, config.continuationScope);
|
|
737
|
+
if (prepared === "hard-cap") {
|
|
738
|
+
voided = {
|
|
739
|
+
kind: "error",
|
|
740
|
+
error: { code: "invalid_request", retryable: false, message: "the turn's required continuation metadata exceeds the hard cap" },
|
|
741
|
+
};
|
|
742
|
+
}
|
|
743
|
+
else {
|
|
744
|
+
heldStop = prepared;
|
|
745
|
+
}
|
|
746
|
+
}
|
|
730
747
|
// ── EC-1 ① — TURN COMMIT ──────────────────────────────────────────
|
|
731
748
|
// The held stop is persisted HERE and only here: iterator done (the
|
|
732
749
|
// stream loop broke cleanly) AND structurally compatible (above). The
|
|
@@ -1273,3 +1290,39 @@ const NEVER_ABORT = {
|
|
|
1273
1290
|
addEventListener: () => { },
|
|
1274
1291
|
removeEventListener: () => { },
|
|
1275
1292
|
};
|
|
1293
|
+
// ── MG-1 (ADR-0051 Amendment 5): the envelope preparation ────────────────
|
|
1294
|
+
//
|
|
1295
|
+
// Pure: stamp the run's scope over whatever the adapter claimed (adapters
|
|
1296
|
+
// are not trusted), strip on an unscoped run, and enforce the two caps —
|
|
1297
|
+
// optional entries drop WHOLE at the soft cap (earliest first, emission
|
|
1298
|
+
// order) with a durable `truncated: true`; a required set over the hard
|
|
1299
|
+
// cap is a provider-contract violation the caller turns into a voided
|
|
1300
|
+
// turn. Sizes are UTF-8 bytes of the serialized entry.
|
|
1301
|
+
const CONTINUATION_SOFT_CAP = 256 * 1024;
|
|
1302
|
+
const CONTINUATION_HARD_CAP = 2 * 1024 * 1024;
|
|
1303
|
+
function prepareContinuation(stop, scope) {
|
|
1304
|
+
const c = stop.continuation;
|
|
1305
|
+
if (c === undefined)
|
|
1306
|
+
return stop;
|
|
1307
|
+
if (scope === undefined) {
|
|
1308
|
+
const { continuation: _stripped, ...rest } = stop;
|
|
1309
|
+
return rest;
|
|
1310
|
+
}
|
|
1311
|
+
const size = (e) => new TextEncoder().encode(e.data).length + new TextEncoder().encode(e.kind).length + 32;
|
|
1312
|
+
const requiredBytes = c.entries.filter((e) => e.required).reduce((n, e) => n + size(e), 0);
|
|
1313
|
+
if (requiredBytes > CONTINUATION_HARD_CAP)
|
|
1314
|
+
return "hard-cap";
|
|
1315
|
+
const kept = [...c.entries];
|
|
1316
|
+
let total = kept.reduce((n, e) => n + size(e), 0);
|
|
1317
|
+
let truncated = false;
|
|
1318
|
+
for (let i = 0; total > CONTINUATION_SOFT_CAP && i < kept.length;) {
|
|
1319
|
+
if (kept[i].required) {
|
|
1320
|
+
i += 1;
|
|
1321
|
+
continue;
|
|
1322
|
+
}
|
|
1323
|
+
total -= size(kept[i]);
|
|
1324
|
+
kept.splice(i, 1);
|
|
1325
|
+
truncated = true;
|
|
1326
|
+
}
|
|
1327
|
+
return { ...stop, continuation: { scope, entries: kept, ...(truncated ? { truncated: true } : {}) } };
|
|
1328
|
+
}
|
package/dist/kernel/project.js
CHANGED
|
@@ -87,20 +87,28 @@ export function projectMessages(events) {
|
|
|
87
87
|
// `thinking` events and attached to the assistant message at flush —
|
|
88
88
|
// deterministic (same events → same messages → same request body, D area).
|
|
89
89
|
let pendingReasoning = null;
|
|
90
|
+
// MG-1 (A5): the committed stop's continuation envelope, attached to the
|
|
91
|
+
// SAME flush its stop triggers — only a durable stop carries one, so a
|
|
92
|
+
// voided draft can never leak stale continuation (invariant ③).
|
|
93
|
+
let pendingContinuation = null;
|
|
90
94
|
const flushAssistant = () => {
|
|
91
95
|
pushText();
|
|
92
96
|
if (blocks.length === 0) {
|
|
93
97
|
assistantSource = undefined;
|
|
98
|
+
pendingContinuation = null;
|
|
94
99
|
return;
|
|
95
100
|
}
|
|
96
101
|
const callIds = blocks.filter((b) => b.type === "tool_use").map((b) => b.callId);
|
|
97
102
|
const reasoning = pendingReasoning;
|
|
98
103
|
pendingReasoning = null;
|
|
104
|
+
const continuation = pendingContinuation;
|
|
105
|
+
pendingContinuation = null;
|
|
99
106
|
out.push({
|
|
100
107
|
role: "assistant",
|
|
101
108
|
blocks: [...blocks],
|
|
102
109
|
...(assistantSource !== undefined ? { source: assistantSource } : {}),
|
|
103
110
|
...(reasoning !== null ? { reasoning } : {}),
|
|
111
|
+
...(continuation !== null ? { continuation } : {}),
|
|
104
112
|
});
|
|
105
113
|
blocks = [];
|
|
106
114
|
assistantSource = undefined;
|
|
@@ -506,6 +514,9 @@ export function projectMessages(events) {
|
|
|
506
514
|
// projects [assistant, results…] in reading order, and the
|
|
507
515
|
// stream-open guards below only ever flush a CLOSED turn's
|
|
508
516
|
// late (post-stop) results.
|
|
517
|
+
// MG-1 (A5): the committed stop hands its envelope to the
|
|
518
|
+
// flush it triggers; every other flush site carries none.
|
|
519
|
+
pendingContinuation = ev.continuation ?? null;
|
|
509
520
|
flushAssistant();
|
|
510
521
|
flushResults();
|
|
511
522
|
break;
|
|
@@ -46,6 +46,13 @@ export interface StreamOptions {
|
|
|
46
46
|
readonly tools?: readonly ToolSpec[];
|
|
47
47
|
readonly maxTokens?: number;
|
|
48
48
|
readonly temperature?: number;
|
|
49
|
+
/** XP-1: the RESOLVED reasoning setting — native wire values only (the
|
|
50
|
+
* runtime's matrix resolves and refuses; adapters serialize per
|
|
51
|
+
* dialect). Absent = provider defaults, byte-identical to pre-XP-1. */
|
|
52
|
+
readonly reasoning?: {
|
|
53
|
+
readonly thinking?: "adaptive" | "enabled" | "disabled";
|
|
54
|
+
readonly effort?: string;
|
|
55
|
+
};
|
|
49
56
|
readonly signal?: AbortSignalLike;
|
|
50
57
|
}
|
|
51
58
|
/**
|
|
@@ -417,10 +417,43 @@ export interface Usage {
|
|
|
417
417
|
readonly cacheWrite: number | null;
|
|
418
418
|
readonly known: boolean;
|
|
419
419
|
}
|
|
420
|
+
/** MG-1 (ADR-0051 Amendment 5): the continuation envelope's scope — WHO
|
|
421
|
+
* may replay it. Kernel-stamped at the Turn Commit append from the run's
|
|
422
|
+
* configured binding; an adapter-supplied scope is always overwritten
|
|
423
|
+
* (adapters are not trusted), and a run with no configured scope has
|
|
424
|
+
* adapter-emitted continuation stripped at the same boundary. */
|
|
425
|
+
export interface ContinuationScope {
|
|
426
|
+
readonly providerId: string;
|
|
427
|
+
readonly apiId: string;
|
|
428
|
+
readonly modelId: string;
|
|
429
|
+
/** Origin only; REQUIRED when providerId === "custom". */
|
|
430
|
+
readonly endpoint?: string;
|
|
431
|
+
}
|
|
432
|
+
/** One opaque provider block. `data` is bytes to the kernel — serialized
|
|
433
|
+
* verbatim by the emitting adapter, replayed verbatim by the scope-matched
|
|
434
|
+
* one, never reconstructed from projected text. */
|
|
435
|
+
export interface ContinuationEntry {
|
|
436
|
+
readonly kind: string;
|
|
437
|
+
/** true = the next request is INVALID without it (never dropped; a
|
|
438
|
+
* required set over the hard cap voids the turn before commit).
|
|
439
|
+
* false = quality-degradable (droppable under the soft cap). */
|
|
440
|
+
readonly required: boolean;
|
|
441
|
+
readonly data: string;
|
|
442
|
+
}
|
|
443
|
+
export interface Continuation {
|
|
444
|
+
readonly scope: ContinuationScope;
|
|
445
|
+
/** EMISSION ORDER, preserved end to end. */
|
|
446
|
+
readonly entries: readonly ContinuationEntry[];
|
|
447
|
+
/** Present only when OPTIONAL entries were dropped at the soft cap. */
|
|
448
|
+
readonly truncated?: true;
|
|
449
|
+
}
|
|
420
450
|
export interface Stop {
|
|
421
451
|
readonly seq: number;
|
|
422
452
|
readonly type: "stop";
|
|
423
453
|
readonly reason: StopReason;
|
|
454
|
+
/** MG-1 (Amendment 5): absent on every pre-A5 log — rule 1's truly
|
|
455
|
+
* optional field; old logs project byte-identically. */
|
|
456
|
+
readonly continuation?: Continuation;
|
|
424
457
|
}
|
|
425
458
|
/**
|
|
426
459
|
* Structured failure classification for MODEL / TRANSPORT errors.
|
package/dist/protocol/events.js
CHANGED
|
@@ -143,6 +143,33 @@ function isTerminal(v) {
|
|
|
143
143
|
* safe integer; the others are null when the provider did not report
|
|
144
144
|
* them.
|
|
145
145
|
*/
|
|
146
|
+
/** A5: shape-checked ONLY when present — rule 1's truly-optional clause.
|
|
147
|
+
* The key set stays open, so older bins load newer logs unchanged. */
|
|
148
|
+
function isContinuation(v) {
|
|
149
|
+
if (v === undefined)
|
|
150
|
+
return true;
|
|
151
|
+
if (typeof v !== "object" || v === null)
|
|
152
|
+
return false;
|
|
153
|
+
const c = v;
|
|
154
|
+
const s = c.scope;
|
|
155
|
+
if (typeof s !== "object" || s === null)
|
|
156
|
+
return false;
|
|
157
|
+
const sc = s;
|
|
158
|
+
if (typeof sc.providerId !== "string" || typeof sc.apiId !== "string" || typeof sc.modelId !== "string")
|
|
159
|
+
return false;
|
|
160
|
+
if (sc.endpoint !== undefined && typeof sc.endpoint !== "string")
|
|
161
|
+
return false;
|
|
162
|
+
if (!Array.isArray(c.entries))
|
|
163
|
+
return false;
|
|
164
|
+
for (const e of c.entries) {
|
|
165
|
+
if (typeof e !== "object" || e === null)
|
|
166
|
+
return false;
|
|
167
|
+
const r = e;
|
|
168
|
+
if (typeof r.kind !== "string" || typeof r.required !== "boolean" || typeof r.data !== "string")
|
|
169
|
+
return false;
|
|
170
|
+
}
|
|
171
|
+
return c.truncated === undefined || c.truncated === true;
|
|
172
|
+
}
|
|
146
173
|
function isUsage(v) {
|
|
147
174
|
if (typeof v.known !== "boolean")
|
|
148
175
|
return false;
|
|
@@ -176,7 +203,7 @@ const EVENT_VALIDATORS = {
|
|
|
176
203
|
isTags(v) && isInvocationSeq(v),
|
|
177
204
|
thinking: (v) => typeof v.text === "string",
|
|
178
205
|
usage: isUsage,
|
|
179
|
-
stop: (v) => STOP_REASONS.has(v.reason),
|
|
206
|
+
stop: (v) => STOP_REASONS.has(v.reason) && isContinuation(v.continuation),
|
|
180
207
|
user_input: (v) => isContent(v.content) && isSource(v),
|
|
181
208
|
compacted: (v) => Array.isArray(v.cleared) &&
|
|
182
209
|
v.cleared.every((c) => isPlainObject(c) &&
|
|
@@ -82,6 +82,13 @@ export interface AssistantMessage {
|
|
|
82
82
|
* follow-up requests). Present only when the turn actually reasoned.
|
|
83
83
|
*/
|
|
84
84
|
readonly reasoning?: string;
|
|
85
|
+
/**
|
|
86
|
+
* MG-1 (ADR-0051 Amendment 5): the turn's committed continuation
|
|
87
|
+
* envelope, derived from its stop. Opaque to everything but the
|
|
88
|
+
* scope-matched adapter; messages are the versioned model-request
|
|
89
|
+
* side (Amendment 3(a)), never the frozen plane.
|
|
90
|
+
*/
|
|
91
|
+
readonly continuation?: import("./events.js").Continuation;
|
|
85
92
|
}
|
|
86
93
|
/**
|
|
87
94
|
* Sent back to the model after a tool ran.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vincemakes/kiso-core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.16.0",
|
|
4
4
|
"description": "kiso (foundation) core \u2014 protocol, event log, loop, hooks, modes, permissions, compaction, delivery truth. The 2,000-line kernel at the bottom of the kiso framework.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
"openai"
|
|
34
34
|
],
|
|
35
35
|
"devDependencies": {
|
|
36
|
-
"@vincemakes/kiso-evals": "0.
|
|
36
|
+
"@vincemakes/kiso-evals": "0.16.0",
|
|
37
37
|
"@types/node": "^26.1.2",
|
|
38
38
|
"typescript": "^5.7.2",
|
|
39
39
|
"vitest": "^3.0.0"
|