@vincemakes/kiso-core 0.1.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.
@@ -0,0 +1,97 @@
1
+ /**
2
+ * L2 — the ReAct loop. The kernel's only loop; everything else is harness.
3
+ *
4
+ * An async generator that yields every event as it happens (never buffers a
5
+ * turn into a list — the agno failure), and converges on exactly one
6
+ * `terminal` event per run (ADR-0004).
7
+ *
8
+ * SINGLE TRUTH (Phase B): the loop holds ONE EventLog. Messages are never
9
+ * stored alongside it — every adapter call derives them via
10
+ * `projectMessages(log.all)` (kernel/project.ts). A fresh log encodes the
11
+ * seed `messages` into events first, so even a one-shot call replays
12
+ * exactly. Compaction is recorded as a `compacted` event and re-applied by
13
+ * the projection, keeping the replay identical to the live run.
14
+ *
15
+ * Per iteration:
16
+ * assemble (onUserMessage / onPreLlm)
17
+ * → adapter.stream(): events yielded straight through, tool calls collected
18
+ * → execute: validation → permission (onPreTool) → handler → rewrite
19
+ * (onPostTool), concurrency-safe calls batched parallel, the rest serial
20
+ * → tool_result events appended
21
+ * no tool calls / maxTurns / abort / max_tokens → terminal event, return
22
+ *
23
+ * Retry lives HERE and only here (ADR-0005): a retryable StructuredError
24
+ * from the adapter is retried with backoff inside the generator frame — and
25
+ * ONLY before anything streamed (Phase B): once a text delta or tool call
26
+ * left the adapter, a failure is an `error` terminal, never a silent
27
+ * re-stream that duplicates output or tool calls.
28
+ */
29
+ import { type Adapter, type AbortSignalLike } from "../protocol/adapter.js";
30
+ import type { Event, StructuredError } from "../protocol/events.js";
31
+ import { EventLog } from "./event-log.js";
32
+ import type { EventInput } from "./event-log.js";
33
+ import type { AssistantBlock, AssistantMessage, Message, ToolResultMessage } from "../protocol/messages.js";
34
+ import { ToolRegistry } from "../tools/registry.js";
35
+ import type { HookHost } from "./hooks.js";
36
+ import type { ModeProfile } from "./mode.js";
37
+ import { type PermissionDecision } from "./permission.js";
38
+ export interface LoopConfig {
39
+ readonly adapter: Adapter;
40
+ readonly model: string;
41
+ readonly systemPrompt?: string;
42
+ readonly registry: ToolRegistry;
43
+ readonly hooks?: HookHost;
44
+ readonly modes?: readonly ModeProfile[];
45
+ /** Active mode name; applies visibleToolNames structurally. */
46
+ readonly mode?: string;
47
+ readonly maxTurns?: number;
48
+ readonly maxRetries?: number;
49
+ /**
50
+ * Seed history. When a `log` is provided, the log IS the truth and this
51
+ * is only used if the log is empty. See ADR-0002 / kernel/project.ts.
52
+ */
53
+ readonly messages?: readonly Message[];
54
+ /** The run's event log. Pass the session's log to make this run durable. */
55
+ readonly log?: EventLog;
56
+ /** Auto-compaction: when the estimated context exceeds the threshold,
57
+ * microcompact old tool results before the next model call. */
58
+ readonly compaction?: {
59
+ readonly thresholdTokens: number;
60
+ };
61
+ readonly signal?: AbortSignalLike;
62
+ readonly temperature?: number;
63
+ readonly maxTokens?: number;
64
+ /**
65
+ * Phase D: the channel that resolves a `defer` permission. When the
66
+ * onPreTool hook defers, the loop persists a `permission_requested`
67
+ * event, yields it, and AWAITS this promise — the same run resumes when
68
+ * a human decides. Absent, a defer degrades to an honest denial.
69
+ */
70
+ readonly resolveApproval?: (decisionId: string) => Promise<PermissionDecision>;
71
+ /**
72
+ * 第四轮(对抗): a verdict the human ALREADY gave before an abort landed.
73
+ * The abort path consults this BEFORE yielding the aborted terminal: a
74
+ * consumed verdict must be recorded (exactly once), never lost — the
75
+ * human's decision outranks the abort.
76
+ */
77
+ readonly approvalVerdict?: (decisionId: string) => boolean | undefined;
78
+ /**
79
+ * C 组: the channel that resolves a failed NON-idempotent execution.
80
+ * The loop persists `uncertain_pending`, yields it, and AWAITS the
81
+ * human verdict — no next model turn, no sibling tool, no auto-retry.
82
+ * Absent, the failure is recorded `abandoned` (never retried).
83
+ */
84
+ readonly resolveUncertainty?: (executionId: string) => Promise<"rerun" | "abandoned">;
85
+ /** 第四轮(对抗): the uncertainty twin of `approvalVerdict`. */
86
+ readonly uncertaintyVerdict?: (executionId: string) => "rerun" | "abandoned" | undefined;
87
+ }
88
+ export declare const DEFAULT_MAX_TURNS = 10;
89
+ export declare const DEFAULT_MAX_RETRIES = 2;
90
+ export declare function loop(config: LoopConfig): AsyncGenerator<Event>;
91
+ /**
92
+ * Adapter exceptions → StructuredError. Anything already shaped like one
93
+ * passes through; everything else is `unknown` — never a regex over error
94
+ * text (ADR-0005).
95
+ */
96
+ export declare function toStructuredError(err: unknown): StructuredError;
97
+ export type { EventInput, AssistantBlock, AssistantMessage, Message, ToolResultMessage };