@ailu-ai/graph-sdk 2.0.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,4260 @@
1
+ import { ModelLike } from '@ailu-ai/model-core';
2
+ export { ChatMessage, DEFAULT_KEY_ENV, InvokeOptions, MissingProviderKeyError, Model, ModelLike, ModelOptions, ModelResponse, ModelSpec, ModelUsage, NoProviderInEnvError, OpenAICompatibleModel, OutputSchema, ProviderEntry, ProviderSlug, ResolvedKeys, SpecModel, TypedModel, UnknownProviderError, assertKnownProvider, model, models, openaiCompatible, parseModelString, resolveProviderKeys, toModelSpec } from '@ailu-ai/model-core';
3
+ import { z } from 'zod';
4
+ import { KeyObject } from 'node:crypto';
5
+
6
+ type MessageId = Brand<string, "MessageId">;
7
+ type ToolCall = {
8
+ id: string;
9
+ name: string;
10
+ input: unknown;
11
+ };
12
+ type BaseMessage = {
13
+ id: MessageId;
14
+ createdAt: Date;
15
+ metadata?: Record<string, unknown>;
16
+ };
17
+ type HumanMessage = BaseMessage & {
18
+ role: "human";
19
+ content: string;
20
+ };
21
+ type AIMessage = BaseMessage & {
22
+ role: "ai";
23
+ content: string;
24
+ toolCalls?: ToolCall[];
25
+ };
26
+ type ToolMessage = BaseMessage & {
27
+ role: "tool";
28
+ toolCallId: string;
29
+ content: string;
30
+ };
31
+ type SystemMessage = BaseMessage & {
32
+ role: "system";
33
+ content: string;
34
+ };
35
+ type Message = HumanMessage | AIMessage | ToolMessage | SystemMessage;
36
+
37
+ type Brand<T, TBrand extends string> = T & {
38
+ readonly __brand: TBrand;
39
+ };
40
+ type NodeId = Brand<string, "NodeId">;
41
+ type EdgeId = Brand<string, "EdgeId">;
42
+ type GraphId = Brand<string, "GraphId">;
43
+ type RunId = Brand<string, "RunId">;
44
+ type ChannelReducer = "replace" | "append" | "merge";
45
+ type ChannelDefinition<T> = {
46
+ type: string;
47
+ reducer: ChannelReducer;
48
+ default?: T;
49
+ /** ADR 0032: never emit this channel's value in run events/logs (masked; still checkpointed). */
50
+ noLog?: boolean;
51
+ };
52
+ type ChannelsSchema = Record<string, ChannelDefinition<unknown>>;
53
+ type ResolvedChannels<TChannels extends ChannelsSchema> = {
54
+ [K in keyof TChannels]: TChannels[K] extends ChannelDefinition<infer TValue> ? TValue : never;
55
+ };
56
+ declare const NODE_TYPES: readonly ["action", "agent", "tool", "human-gate", "subgraph"];
57
+ type NodeType = (typeof NODE_TYPES)[number];
58
+ /** ADR 0076 (product repo) — `"error"` routes from a node to a designated handler once its
59
+ * `retryPolicy` is exhausted, instead of failing the whole run. Absent (the common case): failure
60
+ * behavior is unchanged — the run terminates exactly as before this type existed. */
61
+ declare const EDGE_TYPES: readonly ["default", "conditional", "error"];
62
+ type EdgeType = (typeof EDGE_TYPES)[number];
63
+ /** ADR 0076 — coarse classification of why a node's handler threw, attached to `node_failed`/
64
+ * `node_error_routed` events so a governed error path can react differently to a transient
65
+ * failure (worth a human-visible retry) vs a permanent one (worth surfacing, not retrying).
66
+ * Derived by `graph-runtime` from a thrown error's own `failureCategory` property when present
67
+ * (a convention, not an interface — any thrower, e.g. `llm-gateway`, can set it); defaults to
68
+ * `"unknown"` for a plain `Error`/thrown value that doesn't declare one. */
69
+ declare const FAILURE_CATEGORIES: readonly ["transient", "permanent", "unknown"];
70
+ type FailureCategory = (typeof FAILURE_CATEGORIES)[number];
71
+ /** `cancelled` (ADR 0044): terminal, reached when the embedder's cancellation seam returned
72
+ * true at a node BOUNDARY — the run stopped before scheduling its next node, with its last
73
+ * checkpoint durable and authoritative. Distinct from `failed` (no error occurred) and from
74
+ * `suspended` (it is not waiting on a decision). A cancelled run stays replayable. */
75
+ declare const GRAPH_STATUSES: readonly ["idle", "running", "suspended", "completed", "failed", "cancelled"];
76
+ type GraphStatus = (typeof GRAPH_STATUSES)[number];
77
+ type RetryPolicy = {
78
+ maxAttempts: number;
79
+ backoffMs: number;
80
+ };
81
+ type Command<TChannels extends ChannelsSchema = ChannelsSchema> = {
82
+ goto: NodeId | NodeId[];
83
+ update?: Partial<ResolvedChannels<TChannels>>;
84
+ };
85
+ type NodeDefinition = {
86
+ id: NodeId;
87
+ type: NodeType;
88
+ label: string;
89
+ subgraphId?: GraphId;
90
+ inputMapping?: Record<string, string>;
91
+ outputMapping?: Record<string, string>;
92
+ fanOut?: {
93
+ parallelTo: NodeId[];
94
+ joinAt: NodeId;
95
+ };
96
+ /** ADR 0042 D2/D3 (product ADR 0068 — child workflows): dynamic N-child subgraph fan-out.
97
+ * Present on a `type: "subgraph"` node ALONGSIDE its `subgraphId` (never duplicated here) —
98
+ * `overChannel` is read as a JSON array at execution time, one child spawn per item, run
99
+ * concurrently; results land in `joinAt` as a JSON array, input order preserved. Absent (the
100
+ * common case): the node runs its subgraph exactly once, unchanged. */
101
+ mapSubgraph?: {
102
+ overChannel: string;
103
+ joinAt: string;
104
+ };
105
+ retryPolicy?: RetryPolicy;
106
+ metadata?: Record<string, unknown>;
107
+ };
108
+ type EdgeDefinition = {
109
+ id: EdgeId;
110
+ from: NodeId;
111
+ to: NodeId;
112
+ type: EdgeType;
113
+ condition?: string;
114
+ };
115
+ type GraphState<TChannels extends ChannelsSchema = ChannelsSchema> = {
116
+ runId: RunId;
117
+ graphId: GraphId;
118
+ currentNodeId: NodeId;
119
+ status: GraphStatus;
120
+ channels: ResolvedChannels<TChannels>;
121
+ version: number;
122
+ checkpointId?: string;
123
+ createdAt: string;
124
+ updatedAt: string;
125
+ };
126
+ type GraphDefinition<TChannels extends ChannelsSchema = ChannelsSchema> = {
127
+ id: GraphId;
128
+ version: string;
129
+ name: string;
130
+ recursionLimit?: number;
131
+ channels: TChannels;
132
+ nodes: NodeDefinition[];
133
+ edges: EdgeDefinition[];
134
+ entryNodeId: NodeId;
135
+ metadata?: Record<string, unknown>;
136
+ };
137
+
138
+ declare const GraphStateSchema: z.ZodObject<{
139
+ runId: z.ZodBranded<z.ZodString, "RunId">;
140
+ graphId: z.ZodBranded<z.ZodString, "GraphId">;
141
+ currentNodeId: z.ZodBranded<z.ZodString, "NodeId">;
142
+ status: z.ZodEnum<["idle", "running", "suspended", "completed", "failed", "cancelled"]>;
143
+ channels: z.ZodRecord<z.ZodString, z.ZodUnknown>;
144
+ version: z.ZodNumber;
145
+ checkpointId: z.ZodOptional<z.ZodString>;
146
+ createdAt: z.ZodString;
147
+ updatedAt: z.ZodString;
148
+ }, "strip", z.ZodTypeAny, {
149
+ status: "idle" | "running" | "suspended" | "completed" | "failed" | "cancelled";
150
+ runId: string & z.BRAND<"RunId">;
151
+ graphId: string & z.BRAND<"GraphId">;
152
+ currentNodeId: string & z.BRAND<"NodeId">;
153
+ channels: Record<string, unknown>;
154
+ version: number;
155
+ createdAt: string;
156
+ updatedAt: string;
157
+ checkpointId?: string | undefined;
158
+ }, {
159
+ status: "idle" | "running" | "suspended" | "completed" | "failed" | "cancelled";
160
+ runId: string;
161
+ graphId: string;
162
+ currentNodeId: string;
163
+ channels: Record<string, unknown>;
164
+ version: number;
165
+ createdAt: string;
166
+ updatedAt: string;
167
+ checkpointId?: string | undefined;
168
+ }>;
169
+
170
+ declare const GRAPH_VALIDATION_ERROR_CODES: {
171
+ readonly DUPLICATE_NODE_ID: "DUPLICATE_NODE_ID";
172
+ readonly DUPLICATE_EDGE_ID: "DUPLICATE_EDGE_ID";
173
+ readonly MISSING_ENTRY_NODE: "MISSING_ENTRY_NODE";
174
+ readonly INVALID_EDGE_REFERENCE: "INVALID_EDGE_REFERENCE";
175
+ readonly CYCLE_DETECTED: "CYCLE_DETECTED";
176
+ readonly INVALID_CONDITION_FORMAT: "INVALID_CONDITION_FORMAT";
177
+ readonly MULTIPLE_ERROR_EDGES: "MULTIPLE_ERROR_EDGES";
178
+ };
179
+ type GraphValidationErrorCode = (typeof GRAPH_VALIDATION_ERROR_CODES)[keyof typeof GRAPH_VALIDATION_ERROR_CODES];
180
+ type GraphValidationPath = (string | number)[];
181
+ declare class GraphValidationError extends Error {
182
+ readonly code: GraphValidationErrorCode;
183
+ readonly path: GraphValidationPath;
184
+ constructor(code: GraphValidationErrorCode, message: string, path?: GraphValidationPath);
185
+ }
186
+
187
+ declare const validateGraph: (def: GraphDefinition) => GraphValidationError[];
188
+
189
+ type ArtifactId = string & {
190
+ readonly __brand: "ArtifactId";
191
+ };
192
+ type ArtifactVersion = number;
193
+ declare const ARTIFACT_MEDIA_TYPES: readonly ["application/json", "text/plain", "text/markdown", "application/octet-stream"];
194
+ type ArtifactMediaType = (typeof ARTIFACT_MEDIA_TYPES)[number];
195
+ type Artifact = {
196
+ id: ArtifactId;
197
+ runId: RunId;
198
+ nodeId: NodeId;
199
+ name: string;
200
+ mediaType: ArtifactMediaType;
201
+ version: ArtifactVersion;
202
+ content: unknown;
203
+ createdAt: Date;
204
+ metadata?: Record<string, unknown>;
205
+ };
206
+ type ArtifactRef = {
207
+ id: ArtifactId;
208
+ version: ArtifactVersion;
209
+ };
210
+
211
+ interface ArtifactStore {
212
+ write(artifact: Omit<Artifact, "id" | "version" | "createdAt">): Promise<Artifact>;
213
+ read(id: ArtifactId): Promise<Artifact | undefined>;
214
+ readVersion(id: ArtifactId, version: ArtifactVersion): Promise<Artifact | undefined>;
215
+ listByRun(runId: RunId): Promise<Artifact[]>;
216
+ listVersions(id: ArtifactId): Promise<Artifact[]>;
217
+ }
218
+
219
+ type ArtifactWriteInput = Omit<Artifact, "id" | "version" | "createdAt">;
220
+ declare class InMemoryArtifactStore implements ArtifactStore {
221
+ private readonly artifactsById;
222
+ private readonly latestIdByRunAndName;
223
+ write(artifact: ArtifactWriteInput): Promise<Artifact>;
224
+ read(id: ArtifactId): Promise<Artifact | undefined>;
225
+ readVersion(id: ArtifactId, version: ArtifactVersion): Promise<Artifact | undefined>;
226
+ listByRun(runId: RunId): Promise<Artifact[]>;
227
+ listVersions(id: ArtifactId): Promise<Artifact[]>;
228
+ }
229
+
230
+ type MemoryNamespace = string[];
231
+ type MemoryKey = string;
232
+ type MemoryItem = {
233
+ namespace: MemoryNamespace;
234
+ key: MemoryKey;
235
+ value: unknown;
236
+ createdAt: string;
237
+ updatedAt: string;
238
+ embedding?: number[];
239
+ };
240
+
241
+ interface BaseStore {
242
+ get(namespace: MemoryNamespace, key: MemoryKey): Promise<MemoryItem | undefined>;
243
+ put(namespace: MemoryNamespace, key: MemoryKey, value: unknown): Promise<MemoryItem>;
244
+ delete(namespace: MemoryNamespace, key: MemoryKey): Promise<void>;
245
+ search(namespace: MemoryNamespace, query: string, topK: number): Promise<MemoryItem[]>;
246
+ list(namespace: MemoryNamespace, prefix?: string): Promise<MemoryItem[]>;
247
+ }
248
+
249
+ type BaseCallbackEvent = {
250
+ runId: string;
251
+ nodeId?: string;
252
+ tags?: string[];
253
+ metadata?: Record<string, unknown>;
254
+ timestamp: string;
255
+ };
256
+ type CallbackEvent = (BaseCallbackEvent & {
257
+ type: "onLLMStart";
258
+ input: unknown;
259
+ }) | (BaseCallbackEvent & {
260
+ type: "onLLMToken";
261
+ token: string;
262
+ }) | (BaseCallbackEvent & {
263
+ type: "onLLMEnd";
264
+ output: unknown;
265
+ }) | (BaseCallbackEvent & {
266
+ type: "onLLMError";
267
+ error: string;
268
+ }) | (BaseCallbackEvent & {
269
+ type: "onToolStart";
270
+ tool: string;
271
+ input: unknown;
272
+ }) | (BaseCallbackEvent & {
273
+ type: "onToolEnd";
274
+ tool: string;
275
+ output: unknown;
276
+ }) | (BaseCallbackEvent & {
277
+ type: "onToolError";
278
+ tool: string;
279
+ error: string;
280
+ }) | (BaseCallbackEvent & {
281
+ type: "onNodeStart";
282
+ input: unknown;
283
+ }) | (BaseCallbackEvent & {
284
+ type: "onNodeEnd";
285
+ output: unknown;
286
+ }) | (BaseCallbackEvent & {
287
+ type: "onNodeError";
288
+ error: string;
289
+ }) | (BaseCallbackEvent & {
290
+ type: "onChainStart";
291
+ input: unknown;
292
+ }) | (BaseCallbackEvent & {
293
+ type: "onChainEnd";
294
+ output: unknown;
295
+ }) | (BaseCallbackEvent & {
296
+ type: "onChainError";
297
+ error: string;
298
+ }) | (BaseCallbackEvent & {
299
+ type: "onAgentAction";
300
+ action: string;
301
+ payload?: unknown;
302
+ }) | (BaseCallbackEvent & {
303
+ type: "onAgentFinish";
304
+ result: unknown;
305
+ });
306
+
307
+ interface CallbackHandler {
308
+ onLLMStart?(event: Extract<CallbackEvent, {
309
+ type: "onLLMStart";
310
+ }>): void | Promise<void>;
311
+ onLLMToken?(event: Extract<CallbackEvent, {
312
+ type: "onLLMToken";
313
+ }>): void | Promise<void>;
314
+ onLLMEnd?(event: Extract<CallbackEvent, {
315
+ type: "onLLMEnd";
316
+ }>): void | Promise<void>;
317
+ onLLMError?(event: Extract<CallbackEvent, {
318
+ type: "onLLMError";
319
+ }>): void | Promise<void>;
320
+ onToolStart?(event: Extract<CallbackEvent, {
321
+ type: "onToolStart";
322
+ }>): void | Promise<void>;
323
+ onToolEnd?(event: Extract<CallbackEvent, {
324
+ type: "onToolEnd";
325
+ }>): void | Promise<void>;
326
+ onToolError?(event: Extract<CallbackEvent, {
327
+ type: "onToolError";
328
+ }>): void | Promise<void>;
329
+ onNodeStart?(event: Extract<CallbackEvent, {
330
+ type: "onNodeStart";
331
+ }>): void | Promise<void>;
332
+ onNodeEnd?(event: Extract<CallbackEvent, {
333
+ type: "onNodeEnd";
334
+ }>): void | Promise<void>;
335
+ onNodeError?(event: Extract<CallbackEvent, {
336
+ type: "onNodeError";
337
+ }>): void | Promise<void>;
338
+ onChainStart?(event: Extract<CallbackEvent, {
339
+ type: "onChainStart";
340
+ }>): void | Promise<void>;
341
+ onChainEnd?(event: Extract<CallbackEvent, {
342
+ type: "onChainEnd";
343
+ }>): void | Promise<void>;
344
+ onChainError?(event: Extract<CallbackEvent, {
345
+ type: "onChainError";
346
+ }>): void | Promise<void>;
347
+ onAgentAction?(event: Extract<CallbackEvent, {
348
+ type: "onAgentAction";
349
+ }>): void | Promise<void>;
350
+ onAgentFinish?(event: Extract<CallbackEvent, {
351
+ type: "onAgentFinish";
352
+ }>): void | Promise<void>;
353
+ }
354
+ interface CallbackManager {
355
+ addHandler(handler: CallbackHandler): void;
356
+ removeHandler(handler: CallbackHandler): void;
357
+ emit(event: CallbackEvent): Promise<void>;
358
+ createChild(tags?: string[], metadata?: Record<string, unknown>): CallbackManager;
359
+ }
360
+
361
+ declare const LLM_PROVIDERS: readonly ["openai", "anthropic", "mistral", "google", "ollama", "mock"];
362
+ type LLMProvider = (typeof LLM_PROVIDERS)[number];
363
+ type LLMModel = string;
364
+ /** A text span in a structured message. */
365
+ type LLMTextBlock = {
366
+ type: "text";
367
+ text: string;
368
+ };
369
+ /** An assistant turn requesting a tool call. */
370
+ type LLMToolUseBlock = {
371
+ type: "tool_use";
372
+ id: string;
373
+ name: string;
374
+ input: unknown;
375
+ };
376
+ /** A user turn returning a tool's result, paired to a prior `tool_use` by id. */
377
+ type LLMToolResultBlock = {
378
+ type: "tool_result";
379
+ toolUseId: string;
380
+ content: string;
381
+ isError?: boolean;
382
+ };
383
+ type LLMContentBlock = LLMTextBlock | LLMToolUseBlock | LLMToolResultBlock;
384
+ type LLMMessage = {
385
+ role: "system" | "user" | "assistant";
386
+ /**
387
+ * Either plain text or a list of content blocks. Blocks carry `tool_use` /
388
+ * `tool_result` turns so a tool-calling agent can hold a real multi-turn
389
+ * conversation with the provider instead of stuffing observations into text.
390
+ */
391
+ content: string | LLMContentBlock[];
392
+ };
393
+ /**
394
+ * Tool definition exposed to the provider. Part of the cacheable prefix: the tool
395
+ * list must be deterministic (stable order, stable schema) or it busts the cache.
396
+ */
397
+ type LLMToolDef = {
398
+ name: string;
399
+ description?: string;
400
+ inputSchema: Record<string, unknown>;
401
+ };
402
+ type LLMRequest = {
403
+ provider: LLMProvider;
404
+ model: LLMModel;
405
+ messages: LLMMessage[];
406
+ /**
407
+ * Immutable system prompt. Forms the cacheable prefix together with `tools`.
408
+ * Keep volatile content (dates, timestamps, session ids) OUT of here.
409
+ */
410
+ system?: string;
411
+ tools?: LLMToolDef[];
412
+ maxTokens?: number;
413
+ temperature?: number;
414
+ stream?: boolean;
415
+ };
416
+ /** A structured tool call surfaced by the provider (e.g. an Anthropic `tool_use` block). */
417
+ type LLMToolCall = {
418
+ id: string;
419
+ name: string;
420
+ input: unknown;
421
+ };
422
+ type LLMResponse = {
423
+ content: string;
424
+ /**
425
+ * Structured tool calls the model wants executed. Present when the provider
426
+ * stops on a tool-use turn; consumers should run these instead of parsing the
427
+ * text content for a tool protocol.
428
+ */
429
+ toolCalls?: LLMToolCall[];
430
+ /** Why the model stopped — `"tool_use"` signals it is waiting on tool results. */
431
+ stopReason?: "end_turn" | "tool_use" | "max_tokens" | "stop_sequence" | string;
432
+ usage: {
433
+ promptTokens: number;
434
+ completionTokens: number;
435
+ /** Tokens served from the prompt cache (~0.1x cost). */
436
+ cacheReadTokens?: number;
437
+ /** Tokens written to the prompt cache this call (~1.25x cost). */
438
+ cacheWriteTokens?: number;
439
+ };
440
+ model: string;
441
+ provider: LLMProvider;
442
+ };
443
+ type LLMStreamChunk = {
444
+ delta: string;
445
+ done: boolean;
446
+ };
447
+
448
+ interface LLMProviderAdapter {
449
+ provider: LLMProvider;
450
+ complete(req: LLMRequest): Promise<LLMResponse>;
451
+ stream(req: LLMRequest): AsyncIterable<LLMStreamChunk>;
452
+ }
453
+ interface LLMGateway {
454
+ complete(req: LLMRequest): Promise<LLMResponse>;
455
+ stream(req: LLMRequest): AsyncIterable<LLMStreamChunk>;
456
+ registerAdapter(adapter: LLMProviderAdapter): void;
457
+ }
458
+
459
+ type WorkingMemory = {
460
+ shortTerm: Message[];
461
+ longTerm: BaseStore;
462
+ };
463
+
464
+ type AgentId = string & {
465
+ readonly __brand: "AgentId";
466
+ };
467
+ type Blocker = {
468
+ code: string;
469
+ message: string;
470
+ nodeId?: NodeId;
471
+ };
472
+ type AgentResult = {
473
+ artifacts: ArtifactRef[];
474
+ blockers: Blocker[];
475
+ approvalRequests: Array<{
476
+ subject: ArtifactRef | {
477
+ description: string;
478
+ };
479
+ reason: string;
480
+ }>;
481
+ confidence: number;
482
+ reasoning: string;
483
+ requiresHumanReview: boolean;
484
+ /**
485
+ * Token usage summed across the run's LLM calls (ADR 0028 phase 7a — observability / cost).
486
+ * Present on results produced by the Rust engine; the control plane maps it to cost and to
487
+ * span/trace attributes. Omitted when no LLM call was made.
488
+ */
489
+ usage?: {
490
+ promptTokens: number;
491
+ completionTokens: number;
492
+ cacheReadTokens?: number;
493
+ cacheWriteTokens?: number;
494
+ };
495
+ /**
496
+ * The validated structured output (ADR 0029 phase 8): the parsed JSON value that conformed to
497
+ * the agent's `structuredOutput` middleware schema. Present only when that middleware ran and
498
+ * produced a valid value; omitted otherwise (no schema requested, or lenient mode never
499
+ * validated). The shape is the caller's schema — typed as `unknown`, validate/narrow at the
500
+ * boundary.
501
+ */
502
+ structuredOutput?: unknown;
503
+ };
504
+
505
+ interface Agent<TInput = unknown> {
506
+ id: AgentId;
507
+ name: string;
508
+ description: string;
509
+ run(input: TInput, state: GraphState, context: {
510
+ memory: BaseStore;
511
+ workingMemory: WorkingMemory;
512
+ callbacks?: CallbackManager;
513
+ }): Promise<AgentResult>;
514
+ }
515
+
516
+ type ZodSchema<T> = {
517
+ parse(input: unknown): T;
518
+ };
519
+ type ToolId = string & {
520
+ readonly __brand: "ToolId";
521
+ };
522
+ type ToolDefinition<TInput, TOutput> = {
523
+ id: ToolId;
524
+ name: string;
525
+ description: string;
526
+ inputSchema: ZodSchema<TInput>;
527
+ outputSchema: ZodSchema<TOutput>;
528
+ permissions: string[];
529
+ requiresApproval?: boolean;
530
+ /**
531
+ * JSON Schema for the tool's input, advertised to the LLM. `inputSchema` only
532
+ * validates (`.parse`); this is what the provider needs to emit tool calls.
533
+ */
534
+ jsonSchema?: Record<string, unknown>;
535
+ };
536
+ type ToolHandler<TInput, TOutput> = (input: TInput) => Promise<TOutput>;
537
+ interface ToolRegistry {
538
+ register<TInput, TOutput>(definition: ToolDefinition<TInput, TOutput>, handler: ToolHandler<TInput, TOutput>): void;
539
+ resolve(id: ToolId): {
540
+ definition: ToolDefinition<unknown, unknown>;
541
+ handler: ToolHandler<unknown, unknown>;
542
+ } | undefined;
543
+ list(): ToolDefinition<unknown, unknown>[];
544
+ }
545
+ declare class InMemoryToolRegistry implements ToolRegistry {
546
+ private readonly entries;
547
+ register<TInput, TOutput>(definition: ToolDefinition<TInput, TOutput>, handler: ToolHandler<TInput, TOutput>): void;
548
+ resolve(id: ToolId): {
549
+ definition: ToolDefinition<unknown, unknown>;
550
+ handler: ToolHandler<unknown, unknown>;
551
+ } | undefined;
552
+ list(): ToolDefinition<unknown, unknown>[];
553
+ }
554
+
555
+ /**
556
+ * The `writeTodos` planning tool and its checkpointed state shape — phase 1 of the
557
+ * governed deep-agent harness (ADR 0022/0023). Behaviour-identical to the Rust
558
+ * `agents-core` `todos.rs`: a pure state-write tool the model calls to (re)emit the
559
+ * **authoritative full** todo list. The engine (Rust) executes it and the agent
560
+ * node persists the latest list into {@link TODOS_CHANNEL}.
561
+ *
562
+ * This module is the shared-shape source of truth (types + {@link normalizeTodos})
563
+ * plus the tool definition SDK consumers reference; the real execution path is the
564
+ * Rust engine (the SDK runs Rust-only — ADR 0016).
565
+ */
566
+ type TodoStatus = "pending" | "in_progress" | "completed";
567
+ type TodoItem = {
568
+ id: string;
569
+ text: string;
570
+ status: TodoStatus;
571
+ };
572
+ /**
573
+ * Reserved channel the agent node persists the latest todo list into. Replace
574
+ * semantics: `writeTodos` always writes the full authoritative list, so the channel
575
+ * must not declare an append/merge reducer. Matches the Rust `TODOS_CHANNEL`.
576
+ */
577
+ declare const TODOS_CHANNEL = "__todos";
578
+ /** The `writeTodos` tool name. Matches the Rust `WRITE_TODOS_TOOL`. */
579
+ declare const WRITE_TODOS_TOOL_NAME = "writeTodos";
580
+ type WriteTodosInput = {
581
+ todos: Array<{
582
+ id?: string;
583
+ text: string;
584
+ status: TodoStatus;
585
+ }>;
586
+ };
587
+ /**
588
+ * Normalize raw tool input into an authoritative todo list. Lenient and
589
+ * deterministic — **byte-for-byte parity** with the Rust `normalize_todos`:
590
+ * - iterate `input.todos` in order;
591
+ * - drop any item whose `text` is missing or blank (after trimming);
592
+ * - an item with a missing or blank `id` gets `todo-{n}`, where `n` is its
593
+ * **1-based position in the incoming list** (dropped items still advance `n`);
594
+ * - an unknown or absent `status` coerces to `pending`.
595
+ *
596
+ * A missing / non-array `todos` field yields an empty list.
597
+ */
598
+ declare function normalizeTodos(input: unknown): TodoItem[];
599
+ /** JSON Schema advertised to the LLM — identical to the Rust `write_todos_tool` schema. */
600
+ declare const writeTodosJsonSchema: Record<string, unknown>;
601
+ /**
602
+ * The `writeTodos` tool definition + handler. `requiresApproval` is always false —
603
+ * planning is cheap and never gated. The handler returns the normalized list.
604
+ */
605
+ declare const writeTodosTool: {
606
+ definition: ToolDefinition<WriteTodosInput, TodoItem[]>;
607
+ handler: ToolHandler<WriteTodosInput, TodoItem[]>;
608
+ };
609
+
610
+ /**
611
+ * Versioned prompt registry. Agents reference prompts by id (+ optional version)
612
+ * instead of hardcoding them inline (rule 090). The `system` text is the immutable,
613
+ * cacheable prefix — keep volatile/dynamic values out of it and pass those in the
614
+ * per-call user message instead.
615
+ */
616
+ type PromptTemplate$1 = {
617
+ id: string;
618
+ version: string;
619
+ system: string;
620
+ description?: string;
621
+ };
622
+ interface PromptRegistry {
623
+ register(template: PromptTemplate$1): void;
624
+ get(id: string, version?: string): PromptTemplate$1;
625
+ list(): PromptTemplate$1[];
626
+ }
627
+ declare class InMemoryPromptRegistry implements PromptRegistry {
628
+ private readonly byId;
629
+ private readonly latest;
630
+ register(template: PromptTemplate$1): void;
631
+ get(id: string, version?: string): PromptTemplate$1;
632
+ list(): PromptTemplate$1[];
633
+ }
634
+
635
+ type ReActAgentOptions = {
636
+ id: AgentId;
637
+ name: string;
638
+ description: string;
639
+ llm: LLMGateway;
640
+ tools?: ToolRegistry;
641
+ maxIterations?: number;
642
+ provider?: LLMProvider;
643
+ model?: string;
644
+ /** System prompt source — agents reference prompts by id, never inline them. */
645
+ promptRegistry?: PromptRegistry;
646
+ promptId?: string;
647
+ promptVersion?: string;
648
+ /**
649
+ * Names of `requiresApproval` tools that have already been granted human
650
+ * approval (e.g. injected into state on resume). Listed tools execute instead
651
+ * of being gated again — this is how a suspended-for-approval run continues.
652
+ */
653
+ approvedToolNames?: string[];
654
+ };
655
+ declare class ReActAgent<TInput> implements Agent<TInput> {
656
+ readonly id: AgentId;
657
+ readonly name: string;
658
+ readonly description: string;
659
+ private readonly llm;
660
+ private readonly tools?;
661
+ private readonly maxIterations;
662
+ private readonly provider;
663
+ private readonly model;
664
+ private readonly promptRegistry?;
665
+ private readonly promptId?;
666
+ private readonly promptVersion?;
667
+ private readonly approvedToolNames;
668
+ constructor(options: ReActAgentOptions);
669
+ run(input: TInput, state: GraphState, context: {
670
+ memory: BaseStore;
671
+ workingMemory: WorkingMemory;
672
+ callbacks?: CallbackManager;
673
+ }): Promise<AgentResult>;
674
+ /**
675
+ * Resolve a tool by name and either execute it or gate it. Shared by the native
676
+ * tool_use path and the legacy ACTION: text protocol so both honour the approval
677
+ * rule identically: a `requiresApproval` tool is never self-executed.
678
+ */
679
+ private executeToolCall;
680
+ private resolveSystemPrompt;
681
+ private buildToolDefs;
682
+ }
683
+
684
+ type CheckpointId = string & {
685
+ readonly __brand: "CheckpointId";
686
+ };
687
+ type Checkpoint<TChannels extends ChannelsSchema = ChannelsSchema> = {
688
+ id: CheckpointId;
689
+ runId: RunId;
690
+ graphState: GraphState<TChannels>;
691
+ createdAt: string;
692
+ };
693
+ type RunEvent = {
694
+ type: "node_started";
695
+ runId: RunId;
696
+ nodeId: NodeId;
697
+ timestamp: string;
698
+ } | {
699
+ type: "node_completed";
700
+ runId: RunId;
701
+ nodeId: NodeId;
702
+ output: unknown;
703
+ timestamp: string;
704
+ } | {
705
+ type: "node_failed";
706
+ runId: RunId;
707
+ nodeId: NodeId;
708
+ error: string;
709
+ attempt: number;
710
+ category: FailureCategory;
711
+ timestamp: string;
712
+ } | {
713
+ type: "node_error_routed";
714
+ runId: RunId;
715
+ nodeId: NodeId;
716
+ errorEdgeId: EdgeId;
717
+ toNodeId: NodeId;
718
+ category: FailureCategory;
719
+ error: string;
720
+ timestamp: string;
721
+ } | {
722
+ type: "run_suspended";
723
+ runId: RunId;
724
+ nodeId: NodeId;
725
+ reason: string;
726
+ timestamp: string;
727
+ } | {
728
+ type: "run_resumed";
729
+ runId: RunId;
730
+ nodeId: NodeId;
731
+ timestamp: string;
732
+ } | {
733
+ type: "run_completed";
734
+ runId: RunId;
735
+ finalState: GraphState<ChannelsSchema>;
736
+ timestamp: string;
737
+ } | {
738
+ type: "run_failed";
739
+ runId: RunId;
740
+ error: string;
741
+ timestamp: string;
742
+ } | {
743
+ type: "run_cancelled";
744
+ runId: RunId;
745
+ nodeId: NodeId;
746
+ timestamp: string;
747
+ } | {
748
+ type: "token_delta";
749
+ runId: RunId;
750
+ nodeId: NodeId;
751
+ messageId: string;
752
+ delta: string;
753
+ parentRunId?: RunId;
754
+ spawnId?: number;
755
+ timestamp: string;
756
+ };
757
+
758
+ type NodeExecutionContext = {
759
+ memory: BaseStore;
760
+ };
761
+ type NodeHandler<TChannels extends ChannelsSchema = ChannelsSchema, TInput = unknown, TOutput extends Partial<ResolvedChannels<TChannels>> | Command<TChannels> = Partial<ResolvedChannels<TChannels>> | Command<TChannels>> = (input: TInput, state: GraphState<TChannels>, context: NodeExecutionContext) => Promise<TOutput>;
762
+ interface NodeRegistry {
763
+ register(nodeId: NodeId, handler: NodeHandler): void;
764
+ resolve(nodeId: NodeId): NodeHandler | undefined;
765
+ }
766
+ type ConditionFn = (state: GraphState) => boolean;
767
+ interface ConditionRegistry {
768
+ register(name: string, fn: ConditionFn): void;
769
+ resolve(name: string): ConditionFn | undefined;
770
+ }
771
+ interface Checkpointer {
772
+ save(checkpoint: Checkpoint): Promise<void>;
773
+ load(runId: RunId): Promise<Checkpoint | undefined>;
774
+ loadById(id: CheckpointId): Promise<Checkpoint | undefined>;
775
+ list(runId: RunId): Promise<Checkpoint[]>;
776
+ }
777
+ interface EventBus {
778
+ emit(event: RunEvent): void;
779
+ subscribe(handler: (event: RunEvent) => void): () => void;
780
+ }
781
+
782
+ type StepBudget = {
783
+ maxSteps: number;
784
+ currentSteps: number;
785
+ };
786
+
787
+ declare class InMemoryNodeRegistry implements NodeRegistry {
788
+ private readonly handlers;
789
+ register(nodeId: NodeId, handler: NodeHandler): void;
790
+ resolve(nodeId: NodeId): NodeHandler | undefined;
791
+ }
792
+
793
+ declare class InMemoryConditionRegistry implements ConditionRegistry {
794
+ private readonly conditions;
795
+ register(name: string, fn: ConditionFn): void;
796
+ resolve(name: string): ConditionFn | undefined;
797
+ }
798
+
799
+ declare class InMemoryCheckpointer implements Checkpointer {
800
+ private readonly checkpointsById;
801
+ private readonly latestCheckpointByRunId;
802
+ private readonly checkpointIdsByRunId;
803
+ save(checkpoint: Checkpoint): Promise<void>;
804
+ load(runId: RunId): Promise<Checkpoint | undefined>;
805
+ loadById(id: CheckpointId): Promise<Checkpoint | undefined>;
806
+ list(runId: RunId): Promise<Checkpoint[]>;
807
+ }
808
+
809
+ declare class InMemoryEventBus implements EventBus {
810
+ private readonly subscribers;
811
+ emit(event: RunEvent): void;
812
+ subscribe(handler: (event: RunEvent) => void): () => void;
813
+ }
814
+
815
+ declare const STREAM_MODES: readonly ["values", "updates", "debug", "messages"];
816
+ type StreamMode = (typeof STREAM_MODES)[number];
817
+ type StreamEvent = {
818
+ type: "state_value";
819
+ state: GraphState;
820
+ } | {
821
+ type: "state_update";
822
+ delta: Record<string, unknown>;
823
+ nodeId: NodeId;
824
+ } | {
825
+ type: "message_delta";
826
+ delta: string;
827
+ nodeId: NodeId;
828
+ messageId: string;
829
+ } | {
830
+ type: "tool_call";
831
+ toolId: string;
832
+ input: unknown;
833
+ nodeId: NodeId;
834
+ } | {
835
+ type: "debug";
836
+ payload: unknown;
837
+ nodeId: NodeId;
838
+ };
839
+
840
+ type InterruptConfig = {
841
+ before?: NodeId[];
842
+ after?: NodeId[];
843
+ };
844
+ declare class DynamicInterrupt extends Error {
845
+ readonly reason: string;
846
+ readonly patch?: Record<string, unknown>;
847
+ constructor(reason: string, patch?: Record<string, unknown>);
848
+ }
849
+
850
+ type GraphRuntimeDeps = {
851
+ graph: GraphDefinition<ChannelsSchema>;
852
+ nodeRegistry: NodeRegistry;
853
+ conditionRegistry: ConditionRegistry;
854
+ checkpointer: Checkpointer;
855
+ eventBus: EventBus;
856
+ callbackManager?: CallbackManager;
857
+ memory?: BaseStore;
858
+ stepBudget?: StepBudget;
859
+ subgraphResolver?: (graphId: GraphId) => GraphDefinition | undefined;
860
+ interruptConfig?: InterruptConfig;
861
+ };
862
+ declare class GraphRuntime {
863
+ private readonly graph;
864
+ private readonly nodeRegistry;
865
+ private readonly conditionRegistry;
866
+ private readonly checkpointer;
867
+ private readonly eventBus;
868
+ private readonly callbackManager;
869
+ readonly memory: BaseStore;
870
+ readonly stepBudget: StepBudget;
871
+ private readonly nodeById;
872
+ private readonly subgraphResolver?;
873
+ private readonly interruptConfig?;
874
+ private readonly stateHistoryByRunId;
875
+ private readonly stepsByRunId;
876
+ private readonly inboxByRunId;
877
+ constructor(deps: GraphRuntimeDeps);
878
+ start(runId: RunId, initialData: Record<string, unknown>): Promise<GraphState>;
879
+ stream(runId: RunId, initialData: Record<string, unknown>, mode: StreamMode): AsyncIterable<StreamEvent>;
880
+ resume(runId: RunId): Promise<GraphState>;
881
+ executeNode(nodeId: NodeId, state: GraphState): Promise<GraphState>;
882
+ nextNode(currentNodeId: NodeId, state: GraphState): NodeId | null;
883
+ send(runId: RunId, nodeId: NodeId, input: unknown): Promise<void>;
884
+ /** ADR 0076 — at most one `"error"` edge per node is enforced at validation time
885
+ * (`GRAPH_VALIDATION_ERROR_CODES.MULTIPLE_ERROR_EDGES`), so the first match is unambiguous. */
886
+ private findErrorEdge;
887
+ private selectNextEdge;
888
+ private runLoop;
889
+ private persistCheckpoint;
890
+ getHistory(runId: RunId): GraphState<ChannelsSchema>[];
891
+ updateState(runId: RunId, patch: Partial<Record<string, unknown>>, resumeFrom?: NodeId): Promise<GraphState>;
892
+ getCheckpoints(runId: RunId): Promise<Checkpoint[]>;
893
+ replayFrom(runId: RunId, checkpointId: CheckpointId): Promise<GraphState>;
894
+ applyUpdate(channels: Record<string, unknown>, partialUpdate: Partial<Record<string, unknown>>): Record<string, unknown>;
895
+ private buildInitialChannels;
896
+ private applyInputMapping;
897
+ private applyOutputMapping;
898
+ private setSubgraphRunId;
899
+ private getOrCreateSubgraphRunId;
900
+ private readInterruptMeta;
901
+ private clearInterruptMeta;
902
+ private suspendRun;
903
+ private computeDelta;
904
+ private areEqual;
905
+ private assertRecursionLimit;
906
+ private consumeStepBudget;
907
+ private consumeInjectedInput;
908
+ private resolveCommand;
909
+ private resolveNextNode;
910
+ private executeFanOut;
911
+ private extractMessageEvents;
912
+ private extractToolCallEvents;
913
+ }
914
+
915
+ /**
916
+ * Discriminated-union result type used across the SDK's "safe" entry points
917
+ * (e.g. {@link GraphBuilder.safeCompile}). Mirrors Zod's `safeParse` ergonomics.
918
+ */
919
+ type Result<T, E> = {
920
+ success: true;
921
+ data: T;
922
+ } | {
923
+ success: false;
924
+ error: E;
925
+ };
926
+ /** The "teach" metadata every Ailu error carries (ADR: errors-that-teach) — a stable
927
+ * machine-readable `code`, a one-line `hint` (the fix), and a deep `docUrl`. An AI agent or a
928
+ * human reads these to self-correct without leaving the stack trace. */
929
+ type ErrorTeach = {
930
+ code: string;
931
+ hint?: string;
932
+ docUrl?: string;
933
+ };
934
+ /** Base class for every error thrown by `@ailu-ai/graph-sdk`. Carries a stable `code`,
935
+ * an actionable `hint`, and a `docUrl` — the `.message` stays exactly what was thrown
936
+ * (so existing assertions hold); the teaching lives in the extra fields + {@link format}. */
937
+ declare class AiluSdkError extends Error {
938
+ readonly code: string;
939
+ readonly hint?: string;
940
+ readonly docUrl: string;
941
+ constructor(message: string, teach: ErrorTeach);
942
+ /** Message + hint + doc link, for logs / CLI / an agent reading the failure to self-correct. */
943
+ format(): string;
944
+ }
945
+ /** Thrown when `.compile()` is called on a graph that fails validation. */
946
+ declare class GraphCompileError extends AiluSdkError {
947
+ readonly errors: GraphValidationError[];
948
+ constructor(errors: GraphValidationError[]);
949
+ }
950
+ /** Thrown when two nodes are added under the same id. */
951
+ declare class DuplicateNodeError extends AiluSdkError {
952
+ constructor(nodeId: string);
953
+ }
954
+ /** Thrown when an action node is added without an executable handler. */
955
+ declare class MissingHandlerError extends AiluSdkError {
956
+ constructor(nodeId: string);
957
+ }
958
+ /** Thrown when a builder method references a node id that has not been added yet. */
959
+ declare class UnknownNodeError extends AiluSdkError {
960
+ constructor(nodeId: string, context: string);
961
+ }
962
+ /**
963
+ * Thrown when an agent's `middleware[]` names a GOVERNANCE kind (redact / approvalGate /
964
+ * fsPolicy). The governed layer is engine-injected and sealed — a user may only append
965
+ * EFFICIENCY middleware (compress / terse / contextBudget) — so an ungoverned stack is
966
+ * unrepresentable (ADR 0025 phase 3d, the governed-by-construction invariant).
967
+ */
968
+ declare class GovernanceMiddlewareRejectedError extends AiluSdkError {
969
+ constructor(kind: string);
970
+ }
971
+ /** Thrown when `resume`/`approve` is called but no suspended state exists for that run id on
972
+ * this `CompiledGraph` instance (the Rust path keeps suspended state per-instance). */
973
+ declare class ResumeStateNotFoundError extends AiluSdkError {
974
+ constructor(runId: string);
975
+ }
976
+ /** Thrown when `approveAndResume` is called without naming who approved (`resolvedBy`). */
977
+ declare class ApproverRequiredError extends AiluSdkError {
978
+ constructor();
979
+ }
980
+ /**
981
+ * Thrown by `resumeCatalogGraph` with an `approvalEngine` when the engine has not authorized the
982
+ * resume: a request is still pending, a human gate was rejected, or a granted tool has no matching
983
+ * approved request. `problems` lists each one; nothing ran.
984
+ */
985
+ declare class ApprovalNotGrantedError extends AiluSdkError {
986
+ readonly problems: string[];
987
+ constructor(runId: string, problems: string[]);
988
+ }
989
+
990
+ /**
991
+ * A structured, machine-readable account of where a run stands (ADR errors-that-teach / AI-DX):
992
+ * what state it's in, why it suspended, what it's waiting for, what failed. An AI agent or a
993
+ * human reads this to decide the next move — resume, deliver a signal, fix an input — without
994
+ * trawling the raw event log. Channel **names** only (never values) so it never leaks payloads.
995
+ */
996
+ type RunExplanation = {
997
+ runId: string;
998
+ status: string;
999
+ currentNode: string;
1000
+ /** One-line, human/agent-readable summary of the situation + the next action. */
1001
+ summary: string;
1002
+ /** Present when suspended: why, on which node, and what unblocks it. */
1003
+ suspended?: {
1004
+ reason: string;
1005
+ node: string;
1006
+ awaitingSignal?: string;
1007
+ wakeAt?: string;
1008
+ /** The concrete next action to resume. */
1009
+ nextAction: string;
1010
+ };
1011
+ /** Present when the run (or a node) failed. */
1012
+ failure?: {
1013
+ node?: string;
1014
+ error: string;
1015
+ };
1016
+ /** The channel names present (not their values). */
1017
+ channels: string[];
1018
+ /** The last few lifecycle events, type + node only (when an event log is provided). */
1019
+ recentEvents?: {
1020
+ type: string;
1021
+ node?: string;
1022
+ }[];
1023
+ };
1024
+ /**
1025
+ * Explain a run from its {@link GraphState} (and, optionally, its lifecycle event log).
1026
+ * Pure + read-only — safe to call on any state.
1027
+ */
1028
+ declare function explainRun(state: GraphState, events?: readonly RunEvent[]): RunExplanation;
1029
+
1030
+ declare class DefaultLLMGateway implements LLMGateway {
1031
+ private readonly adapters;
1032
+ registerAdapter(adapter: LLMProviderAdapter): void;
1033
+ complete(req: LLMRequest): Promise<LLMResponse>;
1034
+ stream(req: LLMRequest): AsyncIterable<LLMStreamChunk>;
1035
+ private validateRequest;
1036
+ }
1037
+
1038
+ type MockAdapterOptions = {
1039
+ provider: LLMProvider;
1040
+ response?: LLMResponse;
1041
+ /**
1042
+ * Scripted responses replayed one per `complete()` call (the last repeats once
1043
+ * exhausted). Lets a test drive a multi-turn agent — e.g. a `tool_use` turn
1044
+ * followed by a final-answer turn. Takes precedence over `response`.
1045
+ */
1046
+ responses?: LLMResponse[];
1047
+ chunks?: LLMStreamChunk[];
1048
+ };
1049
+ declare class MockLLMProviderAdapter implements LLMProviderAdapter {
1050
+ readonly provider: LLMProvider;
1051
+ private readonly responses;
1052
+ private readonly chunks;
1053
+ private index;
1054
+ constructor(options: MockAdapterOptions);
1055
+ complete(): Promise<LLMResponse>;
1056
+ stream(): AsyncIterable<LLMStreamChunk>;
1057
+ }
1058
+
1059
+ /**
1060
+ * Provider-shaped request the adapter assembles. This is the cache seam: the
1061
+ * `system` and `tools` blocks carry the cache_control breakpoints and must stay
1062
+ * byte-stable across calls. The default port translates this into the SDK request;
1063
+ * tests fake the port and assert on this shape directly.
1064
+ */
1065
+ type AnthropicCreateParams = {
1066
+ model: string;
1067
+ maxTokens: number;
1068
+ system?: Array<{
1069
+ type: "text";
1070
+ text: string;
1071
+ cacheable?: boolean;
1072
+ }>;
1073
+ tools?: Array<{
1074
+ name: string;
1075
+ description?: string;
1076
+ inputSchema: Record<string, unknown>;
1077
+ cacheable?: boolean;
1078
+ }>;
1079
+ messages: Array<{
1080
+ role: "user" | "assistant";
1081
+ content: string | LLMContentBlock[];
1082
+ }>;
1083
+ };
1084
+ /** Structural subset of the SDK `Message` the adapter actually reads. */
1085
+ type AnthropicRawResponse = {
1086
+ content: Array<{
1087
+ type: string;
1088
+ text?: string;
1089
+ id?: string;
1090
+ name?: string;
1091
+ input?: unknown;
1092
+ }>;
1093
+ stop_reason?: string | null;
1094
+ usage: {
1095
+ input_tokens: number;
1096
+ output_tokens: number;
1097
+ cache_read_input_tokens?: number | null;
1098
+ cache_creation_input_tokens?: number | null;
1099
+ };
1100
+ };
1101
+ /**
1102
+ * The only seam onto the Anthropic SDK. The default implementation wraps a real
1103
+ * client; tests supply a fake so the cache + accounting logic is covered without
1104
+ * a network call or an API key.
1105
+ */
1106
+ interface AnthropicClientPort {
1107
+ create(params: AnthropicCreateParams): Promise<AnthropicRawResponse>;
1108
+ stream(params: AnthropicCreateParams): AsyncIterable<LLMStreamChunk>;
1109
+ }
1110
+ type AnthropicAdapterOptions = {
1111
+ /** Override the model used when the request does not name a Claude model. */
1112
+ defaultModel?: string;
1113
+ /** Inject a client port (tests) or an API key (production). */
1114
+ port?: AnthropicClientPort;
1115
+ apiKey?: string;
1116
+ };
1117
+ declare class AnthropicProviderAdapter implements LLMProviderAdapter {
1118
+ readonly provider: "anthropic";
1119
+ private readonly port;
1120
+ private readonly defaultModel;
1121
+ constructor(options?: AnthropicAdapterOptions);
1122
+ complete(req: LLMRequest): Promise<LLMResponse>;
1123
+ stream(req: LLMRequest): AsyncIterable<LLMStreamChunk>;
1124
+ /**
1125
+ * Assemble the provider request. The cacheable prefix is `tools` then `system`
1126
+ * (Anthropic render order); a breakpoint on the last tool and on the system block
1127
+ * caches that prefix. Sampling params are intentionally dropped — Opus 4.7/4.8
1128
+ * reject `temperature`/`top_p`/`top_k`. No date/timestamp is added here.
1129
+ */
1130
+ private buildParams;
1131
+ private collectSystem;
1132
+ private resolveModel;
1133
+ private toResponse;
1134
+ }
1135
+
1136
+ /**
1137
+ * The OpenAI `/v1/chat/completions` request body the adapter assembles. This is the
1138
+ * seam tests assert against directly: {@link buildRequestBody} is a pure function, so
1139
+ * the request mapping is covered without a network call or an API key.
1140
+ */
1141
+ type OpenAIChatRequestBody = {
1142
+ model: string;
1143
+ messages: OpenAIChatMessage[];
1144
+ tools?: Array<{
1145
+ type: "function";
1146
+ function: {
1147
+ name: string;
1148
+ description?: string;
1149
+ parameters: Record<string, unknown>;
1150
+ };
1151
+ }>;
1152
+ temperature?: number;
1153
+ max_tokens?: number;
1154
+ };
1155
+ /** A single message in the OpenAI chat shape. */
1156
+ type OpenAIChatMessage = {
1157
+ role: "system" | "user" | "assistant" | "tool";
1158
+ content: string;
1159
+ tool_call_id?: string;
1160
+ tool_calls?: Array<{
1161
+ id: string;
1162
+ type: "function";
1163
+ function: {
1164
+ name: string;
1165
+ arguments: string;
1166
+ };
1167
+ }>;
1168
+ };
1169
+ /** Structural subset of the OpenAI chat completion response the adapter reads. */
1170
+ type OpenAIChatResponse = {
1171
+ choices: Array<{
1172
+ message: {
1173
+ content?: string | null;
1174
+ tool_calls?: Array<{
1175
+ id: string;
1176
+ type?: string;
1177
+ function: {
1178
+ name: string;
1179
+ arguments: string;
1180
+ };
1181
+ }> | null;
1182
+ };
1183
+ finish_reason?: string | null;
1184
+ }>;
1185
+ usage?: {
1186
+ prompt_tokens?: number;
1187
+ completion_tokens?: number;
1188
+ };
1189
+ };
1190
+ /**
1191
+ * The only seam onto the HTTP transport. The default implementation POSTs the body to
1192
+ * `baseUrl + '/chat/completions'` via global `fetch`; tests supply a fake so the
1193
+ * request/response mapping is covered without a network call.
1194
+ */
1195
+ interface OpenAICompatibleTransportPort {
1196
+ send(body: OpenAIChatRequestBody): Promise<OpenAIChatResponse>;
1197
+ }
1198
+ type OpenAICompatibleAdapterOptions = {
1199
+ /** Provider key this adapter registers under in the gateway map. Default `'mistral'`. */
1200
+ provider?: LLMProvider;
1201
+ /** API base, e.g. `https://api.mistral.ai/v1` or `http://localhost:11434/v1`. */
1202
+ baseUrl: string;
1203
+ /** Model used when the request does not name a model id for this provider. */
1204
+ defaultModel: string;
1205
+ /** Bearer token; omitted for keyless servers such as a local Ollama. */
1206
+ apiKey?: string;
1207
+ /** Inject a transport port (tests) instead of the default `fetch`-backed one. */
1208
+ port?: OpenAICompatibleTransportPort;
1209
+ };
1210
+ /**
1211
+ * One adapter for any server speaking the OpenAI `/v1/chat/completions` shape. Both
1212
+ * a local **Ollama** server (`http://localhost:11434/v1`, keyless) and **Mistral
1213
+ * cloud** (`https://api.mistral.ai/v1`, bearer key) are driven by this same class;
1214
+ * use {@link OpenAICompatibleProviderAdapter.ollama} / `.mistral` to construct them.
1215
+ */
1216
+ declare class OpenAICompatibleProviderAdapter implements LLMProviderAdapter {
1217
+ readonly provider: LLMProvider;
1218
+ private readonly port;
1219
+ private readonly baseUrl;
1220
+ private readonly defaultModel;
1221
+ constructor(options: OpenAICompatibleAdapterOptions);
1222
+ /** Mistral cloud: bearer-keyed, hosted at `https://api.mistral.ai/v1`. */
1223
+ static mistral(apiKey?: string, model?: string): OpenAICompatibleProviderAdapter;
1224
+ /** Google Gemini via its OpenAI-compatible endpoint; registers under the `google` provider. */
1225
+ static google(apiKey?: string, model?: string): OpenAICompatibleProviderAdapter;
1226
+ /** OpenAI cloud (the canonical OpenAI-compatible API); registers under the `openai` provider. */
1227
+ static openai(apiKey?: string, model?: string): OpenAICompatibleProviderAdapter;
1228
+ /**
1229
+ * A local Ollama server (keyless, `http://localhost:11434/v1`). Registers under the
1230
+ * `'mistral'` provider key so it routes through the same gateway slot as Mistral cloud.
1231
+ */
1232
+ static ollama(model?: string, baseUrl?: string): OpenAICompatibleProviderAdapter;
1233
+ complete(req: LLMRequest): Promise<LLMResponse>;
1234
+ stream(req: LLMRequest): AsyncIterable<LLMStreamChunk>;
1235
+ private toResponse;
1236
+ }
1237
+
1238
+ /**
1239
+ * An abstract capability tier. Wire-compatible (camelCase) with the Rust
1240
+ * `ModelTier` enum in `crates/llm-gateway`.
1241
+ */
1242
+ type ModelTier = "frontier" | "balanced" | "fast" | "creative";
1243
+ /** All four tiers, in declaration order — handy for seeding tables. */
1244
+ declare const MODEL_TIERS: readonly ModelTier[];
1245
+ /**
1246
+ * The outcome of {@link ModelPolicy.resolve}: a concrete provider + model, plus
1247
+ * whether the model came from the recommended per-tier defaults (`true`) or from
1248
+ * an explicit override (`false`).
1249
+ */
1250
+ type ModelChoice = {
1251
+ provider: LLMProvider;
1252
+ model: string;
1253
+ recommended: boolean;
1254
+ };
1255
+ /** `tier -> model` for a single provider. */
1256
+ type TierModelTable = Record<ModelTier, string>;
1257
+ /** Optional override passed to {@link ModelPolicy.resolve}. */
1258
+ type ResolveOverride = {
1259
+ provider?: LLMProvider;
1260
+ model?: string;
1261
+ };
1262
+ /**
1263
+ * The shared capability-tier contract defaults: the recommended model for each
1264
+ * provider at each tier. Mirrors the Rust `ModelPolicy::default` table exactly.
1265
+ */
1266
+ declare const DEFAULT_TIER_TABLE: Partial<Record<LLMProvider, TierModelTable>>;
1267
+ /** The default cross-provider preference order, highest first. */
1268
+ declare const DEFAULT_PREFERENCE: readonly LLMProvider[];
1269
+ /**
1270
+ * Capability-tier model policy: map an abstract capability tier
1271
+ * (`frontier` / `balanced` / `fast` / `creative`) onto a concrete
1272
+ * `{ provider, model }` choice, given which providers are actually available.
1273
+ *
1274
+ * Mirrors the Rust `crates/llm-gateway` `ModelPolicy` byte for byte in behaviour
1275
+ * and wire shape. The point: "I only have Mistral" -> every tier resolves to the
1276
+ * mistral column; "only Anthropic" -> `fast` -> haiku, `frontier` -> opus,
1277
+ * `creative` -> fable.
1278
+ */
1279
+ declare class ModelPolicy {
1280
+ private readonly table;
1281
+ private readonly preference;
1282
+ /**
1283
+ * Construct a policy. Either argument may be omitted to keep the contract
1284
+ * default for that piece.
1285
+ */
1286
+ constructor(options?: {
1287
+ table?: Partial<Record<LLMProvider, TierModelTable>>;
1288
+ preference?: readonly LLMProvider[];
1289
+ });
1290
+ /**
1291
+ * Which providers are usable given the current process environment:
1292
+ * `anthropic` iff `ANTHROPIC_API_KEY` is set; `mistral` iff `MISTRAL_API_KEY`
1293
+ * is set; `ollama` iff `AILU_USE_OLLAMA=1`. Order follows the policy
1294
+ * preference so callers get a deterministic list.
1295
+ */
1296
+ availableFromEnv(env?: NodeJS.ProcessEnv): LLMProvider[];
1297
+ /**
1298
+ * Resolve a capability tier to a concrete `{ provider, model, recommended }`.
1299
+ *
1300
+ * - An explicit `override.provider` and/or `override.model` wins, with
1301
+ * `recommended = false`. When only one of the two is given, the other is
1302
+ * filled from the policy: an override provider maps the tier to that
1303
+ * provider's recommended model; an override model rides on the first
1304
+ * available provider (or the override provider if also given).
1305
+ * - Otherwise the highest-preference provider that is both available and
1306
+ * present in the table supplies its tier model, with `recommended = true`.
1307
+ * - If nothing is available, the mock provider is returned.
1308
+ */
1309
+ resolve(tier: ModelTier, available: readonly LLMProvider[], override?: ResolveOverride): ModelChoice;
1310
+ /** The recommended model for a provider+tier from the table, if present. */
1311
+ private modelFor;
1312
+ /** The first preference-ordered provider that is in `available`. */
1313
+ private firstAvailable;
1314
+ }
1315
+
1316
+ type ApprovalId = string & {
1317
+ readonly __brand: "ApprovalId";
1318
+ };
1319
+ declare const APPROVAL_STATUSES: readonly ["pending", "approved", "rejected"];
1320
+ type ApprovalStatus = (typeof APPROVAL_STATUSES)[number];
1321
+ type ApprovalRequest = {
1322
+ id: ApprovalId;
1323
+ runId: RunId;
1324
+ nodeId: NodeId;
1325
+ requestedBy: string;
1326
+ subject: ArtifactRef | {
1327
+ description: string;
1328
+ };
1329
+ status: ApprovalStatus;
1330
+ resolvedBy?: string;
1331
+ resolvedAt?: Date;
1332
+ rejectionReason?: string;
1333
+ createdAt: Date;
1334
+ /** Tenant scope (ADR 0036 #4) — set by the control plane; the engine is tenant-agnostic. */
1335
+ tenantId?: string;
1336
+ };
1337
+
1338
+ type RequestApprovalParams = {
1339
+ runId: RunId;
1340
+ nodeId: NodeId;
1341
+ requestedBy: string;
1342
+ subject: ArtifactRef | {
1343
+ description: string;
1344
+ };
1345
+ /**
1346
+ * Tenant the approval belongs to (ADR 0036 #4). Optional + back-compat: the engine itself is
1347
+ * tenant-agnostic and omits it; the CONTROL PLANE supplies it so an approval can be tenant-scoped
1348
+ * at the persistence layer (and so off-run gates — e.g. A2A outbound delegation — carry a tenant
1349
+ * even without a real run row). When set, an impl SHOULD persist + filter on it.
1350
+ */
1351
+ tenantId?: string;
1352
+ };
1353
+ interface ApprovalEngine {
1354
+ request(params: RequestApprovalParams): Promise<ApprovalRequest>;
1355
+ approve(id: ApprovalId, resolvedBy: string): Promise<ApprovalRequest>;
1356
+ reject(id: ApprovalId, resolvedBy: string, reason: string): Promise<ApprovalRequest>;
1357
+ getPending(runId?: RunId): Promise<ApprovalRequest[]>;
1358
+ getById(id: ApprovalId): Promise<ApprovalRequest | undefined>;
1359
+ }
1360
+
1361
+ declare class ApprovalSelfApprovalError extends Error {
1362
+ constructor(approvalId: ApprovalId);
1363
+ }
1364
+ declare class ApprovalAlreadyResolvedError extends Error {
1365
+ constructor(approvalId: ApprovalId);
1366
+ }
1367
+ declare class ApprovalNotFoundError extends Error {
1368
+ constructor(approvalId: ApprovalId);
1369
+ }
1370
+
1371
+ declare class InMemoryApprovalEngine implements ApprovalEngine {
1372
+ private readonly approvals;
1373
+ request(params: RequestApprovalParams): Promise<ApprovalRequest>;
1374
+ approve(id: ApprovalId, resolvedBy: string): Promise<ApprovalRequest>;
1375
+ reject(id: ApprovalId, resolvedBy: string, reason: string): Promise<ApprovalRequest>;
1376
+ getPending(runId?: RunId): Promise<ApprovalRequest[]>;
1377
+ getById(id: ApprovalId): Promise<ApprovalRequest | undefined>;
1378
+ private getOrThrow;
1379
+ private ensureCanResolve;
1380
+ }
1381
+
1382
+ /**
1383
+ * Tamper-evident attestation of approval decisions.
1384
+ *
1385
+ * Each resolved approval is hashed (over a canonical, key-sorted view) and signed
1386
+ * with Ed25519. Records are chained — every signature covers the previous record's
1387
+ * hash — so neither a single field nor the ordering of decisions can be altered
1388
+ * after the fact without breaking verification. This is the audit primitive a
1389
+ * regulated environment needs: "what did the agents do, and who approved it" becomes
1390
+ * cryptographically verifiable.
1391
+ */
1392
+ type AttestationView = {
1393
+ approvalId: string;
1394
+ runId: string;
1395
+ status: "approved" | "rejected";
1396
+ resolvedBy: string;
1397
+ subject: string;
1398
+ decidedAt: string;
1399
+ };
1400
+ type AttestationRecord = AttestationView & {
1401
+ algorithm: "ed25519";
1402
+ /** SHA-256 (hex) of the canonical {@link AttestationView}. */
1403
+ payloadHash: string;
1404
+ /** Previous record's `payloadHash`, linking the chain. `null` for the first. */
1405
+ prevHash: string | null;
1406
+ /** Base64 SPKI/DER public key that verifies `signature`. */
1407
+ publicKey: string;
1408
+ /** Base64 Ed25519 signature over the chain hash. */
1409
+ signature: string;
1410
+ };
1411
+ /** Deterministic JSON: object keys sorted recursively so the hash is stable. */
1412
+ declare const canonicalJson: (value: unknown) => string;
1413
+ /** Signs approval decisions with an Ed25519 key pair. */
1414
+ declare class Ed25519Attestor {
1415
+ private readonly privateKey;
1416
+ private readonly publicKeyB64;
1417
+ constructor(keys?: {
1418
+ privateKey: KeyObject;
1419
+ publicKey: KeyObject;
1420
+ });
1421
+ /** Sign a resolved approval, chaining it after `prevHash`. */
1422
+ attest(request: ApprovalRequest, prevHash?: string | null): AttestationRecord;
1423
+ }
1424
+ /** Verify a single record: its payload hash is intact and the signature is valid. */
1425
+ declare const verifyAttestation: (record: AttestationRecord) => boolean;
1426
+ /** Verify a full chain: every record valid and correctly linked to its predecessor. */
1427
+ declare const verifyChain: (records: AttestationRecord[]) => boolean;
1428
+
1429
+ /** Default channel an agent node writes its {@link import("@ailu-ai/agents-core").AgentResult} into. */
1430
+ declare const DEFAULT_AGENT_OUTPUT_CHANNEL = "agentResult";
1431
+ /**
1432
+ * Channel holding the names of tools whose human approval has been granted. The
1433
+ * control plane writes it (see `CompiledGraph.approveAndResume`) before resuming a
1434
+ * run that suspended for approval; the agent then executes those tools.
1435
+ */
1436
+ declare const APPROVED_TOOLS_CHANNEL = "__approvedTools";
1437
+ /** Reason carried by the dynamic interrupt an agent node raises when it needs approval. */
1438
+ declare const AGENT_APPROVAL_INTERRUPT = "agent-approval-required";
1439
+ /**
1440
+ * Channel holding the ApprovalEngine request ids created when a run suspends for
1441
+ * approval. On resume the node looks each up; the ones the engine reports as
1442
+ * `approved` unlock their tools — the engine is the source of truth, not a flag.
1443
+ */
1444
+ declare const APPROVAL_IDS_CHANNEL = "__approvalIds";
1445
+ /** Where an agent node gets its system prompt. */
1446
+ type AgentPromptSource = {
1447
+ registry: PromptRegistry;
1448
+ id: string;
1449
+ version?: string;
1450
+ }
1451
+ /** Inline convenience: the SDK registers this string and references it by id. */
1452
+ | {
1453
+ system: string;
1454
+ };
1455
+ /**
1456
+ * Governed long-term memory overlay for an agent node (ADR 0026 phase 11). `namespace` is the
1457
+ * tenant-scoped memory partition; `topK`/`recall` tune retrieval. The principal is sealed by the
1458
+ * engine. The OSS engine recalls/persists in-memory; the control plane swaps a Neo4j-backed store
1459
+ * (native vector index + entity graph) behind the same seam.
1460
+ */
1461
+ type MemoryConfig = {
1462
+ namespace: string;
1463
+ topK?: number;
1464
+ recall?: "vector" | "graph" | "both";
1465
+ };
1466
+ /**
1467
+ * Governed skills overlay for an agent node (ADR 0035 phase 12) — progressive disclosure for deep
1468
+ * agents. The engine selects skills (explicit `required` pins by `name@version` + advisory vector
1469
+ * top-k over descriptions, capped by `advisoryK`) from this `namespace` before the run and prepends
1470
+ * their bodies to the seed. The `namespace` is tenant-scoped (sealed by the engine); a skill that
1471
+ * grants capability (`requires`) stays withheld until granted. Applies to this agent AND its
1472
+ * `mapAgents`/`taskNode` sub-agents (same build path → deepagents parity). The OSS engine selects
1473
+ * from an in-memory registry; the control plane swaps a Postgres-backed store behind the same seam.
1474
+ */
1475
+ type SkillConfig = {
1476
+ namespace: string;
1477
+ /** Explicit `name@version` pins — the must-apply playbooks, always loaded (when granted). */
1478
+ required?: string[];
1479
+ /** Cap on advisory (vector-selected) skills. Default 3; 0 = pins only. */
1480
+ advisoryK?: number;
1481
+ };
1482
+ /**
1483
+ * One skill RECORD supplied by the control plane for a run (ADR 0049 B-3) — the store contents the
1484
+ * engine builds its run-scoped `SkillStore` from. Matches Rust `Skill` (camelCase). `description` is the
1485
+ * always-resident L1 index; `body` is the on-demand L2; `requires` are approval-gated capability keys.
1486
+ * `allowedTools`/`license`/`metadata` are open-standard SKILL.md fields (ADR 0065 D1) — stored and
1487
+ * round-tripped through import/export, but `allowedTools` is NOT enforced by `SkillMiddleware` yet
1488
+ * (distinct from `requires`, which is the actual capability gate).
1489
+ */
1490
+ type SkillRecord = {
1491
+ name: string;
1492
+ version: string;
1493
+ namespace: string;
1494
+ description: string;
1495
+ body?: string;
1496
+ requires?: string[];
1497
+ allowedTools?: string[];
1498
+ license?: string;
1499
+ metadata?: Record<string, unknown>;
1500
+ resources?: Array<{
1501
+ kind?: string;
1502
+ ref: string;
1503
+ }>;
1504
+ };
1505
+ /** Config for {@link GraphBuilder.agentNode}. */
1506
+ type AgentNodeConfig = {
1507
+ /**
1508
+ * @deprecated Ignored: the engine calls the model itself. Use {@link AgentNodeConfig.model}
1509
+ * (`model.openai("gpt-4o")`), and `AILU_LLM_MOCK=1` to run offline.
1510
+ */
1511
+ llm?: LLMGateway;
1512
+ prompt: AgentPromptSource;
1513
+ tools?: ToolRegistry;
1514
+ /**
1515
+ * @deprecated Use {@link AgentNodeConfig.model}: `model.openai("gpt-4o")` or `"openai:gpt-4o"`.
1516
+ * An unknown provider throws `UnknownProviderError`.
1517
+ */
1518
+ provider?: LLMProvider;
1519
+ /**
1520
+ * The model to run: `model.openai("gpt-4o")`, `model.fast`, or a `"provider:model"` string
1521
+ * (`"openai:gpt-4o"`). A bare model id without a provider runs on Anthropic. The model's
1522
+ * provider and tier win over the deprecated flat `provider`/`tier` fields.
1523
+ */
1524
+ model?: string | ModelLike;
1525
+ /**
1526
+ * @deprecated Use a tier model: `model.fast`, `model.mistral.frontier`. Capability tier
1527
+ * (`"frontier" | "balanced" | "fast" | "creative"`), resolved by the engine against the
1528
+ * provider keys present in the environment.
1529
+ */
1530
+ tier?: ModelTier;
1531
+ /**
1532
+ * A named {@link AgentProfile} (`"fast" | "frontier-careful" | "governed-deep"`, ADR 0025
1533
+ * phase 3d) that sets the model tier, efficiency middleware, and suspend/fs defaults in one
1534
+ * shot. Explicit fields (`tier`, `suspendForApproval`, `enableFs`, `middleware`, the flat
1535
+ * `outputStyle`/`contextBudget` knobs) always override the profile's defaults. The governed
1536
+ * layer (redaction, approval gate, fs policy) is identical regardless of profile.
1537
+ */
1538
+ profile?: AgentProfile;
1539
+ /**
1540
+ * Extra EFFICIENCY middleware to append (ADR 0025 phase 3d), e.g. `[{ kind: "compress" }]`.
1541
+ * Efficiency-only by type: governance kinds (redact / approval gate / fs policy) are
1542
+ * engine-injected and rejected here ({@link GovernanceMiddlewareRejectedError}), so an
1543
+ * ungoverned stack is unrepresentable. Merged after the profile + the flat knobs, with
1544
+ * an explicit entry of the same `kind` winning (dedup, last-writer).
1545
+ */
1546
+ middleware?: EfficiencyMiddlewareSpec[];
1547
+ maxIterations?: number;
1548
+ name?: string;
1549
+ description?: string;
1550
+ /** Channel that receives the agent's result. Defaults to {@link DEFAULT_AGENT_OUTPUT_CHANNEL}. */
1551
+ outputChannel?: string;
1552
+ /**
1553
+ * Token-efficiency (ADR 0014). `"terse"` appends a compact-output directive to the
1554
+ * system prompt — cuts output tokens on **prose** stages (lossy; not for code). Default off.
1555
+ */
1556
+ outputStyle?: "terse";
1557
+ /**
1558
+ * Cap (in chars) on the agent's seed message — the injected `Input: <input>\nState:
1559
+ * <state>` dump — to avoid re-feeding an unbounded channel map to every agent. The cap
1560
+ * covers the whole seed message (ADR 0014 intent), not the `State` portion alone. Default: no cap.
1561
+ */
1562
+ contextBudget?: number;
1563
+ /**
1564
+ * Durable channel the agent's `writeTodos` list is persisted into (ADR 0022/0023,
1565
+ * phase 1). When set and the agent has the `writeTodos` tool, the engine writes the
1566
+ * authoritative todo list here in the same checkpointed update as the result, so
1567
+ * downstream nodes can read the plan. Default: no durable sink (the list still
1568
+ * appears in the result). Conventionally {@link import("@ailu-ai/agents-core").TODOS_CHANNEL} (`"__todos"`).
1569
+ */
1570
+ todosChannel?: string;
1571
+ /**
1572
+ * Channel carrying this run's multimodal input (ADR 0030 phase 9e): a `ContentBlock[]`
1573
+ * value (`{ type: "image" | "audio" | "file", source }` + optional `{ type: "text" }`).
1574
+ * When set, the agent's seed message becomes multimodal — a text Input/State digest plus
1575
+ * the media blocks — and this channel is excluded from the stringified State (so binary
1576
+ * bytes are never re-fed as text). Seed the channel via the run's `initialData`. Default:
1577
+ * text-only seed.
1578
+ */
1579
+ inputBlocksChannel?: string;
1580
+ /** The only channels the agent is shown in its seed state (context isolation); default all. */
1581
+ visibleChannels?: string[];
1582
+ /**
1583
+ * Governed long-term memory (ADR 0026 phase 11). When set, the engine recalls from this
1584
+ * namespace before the run (vector) and persists the run's reasoning after, attributed. The
1585
+ * `namespace` is tenant-scoped (the control plane validates access) and the principal is
1586
+ * sealed by the engine — never user-routable. `topK`/`recall` tune retrieval quality.
1587
+ */
1588
+ memory?: MemoryConfig;
1589
+ /**
1590
+ * Governed skills — progressive disclosure (ADR 0035 phase 12). When set, the engine selects
1591
+ * skills (explicit `required` pins + advisory vector top-k) from this `namespace` before the run
1592
+ * and prepends their bodies to the seed. Capability-granting (`requires`) skills are withheld
1593
+ * until granted. Applies to `mapAgents`/`taskNode` sub-agents too (deepagents parity).
1594
+ */
1595
+ skills?: SkillConfig;
1596
+ /**
1597
+ * Opt this agent into the governed virtual filesystem tools (ADR 0024 phase 2b):
1598
+ * `read_file`/`ls`/`glob`/`grep`/`write_file`/`edit_file`/`delete_file`/`move_file`,
1599
+ * run-scoped over a versioned artifact store and enforced by the graph's
1600
+ * {@link GraphBuilder.fsPolicy} (fail-closed read-only by default). Default off.
1601
+ */
1602
+ enableFs?: boolean;
1603
+ /**
1604
+ * When true, the node suspends the whole run (a dynamic interrupt) the moment the
1605
+ * agent needs approval, instead of just flagging `requiresHumanReview`. Resume with
1606
+ * `CompiledGraph.approveAndResume(runId, { approvedTools })` to continue. Default false.
1607
+ */
1608
+ suspendForApproval?: boolean;
1609
+ /**
1610
+ * @deprecated Removed: a graph that sets it fails to compile. Use `suspendForApproval` with
1611
+ * `approveAndResume`, or pass the engine to the catalog runner:
1612
+ * `runCatalogGraph(app.definition, { approvalEngine })`.
1613
+ */
1614
+ approvalEngine?: ApprovalEngine;
1615
+ label?: string;
1616
+ };
1617
+ /**
1618
+ * A signed-off agent profile (ADR 0025 phase 3d) — a named bundle that expands to a model
1619
+ * tier, an efficiency-middleware set, and suspend/fs defaults. The GOVERNED layer (PII
1620
+ * redaction, the approval gate, fs policy) is identical across all profiles — you cannot
1621
+ * "buy out" of governance. Explicit `tier`/`suspendForApproval`/`enableFs`/`middleware` on
1622
+ * the config always win over the profile's defaults.
1623
+ *
1624
+ * - `fast` — `fast` tier, full efficiency (compress + terse + tight 4k context budget), no
1625
+ * suspend. For high-throughput, low-stakes prose work.
1626
+ * - `frontier-careful` — `frontier` tier, NO compression (preserve fidelity), a roomy 16k
1627
+ * budget, reflection, suspend-on-approval. For high-stakes reasoning where lossy compression
1628
+ * is unsafe.
1629
+ * - `governed-deep` — the deep-agent one-liner: `balanced` tier, full efficiency (12k budget),
1630
+ * reflection, suspend-on-approval, and the governed virtual filesystem enabled.
1631
+ */
1632
+ type AgentProfile = "fast" | "frontier-careful" | "governed-deep";
1633
+ /**
1634
+ * An EFFICIENCY / quality middleware a user may append to an agent (ADR 0025 phase 3d/3e). The
1635
+ * governance kinds (`redact` / `approvalGate` / `fsPolicy`) are deliberately NOT part of this
1636
+ * union — they are engine-injected and sealed, so a user cannot express an ungoverned stack
1637
+ * (the governed-by-construction invariant). `retry` / `rateLimit` stay deferred (retry belongs
1638
+ * at the gateway; a rate-limit delay would conflict with the engine's determinism).
1639
+ *
1640
+ * - `compress` — route messages through the prompt-compression service (no-op if unconfigured).
1641
+ * - `terse` — append a compact-output directive to the system prompt (lossy; prose only).
1642
+ * - `contextBudget` — cap the agent's seed message (the injected `Input`/`State` dump) to `chars` characters.
1643
+ * - `reflection` — one self-critique after the run (ADR 0025 phase 3e): a weak result is flagged in
1644
+ * the reasoning (`reflection:needs_review:<issues>`) for observability / downstream routing (a
1645
+ * conditional edge can gate on it). It does NOT set `requiresHumanReview` — that would re-suspend
1646
+ * forever on resume. Additive — the full critique→revise loop stays the standalone reflection
1647
+ * node. `threshold` (0..1, default 0.8) is the acceptance bar.
1648
+ * - `structuredOutput` — constrain the agent's output to a JSON Schema (ADR 0029 phase 8). The
1649
+ * engine sets the provider's native constraint (OpenAI `response_format`, Anthropic forced tool,
1650
+ * Gemini `responseSchema`) AND validates the result in-engine (the floor). The validated value
1651
+ * lands on `AgentResult.structuredOutput`. `mode: "required"` (default) fails closed with a typed
1652
+ * error after `retryCap` (default 2) deterministic re-prompts; `mode: "lenient"` falls back to raw
1653
+ * text. It is an EFFICIENCY kind (output-shaping), and the approval gate stays intrinsic — so it
1654
+ * cannot route around governance.
1655
+ */
1656
+ type EfficiencyMiddlewareSpec = {
1657
+ kind: "compress";
1658
+ } | {
1659
+ kind: "terse";
1660
+ } | {
1661
+ kind: "contextBudget";
1662
+ params: {
1663
+ chars: number;
1664
+ };
1665
+ } | {
1666
+ kind: "reflection";
1667
+ params?: {
1668
+ threshold?: number;
1669
+ };
1670
+ } | {
1671
+ kind: "structuredOutput";
1672
+ params: {
1673
+ schema: Record<string, unknown>;
1674
+ name?: string;
1675
+ strict?: boolean;
1676
+ mode?: "required" | "lenient";
1677
+ retryCap?: number;
1678
+ };
1679
+ };
1680
+ /**
1681
+ * Governance middleware kinds the SDK rejects in {@link AgentNodeConfig.middleware}: they are
1682
+ * engine-injected (the GOVERNED layer), never user-supplied. Shared so the SDK throw-gate and
1683
+ * the contracts efficiency-only schema list the SAME kinds (they cannot drift). Passing one to
1684
+ * the builder throws `GovernanceMiddlewareRejectedError`. (The runtime enforcer on the Rust
1685
+ * side is independent: the bridge match only honours efficiency kinds and ignores these.)
1686
+ */
1687
+ declare const GOVERNANCE_MIDDLEWARE_KINDS: readonly ["redact", "approvalGate", "fsPolicy"];
1688
+ /** A filesystem permission verb (ADR 0024): `deny` < `read` < `gate` < `write`. */
1689
+ type FsPermVerb = "deny" | "read" | "write" | "gate";
1690
+ /**
1691
+ * A per-path filesystem permission rule (ADR 0024 phase 2b): a glob (`*` within a
1692
+ * path segment, `**` across) mapped to a verb. Compiled into the run's fail-closed
1693
+ * path policy ({@link GraphBuilder.fsPolicy}). An unmatched path resolves to `read`.
1694
+ */
1695
+ type FsPolicyRule = {
1696
+ glob: string;
1697
+ verb: FsPermVerb;
1698
+ };
1699
+ /**
1700
+ * Config for {@link GraphBuilder.mapAgents} — a dynamic fan-out (ADR 0027 phase 4b): run
1701
+ * `subAgent` once per item in the `overChannel` array, concurrently, and write the per-item
1702
+ * results — in input order (deterministic) — into `joinAt` as an array. Each spawn gets one item
1703
+ * as its input and shares the run's channels.
1704
+ */
1705
+ type MapAgentNodeConfig = {
1706
+ /** Channel holding the array of items to map the sub-agent over. */
1707
+ overChannel: string;
1708
+ /** The sub-agent to run per item (its own ReAct agent config). */
1709
+ subAgent: AgentNodeConfig;
1710
+ /** Channel the array of per-item results lands in (one entry per item, in input order). */
1711
+ joinAt: string;
1712
+ /** When true, a spawn that needs approval suspends the whole map (default false). */
1713
+ suspendForApproval?: boolean;
1714
+ label?: string;
1715
+ };
1716
+ /** The wire projection of a {@link MapAgentNodeConfig} the Rust bridge consumes (`map_agents`). */
1717
+ type RustMapAgentConfig = {
1718
+ overChannel: string;
1719
+ joinAt: string;
1720
+ agent: RustAgentConfig;
1721
+ suspendForApproval: boolean;
1722
+ };
1723
+ /** Config for {@link GraphBuilder.toolNode}. */
1724
+ type ToolNodeConfig = {
1725
+ tools: ToolRegistry;
1726
+ /** Execute all tool calls concurrently instead of sequentially. */
1727
+ parallel?: boolean;
1728
+ label?: string;
1729
+ };
1730
+ /**
1731
+ * Config for {@link GraphBuilder.taskNode} — spawn a sub-agent in an isolated context
1732
+ * that returns a single compressed report (ADR 0022/0023, phase 1). The spawn is sugar
1733
+ * over a one-node subgraph, so it inherits the runtime's guarantees: checkpointed,
1734
+ * audited, and human-gate-preserving (a sub-agent that suspends for approval suspends
1735
+ * the whole run).
1736
+ */
1737
+ type TaskNodeConfig = {
1738
+ /** The sub-agent to spawn (its own ReAct agent config). */
1739
+ subAgent: AgentNodeConfig;
1740
+ /**
1741
+ * The channel that feeds the sub-agent its objective — the ONLY channel projected
1742
+ * into the child (isolation). Default `"objective"`.
1743
+ */
1744
+ objectiveChannel?: string;
1745
+ /**
1746
+ * The channel the sub-agent's report lands in — the ONLY channel projected back to
1747
+ * the parent (a single value out). Default `"report"`.
1748
+ */
1749
+ reportChannel?: string;
1750
+ /**
1751
+ * Run the sub-agent with `outputStyle: "terse"` so the report is a summary, not a
1752
+ * full transcript. Default true.
1753
+ */
1754
+ compress?: boolean;
1755
+ label?: string;
1756
+ };
1757
+ /**
1758
+ * A tool's name plus its TS `execute` fn — the data the Rust bridge needs to back a
1759
+ * `jsToolName` with a JS callback. The Rust engine never imports the tool registry;
1760
+ * it calls this `execute` over the napi seam (`on_node` with `kind:"tool"`).
1761
+ */
1762
+ type RustToolBinding = {
1763
+ name: string;
1764
+ execute: (input: unknown) => Promise<unknown>;
1765
+ };
1766
+ /**
1767
+ * How one of an agent's tools is advertised to the LLM (→ Rust `AgentSpec.toolSpecs`): the tool's
1768
+ * `description` and the JSON Schema of its input (`ToolDefinition.jsonSchema`). Serializable, so
1769
+ * it also rides the persisted agent carrier.
1770
+ */
1771
+ type RustToolSpec = {
1772
+ name: string;
1773
+ description?: string;
1774
+ jsonSchema?: Record<string, unknown>;
1775
+ };
1776
+ /**
1777
+ * The serializable shape of an agent node, plus its JS-backed tool executes, that
1778
+ * the Rust engine bridge consumes (see `EngineSpec.agents` / `jsToolNames`). It is a
1779
+ * pure projection of {@link AgentNodeConfig} — the system prompt is the *resolved*
1780
+ * string (never a registry reference), since the bridge has no prompt registry.
1781
+ *
1782
+ * The LLM gateway itself is **not** carried: the Rust agent path builds its own
1783
+ * gateway (env adapters or a deterministic mock). A graph whose agents rely on a
1784
+ * specific TS `AgentNodeConfig.llm` therefore keeps its semantics only on the TS
1785
+ * engine; the Rust path is opt-in for agents (see `CompiledGraph`).
1786
+ */
1787
+ type RustAgentConfig = {
1788
+ provider: string;
1789
+ model?: string;
1790
+ /**
1791
+ * Abstract capability tier carried to the Rust `AgentSpec.tier`. When set with no
1792
+ * explicit `model`, the Rust bridge resolves the concrete model via `ModelPolicy`
1793
+ * against the process env. An explicit `model` always wins.
1794
+ */
1795
+ tier?: ModelTier;
1796
+ /**
1797
+ * A custom OpenAI-compatible endpoint (`model.openaiCompatible({ baseURL })`). The Rust bridge
1798
+ * serves the agent through the OpenAI-wire adapter at this URL, with the key read only from
1799
+ * {@link apiKeyEnv} (never `OPENAI_API_KEY`).
1800
+ */
1801
+ baseURL?: string;
1802
+ /** Env var holding the key for {@link baseURL}; unset means a keyless endpoint. */
1803
+ apiKeyEnv?: string;
1804
+ /** Resolved system prompt string. */
1805
+ system?: string;
1806
+ toolNames: string[];
1807
+ /** What the LLM is told about each tool: its description and input JSON Schema. */
1808
+ toolSpecs?: RustToolSpec[];
1809
+ maxIterations?: number;
1810
+ suspendForApproval: boolean;
1811
+ /** Tools (by name) requiring approval — those marked `requiresApproval`. */
1812
+ approvalToolNames: string[];
1813
+ outputChannel: string;
1814
+ /** ADR 0014 — terse output directive on the system prompt. */
1815
+ outputStyle?: "terse";
1816
+ /** ADR 0014 — cap (chars) on the injected seed message (the `Input`/`State` dump). */
1817
+ contextBudget?: number;
1818
+ /** ADR 0022/0023 — durable channel the `writeTodos` list is persisted into. */
1819
+ todosChannel?: string;
1820
+ /** ADR 0030 phase 9e — channel carrying the run's multimodal input blocks. */
1821
+ inputBlocksChannel?: string;
1822
+ /** The only channels the agent is shown in its seed state (context isolation); default all. */
1823
+ visibleChannels?: string[];
1824
+ /** ADR 0026 phase 11 — governed long-term memory overlay. */
1825
+ memory?: MemoryConfig;
1826
+ /** ADR 0035 phase 12 — governed skills (progressive disclosure) overlay. */
1827
+ skills?: SkillConfig;
1828
+ /** ADR 0024 phase 2b — opt this agent into the governed virtual filesystem tools. */
1829
+ enableFs?: boolean;
1830
+ /**
1831
+ * ADR 0025 phase 3d — the SDK-resolved EFFICIENCY middleware list: the profile + explicit
1832
+ * `middleware[]` + the legacy `outputStyle`/`contextBudget` knobs expanded into one ordered
1833
+ * list of `{ kind, params }` data entries the Rust bridge turns into `push_efficiency`
1834
+ * calls. The governed layer is never carried here (the bridge injects it). Always present
1835
+ * (possibly empty) on the live builder path; absent on a pre-3d persisted carrier (the Rust
1836
+ * bridge then falls back to the legacy flat knobs).
1837
+ */
1838
+ resolvedMiddleware?: EfficiencyMiddlewareSpec[];
1839
+ /** JS-backed tool executes, one per tool in the registry. */
1840
+ toolBindings: RustToolBinding[];
1841
+ /**
1842
+ * SDK-only (never serialized to the wire): whether this agent node sets the removed
1843
+ * `approvalEngine` option, which makes the graph fail to compile (see `CompiledGraph`).
1844
+ */
1845
+ usesApprovalEngine: boolean;
1846
+ };
1847
+ /**
1848
+ * The approval binding an agent node contributes to {@link CompiledGraph}: the principal that
1849
+ * *requests* approvals on this node's behalf (`config.name ?? nodeId`) and the names of its
1850
+ * approval-gated tools. {@link CompiledGraph.approveAndResume} uses it to stamp each granted
1851
+ * tool's `requestedBy` for the engine's no-self-approval check.
1852
+ */
1853
+ type AgentApprovalBinding = {
1854
+ approvalEngine?: ApprovalEngine;
1855
+ requestedBy: string;
1856
+ approvalToolNames: string[];
1857
+ };
1858
+ /** Project an {@link AgentNodeConfig} into its {@link AgentApprovalBinding}. */
1859
+ declare const toAgentApprovalBinding: (nodeId: string, config: AgentNodeConfig) => AgentApprovalBinding;
1860
+ /**
1861
+ * Project an {@link AgentNodeConfig} into the {@link RustAgentConfig} the Rust engine
1862
+ * bridge consumes. Resolves the system prompt to a concrete string, pulls the tool
1863
+ * names / approval flags / executes out of the registry, and desugars the profile +
1864
+ * middleware into a {@link RustAgentConfig.resolvedMiddleware} list. Pure — no LLM call.
1865
+ */
1866
+ declare const toRustAgentConfig: (nodeId: string, config: AgentNodeConfig) => RustAgentConfig;
1867
+ /** Config for {@link streamAgentTokens}. */
1868
+ type StreamAgentConfig = {
1869
+ llm: LLMGateway;
1870
+ prompt: AgentPromptSource;
1871
+ provider?: LLMProvider;
1872
+ model?: string;
1873
+ };
1874
+ /**
1875
+ * Stream an agent's reply token by token through the gateway's `stream()`. This is
1876
+ * the single-turn (no-tools) path — ideal for a chat UI that wants live output.
1877
+ * Yields text deltas as they arrive and returns when the provider signals done.
1878
+ *
1879
+ * ```ts
1880
+ * for await (const delta of streamAgentTokens({ llm, prompt: { system } }, "Bonjour ?")) {
1881
+ * process.stdout.write(delta);
1882
+ * }
1883
+ * ```
1884
+ */
1885
+ declare function streamAgentTokens(config: StreamAgentConfig, input: unknown): AsyncIterable<string>;
1886
+ declare const createAgentNodeHandler: (nodeId: string, config: AgentNodeConfig) => NodeHandler;
1887
+ /**
1888
+ * Build the handler for a tool node: executes the tool calls emitted by the last
1889
+ * AI message in the `messages` channel. Tools flagged `requiresApproval` suspend
1890
+ * the run via a dynamic interrupt instead of executing.
1891
+ */
1892
+ declare const createToolNodeHandler: (config: ToolNodeConfig) => NodeHandler;
1893
+
1894
+ /**
1895
+ * The component library surface: pure (no-LLM) compute building blocks addressable
1896
+ * by a string `kind` plus a `params` object. Mirrors the Rust `ailu_components`
1897
+ * library (`crates/components`) one-for-one in kind, params and behaviour.
1898
+ *
1899
+ * Each factory in {@link components} returns a {@link ComponentDescriptor}: the Phase
1900
+ * C carrier the Rust engine needs (`{ kind, params }`, surfaced as the graph's
1901
+ * `componentNodes` map) **and** a faithful TypeScript `handler` for the fallback path
1902
+ * when the native addon is absent. The components are simple and pure, so the two
1903
+ * implementations agree byte-for-byte on ASCII input.
1904
+ *
1905
+ * Use {@link GraphBuilder.component} to push a node carrying both:
1906
+ *
1907
+ * ```ts
1908
+ * import { createGraph, components } from "@ailu-ai/graph-sdk";
1909
+ *
1910
+ * const app = createGraph({ name: "prompt" })
1911
+ * .channel("name", { type: "string", default: "" })
1912
+ * .channel("prompt", { type: "string", default: "" })
1913
+ * .component("build", components.promptBuilder({ template: "Hi {{name}}", into: "prompt" }))
1914
+ * .compile();
1915
+ * ```
1916
+ */
1917
+
1918
+ /** The component kinds the library knows, matching `ComponentRegistry::kinds()`. */
1919
+ type ComponentKind = "promptBuilder" | "jsonValidator" | "outputParser" | "router" | "retriever" | "reranker" | "textCleaner" | "documentSplitter" | "htmlToText" | "csvParser" | "documentJoiner" | "deduplicator" | "truncator" | "regexExtractor" | "answerBuilder" | "fieldMapper" | "fieldExtractor" | "bm25Retriever" | "keywordRetriever" | "sentenceWindowSplitter" | "languageDetector" | "metadataFilter" | "listJoiner" | "mergeRanker" | "evaluator" | "chatMessageBuilder" | "conditionalRouter" | "documentWriter";
1920
+ /**
1921
+ * The serializable projection of a component node the Rust engine bridge consumes
1922
+ * (the Phase C `componentNodes` carrier, camelCase): a component `kind` plus its
1923
+ * validated `params` object. Pure data — no closures.
1924
+ */
1925
+ type RustComponentConfig = {
1926
+ kind: ComponentKind;
1927
+ params: Record<string, unknown>;
1928
+ };
1929
+ /**
1930
+ * What a component factory returns: the Phase C carrier (`kind` + `params`) so the
1931
+ * node runs natively on Rust, plus an equivalent TS {@link NodeHandler} for the
1932
+ * fallback path. {@link GraphBuilder.component} pushes a node from this descriptor.
1933
+ */
1934
+ type ComponentDescriptor = RustComponentConfig & {
1935
+ /** The faithful TS-equivalent handler used when the native addon is absent. */
1936
+ handler: NodeHandler;
1937
+ };
1938
+ /**
1939
+ * What an **integration component** factory returns (the vendor-I/O pattern, e.g.
1940
+ * {@link components.httpFetch} / {@link components.webSearch}). Unlike a
1941
+ * {@link ComponentDescriptor}, an integration component is **not** a Rust component:
1942
+ * it has no `kind`/`params` carrier and is **not** registered in `componentNodes`.
1943
+ * It is a plain `NodeHandler` (an injectable closure over an injected I/O impl) added
1944
+ * as a regular JS node via {@link import("./builder.js").GraphBuilder.node}; on the
1945
+ * Rust engine it runs over the async JS seam (`on_node`) like any other JS handler.
1946
+ *
1947
+ * ```ts
1948
+ * createGraph({ name: "fetch" })
1949
+ * .channel("body", { type: "json", default: null })
1950
+ * .node("get", components.httpFetch({ url: "https://x", into: "body", fetchImpl: fake }));
1951
+ * ```
1952
+ */
1953
+ type IntegrationComponentHandler = NodeHandler;
1954
+ /** ADR 0044 D3 (ailu#578) capsule-assembly config for {@link components.promptBuilder}.
1955
+ * `chunksFrom`/`queryFrom` are required, `discardedFrom` is optional (defaults to none) — a
1956
+ * capsule-assembly node needs to be told explicitly which upstream channel holds the query and
1957
+ * which channels hold this pipeline's discarded candidates (they can be scattered across several
1958
+ * stages — D2's `{into}Discarded` channels are stage-local, not a single sibling of `chunksFrom`). */
1959
+ type PromptBuilderCapsuleConfig = {
1960
+ into: string;
1961
+ chunksFrom: string;
1962
+ queryFrom: string;
1963
+ discardedFrom?: string[];
1964
+ };
1965
+ /** Params for {@link components.promptBuilder}. */
1966
+ type PromptBuilderParams = {
1967
+ /** Template with `{{var}}` placeholders filled from the channels. */
1968
+ template: string;
1969
+ /** Channel the rendered string is written into. */
1970
+ into: string;
1971
+ /** Optional retrieval-proof-capsule assembly (ADR 0044 D3) — captures the exact set that
1972
+ * influenced the model, hashed at the ONE node that knows what text was actually templated. */
1973
+ capsule?: PromptBuilderCapsuleConfig;
1974
+ };
1975
+ /** Params for {@link components.jsonValidator}. */
1976
+ type JsonValidatorParams = {
1977
+ /** Channel whose value is validated. */
1978
+ from: string;
1979
+ /** Required object keys to assert present. */
1980
+ requiredKeys?: string[];
1981
+ /** Expected JSON type (`"object" | "array" | "string" | ...`). */
1982
+ expectType?: "string" | "number" | "boolean" | "object" | "array" | "null";
1983
+ /** Channel receiving the `boolean` validity flag. */
1984
+ okInto: string;
1985
+ /** Channel receiving the `string[]` of validation errors. */
1986
+ errorsInto: string;
1987
+ };
1988
+ /** Params for {@link components.outputParser}. */
1989
+ type OutputParserParams = {
1990
+ /** Text channel to extract the first JSON value from. */
1991
+ from: string;
1992
+ /** Channel receiving the parsed value (or `null` when none is found). */
1993
+ into: string;
1994
+ };
1995
+ /** One routing rule for {@link components.router}. */
1996
+ type RouterRule = {
1997
+ /** Exact match against the textual form of the `from` value. */
1998
+ equals?: string;
1999
+ /** Substring match against the textual form of the `from` value. */
2000
+ contains?: string;
2001
+ /** The route string emitted when this rule matches. */
2002
+ route: string;
2003
+ };
2004
+ /** Params for {@link components.router}. */
2005
+ type RouterParams = {
2006
+ /** Channel whose value is matched against the rules. */
2007
+ from: string;
2008
+ /** Ordered rules; the first match wins. */
2009
+ rules: RouterRule[];
2010
+ /** Route emitted when no rule matches. */
2011
+ defaultRoute: string;
2012
+ /** Channel the chosen route string is written into. */
2013
+ into: string;
2014
+ };
2015
+ /** A candidate document for {@link components.retriever}. */
2016
+ type RetrieverDoc = {
2017
+ id: string;
2018
+ content: string;
2019
+ };
2020
+ /** Params for {@link components.retriever}. */
2021
+ type RetrieverParams = {
2022
+ /** Channel holding the query text (falls back to this literal when the channel is empty). */
2023
+ query: string;
2024
+ /** Channel receiving the top-`k` `{ id, content, score }` array. */
2025
+ into: string;
2026
+ /** Number of results to keep (default 4). */
2027
+ k?: number;
2028
+ /** The corpus to score against. */
2029
+ docs: RetrieverDoc[];
2030
+ };
2031
+ /** Params for {@link components.reranker}. */
2032
+ type RerankerParams = {
2033
+ /** Channel holding the retrieval-result array to reorder. */
2034
+ from: string;
2035
+ /** Channel receiving the reordered array. */
2036
+ into: string;
2037
+ /** Optional channel holding the query text the cross-encoder (`AILU_RERANK_ENDPOINT`)
2038
+ * re-scores against. Without an endpoint, items keep their upstream order (by `score`). */
2039
+ query?: string;
2040
+ };
2041
+ /** Params for {@link components.textCleaner}. */
2042
+ type TextCleanerParams = {
2043
+ /** Channel whose text is normalised. */
2044
+ from: string;
2045
+ /** Channel receiving the cleaned text. */
2046
+ into: string;
2047
+ /** Lowercase the text. Defaults to `false`. */
2048
+ lowercase?: boolean;
2049
+ /** Strip `<…>` HTML tags. Defaults to `false`. */
2050
+ stripHtml?: boolean;
2051
+ /** Collapse runs of whitespace to a single space. Defaults to `false`. */
2052
+ collapseWhitespace?: boolean;
2053
+ /** Trim leading/trailing whitespace. Defaults to `false`. */
2054
+ trim?: boolean;
2055
+ };
2056
+ /** Params for {@link components.documentSplitter}. */
2057
+ type DocumentSplitterParams = {
2058
+ /** Channel holding the text to split. */
2059
+ from: string;
2060
+ /** Channel receiving the `string[]` of chunks. */
2061
+ into: string;
2062
+ /** Split unit: `"chars"` sliding windows or greedy `"sentences"` packing. */
2063
+ by: "chars" | "sentences";
2064
+ /** Window size in characters (`by:"chars"`) or sentences (`by:"sentences"`). Must be > 0. */
2065
+ size: number;
2066
+ /** Overlap repeated at the start of each next chunk. Must be smaller than `size`. Defaults to 0. */
2067
+ overlap?: number;
2068
+ };
2069
+ /** Params for {@link components.htmlToText}. */
2070
+ type HtmlToTextParams = {
2071
+ /** Channel holding the HTML text. */
2072
+ from: string;
2073
+ /** Channel receiving the tag-stripped, entity-decoded text. */
2074
+ into: string;
2075
+ };
2076
+ /** Params for {@link components.csvParser}. */
2077
+ type CsvParserParams = {
2078
+ /** Channel holding the CSV text. */
2079
+ from: string;
2080
+ /** Channel receiving the parsed rows array. */
2081
+ into: string;
2082
+ /** Single-character cell delimiter. Defaults to `","`. */
2083
+ delimiter?: string;
2084
+ /** When `true` (default) the first row supplies object keys; otherwise rows are arrays. */
2085
+ header?: boolean;
2086
+ };
2087
+ /** Params for {@link components.documentJoiner}. */
2088
+ type DocumentJoinerParams = {
2089
+ /** Channels whose array values are concatenated in order. */
2090
+ fromChannels: string[];
2091
+ /** Channel receiving the merged array. */
2092
+ into: string;
2093
+ /** Optional object field to de-duplicate the merged items by. */
2094
+ dedupeBy?: string;
2095
+ };
2096
+ /** Params for {@link components.deduplicator}. */
2097
+ type DeduplicatorParams = {
2098
+ /** Channel holding the array to de-duplicate. */
2099
+ from: string;
2100
+ /** Channel receiving the de-duplicated array. */
2101
+ into: string;
2102
+ /** Optional object field to compare items by (else whole-value compare). */
2103
+ key?: string;
2104
+ };
2105
+ /** Params for {@link components.truncator}. */
2106
+ type TruncatorParams = {
2107
+ /** Channel holding the text to truncate. */
2108
+ from: string;
2109
+ /** Channel receiving the (possibly truncated) text. */
2110
+ into: string;
2111
+ /** Maximum character length (the ellipsis counts against this budget). */
2112
+ maxChars: number;
2113
+ /** Suffix appended when truncated. Defaults to `"…"`. */
2114
+ ellipsis?: string;
2115
+ };
2116
+ /** Params for {@link components.regexExtractor}. */
2117
+ type RegexExtractorParams = {
2118
+ /** Channel holding the text to match against. */
2119
+ from: string;
2120
+ /** Channel receiving the match (or matches when `all`). */
2121
+ into: string;
2122
+ /**
2123
+ * Literal-substring pattern with optional leading `^` (start) and trailing `$`
2124
+ * (end) anchors. No character classes / quantifiers / capture groups.
2125
+ */
2126
+ pattern: string;
2127
+ /** Accepted for forward-compat; only `0` (the whole match) is supported. Defaults to 0. */
2128
+ group?: number;
2129
+ /** When `true`, return every non-overlapping occurrence as an array. Defaults to `false`. */
2130
+ all?: boolean;
2131
+ };
2132
+ /** Params for {@link components.answerBuilder}. */
2133
+ type AnswerBuilderParams = {
2134
+ /** Channel supplying the core answer text. */
2135
+ from: string;
2136
+ /** Channel receiving the assembled answer. */
2137
+ into: string;
2138
+ /** Optional channel holding a retrieval-result array rendered as numbered citations. */
2139
+ contextFrom?: string;
2140
+ /** Optional `{{answer}}`/`{{citations}}` template controlling the layout. */
2141
+ template?: string;
2142
+ };
2143
+ /** Params for {@link components.fieldMapper}. */
2144
+ type FieldMapperParams = {
2145
+ /** Channel holding the source object. */
2146
+ from: string;
2147
+ /** Channel receiving the remapped object. */
2148
+ into: string;
2149
+ /** `{ outKey: inKeyPath }` map; `inKeyPath` is a dotted path into the source. */
2150
+ mapping: Record<string, string>;
2151
+ };
2152
+ /** Params for {@link components.fieldExtractor}. */
2153
+ type FieldExtractorParams = {
2154
+ /** Channel holding the source value. */
2155
+ from: string;
2156
+ /** Channel receiving the extracted scalar. */
2157
+ into: string;
2158
+ /** Optional dotted path descended into the `from` value (else the whole value). */
2159
+ path?: string;
2160
+ /**
2161
+ * When `true`, if the resulting value is a string containing a `"final:"` marker,
2162
+ * return only the text after the **last** `"final:"` (trimmed) — reduces an
2163
+ * agent reasoning trace to its final answer. Non-strings pass through. Defaults to `false`.
2164
+ */
2165
+ finalOnly?: boolean;
2166
+ };
2167
+ /** A candidate document for the lexical retrievers. */
2168
+ type LexicalDoc = {
2169
+ id: string;
2170
+ content: string;
2171
+ };
2172
+ /** Params for {@link components.bm25Retriever}. */
2173
+ type Bm25RetrieverParams = {
2174
+ /** Channel holding the query text (falls back to this literal when empty). */
2175
+ query: string;
2176
+ /** Channel receiving the top-`k` `{ id, content, score }` array. */
2177
+ into: string;
2178
+ /** Number of results to keep (default 4). */
2179
+ k?: number;
2180
+ /** The corpus to rank. */
2181
+ docs: LexicalDoc[];
2182
+ /** BM25 term-frequency saturation. Defaults to 1.2. */
2183
+ k1?: number;
2184
+ /** BM25 length-normalization. Defaults to 0.75. */
2185
+ b?: number;
2186
+ };
2187
+ /** Params for {@link components.keywordRetriever}. */
2188
+ type KeywordRetrieverParams = {
2189
+ /** Channel holding the query text (falls back to this literal when empty). */
2190
+ query: string;
2191
+ /** Channel receiving the top-`k` `{ id, content, score }` array. */
2192
+ into: string;
2193
+ /** Number of results to keep (default 4). */
2194
+ k?: number;
2195
+ /** The corpus to rank. */
2196
+ docs: LexicalDoc[];
2197
+ };
2198
+ /** Params for {@link components.sentenceWindowSplitter}. */
2199
+ type SentenceWindowSplitterParams = {
2200
+ /** Channel holding the text to split. */
2201
+ from: string;
2202
+ /** Channel receiving the `string[]` of overlapping sentence windows. */
2203
+ into: string;
2204
+ /** Sentences per window. Defaults to 3. */
2205
+ windowSize?: number;
2206
+ /** Sentences advanced between windows (`1 <= stride <= windowSize`). Defaults to 1. */
2207
+ stride?: number;
2208
+ };
2209
+ /** Params for {@link components.languageDetector}. */
2210
+ type LanguageDetectorParams = {
2211
+ /** Channel holding the text to classify. */
2212
+ from: string;
2213
+ /** Channel receiving the detected language code (`"en" | "fr" | ... | "und"`). */
2214
+ into: string;
2215
+ /** Optional channel receiving the winning language's share of hits in `[0, 1]`. */
2216
+ confidenceInto?: string;
2217
+ };
2218
+ /** A predicate operator shared by {@link components.metadataFilter} and {@link components.conditionalRouter}. */
2219
+ type PredicateOp = "equals" | "notEquals" | "contains" | "exists" | "absent" | "gt" | "gte" | "lt" | "lte";
2220
+ /** Params for {@link components.metadataFilter}. */
2221
+ type MetadataFilterParams = {
2222
+ /** Channel holding the array to filter. */
2223
+ from: string;
2224
+ /** Channel receiving the filtered array. */
2225
+ into: string;
2226
+ /** Dotted path into each item compared by the predicate. */
2227
+ field: string;
2228
+ /** The predicate operator. */
2229
+ op: PredicateOp;
2230
+ /** The comparison value (required except for `exists`/`absent`). */
2231
+ value?: unknown;
2232
+ };
2233
+ /** Params for {@link components.listJoiner}. */
2234
+ type ListJoinerParams = {
2235
+ /** Channels whose array values are combined. */
2236
+ fromChannels: string[];
2237
+ /** Channel receiving the combined array. */
2238
+ into: string;
2239
+ /** Combine mode: `"concat"` (default), `"union"` (dedupe), or `"interleave"`. */
2240
+ mode?: "concat" | "union" | "interleave";
2241
+ };
2242
+ /** Params for {@link components.mergeRanker}. */
2243
+ type MergeRankerParams = {
2244
+ /** Channels each holding a retrieval-result array to fuse. */
2245
+ fromChannels: string[];
2246
+ /** Channel receiving the fused `{ id, content, score }` array. */
2247
+ into: string;
2248
+ /** Object field identifying items across lists. Defaults to `"id"`. */
2249
+ idKey?: string;
2250
+ /** Keep only the top-`k` fused results (default: keep all). */
2251
+ k?: number;
2252
+ /** Reciprocal Rank Fusion constant. Defaults to 60. */
2253
+ rrfK?: number;
2254
+ };
2255
+ /** Params for {@link components.evaluator}. */
2256
+ type EvaluatorParams = {
2257
+ /** Channel holding the expected/reference text. */
2258
+ expectedFrom: string;
2259
+ /** Channel holding the actual/candidate text. */
2260
+ actualFrom: string;
2261
+ /** Channel receiving the numeric score in `[0, 1]`. */
2262
+ into: string;
2263
+ /** Scoring metric. Defaults to `"tokenF1"`. */
2264
+ metric?: "tokenF1" | "overlap" | "exact";
2265
+ /** Optional channel receiving a boolean `score >= threshold`. */
2266
+ passInto?: string;
2267
+ /** Pass threshold for `passInto`. Defaults to 0.5. */
2268
+ threshold?: number;
2269
+ };
2270
+ /** One message spec for {@link components.chatMessageBuilder}. */
2271
+ type ChatMessageSpec = {
2272
+ /** The message role. */
2273
+ role: "system" | "user" | "assistant";
2274
+ /** A literal body, rendered through the `{{var}}` template engine. */
2275
+ content?: string;
2276
+ /** A channel name whose value supplies the body verbatim. */
2277
+ contentFrom?: string;
2278
+ };
2279
+ /** Params for {@link components.chatMessageBuilder}. */
2280
+ type ChatMessageBuilderParams = {
2281
+ /** Channel receiving the `[{ role, content }]` array. */
2282
+ into: string;
2283
+ /** The ordered message specs. */
2284
+ messages: ChatMessageSpec[];
2285
+ /** Optional channel prepended as a leading system message when non-empty. */
2286
+ systemFrom?: string;
2287
+ };
2288
+ /** One branch for {@link components.conditionalRouter}. */
2289
+ type ConditionalRouterBranch = {
2290
+ /** The predicate evaluated against the channels (`field` is a dotted path). */
2291
+ when: {
2292
+ field: string;
2293
+ op: PredicateOp;
2294
+ value?: unknown;
2295
+ };
2296
+ /** The route string emitted when `when` holds. */
2297
+ route: string;
2298
+ };
2299
+ /** Params for {@link components.conditionalRouter}. */
2300
+ type ConditionalRouterParams = {
2301
+ /** Channel the chosen route string is written into. */
2302
+ into: string;
2303
+ /** Route emitted when no branch matches. */
2304
+ defaultRoute: string;
2305
+ /** Ordered branches; the first matching branch wins. */
2306
+ branches: ConditionalRouterBranch[];
2307
+ };
2308
+ /** Params for {@link components.documentWriter}. */
2309
+ type DocumentWriterParams = {
2310
+ /** Channel holding the incoming documents array to append. */
2311
+ from: string;
2312
+ /** Channel receiving the accumulated store array. */
2313
+ into: string;
2314
+ /** Channel holding the current store. Defaults to `into`. */
2315
+ store?: string;
2316
+ /** Optional object field to de-duplicate the merged store by. */
2317
+ dedupeBy?: string;
2318
+ };
2319
+ /**
2320
+ * The result of an HTTP fetch surfaced into the `into` channel.
2321
+ *
2322
+ * The default impl never throws on a non-2xx response or a transport error — a
2323
+ * failure is surfaced as data so a graph degrades gracefully instead of crashing
2324
+ * the run:
2325
+ * - on a completed response: `{ status, ok, body, json }` (`json` is the parsed
2326
+ * body when the `content-type` is JSON, else `undefined`; `ok` mirrors
2327
+ * `Response.ok`, i.e. a 2xx status);
2328
+ * - on a transport error / timeout: `{ ok: false, error }` (`status`/`body` absent).
2329
+ */
2330
+ type HttpFetchResult = {
2331
+ /** The HTTP status code, when a response was received. */
2332
+ status?: number;
2333
+ /** `true` for a 2xx response; `false` on a non-2xx response or a transport error. */
2334
+ ok: boolean;
2335
+ /** The response body as text, when a response was received. */
2336
+ body?: string;
2337
+ /** The parsed JSON body, present only when the response `content-type` was JSON. */
2338
+ json?: unknown;
2339
+ /** The error message, present only on a transport error / timeout. */
2340
+ error?: string;
2341
+ };
2342
+ /**
2343
+ * The transport an {@link HttpFetchImpl} is invoked with: the resolved URL plus the
2344
+ * request options the default impl assembled from the params (method/headers/body/
2345
+ * the abort `signal` driving the timeout). Mirrors the `(input, init)` shape of the
2346
+ * WHATWG `fetch`, so `globalThis.fetch` is itself a valid impl.
2347
+ */
2348
+ type HttpFetchRequestInit = {
2349
+ method: string;
2350
+ headers?: Record<string, string>;
2351
+ body?: string;
2352
+ signal?: AbortSignal;
2353
+ };
2354
+ /**
2355
+ * An injectable fetch implementation. Receives the resolved URL and the assembled
2356
+ * request init, and must resolve to something `Response`-shaped (`status`, `ok`,
2357
+ * `text()`, `headers.get()`). The real `globalThis.fetch` satisfies this — the
2358
+ * default impl simply calls it — so a test can inject a fake `Response`-like object.
2359
+ */
2360
+ type HttpFetchImpl = (url: string, init: HttpFetchRequestInit) => Promise<HttpFetchResponseLike> | HttpFetchResponseLike;
2361
+ /** The minimal `Response`-shaped surface the default httpFetch impl consumes. */
2362
+ type HttpFetchResponseLike = {
2363
+ status: number;
2364
+ ok: boolean;
2365
+ text(): Promise<string> | string;
2366
+ headers: {
2367
+ get(name: string): string | null;
2368
+ };
2369
+ };
2370
+ /** Params for {@link components.httpFetch}. */
2371
+ type HttpFetchParams = {
2372
+ /** A literal URL to fetch (mutually exclusive with `urlFrom`). */
2373
+ url?: string;
2374
+ /** A channel whose value supplies the URL (takes precedence when its channel is set). */
2375
+ urlFrom?: string;
2376
+ /** Channel receiving the {@link HttpFetchResult}. */
2377
+ into: string;
2378
+ /** HTTP method. Defaults to `"GET"`. */
2379
+ method?: string;
2380
+ /** Request headers sent with the call. */
2381
+ headers?: Record<string, string>;
2382
+ /** Request body (sent verbatim) for non-GET methods. */
2383
+ body?: string;
2384
+ /** Abort the request after this many milliseconds (drives an `AbortController`). */
2385
+ timeoutMs?: number;
2386
+ /**
2387
+ * The transport to call. Defaults to the real `globalThis.fetch`. Inject a fake
2388
+ * (a `Response`-like returning function) to keep a test offline, or to point the
2389
+ * call at a stub transport.
2390
+ */
2391
+ fetchImpl?: HttpFetchImpl;
2392
+ };
2393
+ /** One web-search result: a title, a url and a snippet. */
2394
+ type WebSearchResult = {
2395
+ title: string;
2396
+ url: string;
2397
+ snippet: string;
2398
+ };
2399
+ /**
2400
+ * What the web-search connector writes into the `into` channel: the normalized
2401
+ * `results` plus an optional `note`. The default connector populates `note` when it
2402
+ * degrades gracefully (e.g. no `TAVILY_API_KEY`), leaving `results` empty so a graph
2403
+ * runs without crashing.
2404
+ */
2405
+ type WebSearchOutcome = {
2406
+ results: WebSearchResult[];
2407
+ note?: string;
2408
+ };
2409
+ /**
2410
+ * An injectable web-search implementation. Receives the query + `k`, returns either a
2411
+ * bare `WebSearchResult[]` (which is wrapped as `{ results }`) or a full
2412
+ * {@link WebSearchOutcome} (so an impl can attach its own `note`).
2413
+ */
2414
+ type WebSearchImpl = (query: string, k: number) => Promise<WebSearchResult[] | WebSearchOutcome> | WebSearchResult[] | WebSearchOutcome;
2415
+ /**
2416
+ * The minimal transport the default Tavily connector posts through: a function with
2417
+ * the WHATWG `fetch` `(url, init)` shape resolving to a `Response`-like object. The
2418
+ * real `globalThis.fetch` satisfies it; a test injects a fake to stay offline.
2419
+ */
2420
+ type WebSearchTransport = (url: string, init: {
2421
+ method: string;
2422
+ headers: Record<string, string>;
2423
+ body: string;
2424
+ }) => Promise<HttpFetchResponseLike> | HttpFetchResponseLike;
2425
+ /** Params for {@link components.webSearch}. */
2426
+ type WebSearchParams = {
2427
+ /** A literal query (mutually exclusive with `queryFrom`). */
2428
+ query?: string;
2429
+ /** A channel whose value supplies the query (takes precedence when its channel is set). */
2430
+ queryFrom?: string;
2431
+ /** Channel receiving the {@link WebSearchOutcome}. */
2432
+ into: string;
2433
+ /** Number of results to request. Defaults to 3. */
2434
+ k?: number;
2435
+ /**
2436
+ * The search implementation to call. Defaults to a real Tavily connector behind the
2437
+ * `TAVILY_API_KEY` env var: when the key is set it POSTs to the Tavily API; when it
2438
+ * is absent it degrades gracefully (no network, empty results + a note). Inject a
2439
+ * fake to keep a test offline or to point at another provider.
2440
+ */
2441
+ searchImpl?: WebSearchImpl;
2442
+ /**
2443
+ * The HTTP transport the default Tavily connector posts through. Defaults to the real
2444
+ * `globalThis.fetch`. Inject a fake to exercise the connector offline. Ignored when
2445
+ * `searchImpl` is supplied.
2446
+ */
2447
+ transport?: WebSearchTransport;
2448
+ };
2449
+ /**
2450
+ * The component factory surface. Each factory validates nothing here (the Rust
2451
+ * `ComponentRegistry::build_handler` validates `params` at graph-build time, and the
2452
+ * TS handlers tolerate the same shapes), returning a {@link ComponentDescriptor}
2453
+ * carrying both the native carrier and a faithful TS handler.
2454
+ */
2455
+ declare const components: {
2456
+ /** Render `{{var}}` placeholders from the channels into a target channel. */
2457
+ readonly promptBuilder: (params: PromptBuilderParams) => ComponentDescriptor;
2458
+ /** Validate a channel value's type / required keys, writing an ok flag + errors. */
2459
+ readonly jsonValidator: (params: JsonValidatorParams) => ComponentDescriptor;
2460
+ /** Extract the first JSON object/array from a text channel into a target channel. */
2461
+ readonly outputParser: (params: OutputParserParams) => ComponentDescriptor;
2462
+ /** Pick a route string from a channel value (pairs with a conditional edge). */
2463
+ readonly router: (params: RouterParams) => ComponentDescriptor;
2464
+ /** Score candidate docs against a query and keep the top-`k`. */
2465
+ readonly retriever: (params: RetrieverParams) => ComponentDescriptor;
2466
+ /** Reorder a retrieval-result array, optionally re-scoring against a query. */
2467
+ readonly reranker: (params: RerankerParams) => ComponentDescriptor;
2468
+ /** Normalise a text channel: strip HTML, lowercase, collapse whitespace, trim. */
2469
+ readonly textCleaner: (params: TextCleanerParams) => ComponentDescriptor;
2470
+ /** Split a text channel into an array of chunk strings by chars or sentences. */
2471
+ readonly documentSplitter: (params: DocumentSplitterParams) => ComponentDescriptor;
2472
+ /** Strip HTML tags from a text channel and decode the common named entities. */
2473
+ readonly htmlToText: (params: HtmlToTextParams) => ComponentDescriptor;
2474
+ /** Parse a CSV text channel into an array of row objects (or arrays). */
2475
+ readonly csvParser: (params: CsvParserParams) => ComponentDescriptor;
2476
+ /** Concatenate the array values across several channels into one merged array. */
2477
+ readonly documentJoiner: (params: DocumentJoinerParams) => ComponentDescriptor;
2478
+ /** De-duplicate an array channel, keeping the first occurrence and order. */
2479
+ readonly deduplicator: (params: DeduplicatorParams) => ComponentDescriptor;
2480
+ /** Truncate a text channel to at most `maxChars` characters with an ellipsis. */
2481
+ readonly truncator: (params: TruncatorParams) => ComponentDescriptor;
2482
+ /** Extract literal-pattern matches (with `^`/`$` anchors) from a text channel. */
2483
+ readonly regexExtractor: (params: RegexExtractorParams) => ComponentDescriptor;
2484
+ /** Assemble a final answer string, optionally appending numbered citations. */
2485
+ readonly answerBuilder: (params: AnswerBuilderParams) => ComponentDescriptor;
2486
+ /** Remap an object channel's fields (by dotted path) into a new object. */
2487
+ readonly fieldMapper: (params: FieldMapperParams) => ComponentDescriptor;
2488
+ /** Extract a scalar from a channel (optional dotted path; finalOnly reduces an agent trace to its final answer). */
2489
+ readonly fieldExtractor: (params: FieldExtractorParams) => ComponentDescriptor;
2490
+ /** Lexical BM25 ranking of a corpus against a query; keep the top-`k`. */
2491
+ readonly bm25Retriever: (params: Bm25RetrieverParams) => ComponentDescriptor;
2492
+ /** Keyword-overlap ranking of a corpus against a query; keep the top-`k`. */
2493
+ readonly keywordRetriever: (params: KeywordRetrieverParams) => ComponentDescriptor;
2494
+ /** Split text into overlapping windows of whole sentences. */
2495
+ readonly sentenceWindowSplitter: (params: SentenceWindowSplitterParams) => ComponentDescriptor;
2496
+ /** Heuristic language detection over a small set of common languages. */
2497
+ readonly languageDetector: (params: LanguageDetectorParams) => ComponentDescriptor;
2498
+ /** Filter an array channel by a dotted-path metadata predicate. */
2499
+ readonly metadataFilter: (params: MetadataFilterParams) => ComponentDescriptor;
2500
+ /** Combine several array channels by concat / union / interleave. */
2501
+ readonly listJoiner: (params: ListJoinerParams) => ComponentDescriptor;
2502
+ /** Fuse several retrieval streams into one ranking with Reciprocal Rank Fusion. */
2503
+ readonly mergeRanker: (params: MergeRankerParams) => ComponentDescriptor;
2504
+ /** Score actual vs expected text (token-F1 / overlap / exact), with an optional pass flag. */
2505
+ readonly evaluator: (params: EvaluatorParams) => ComponentDescriptor;
2506
+ /** Assemble a role-tagged chat-message array an LLM generator consumes. */
2507
+ readonly chatMessageBuilder: (params: ChatMessageBuilderParams) => ComponentDescriptor;
2508
+ /** Multi-branch rule routing over the channels (pairs with a conditional edge). */
2509
+ readonly conditionalRouter: (params: ConditionalRouterParams) => ComponentDescriptor;
2510
+ /** Append documents into an in-state document store array (optionally de-duplicating). */
2511
+ readonly documentWriter: (params: DocumentWriterParams) => ComponentDescriptor;
2512
+ /**
2513
+ * **Integration component (vendor I/O).** Perform an HTTP request, writing an
2514
+ * {@link HttpFetchResult} (`{ status, ok, body, json }`) to `into`. This is **not**
2515
+ * a Rust component: it returns a plain {@link NodeHandler} added with
2516
+ * {@link import("./builder.js").GraphBuilder.node} and runs over the JS seam on the
2517
+ * Rust engine. The default transport is the real `globalThis.fetch` (supports
2518
+ * `method`/`headers`/`body`/`timeoutMs` via an `AbortController`); it never throws —
2519
+ * a non-2xx is surfaced via `ok`/`status`, and an error/timeout writes
2520
+ * `{ ok: false, error }`. Inject `fetchImpl` to override the transport (e.g. offline
2521
+ * in tests).
2522
+ */
2523
+ readonly httpFetch: (params: HttpFetchParams) => IntegrationComponentHandler;
2524
+ /**
2525
+ * **Integration component (vendor I/O).** Run a web search, writing a
2526
+ * {@link WebSearchOutcome} (`{ results, note? }`) to `into`. This is **not** a Rust
2527
+ * component: it returns a plain {@link NodeHandler} added with
2528
+ * {@link import("./builder.js").GraphBuilder.node} and runs over the JS seam on the
2529
+ * Rust engine. The default is a real Tavily connector behind `TAVILY_API_KEY`
2530
+ * (POSTs to `https://api.tavily.com/search`, normalizing results to
2531
+ * `[{ title, url, snippet }]`); when the key is absent it degrades gracefully with
2532
+ * **no** network call (empty results + a note). Inject `searchImpl` to override the
2533
+ * provider, or `transport` to keep the Tavily connector offline.
2534
+ */
2535
+ readonly webSearch: (params: WebSearchParams) => IntegrationComponentHandler;
2536
+ };
2537
+
2538
+ /** A map of channel name → value type. The generic that flows through the builder. */
2539
+ type ChannelValues = Record<string, unknown>;
2540
+ /** The starting (channel-less) state of a fresh {@link import("./builder.js").GraphBuilder}. */
2541
+ type EmptyChannels = Record<never, never>;
2542
+ /** {@link GraphState} with strongly-typed channels. */
2543
+ type TypedGraphState<TState extends ChannelValues> = Omit<GraphState, "channels"> & {
2544
+ channels: TState;
2545
+ };
2546
+ /**
2547
+ * What a node handler may return: a partial update to the declared channels
2548
+ * (type-checked) plus any additional keys (the runtime passes unknown keys
2549
+ * through), or a routing {@link Command}.
2550
+ */
2551
+ type ChannelUpdate<TState extends ChannelValues> = (Partial<TState> & Record<string, unknown>) | Command;
2552
+ /** A node handler with strongly-typed state and return value. */
2553
+ type TypedNodeHandler<TState extends ChannelValues> = (input: unknown, state: TypedGraphState<TState>, context: NodeExecutionContext) => Promise<ChannelUpdate<TState>>;
2554
+ /** A conditional-edge predicate over strongly-typed state. */
2555
+ type TypedCondition<TState extends ChannelValues> = (state: TypedGraphState<TState>) => boolean;
2556
+ /** Initial data accepted by a run: declared channels (typed) plus arbitrary extras. */
2557
+ type InitialData<TState extends ChannelValues> = Partial<TState> & Record<string, unknown>;
2558
+
2559
+ /** Options for {@link CompiledGraph.approveAndResume}. */
2560
+ type ApproveAndResumeOptions = {
2561
+ /**
2562
+ * Approval-gated tools the human has granted; they execute on resume. A bare string
2563
+ * grants a tool by name; `{ name, key }` grants a CONTENT-SCOPED guarded fs write
2564
+ * (ADR 0024 phase 2c) pinned to the exact call — pass the `approvalKey` surfaced on the
2565
+ * suspended run's pending approval, so only the approved path+content unlocks.
2566
+ */
2567
+ approvedTools: Array<string | {
2568
+ name: string;
2569
+ key?: string;
2570
+ }>;
2571
+ /**
2572
+ * Who approved: a person, never the agent that requested the tool. Required. It is
2573
+ * recorded as each granted tool's `resolvedBy` and carried to the Rust engine, which
2574
+ * rejects the resume if it equals the tool's requester (no self-approval).
2575
+ */
2576
+ resolvedBy: string;
2577
+ };
2578
+ /** Wiring assembled by {@link GraphBuilder.compile} and handed to a {@link CompiledGraph}. */
2579
+ type CompiledGraphParts = {
2580
+ definition: GraphDefinition;
2581
+ handlers: Map<string, NodeHandler>;
2582
+ conditions: Map<string, ConditionFn>;
2583
+ /**
2584
+ * Per agent node, the serializable config + JS tool executes the Rust engine bridge
2585
+ * needs (see {@link RustAgentConfig}). Empty for graphs with no agent nodes.
2586
+ */
2587
+ agentConfigs?: Map<string, RustAgentConfig>;
2588
+ /**
2589
+ * Per agent node, the principal that requests approvals and the node's gated tool names.
2590
+ * {@link CompiledGraph.approveAndResume} uses it to stamp each granted tool's `requestedBy`
2591
+ * for the engine's no-self-approval check. Empty for graphs with no agent nodes.
2592
+ */
2593
+ agentApprovals?: Map<string, AgentApprovalBinding>;
2594
+ /**
2595
+ * Per component node, the `{ kind, params }` carrier the Rust engine bridge needs to
2596
+ * run the native component handler (see {@link RustComponentConfig}). Empty for
2597
+ * graphs with no component nodes.
2598
+ */
2599
+ componentConfigs?: Map<string, RustComponentConfig>;
2600
+ /**
2601
+ * Per `mapAgents` node, the dynamic-fan-out carrier (over-channel, join channel, the sub-agent
2602
+ * config) the Rust bridge needs (ADR 0027 phase 4b). Empty for graphs with no mapAgents nodes.
2603
+ */
2604
+ mapAgentConfigs?: Map<string, RustMapAgentConfig>;
2605
+ /**
2606
+ * Child graphs that `subgraph`-type nodes resolve into (their node handlers /
2607
+ * conditions / agent / component configs are already merged into the maps above, by
2608
+ * global node id). Carried to the Rust engine as `EngineSpec.subgraphs`. Empty for
2609
+ * graphs with no subgraph nodes.
2610
+ */
2611
+ subgraphs?: GraphDefinition[];
2612
+ /**
2613
+ * Per-path filesystem permission rules (ADR 0024 phase 2b) applied run-wide to every
2614
+ * fs-enabled agent. Carried to the Rust engine as `EngineSpec.fsPolicy`. Empty/omitted
2615
+ * = fail-closed read-only everywhere.
2616
+ */
2617
+ fsPolicy?: FsPolicyRule[];
2618
+ };
2619
+ /** Options accepted by {@link CompiledGraph.run} / {@link CompiledGraph.stream}. */
2620
+ type RunOptions = {
2621
+ /** Provide a stable run id (e.g. to correlate with an external system). */
2622
+ runId?: RunId;
2623
+ /**
2624
+ * Pre-queue dynamic-message inputs (`send`) before the run: per node id, a FIFO list
2625
+ * each consumed by that node's next execution via the reserved `__injected` channel
2626
+ * (read it with {@link import("./send.js").readInjected}). The map-reduce seam.
2627
+ */
2628
+ inbox?: Record<string, unknown[]>;
2629
+ };
2630
+ /**
2631
+ * `AILU_SDK_ENGINE`, read when a graph compiles. The Rust engine is the only engine:
2632
+ * - `"auto"` (default) and `"rust"`: run on the Rust engine. Under `"auto"`, a graph whose
2633
+ * agent node sets the removed `approvalEngine` option fails to compile instead.
2634
+ * - `"ts"`: removed; compiling fails with {@link RustEngineRequiredError}.
2635
+ */
2636
+ type EnginePreference = "auto" | "rust" | "ts";
2637
+ /**
2638
+ * Thrown at compile time when the graph cannot run on the **Rust engine**. The SDK is a thin
2639
+ * surface over the native engine and has **no TypeScript fallback** — it never silently degrades.
2640
+ */
2641
+ declare class RustEngineRequiredError extends AiluSdkError {
2642
+ constructor(preference: EnginePreference);
2643
+ }
2644
+ /**
2645
+ * A validated, runnable graph. Holds the engine wiring (registries, checkpointer, event bus) so
2646
+ * callers don't touch the lower-level `@ailu-ai/graph-runtime` primitives unless they want to.
2647
+ *
2648
+ * Execution runs **exclusively on the Rust engine** via `@ailu-ai/napi` (a required dependency).
2649
+ * There is **no TypeScript fallback** — {@link CompiledGraph} throws {@link RustEngineRequiredError}
2650
+ * at compile time if the native engine cannot run the graph.
2651
+ */
2652
+ declare class CompiledGraph<TState extends ChannelValues = ChannelValues> {
2653
+ readonly definition: GraphDefinition;
2654
+ private readonly checkpointer;
2655
+ private readonly eventBus;
2656
+ private readonly runtime;
2657
+ /**
2658
+ * The Rust runner. Never `null` once constructed (the constructor throws otherwise).
2659
+ * Typed on `ChannelValues` (not `TState`) on purpose: the runner round-trips state as
2660
+ * serialized `GraphState`/JSON, so it never needs the precise channel shape — and
2661
+ * keeping `TState` out of every *field* keeps `CompiledGraph<TState>` variance-friendly
2662
+ * (a `CompiledGraph<Specific>` stays assignable to `CompiledGraph<ChannelValues>`,
2663
+ * e.g. when stored in a heterogeneous registry). The public methods re-narrow to
2664
+ * `TState` at their boundary.
2665
+ */
2666
+ private readonly rustRunner;
2667
+ /** The last suspended state seen per run id, fed back into the Rust resume/approve. */
2668
+ private readonly suspendedStates;
2669
+ /** Per agent node, the approval principal used by {@link approveAndResume}. */
2670
+ private readonly agentApprovals;
2671
+ constructor(parts: CompiledGraphParts);
2672
+ /**
2673
+ * Build the Rust runner, or return `null` when the graph can't run: the native addon is
2674
+ * missing, `AILU_SDK_ENGINE=ts` is set, or (under `auto`) an agent node sets the removed
2675
+ * `approvalEngine` option. The constructor turns `null` into {@link RustEngineRequiredError}.
2676
+ *
2677
+ * Agents run natively with the engine's own gateway (the `llm` option is ignored). JS node
2678
+ * handlers and tool `execute` functions are called over the napi seam, which awaits them. A
2679
+ * handler returning a routing `Command` (`{ goto }`) has its `goto` ignored: route with a
2680
+ * conditional edge instead.
2681
+ */
2682
+ private maybeCreateRustRunner;
2683
+ /**
2684
+ * Adapt the (async) JS node handlers into the async producers the Rust seam needs.
2685
+ * The Rust side awaits the returned promise, so a handler doing real async work
2686
+ * round-trips faithfully. A handler that returns a routing {@link Command} keeps only
2687
+ * its `update` map (see {@link toUpdateObject}): the engine routes by the graph's edges.
2688
+ */
2689
+ private buildNodeFns;
2690
+ /** Async tool executes for every JS-backed tool across all agent nodes. */
2691
+ private buildToolFns;
2692
+ /** The named condition predicates, retyped for the Rust seam (already synchronous). */
2693
+ private buildConditionFns;
2694
+ /** True when this graph executes on the Rust engine. */
2695
+ get usesRustEngine(): boolean;
2696
+ /** Start a fresh run from the entry node and execute until completion or suspension. */
2697
+ run(initialData?: InitialData<TState>, options?: RunOptions): Promise<TypedGraphState<TState>>;
2698
+ /** Resume a previously suspended run from its latest checkpoint. */
2699
+ resume(runId: RunId): Promise<TypedGraphState<TState>>;
2700
+ /**
2701
+ * Grant approval for the named tools and resume a run that suspended for approval
2702
+ * (an agent node with `suspendForApproval`). The approved tools are written into
2703
+ * `__approvedTools` by the engine before resuming, so the agent re-runs and executes
2704
+ * the now-approved tools instead of gating them again. An agent never approves its own
2705
+ * tools; this is the human seam.
2706
+ */
2707
+ approveAndResume(runId: RunId, options: ApproveAndResumeOptions): Promise<TypedGraphState<TState>>;
2708
+ /**
2709
+ * Deliver an external signal to a run suspended on a `waitForSignal` node, then
2710
+ * resume it: the payload is injected into the `__signals` channel under `name` and
2711
+ * the run advances past the waiting node. The seam a control plane uses to wake a
2712
+ * run on an external event (a webhook, a message, an approval-out-of-band).
2713
+ *
2714
+ * Durable timers + external signals run natively on the **Rust engine** (the only
2715
+ * runtime). The seam a control plane uses to wake a run on an external event.
2716
+ */
2717
+ signal(runId: RunId, name: string, payload?: unknown): Promise<TypedGraphState<TState>>;
2718
+ /**
2719
+ * Project granted tool names into the wire shape the Rust engine validates: each
2720
+ * tool carries the principal that requested it (the owning agent node) and the
2721
+ * distinct principal granting it. Names are sorted so the wire payload is
2722
+ * deterministic regardless of the caller's ordering.
2723
+ */
2724
+ private toApprovedToolWire;
2725
+ /** The agent node that declared `toolName` as approval-gated, as the request principal. */
2726
+ private requesterOfTool;
2727
+ /**
2728
+ * Stream events as the graph executes. See {@link StreamMode} for the available
2729
+ * shapes. The modes are projected, incrementally, over the run-event feed that crosses
2730
+ * napi:
2731
+ * - `updates` — a `state_update` per node completion (`delta` = the node's output).
2732
+ * - `values` — a full `state_value` per node completion, accumulated by replaying the
2733
+ * node deltas through the channel reducers (the SDK mirrors the engine's reducers),
2734
+ * plus a final authoritative `state_value` from the resolved run.
2735
+ * - `messages` — a `message_delta` per new entry appended to the `messages` channel,
2736
+ * plus (ADR 0033, when the run opts into `streamTokens`) a `message_delta` per LLM
2737
+ * token delta, grouped by `messageId`.
2738
+ * - `debug` — every run-lifecycle event wrapped as a `debug` payload.
2739
+ */
2740
+ stream(initialData: InitialData<TState>, mode: StreamMode, options?: RunOptions): AsyncIterable<StreamEvent>;
2741
+ /** Seed a running channel map from the graph's channel defaults + the run's input. */
2742
+ private seedChannels;
2743
+ /** Apply a node delta to the running channels via the declared reducers (engine parity). */
2744
+ private applyDelta;
2745
+ /**
2746
+ * Drive the Rust run and project its forwarded run-event feed into {@link StreamEvent}s,
2747
+ * incrementally for every mode. Events arrive via the runner's subscriber while the run
2748
+ * promise is in flight; a small wake/queue interleaves them with the run's completion.
2749
+ */
2750
+ private streamViaRust;
2751
+ /** A channels-only synthetic GraphState for a `values` stream step. */
2752
+ private syntheticState;
2753
+ /** Subscribe to the run-event lifecycle stream. Returns an unsubscribe function. */
2754
+ onEvent(handler: (event: RunEvent) => void): () => void;
2755
+ /**
2756
+ * @deprecated The in-process TypeScript runtime. It does not run this graph: the Rust engine
2757
+ * does, through `run` / `resume` / `approveAndResume` / `signal` / `stream`.
2758
+ */
2759
+ get engine(): GraphRuntime;
2760
+ /**
2761
+ * Explain where a run stands (ADR AI-DX): its status, why it suspended, what unblocks it, and
2762
+ * what failed — a structured account a human or an AI agent reads to pick the next move. Reads
2763
+ * the suspended state this instance retains; an unknown run id (it completed, failed, or runs on
2764
+ * another instance) returns a `status: "unknown"` explanation. For an arbitrary run, pass its
2765
+ * `GraphState` to {@link explainRun} directly.
2766
+ */
2767
+ explain(runId: RunId): RunExplanation;
2768
+ /** Record a run's state if it suspended, so resume/approve can feed it back to Rust. */
2769
+ private captureSuspension;
2770
+ private requireSuspendedState;
2771
+ }
2772
+
2773
+ /** Options passed to {@link createGraph}. */
2774
+ type CreateGraphOptions = {
2775
+ name: string;
2776
+ /** Defaults to a slugified `name`. */
2777
+ id?: string;
2778
+ /** Semver-ish version string. Defaults to `"0.0.0"`. */
2779
+ version?: string;
2780
+ recursionLimit?: number;
2781
+ metadata?: Record<string, unknown>;
2782
+ };
2783
+ /** Channel shorthand: `reducer` defaults to `"replace"`. The value type is inferred from `default`. */
2784
+ type ChannelInput<TValue = unknown> = {
2785
+ type: string;
2786
+ reducer?: ChannelReducer;
2787
+ default?: TValue;
2788
+ /** ADR 0032: never emit this channel's value in run events/logs (masked; still checkpointed). */
2789
+ noLog?: boolean;
2790
+ };
2791
+ /** Config form for non-trivial nodes. A bare handler is the common case. */
2792
+ type NodeInput<TState extends ChannelValues> = {
2793
+ type?: NodeType;
2794
+ handler?: TypedNodeHandler<TState>;
2795
+ label?: string;
2796
+ retryPolicy?: RetryPolicy;
2797
+ metadata?: Record<string, unknown>;
2798
+ };
2799
+ /**
2800
+ * Fluent builder for an Ailu graph. Add channels, nodes and edges, then
2801
+ * {@link GraphBuilder.compile} into a runnable {@link CompiledGraph}.
2802
+ *
2803
+ * The `TState` type parameter accumulates the declared channels as you call
2804
+ * `.channel(...)`, so handler state and the result of `run`/`resume` are fully
2805
+ * typed without any manual annotation.
2806
+ *
2807
+ * Conditions are always **named predicates** registered here — never `eval`'d
2808
+ * strings — which is what keeps conditional routing safe and inspectable.
2809
+ */
2810
+ declare class GraphBuilder<TState extends ChannelValues = EmptyChannels> {
2811
+ private readonly options;
2812
+ private readonly channels;
2813
+ private readonly nodes;
2814
+ private readonly edges;
2815
+ private readonly handlers;
2816
+ private readonly conditions;
2817
+ /** Per agent node, the serializable config the Rust engine bridge needs. */
2818
+ private readonly agentConfigs;
2819
+ private readonly mapAgentConfigs;
2820
+ /** Per agent node, the governance binding (approval engine + requester) for resume. */
2821
+ private readonly agentApprovals;
2822
+ /** Per component node, the `{ kind, params }` carrier the Rust engine bridge needs. */
2823
+ private readonly componentConfigs;
2824
+ /** Child graphs registered as `subgraph` nodes, keyed by their (global) graph id. */
2825
+ private readonly subgraphDefs;
2826
+ /** Per-path filesystem permission rules (ADR 0024 phase 2b), applied run-wide. */
2827
+ private readonly fsPolicyRules;
2828
+ private entryNodeId;
2829
+ constructor(options: CreateGraphOptions);
2830
+ /** Reinterpret `this` under a wider channel type after declaring a new channel. */
2831
+ private widen;
2832
+ /** Declare a state channel. `reducer` defaults to `"replace"`; value type inferred from `default`. */
2833
+ channel<TName extends string, TValue = unknown>(name: TName, definition: ChannelInput<TValue>): GraphBuilder<TState & {
2834
+ [K in TName]: TValue;
2835
+ }>;
2836
+ /** Declare an append-reduced `messages` channel (the conversational default). */
2837
+ messagesChannel<TName extends string = "messages">(name?: TName): GraphBuilder<TState & {
2838
+ [K in TName]: Message[];
2839
+ }>;
2840
+ /** Shared node-registration path: dedupe, register the handler, default the entry. */
2841
+ private pushNode;
2842
+ /** Declare a channel only if it hasn't been declared yet (for node helpers that need one). */
2843
+ private ensureChannel;
2844
+ /** Add a node. Pass a handler for the common action case, or a config object. */
2845
+ node(id: string, handlerOrConfig: TypedNodeHandler<TState> | NodeInput<TState>): this;
2846
+ /** Convenience for a `human-gate` node that suspends the run for approval. */
2847
+ humanGate(id: string, options?: {
2848
+ label?: string;
2849
+ }): this;
2850
+ /**
2851
+ * Add an agent node: a ReAct agent driven by an LLM gateway. Its result lands in
2852
+ * `config.outputChannel` (default `"agentResult"`), which is auto-declared and
2853
+ * added to the typed state. Route on `result.requiresHumanReview` to gate
2854
+ * sensitive tool use.
2855
+ */
2856
+ agentNode<TOut extends string = typeof DEFAULT_AGENT_OUTPUT_CHANNEL>(id: string, config: AgentNodeConfig & {
2857
+ outputChannel?: TOut;
2858
+ }): GraphBuilder<TState & {
2859
+ [K in TOut]: AgentResult;
2860
+ }>;
2861
+ /**
2862
+ * Add a tool node: executes the tool calls emitted by the last AI message in the
2863
+ * `messages` channel (auto-declared as an append-reduced messages channel).
2864
+ */
2865
+ toolNode(id: string, config: ToolNodeConfig): GraphBuilder<TState & {
2866
+ messages: Message[];
2867
+ }>;
2868
+ /**
2869
+ * Add a **component node**: a pure (no-LLM) compute building block from
2870
+ * {@link import("./components.js").components} (e.g. `promptBuilder`, `router`,
2871
+ * `retriever`). The node carries the `{ kind, params }` carrier and runs natively on the
2872
+ * Rust engine: the bridge routes a `componentNodes` entry to the native handler.
2873
+ *
2874
+ * ```ts
2875
+ * createGraph({ name: "p" })
2876
+ * .channel("name", { type: "string", default: "" })
2877
+ * .channel("prompt", { type: "string", default: "" })
2878
+ * .component("build", components.promptBuilder({ template: "Hi {{name}}", into: "prompt" }));
2879
+ * ```
2880
+ */
2881
+ component(id: string, descriptor: ComponentDescriptor, options?: {
2882
+ label?: string;
2883
+ }): this;
2884
+ /**
2885
+ * Add a **subgraph node**: nest another graph (built with its own
2886
+ * {@link GraphBuilder}) as a single node. On entry the parent's channels are
2887
+ * projected into the child via `inputMapping` (`childKey → parentKey`; omit to copy
2888
+ * all parent channels); on completion the child's channels are merged back via
2889
+ * `outputMapping` (`parentKey → childKey`; omit to spread all child channels onto
2890
+ * the parent). If the child suspends (e.g. an internal human gate), the parent
2891
+ * suspends at this node and a parent `resume` re-attaches to the child.
2892
+ *
2893
+ * The child's wiring (node handlers, conditions, agent/component configs) is merged
2894
+ * into the parent — child runs share the parent's registries, keyed by GLOBAL node
2895
+ * id — so child node ids must not collide with the parent's. Declare on the parent
2896
+ * any channels the `outputMapping` writes into.
2897
+ *
2898
+ * ```ts
2899
+ * const child = createGraph({ name: "double", id: "double" })
2900
+ * .channel("in", { type: "number", default: 0 })
2901
+ * .channel("out", { type: "number", default: 0 })
2902
+ * .node("calc", (s) => ({ out: (s.in as number) * 2 }));
2903
+ * createGraph({ name: "parent" })
2904
+ * .channel("x", { type: "number", default: 21 })
2905
+ * .channel("y", { type: "number", default: 0 })
2906
+ * .subgraph("sub", child, { inputMapping: { in: "x" }, outputMapping: { y: "out" } });
2907
+ * ```
2908
+ */
2909
+ subgraph<TChild extends ChannelValues>(id: string, child: GraphBuilder<TChild>, options?: {
2910
+ inputMapping?: Record<string, string>;
2911
+ outputMapping?: Record<string, string>;
2912
+ label?: string;
2913
+ }): this;
2914
+ /**
2915
+ * Add a **task node** (ADR 0022/0023, phase 1): spawn a sub-agent in an isolated
2916
+ * context that returns a single compressed report. It is sugar over
2917
+ * {@link GraphBuilder.subgraph} — the sub-agent runs as a one-node child graph, so
2918
+ * the spawn is a real node: **checkpointed, audited, and human-gate-preserving**.
2919
+ * If the sub-agent suspends for approval, the whole run suspends and a parent
2920
+ * `resume`/`approveAndResume` re-attaches to it. No new runtime path is added.
2921
+ *
2922
+ * Isolation: only `objectiveChannel` crosses into the child (`inputMapping`), and
2923
+ * only the child's `reportChannel` crosses back (`outputMapping`) — the sub-agent
2924
+ * never sees the parent's other channels, and the parent never sees the child's
2925
+ * intermediate work. With `compress` (default), the sub-agent runs
2926
+ * `outputStyle: "terse"` so the report is a summary, not a full transcript.
2927
+ *
2928
+ * ```ts
2929
+ * createGraph({ name: "research" })
2930
+ * .channel("objective", { type: "string", default: "" })
2931
+ * .taskNode("dig", { subAgent: { llm, prompt: { system: "Research deeply." } } });
2932
+ * // -> state.report : AgentResult
2933
+ * ```
2934
+ */
2935
+ taskNode<TReport extends string = "report">(id: string, config: TaskNodeConfig & {
2936
+ reportChannel?: TReport;
2937
+ }): GraphBuilder<TState & {
2938
+ [K in TReport]: AgentResult;
2939
+ }>;
2940
+ /**
2941
+ * Add a **mapAgents node** (ADR 0027 phase 4b — dynamic fan-out): run `config.subAgent` once
2942
+ * per item in the `config.overChannel` array, **concurrently**, and write the per-item results
2943
+ * — in **input order** (deterministic, resumable) — into `config.joinAt` as an array. Each spawn
2944
+ * gets one item as its `input` and shares the run's channels. If a spawn needs approval and
2945
+ * `suspendForApproval` is set, the whole map suspends; resume re-runs it.
2946
+ *
2947
+ * ```ts
2948
+ * createGraph({ name: "fanout" })
2949
+ * .channel("items", { type: "json", default: [] })
2950
+ * .mapAgents("research", {
2951
+ * overChannel: "items",
2952
+ * subAgent: { llm, prompt: { system: "Summarise the item." } },
2953
+ * joinAt: "summaries"
2954
+ * });
2955
+ * ```
2956
+ */
2957
+ mapAgents<TJoin extends string>(id: string, config: MapAgentNodeConfig & {
2958
+ joinAt: TJoin;
2959
+ }): GraphBuilder<TState & {
2960
+ [K in TJoin]: AgentResult[];
2961
+ }>;
2962
+ /**
2963
+ * @internal Extract this builder's wiring so it can be nested as a subgraph by a
2964
+ * parent {@link GraphBuilder.subgraph}. Returns live maps (the parent merges them);
2965
+ * not part of the public authoring API.
2966
+ */
2967
+ toSubgraphParts(): {
2968
+ definition: GraphDefinition;
2969
+ handlers: Map<string, NodeHandler>;
2970
+ conditions: Map<string, ConditionFn>;
2971
+ agentConfigs: Map<string, RustAgentConfig>;
2972
+ mapAgentConfigs: Map<string, RustMapAgentConfig>;
2973
+ agentApprovals: Map<string, AgentApprovalBinding>;
2974
+ componentConfigs: Map<string, RustComponentConfig>;
2975
+ subgraphDefs: Map<string, GraphDefinition>;
2976
+ };
2977
+ /**
2978
+ * Fan out from an existing node to a fixed set of branch nodes that run
2979
+ * **concurrently** on the Rust engine, then join at `joinAt`. Each branch executes
2980
+ * from the same pre-fan-out state snapshot and the branch updates are merged in the
2981
+ * declared `parallelTo` order (deterministic, regardless of which branch finishes
2982
+ * first — ADR 0015). The `from` node runs first (its handler/agent), then its branches
2983
+ * scatter; control resumes at `joinAt` once every branch completes.
2984
+ *
2985
+ * This is the supported way to run **N parallel LLM calls** (each branch an
2986
+ * {@link GraphBuilder.agentNode}) on the public SDK — no static edges are needed for
2987
+ * the fan-out itself (it is its own routing). `parallelTo` is a fixed set declared at
2988
+ * build time; dynamic per-item map over a runtime-sized list is a separate primitive.
2989
+ *
2990
+ * `from` must already be added; `parallelTo` and `joinAt` are validated at compile.
2991
+ */
2992
+ fanOut(from: string, parallelTo: string[], joinAt: string): this;
2993
+ /** Add an unconditional edge from one node to another. */
2994
+ edge(from: string, to: string): this;
2995
+ /**
2996
+ * Add a conditional edge guarded by a named predicate. The predicate is
2997
+ * registered under `conditionName` and evaluated against the live (typed) state.
2998
+ */
2999
+ conditionalEdge(from: string, to: string, conditionName: string, predicate: TypedCondition<TState>): this;
3000
+ /**
3001
+ * ADR 0076 — governed error branch: once `from`'s `retryPolicy` is exhausted, the run reroutes
3002
+ * to `to` instead of failing (`node_error_routed` event, `channels.__lastError` populated)
3003
+ * rather than the run terminating (`run_failed`). At most one per node (enforced at compile via
3004
+ * `validateGraph`). Implemented in BOTH the Rust engine (`crates/graph-core`/`graph-runtime`,
3005
+ * the path every catalog/native run takes) and the TS legacy `@ailu-ai/graph-runtime` —
3006
+ * this builder always compiles to Rust, so this edge type works end to end here.
3007
+ */
3008
+ errorEdge(from: string, to: string): this;
3009
+ /** Override the entry node (defaults to the first node added). */
3010
+ entry(nodeId: string): this;
3011
+ private buildDefinition;
3012
+ /**
3013
+ * Declare per-path filesystem permission rules (ADR 0024 phase 2b) applied run-wide
3014
+ * to every agent created with `enableFs: true`. Verbs: `deny|read|write|gate`;
3015
+ * resolution is most-specific-glob-wins, fail-closed — an unmatched path resolves to
3016
+ * `read`, so writes need an explicit `write` rule (`gate` is enforced from phase 2c).
3017
+ * Repeated calls append. `*` matches within a path segment, `**` across segments.
3018
+ *
3019
+ * ```ts
3020
+ * createGraph({ name: "deep" })
3021
+ * .fsPolicy([{ glob: "scratch/**", verb: "write" }, { glob: "secret/**", verb: "deny" }])
3022
+ * .agentNode("worker", { llm, prompt: { system: "..." }, enableFs: true });
3023
+ * ```
3024
+ */
3025
+ fsPolicy(rules: FsPolicyRule[]): this;
3026
+ /** Validate and compile, returning a {@link Result} instead of throwing. */
3027
+ safeCompile(): Result<CompiledGraph<TState>, GraphCompileError>;
3028
+ /** Validate and compile into a runnable graph. Throws {@link GraphCompileError} on failure. */
3029
+ compile(): CompiledGraph<TState>;
3030
+ }
3031
+ /** Entry point: start building a graph. */
3032
+ declare const createGraph: (options: CreateGraphOptions) => GraphBuilder<EmptyChannels>;
3033
+
3034
+ /**
3035
+ * Durable-timer / external-signal helpers for node handlers (ADR 0009). A node handler
3036
+ * returns one of these to make the run SUSPEND after applying its channel update:
3037
+ * - {@link sleepUntil} — a durable timer: the run waits until an external scheduler
3038
+ * resumes it at `wakeAt` (the engine never sleeps — `wakeAt` is opaque data).
3039
+ * - {@link waitForSignal} — wait for a named external signal delivered via
3040
+ * {@link import("./compiled-graph.js").CompiledGraph.signal}; optionally with a
3041
+ * timeout `wakeAt` (signal-or-timeout).
3042
+ *
3043
+ * Both are reserved-key markers the Rust engine recognises across the napi seam; they
3044
+ * run on the Rust engine (the production runtime), not the TypeScript dev fallback.
3045
+ */
3046
+ /** Reserved handler-return key requesting a durable-timer suspension. */
3047
+ declare const SLEEP_UNTIL_KEY = "__sleepUntil";
3048
+ /** Reserved handler-return key requesting a signal-wait suspension. */
3049
+ declare const WAIT_FOR_SIGNAL_KEY = "__waitForSignal";
3050
+ /** Channel key carrying the suspend reason + scheduler hints on a suspended run. */
3051
+ declare const SUSPEND_META_KEY = "__suspend";
3052
+ /** Channel key carrying delivered signal payloads, keyed by signal name. */
3053
+ declare const SIGNALS_KEY = "__signals";
3054
+ /** Why a run is suspended, with any timer / signal scheduler hints. */
3055
+ type SuspendMeta = {
3056
+ /** `"human-gate" | "interrupt" | "timer" | "signal"`. */
3057
+ reason: string;
3058
+ /** For a durable timer (or signal-or-timeout): when to resume. Opaque to the engine. */
3059
+ wakeAt?: string;
3060
+ /** For a signal wait: the signal name a `signal(...)` must deliver. */
3061
+ awaitingSignal?: string;
3062
+ };
3063
+ /**
3064
+ * Node-handler return: suspend as a **durable timer** until `wakeAt`, applying `update`
3065
+ * to the channels first. `wakeAt` is an opaque deadline (e.g. ISO-8601) — the engine
3066
+ * stores it and never reads a clock; the control-plane scheduler resumes the run then.
3067
+ * On resume the run advances past this node.
3068
+ */
3069
+ declare const sleepUntil: (wakeAt: string, update?: Record<string, unknown>) => Record<string, unknown>;
3070
+ /**
3071
+ * Node-handler return: suspend awaiting the external signal `name`, applying `update`
3072
+ * first. Deliver it with {@link import("./compiled-graph.js").CompiledGraph.signal};
3073
+ * the payload lands in `__signals[name]`. Pass `wakeAt` for a signal-OR-timeout (the
3074
+ * run also wakes at `wakeAt` if the signal never arrives).
3075
+ */
3076
+ declare const waitForSignal: (name: string, options?: {
3077
+ wakeAt?: string;
3078
+ update?: Record<string, unknown>;
3079
+ }) => Record<string, unknown>;
3080
+ /**
3081
+ * Read the suspend metadata off a (suspended) run state — the control-plane scheduler
3082
+ * uses `wakeAt` to know when to resume a timer and `awaitingSignal` to route a signal.
3083
+ * Returns `undefined` when the run is not suspended on a timer / signal.
3084
+ */
3085
+ declare const readSuspendMeta: (state: Pick<GraphState, "channels">) => SuspendMeta | undefined;
3086
+ /** Read a delivered signal's payload from a run state's `__signals` channel. */
3087
+ declare const readSignal: (state: Pick<GraphState, "channels">, name: string) => unknown;
3088
+
3089
+ /**
3090
+ * Dynamic-message (`send`) helpers. Pre-queue inputs for a node via
3091
+ * {@link import("./compiled-graph.js").RunOptions.inbox}; each node execution consumes
3092
+ * the next queued input, exposed under the reserved `__injected` channel. A node handler
3093
+ * reads it with {@link readInjected}. The map-reduce / dynamic-dispatch seam.
3094
+ */
3095
+ /** Reserved channel exposing a `send`-injected input to a node handler (per execution). */
3096
+ declare const INJECTED_KEY = "__injected";
3097
+ /**
3098
+ * Read the `send`-injected input from a node's state, if the node consumed a queued
3099
+ * input this execution; `undefined` otherwise. The value is visible to the handler only
3100
+ * and is never persisted into the run's channels.
3101
+ */
3102
+ declare const readInjected: (state: Pick<GraphState, "channels">) => unknown;
3103
+
3104
+ /**
3105
+ * Canonical example graphs authored with the SDK. Shipped so the Studio can render
3106
+ * them and the control plane can seed them — one source of truth for both. Only the
3107
+ * `.definition` (plain data) is meant to cross boundaries.
3108
+ */
3109
+ type ExampleGraph = {
3110
+ slug: string;
3111
+ name: string;
3112
+ description: string;
3113
+ definition: GraphDefinition;
3114
+ };
3115
+ /** Build the example graph definitions. Pure — no LLM call, no I/O. */
3116
+ declare const exampleGraphs: () => ExampleGraph[];
3117
+
3118
+ /** One member's answer to the query. */
3119
+ type MemberAnswer = {
3120
+ memberId: string;
3121
+ content: string;
3122
+ };
3123
+ /** An answer stripped of its author, relabeled + shuffled so a reviewer can't favour its own. */
3124
+ type AnonymizedAnswer = {
3125
+ label: string;
3126
+ content: string;
3127
+ memberId: string;
3128
+ };
3129
+ /**
3130
+ * Strip authorship, relabel `A,B,C,…`, and shuffle deterministically by `seed` (so the same run
3131
+ * replays identically). Reviewers see only `{ label, content }`; the `memberId` is retained so the
3132
+ * control plane can de-anonymize the audit trail after ranking — never shown to a reviewer.
3133
+ */
3134
+ declare const anonymizeAndShuffle: (answers: MemberAnswer[], seed: string) => AnonymizedAnswer[];
3135
+ /**
3136
+ * Aggregate reviewer rankings into a consensus order (Borda count). Each ranking is an ordered list
3137
+ * of labels, best-first; a label at position `p` of `n` scores `n - p`. Unranked labels score 0.
3138
+ * Returns the labels best-first; ties break by label asc (deterministic). A ranking's duplicate or
3139
+ * unknown labels are ignored.
3140
+ */
3141
+ declare const aggregateRanks: (rankings: string[][], labels: string[]) => string[];
3142
+ /**
3143
+ * Parse a reviewer's free-text reply into an ordered list of labels (the labels it names, in order,
3144
+ * deduped) — tolerant of prose like "I rank B first, then A, then C". Only whole words exactly equal
3145
+ * to a label count (case-sensitive, so the article "a" is not label A; "Answer" is not label A
3146
+ * either). Unknown labels are dropped.
3147
+ */
3148
+ declare const parseRanking: (text: string, labels: string[]) => string[];
3149
+ /** A council seat (member / reviewer / chair) — an agent-node config minus its output channel. */
3150
+ type CouncilSeat = Omit<AgentNodeConfig, "outputChannel">;
3151
+ type CouncilOptions = {
3152
+ /** Channel holding the query text. Default `"query"`. */
3153
+ queryChannel?: string;
3154
+ /** The member agents (fixed N). Each answers the query independently, in parallel. */
3155
+ members: CouncilSeat[];
3156
+ /** Reviewers that rank the anonymized field. Default: one reviewer per member. */
3157
+ reviewers?: CouncilSeat[];
3158
+ /** The chair that synthesizes the final answer from the aggregated field (writes `answer`). */
3159
+ chair: CouncilSeat;
3160
+ /** Suspend on a human gate before the chair synthesizes (high-stakes). Default false. */
3161
+ humanGate?: boolean;
3162
+ /** Seed for the deterministic anonymize-shuffle (replay). Default `"council"`. */
3163
+ seed?: string;
3164
+ };
3165
+ /**
3166
+ * Build a governed LLM Council (ADR 0013 / ADR 0061) as a native **catalog** GraphDefinition: dispatch
3167
+ * → members (fan-out) → `councilAnonymize` → reviewers (fan-out, rank the field) → `councilAggregate` →
3168
+ * [optional human gate] → chair synthesis. Members/reviewers/chair are agent-carrier nodes, each
3169
+ * shown only the channels it needs: a reviewer ranks labelled answers without seeing who wrote them
3170
+ * (the label → member key goes to `fieldKey`, for the audit trail); anonymize/aggregate are Rust catalog components
3171
+ * (their deterministic logic mirrors the exported pure helpers). Every node carries a `component`/
3172
+ * `agent` carrier, so the graph runs on the Rust engine via `runCatalogGraph` — no JS handlers. Fixed N
3173
+ * (the member list length) via the runtime fan-out. Returns the definition; run it with
3174
+ * `runCatalogGraph(council(...))`.
3175
+ */
3176
+ declare const council: (options: CouncilOptions) => GraphDefinition;
3177
+
3178
+ /**
3179
+ * The Doc-QA REFERENCE GRAPH — a complete input → output retrieval-augmented
3180
+ * question-answering pipeline, composed entirely from the catalog (pure components +
3181
+ * one prebuilt-style agent), authored once and runnable two ways:
3182
+ *
3183
+ * 1. as a {@link CompiledGraph} via {@link buildDocQaReference} — runs on the Rust engine;
3184
+ * 2. as a plain {@link GraphDefinition} via {@link docQaReferenceDefinition} — every
3185
+ * node carries the shared `node.metadata.component` / `node.metadata.agent`
3186
+ * carrier, so the control plane can persist it, the Studio can render it, and the
3187
+ * catalog run path (`runCatalogGraph`) can execute it on the Rust engine.
3188
+ *
3189
+ * ── THE PIPELINE ──────────────────────────────────────────────────────────────
3190
+ * INPUT { question, documents }
3191
+ * → clean (textCleaner) normalise the raw documents text
3192
+ * → split (documentSplitter) chunk it into passages
3193
+ * → retrieve (retriever) deterministic mock-embedding top-k over the corpus
3194
+ * → rerank (reranker) reorder the hits against the question
3195
+ * → prompt (promptBuilder) build a grounded prompt from context + question
3196
+ * → answer (AGENT, balanced) a grounded RAG answerer writes its AgentResult
3197
+ * → extract (fieldExtractor) reduce AgentResult.reasoning to the final answer text
3198
+ * → assemble (answerBuilder) answer text + numbered citations → OUTPUT { answer }
3199
+ * OUTPUT { answer }
3200
+ *
3201
+ * Single input set, single output channel. Runs on Mistral's balanced-tier model
3202
+ * (`MISTRAL_API_KEY`), or offline and deterministic with `AILU_LLM_MOCK=1`.
3203
+ *
3204
+ * The retriever scores against a fixed corpus baked into its params (the Rust
3205
+ * `retriever` component's `docs` are configuration, not a channel). The `documents`
3206
+ * INPUT channel feeds the clean → split ingestion-prep stages so the graph exercises a
3207
+ * real document-preparation front-end; the corpus the retriever ranks is the same
3208
+ * knowledge the documents describe, kept in params so the run is fully reproducible.
3209
+ */
3210
+
3211
+ /** The default knowledge corpus the retriever ranks against. */
3212
+ declare const DEFAULT_REFERENCE_CORPUS: RetrieverDoc[];
3213
+ /** Options for {@link buildDocQaReference} / {@link docQaReferenceDefinition}. */
3214
+ type DocQaReferenceOptions = {
3215
+ /** @deprecated Ignored: the engine calls the model itself. Use `AILU_LLM_MOCK=1` to run offline. */
3216
+ llm?: LLMGateway;
3217
+ /** The corpus the retriever ranks against. Defaults to {@link DEFAULT_REFERENCE_CORPUS}. */
3218
+ corpus?: RetrieverDoc[];
3219
+ /** How many documents the retriever keeps before reranking. Defaults to 3. */
3220
+ k?: number;
3221
+ /** The answerer's capability tier. Defaults to `"balanced"`. */
3222
+ tier?: ModelTier;
3223
+ /** The answerer's provider. Defaults to `"mistral"`. */
3224
+ provider?: "openai" | "anthropic" | "mistral";
3225
+ };
3226
+ /**
3227
+ * Build the Doc-QA reference graph as a runnable {@link CompiledGraph}. Drive it with
3228
+ * a single input set and read the single output channel:
3229
+ *
3230
+ * ```ts
3231
+ * const app = buildDocQaReference();
3232
+ * const out = await app.run({ question: "How does Ailu resume after a crash?", documents: "…" });
3233
+ * console.log(out.channels.answer);
3234
+ * ```
3235
+ */
3236
+ declare const buildDocQaReference: (options?: DocQaReferenceOptions) => CompiledGraph;
3237
+ /**
3238
+ * The Doc-QA reference graph as a plain {@link GraphDefinition} carrying the shared
3239
+ * `node.metadata.component` / `node.metadata.agent` carrier on every node. This is the
3240
+ * form the control plane persists, the Studio renders, and `runCatalogGraph` executes
3241
+ * on the Rust engine. Pure data — no handler closures, no LLM gateway.
3242
+ */
3243
+ declare const docQaReferenceDefinition: (options?: DocQaReferenceOptions) => GraphDefinition;
3244
+
3245
+ /**
3246
+ * Prebuilt, tier-tagged micro-agent graphs. Each factory returns a runnable
3247
+ * {@link CompiledGraph} pre-wired with its capability {@link ModelTier} (matching the
3248
+ * Rust `PrebuiltAgent` definitions in `crates/components`), so the concrete model is
3249
+ * resolved by the {@link ModelPolicy} from the providers actually available — on Rust
3250
+ * by the bridge, on the TS fallback path by the SDK.
3251
+ *
3252
+ * A prebuilt agent is a one-agent graph, except {@link prebuilt.ragAnswerer}, which
3253
+ * composes the `retriever` + `reranker` components with an agent step.
3254
+ *
3255
+ * ```ts
3256
+ * import { prebuilt } from "@ailu-ai/graph-sdk";
3257
+ *
3258
+ * const result = await prebuilt.summarizer().run({ question: "long text…" });
3259
+ * console.log(result.channels.summary);
3260
+ * ```
3261
+ *
3262
+ * Each agent reads its provider's API key from the environment and fails with an error
3263
+ * naming the variable when none is set; `AILU_LLM_MOCK=1` runs it on the engine's
3264
+ * deterministic offline mock instead. Supply `model` to pin a concrete model, or
3265
+ * `tierOverride` to change the capability tier.
3266
+ */
3267
+
3268
+ /** Light options accepted by every prebuilt-agent factory. */
3269
+ type PrebuiltOptions = {
3270
+ /**
3271
+ * @deprecated Ignored: the Rust engine builds the gateway from the model and the
3272
+ * environment's API keys (`AILU_LLM_MOCK=1` for the offline mock).
3273
+ */
3274
+ llm?: LLMGateway;
3275
+ /** Override the capability tier the agent's model is resolved from. */
3276
+ tierOverride?: ModelTier;
3277
+ /**
3278
+ * Pin a concrete model, bypassing tier resolution (the explicit-override
3279
+ * precedence: an explicit model always wins over the tier).
3280
+ */
3281
+ model?: string;
3282
+ /**
3283
+ * The provider slot for the request (and the slot the default mock gateway
3284
+ * registers under). Defaults to `"anthropic"`. The actual adapter is the mock
3285
+ * unless a custom `llm` is supplied.
3286
+ */
3287
+ provider?: LLMProvider;
3288
+ };
3289
+ /** Options for {@link prebuilt.ragAnswerer}: the simple options plus its retrieval corpus. */
3290
+ type RagAnswererOptions = PrebuiltOptions & {
3291
+ /** The corpus the retriever scores against. Defaults to a small built-in set. */
3292
+ docs?: RetrieverDoc[];
3293
+ /** How many documents the retriever keeps before reranking. Defaults to 4. */
3294
+ k?: number;
3295
+ /** Channel the question is read from (the retriever query). Defaults to `"question"`. */
3296
+ questionChannel?: string;
3297
+ };
3298
+ /**
3299
+ * The prebuilt micro-agent surface. Each factory returns a runnable
3300
+ * {@link CompiledGraph} pre-wired with the agent's tier (matching the Rust
3301
+ * `PrebuiltAgent` definitions).
3302
+ */
3303
+ declare const prebuilt: {
3304
+ /** Condense input text into a short, faithful summary (writes `summary`). */
3305
+ readonly summarizer: (options?: PrebuiltOptions) => CompiledGraph;
3306
+ /** Assign the input to one label from a fixed set (writes `label`). */
3307
+ readonly classifier: (options?: PrebuiltOptions) => CompiledGraph;
3308
+ /** Extract structured fields from text as JSON (writes `extracted`). */
3309
+ readonly extractor: (options?: PrebuiltOptions) => CompiledGraph;
3310
+ /** Generate a SQL query from a natural-language request (writes `sql`). */
3311
+ readonly sqlGenerator: (options?: PrebuiltOptions) => CompiledGraph;
3312
+ /** Answer a question grounded in retrieved documents (retriever + reranker + agent). */
3313
+ readonly ragAnswerer: (options?: RagAnswererOptions) => CompiledGraph;
3314
+ /** Decide on a refund, gated behind human approval before the `refund` tool runs. */
3315
+ readonly refundApprover: (options?: PrebuiltOptions) => CompiledGraph;
3316
+ /** Translate input text into a target language (fast; writes `translation`). */
3317
+ readonly translator: (options?: PrebuiltOptions) => CompiledGraph;
3318
+ /** Classify the emotional tone of the input (fast; writes `sentiment`). */
3319
+ readonly sentimentAnalyzer: (options?: PrebuiltOptions) => CompiledGraph;
3320
+ /** Extract named entities from text as a JSON array (fast; writes `entities`). */
3321
+ readonly entityExtractor: (options?: PrebuiltOptions) => CompiledGraph;
3322
+ /** Redact personally identifiable information from text (fast; writes `redacted`). */
3323
+ readonly piiRedactor: (options?: PrebuiltOptions) => CompiledGraph;
3324
+ /** Map the input to a single conversational intent label (fast; writes `intent`). */
3325
+ readonly intentClassifier: (options?: PrebuiltOptions) => CompiledGraph;
3326
+ /** Generate a short, descriptive title for the input (fast; writes `title`). */
3327
+ readonly titleGenerator: (options?: PrebuiltOptions) => CompiledGraph;
3328
+ /** Extract the key terms from the input as a JSON array (fast; writes `keywords`). */
3329
+ readonly keywordExtractor: (options?: PrebuiltOptions) => CompiledGraph;
3330
+ /** Answer a question directly and concisely (balanced; writes `answer`). */
3331
+ readonly questionAnswerer: (options?: PrebuiltOptions) => CompiledGraph;
3332
+ /** Review a code snippet or diff for correctness and quality (frontier; writes `review`). */
3333
+ readonly codeReviewer: (options?: PrebuiltOptions) => CompiledGraph;
3334
+ /** Polish prose for clarity, grammar, flow, and tone (creative; writes `edited`). */
3335
+ readonly copyEditor: (options?: PrebuiltOptions) => CompiledGraph;
3336
+ };
3337
+
3338
+ /**
3339
+ * Real text embeddings as an exported SDK helper (NOT a catalog component kind).
3340
+ *
3341
+ * {@link createEmbeddings} returns an {@link Embeddings} whose `embed` turns a batch of
3342
+ * texts into dense vectors. The default transport POSTs to the provider's embeddings API
3343
+ * (`/embeddings` with `{ model, input }` and a `Bearer` key), parsing `data[].embedding` —
3344
+ * both Mistral and OpenAI's embeddings APIs share this exact response shape, so ONE parser
3345
+ * and ONE transport builder serve both. The {@link CreateEmbeddingsOptions.transport} hook
3346
+ * overrides the network call so a test can return deterministic vectors with no real
3347
+ * network. This is the embedding backbone behind
3348
+ * {@link import("./semantic-retriever.js").semanticRetriever}.
3349
+ *
3350
+ * ```ts
3351
+ * import { createEmbeddings } from "@ailu-ai/graph-sdk";
3352
+ *
3353
+ * const embeddings = createEmbeddings({ apiKey: process.env.MISTRAL_API_KEY });
3354
+ * const [a, b] = await embeddings.embed(["hello", "world"]);
3355
+ *
3356
+ * // A second provider (issue #541 — product-side gap: only one provider was ever wired):
3357
+ * const openai = createEmbeddings({ provider: "openai", apiKey: process.env.OPENAI_API_KEY });
3358
+ * ```
3359
+ */
3360
+ /** An embedder: turn a batch of texts into one dense vector each (order-preserving). */
3361
+ type Embeddings = {
3362
+ /** Embed `texts` into a `number[][]` of the same length and order. */
3363
+ embed(texts: string[]): Promise<number[][]>;
3364
+ };
3365
+ /**
3366
+ * The transport an embeddings client posts through: it receives the assembled request
3367
+ * body and must resolve to the parsed JSON response (the `{ data: [{ embedding }] }`
3368
+ * shape both Mistral's and OpenAI's embeddings APIs return). The real default builds this
3369
+ * from `fetch`; a test injects a fake to stay offline.
3370
+ */
3371
+ type EmbeddingsTransport = (body: EmbeddingsRequestBody) => Promise<unknown> | unknown;
3372
+ /** The request body POSTed to the embeddings endpoint (`{ model, input }`). `dimensions` is
3373
+ * OpenAI-specific (its `text-embedding-3-*` models support down-projecting via this field);
3374
+ * omitted from the body entirely when not set, so it's a no-op for a provider that ignores it. */
3375
+ type EmbeddingsRequestBody = {
3376
+ model: string;
3377
+ input: string[];
3378
+ dimensions?: number;
3379
+ };
3380
+ /** The embeddings providers `createEmbeddings` knows how to reach directly. */
3381
+ type EmbeddingsProvider = "mistral" | "openai";
3382
+ /** Options for {@link createEmbeddings}. */
3383
+ type CreateEmbeddingsOptions = {
3384
+ /** The embeddings provider. Defaults to `"mistral"` (unchanged from before this option existed). */
3385
+ provider?: EmbeddingsProvider;
3386
+ /** API key. Defaults to `process.env.MISTRAL_API_KEY`/`OPENAI_API_KEY` per `provider`. Required
3387
+ * unless `transport` is injected. */
3388
+ apiKey?: string;
3389
+ /** Embedding model. Defaults to the provider's own default (`mistral-embed` / `text-embedding-3-small`). */
3390
+ model?: string;
3391
+ /** API base URL. Defaults to the provider's own default. */
3392
+ baseUrl?: string;
3393
+ /** Down-project the output vectors to this many dimensions (OpenAI `text-embedding-3-*` only —
3394
+ * a provider that doesn't support it silently ignores an unset field, never sent unless set). */
3395
+ dimensions?: number;
3396
+ /**
3397
+ * An injectable transport overriding the default `fetch`-based call. Receives the
3398
+ * request body and returns the parsed JSON response. Inject a fake to keep a test
3399
+ * offline (or to point at a stub) — when set, no API key is required.
3400
+ */
3401
+ transport?: EmbeddingsTransport;
3402
+ };
3403
+ /** Raised when no API key and no transport were supplied, so a real call is impossible. */
3404
+ declare class MissingEmbeddingsKeyError extends Error {
3405
+ constructor(provider?: EmbeddingsProvider, envVar?: string);
3406
+ }
3407
+ /** Raised when the embeddings response doesn't carry the expected `data[].embedding` shape. */
3408
+ declare class EmbeddingsResponseError extends Error {
3409
+ constructor(detail: string);
3410
+ }
3411
+ /**
3412
+ * Create an {@link Embeddings} client for `options.provider` (defaults to `"mistral"`,
3413
+ * unchanged from before this option existed). With the default transport it POSTs to the
3414
+ * provider's own base URL with `{ model, input: texts, dimensions? }` and `Authorization:
3415
+ * Bearer (apiKey || process.env[<provider's env var>])`, parsing `data[].embedding` (the
3416
+ * same response shape for both wired providers). Inject `transport` to override that for
3417
+ * offline tests. Throws {@link MissingEmbeddingsKeyError} when neither a key nor a
3418
+ * transport is available.
3419
+ */
3420
+ declare const createEmbeddings: (options?: CreateEmbeddingsOptions) => Embeddings;
3421
+
3422
+ /**
3423
+ * A small embedding-backed vector store as an exported SDK helper (NOT a catalog
3424
+ * component kind). {@link createVectorStore} keeps `{ id, content, embedding, metadata? }`
3425
+ * items and answers nearest-neighbour {@link VectorStore.query} calls by cosine
3426
+ * similarity (descending). In-memory by default; when `persistPath` is set the store is
3427
+ * persisted to / loaded from a round-trippable JSON file (synchronous `fs`). The exported
3428
+ * {@link cosineSimilarity} powers the ranking and is reusable on its own.
3429
+ *
3430
+ * ```ts
3431
+ * import { createVectorStore } from "@ailu-ai/graph-sdk";
3432
+ *
3433
+ * const store = createVectorStore();
3434
+ * store.upsert([{ id: "a", content: "hello", embedding: [1, 0] }]);
3435
+ * const hits = store.query([1, 0], 1); // [{ id: "a", content: "hello", score: 1 }]
3436
+ * ```
3437
+ */
3438
+ /** An item stored in the vector store: an id, its text, its embedding and optional metadata. */
3439
+ type VectorStoreItem = {
3440
+ id: string;
3441
+ content: string;
3442
+ embedding: number[];
3443
+ metadata?: Record<string, unknown>;
3444
+ };
3445
+ /** A single nearest-neighbour result: the item (minus its embedding) plus a cosine score. */
3446
+ type VectorStoreMatch = {
3447
+ id: string;
3448
+ content: string;
3449
+ score: number;
3450
+ metadata?: Record<string, unknown>;
3451
+ };
3452
+ /** The vector store surface returned by {@link createVectorStore}. */
3453
+ type VectorStore = {
3454
+ /** Insert or replace items by `id` (last write wins); persists when `persistPath` is set. */
3455
+ upsert(items: VectorStoreItem[]): void;
3456
+ /** Return the top-`k` items by cosine similarity to `embedding`, highest score first. */
3457
+ query(embedding: number[], k: number): VectorStoreMatch[];
3458
+ /** The number of items currently held. */
3459
+ size(): number;
3460
+ };
3461
+ /** Options for {@link createVectorStore}. */
3462
+ type CreateVectorStoreOptions = {
3463
+ /**
3464
+ * When set, the store loads its items from this JSON file on creation (if present) and
3465
+ * rewrites it on every {@link VectorStore.upsert}. The file is a round-trippable JSON
3466
+ * array of {@link VectorStoreItem}. Omit for a purely in-memory store.
3467
+ */
3468
+ persistPath?: string;
3469
+ };
3470
+ /**
3471
+ * Cosine similarity of two vectors. Compares over the shorter length (missing
3472
+ * components count as 0) and returns `0` when either vector has zero magnitude, so a
3473
+ * degenerate input never produces `NaN`.
3474
+ */
3475
+ declare const cosineSimilarity: (a: number[], b: number[]) => number;
3476
+ /**
3477
+ * Create a {@link VectorStore}. In-memory by default; with `persistPath` it loads any
3478
+ * existing JSON file on creation and rewrites it on every upsert (round-trippable).
3479
+ */
3480
+ declare const createVectorStore: (options?: CreateVectorStoreOptions) => VectorStore;
3481
+
3482
+ /**
3483
+ * A semantic (vector-store) retrieval connector as an exported SDK helper — the "real
3484
+ * embeddings" sibling of the deterministic mock `components.retriever`. It is NOT a new
3485
+ * catalog component kind: {@link semanticRetriever} returns a plain {@link NodeHandler}
3486
+ * added with {@link import("./builder.js").GraphBuilder.node} (the same vendor-I/O shape
3487
+ * as `components.httpFetch` / `components.webSearch`), so on the Rust engine it runs over
3488
+ * the async JS seam (`on_node`) like any other JS node.
3489
+ *
3490
+ * On each run it (optionally) embeds `docs` into the {@link VectorStore}, embeds the
3491
+ * query, then writes the top-`k` `{ id, content, score }` matches to the `into` channel.
3492
+ * `embeddings` defaults to a real Mistral client ({@link createEmbeddings}); inject a
3493
+ * fake {@link Embeddings} to keep a test offline and deterministic.
3494
+ *
3495
+ * ```ts
3496
+ * import { createGraph, semanticRetriever } from "@ailu-ai/graph-sdk";
3497
+ *
3498
+ * createGraph({ name: "semantic" })
3499
+ * .channel("q", { type: "string", default: "" })
3500
+ * .channel("hits", { type: "json", default: [] })
3501
+ * .node("retrieve", semanticRetriever({
3502
+ * queryFrom: "q",
3503
+ * into: "hits",
3504
+ * k: 3,
3505
+ * docs: [{ id: "d1", content: "Ailu is a graph runtime." }],
3506
+ * embeddings: fakeEmbeddings // deterministic in tests
3507
+ * }));
3508
+ * ```
3509
+ */
3510
+
3511
+ /** A candidate document to embed into the store before querying. */
3512
+ type SemanticRetrieverDoc = {
3513
+ id: string;
3514
+ content: string;
3515
+ };
3516
+ /** Params for {@link semanticRetriever}. */
3517
+ type SemanticRetrieverParams = {
3518
+ /** A literal query (mutually exclusive with `queryFrom`). */
3519
+ query?: string;
3520
+ /** A channel whose value supplies the query (takes precedence when its channel is set). */
3521
+ queryFrom?: string;
3522
+ /** Channel receiving the top-`k` `{ id, content, score }` array. */
3523
+ into: string;
3524
+ /** Number of results to keep. Defaults to 4. */
3525
+ k?: number;
3526
+ /**
3527
+ * Documents to embed into the store before querying. Each run embeds and upserts these
3528
+ * (idempotent by id). Omit to query against an already-populated injected `store`.
3529
+ */
3530
+ docs?: SemanticRetrieverDoc[];
3531
+ /**
3532
+ * The vector store to upsert into / query. Defaults to a fresh in-memory store created
3533
+ * per factory call. Inject a shared/persistent store to reuse embeddings across runs.
3534
+ */
3535
+ store?: VectorStore;
3536
+ /**
3537
+ * The embeddings client. Defaults to a real Mistral client ({@link createEmbeddings}).
3538
+ * Inject a fake (deterministic vectors) to keep a test offline.
3539
+ */
3540
+ embeddings?: Embeddings;
3541
+ };
3542
+ /**
3543
+ * Build the semantic-retriever node handler. Each invocation embeds any supplied `docs`
3544
+ * into the store, embeds the resolved query, and writes the top-`k`
3545
+ * {@link VectorStoreMatch} array (`{ id, content, score }`) to `into`. Throws no special
3546
+ * error class — it surfaces the underlying embeddings error if the real client can't run
3547
+ * (no key / no transport), which is the honest failure mode for a real connector.
3548
+ */
3549
+ declare const semanticRetriever: (params: SemanticRetrieverParams) => NodeHandler;
3550
+
3551
+ /**
3552
+ * The static catalog metadata that backs the API's `/catalog` endpoint: one entry per
3553
+ * component, prebuilt agent and capability tier. This is the SDK's source of truth for
3554
+ * the building-block library; the API validates these arrays against the
3555
+ * `@ailu-ai/contracts` catalog DTOs and forwards them to Studio unchanged.
3556
+ *
3557
+ * The arrays mirror, one-for-one:
3558
+ * - the 30 component factories in {@link import("./components.js").components} (28 pure
3559
+ * Rust-backed components + 2 vendor-I/O integration components), with their real
3560
+ * factory params;
3561
+ * - the 16 prebuilt micro-agent definitions (the Rust `PrebuiltAgent` table);
3562
+ * - the 4 capability tiers, each carrying the {@link DEFAULT_TIER_TABLE} per-provider
3563
+ * recommended models.
3564
+ *
3565
+ * The shapes are deliberately plain data (no closures) so they map 1:1 onto the
3566
+ * contracts DTOs and serialize over the wire as-is.
3567
+ */
3568
+
3569
+ /** The category buckets a component falls into in the library. */
3570
+ type ComponentCategory = "prompt" | "validation" | "parsing" | "routing" | "retrieval" | "text" | "data" | "integration" | "splitter" | "generation" | "evaluation" | "writer";
3571
+ /** A single parameter a component factory accepts. */
3572
+ type ComponentParamMeta = {
3573
+ name: string;
3574
+ type: string;
3575
+ required: boolean;
3576
+ description: string;
3577
+ };
3578
+ /** One entry in the component library: a `kind` plus its presentation + params. */
3579
+ type ComponentCatalogEntry = {
3580
+ /** The component kind — a {@link ComponentKind} for pure components, or an integration name. */
3581
+ kind: ComponentKind | "httpFetch" | "webSearch";
3582
+ title: string;
3583
+ category: ComponentCategory;
3584
+ description: string;
3585
+ params: ComponentParamMeta[];
3586
+ /** `true` for the vendor-I/O integration components (httpFetch / webSearch). */
3587
+ integration: boolean;
3588
+ };
3589
+ /** One entry in the prebuilt-agent catalog, mirroring the Rust `PrebuiltAgent` table. */
3590
+ type PrebuiltAgentCatalogEntry = {
3591
+ name: string;
3592
+ title: string;
3593
+ description: string;
3594
+ tier: ModelTier;
3595
+ tools: string[];
3596
+ suspendForApproval: boolean;
3597
+ outputChannel: string;
3598
+ };
3599
+ /** Describes one capability tier plus its recommended per-provider models. */
3600
+ type ModelTierInfo = {
3601
+ tier: ModelTier;
3602
+ description: string;
3603
+ /** `provider -> model` recommended defaults for this tier. */
3604
+ models: Record<string, string>;
3605
+ };
3606
+ /**
3607
+ * The 30 component catalog entries: 28 pure Rust-backed components (whose `kind`
3608
+ * matches {@link ComponentKind} / `ComponentRegistry::kinds()`) plus 2 vendor-I/O
3609
+ * integration components. Params mirror the real factory `*Params` types in
3610
+ * `./components.ts`.
3611
+ */
3612
+ declare const componentCatalog: readonly ComponentCatalogEntry[];
3613
+ /**
3614
+ * The 16 prebuilt micro-agent catalog entries, mirroring the Rust `PrebuiltAgent`
3615
+ * table (`crates/components/src/prebuilt.rs`) and the SDK `prebuilt-agents.ts` `DEFS`:
3616
+ * name, tier, description, tools, suspend flag and output channel.
3617
+ */
3618
+ declare const prebuiltCatalog: readonly PrebuiltAgentCatalogEntry[];
3619
+ /**
3620
+ * The 4 capability tiers, each carrying its description and the per-provider
3621
+ * recommended models from {@link DEFAULT_TIER_TABLE} (anthropic / mistral / ollama).
3622
+ * Derived from the gateway table so the catalog tracks the source of truth.
3623
+ */
3624
+ declare const tierCatalog: readonly ModelTierInfo[];
3625
+
3626
+ /**
3627
+ * Generate `llms.txt` (the [llmstxt.org](https://llmstxt.org) convention): a single, accurate
3628
+ * ground-truth file an AI coding agent reads to use Ailu **without hallucinating the API**.
3629
+ * Built from the same catalogs the engine validates against, so it cannot drift. Pure — no I/O.
3630
+ */
3631
+ declare function generateLlmsTxt(): string;
3632
+
3633
+ /**
3634
+ * A minimal JSON Schema (the subset we emit). Enough for an AI agent or a validator to know a
3635
+ * component's parameter shape before compiling a graph (ADR AI-DX). Generated from the catalog's
3636
+ * declared param `type` strings — the single source of truth, so it can't drift from the docs.
3637
+ */
3638
+ type JsonSchema = {
3639
+ type?: string;
3640
+ enum?: string[];
3641
+ items?: JsonSchema;
3642
+ description?: string;
3643
+ properties?: Record<string, JsonSchema>;
3644
+ required?: string[];
3645
+ additionalProperties?: boolean;
3646
+ };
3647
+ /** Map a catalog param `type` string (a TS-ish annotation) to a JSON Schema fragment. */
3648
+ declare function paramTypeToJsonSchema(type: string): JsonSchema;
3649
+ /** One component's identity + its parameter JSON Schema. */
3650
+ type ComponentSchema = {
3651
+ kind: string;
3652
+ title: string;
3653
+ category: string;
3654
+ description: string;
3655
+ integration: boolean;
3656
+ paramsSchema: JsonSchema;
3657
+ };
3658
+ /** The JSON Schema of one catalog entry's params. */
3659
+ declare function componentSchema(entry: ComponentCatalogEntry): ComponentSchema;
3660
+ /** Per-node JSON Schemas for the whole component library — what `list_catalog_with_schemas`
3661
+ * (MCP) and the docs reference emit. Keyed by `kind`. */
3662
+ declare function componentSchemas(): Record<string, ComponentSchema>;
3663
+
3664
+ /**
3665
+ * `ailu dev` — the local run inspector (ADR DX batch 4). Run a graph and **watch it think** in
3666
+ * the browser: a live node-by-node timeline, the lifecycle-event stream, each node's output, and a
3667
+ * **governance lens** that marks exactly where the run suspended (human gate / approval) and why —
3668
+ * with `explain()` and a one-click resume. Dependency-free (node:http only) and self-contained
3669
+ * (the page is inline HTML/JS — no CDN, no build step), so it drops into any project.
3670
+ *
3671
+ * v1 streams a single run live. The replay-from-checkpoint primitive now exists —
3672
+ * {@link replayCatalogGraph} forks a deterministic re-execution from any checkpoint (ADR 0038) and
3673
+ * {@link verifyReplayDecisions} proves it reproduces the attested decisions. Wiring that into a
3674
+ * one-click "rewind & replay" governance lens in this page is the remaining v2 surface.
3675
+ */
3676
+ type InspectorHandle = {
3677
+ /** The URL the inspector is served at. */
3678
+ url: string;
3679
+ /** A promise that resolves when the inspected run settles (completed / failed / suspended). */
3680
+ done: Promise<void>;
3681
+ /** Shut the server down. */
3682
+ close: () => Promise<void>;
3683
+ };
3684
+ type InspectorOptions = {
3685
+ /** Port to listen on (default 4517; 0 picks a free port). */
3686
+ port?: number;
3687
+ /** Host to bind (default 127.0.0.1 — local only). */
3688
+ host?: string;
3689
+ };
3690
+ /** Serve a live inspector for a single run of `app`. Drives `app.run(initialData)`, streaming
3691
+ * every lifecycle event to the page over SSE; surfaces `explain()` on suspend/settle. */
3692
+ declare function serveInspector<TState extends ChannelValues>(app: CompiledGraph<TState>, initialData: InitialData<TState>, options?: InspectorOptions): Promise<InspectorHandle>;
3693
+
3694
+ /** True when graph validation is being served by the Rust core. */
3695
+ declare const rustValidatorActive: () => boolean;
3696
+
3697
+ /** True when the native addon exposes the async run bridge (execution can use Rust). */
3698
+ declare const rustEngineAvailable: () => boolean;
3699
+ /**
3700
+ * One granted tool on the approve path, with the governance provenance the Rust
3701
+ * guard-rail validates (matches Rust `ApprovedTool`, camelCase): the principal who
3702
+ * *requested* the approval and the (distinct) principal who *resolved* it. The bridge
3703
+ * rejects the resume if `resolvedBy` is empty or equals `requestedBy` (no self-approval).
3704
+ */
3705
+ type ApprovedToolWire = {
3706
+ name: string;
3707
+ requestedBy: string;
3708
+ resolvedBy: string;
3709
+ /**
3710
+ * Content-scoped grant key (ADR 0024 phase 2c): `"<name>#<sha256(input)>"` for a
3711
+ * guarded fs write, pinning the grant to the exact call. When set, the engine writes
3712
+ * THIS key (not the bare name) into `__approvedTools`. Omitted for a name-only grant.
3713
+ */
3714
+ key?: string;
3715
+ };
3716
+
3717
+ /**
3718
+ * Observability (ADR 0028 phase 7): turn a run's lifecycle events into **OTLP spans** and ship
3719
+ * them to any OpenTelemetry endpoint — LangSmith, Langfuse, Phoenix, Datadog, Grafana, … — plus
3720
+ * a **cost** mapping from the token usage the engine now reports on `AgentResult.usage`.
3721
+ *
3722
+ * This lives in the SDK, not the engine: the engine already emits a `RunEvent` per node
3723
+ * transition (the audit journal) and `AgentResult.usage` per agent — observability is a *read
3724
+ * view* over that, an integration concern. The engine stays lean; nothing here can alter a run.
3725
+ *
3726
+ * ```ts
3727
+ * const stop = exportTracesToOtlp(app, { endpoint: process.env.OTEL_EXPORTER_OTLP_ENDPOINT });
3728
+ * await app.run({ … });
3729
+ * stop(); // flushes + unsubscribes (also auto-flushes on run_completed / run_failed)
3730
+ * ```
3731
+ */
3732
+
3733
+ /** Price of a model, in US dollars per 1,000,000 tokens. */
3734
+ type ModelPrice = {
3735
+ inPerMtok: number;
3736
+ outPerMtok: number;
3737
+ };
3738
+ /** A price table keyed by model id (the `AgentResult.usage` carries the model on its calls). */
3739
+ type PriceBook = Record<string, ModelPrice>;
3740
+ /** Token usage shape (matches `AgentResult.usage` / the engine's `LlmUsage`). */
3741
+ type TokenUsage = {
3742
+ promptTokens: number;
3743
+ completionTokens: number;
3744
+ cacheReadTokens?: number;
3745
+ cacheWriteTokens?: number;
3746
+ };
3747
+ /**
3748
+ * A small default price book ($/Mtok, indicative — prices drift; supply your own to override).
3749
+ * Keys are model ids; an unknown model costs `0` (and is reported as such, never guessed).
3750
+ */
3751
+ declare const DEFAULT_PRICE_BOOK: PriceBook;
3752
+ /**
3753
+ * Compute the US-dollar cost of a usage record against a price book. Cache-read tokens (when
3754
+ * priced separately) are not modelled here — they fold into prompt tokens at the input rate, a
3755
+ * conservative upper bound. Returns `0` for an unknown model (never a guess).
3756
+ */
3757
+ declare function computeCost(usage: TokenUsage, model: string, book?: PriceBook): number;
3758
+ /** Minimal `fetch` shape, so the exporter is injectable for tests (no real network). */
3759
+ type OtlpFetch = (url: string, init: {
3760
+ method: string;
3761
+ headers: Record<string, string>;
3762
+ body: string;
3763
+ }) => Promise<{
3764
+ ok: boolean;
3765
+ status: number;
3766
+ }>;
3767
+ type OtelExporterOptions = {
3768
+ /** OTLP/HTTP traces endpoint. Defaults to `AILU_OTEL_EXPORTER_URL` env. */
3769
+ endpoint?: string;
3770
+ /** `service.name` resource attribute. Default `"ailu"`. */
3771
+ serviceName?: string;
3772
+ /** Extra headers (e.g. an API key for LangSmith / Langfuse). */
3773
+ headers?: Record<string, string>;
3774
+ /** Price book for the `ailu.cost.usd` span attribute. Default {@link DEFAULT_PRICE_BOOK}. */
3775
+ priceBook?: PriceBook;
3776
+ /** Injected `fetch` (tests). Defaults to the global `fetch`. */
3777
+ fetchImpl?: OtlpFetch;
3778
+ };
3779
+ type OpenSpan = {
3780
+ spanId: string;
3781
+ name: string;
3782
+ startNano: string;
3783
+ nodeId?: string;
3784
+ };
3785
+ type FinishedSpan = OpenSpan & {
3786
+ endNano: string;
3787
+ status: 0 | 1 | 2;
3788
+ attributes: Record<string, string | number | boolean>;
3789
+ };
3790
+ /**
3791
+ * Build the OTLP/HTTP-JSON `traces` payload for one run: a root span for the run plus one span
3792
+ * per completed/failed node, each tagged with `ailu.run_id` / `ailu.node_id`, and agent
3793
+ * nodes additionally with token usage + computed cost.
3794
+ */
3795
+ declare function buildOtlpPayload(runId: string, spans: FinishedSpan[], serviceName: string): string;
3796
+ /**
3797
+ * Subscribe to `app`'s run-lifecycle events and export each run as an OTLP trace. Returns an
3798
+ * unsubscribe fn; the run is flushed automatically on `run_completed` / `run_failed` (and on the
3799
+ * returned fn). **Fail-open**: an export error is swallowed (best-effort observability never
3800
+ * fails a run). A missing endpoint disables export (the returned fn is a no-op).
3801
+ */
3802
+ declare function exportTracesToOtlp(app: CompiledGraph, options?: OtelExporterOptions): () => void;
3803
+
3804
+ /**
3805
+ * Run a **catalog graph** on the Rust engine.
3806
+ *
3807
+ * A catalog graph is a plain {@link GraphDefinition} (e.g. one authored in the Studio
3808
+ * graph editor, persisted as data, with no in-process TS handlers) whose nodes carry
3809
+ * the SHARED CARRIER in `node.metadata`:
3810
+ *
3811
+ * - a COMPONENT node carries `node.metadata.component = { kind, params }`
3812
+ * - an AGENT node carries `node.metadata.agent = { provider?, model?, tier?, system?,
3813
+ * toolNames?, maxIterations?, suspendForApproval?, approvalToolNames?, outputChannel? }`
3814
+ *
3815
+ * This is the seam the control plane (`apps/api`) uses to EXECUTE a graph built from
3816
+ * the catalog: it reads each node's metadata, assembles the engine's
3817
+ * `EngineSpec.componentNodes` + `agents` maps + the `jsNodeIds` for plain
3818
+ * action/tool nodes, and drives the run on the **Rust engine** via `@ailu-ai/napi`.
3819
+ *
3820
+ * Unlike {@link import("./builder.js").GraphBuilder}, there are no TS handler closures
3821
+ * here — components and agents run **natively** in Rust, and plain action/tool nodes
3822
+ * are inert JS seams (they return an empty channel update). The carrier IS the wiring.
3823
+ *
3824
+ * The carrier readers below mirror the canonical Zod schema in
3825
+ * `@ailu-ai/contracts` (`node-metadata.ts`); the SDK stays dependency-free of the
3826
+ * contracts package, so the narrowing is duplicated structurally here. The control
3827
+ * plane is free to validate the carrier with the contracts schema before handing the
3828
+ * definition to this runner.
3829
+ */
3830
+
3831
+ /** The component carrier on `node.metadata.component`. Mirrors the contracts schema. */
3832
+ type ComponentCarrier = {
3833
+ kind: string;
3834
+ params: Record<string, unknown>;
3835
+ };
3836
+ /** The agent carrier on `node.metadata.agent`. Mirrors the contracts schema. */
3837
+ type AgentCarrier = {
3838
+ provider?: string;
3839
+ model?: string;
3840
+ tier?: ModelTier;
3841
+ /** A custom OpenAI-compatible endpoint, and the env var naming its key (`model.openaiCompatible`). */
3842
+ baseURL?: string;
3843
+ apiKeyEnv?: string;
3844
+ system?: string;
3845
+ toolNames?: string[];
3846
+ /** Each tool's description + input JSON Schema, as the LLM sees them. */
3847
+ toolSpecs?: RustToolSpec[];
3848
+ maxIterations?: number;
3849
+ suspendForApproval?: boolean;
3850
+ approvalToolNames?: string[];
3851
+ outputChannel?: string;
3852
+ /** ADR 0014 — terse output directive on the system prompt. */
3853
+ outputStyle?: "terse";
3854
+ /** ADR 0014 — cap (chars) on the agent's seed message (the injected `Input`/`State` dump). */
3855
+ contextBudget?: number;
3856
+ /** ADR 0022/0023 — durable channel the agent's `writeTodos` list is persisted into. */
3857
+ todosChannel?: string;
3858
+ /** ADR 0030 phase 9e — channel carrying the run's multimodal input blocks. */
3859
+ inputBlocksChannel?: string;
3860
+ /** The only channels the agent is shown in its seed state (context isolation). */
3861
+ visibleChannels?: string[];
3862
+ /** ADR 0026 phase 11 — governed long-term memory overlay. */
3863
+ memory?: {
3864
+ namespace: string;
3865
+ topK?: number;
3866
+ recall?: "vector" | "graph" | "both";
3867
+ };
3868
+ /** ADR 0035 phase 12 — governed skills (progressive disclosure) overlay. */
3869
+ skills?: {
3870
+ namespace: string;
3871
+ required?: string[];
3872
+ advisoryK?: number;
3873
+ };
3874
+ /** ADR 0024 — opt this agent into the governed virtual filesystem tools. */
3875
+ enableFs?: boolean;
3876
+ /**
3877
+ * ADR 0075 (issue #566 G3) — attach an external MCP server's tools to this agent, mid-run. The
3878
+ * control plane resolves this to a connection, discovers the server's tools, and merges them into
3879
+ * `toolNames`/`approvalToolNames` before the run reaches this carrier — the Rust engine itself
3880
+ * never resolves an MCP connection.
3881
+ */
3882
+ mcpConnectionId?: string;
3883
+ /**
3884
+ * Issue #566 G19 — governed action-tool connectors, keyed by provider id ("slack" first).
3885
+ * Resolved the same way as `mcpConnectionId`; generalized to a map so future providers don't
3886
+ * need a new engine field/release each time.
3887
+ */
3888
+ actionConnections?: Record<string, string>;
3889
+ /**
3890
+ * ADR 0025 phase 3d — the resolved efficiency middleware list. Present on graphs built by
3891
+ * the phase-3d SDK; absent on a pre-3d persisted node (the Rust bridge then falls back to
3892
+ * the legacy `outputStyle`/`contextBudget` knobs above, so old graphs keep their behaviour).
3893
+ */
3894
+ resolvedMiddleware?: EfficiencyMiddlewareSpec[];
3895
+ };
3896
+ /**
3897
+ * The mapAgents carrier on `node.metadata.mapAgents` (ADR 0027 phase 4b — dynamic fan-out). Mirrors
3898
+ * the contracts schema: run `subAgent` once per item in `overChannel` and collect the per-item results
3899
+ * (input order) into `joinAt`. The sub-agent is a full agent carrier → skills/memory/fs/planning apply.
3900
+ */
3901
+ type MapAgentCarrier = {
3902
+ overChannel: string;
3903
+ joinAt: string;
3904
+ subAgent: AgentCarrier;
3905
+ suspendForApproval?: boolean;
3906
+ };
3907
+ /** Outcome of a catalog-graph run: the terminal/suspended state and a flat status. */
3908
+ type CatalogRunOutcome = {
3909
+ /** The final (or suspended) graph state, channels included. */
3910
+ state: GraphState;
3911
+ /** `"running" | "suspended" | "completed" | "failed" | "cancelled"` — the state's status.
3912
+ * `"cancelled"` (ADR 0044) means the run stopped at a node boundary because `options.signal`
3913
+ * was aborted; its last checkpoint is durable, so it stays resumable and replayable. */
3914
+ status: string;
3915
+ /** True when execution ran on the Rust engine (always, since this seam requires it). */
3916
+ usedRustEngine: true;
3917
+ /**
3918
+ * Replay-as-evidence (ADR 0038): the recorded LLM I/O + clock journal (`{ decisions, clock }`
3919
+ * JSON) when the run executed in record mode (`AILU_LLM_RECORD`); `undefined` otherwise. The
3920
+ * control plane persists it to re-feed a later replay (`verify-replay`).
3921
+ */
3922
+ replayJournal?: string;
3923
+ /**
3924
+ * Replay-as-evidence (ADR 0040): the run's ENTRY state (initial state, before the entry node ran),
3925
+ * surfaced only on a record-mode run; `undefined` otherwise. The control plane persists it as the
3926
+ * checkpoint a later `verify-replay` seeds {@link replayCatalogGraph} from.
3927
+ */
3928
+ entryState?: GraphState;
3929
+ /**
3930
+ * The pending approvals — the subjects the run requested when it suspended on a gate (empty if it
3931
+ * completed without gating). On a {@link replayCatalogGraph} this is what the deterministic
3932
+ * re-execution requested: the faithfulness signal `verify-replay` compares to the attested chain.
3933
+ */
3934
+ pendingApprovals?: {
3935
+ subject: string;
3936
+ reason: string;
3937
+ approvalKey?: string;
3938
+ input?: unknown;
3939
+ }[];
3940
+ };
3941
+ /** Options for {@link runCatalogGraph} / {@link resumeCatalogGraph}. */
3942
+ type RunCatalogGraphOptions = {
3943
+ /** A stable run id. Defaults to a generated one. */
3944
+ runId?: RunId;
3945
+ /** Initial channel data seeding the run. */
3946
+ initialData?: Record<string, unknown>;
3947
+ /** Subscribe to forwarded run-lifecycle events (every node transition). */
3948
+ onEvent?: (event: RunEvent) => void;
3949
+ /**
3950
+ * Cooperative cancellation (ADR 0044) — the kill switch. Abort this signal and the engine
3951
+ * stops the run at the **next node boundary**: it finishes the node it is on, writes that
3952
+ * node's checkpoint, emits a `run_cancelled` event and returns a terminal
3953
+ * {@link CatalogRunOutcome} with `status: "cancelled"`.
3954
+ *
3955
+ * It is **cooperative, never pre-emptive**. A node already executing (an agent mid-LLM-call,
3956
+ * a tool mid-HTTP-request) always runs to completion — so cancelling can never tear state,
3957
+ * and the last checkpoint always remains authoritative. That also means cancellation is not
3958
+ * instantaneous: its latency is the duration of the node in flight. A caller that needs a
3959
+ * *bounded* stop must impose its own deadline around this call; the engine deliberately
3960
+ * offers no hard abort, because killing a run mid-node is precisely what would break the
3961
+ * checkpoint-per-node guarantee everything else depends on.
3962
+ *
3963
+ * An ALREADY-aborted signal stops the run before its first node executes. Omit for the
3964
+ * previous behaviour (a run that can only complete, suspend or fail).
3965
+ */
3966
+ signal?: AbortSignal;
3967
+ /**
3968
+ * Opt into per-token streaming (ADR 0033 phase 13 / ADR 0060). When true, an agent node's LLM call
3969
+ * streams real provider deltas, surfaced as `token_delta` {@link RunEvent}s over {@link onEvent} — so
3970
+ * a catalog run (e.g. Governed Ask) can stream its answer token-by-token, not just return a final
3971
+ * result. The assembled state is byte-identical either way (deltas bypass the checkpoint/journal —
3972
+ * observational only). Default false (the run returns its terminal state with no token events).
3973
+ */
3974
+ streamTokens?: boolean;
3975
+ /**
3976
+ * Route the run's approvals through an {@link ApprovalEngine}. When present, the
3977
+ * agents run natively on Rust as usual, but the moment the run suspends for approval
3978
+ * the seam files one request per gated tool (`requestedBy = nodeId`, the agent's own
3979
+ * subject) and stashes the engine ids in the `__approvalIds` channel of the returned
3980
+ * state — so a human resolves them out of band (the engine forbids self-approval).
3981
+ * Pass the same engine to {@link resumeCatalogGraph}: it refuses to resume until the
3982
+ * engine has approved what the run waits on. Absent: the run is ungoverned.
3983
+ */
3984
+ approvalEngine?: ApprovalEngine;
3985
+ /**
3986
+ * Per-provider API keys injected by the control plane (ADR 0010), keyed by provider
3987
+ * slug (`openai`, `anthropic`, `mistral`, …). Threaded into the Rust `EngineSpec` so
3988
+ * the gateway resolves each agent's key tenant-key-first then host env. Omit to rely
3989
+ * purely on the host env (local dev, tests).
3990
+ */
3991
+ providerKeys?: Record<string, string>;
3992
+ /**
3993
+ * Per-path filesystem permission rules (ADR 0024 phase 2d) the control plane resolved
3994
+ * for this run (from its owner-only `fs_path_policy` table), compiled into the engine's
3995
+ * `EngineSpec.fsPolicy` and applied to every fs-enabled agent. Omit for fail-closed
3996
+ * read-only everywhere.
3997
+ */
3998
+ fsPolicy?: FsPolicyRule[];
3999
+ /**
4000
+ * The tenant's governed skills for this run (ADR 0049 B-3) — the control plane's skill store. The
4001
+ * engine builds a run-scoped, tenant-isolated store from these and each agent's SkillMiddleware
4002
+ * selects from it. Omit/empty → the OSS shared in-memory store (no skills).
4003
+ */
4004
+ skills?: SkillRecord[];
4005
+ /**
4006
+ * Host tools for this run (ADR 0041 D1): JS-backed `{ name, execute }` bindings made callable by
4007
+ * ANY catalog agent whose `toolNames` includes the name — through the same napi host-tool seam the
4008
+ * in-process builder path uses (`on_node` `kind:"tool"`). Names NOT bound here keep the no-op stub
4009
+ * behaviour, so a graph remains pure data and existing runs are untouched. Supplied per CALL, never
4010
+ * persisted with the graph. NOTE (ADR 0041 E2): until the replay journal records host-tool results,
4011
+ * the caller must not combine `tools` with record-mode replay evidence — a replayed run would
4012
+ * re-execute the tools and may diverge.
4013
+ */
4014
+ tools?: RustToolBinding[];
4015
+ /**
4016
+ * Child graph definitions for `subgraph`-type nodes (ADR 0042, product ADR 0068 — child
4017
+ * workflows). A catalog node with `type: "subgraph"` + `subgraphId` resolves against this list,
4018
+ * exactly like the in-process builder path (`GraphBuilder.subgraph()` → `CompiledGraph`) already
4019
+ * does — `execute_subgraph` (the Rust engine) recursively starts/resumes the child sharing this
4020
+ * run's checkpointer, propagates the child's suspension to the parent, and propagates a child
4021
+ * failure as the parent's own error. Omit/empty for a graph with no subgraph nodes (today's
4022
+ * behaviour, unchanged).
4023
+ */
4024
+ subgraphs?: GraphDefinition[];
4025
+ };
4026
+ /** Raised when the native engine is unavailable — catalog graphs require it. */
4027
+ declare class RustEngineUnavailableError extends Error {
4028
+ constructor();
4029
+ }
4030
+ /** Narrow a node's open metadata bag to its COMPONENT carrier, if present and valid. */
4031
+ declare const readComponentCarrier: (metadata: Record<string, unknown> | undefined) => ComponentCarrier | undefined;
4032
+ /** Narrow a node's open metadata bag to its AGENT carrier, if present and valid. */
4033
+ declare const readAgentCarrier: (metadata: Record<string, unknown> | undefined) => AgentCarrier | undefined;
4034
+ /** Narrow a node's open metadata bag to its mapAgents (dynamic fan-out) carrier, if present + valid. */
4035
+ declare const readMapAgentCarrier: (metadata: Record<string, unknown> | undefined) => MapAgentCarrier | undefined;
4036
+ /**
4037
+ * Run a catalog {@link GraphDefinition} (whose nodes carry `node.metadata.component`
4038
+ * and `node.metadata.agent`) to completion or suspension on the **Rust engine**.
4039
+ *
4040
+ * Throws {@link RustEngineUnavailableError} when the native addon is absent.
4041
+ */
4042
+ declare const runCatalogGraph: (definition: GraphDefinition, options?: RunCatalogGraphOptions) => Promise<CatalogRunOutcome>;
4043
+ /**
4044
+ * Resume a previously-suspended catalog run (e.g. past a human gate) from its
4045
+ * serialized {@link GraphState}, on the **Rust engine**. The bridge seeds its
4046
+ * checkpointer with this state and resumes from it.
4047
+ *
4048
+ * With an `approvalEngine`, the resume first checks the engine: every request the run
4049
+ * waits on must be decided, no human gate may be rejected, and every granted tool must
4050
+ * match an approved request. Otherwise it throws {@link ApprovalNotGrantedError} and
4051
+ * nothing runs.
4052
+ *
4053
+ * Throws {@link RustEngineUnavailableError} when the native addon is absent.
4054
+ */
4055
+ declare const resumeCatalogGraph: (definition: GraphDefinition, state: GraphState, options?: Pick<RunCatalogGraphOptions, "onEvent" | "approvalEngine" | "providerKeys" | "fsPolicy" | "skills" | "tools" | "subgraphs" | "signal"> & {
4056
+ /**
4057
+ * Human-granted tools to unlock on resume, each carrying its `{ name, requestedBy,
4058
+ * resolvedBy }` provenance. Passed straight through to the Rust bridge, which
4059
+ * re-validates the no-self-approval invariant per tool on `Entry::Resume` and writes
4060
+ * only the validated names into `__approvedTools`. With an `approvalEngine`, each
4061
+ * grant must also match a request the engine records as approved by `resolvedBy`.
4062
+ * Omitted/empty: an ordinary resume that unlocks no tools.
4063
+ */
4064
+ approvedTools?: ApprovedToolWire[];
4065
+ }) => Promise<CatalogRunOutcome>;
4066
+ /**
4067
+ * Replay-as-evidence (ADR 0038): re-execute a recorded catalog run from `checkpointId`, re-feeding
4068
+ * its `replayJournal` (LLM outputs + timestamps from a record-mode {@link runCatalogGraph}) on the
4069
+ * Rust engine so the re-derivation is deterministic. A forked, READ-ONLY run — it never files
4070
+ * approval requests or opens gates. Returns the replayed state; the caller compares its governed
4071
+ * decisions to the attested chain via {@link verifyReplayDecisions}. Requires a native addon with
4072
+ * replay support (`engineReplay`); throws otherwise.
4073
+ */
4074
+ declare const replayCatalogGraph: (definition: GraphDefinition, state: GraphState, checkpointId: string, replayJournal: string, options?: Pick<RunCatalogGraphOptions, "onEvent" | "providerKeys" | "fsPolicy" | "skills" | "subgraphs">) => Promise<CatalogRunOutcome>;
4075
+ /**
4076
+ * Subject prefix for a `human-gate` node's own {@link ApprovalEngine} request (issue
4077
+ * #496), distinct from {@link TOOL_SUBJECT_PREFIX}-style tool subjects an agent files —
4078
+ * the control plane uses this to tell a rejected GATE apart from a rejected TOOL when
4079
+ * deciding whether a run becomes `"rejected"` (a tool rejection just leaves a tool
4080
+ * unlocked; a gate rejection must block `resume()` outright).
4081
+ */
4082
+ declare const GATE_SUBJECT_PREFIX = "gate:";
4083
+ /** Type guard a node carries either catalog carrier. Useful to decide the run path. */
4084
+ declare const isCatalogGraph: (definition: GraphDefinition) => boolean;
4085
+
4086
+ /** One governance decision, reduced to what a replay can faithfully reproduce. */
4087
+ type ReplayDecision = {
4088
+ /** `"approved" | "rejected"`. */
4089
+ status: string;
4090
+ /** The decision subject, derived identically to the attestation (`description || canonicalJson`). */
4091
+ subject: string;
4092
+ };
4093
+ /** The result of comparing the attested decisions to the replayed ones, in order. */
4094
+ type VerifyReplayResult = {
4095
+ /** True iff every attested decision is reproduced, in the same order, by the replay. */
4096
+ ok: boolean;
4097
+ attested: ReplayDecision[];
4098
+ replayed: ReplayDecision[];
4099
+ /** Per-position divergences (missing on either side, or a status/subject differs). */
4100
+ mismatches: {
4101
+ index: number;
4102
+ attested?: ReplayDecision;
4103
+ replayed?: ReplayDecision;
4104
+ }[];
4105
+ };
4106
+ /**
4107
+ * Compare the ordered `{ status, subject }` decision sets of the attested chain and a replayed run.
4108
+ * Order matters (a dropped, reordered, or status-flipped decision is a mismatch). Pure + crypto-free.
4109
+ */
4110
+ declare const verifyReplayDecisions: (attested: ReplayDecision[], replayed: ReplayDecision[]) => VerifyReplayResult;
4111
+
4112
+ /**
4113
+ * The final answer of an agent run: the text after the last `final:` marker in its `reasoning`
4114
+ * (the whole `reasoning` when there is no marker). An empty string for a missing result.
4115
+ *
4116
+ * ```ts
4117
+ * const out = await app.run({ question: "What is a checkpoint?" });
4118
+ * console.log(finalAnswer(out.channels.agentResult));
4119
+ * ```
4120
+ *
4121
+ * For a typed answer, run the agent with a `structuredOutput` middleware and read
4122
+ * `result.structuredOutput` instead.
4123
+ */
4124
+ declare const finalAnswer: (result: Pick<AgentResult, "reasoning"> | undefined) => string;
4125
+
4126
+ type Loc$1 = {
4127
+ line: number;
4128
+ col: number;
4129
+ file: string;
4130
+ };
4131
+
4132
+ type Diagnostic$1 = {
4133
+ code: string;
4134
+ message: string;
4135
+ loc: Loc$1;
4136
+ severity: "error" | "warning";
4137
+ };
4138
+
4139
+ declare const compileGraphFile: (content: string, file: string) => {
4140
+ result?: GraphDefinition;
4141
+ diagnostics: Diagnostic$1[];
4142
+ };
4143
+
4144
+ type Loc = {
4145
+ line: number;
4146
+ col: number;
4147
+ file: string;
4148
+ };
4149
+
4150
+ type Diagnostic = {
4151
+ code: string;
4152
+ message: string;
4153
+ loc: Loc;
4154
+ severity: "error" | "warning";
4155
+ };
4156
+
4157
+ type PromptTemplate = {
4158
+ name: string;
4159
+ template: string;
4160
+ diagnostics: Diagnostic[];
4161
+ render: (variables: Record<string, unknown>) => {
4162
+ content: string;
4163
+ diagnostics: Diagnostic[];
4164
+ };
4165
+ };
4166
+ type AgentConfig = {
4167
+ id: string;
4168
+ description: string;
4169
+ prompt: string;
4170
+ tools: string[];
4171
+ };
4172
+ type ChainDefinition = {
4173
+ id: string;
4174
+ steps: Array<{
4175
+ agentId: string;
4176
+ input?: Record<string, unknown>;
4177
+ }>;
4178
+ };
4179
+
4180
+ type CompileResult = PromptTemplate | AgentConfig | ChainDefinition;
4181
+ declare const compileFile: (content: string, file: string) => {
4182
+ result?: CompileResult;
4183
+ diagnostics: Diagnostic[];
4184
+ };
4185
+
4186
+ /**
4187
+ * Resource-search seam types (ADR 0011). A `SearchDocument` is what a producer (graphs, KB,
4188
+ * agent-types) pushes into the index; a `SearchHit` is what a query returns. These are the
4189
+ * wire-neutral shapes the engine owns — the control plane maps them to/from contracts DTOs.
4190
+ */
4191
+ /** The kinds of resource the unified search indexes. */
4192
+ type SearchResourceType = "graph" | "agent" | "kb";
4193
+ /** One indexable resource. `tenantId === null` marks a global resource (catalog graph, agent
4194
+ * type) visible to every tenant. `namespace` is carried for KB docs (used in the href / display). */
4195
+ interface SearchDocument {
4196
+ type: SearchResourceType;
4197
+ /** Unique within its `type` (e.g. a graph id, a `namespace:path` for KB). */
4198
+ id: string;
4199
+ tenantId: string | null;
4200
+ namespace?: string;
4201
+ /** Short, high-weight label shown as the result title. */
4202
+ title: string;
4203
+ /** Full searchable body (description, content, labels…). */
4204
+ text: string;
4205
+ /** Studio route to open the resource. */
4206
+ href: string;
4207
+ /** ISO-8601 last-updated, for tie-breaking / display. */
4208
+ updatedAt?: string;
4209
+ }
4210
+ /** One ranked result. */
4211
+ interface SearchHit {
4212
+ type: SearchResourceType;
4213
+ id: string;
4214
+ tenantId: string | null;
4215
+ namespace?: string;
4216
+ title: string;
4217
+ /** A short excerpt around the match (or the title when the body did not match). */
4218
+ snippet: string;
4219
+ href: string;
4220
+ /** Higher is more relevant. Backend-specific scale; only the ordering is meaningful. */
4221
+ score: number;
4222
+ }
4223
+ /** Query scoping. `tenantId` is mandatory — a search is always tenant-scoped (global docs are
4224
+ * additionally included). `types` restricts to a subset; omitted means all. */
4225
+ interface SearchQueryOptions {
4226
+ tenantId: string;
4227
+ types?: SearchResourceType[];
4228
+ limit?: number;
4229
+ }
4230
+ /**
4231
+ * Pluggable search backend. The engine ships {@link InMemorySearchProvider}; the control plane
4232
+ * provides an Elasticsearch implementation behind the same interface (ADR 0011).
4233
+ */
4234
+ interface SearchProvider {
4235
+ /** Idempotent one-time setup (create the ES index/mapping). No-op for in-memory. */
4236
+ ensureReady(): Promise<void>;
4237
+ /** Upsert documents (keyed by `type` + `id`). */
4238
+ index(documents: SearchDocument[]): Promise<void>;
4239
+ /** Remove one document by `type` + `id`. Missing ids are ignored. */
4240
+ remove(type: SearchResourceType, id: string): Promise<void>;
4241
+ /** Tenant-scoped ranked search. Returns at most `opts.limit` hits (default 10). */
4242
+ search(query: string, opts: SearchQueryOptions): Promise<SearchHit[]>;
4243
+ }
4244
+ /** Default result cap when a query omits `limit`. */
4245
+ declare const DEFAULT_SEARCH_LIMIT = 10;
4246
+
4247
+ /**
4248
+ * Dependency-free {@link SearchProvider}: token-overlap scoring over an in-memory map, with
4249
+ * tenant + type filtering and snippeting. The OSS default and the dev/test/no-Elasticsearch
4250
+ * backend (ADR 0011). Title matches weigh more than body matches.
4251
+ */
4252
+ declare class InMemorySearchProvider implements SearchProvider {
4253
+ private readonly docs;
4254
+ ensureReady(): Promise<void>;
4255
+ index(documents: SearchDocument[]): Promise<void>;
4256
+ remove(type: SearchResourceType, id: string): Promise<void>;
4257
+ search(query: string, opts: SearchQueryOptions): Promise<SearchHit[]>;
4258
+ }
4259
+
4260
+ export { AGENT_APPROVAL_INTERRUPT, type AIMessage, APPROVAL_IDS_CHANNEL, APPROVED_TOOLS_CHANNEL, type AgentApprovalBinding, type AgentCarrier, type AgentId, type AgentNodeConfig, type AgentProfile, type AgentPromptSource, type AgentResult, AiluSdkError, type AnonymizedAnswer, type AnswerBuilderParams, AnthropicProviderAdapter, ApprovalAlreadyResolvedError, type ApprovalEngine, type ApprovalId, ApprovalNotFoundError, ApprovalNotGrantedError, type ApprovalRequest, ApprovalSelfApprovalError, type ApproveAndResumeOptions, type ApprovedToolWire, ApproverRequiredError, type Artifact, type ArtifactId, type ArtifactStore, type ArtifactVersion, type AttestationRecord, type BaseStore, type Bm25RetrieverParams, type CatalogRunOutcome, type ChannelInput, type ChannelReducer, type ChannelUpdate, type ChannelValues, type ChatMessageBuilderParams, type ChatMessageSpec, type Checkpoint, type CheckpointId, type Checkpointer, type Command, CompiledGraph, type CompiledGraphParts, type ComponentCarrier, type ComponentCatalogEntry, type ComponentCategory, type ComponentDescriptor, type ComponentKind, type ComponentParamMeta, type ComponentSchema, type ConditionFn, type ConditionalRouterBranch, type ConditionalRouterParams, type CouncilOptions, type CouncilSeat, type CreateEmbeddingsOptions, type CreateGraphOptions, type CreateVectorStoreOptions, type CsvParserParams, DEFAULT_AGENT_OUTPUT_CHANNEL, DEFAULT_PREFERENCE, DEFAULT_PRICE_BOOK, DEFAULT_REFERENCE_CORPUS, DEFAULT_SEARCH_LIMIT, DEFAULT_TIER_TABLE, type DeduplicatorParams, DefaultLLMGateway, type DocQaReferenceOptions, type DocumentJoinerParams, type DocumentSplitterParams, type DocumentWriterParams, DuplicateNodeError, DynamicInterrupt, Ed25519Attestor, type EdgeDefinition, type EdgeId, type EfficiencyMiddlewareSpec, type Embeddings, type EmbeddingsProvider, type EmbeddingsRequestBody, EmbeddingsResponseError, type EmbeddingsTransport, type EmptyChannels, type EvaluatorParams, type ExampleGraph, type FieldMapperParams, type FsPermVerb, type FsPolicyRule, GATE_SUBJECT_PREFIX, GOVERNANCE_MIDDLEWARE_KINDS, GovernanceMiddlewareRejectedError, GraphBuilder, GraphCompileError, type GraphDefinition, type GraphId, GraphRuntime, type GraphState, GraphStateSchema, type GraphStatus, GraphValidationError, type HtmlToTextParams, type HttpFetchImpl, type HttpFetchParams, type HttpFetchRequestInit, type HttpFetchResponseLike, type HttpFetchResult, INJECTED_KEY, InMemoryApprovalEngine, InMemoryArtifactStore, InMemoryCheckpointer, InMemoryConditionRegistry, InMemoryEventBus, InMemoryNodeRegistry, InMemoryPromptRegistry, InMemorySearchProvider, InMemoryToolRegistry, type InitialData, type InspectorHandle, type InspectorOptions, type IntegrationComponentHandler, type InterruptConfig, type JsonSchema, type JsonValidatorParams, type KeywordRetrieverParams, type LLMGateway, type LLMModel, type LLMProvider, type LLMProviderAdapter, type LLMRequest, type LLMResponse, type LLMStreamChunk, type LLMToolCall, type LanguageDetectorParams, type LexicalDoc, type ListJoinerParams, MODEL_TIERS, type MapAgentCarrier, type MapAgentNodeConfig, type MemberAnswer, type MemoryItem, type MemoryKey, type MemoryNamespace, type MergeRankerParams, type Message, type MessageId, type MetadataFilterParams, MissingEmbeddingsKeyError, MissingHandlerError, MockLLMProviderAdapter, type ModelChoice, ModelPolicy, type ModelPrice, type ModelTier, type ModelTierInfo, type NodeDefinition, type NodeHandler, type NodeId, type NodeInput, type NodeType, type OpenAIChatRequestBody, type OpenAIChatResponse, type OpenAICompatibleAdapterOptions, OpenAICompatibleProviderAdapter, type OpenAICompatibleTransportPort, type OtelExporterOptions, type OtlpFetch, type OutputParserParams, type PrebuiltAgentCatalogEntry, type PrebuiltOptions, type PredicateOp, type PriceBook, type PromptBuilderParams, type PromptRegistry, type RagAnswererOptions, ReActAgent, type RegexExtractorParams, type ReplayDecision, type RequestApprovalParams, type RerankerParams, type ResolveOverride, type Result, ResumeStateNotFoundError, type RetrieverDoc, type RetrieverParams, type RouterParams, type RouterRule, type RunCatalogGraphOptions, type RunEvent, type RunExplanation, type RunId, type RunOptions, type RustAgentConfig, type RustComponentConfig, RustEngineRequiredError, RustEngineUnavailableError, type RustMapAgentConfig, type RustToolBinding, type RustToolSpec, SIGNALS_KEY, SLEEP_UNTIL_KEY, SUSPEND_META_KEY, type SearchDocument, type SearchHit, type SearchProvider, type SearchQueryOptions, type SearchResourceType, type SemanticRetrieverDoc, type SemanticRetrieverParams, type SentenceWindowSplitterParams, type SkillConfig, type SkillRecord, type StreamAgentConfig, type StreamEvent, type StreamMode, type SuspendMeta, TODOS_CHANNEL, type TaskNodeConfig, type TextCleanerParams, type TierModelTable, type TodoItem, type TodoStatus, type TokenUsage, type ToolCall, type ToolDefinition, type ToolId, type ToolNodeConfig, type ToolRegistry, type TruncatorParams, type TypedCondition, type TypedGraphState, type TypedNodeHandler, UnknownNodeError, type VectorStore, type VectorStoreItem, type VectorStoreMatch, type VerifyReplayResult, WAIT_FOR_SIGNAL_KEY, WRITE_TODOS_TOOL_NAME, type WebSearchImpl, type WebSearchOutcome, type WebSearchParams, type WebSearchResult, type WebSearchTransport, type WriteTodosInput, aggregateRanks, anonymizeAndShuffle, buildDocQaReference, buildOtlpPayload, canonicalJson, compileFile, compileGraphFile, componentCatalog, componentSchema, componentSchemas, components, computeCost, cosineSimilarity, council, createAgentNodeHandler, createEmbeddings, createGraph, createToolNodeHandler, createVectorStore, docQaReferenceDefinition, exampleGraphs, explainRun, exportTracesToOtlp, finalAnswer, generateLlmsTxt, isCatalogGraph, normalizeTodos, paramTypeToJsonSchema, parseRanking, prebuilt, prebuiltCatalog, readAgentCarrier, readComponentCarrier, readInjected, readMapAgentCarrier, readSignal, readSuspendMeta, replayCatalogGraph, resumeCatalogGraph, runCatalogGraph, rustEngineAvailable, rustValidatorActive, semanticRetriever, serveInspector, sleepUntil, streamAgentTokens, tierCatalog, toAgentApprovalBinding, toRustAgentConfig, validateGraph, verifyAttestation, verifyChain, verifyReplayDecisions, waitForSignal, writeTodosJsonSchema, writeTodosTool };