@kalphq/core 0.0.0-dev-20260507190243

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,304 @@
1
+ /**
2
+ * Core runtime types for the Kalp v2 Orchestration Reactor.
3
+ *
4
+ * These types define the execution event model, task primitives, and runtime
5
+ * event contracts. They are intentionally infrastructure-agnostic — no
6
+ * Cloudflare, Durable Object, or platform-specific types appear here.
7
+ *
8
+ * @module
9
+ */
10
+
11
+ import type { IRNodeId } from "@kalphq/sdk";
12
+
13
+ // ────────────────────────────────────────────────────────────────────────────
14
+ // Execution Model — identity, hierarchy, and observability
15
+ // ────────────────────────────────────────────────────────────────────────────
16
+
17
+ /**
18
+ * Categories of untracked IO that Kalp can detect (best-effort).
19
+ *
20
+ * - `"network"` — bare `globalThis.fetch` or similar outside `actions.fetch`.
21
+ * - `"timer"` — `setTimeout` / `setInterval` outside `actions.wait`.
22
+ * - `"fs"` — Node `fs` access (not applicable in all runtimes; kept for portability).
23
+ * - `"unknown"` — anything else the heuristic can’t classify.
24
+ */
25
+ export type UntrackedIOSource = "network" | "timer" | "fs" | "unknown";
26
+
27
+ /**
28
+ * Execution identity carried through every handler invocation.
29
+ *
30
+ * Hierarchy:
31
+ * ```
32
+ * thread = actor instance (identified by threadId)
33
+ * +-- trace = one handleEvent() call into the actor
34
+ * +-- execution = one handler invocation (step/tool/lifecycle run)
35
+ * ```
36
+ *
37
+ * `threadId` is opaque in Core. The adapter maps it to infrastructure
38
+ * (e.g. Durable Object id, in-memory key). Core never parses or assumes its format.
39
+ */
40
+ export interface ExecutionContext {
41
+ /** Unique per handler invocation (UUID). Distinguishes retries, loop iterations. */
42
+ executionId: string;
43
+ /** Per `handleEvent()` call. Groups all executions from one external stimulus. */
44
+ traceId: string;
45
+ /** Opaque actor identifier. Adapter maps to infrastructure (CF: DO id, tests: in-memory). */
46
+ threadId: string;
47
+ /** Total count of detected untracked IO operations. */
48
+ untrackedIOCount: number;
49
+ /** Breakdown of untracked IO by source type. */
50
+ untrackedIOByType: Record<UntrackedIOSource, number>;
51
+ /** Whether any plugin has been loaded (observability may be incomplete). */
52
+ hasUntrustedPlugins: boolean;
53
+ }
54
+
55
+ // ────────────────────────────────────────────────────────────────────────────
56
+ // Execution Events — the system's source of truth
57
+ // ────────────────────────────────────────────────────────────────────────────
58
+
59
+ /**
60
+ * Union of all structured events emitted during agent execution.
61
+ *
62
+ * Every runtime operation MUST emit an event. If it doesn't emit an event,
63
+ * it doesn't exist in the system. This is the fundamental invariant.
64
+ *
65
+ * These events enable: replay, debugging, billing, observability, and
66
+ * deterministic re-execution.
67
+ */
68
+ export type ExecutionEvent =
69
+ | {
70
+ type: "node.started";
71
+ nodeId: IRNodeId;
72
+ executionId: string;
73
+ traceId: string;
74
+ threadId: string;
75
+ timestamp: number;
76
+ }
77
+ | {
78
+ type: "node.completed";
79
+ nodeId: IRNodeId;
80
+ result: unknown;
81
+ executionId: string;
82
+ traceId: string;
83
+ threadId: string;
84
+ timestamp: number;
85
+ }
86
+ | {
87
+ type: "primitive.invoked";
88
+ name: string;
89
+ params: unknown;
90
+ result: unknown;
91
+ executionId: string;
92
+ traceId: string;
93
+ threadId: string;
94
+ timestamp: number;
95
+ }
96
+ | {
97
+ type: "action.run";
98
+ target: string;
99
+ input: unknown;
100
+ idempotencyKey?: string;
101
+ executionId: string;
102
+ traceId: string;
103
+ threadId: string;
104
+ timestamp: number;
105
+ }
106
+ | {
107
+ type: "action.run.completed";
108
+ target: string;
109
+ result: unknown;
110
+ idempotencyKey?: string;
111
+ executionId: string;
112
+ traceId: string;
113
+ threadId: string;
114
+ timestamp: number;
115
+ }
116
+ | {
117
+ type: "action.wait";
118
+ duration: string | number;
119
+ executionId: string;
120
+ traceId: string;
121
+ threadId: string;
122
+ timestamp: number;
123
+ }
124
+ | {
125
+ type: "action.loop.start";
126
+ loopId: string;
127
+ executionId: string;
128
+ traceId: string;
129
+ threadId: string;
130
+ timestamp: number;
131
+ }
132
+ | {
133
+ type: "action.loop.iteration";
134
+ loopId: string;
135
+ iteration: number;
136
+ executionId: string;
137
+ traceId: string;
138
+ threadId: string;
139
+ timestamp: number;
140
+ }
141
+ | {
142
+ type: "action.loop.end";
143
+ loopId: string;
144
+ reason: string;
145
+ executionId: string;
146
+ traceId: string;
147
+ threadId: string;
148
+ timestamp: number;
149
+ }
150
+ | {
151
+ type: "action.fetch";
152
+ url: string;
153
+ method: string;
154
+ status: number;
155
+ durationMs: number;
156
+ idempotencyKey?: string;
157
+ executionId: string;
158
+ traceId: string;
159
+ threadId: string;
160
+ timestamp: number;
161
+ }
162
+ | {
163
+ type: "action.emit";
164
+ event: string;
165
+ payload: unknown;
166
+ executionId: string;
167
+ traceId: string;
168
+ threadId: string;
169
+ timestamp: number;
170
+ }
171
+ | {
172
+ type: "action.schedule";
173
+ at: number;
174
+ payload: unknown;
175
+ executionId: string;
176
+ traceId: string;
177
+ threadId: string;
178
+ timestamp: number;
179
+ }
180
+ | {
181
+ type: "state.write";
182
+ key: string;
183
+ value: unknown;
184
+ executionId: string;
185
+ traceId: string;
186
+ threadId: string;
187
+ timestamp: number;
188
+ }
189
+ | {
190
+ type: "state.read";
191
+ key: string;
192
+ value: unknown;
193
+ executionId: string;
194
+ traceId: string;
195
+ threadId: string;
196
+ timestamp: number;
197
+ }
198
+ | {
199
+ type: "action.ask";
200
+ executionId: string;
201
+ traceId: string;
202
+ threadId: string;
203
+ timestamp: number;
204
+ }
205
+ | {
206
+ type: "action.approval";
207
+ executionId: string;
208
+ traceId: string;
209
+ threadId: string;
210
+ timestamp: number;
211
+ }
212
+ | {
213
+ type: "action.call";
214
+ contract: string;
215
+ input: unknown;
216
+ executionId: string;
217
+ traceId: string;
218
+ threadId: string;
219
+ timestamp: number;
220
+ }
221
+ | {
222
+ type: "execution.untracked";
223
+ source: UntrackedIOSource;
224
+ location?: string;
225
+ nodeId?: IRNodeId;
226
+ action?: string;
227
+ executionId: string;
228
+ traceId: string;
229
+ threadId: string;
230
+ timestamp: number;
231
+ }
232
+ | {
233
+ type: "log";
234
+ level: "debug" | "info" | "warn" | "error";
235
+ msg: string;
236
+ data?: Record<string, unknown>;
237
+ executionId: string;
238
+ traceId: string;
239
+ threadId: string;
240
+ timestamp: number;
241
+ }
242
+ | {
243
+ type: "error";
244
+ nodeId?: IRNodeId;
245
+ error: string;
246
+ executionId: string;
247
+ traceId: string;
248
+ threadId: string;
249
+ timestamp: number;
250
+ };
251
+
252
+ // ────────────────────────────────────────────────────────────────────────────
253
+ // Runtime Events — external stimuli entering the reactor
254
+ // ────────────────────────────────────────────────────────────────────────────
255
+
256
+ /**
257
+ * An external event that enters the reactor from the host adapter.
258
+ * Each event maps to an entry in the IR via `type` → `ir.entries[type]`.
259
+ */
260
+ export interface RuntimeEvent {
261
+ /** Event name matching an IR entry key (e.g. "onMessage", "route:GET:/health"). */
262
+ type: string;
263
+ /** Arbitrary payload associated with the event. */
264
+ payload: unknown;
265
+ /** Opaque thread identifier. Adapter sets this from infrastructure (DO id, etc.). */
266
+ threadId?: string;
267
+ }
268
+
269
+ // ────────────────────────────────────────────────────────────────────────────
270
+ // Execution Tasks — internal work items in the reactor queue
271
+ // ────────────────────────────────────────────────────────────────────────────
272
+
273
+ /**
274
+ * A task in the reactor's internal execution queue.
275
+ * Created when a node needs to be processed (entry traversal, handler execution, etc.).
276
+ */
277
+ export interface ExecutionTask {
278
+ /** The IR node to process. */
279
+ nodeId: IRNodeId;
280
+ /** Context data passed to the handler. */
281
+ context: unknown;
282
+ /**
283
+ * Optional resolve callback for action.run intent pattern.
284
+ * When a handler calls `actions.run(step, input)`, the reactor creates a
285
+ * task with a resolve function. The handler's Promise awaits this resolve.
286
+ */
287
+ resolve?: (result: unknown) => void;
288
+ }
289
+
290
+ // ────────────────────────────────────────────────────────────────────────────
291
+ // Handler Module — the shape of a bundled handler
292
+ // ────────────────────────────────────────────────────────────────────────────
293
+
294
+ /**
295
+ * The runtime representation of a bundled handler module.
296
+ *
297
+ * Each handler is an isolated function that receives a {@link HandlerContext}
298
+ * (from `@kalphq/sdk`) and returns a result. The reactor executes these in a
299
+ * sandboxed context with intercepted primitives.
300
+ */
301
+ export interface HandlerModule {
302
+ /** The handler's entry function. */
303
+ default: (context: unknown, input?: unknown) => Promise<unknown>;
304
+ }
package/src/env.d.ts ADDED
@@ -0,0 +1,142 @@
1
+ /**
2
+ * Ambient type declarations for the core package.
3
+ *
4
+ * Core is infrastructure-agnostic. These declarations provide the minimal
5
+ * global types required by the engine without pulling in DOM or CF-specific libs.
6
+ *
7
+ * These types exist in every JS runtime that Kalp targets
8
+ * (Cloudflare Workers, Node 18+, Bun, Deno).
9
+ */
10
+
11
+ /* eslint-disable no-var */
12
+
13
+ // ── Crypto ──────────────────────────────────────────────────────────────────
14
+
15
+ declare var crypto: {
16
+ randomUUID(): `${string}-${string}-${string}-${string}-${string}`;
17
+ };
18
+
19
+ // ── Fetch API (WinterCG-compatible subset) ──────────────────────────────────
20
+
21
+ declare class URL {
22
+ constructor(input: string, base?: string | URL);
23
+ readonly href: string;
24
+ readonly origin: string;
25
+ readonly protocol: string;
26
+ readonly host: string;
27
+ readonly hostname: string;
28
+ readonly port: string;
29
+ readonly pathname: string;
30
+ readonly search: string;
31
+ readonly searchParams: URLSearchParams;
32
+ readonly hash: string;
33
+ toString(): string;
34
+ toJSON(): string;
35
+ static createObjectURL(blob: Blob): string;
36
+ static revokeObjectURL(url: string): void;
37
+ }
38
+
39
+ declare class URLSearchParams implements Iterable<[string, string]> {
40
+ constructor(init?: string | Record<string, string> | [string, string][]);
41
+ append(name: string, value: string): void;
42
+ delete(name: string): void;
43
+ get(name: string): string | null;
44
+ getAll(name: string): string[];
45
+ has(name: string): boolean;
46
+ set(name: string, value: string): void;
47
+ toString(): string;
48
+ entries(): IterableIterator<[string, string]>;
49
+ keys(): IterableIterator<string>;
50
+ values(): IterableIterator<string>;
51
+ [Symbol.iterator](): IterableIterator<[string, string]>;
52
+ }
53
+
54
+ declare class Headers implements Iterable<[string, string]> {
55
+ constructor(init?: HeadersInit);
56
+ append(name: string, value: string): void;
57
+ delete(name: string): void;
58
+ get(name: string): string | null;
59
+ has(name: string): boolean;
60
+ set(name: string, value: string): void;
61
+ entries(): IterableIterator<[string, string]>;
62
+ keys(): IterableIterator<string>;
63
+ values(): IterableIterator<string>;
64
+ forEach(
65
+ callback: (value: string, key: string, parent: Headers) => void,
66
+ ): void;
67
+ [Symbol.iterator](): IterableIterator<[string, string]>;
68
+ }
69
+
70
+ type HeadersInit = Headers | Record<string, string> | [string, string][];
71
+
72
+ interface RequestInit {
73
+ method?: string;
74
+ headers?: HeadersInit;
75
+ body?: BodyInit | null;
76
+ signal?: AbortSignal;
77
+ redirect?: "follow" | "error" | "manual";
78
+ }
79
+
80
+ type BodyInit =
81
+ | string
82
+ | ArrayBuffer
83
+ | Uint8Array
84
+ | ReadableStream
85
+ | FormData
86
+ | URLSearchParams
87
+ | Blob;
88
+
89
+ declare class Request {
90
+ constructor(input: string | URL | Request, init?: RequestInit);
91
+ readonly url: string;
92
+ readonly method: string;
93
+ readonly headers: Headers;
94
+ readonly body: ReadableStream | null;
95
+ readonly bodyUsed: boolean;
96
+ json(): Promise<unknown>;
97
+ text(): Promise<string>;
98
+ arrayBuffer(): Promise<ArrayBuffer>;
99
+ clone(): Request;
100
+ }
101
+
102
+ interface ResponseInit {
103
+ status?: number;
104
+ statusText?: string;
105
+ headers?: HeadersInit;
106
+ }
107
+
108
+ declare class Response {
109
+ constructor(body?: BodyInit | null, init?: ResponseInit);
110
+ readonly ok: boolean;
111
+ readonly status: number;
112
+ readonly statusText: string;
113
+ readonly headers: Headers;
114
+ readonly body: ReadableStream | null;
115
+ readonly bodyUsed: boolean;
116
+ json(): Promise<unknown>;
117
+ text(): Promise<string>;
118
+ arrayBuffer(): Promise<ArrayBuffer>;
119
+ clone(): Response;
120
+ static json(data: unknown, init?: ResponseInit): Response;
121
+ static redirect(url: string, status?: number): Response;
122
+ }
123
+
124
+ declare class Blob {
125
+ constructor(parts?: BlobPart[], options?: BlobPropertyBag);
126
+ readonly size: number;
127
+ readonly type: string;
128
+ text(): Promise<string>;
129
+ arrayBuffer(): Promise<ArrayBuffer>;
130
+ slice(start?: number, end?: number, contentType?: string): Blob;
131
+ }
132
+
133
+ type BlobPart = string | ArrayBuffer | Uint8Array | Blob;
134
+
135
+ interface BlobPropertyBag {
136
+ type?: string;
137
+ }
138
+
139
+ declare function fetch(
140
+ input: string | URL | Request,
141
+ init?: RequestInit,
142
+ ): Promise<Response>;
package/src/factory.ts ADDED
@@ -0,0 +1,69 @@
1
+ /**
2
+ * Engine factory — the ONLY way to create a reactor instance.
3
+ *
4
+ * This file is deliberately separate from the barrel (index.ts) to prevent
5
+ * coupling between type consumers and runtime consumers.
6
+ *
7
+ * Import path: `@kalphq/core/factory`
8
+ *
9
+ * This is NOT a composition root. It does NOT create adapters.
10
+ * Platform packages (cloudflare, node, bun) create adapters in their own
11
+ * wiring files and pass them here.
12
+ *
13
+ * @module
14
+ */
15
+
16
+ import type { IRGraph } from "@kalphq/sdk";
17
+ import { OrchestrationReactor } from "@/engine/reactor";
18
+ import type { HandlerModule } from "@/engine/types";
19
+ import type { PersistenceAdapter, SchedulerAdapter } from "@/adapters/interfaces";
20
+ import type { RuntimeProviders } from "@/engine/context-builder";
21
+
22
+ /**
23
+ * Configuration for creating a new reactor instance.
24
+ *
25
+ * All fields are required. Platform wiring files are responsible for
26
+ * creating the adapter instances and passing them here.
27
+ */
28
+ export interface ReactorConfig {
29
+ /** The compiled IR graph for the agent. */
30
+ ir: IRGraph;
31
+ /** Map from moduleRef to bundled handler module. */
32
+ bundles: Map<string, HandlerModule>;
33
+ /** Composite persistence adapter (state, events, idempotency, threads). */
34
+ persistence: PersistenceAdapter;
35
+ /** Scheduler adapter for deferred wake-ups. */
36
+ scheduler: SchedulerAdapter;
37
+ /** External providers for ai, auth, memory, vault. */
38
+ providers: RuntimeProviders;
39
+ }
40
+
41
+ /**
42
+ * Creates a new {@link OrchestrationReactor} instance.
43
+ *
44
+ * This is the standard entrypoint for all platform adapters.
45
+ * The factory performs no validation — callers are responsible for
46
+ * ensuring the config is complete and correct.
47
+ *
48
+ * @param config - The reactor configuration.
49
+ * @returns A fully initialized reactor ready to handle events.
50
+ *
51
+ * @example
52
+ * ```ts
53
+ * import { createReactor } from "@kalphq/core/factory";
54
+ *
55
+ * const reactor = createReactor({
56
+ * ir, bundles, persistence, scheduler, providers,
57
+ * });
58
+ * await reactor.handleEvent(event);
59
+ * ```
60
+ */
61
+ export function createReactor(config: ReactorConfig): OrchestrationReactor {
62
+ return new OrchestrationReactor(
63
+ config.ir,
64
+ config.bundles,
65
+ config.persistence,
66
+ config.scheduler,
67
+ config.providers,
68
+ );
69
+ }
package/src/index.ts ADDED
@@ -0,0 +1,61 @@
1
+ /**
2
+ * @kalphq/core — Pure orchestration engine (infrastructure-agnostic).
3
+ *
4
+ * This barrel exports the engine, adapter interfaces, and primitives.
5
+ * No implementations, no Cloudflare types, no runtime-specific code.
6
+ *
7
+ * For reactor instantiation, use `@kalphq/core/factory`:
8
+ * ```ts
9
+ * import { createReactor } from "@kalphq/core/factory";
10
+ * ```
11
+ *
12
+ * @module
13
+ */
14
+
15
+ // ────────────────────────────────────────────────────────────────────────────
16
+ // Engine
17
+ // ────────────────────────────────────────────────────────────────────────────
18
+
19
+ export { OrchestrationReactor } from "@/engine/reactor";
20
+ export { ExecutionLog } from "@/engine/execution-log";
21
+ export { buildHandlerContext } from "@/engine/context-builder";
22
+ export type { RuntimeProviders } from "@/engine/context-builder";
23
+
24
+ // ────────────────────────────────────────────────────────────────────────────
25
+ // Types
26
+ // ────────────────────────────────────────────────────────────────────────────
27
+
28
+ export type {
29
+ ExecutionEvent,
30
+ ExecutionContext,
31
+ UntrackedIOSource,
32
+ RuntimeEvent,
33
+ ExecutionTask,
34
+ HandlerModule,
35
+ } from "@/engine/types";
36
+
37
+ // ────────────────────────────────────────────────────────────────────────────
38
+ // Adapter interfaces (contracts only — no implementations)
39
+ // ────────────────────────────────────────────────────────────────────────────
40
+
41
+ export type {
42
+ PersistenceAdapter,
43
+ SchedulerAdapter,
44
+ TransportAdapter,
45
+ CrossThreadAdapter,
46
+ StateStore,
47
+ EventStore,
48
+ IdempotencyStore,
49
+ ThreadStore,
50
+ } from "@/adapters/interfaces";
51
+
52
+ // ────────────────────────────────────────────────────────────────────────────
53
+ // Primitives
54
+ // ────────────────────────────────────────────────────────────────────────────
55
+
56
+ export { createAIPrimitive } from "@/engine/primitives/ai";
57
+ export type { AIProvider } from "@/engine/primitives/ai";
58
+ export { createStoragePrimitive } from "@/engine/primitives/storage";
59
+ export { createActionsPrimitive } from "@/engine/primitives/actions";
60
+ export type { DispatchAction } from "@/engine/primitives/actions";
61
+ export { createHttpPrimitive } from "@/engine/primitives/http";
package/tsconfig.json ADDED
@@ -0,0 +1,23 @@
1
+ {
2
+ "extends": "../typescript-config/base.json",
3
+ "compilerOptions": {
4
+ "target": "ESNext",
5
+ "lib": ["ESNext"],
6
+ "module": "ESNext",
7
+ "moduleResolution": "Bundler",
8
+ "resolveJsonModule": true,
9
+ "allowJs": true,
10
+ "checkJs": false,
11
+ "noEmit": true,
12
+ "isolatedModules": true,
13
+ "allowSyntheticDefaultImports": true,
14
+ "esModuleInterop": true,
15
+ "forceConsistentCasingInFileNames": true,
16
+ "strict": true,
17
+ "skipLibCheck": true,
18
+ "paths": {
19
+ "@/*": ["./src/*"]
20
+ }
21
+ },
22
+ "include": ["src"]
23
+ }
package/tsup.config.ts ADDED
@@ -0,0 +1,9 @@
1
+ import { defineConfig } from "tsup";
2
+
3
+ export default defineConfig({
4
+ entry: ["src/index.ts", "src/factory.ts"],
5
+ format: ["esm"],
6
+ dts: true,
7
+ clean: true,
8
+ sourcemap: true,
9
+ });