@vincemakes/kiso-runtime 0.41.2 → 0.42.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/agent.d.ts CHANGED
@@ -89,6 +89,12 @@ export interface AgentDefinition {
89
89
  readonly tool: string;
90
90
  readonly line: string;
91
91
  }>;
92
+ /** 0.42.0: "off" withholds the generated "Tool use:" block entirely —
93
+ * no fixed directives, no snippets, no guidelines — so the system
94
+ * prompt is exactly the configured text (a host matching another
95
+ * system byte for byte). The tool schemas still ride the request.
96
+ * Default "on". Type-only additive; never part of the profile digest. */
97
+ readonly toolTable?: "on" | "off";
92
98
  /** E1: loaded extensions — their tools merge into the registry (a name
93
99
  * collision with a built-in is a loud startup error), their hooks
94
100
  * compose after the agent's own (the existing come first), their approvals join the
package/dist/agent.js CHANGED
@@ -178,6 +178,7 @@ export class AgentRuntime {
178
178
  ...(this.#definition.maxRetries !== undefined ? { maxRetries: this.#definition.maxRetries } : {}),
179
179
  ...(this.#definition.streamIdleMs !== undefined ? { streamIdleMs: this.#definition.streamIdleMs } : {}),
180
180
  ...(this.#definition.toolRules !== undefined ? { toolRules: this.#definition.toolRules } : {}),
181
+ ...(this.#definition.toolTable !== undefined ? { toolTable: this.#definition.toolTable } : {}),
181
182
  ...(this.#definition.extensions !== undefined ? { extensions: this.#definition.extensions } : {}),
182
183
  };
183
184
  // 0.40.0: the last bill's time rides the RECORD, not the event.
package/dist/compose.d.ts CHANGED
@@ -3,7 +3,7 @@
3
3
  * from session.ts: the extension system-prompt appends, the extension
4
4
  * hook composition (the existing come first), and the loop's microcompact config lookup.
5
5
  */
6
- import type { ApprovalChain, Event, HookHost, KisoExtension, ToolRegistry } from "@vincemakes/kiso-core";
6
+ import type { Adapter, ApprovalChain, Event, HookHost, KisoExtension, ToolRegistry } from "@vincemakes/kiso-core";
7
7
  import type { SessionConfig } from "./session.js";
8
8
  /**
9
9
  * 0.1.40 (R-C item 1) — the tool substitution table: vocabulary rows, each
@@ -29,7 +29,12 @@ export type ToolRules = ReadonlyArray<{
29
29
  * then the tool table (generated machinery sits BETWEEN the base and the
30
30
  * extension appends). One function, so an in-band summary outside a run
31
31
  * (ADR-0055 A2, /compact) sends the exact prefix a run sends. */
32
- export declare function runBasePrompt(systemPrompt: string | undefined, registry: ToolRegistry, rules?: ToolRules): string | undefined;
32
+ /** 0.42.0: `"off"` leaves the base prompt exactly as configured — no
33
+ * "Tool use:" block, no snippets, no guidelines — for a host whose prompt
34
+ * must match another system's byte for byte. The tool SCHEMAS still ride
35
+ * the request; only the generated prose is withheld. */
36
+ export type ToolTableMode = "on" | "off";
37
+ export declare function runBasePrompt(systemPrompt: string | undefined, registry: ToolRegistry, rules?: ToolRules, table?: ToolTableMode): string | undefined;
33
38
  /** The table, or "" when the registry is empty (no vocabulary, no tools). */
34
39
  export declare function composeToolTable(registry: ToolRegistry, rules?: ToolRules): string;
35
40
  /**
@@ -37,7 +42,20 @@ export declare function composeToolTable(registry: ToolRegistry, rules?: ToolRul
37
42
  * order, \n\n-joined — deterministic (same extension list → same prompt).
38
43
  * No appends → the base passes through byte-identical.
39
44
  */
45
+ /** An extension's append as text: a string as is, a function evaluated
46
+ * NOW (0.42.0 — call this once per attempt). 0.42.1 (#14): a function
47
+ * may answer undefined — no append this attempt, no separator. */
48
+ export declare function appendOf(extension: KisoExtension): string | undefined;
40
49
  export declare function composeSystemPrompt(base: string | undefined, extensions: readonly KisoExtension[]): string | undefined;
