@vincemakes/kiso-runtime 0.47.2 → 0.48.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.
@@ -167,6 +167,14 @@ const chatgptRow = (model, opts = {}) => ({
167
167
  const OPENAI_MODELS_ASOF_2 = "2026-09-09";
168
168
  const CHATGPT_PRESETS_SOURCE_2 = "https://github.com/openai/codex/blob/634ebc1865c6ac840ed3ba118f040d527bf4b55d/codex-rs/models-manager/models.json";
169
169
  const ULTRA_LADDER = ["low", "medium", "high", "xhigh", "max", "ultra"];
170
+ /** 0.48.0 (finding 0473-F2): the presets at a later commit, read 2026-10-09.
171
+ * The owner's `sol` profile moved to `gpt-6.1-sol` on 2026-10-08, and with
172
+ * no row kiso sent it no effort: the remembered `high` never reached the
173
+ * wire and no reasoning summary came back. The id answered on the
174
+ * subscription backend that night (the 0.47.3 live sessions). No
175
+ * first-party row: no model page was read for it. */
176
+ const OPENAI_MODELS_ASOF_3 = "2026-10-09";
177
+ const CHATGPT_PRESETS_SOURCE_3 = "https://github.com/openai/codex/blob/d63a9b8344cfe58bc78bbe319b560378fc8756ef/codex-rs/models-manager/models.json";
170
178
  /** The v1 table. Nulls outnumber numbers ON PURPOSE: only values with a
171
179
  * named source enter; everything else waits for one. */
172
180
  const ENTRIES = [
@@ -409,6 +417,7 @@ const ENTRIES = [
409
417
  openaiRow("gpt-5.6-sol", "medium", { inputPerM: 4, outputPerM: 20, cacheReadPerM: 0.4 }, "https://developers.openai.com/api/docs/models/gpt-5.6-sol", { levels: ["none", "low", "medium", "high", "xhigh", "max"], asOf: OPENAI_MODELS_ASOF_2 }),
410
418
  chatgptRow("gpt-6-astra", { levels: ULTRA_LADDER, default: "low", source: CHATGPT_PRESETS_SOURCE_2, asOf: OPENAI_MODELS_ASOF_2 }),
411
419
  chatgptRow("gpt-5.6-sol", { levels: ULTRA_LADDER, default: "low", source: CHATGPT_PRESETS_SOURCE_2, asOf: OPENAI_MODELS_ASOF_2 }),
420
+ chatgptRow("gpt-6.1-sol", { levels: ULTRA_LADDER, default: "low", source: CHATGPT_PRESETS_SOURCE_3, asOf: OPENAI_MODELS_ASOF_3 }),
412
421
  // The compat table's SECOND row (roadmap R3.2 §1a step 1): GLM 5.3 Flash
413
422
  // through OpenRouter, read from OpenRouter's models API on 2026-09-09. The
414
423
  // id is OpenRouter's, so the row is keyed to that origin; the
package/dist/run.js CHANGED
@@ -7,7 +7,7 @@ import { denialResult, loop, validateArgs } from "@vincemakes/kiso-core";
7
7
  import { ABORTED, MergedSignal, abortable, openRunId } from "./recovery.js";
8
8
  import { resolveReasoning } from "./provider/metadata.js";
9
9
  import { deriveRecoveryPlan, invocationSeqOf } from "./recovery-plan.js";
10
- import { appendOf, composeApprovalChain, joinPrompt, promptPerAttempt, runBasePrompt } from "./compose.js";
10
+ import { appendOf, composeApprovalChain, composeToolTable, joinPrompt, promptPerAttempt, runBasePrompt } from "./compose.js";
11
11
  import { truncationGuard } from "./truncation-guard.js";
12
12
  import { overflowBelt } from "./overflow-belt.js";
13
13
  import { DEFAULT_STREAM_IDLE_MS, idleGuard } from "./idle-guard.js";
@@ -212,7 +212,11 @@ export class Run {
212
212
  // that layer, records the bytes the model was sent. The in-band
213
213
  // summary reads the last attempt's snapshot (the prefix the run
214
214
  // last sent) or takes one if no attempt has happened yet.
215
- const rentParts = { ...(this.#config.systemPrompt !== undefined ? { base: this.#config.systemPrompt } : {}), appends: [] };
215
+ // RG-F2: the generated table is a ledger line of its own — the
216
+ // same text runBasePrompt placed between the base and the appends
217
+ // (one registry read at the same instant, so it cannot differ).
218
+ const table = (this.#config.toolTable ?? "on") === "on" ? composeToolTable(this.#config.registry, this.#config.toolRules ?? []) : "";
219
+ const rentParts = { ...(this.#config.systemPrompt !== undefined ? { base: this.#config.systemPrompt } : {}), ...(table !== "" ? { table } : {}), appends: [] };
216
220
  let lastPrompt;
217
221
  let taken = false;
218
222
  const snapshot = () => {
package/dist/session.d.ts CHANGED
@@ -130,7 +130,7 @@ export declare class AgentSession {
130
130
  /** Deliver this session's task transitions to the model: into a live
131
131
  * run at its next safe point, or — idle — held for the next run, or one
132
132
  * continuation run through `onWake` (off with `wake: false`). */
133
- useTasks(manager: TaskManager, options?: Pick<TaskDeliveryOptions, "wake" | "onWake" | "windowMs" | "onLost"> & {
133
+ useTasks(manager: TaskManager, options?: Pick<TaskDeliveryOptions, "wake" | "onWake" | "windowMs" | "maxWakes" | "onLost"> & {
134
134
  readonly autoDetachMinAgeMs?: number;
135
135
  }): () => void;
136
136
  /** ADR-0057 §5 (3e D1) — a person's steer arrived: the foreground
@@ -15,8 +15,8 @@
15
15
  * Exactly once, from the log: a transition counts as delivered when a
16
16
  * `user_input` whose `via.items` names it is in the session's log (ADR-0051
17
17
  * Amendment 8). The set is a cache rebuilt from the log, never a second
18
- * truth. Lineage from the log too: a task started inside a wake run (a run
19
- * whose first input is a task notice) never wakes — depth 1.
18
+ * truth. The chain budget from the log too (ADR-0058 Amendment 9): the wake
19
+ * runs since the last run a person started are counted, never kept.
20
20
  *
21
21
  * The summary snapshot is what the MODEL was told, never the live journal:
22
22
  * a compaction must not reveal a transition that has not been delivered.
@@ -43,12 +43,16 @@ import { type Run } from "../run.js";
43
43
  import { type TaskInfo, type TaskManager } from "./manager.js";
44
44
  export interface TaskDeliveryOptions {
45
45
  readonly manager: TaskManager;
46
- /** The session's durable events: receipts and lineage are read here. */
46
+ /** The session's durable events: receipts and the chain are read here. */
47
47
  readonly events: () => readonly Event[];
48
48
  /** The session's live run, when one is running. */
49
49
  readonly liveRun: () => Run | undefined;
50
50
  /** False turns every wake into a notify (the switch, ADR-0058 §8.4). */
51
51
  readonly wake?: boolean;
52
+ /** ADR-0058 Amendment 9 — the chain budget: how many autonomous wakes may follow
53
+ * one person's input before a terminal delivers as a notify instead.
54
+ * Replaces ADR-0058 §8 guard 3 (lineage depth 1). Default 20. */
55
+ readonly maxWakes?: number;
52
56
  /** Start ONE continuation run whose first input is this notice. */
53
57
  readonly onWake?: (input: {
54
58
  readonly content: string;
@@ -65,6 +69,7 @@ export interface TaskDeliveryOptions {
65
69
  /** Read a task's output tail (injectable for tests). */
66
70
  readonly readTail?: (task: TaskInfo, bytes: number) => string;
67
71
  }
72
+ export declare const DEFAULT_MAX_WAKES = 20;
68
73
  export declare class TaskDelivery {
69
74
  #private;
70
75
  constructor(options: TaskDeliveryOptions);
@@ -15,8 +15,8 @@
15
15
  * Exactly once, from the log: a transition counts as delivered when a
16
16
  * `user_input` whose `via.items` names it is in the session's log (ADR-0051
17
17
  * Amendment 8). The set is a cache rebuilt from the log, never a second
18
- * truth. Lineage from the log too: a task started inside a wake run (a run
19
- * whose first input is a task notice) never wakes — depth 1.
18
+ * truth. The chain budget from the log too (ADR-0058 Amendment 9): the wake
19
+ * runs since the last run a person started are counted, never kept.
20
20
  *
21
21
  * The summary snapshot is what the MODEL was told, never the live journal:
22
22
  * a compaction must not reveal a transition that has not been delivered.
@@ -44,6 +44,7 @@ import { isRuntimeInput } from "@vincemakes/kiso-core";
44
44
  import { NOTICE_FOOTER as FOOTER } from "../run.js";
45
45
  import { endTransitionOf } from "./manager.js";
46
46
  const key = (i) => `${i.taskId}:${i.transition}`;
47
+ export const DEFAULT_MAX_WAKES = 20;
47
48
  /** ADR-0058 3d (D3): a child's excerpt, and a group's excerpts together. */
48
49
  const CHILD_EXCERPT_BYTES = 4_096;
49
50
  const GROUP_EXCERPT_BYTES = 16_384;
@@ -166,7 +167,8 @@ export class TaskDelivery {
166
167
  const mode = this.#modeOf(task, item);
167
168
  if (mode === "silent")
168
169
  return;
169
- this.#buffer.push({ item, line: this.#line(task, item), mode });
170
+ const spent = mode === "notify" && this.#eligible(task, item) && !this.#underBudget();
171
+ this.#buffer.push({ item, line: spent ? `${this.#line(task, item)}\nchain budget spent: ${this.#wakesSinceLastPerson()} autonomous wakes since the person's last message — continue when they speak` : this.#line(task, item), mode });
170
172
  if (this.#timer === null)
171
173
  this.#timer = setTimeout(() => this.#flush(), this.#o.windowMs ?? 1_000);
172
174
  }
@@ -221,7 +223,7 @@ export class TaskDelivery {
221
223
  }
222
224
  this.#scanned = events.length;
223
225
  }
224
- /** ADR-0058 §8 defaults, then the guards: the switch, and lineage. */
226
+ /** ADR-0058 §8 defaults, then the guards: the switch, and the chain budget. */
225
227
  #modeOf(task, item) {
226
228
  if (item.transition === "ready" || item.transition === "stopped" || item.transition === "unknown")
227
229
  return "notify";
@@ -232,17 +234,45 @@ export class TaskDelivery {
232
234
  return this.#wakes(item) ? "wake" : "notify";
233
235
  }
234
236
  #wakes(item) {
235
- if (this.#o.wake === false)
236
- return false;
237
- if (item.transition !== "exited" && item.transition !== "failed")
238
- return false;
239
237
  const task = this.#o.manager.get(item.taskId);
240
- if (task === undefined || task.executionId === undefined || task.profile === "service")
238
+ if (task === undefined || !this.#eligible(task, item))
241
239
  return false;
242
240
  // a child wakes only with its whole group (D2): never on its own
243
241
  if (task.agent !== undefined && !this.#complete(this.#groupOf(task)))
244
242
  return false;
245
- return !this.#startedInWakeRun(task.executionId);
243
+ return this.#underBudget();
244
+ }
245
+ /** The transition could wake, budget aside: the switch, a terminal, an
246
+ * execution's task, not a service. */
247
+ #eligible(task, item) {
248
+ if (this.#o.wake === false)
249
+ return false;
250
+ if (item.transition !== "exited" && item.transition !== "failed")
251
+ return false;
252
+ if (task.executionId === undefined || task.profile === "service")
253
+ return false;
254
+ return true;
255
+ }
256
+ /** ADR-0058 Amendment 9: the chain budget, derived from the log — never a
257
+ * counter. The count is the wake runs (first input a runtime notice)
258
+ * since the last run a person started. */
259
+ #underBudget() {
260
+ return this.#wakesSinceLastPerson() < (this.#o.maxWakes ?? DEFAULT_MAX_WAKES);
261
+ }
262
+ #wakesSinceLastPerson() {
263
+ let wakes = 0;
264
+ let firstOfRun = true;
265
+ for (const e of this.#o.events()) {
266
+ if (e.type === "terminal") {
267
+ firstOfRun = true;
268
+ continue;
269
+ }
270
+ if (e.type !== "user_input" || !firstOfRun)
271
+ continue;
272
+ firstOfRun = false;
273
+ wakes = isRuntimeInput(e) ? wakes + 1 : 0;
274
+ }
275
+ return wakes;
246
276
  }
247
277
  /** ADR-0058 3d: an agent task has ended — a failure goes into a live run
248
278
  * at once; every end waits for its group. */
@@ -383,22 +413,6 @@ export class TaskDelivery {
383
413
  attrs.push(existsSync(result) ? `result="${result}"` : `output="${task.outputPath}"`);
384
414
  return `<kiso-task ${attrs.join(" ")}/>`;
385
415
  }
386
- /** Lineage depth 1: the run that started the task began with a notice. */
387
- #startedInWakeRun(executionId) {
388
- const events = this.#o.events();
389
- const at = events.findIndex((e) => e.type === "tool_execution_started" && e.executionId === executionId);
390
- if (at < 0)
391
- return false;
392
- let start = 0;
393
- for (let n = at - 1; n >= 0; n--) {
394
- if (events[n].type === "terminal") {
395
- start = n + 1;
396
- break;
397
- }
398
- }
399
- const first = events.slice(start, at).find((e) => e.type === "user_input");
400
- return isRuntimeInput(first);
401
- }
402
416
  #line(task, item) {
403
417
  const attrs = [`id="${task.id}"`, `status="${item.transition}"`];
404
418
  if (task.state.kind === "ended" && task.state.exitCode !== null)
@@ -176,6 +176,7 @@ export class RequestTracer {
176
176
  const rent = buildRentLedger({
177
177
  model: options.model,
178
178
  ...(rentParts?.base !== undefined ? { base: rentParts.base } : {}),
179
+ ...(rentParts?.table !== undefined ? { table: rentParts.table } : {}),
179
180
  ...(rentParts?.appends !== undefined ? { appends: rentParts.appends } : {}),
180
181
  ...(tools !== undefined ? { tools } : {}),
181
182
  });
@@ -11,10 +11,17 @@
11
11
  *
12
12
  * - system:base the session's own prompt as the CLI handed it
13
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).
14
+ * runtime cannot split those, R3).
15
+ * - system:tools the runtime's generated tool table (compose.ts,
16
+ * composeToolTable): the routing rows, the fixed
17
+ * directives, each tool's snippet and guidelines,
18
+ * between the base and the appends. OVERTURNS the
19
+ * E3 note that it "stays out of the ledger (counts
20
+ * live in tool:<name> lines)": the table is prose
21
+ * the tool specs do not contain (1,686 chars at
22
+ * f42a0edc), so the ledger was short by that much
23
+ * on every request (finding RG-F2, kiso-doc
24
+ * plan-rent-gate rev 2).
18
25
  * - system:ext:<name> one line per extension with a non-empty
19
26
  * systemPrompt.append, measured on the append
20
27
  * string, in load order.
@@ -44,7 +51,7 @@
44
51
  */
45
52
  import type { ToolSpec } from "@vincemakes/kiso-core";
46
53
  export interface RentLine {
47
- /** "system:base" | "system:ext:<name>" | "tool:<name>" | "envelope" */
54
+ /** "system:base" | "system:tools" | "system:ext:<name>" | "tool:<name>" | "envelope" */
48
55
  surface: string;
49
56
  /** length of the serialized surface, measured, never a copy */
50
57
  chars: number;
@@ -59,6 +66,9 @@ export declare const RENT_LINE_FIELDS: readonly ["surface", "chars", "estTokens"
59
66
  * systemPrompt is the result — the parts are the ledger's inputs). */
60
67
  export interface RentParts {
61
68
  base?: string;
69
+ /** RG-F2: the generated tool table's text, as composed for the run
70
+ * (absent when the table is off or the registry is empty). */
71
+ table?: string;
62
72
  appends?: readonly {
63
73
  name: string;
64
74
  text: string;
@@ -68,7 +78,7 @@ export interface RentInput extends RentParts {
68
78
  model: string;
69
79
  tools?: readonly ToolSpec[];
70
80
  }
71
- /** The one ledger: system:base, then system:ext:* (load order), then
81
+ /** The one ledger: system:base, then system:tools, then system:ext:* (load order), then
72
82
  * tool:* (the array's order — the registry's toSpecs() order), then the
73
83
  * envelope. Absent surfaces contribute no lines (R9). */
74
84
  export declare function buildRentLedger(input: RentInput): RentLine[];
@@ -11,10 +11,17 @@
11
11
  *
12
12
  * - system:base the session's own prompt as the CLI handed it
13
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).
14
+ * runtime cannot split those, R3).
15
+ * - system:tools the runtime's generated tool table (compose.ts,
16
+ * composeToolTable): the routing rows, the fixed
17
+ * directives, each tool's snippet and guidelines,
18
+ * between the base and the appends. OVERTURNS the
19
+ * E3 note that it "stays out of the ledger (counts
20
+ * live in tool:<name> lines)": the table is prose
21
+ * the tool specs do not contain (1,686 chars at
22
+ * f42a0edc), so the ledger was short by that much
23
+ * on every request (finding RG-F2, kiso-doc
24
+ * plan-rent-gate rev 2).
18
25
  * - system:ext:<name> one line per extension with a non-empty
19
26
  * systemPrompt.append, measured on the append
20
27
  * string, in load order.
@@ -49,13 +56,15 @@ const line = (surface, text) => ({
49
56
  chars: text.length,
50
57
  estTokens: Math.ceil(text.length / 4),
51
58
  });
52
- /** The one ledger: system:base, then system:ext:* (load order), then
59
+ /** The one ledger: system:base, then system:tools, then system:ext:* (load order), then
53
60
  * tool:* (the array's order — the registry's toSpecs() order), then the
54
61
  * envelope. Absent surfaces contribute no lines (R9). */
55
62
  export function buildRentLedger(input) {
56
63
  const lines = [];
57
64
  if (input.base !== undefined)
58
65
  lines.push(line("system:base", input.base));
66
+ if (input.table !== undefined && input.table !== "")
67
+ lines.push(line("system:tools", input.table));
59
68
  for (const append of input.appends ?? [])
60
69
  lines.push(line(`system:ext:${append.name}`, append.text));
61
70
  for (const tool of input.tools ?? []) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vincemakes/kiso-runtime",
3
- "version": "0.47.2",
3
+ "version": "0.48.0",
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.47.2"
28
+ "@vincemakes/kiso-core": "0.48.0"
29
29
  },
30
30
  "peerDependencies": {
31
- "@vincemakes/kiso-provider-anthropic": "0.47.2",
32
- "@vincemakes/kiso-provider-openai": "0.47.2",
33
- "@vincemakes/kiso-provider-openai-responses": "0.47.2"
31
+ "@vincemakes/kiso-provider-anthropic": "0.48.0",
32
+ "@vincemakes/kiso-provider-openai": "0.48.0",
33
+ "@vincemakes/kiso-provider-openai-responses": "0.48.0"
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.47.2",
47
+ "@vincemakes/kiso-evals": "0.48.0",
48
48
  "@types/node": "^26.1.2",
49
49
  "typescript": "^5.7.2",
50
50
  "vitest": "^3.0.0"