@kalphq/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,33 @@
1
+ import { config as baseConfig } from "@repo/eslint-config/base";
2
+
3
+ /** @type {import("eslint").Linter.Config[]} */
4
+ export default [
5
+ ...baseConfig,
6
+ {
7
+ rules: {
8
+ "no-restricted-imports": [
9
+ "error",
10
+ {
11
+ patterns: [
12
+ {
13
+ group: ["@kalphq/cloudflare*"],
14
+ message: "Core cannot depend on Cloudflare package.",
15
+ },
16
+ {
17
+ group: ["@kalphq/test-utils*"],
18
+ message: "Core cannot depend on test-utils.",
19
+ },
20
+ {
21
+ group: ["cloudflare:*"],
22
+ message: "Core cannot import CF platform APIs.",
23
+ },
24
+ {
25
+ group: ["@cloudflare/*"],
26
+ message: "Core cannot import CF worker types.",
27
+ },
28
+ ],
29
+ },
30
+ ],
31
+ },
32
+ },
33
+ ];
package/package.json ADDED
@@ -0,0 +1,30 @@
1
+ {
2
+ "name": "@kalphq/core",
3
+ "version": "0.1.0",
4
+ "license": "MIT",
5
+ "author": "Kalp HQ",
6
+ "publishConfig": {
7
+ "access": "public"
8
+ },
9
+ "type": "module",
10
+ "exports": {
11
+ ".": {
12
+ "types": "./dist/index.d.ts",
13
+ "import": "./dist/index.js"
14
+ },
15
+ "./factory": {
16
+ "types": "./dist/factory.d.ts",
17
+ "import": "./dist/factory.js"
18
+ }
19
+ },
20
+ "dependencies": {
21
+ "@kalphq/sdk": "0.3.0"
22
+ },
23
+ "devDependencies": {
24
+ "tsup": "^8.3.5",
25
+ "typescript": "^5.5.2"
26
+ },
27
+ "scripts": {
28
+ "build": "tsup"
29
+ }
30
+ }
@@ -0,0 +1,248 @@
1
+ /**
2
+ * Adapter interfaces for the Kalp v2 Orchestration Reactor.
3
+ *
4
+ * These contracts decouple the runtime core from any specific infrastructure.
5
+ * The reactor never touches storage, scheduling, or transport directly —
6
+ * it always goes through these interfaces.
7
+ *
8
+ * Implementations:
9
+ * - Cloudflare DO adapter (DurableObjectStorage, Alarms, fetch)
10
+ * - InMemory adapter (Maps, timeouts) — local dev / tests
11
+ *
12
+ * @module
13
+ */
14
+
15
+ import type { ExecutionEvent } from "../engine/types";
16
+
17
+ // ────────────────────────────────────────────────────────────────────────────
18
+ // Sub-stores (decomposed persistence)
19
+ // ────────────────────────────────────────────────────────────────────────────
20
+
21
+ /**
22
+ * KV-like state store for agent runtime data.
23
+ *
24
+ * Supports get/set/delete and atomic transactions. Co-located with the
25
+ * actor — adapters decide backing store (DO storage, Redis, in-memory).
26
+ */
27
+ export interface StateStore {
28
+ /**
29
+ * Reads a value from state.
30
+ *
31
+ * @param key - The state key.
32
+ * @returns The stored value, or `null` if not found.
33
+ */
34
+ get(key: string): Promise<unknown>;
35
+
36
+ /**
37
+ * Writes a value to state.
38
+ *
39
+ * @param key - The state key.
40
+ * @param value - The value to store (must be JSON-serializable).
41
+ */
42
+ set(key: string, value: unknown): Promise<void>;
43
+
44
+ /**
45
+ * Deletes a key from state.
46
+ *
47
+ * @param key - The state key to delete.
48
+ */
49
+ delete(key: string): Promise<void>;
50
+
51
+ /**
52
+ * Atomically increments a numeric value.
53
+ *
54
+ * @param key - The key to increment.
55
+ * @param amount - The amount to add.
56
+ * @returns The new value.
57
+ */
58
+ increment(key: string, amount: number): Promise<number>;
59
+
60
+ /**
61
+ * Executes a function within an atomic transaction.
62
+ *
63
+ * @param fn - The transactional function receiving a scoped store.
64
+ * @returns The transaction's return value.
65
+ */
66
+ transaction<T>(fn: (tx: StateStore) => Promise<T>): Promise<T>;
67
+ }
68
+
69
+ /**
70
+ * Append-only execution event log.
71
+ *
72
+ * All structured events flow through this store. It is the system's source
73
+ * of truth for replay, debugging, and observability.
74
+ */
75
+ export interface EventStore {
76
+ /**
77
+ * Appends a structured event to the execution log.
78
+ * Events are append-only and ordered by insertion time.
79
+ *
80
+ * @param event - The execution event to persist.
81
+ */
82
+ append(event: ExecutionEvent): Promise<void>;
83
+
84
+ /**
85
+ * Loads all events from the execution log, ordered by insertion.
86
+ * Used for replay, debugging, and rehydration.
87
+ *
88
+ * @returns An ordered array of all persisted execution events.
89
+ */
90
+ loadAll(): Promise<ExecutionEvent[]>;
91
+
92
+ /**
93
+ * Loads events filtered by thread ID.
94
+ *
95
+ * @param threadId - The thread to filter by.
96
+ * @returns Events belonging to the specified thread.
97
+ */
98
+ loadByThread(threadId: string): Promise<ExecutionEvent[]>;
99
+
100
+ /**
101
+ * Loads events filtered by trace ID.
102
+ *
103
+ * @param traceId - The trace to filter by.
104
+ * @returns Events belonging to the specified trace.
105
+ */
106
+ loadByTrace(traceId: string): Promise<ExecutionEvent[]>;
107
+ }
108
+
109
+ /**
110
+ * KV store with TTL for idempotency deduplication.
111
+ *
112
+ * Actor-scoped (not global). Stored via the adapter's backing store.
113
+ * Zero race conditions since the actor is single-threaded.
114
+ */
115
+ export interface IdempotencyStore {
116
+ /**
117
+ * Retrieves a cached result by idempotency key.
118
+ *
119
+ * @param key - The composite idempotency key.
120
+ * @returns The cached result, or `null` if not found or expired.
121
+ */
122
+ get(key: string): Promise<unknown | null>;
123
+
124
+ /**
125
+ * Stores a result with an optional TTL.
126
+ *
127
+ * @param key - The composite idempotency key.
128
+ * @param result - The result to cache.
129
+ * @param opts - Optional settings (TTL in milliseconds).
130
+ */
131
+ set(key: string, result: unknown, opts?: { ttlMs?: number }): Promise<void>;
132
+ }
133
+
134
+ /**
135
+ * Thread metadata store.
136
+ *
137
+ * Stores and retrieves metadata associated with a thread (actor instance).
138
+ */
139
+ export interface ThreadStore {
140
+ /**
141
+ * Retrieves metadata for a thread.
142
+ *
143
+ * @param threadId - The thread identifier.
144
+ * @returns The thread metadata, or `null` if not set.
145
+ */
146
+ getMeta(threadId: string): Promise<Record<string, unknown> | null>;
147
+
148
+ /**
149
+ * Stores metadata for a thread.
150
+ *
151
+ * @param threadId - The thread identifier.
152
+ * @param meta - The metadata to store.
153
+ */
154
+ setMeta(threadId: string, meta: Record<string, unknown>): Promise<void>;
155
+ }
156
+
157
+ // ────────────────────────────────────────────────────────────────────────────
158
+ // Persistence (composite adapter)
159
+ // ────────────────────────────────────────────────────────────────────────────
160
+
161
+ /**
162
+ * Composite persistence adapter decomposed into specialized sub-stores.
163
+ *
164
+ * This prevents a single monolithic adapter from hiding 4 different
165
+ * storage patterns behind one interface. Each sub-store has clear semantics.
166
+ */
167
+ export interface PersistenceAdapter {
168
+ /** KV-like state store (get/set/delete/transaction). */
169
+ state: StateStore;
170
+ /** Append-only execution event log. */
171
+ events: EventStore;
172
+ /** KV with TTL for idempotency deduplication. */
173
+ idempotency: IdempotencyStore;
174
+ /** Thread metadata store. */
175
+ threads: ThreadStore;
176
+ }
177
+
178
+ // ────────────────────────────────────────────────────────────────────────────
179
+ // Scheduling
180
+ // ────────────────────────────────────────────────────────────────────────────
181
+
182
+ /**
183
+ * Scheduler adapter for deferred execution (alarms, timers).
184
+ *
185
+ * Used by `actions.wait`, `actions.loop`, and `actions.schedule` for future
186
+ * wake-ups. The adapter is responsible for persisting the alarm and re-entering
187
+ * the reactor when it fires.
188
+ *
189
+ * Adapters may have limitations (e.g. CF: 1 alarm per DO). Core handles
190
+ * multi-schedule queuing in {@link StateStore}; adapter fires one at a time.
191
+ *
192
+ * **Best-effort timing** — scheduled events are approximate, not guaranteed
193
+ * to fire at the exact requested time.
194
+ */
195
+ export interface SchedulerAdapter {
196
+ /**
197
+ * Schedules a wake-up at the given timestamp (ms since epoch).
198
+ * If an alarm already exists, it should be replaced.
199
+ *
200
+ * @param at - Unix timestamp in milliseconds for the wake-up.
201
+ */
202
+ schedule(at: number): Promise<void>;
203
+
204
+ /**
205
+ * Cancels any pending scheduled wake-up.
206
+ */
207
+ cancel(): Promise<void>;
208
+ }
209
+
210
+ // ────────────────────────────────────────────────────────────────────────────
211
+ // Transport
212
+ // ────────────────────────────────────────────────────────────────────────────
213
+
214
+ /**
215
+ * Transport adapter for sending responses back to the caller.
216
+ *
217
+ * Used when the reactor needs to push data to a connected client
218
+ * (e.g. WebSocket message, streaming response).
219
+ */
220
+ export interface TransportAdapter {
221
+ /**
222
+ * Sends a response payload to the connected client.
223
+ *
224
+ * @param response - The data to send (will be JSON-serialized).
225
+ */
226
+ send(response: unknown): Promise<void>;
227
+ }
228
+
229
+ // ────────────────────────────────────────────────────────────────────────────
230
+ // Cross-thread messaging
231
+ // ────────────────────────────────────────────────────────────────────────────
232
+
233
+ /**
234
+ * Adapter for cross-thread (cross-actor) messaging.
235
+ *
236
+ * Core never uses HTTP semantics directly. The adapter maps to the
237
+ * appropriate transport (CF: fetch to DO, Node: IPC, tests: direct call).
238
+ */
239
+ export interface CrossThreadAdapter {
240
+ /**
241
+ * Sends an event to another thread (actor).
242
+ *
243
+ * @param targetThreadId - The target thread's opaque identifier.
244
+ * @param event - The event name.
245
+ * @param payload - The event payload.
246
+ */
247
+ send(targetThreadId: string, event: string, payload: unknown): Promise<void>;
248
+ }
@@ -0,0 +1,151 @@
1
+ /**
2
+ * Handler context builder for the Kalp v2 Orchestration Reactor.
3
+ *
4
+ * Assembles the {@link HandlerContext} that is passed to every handler function.
5
+ * Each primitive in the context is intercepted — all operations emit structured
6
+ * events to the execution log.
7
+ *
8
+ * The context shape matches the SDK's `HandlerContext` interface exactly,
9
+ * preserving DX:
10
+ *
11
+ * ```ts
12
+ * const { fetch, loop, run, wait } = actions;
13
+ * const { delete: deleteKey, get, put } = storage;
14
+ * const { classify, generate, stream } = ai;
15
+ * ```
16
+ *
17
+ * @module
18
+ */
19
+
20
+ import type {
21
+ HandlerContext,
22
+ KalpAuth,
23
+ KalpMemory,
24
+ KalpVault,
25
+ } from "@kalphq/sdk";
26
+ import type { StateStore, SchedulerAdapter } from "@/adapters/interfaces";
27
+ import type { ExecutionLog } from "@/engine/execution-log";
28
+ import type { AIProvider } from "@/engine/primitives/ai";
29
+ import type { DispatchAction } from "@/engine/primitives/actions";
30
+ import { createAIPrimitive } from "@/engine/primitives/ai";
31
+ import { createStoragePrimitive } from "@/engine/primitives/storage";
32
+ import { createActionsPrimitive } from "@/engine/primitives/actions";
33
+ import { createMcpPrimitive } from "@/engine/primitives/mcp";
34
+ import { createDatePrimitive } from "@/engine/primitives/date";
35
+ import { createMathPrimitive } from "@/engine/primitives/math";
36
+ import { createAgentMetaPrimitive } from "@/engine/primitives/agent-meta";
37
+ import type { ExecutionContext } from "@/engine/types";
38
+
39
+ // ────────────────────────────────────────────────────────────────────────────
40
+ // External providers injected by the host adapter
41
+ // ────────────────────────────────────────────────────────────────────────────
42
+
43
+ /**
44
+ * External provider implementations injected by the host adapter.
45
+ * These are NOT part of the reactor core — they come from infrastructure.
46
+ */
47
+ export interface RuntimeProviders {
48
+ /** AI provider implementation (e.g. OpenAI, Anthropic, Cloudflare AI). */
49
+ ai: AIProvider;
50
+ /** Authentication context for the current request. */
51
+ auth: KalpAuth;
52
+ /** Memory (conversation history) provider. */
53
+ memory: KalpMemory;
54
+ /** Secrets vault provider. */
55
+ vault: KalpVault;
56
+ }
57
+
58
+ // ────────────────────────────────────────────────────────────────────────────
59
+ // Context builder
60
+ // ────────────────────────────────────────────────────────────────────────────
61
+
62
+ /**
63
+ * Builds a {@link HandlerContext} with fully intercepted primitives.
64
+ *
65
+ * Every operation on the returned context (ai.generate, storage.put,
66
+ * actions.run, etc.) is intercepted to emit structured events to the
67
+ * execution log. The handler never touches infrastructure directly.
68
+ *
69
+ * @param log - The execution log for event emission.
70
+ * @param stateStore - The state sub-store for KV operations.
71
+ * @param scheduler - The scheduler adapter for deferred execution.
72
+ * @param dispatch - Callback to enqueue handler tasks in the reactor.
73
+ * @param providers - External providers (ai, auth, memory, vault).
74
+ * @param execCtx - Optional execution context for event identity.
75
+ * @param agentConfig - Optional agent configuration for metadata.
76
+ * @returns A complete {@link HandlerContext} matching the SDK interface.
77
+ */
78
+ export function buildHandlerContext(
79
+ log: ExecutionLog,
80
+ stateStore: StateStore,
81
+ scheduler: SchedulerAdapter,
82
+ dispatch: DispatchAction,
83
+ providers: RuntimeProviders,
84
+ execCtx?: ExecutionContext,
85
+ agentConfig?: {
86
+ name: string;
87
+ systemPrompt: string;
88
+ metadata?: Record<string, unknown>;
89
+ },
90
+ ): HandlerContext {
91
+ const ids = {
92
+ executionId: execCtx?.executionId ?? "",
93
+ traceId: execCtx?.traceId ?? "",
94
+ threadId: execCtx?.threadId ?? "",
95
+ };
96
+
97
+ return {
98
+ ai: createAIPrimitive(providers.ai, log),
99
+ storage: createStoragePrimitive(stateStore, log, execCtx),
100
+ actions: createActionsPrimitive(log, scheduler, dispatch, execCtx),
101
+ auth: providers.auth,
102
+ memory: providers.memory,
103
+ vault: providers.vault,
104
+ mcp: createMcpPrimitive(log, execCtx),
105
+ agent: createAgentMetaPrimitive(log, execCtx, agentConfig),
106
+ date: createDatePrimitive(log, execCtx),
107
+ math: createMathPrimitive(log, execCtx),
108
+ log: {
109
+ debug: (msg, data) =>
110
+ void log.emit({
111
+ type: "log",
112
+ level: "debug",
113
+ msg,
114
+ data,
115
+ ...ids,
116
+ timestamp: Date.now(),
117
+ }),
118
+ info: (msg, data) =>
119
+ void log.emit({
120
+ type: "log",
121
+ level: "info",
122
+ msg,
123
+ data,
124
+ ...ids,
125
+ timestamp: Date.now(),
126
+ }),
127
+ warn: (msg, data) =>
128
+ void log.emit({
129
+ type: "log",
130
+ level: "warn",
131
+ msg,
132
+ data,
133
+ ...ids,
134
+ timestamp: Date.now(),
135
+ }),
136
+ error: (err, data) => {
137
+ const msg = err instanceof Error ? err.message : String(err);
138
+ const errorData =
139
+ err instanceof Error ? { ...data, stack: err.stack } : data;
140
+ void log.emit({
141
+ type: "log",
142
+ level: "error",
143
+ msg,
144
+ data: errorData,
145
+ ...ids,
146
+ timestamp: Date.now(),
147
+ });
148
+ },
149
+ },
150
+ };
151
+ }
@@ -0,0 +1,73 @@
1
+ /**
2
+ * Structured execution log for the Kalp v2 Orchestration Reactor.
3
+ *
4
+ * The execution log is the system's source of truth. Every runtime operation
5
+ * emits a structured event that is persisted via the {@link PersistenceAdapter}.
6
+ * This enables replay, time-travel debugging, billing, and observability.
7
+ *
8
+ * Rule: **If it doesn't emit an event, it doesn't exist.**
9
+ *
10
+ * @module
11
+ */
12
+
13
+ import type { EventStore } from "@/adapters/interfaces";
14
+ import type { ExecutionEvent } from "@/engine/types";
15
+
16
+ /**
17
+ * Append-only structured event log backed by an {@link EventStore}.
18
+ *
19
+ * All reactor operations flow through this class. It provides a unified
20
+ * interface for emitting and loading events regardless of the underlying
21
+ * storage mechanism.
22
+ */
23
+ export class ExecutionLog {
24
+ /** In-memory buffer of events emitted during the current processing cycle. */
25
+ private buffer: ExecutionEvent[] = [];
26
+
27
+ /**
28
+ * Creates a new execution log backed by the given event store.
29
+ *
30
+ * @param eventStore - The store used to persist and load events.
31
+ */
32
+ constructor(private eventStore: EventStore) {}
33
+
34
+ /**
35
+ * Emits a structured execution event.
36
+ *
37
+ * The event is immediately persisted via the adapter and buffered in memory
38
+ * for the current processing cycle.
39
+ *
40
+ * @param event - The execution event to emit.
41
+ */
42
+ async emit(event: ExecutionEvent): Promise<void> {
43
+ this.buffer.push(event);
44
+ await this.eventStore.append(event);
45
+ }
46
+
47
+ /**
48
+ * Loads all previously persisted events from the adapter.
49
+ *
50
+ * Used for replay, rehydration, and debugging.
51
+ *
52
+ * @returns An ordered array of all persisted execution events.
53
+ */
54
+ async load(): Promise<ExecutionEvent[]> {
55
+ return this.eventStore.loadAll();
56
+ }
57
+
58
+ /**
59
+ * Returns events emitted during the current processing cycle (in-memory only).
60
+ *
61
+ * @returns The in-memory event buffer.
62
+ */
63
+ getBuffer(): ReadonlyArray<ExecutionEvent> {
64
+ return this.buffer;
65
+ }
66
+
67
+ /**
68
+ * Clears the in-memory buffer. Called between processing cycles.
69
+ */
70
+ clearBuffer(): void {
71
+ this.buffer = [];
72
+ }
73
+ }