@vincemakes/kiso-core 0.42.2 → 0.43.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/index.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  export * from "./protocol/index.js";
2
2
  export * from "./tools/tool.js";
3
3
  export * from "./tools/registry.js";
4
+ export { validateArgs } from "./tools/validate.js";
4
5
  export * from "./errors.js";
5
6
  export * from "./kernel/event-log.js";
6
7
  export * from "./kernel/hooks.js";
package/dist/index.js CHANGED
@@ -1,6 +1,7 @@
1
1
  export * from "./protocol/index.js";
2
2
  export * from "./tools/tool.js";
3
3
  export * from "./tools/registry.js";
4
+ export { validateArgs } from "./tools/validate.js";
4
5
  export * from "./errors.js";
5
6
  export * from "./kernel/event-log.js";
6
7
  export * from "./kernel/hooks.js";
@@ -2,7 +2,7 @@
2
2
  * L2 — context-economy primitives, the MECHANICAL half.
3
3
  *
4
4
  * The kernel's compaction policy is identity preservation, not summary
5
- * (mauri ADR-0007): keep the message SHELL (id, role, position), replace
5
+ * (the predecessor's ADR-0007): keep the message SHELL (id, role, position), replace
6
6
  * the content with a marker, zero LLM calls. A summary is a NEW message; it
7
7
  * never rewrites an old one. Messages are immutable (ADR-0002) — "clearing"
8
8
  * is append, not mutation.
@@ -21,7 +21,7 @@ import type { Message } from "../protocol/messages.js";
21
21
  /**
22
22
  * Rough token estimate (chars/4 + structural overhead). Calibration-free on
23
23
  * purpose: context economy only needs a stable MONOTONE proxy, not an exact
24
- * count — the threshold absorbs the error (mauri ADR-0007). The proxy's
24
+ * count — the threshold absorbs the error (the predecessor's ADR-0007). The proxy's
25
25
  * three-word contract, and the pins that hold it, are
26
26
  * packages/core/tests/sc1b-estimator.test.ts.
27
27
  */
@@ -2,7 +2,7 @@
2
2
  * L2 — context-economy primitives, the MECHANICAL half.
3
3
  *
4
4
  * The kernel's compaction policy is identity preservation, not summary
5
- * (mauri ADR-0007): keep the message SHELL (id, role, position), replace
5
+ * (the predecessor's ADR-0007): keep the message SHELL (id, role, position), replace
6
6
  * the content with a marker, zero LLM calls. A summary is a NEW message; it
7
7
  * never rewrites an old one. Messages are immutable (ADR-0002) — "clearing"
8
8
  * is append, not mutation.
@@ -64,7 +64,7 @@ function textTokens(text) {
64
64
  /**
65
65
  * Rough token estimate (chars/4 + structural overhead). Calibration-free on
66
66
  * purpose: context economy only needs a stable MONOTONE proxy, not an exact
67
- * count — the threshold absorbs the error (mauri ADR-0007). The proxy's
67
+ * count — the threshold absorbs the error (the predecessor's ADR-0007). The proxy's
68
68
  * three-word contract, and the pins that hold it, are
69
69
  * packages/core/tests/sc1b-estimator.test.ts.
70
70
  */
@@ -1,5 +1,5 @@
1
1
  /**
2
- * L2 — the hook vocabulary: 3 phases + lifecycle (mauri ADR-0006).
2
+ * L2 — the hook vocabulary: 3 phases + lifecycle (the predecessor's ADR-0006).
3
3
  *
4
4
  * ReAct's loop has exactly three model↔world interfaces — assemble (feed the
5
5
  * model), model (the model speaks), execute (the model makes the world work)
@@ -9,7 +9,7 @@
9
9
  *
10
10
  * Payload types are SHARED between fire-site and read-site: a hook author
11
11
  * cannot drift a key name, because the keys do not exist — the payload is a
12
- * typed object (the mauri payload-contract discipline, ADR-0017, as a
12
+ * typed object (the predecessor's payload-contract discipline, ADR-0017, as a
13
13
  * compile-time fact instead of a written rule).
14
14
  */
15
15
  import type { Event } from "../protocol/events.js";
@@ -1,5 +1,5 @@
1
1
  /**
2
- * L2 — the hook vocabulary: 3 phases + lifecycle (mauri ADR-0006).
2
+ * L2 — the hook vocabulary: 3 phases + lifecycle (the predecessor's ADR-0006).
3
3
  *
4
4
  * ReAct's loop has exactly three model↔world interfaces — assemble (feed the
5
5
  * model), model (the model speaks), execute (the model makes the world work)
@@ -9,7 +9,7 @@
9
9
  *
10
10
  * Payload types are SHARED between fire-site and read-site: a hook author
11
11
  * cannot drift a key name, because the keys do not exist — the payload is a
12
- * typed object (the mauri payload-contract discipline, ADR-0017, as a
12
+ * typed object (the predecessor's payload-contract discipline, ADR-0017, as a
13
13
  * compile-time fact instead of a written rule).
14
14
  */
15
15
  /** Every hook optional; unset = pass-through. The kernel runs with this. */
@@ -33,6 +33,7 @@
33
33
  * re-stream that duplicates output or tool calls.
34
34
  */
35
35
  import { isAdapterEvent } from "../protocol/adapter.js";
36
+ import { ERROR_CODES } from "../protocol/error-codes.js";
36
37
  import { EventLog } from "./event-log.js";
37
38
  import { ToolRegistry } from "../tools/registry.js";
38
39
  import { validateArgs } from "../tools/validate.js";
@@ -343,7 +344,7 @@ export async function* loop(config) {
343
344
  }
344
345
  return { wait, release };
345
346
  };
346
- const launch = (call) => {
347
+ const launch = (call, rawInput) => {
347
348
  execActive += 1;
348
349
  const tool = table.get(call.name);
349
350
  const slot = reserve(tool);
@@ -455,7 +456,7 @@ export async function* loop(config) {
455
456
  return;
456
457
  await acquireWindow();
457
458
  try {
458
- await runLedgered(call, table, hooks, { signal: signal ?? NEVER_ABORT, ...(config.sessionId !== undefined ? { sessionId: config.sessionId } : {}) }, signal, pushExec);
459
+ await runLedgered(call, table, hooks, { signal: signal ?? NEVER_ABORT, ...(config.sessionId !== undefined ? { sessionId: config.sessionId } : {}), ...(rawInput !== undefined ? { rawInput } : {}) }, signal, pushExec);
459
460
  }
460
461
  finally {
461
462
  releaseWindow();
@@ -588,6 +589,12 @@ export async function* loop(config) {
588
589
  yield await terminal({ kind: "aborted", by: "user" });
589
590
  return;
590
591
  }
592
+ // 0.43.0 (#13): the lexical arguments, buffered PER ATTEMPT — a new
593
+ // table for every stream, so a retry after an abandoned draft never
594
+ // pastes that draft's text onto a re-used callId; consumed at the
595
+ // call's end. callId is the attempt's correlation key only; the
596
+ // durable identity stays the tool_call_end's seq.
597
+ const raw = new Map();
591
598
  try {
592
599
  const stream = config.adapter.stream({
593
600
  model: config.model,
@@ -649,13 +656,16 @@ export async function* loop(config) {
649
656
  // R-E 0.1.43: the append precedes the launch — the call's
650
657
  // framework seq is assigned here; invocationSeq must never
651
658
  // be the adapter's stream-local hint (ADR-0002).
659
+ if (ev.type === "tool_call_input_delta")
660
+ raw.set(ev.callId, (raw.get(ev.callId) ?? "") + ev.inputJsonDelta);
652
661
  const full = log.append(ev);
653
662
  if (full.type === "tool_call_end") {
654
663
  pending.push(full);
655
664
  // streaming execution: the call launches immediately — the decide
656
665
  // and the ledgered run proceed in parallel with the
657
666
  // model stream.
658
- launch(full);
667
+ launch(full, raw.get(full.callId));
668
+ raw.delete(full.callId);
659
669
  }
660
670
  if (hooks.onEvent)
661
671
  await hooks.onEvent(full, {}).catch(() => { });
@@ -1345,11 +1355,19 @@ export function toStructuredError(err) {
1345
1355
  if (typeof err === "object" && err !== null) {
1346
1356
  const e = err;
1347
1357
  if (typeof e.code === "string" && typeof e.retryable === "boolean") {
1358
+ // 0.43.0 (#19): every value this returns satisfies the durable
1359
+ // StructuredError schema the store reads back with — a host
1360
+ // adapter's vendor code becomes "unknown" with the code kept in the
1361
+ // message; a status outside the non-negative safe integers is
1362
+ // dropped; retryable rides as given. The kernel never writes a
1363
+ // terminal the store cannot read. The set is the protocol's own,
1364
+ // internal — a host cannot see or change it.
1365
+ const known = ERROR_CODES.has(e.code);
1348
1366
  return {
1349
- code: e.code,
1350
- ...(e.status !== undefined ? { status: e.status } : {}),
1367
+ code: known ? e.code : "unknown",
1368
+ ...(typeof e.status === "number" && Number.isSafeInteger(e.status) && e.status >= 0 ? { status: e.status } : {}),
1351
1369
  retryable: e.retryable,
1352
- message: typeof e.message === "string" ? e.message : String(err),
1370
+ message: `${known ? "" : `[${e.code}] `}${typeof e.message === "string" ? e.message : String(err)}`,
1353
1371
  // CX-1 F8: the provider's Retry-After rides through to the kernel
1354
1372
  ...(typeof e.retryAfterMs === "number" && Number.isFinite(e.retryAfterMs) && e.retryAfterMs >= 0 ? { retryAfterMs: e.retryAfterMs } : {}),
1355
1373
  };
@@ -1,5 +1,5 @@
1
1
  /**
2
- * L2 — permission as a negotiation, not a gate (mauri ADR-0002).
2
+ * L2 — permission as a negotiation, not a gate (the predecessor's ADR-0002).
3
3
  *
4
4
  * A permission decision is a dialog step with state and an upgrade path:
5
5
  * allow one call, deny with a reason fed back to the model, or defer to a
@@ -1,5 +1,5 @@
1
1
  /**
2
- * L2 — permission as a negotiation, not a gate (mauri ADR-0002).
2
+ * L2 — permission as a negotiation, not a gate (the predecessor's ADR-0002).
3
3
  *
4
4
  * A permission decision is a dialog step with state and an upgrade path:
5
5
  * allow one call, deny with a reason fed back to the model, or defer to a
@@ -206,6 +206,9 @@ export function projectMessages(events) {
206
206
  let resultBuf = [];
207
207
  const callOrder = new Map();
208
208
  let callOrderNext = 0;
209
+ // 0.43.0 (#13): the lexical arguments per open call — set on the first
210
+ // delta (presence, even empty), taken at the call's end.
211
+ const raw = new Map();
209
212
  // R-E 0.1.44 (sentence 1): the declarations that PROJECTED (their
210
213
  // tool_use blocks were actually pushed) — the pair-atomicity signal a
211
214
  // tool_result consults. A declaration the void skipped is not here, so
@@ -366,8 +369,10 @@ export function projectMessages(events) {
366
369
  case "tool_call_start":
367
370
  if (ev.source !== undefined)
368
371
  assistantSource = ev.source;
372
+ raw.delete(ev.callId);
369
373
  break;
370
374
  case "tool_call_input_delta":
375
+ raw.set(ev.callId, (raw.get(ev.callId) ?? "") + ev.inputJsonDelta);
371
376
  break; // the parsed input arrives at tool_call_end
372
377
  case "tool_call_end":
373
378
  // 0.1.26: a tool_call_end with BUFFERED RESULTS opens the
@@ -388,7 +393,9 @@ export function projectMessages(events) {
388
393
  callId: ev.callId,
389
394
  name: ev.name,
390
395
  input: ev.input ?? {},
396
+ ...(raw.has(ev.callId) ? { rawInput: raw.get(ev.callId) } : {}),
391
397
  });
398
+ raw.delete(ev.callId); // consumed by THIS invocation — a later same-callId call inherits nothing
392
399
  break;
393
400
  case "tool_result": {
394
401
  // R-E 0.1.44 (sentence 1): PAIR ATOMICITY — a result whose
@@ -597,6 +604,9 @@ export function messagesToEvents(messages) {
597
604
  name: block.name,
598
605
  ...(msg.source !== undefined ? { source: msg.source } : {}),
599
606
  });
607
+ // 0.43.0 (#13): a block with rawInput encodes ONE delta (even ""), so Message → Events → Message is lossless
608
+ if (block.rawInput !== undefined)
609
+ out.push({ type: "tool_call_input_delta", callId: block.callId, inputJsonDelta: block.rawInput });
600
610
  out.push({
601
611
  type: "tool_call_end",
602
612
  callId: block.callId,
@@ -0,0 +1,11 @@
1
+ /**
2
+ * The durable protocol's error codes — ONE set, shared by the validator
3
+ * (isKisoEvent) and the kernel's normalizer (toStructuredError), so the
4
+ * kernel can never write a terminal the store will not read back.
5
+ *
6
+ * INTERNAL: not re-exported from `protocol/index.ts` or the package root,
7
+ * on purpose — a host must not be able to see, let alone mutate, the
8
+ * whitelist the two sides agree on. The public type is `ErrorCode`.
9
+ */
10
+ import type { ErrorCode } from "./events.js";
11
+ export declare const ERROR_CODES: ReadonlySet<ErrorCode>;
@@ -0,0 +1,11 @@
1
+ export const ERROR_CODES = new Set([
2
+ "rate_limit",
3
+ "overloaded",
4
+ "network",
5
+ "timeout",
6
+ "quota",
7
+ "api_5xx",
8
+ "context_overflow",
9
+ "invalid_request",
10
+ "unknown",
11
+ ]);
@@ -22,13 +22,6 @@
22
22
  * This module is almost types-only: the only runtime value it emits is
23
23
  * `isKisoEvent`, the type guard the session store validates records with.
24
24
  */
25
- /**
26
- * Why the model stopped producing. Provider-specific reasons are mapped at
27
- * the ADAPTER boundary into this closed union (Area 6) — `refusal`,
28
- * `pause_turn`, `content_filter`, and `context_window` are never allowed
29
- * to degrade into a normal `end_turn`. A new SDK enum that lacks a mapping
30
- * is a compile error in the adapters' exhaustive switches.
31
- */
32
25
  export type StopReason = "end_turn" | "tool_use" | "max_tokens" | "stop_sequence" | "abort" | "error" | "refusal" | "pause_turn" | "content_filter" | "context_window" | "function_call";
33
26
  /**
34
27
  * Structured failure classification, layered ON TOP OF `isError: boolean`.
@@ -22,6 +22,14 @@
22
22
  * This module is almost types-only: the only runtime value it emits is
23
23
  * `isKisoEvent`, the type guard the session store validates records with.
24
24
  */
25
+ /**
26
+ * Why the model stopped producing. Provider-specific reasons are mapped at
27
+ * the ADAPTER boundary into this closed union (Area 6) — `refusal`,
28
+ * `pause_turn`, `content_filter`, and `context_window` are never allowed
29
+ * to degrade into a normal `end_turn`. A new SDK enum that lacks a mapping
30
+ * is a compile error in the adapters' exhaustive switches.
31
+ */
32
+ import { ERROR_CODES } from "./error-codes.js";
25
33
  // ── deep-shape helpers (round 5): every variant is validated field by field —
26
34
  // legal enums, Terminal union members, Usage known/token combos, content
27
35
  // blocks, plain-object inputs, optional fields when present. A record that
@@ -45,17 +53,6 @@ const STOP_REASONS = new Set([
45
53
  "function_call",
46
54
  ]);
47
55
  const TOOL_ERROR_KINDS = new Set(["invalid_input", "precondition", "transient", "fatal"]);
48
- const ERROR_CODES = new Set([
49
- "rate_limit",
50
- "overloaded",
51
- "network",
52
- "timeout",
53
- "quota",
54
- "api_5xx",
55
- "context_overflow",
56
- "invalid_request",
57
- "unknown",
58
- ]);
59
56
  const MESSAGE_SOURCES = new Set([
60
57
  "user",
61
58
  "suggestion",
@@ -10,7 +10,7 @@
10
10
  * carries images. Forcing every caller into the array shape to serve the
11
11
  * 5% that need vision is a tax paid on every line of product code.
12
12
  *
13
- * See ADR-0003 (sum type). The tool contract's record is mauri ADR-0008 —
13
+ * See ADR-0003 (sum type). The tool contract's record is the predecessor's ADR-0008 —
14
14
  * the PREDECESSOR series, cited the same way in kernel/hooks.ts and
15
15
  * kernel/mode.ts: kiso's own numbers 0006-0019 were NEVER assigned
16
16
  * (docs/adrs/README.md), so an unprefixed "ADR-0008" sends a reader to a
@@ -70,6 +70,15 @@ export interface AssistantToolUseBlock {
70
70
  readonly callId: string;
71
71
  readonly name: string;
72
72
  readonly input: Readonly<Record<string, unknown>>;
73
+ /** 0.43.0 (#13): the arguments exactly as the model streamed them — the
74
+ * LEXICAL companion of `input` (`5.0` stays `5.0`), present when the
75
+ * provider streamed deltas, absent otherwise. Carried for the tool
76
+ * and the host; NOT replayed to the provider (0430-B1: replaying it
77
+ * changed every call's bytes and the paired bench read more requests
78
+ * on that arm — the adapters replay the canonical form). Never a
79
+ * second argument channel: `input` stays the semantic basis of
80
+ * validation, permission and execution. */
81
+ readonly rawInput?: string;
73
82
  }
74
83
  export type AssistantBlock = AssistantTextBlock | AssistantToolUseBlock;
75
84
  export interface AssistantMessage {
@@ -10,7 +10,7 @@
10
10
  * carries images. Forcing every caller into the array shape to serve the
11
11
  * 5% that need vision is a tax paid on every line of product code.
12
12
  *
13
- * See ADR-0003 (sum type). The tool contract's record is mauri ADR-0008 —
13
+ * See ADR-0003 (sum type). The tool contract's record is the predecessor's ADR-0008 —
14
14
  * the PREDECESSOR series, cited the same way in kernel/hooks.ts and
15
15
  * kernel/mode.ts: kiso's own numbers 0006-0019 were NEVER assigned
16
16
  * (docs/adrs/README.md), so an unprefixed "ADR-0008" sends a reader to a
@@ -38,6 +38,16 @@ export interface ToolContext {
38
38
  * tool runs with the exact call, even among same-named parallel calls. */
39
39
  readonly callId?: string;
40
40
  readonly executionId?: string;
41
+ /** 0.43.0 (#13): the invocation's arguments exactly as the model
42
+ * streamed them — the lexical companion of the parsed input the
43
+ * handler receives; absent when no delta was streamed. It is NOT a
44
+ * second argument channel: validation, permission and execution read
45
+ * the parsed input. ToolContext is derived from the durable invocation
46
+ * and execution, never from ephemeral process state: a fresh and a
47
+ * recovered execution receive the same invocation context (sessionId,
48
+ * callId, rawInput), and executionId is the id of the durable
49
+ * execution each path writes. */
50
+ readonly rawInput?: string;
41
51
  }
42
52
  /**
43
53
  * round 5(P1-9): a DISCRIMINATED union — `errorKind` is structurally
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vincemakes/kiso-core",
3
- "version": "0.42.2",
3
+ "version": "0.43.0",
4
4
  "description": "kiso 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.42.2",
36
+ "@vincemakes/kiso-evals": "0.43.0",
37
37
  "@types/node": "^26.1.2",
38
38
  "typescript": "^5.7.2",
39
39
  "vitest": "^3.0.0"