@vincemakes/kiso-runtime 0.12.0 → 0.13.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.
@@ -86,17 +86,38 @@ export function deriveRecoveryPlan(events, scope) {
86
86
  const uncertain = [...executionLedger(events).values()].filter((r) => r.status === "uncertain");
87
87
  if (uncertain.length > 0)
88
88
  return { kind: "RESOLVE_UNCERTAIN", executionId: uncertain[0].executionId };
89
- // 4. Gap B: a text-bearing no-stop suffix is an abandoned draft — void it.
90
- // Text-only detection (the 0.1.44 verification): a bare tool-call
91
- // suffix is the legal approval-panel pause, never a draft. The
92
- // boundary/draft scans run over the OPEN RUN's events INCLUDING the
93
- // driver's own appends the driver re-derives after every append,
94
- // and the marker IT appended must already be the last boundary (the
95
- // old one-pass Gap B never needed this: it ran before any append).
89
+ // 4. Gap B: a no-stop suffix of MODEL OUTPUT is an abandoned draft — void
90
+ // it. The boundary/draft scans run over the OPEN RUN's events
91
+ // INCLUDING the driver's own appends the driver re-derives after
92
+ // every append, and the marker IT appended must already be the last
93
+ // boundary (the old one-pass Gap B never needed this: it ran before
94
+ // any append).
95
+ //
96
+ // EC-1, finding EC1-F1: the scan counts `tool_call_end` too. It used
97
+ // to be text-only, on the 0.1.44 reasoning that "a bare tool-call
98
+ // suffix is the legal approval-panel pause, never a draft" — but that
99
+ // reasoning only ever held for a suffix carrying a REQUEST (the
100
+ // liveAsk clause below, which still decides that case). A bare call
101
+ // with no request is not a pause; it is an uncommitted draft, and
102
+ // leaving it un-voided projects an assistant `tool_use` block with no
103
+ // result — a provider 400. Pre-EC-1 the window was microseconds wide
104
+ // (the stop was persisted the moment it arrived); ① holds the stop
105
+ // until the stream is exhausted, so crash-pair A lands in this shape
106
+ // by construction.
96
107
  const openEvents = openRunEvents(events, scope);
97
108
  const boundary = [...openEvents].reverse().find(isBoundary);
98
109
  if (boundary !== undefined) {
99
- const afterBoundary = openEvents.some((e) => (e.type === "text_delta" || e.type === "thinking") && e.seq > boundary.seq);
110
+ // A call only makes a DRAFT while it is still pure intent. Once it
111
+ // has a durable `tool_execution_started` the world may have moved,
112
+ // and the existing repair passes own it: voiding such a call would
113
+ // strand a real receipt behind a voided declaration (pair atomicity
114
+ // would then drop its result and the model would never learn the
115
+ // outcome of work that actually happened). Pre-EC-1 logs contain
116
+ // exactly that shape — a call that launched mid-stream and finished
117
+ // before its stop was persisted — and they must keep recovering the
118
+ // way they always did.
119
+ const unexecuted = (e) => e.type === "tool_call_end" && !openEvents.some((x) => x.type === "tool_execution_started" && x.callId === e.callId);
120
+ const afterBoundary = openEvents.some((e) => (e.type === "text_delta" || e.type === "thinking" || unexecuted(e)) && e.seq > boundary.seq);
100
121
  // The approval-panel pause: a suffix that carries a pending ask of the
101
122
  // LIVE turn — the last boundary is the user_input, no stop since — is
102
123
  // the human's pause, never a draft: the call was extracted and asked,
@@ -105,6 +126,16 @@ export function deriveRecoveryPlan(events, scope) {
105
126
  // crash mid-pause leaves exactly this shape.) A request AFTER a STOP
106
127
  // is different: it is the draft's own ask (0143's shape) — the marker
107
128
  // voids it and the request expires with the draft.
129
+ //
130
+ // EC-1 ERA NOTE — this clause is now GENERATION COMPAT, and is kept
131
+ // for that reason alone. A kiso at 0.13.0 or later cannot produce the
132
+ // shape: asks moved AFTER Turn Commit (③), so a durable
133
+ // `permission_requested` always has a durable stop before it. Logs
134
+ // written by EARLIER bins do carry it, they are on disk, and they
135
+ // must keep re-presenting their ask instead of being voided as
136
+ // drafts. Deleting this clause would silently change how pre-EC-1
137
+ // sessions recover; it stays until the generation corpus no longer
138
+ // contains a pre-EC-1 era.
108
139
  const liveAsk = afterBoundary &&
109
140
  boundary.type === "user_input" &&
110
141
  openEvents.some((e) => e.type === "permission_requested" && e.seq > boundary.seq);
@@ -26,25 +26,52 @@
26
26
  * 4. NO STOP AT ALL drops the held calls entirely — the kernel already
27
27
  * voids that malformed turn (invalid_request).
28
28
  *
29
- * THE CONSERVATISM SPLIT core and runtime differ here BY DESIGN:
30
- *
31
- * - the RAW KERNEL STREAMS. `loop()` over a bare adapter launches each
32
- * validated, policy-allowed call the moment its `tool_call_end` lands
33
- * (ADR-0024 Amendment 1, streaming execution): maximum overlap with
34
- * the model stream, trusting calls as they arrive.
35
- * - the FLAGSHIP RUNTIME STOP-GATES. `run.ts` composes this wrapper into
36
- * EVERY run unconditionally, so the product pays a real latency cost —
37
- * the 0.1.26 mid-stream launch timing is gone (the parallel window is
38
- * NOT: the released batch still runs concurrently) — to buy the
39
- * guarantee.
40
- *
41
- * So the kernel's default is speed and the flagship's default is safety;
42
- * an embedder choosing the kernel alone chooses the streaming launch, and
43
- * applying this wrapper is how they opt into the guarantee. The kernel
44
- * machinery (the launch, the window, the voided settle) is untouched
45
- * either way the gate lives at the adapter boundary.
46
- *
47
- * Pinned by `packages/runtime/tests/truncation-guard.test.ts`.
29
+ * EC-1 THE CONTRACT AMENDMENT. The kernel closed the destructive half
30
+ * of this by itself: `max_tokens` cannot carry a tool call, so a truncated
31
+ * turn never reaches Turn Commit, and a commit-required handler never starts
32
+ * before that commit. What max_tokens means now, in full the four clauses
33
+ * that replace the old "zero tools executed on truncation" line:
34
+ *
35
+ * 1. COMMIT-REQUIRED CALLS NEVER EXECUTE. On either path, guarded or bare.
36
+ * This is the kernel's guarantee, not the wrapper's, and it holds for
37
+ * every tool that declares nothing — which is every write, edit and
38
+ * shell tool kiso ships.
39
+ * 2. PRECOMMIT-SAFE CALLS MAY ALREADY HAVE EXECUTED, and that execution is
40
+ * DECLARED HARMLESS. A tool carrying `effects.precommitSafe` says
41
+ * running it before the turn commits is harmless for EVERY invocation
42
+ * read-only, free, local. Bare, such a call launches during the stream
43
+ * and a truncated turn may find its receipt already durable. That is
44
+ * the certificate being spent, not a leak.
45
+ * 3. THE TURN IS NOT COMMITTED. No durable stop is written. The calls are
46
+ * an uncommitted draft, which is what the resume sees.
47
+ * 4. PRECOMMIT RESULTS NEVER LEGITIMIZE IT (invariant 7). A durable
48
+ * receipt from clause 2 is an execution fact and nothing more: it does
49
+ * not commit the invocation, and it does not make the model turn valid.
50
+ *
51
+ * WHAT THE WRAPPER STILL BUYS, given all that:
52
+ *
53
+ * - REPORTING. It releases the held batch with `input: null`, so every
54
+ * call is ANSWERED with an honest invalid_input result. Bare, the same
55
+ * turn leaves its calls with no results at all — an uncommitted draft
56
+ * the resume must void.
57
+ * - THE PRECOMMIT CASE. The hold sits UPSTREAM of the kernel: a held call
58
+ * never reaches the loop until the stop is known, so clause 2's "may
59
+ * already have executed" is exactly what the guard removes. Guarded,
60
+ * nothing runs at all — not even a declared read.
61
+ *
62
+ * So the conservatism split did not disappear, it MOVED. It used to be the
63
+ * difference between a destructive edit running and not running; it is now
64
+ * the difference between a harmless read running and not running, plus the
65
+ * reporting. The kernel's default is speed for CERTIFIED calls and safety
66
+ * for everything else; the flagship runtime composes this wrapper into every
67
+ * run and pays the latency to have neither.
68
+ *
69
+ * Pinned by `packages/runtime/tests/truncation-guard.test.ts` (clauses 1-3
70
+ * of the wrapper contract, byte-unchanged across EC-1 — the amendment did
71
+ * not weaken the guard) and by
72
+ * `packages/runtime/tests/sc1-truncation-contract-pins.test.ts` (clause 4 of
73
+ * the wrapper contract, and the four amended max_tokens clauses above — the
74
+ * declared TRUNCATION CLASS).
48
75
  */
49
76
  import type { Adapter } from "@vincemakes/kiso-core";
50
77
  /** Wrap the adapter so a truncated turn's tool batch can never execute. */
@@ -26,25 +26,52 @@
26
26
  * 4. NO STOP AT ALL drops the held calls entirely — the kernel already
27
27
  * voids that malformed turn (invalid_request).
28
28
  *
29
- * THE CONSERVATISM SPLIT core and runtime differ here BY DESIGN:
29
+ * EC-1 THE CONTRACT AMENDMENT. The kernel closed the destructive half
30
+ * of this by itself: `max_tokens` cannot carry a tool call, so a truncated
31
+ * turn never reaches Turn Commit, and a commit-required handler never starts
32
+ * before that commit. What max_tokens means now, in full — the four clauses
33
+ * that replace the old "zero tools executed on truncation" line:
30
34
  *
31
- * - the RAW KERNEL STREAMS. `loop()` over a bare adapter launches each
32
- * validated, policy-allowed call the moment its `tool_call_end` lands
33
- * (ADR-0024 Amendment 1, streaming execution): maximum overlap with
34
- * the model stream, trusting calls as they arrive.
35
- * - the FLAGSHIP RUNTIME STOP-GATES. `run.ts` composes this wrapper into
36
- * EVERY run unconditionally, so the product pays a real latency cost —
37
- * the 0.1.26 mid-stream launch timing is gone (the parallel window is
38
- * NOT: the released batch still runs concurrently) to buy the
39
- * guarantee.
35
+ * 1. COMMIT-REQUIRED CALLS NEVER EXECUTE. On either path, guarded or bare.
36
+ * This is the kernel's guarantee, not the wrapper's, and it holds for
37
+ * every tool that declares nothing which is every write, edit and
38
+ * shell tool kiso ships.
39
+ * 2. PRECOMMIT-SAFE CALLS MAY ALREADY HAVE EXECUTED, and that execution is
40
+ * DECLARED HARMLESS. A tool carrying `effects.precommitSafe` says
41
+ * running it before the turn commits is harmless for EVERY invocation
42
+ * read-only, free, local. Bare, such a call launches during the stream
43
+ * and a truncated turn may find its receipt already durable. That is
44
+ * the certificate being spent, not a leak.
45
+ * 3. THE TURN IS NOT COMMITTED. No durable stop is written. The calls are
46
+ * an uncommitted draft, which is what the resume sees.
47
+ * 4. PRECOMMIT RESULTS NEVER LEGITIMIZE IT (invariant 7). A durable
48
+ * receipt from clause 2 is an execution fact and nothing more: it does
49
+ * not commit the invocation, and it does not make the model turn valid.
40
50
  *
41
- * So the kernel's default is speed and the flagship's default is safety;
42
- * an embedder choosing the kernel alone chooses the streaming launch, and
43
- * applying this wrapper is how they opt into the guarantee. The kernel
44
- * machinery (the launch, the window, the voided settle) is untouched
45
- * either way — the gate lives at the adapter boundary.
51
+ * WHAT THE WRAPPER STILL BUYS, given all that:
46
52
  *
47
- * Pinned by `packages/runtime/tests/truncation-guard.test.ts`.
53
+ * - REPORTING. It releases the held batch with `input: null`, so every
54
+ * call is ANSWERED with an honest invalid_input result. Bare, the same
55
+ * turn leaves its calls with no results at all — an uncommitted draft
56
+ * the resume must void.
57
+ * - THE PRECOMMIT CASE. The hold sits UPSTREAM of the kernel: a held call
58
+ * never reaches the loop until the stop is known, so clause 2's "may
59
+ * already have executed" is exactly what the guard removes. Guarded,
60
+ * nothing runs at all — not even a declared read.
61
+ *
62
+ * So the conservatism split did not disappear, it MOVED. It used to be the
63
+ * difference between a destructive edit running and not running; it is now
64
+ * the difference between a harmless read running and not running, plus the
65
+ * reporting. The kernel's default is speed for CERTIFIED calls and safety
66
+ * for everything else; the flagship runtime composes this wrapper into every
67
+ * run and pays the latency to have neither.
68
+ *
69
+ * Pinned by `packages/runtime/tests/truncation-guard.test.ts` (clauses 1-3
70
+ * of the wrapper contract, byte-unchanged across EC-1 — the amendment did
71
+ * not weaken the guard) and by
72
+ * `packages/runtime/tests/sc1-truncation-contract-pins.test.ts` (clause 4 of
73
+ * the wrapper contract, and the four amended max_tokens clauses above — the
74
+ * declared TRUNCATION CLASS).
48
75
  */
49
76
  /** Wrap the adapter so a truncated turn's tool batch can never execute. */
50
77
  export function truncationGuard(adapter) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vincemakes/kiso-runtime",
3
- "version": "0.12.0",
3
+ "version": "0.13.0",
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.12.0"
28
+ "@vincemakes/kiso-core": "0.13.0"
29
29
  },
30
30
  "peerDependencies": {
31
- "@vincemakes/kiso-provider-anthropic": "0.12.0",
32
- "@vincemakes/kiso-provider-openai": "0.12.0"
31
+ "@vincemakes/kiso-provider-anthropic": "0.13.0",
32
+ "@vincemakes/kiso-provider-openai": "0.13.0"
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.12.0",
43
+ "@vincemakes/kiso-evals": "0.13.0",
44
44
  "@types/node": "^26.1.2",
45
45
  "typescript": "^5.7.2",
46
46
  "vitest": "^3.0.0"