50
+ /** The base and the append texts, \n\n-joined; no appends → the base as is. */
51
+ export declare function joinPrompt(base: string | undefined, appends: readonly string[]): string | undefined;
52
+ /** 0.42.0: the OUTERMOST adapter layer — `compose` runs exactly once per
53
+ * provider attempt (a retry is a new attempt) and its result is the
54
+ * request's system prompt as every inner layer, the tracer first, sees
55
+ * it: what the trace records is what the model was sent, by construction.
56
+ * Never a getter on the config — the kernel and the tracer each read
57
+ * their field twice, and an observer must not run the product's code. */
58
+ export declare function promptPerAttempt(adapter: Adapter, compose: () => string | undefined): Adapter;
41
59
  /**
42
60
  * E1: extension hooks compose AFTER the agent's own (the existing come first — the existing
43
61
  * hook sees every event first). Observers all run, in order; onUserMessage
package/dist/compose.js CHANGED
@@ -3,13 +3,14 @@
3
3
  * from session.ts: the extension system-prompt appends, the extension
4
4
  * hook composition (the existing come first), and the loop's microcompact config lookup.
5
5
  */
6
- /** The base a run's system prompt is composed on: the session's prompt,
7
- * then the tool table (generated machinery sits BETWEEN the base and the
8
- * extension appends). One function, so an in-band summary outside a run
9
- * (ADR-0055 A2, /compact) sends the exact prefix a run sends. */
10
- export function runBasePrompt(systemPrompt, registry, rules = []) {
6
+ export function runBasePrompt(systemPrompt, registry, rules = [], table = "on") {
7
+ // 0.42.1 (the products' request #15): "" composes as ABSENT — never a
8
+ // bare separator ahead of the table or the appends.
9
+ const base = systemPrompt === "" ? undefined : systemPrompt;
10
+ if (table === "off")
11
+ return base;
11
12
  const toolTable = composeToolTable(registry, rules);
12
- return toolTable === "" ? systemPrompt : systemPrompt === undefined ? toolTable : `${systemPrompt}\n\n${toolTable}`;
13
+ return toolTable === "" ? base : base === undefined ? toolTable : `${base}\n\n${toolTable}`;
13
14
  }
14
15
  /** The table, or "" when the registry is empty (no vocabulary, no tools). */
15
16
  export function composeToolTable(registry, rules = []) {
@@ -37,11 +38,40 @@ export function composeToolTable(registry, rules = []) {
37
38
  * order, \n\n-joined — deterministic (same extension list → same prompt).
38
39
  * No appends → the base passes through byte-identical.
39
40
  */
41
+ /** An extension's append as text: a string as is, a function evaluated
42
+ * NOW (0.42.0 — call this once per attempt). 0.42.1 (#14): a function
43
+ * may answer undefined — no append this attempt, no separator. */
44
+ export function appendOf(extension) {
45
+ const append = extension.systemPrompt?.append;
46
+ return typeof append === "function" ? append() : append;
47
+ }
40
48
  export function composeSystemPrompt(base, extensions) {
41
- const appends = extensions.flatMap((e) => (e.systemPrompt?.append === undefined ? [] : [e.systemPrompt.append]));
49
+ return joinPrompt(base, extensions.flatMap((e) => {
50
+ const text = appendOf(e);
51
+ return text === undefined ? [] : [text];
52
+ }));
53
+ }
54
+ /** The base and the append texts, \n\n-joined; no appends → the base as is. */
55
+ export function joinPrompt(base, appends) {
56
+ const head = base === "" ? undefined : base; // 0.42.1 (#15): "" is absent
42
57
  if (appends.length === 0)
43
- return base;
44
- return base === undefined ? appends.join("\n\n") : `${base}\n\n${appends.join("\n\n")}`;
58
+ return head;
59
+ return head === undefined ? appends.join("\n\n") : `${head}\n\n${appends.join("\n\n")}`;
60
+ }
61
+ /** 0.42.0: the OUTERMOST adapter layer — `compose` runs exactly once per
62
+ * provider attempt (a retry is a new attempt) and its result is the
63
+ * request's system prompt as every inner layer, the tracer first, sees
64
+ * it: what the trace records is what the model was sent, by construction.
65
+ * Never a getter on the config — the kernel and the tracer each read
66
+ * their field twice, and an observer must not run the product's code. */
67
+ export function promptPerAttempt(adapter, compose) {
68
+ return {
69
+ stream: (options) => {
70
+ const systemPrompt = compose();
71
+ const { systemPrompt: _fixed, ...rest } = options;
72
+ return adapter.stream(systemPrompt === undefined ? rest : { ...rest, systemPrompt });
73
+ },
74
+ };
45
75
  }
46
76
  /**
47
77
  * E1: extension hooks compose AFTER the agent's own (the existing come first — the existing
package/dist/run.js CHANGED
@@ -7,7 +7,7 @@ import { denialResult, loop } 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 { composeApprovalChain, composeSystemPrompt, runBasePrompt } from "./compose.js";
10
+ import { appendOf, composeApprovalChain, 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";
@@ -88,16 +88,33 @@ export class Run {
88
88
  // session's base prompt and the extension appends: generated
89
89
  // machinery never outranks the deliberate extension text (the E2
90
90
  // "append lands at the END" contract holds). "" when empty.
91
- const basePrompt = runBasePrompt(this.#config.systemPrompt, this.#config.registry, this.#config.toolRules ?? []);
91
+ const basePrompt = runBasePrompt(this.#config.systemPrompt, this.#config.registry, this.#config.toolRules ?? [], this.#config.toolTable ?? "on");
92
92
  // E3 — the ledger's parts: the base as CONFIGURED (what the CLI
93
93
  // handed the runtime — the tool table is generated machinery, R3)
94
94
  // and the extension appends in load order (R4 attribution). The
95
95
  // composed string below is their result; the ledger counts the
96
96
  // parts, observation-only. exactOptionalPropertyTypes: an absent
97
97
  // surface is an absent key — never an explicit undefined (R9).
98
- const rentParts = {
99
- ...(this.#config.systemPrompt !== undefined ? { base: this.#config.systemPrompt } : {}),
100
- appends: (this.#config.extensions ?? []).flatMap((e) => e.systemPrompt?.append === undefined ? [] : [{ name: e.name, text: e.systemPrompt.append }]),
98
+ // 0.42.0 (the products' request #11): an extension's append is
99
+ // evaluated ONCE PER PROVIDER ATTEMPT, never during a tool — by
100
+ // `snapshot`, which the outermost adapter layer (promptPerAttempt,
101
+ // in loopConfig) calls as it builds each attempt's request. The
102
+ // one evaluation composes the request's prompt from the fixed
103
+ // base AND fills the rent ledger's `appends`: the tracer, inside
104
+ // that layer, records the bytes the model was sent. The in-band
105
+ // summary reads the last attempt's snapshot (the prefix the run
106
+ // last sent) or takes one if no attempt has happened yet.
107
+ const rentParts = { ...(this.#config.systemPrompt !== undefined ? { base: this.#config.systemPrompt } : {}), appends: [] };
108
+ let lastPrompt;
109
+ let taken = false;
110
+ const snapshot = () => {
111
+ const appends = (this.#config.extensions ?? []).flatMap((e) => {
112
+ const text = appendOf(e);
113
+ return text === undefined ? [] : [{ name: e.name, text }];
114
+ });
115
+ rentParts.appends = appends;
116
+ taken = true;
117
+ return (lastPrompt = joinPrompt(basePrompt, appends.map((a) => a.text)));
101
118
  };
102
119
  // E1 (1.2.0): the request tracer — the observation ledger. It
103
120
  // sits at the adapter boundary; the model-visible byte stream is
@@ -124,7 +141,6 @@ export class Run {
124
141
  // E2: the session's own systemPrompt first, then every extension
125
142
  // append in LOAD order — deterministic (same extensions → same
126
143
  // prompt); no appends → byte-identical to the extension-less run.
127
- const systemPrompt = composeSystemPrompt(basePrompt, this.#config.extensions ?? []);
128
144
  const approvalChain = composeApprovalChain(this.#config.extensions ?? []);
129
145
  // ADR-0055 Amendment 1 (A1b): the compaction point the kernel asks
130
146
  // before every request — the run's own prefix, so an in-band
@@ -139,7 +155,7 @@ export class Run {
139
155
  wireReasoning = undefined;
140
156
  }
141
157
  const compact = this.#session.compactionPoint({
142
- ...(systemPrompt !== undefined ? { systemPrompt } : {}),
158
+ systemPrompt: () => (taken ? lastPrompt : snapshot()),
143
159
  tools: () => this.#config.registry.snapshot().specs,
144
160
  ...(wireReasoning !== undefined ? { reasoning: wireReasoning } : {}),
145
161
  signal,
@@ -154,10 +170,9 @@ export class Run {
154
170
  // ADR-0055 Amendment 2 (decision 3): the overflow belt sits outside
155
171
  // the guards, so a stall or a cut on a request known not to fit is
156
172
  // classified as the overflow it is, and the tracer records that.
157
- adapter: traceGuard(tracer, overflowBelt(truncationGuard(idleGuard(this.#adapter, this.#config.streamIdleMs ?? DEFAULT_STREAM_IDLE_MS)), () => this.#session.overflowMeasure())), // tracer assigned above, before loopConfig
173
+ adapter: promptPerAttempt(traceGuard(tracer, overflowBelt(truncationGuard(idleGuard(this.#adapter, this.#config.streamIdleMs ?? DEFAULT_STREAM_IDLE_MS)), () => this.#session.overflowMeasure())), snapshot), // tracer assigned above, before loopConfig
158
174
  model: this.#config.model,
159
175
  sessionId: this.#session.id, // P3: tools see their session (ToolContext.sessionId)
160
- ...(systemPrompt !== undefined ? { systemPrompt } : {}),
161
176
  registry: this.#config.registry,
162
177
  ...(this.#config.hooks !== undefined ? { hooks: this.#config.hooks } : {}),
163
178
  ...(this.#config.maxTurns !== undefined ? { maxTurns: this.#config.maxTurns } : {}),
package/dist/session.d.ts CHANGED
@@ -147,7 +147,8 @@ export declare class AgentSession {
147
147
  * standing prune here, for SDK callers — the kernel no longer has one.
148
148
  */
149
149
  compactionPoint(run: {
150
- readonly systemPrompt?: string;
150
+ /** 0.42.0: a thunk — evaluated ONCE per in-band summary (the run's per-attempt snapshot). */
151
+ readonly systemPrompt?: string | (() => string | undefined);
151
152
  readonly tools: () => readonly ToolSpec[];
152
153
  readonly reasoning?: {
153
154
  readonly thinking?: "adaptive" | "enabled" | "disabled";
@@ -559,6 +560,8 @@ export interface SessionConfig {
559
560
  readonly tool: string;
560
561
  readonly line: string;
561
562
  }>;
563
+ /** 0.42.0: "off" withholds the generated tool table entirely (see AgentDefinition). */
564
+ readonly toolTable?: "on" | "off";
562
565
  /**
563
566
  * E1: loaded extensions — their tools join the registry (idempotently;
564
567
  * a collision with a built-in name was already rejected at agent
package/dist/session.js CHANGED
@@ -394,12 +394,13 @@ export class AgentSession {
394
394
  const boundary = checkpointBoundarySeq(events, { keepTokens: tail });
395
395
  if (boundary === undefined)
396
396
  return null;
397
+ const summaryPrompt = typeof run.systemPrompt === "function" ? run.systemPrompt() : run.systemPrompt;
397
398
  let path;
398
399
  let result;
399
400
  try {
400
401
  ({ result, path } = await this.#checkpointCall({
401
402
  messages,
402
- inBand: { ...(run.systemPrompt !== undefined ? { systemPrompt: run.systemPrompt } : {}), tools: run.tools() },
403
+ inBand: { ...(summaryPrompt !== undefined ? { systemPrompt: summaryPrompt } : {}), tools: run.tools() },
403
404
  ...(run.reasoning !== undefined ? { reasoning: run.reasoning } : {}),
404
405
  serialized: () => serializeCovered({ events, prevPoint: lastSummaryPoint(events), boundary }),
405
406
  serializedReasoning: true,
@@ -842,7 +843,7 @@ export class AgentSession {
842
843
  // the one fallback. The budget is unchanged: measured for the
843
844
  // manual gesture, fixed for the policy (0.39.2).
844
845
  const cfg = this.#effectiveConfig();
845
- const systemPrompt = composeSystemPrompt(runBasePrompt(cfg.systemPrompt, cfg.registry, cfg.toolRules ?? []), cfg.extensions ?? []);
846
+ const systemPrompt = composeSystemPrompt(runBasePrompt(cfg.systemPrompt, cfg.registry, cfg.toolRules ?? [], cfg.toolTable ?? "on"), cfg.extensions ?? []);
846
847
  const wire = cfg.reasoning === undefined ? undefined : resolveReasoning(binding.model, cfg.reasoning, binding.baseUrl);
847
848
  const { result: call, path } = await this.#checkpointCall({
848
849
  messages: this.projected(),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vincemakes/kiso-runtime",
3
- "version": "0.41.2",
3
+ "version": "0.42.1",
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.41.2"
28
+ "@vincemakes/kiso-core": "0.42.1"
29
29
  },
30
30
  "peerDependencies": {
31
- "@vincemakes/kiso-provider-anthropic": "0.41.2",
32
- "@vincemakes/kiso-provider-openai": "0.41.2",
33
- "@vincemakes/kiso-provider-openai-responses": "0.41.2"
31
+ "@vincemakes/kiso-provider-anthropic": "0.42.1",
32
+ "@vincemakes/kiso-provider-openai": "0.42.1",
33
+ "@vincemakes/kiso-provider-openai-responses": "0.42.1"
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.41.2",
47
+ "@vincemakes/kiso-evals": "0.42.1",
48
48
  "@types/node": "^26.1.2",
49
49
  "typescript": "^5.7.2",
50
50
  "vitest": "^3.0.0"