la-machina-engine 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +1239 -0
- package/dist/index.cjs +9841 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +3587 -0
- package/dist/index.d.ts +3587 -0
- package/dist/index.js +9758 -0
- package/dist/index.js.map +1 -0
- package/package.json +96 -0
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,3587 @@
|
|
|
1
|
+
import * as zod from 'zod';
|
|
2
|
+
import { z, ZodTypeAny } from 'zod';
|
|
3
|
+
import { ContentBlockParam, MessageParam } from '@anthropic-ai/sdk/resources/messages';
|
|
4
|
+
import { Transport } from '@modelcontextprotocol/sdk/shared/transport.js';
|
|
5
|
+
import { JSONRPCMessage } from '@modelcontextprotocol/sdk/types.js';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* StorageAdapter — abstract filesystem interface for la-machina-engine.
|
|
9
|
+
*
|
|
10
|
+
* All paths are relative to the adapter's root. The adapter enforces
|
|
11
|
+
* path safety (no traversal, no absolute paths, no null bytes) and
|
|
12
|
+
* provides a filesystem-shaped API that subsystems can use without
|
|
13
|
+
* knowing whether the backend is local disk, R2, or any other
|
|
14
|
+
* S3-compatible object store.
|
|
15
|
+
*
|
|
16
|
+
* Implementations ship in this package:
|
|
17
|
+
* - `LocalStorageAdapter` (src/storage/localAdapter.ts) — fs-backed
|
|
18
|
+
* - `R2StorageAdapter` (src/storage/r2Adapter.ts) — S3-compatible
|
|
19
|
+
*
|
|
20
|
+
* Users may supply custom adapters implementing this interface.
|
|
21
|
+
*/
|
|
22
|
+
interface FileStat {
|
|
23
|
+
readonly size: number;
|
|
24
|
+
readonly mtime: Date;
|
|
25
|
+
}
|
|
26
|
+
interface StorageAdapter {
|
|
27
|
+
/** Read a file as UTF-8 text. Returns null if file doesn't exist. */
|
|
28
|
+
readFile(relativePath: string): Promise<string | null>;
|
|
29
|
+
/** Write a file atomically. Creates parent directories if needed. */
|
|
30
|
+
writeFile(relativePath: string, content: string): Promise<void>;
|
|
31
|
+
/** Append to a file. Creates it if it doesn't exist. */
|
|
32
|
+
appendFile(relativePath: string, content: string): Promise<void>;
|
|
33
|
+
/** Delete a file. No-op if it doesn't exist. */
|
|
34
|
+
deleteFile(relativePath: string): Promise<void>;
|
|
35
|
+
/** Check if a file or directory exists. */
|
|
36
|
+
exists(relativePath: string): Promise<boolean>;
|
|
37
|
+
/**
|
|
38
|
+
* List entries in a directory. Returns just the names (not full paths).
|
|
39
|
+
* Returns an empty array if the directory doesn't exist.
|
|
40
|
+
*/
|
|
41
|
+
listDir(relativePath: string): Promise<string[]>;
|
|
42
|
+
/** Create a directory (recursive). No-op on S3-like backends. */
|
|
43
|
+
mkdir(relativePath: string): Promise<void>;
|
|
44
|
+
/** Check if a path is a directory. */
|
|
45
|
+
isDirectory(relativePath: string): Promise<boolean>;
|
|
46
|
+
/** Get file metadata. Returns null if file doesn't exist. */
|
|
47
|
+
stat(relativePath: string): Promise<FileStat | null>;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Two-root storage model:
|
|
51
|
+
*
|
|
52
|
+
* - `global`: rooted at `{rootPath}/.claude/` — identity, universal rules
|
|
53
|
+
* - `workspace`: rooted at `{rootPath}/workspaces/{workspaceId}/.claude/` —
|
|
54
|
+
* project-specific memory, skills, sessions, episodes
|
|
55
|
+
*
|
|
56
|
+
* Every consumer gets a pair of adapters via `createEngineStorage()`.
|
|
57
|
+
*/
|
|
58
|
+
interface EngineStorage {
|
|
59
|
+
readonly global: StorageAdapter;
|
|
60
|
+
readonly workspace: StorageAdapter;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Error thrown by all storage operations. `cause` is preserved as-is
|
|
64
|
+
* (usually an `ErrnoException` or AWS SDK error) so callers can inspect
|
|
65
|
+
* the underlying failure.
|
|
66
|
+
*/
|
|
67
|
+
declare class StorageError extends Error {
|
|
68
|
+
readonly cause?: unknown;
|
|
69
|
+
constructor(message: string, cause?: unknown);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* R2BindingStorageAdapter — Cloudflare Workers R2 binding (NOT S3 SDK).
|
|
74
|
+
*
|
|
75
|
+
* Unlike `R2StorageAdapter` which talks to R2 over the S3 REST protocol,
|
|
76
|
+
* this adapter uses the native R2 binding exposed to Workers runtime:
|
|
77
|
+
*
|
|
78
|
+
* [[r2_buckets]]
|
|
79
|
+
* binding = "BUCKET"
|
|
80
|
+
* bucket_name = "my-bucket"
|
|
81
|
+
*
|
|
82
|
+
* // In code:
|
|
83
|
+
* new R2BindingStorageAdapter(env.BUCKET, 'tenant/acme')
|
|
84
|
+
*
|
|
85
|
+
* Why use this over `R2StorageAdapter` on Workers?
|
|
86
|
+
* - `@aws-sdk/client-s3` is heavy (~500 KB) and the Workers-compatibility
|
|
87
|
+
* story for ListObjectsV2 XML parsing is brittle; CommonPrefixes come
|
|
88
|
+
* back empty in some runtimes, silently breaking `listDir`.
|
|
89
|
+
* - The R2 binding API (`bucket.list`, `bucket.get`, `bucket.put`,
|
|
90
|
+
* `bucket.delete`) is native, typed, and uses native JSON responses.
|
|
91
|
+
* - No AWS credentials, no endpoint URL — the binding handles auth.
|
|
92
|
+
*
|
|
93
|
+
* Use `R2StorageAdapter` on Node or anywhere you don't have a Workers
|
|
94
|
+
* binding (local dev with Miniflare's S3 endpoint, backfills, etc).
|
|
95
|
+
* Use this adapter inside Workers for reliability and bundle size.
|
|
96
|
+
*
|
|
97
|
+
* All pure JS — no `node:` imports, Workers-compatible.
|
|
98
|
+
*/
|
|
99
|
+
|
|
100
|
+
interface R2Object {
|
|
101
|
+
key: string;
|
|
102
|
+
size: number;
|
|
103
|
+
uploaded: Date;
|
|
104
|
+
httpMetadata?: R2HTTPMetadata;
|
|
105
|
+
customMetadata?: Record<string, string>;
|
|
106
|
+
}
|
|
107
|
+
interface R2ObjectBody extends R2Object {
|
|
108
|
+
text(): Promise<string>;
|
|
109
|
+
arrayBuffer(): Promise<ArrayBuffer>;
|
|
110
|
+
blob?(): Promise<Blob>;
|
|
111
|
+
}
|
|
112
|
+
interface R2Objects {
|
|
113
|
+
objects: R2Object[];
|
|
114
|
+
truncated: boolean;
|
|
115
|
+
cursor?: string;
|
|
116
|
+
delimitedPrefixes?: string[];
|
|
117
|
+
}
|
|
118
|
+
interface R2HTTPMetadata {
|
|
119
|
+
contentType?: string;
|
|
120
|
+
contentLanguage?: string;
|
|
121
|
+
contentDisposition?: string;
|
|
122
|
+
contentEncoding?: string;
|
|
123
|
+
cacheControl?: string;
|
|
124
|
+
cacheExpiry?: Date;
|
|
125
|
+
}
|
|
126
|
+
interface R2ListOptions {
|
|
127
|
+
prefix?: string;
|
|
128
|
+
delimiter?: string;
|
|
129
|
+
cursor?: string;
|
|
130
|
+
limit?: number;
|
|
131
|
+
include?: Array<'httpMetadata' | 'customMetadata'>;
|
|
132
|
+
}
|
|
133
|
+
interface R2PutOptions {
|
|
134
|
+
httpMetadata?: R2HTTPMetadata;
|
|
135
|
+
customMetadata?: Record<string, string>;
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Structural type for `env.BUCKET` — intentionally not imported from
|
|
139
|
+
* `@cloudflare/workers-types` so this adapter has no extra peer deps.
|
|
140
|
+
*/
|
|
141
|
+
interface R2BucketBinding {
|
|
142
|
+
head(key: string): Promise<R2Object | null>;
|
|
143
|
+
get(key: string): Promise<R2ObjectBody | null>;
|
|
144
|
+
put(key: string, value: string | ArrayBuffer | ArrayBufferView | ReadableStream | Blob | null, options?: R2PutOptions): Promise<R2Object>;
|
|
145
|
+
delete(keys: string | string[]): Promise<void>;
|
|
146
|
+
list(options?: R2ListOptions): Promise<R2Objects>;
|
|
147
|
+
}
|
|
148
|
+
declare class R2BindingStorageAdapter implements StorageAdapter {
|
|
149
|
+
private readonly bucket;
|
|
150
|
+
private readonly rootPrefix;
|
|
151
|
+
constructor(bucket: R2BucketBinding, rootPrefix: string);
|
|
152
|
+
private key;
|
|
153
|
+
readFile(rel: string): Promise<string | null>;
|
|
154
|
+
writeFile(rel: string, content: string): Promise<void>;
|
|
155
|
+
appendFile(rel: string, content: string): Promise<void>;
|
|
156
|
+
deleteFile(rel: string): Promise<void>;
|
|
157
|
+
exists(rel: string): Promise<boolean>;
|
|
158
|
+
/**
|
|
159
|
+
* List direct children of `rel` (files + subdirectory names). Returns
|
|
160
|
+
* just the names (not full keys). Empty array if path doesn't exist.
|
|
161
|
+
*
|
|
162
|
+
* Uses the native R2 list API with `delimiter: '/'` which returns
|
|
163
|
+
* `delimitedPrefixes` for "subdirectories" and `objects` for files —
|
|
164
|
+
* equivalent to S3's CommonPrefixes + Contents but parsed by the
|
|
165
|
+
* binding itself (no XML, no SDK).
|
|
166
|
+
*/
|
|
167
|
+
listDir(rel: string): Promise<string[]>;
|
|
168
|
+
mkdir(rel: string): Promise<void>;
|
|
169
|
+
isDirectory(rel: string): Promise<boolean>;
|
|
170
|
+
stat(rel: string): Promise<FileStat | null>;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Compaction config + result types.
|
|
175
|
+
*/
|
|
176
|
+
|
|
177
|
+
type CompactionStrategy = 'drop-middle' | 'summarize' | 'session-memory' | 'auto';
|
|
178
|
+
interface ResolvedCompactionConfig {
|
|
179
|
+
/** Which strategy to use. 'auto' picks the best one at runtime. */
|
|
180
|
+
readonly strategy: CompactionStrategy;
|
|
181
|
+
/** Fraction (0-1) of context window that triggers compaction. */
|
|
182
|
+
readonly threshold: number;
|
|
183
|
+
/** How many recent messages to keep after compaction. */
|
|
184
|
+
readonly keepLast: number;
|
|
185
|
+
/** Max tokens for the LLM-generated summary. */
|
|
186
|
+
readonly summaryMaxTokens: number;
|
|
187
|
+
/** Enable time-based microcompaction of old tool results. */
|
|
188
|
+
readonly microcompact: boolean;
|
|
189
|
+
/** Age in ms after which tool results are eligible for microcompaction. */
|
|
190
|
+
readonly microcompactAgeMs: number;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Coordinator config types.
|
|
195
|
+
*/
|
|
196
|
+
interface ResolvedCoordinatorConfig {
|
|
197
|
+
/** Whether coordinator mode is active. Default: false. */
|
|
198
|
+
readonly enabled: boolean;
|
|
199
|
+
/**
|
|
200
|
+
* Tools available to worker agents. Workers deliberately lack Agent
|
|
201
|
+
* (no sub-delegation) and typically lack MCP (coordinator controls
|
|
202
|
+
* external access). Default: core file/search tools only.
|
|
203
|
+
*/
|
|
204
|
+
readonly workerTools: readonly string[];
|
|
205
|
+
/** Max concurrent workers the coordinator can spawn. Default: 5. */
|
|
206
|
+
readonly maxConcurrentWorkers: number;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* streaming — normalize raw Anthropic stream events into a small,
|
|
211
|
+
* well-shaped union that the engine's agent loop can consume.
|
|
212
|
+
*
|
|
213
|
+
* The Anthropic SDK emits a discriminated union of six raw events per
|
|
214
|
+
* stream:
|
|
215
|
+
* message_start — metadata and initial usage
|
|
216
|
+
* content_block_start — begin a text or tool_use block
|
|
217
|
+
* content_block_delta — partial text or input_json chunk
|
|
218
|
+
* content_block_stop — end of a block
|
|
219
|
+
* message_delta — stop_reason + final output_tokens
|
|
220
|
+
* message_stop — terminal event
|
|
221
|
+
*
|
|
222
|
+
* This module collects deltas into complete blocks and emits a cleaner
|
|
223
|
+
* union: `text` / `tool_use` / `message_stop`. Tool_use input JSON is
|
|
224
|
+
* assembled and parsed once at content_block_stop so downstream code
|
|
225
|
+
* sees a typed object rather than raw partial JSON.
|
|
226
|
+
*
|
|
227
|
+
* Errors:
|
|
228
|
+
* - Malformed tool_use JSON → `StreamParseError`
|
|
229
|
+
* - Stream ends without `message_stop` → `StreamIncompleteError`
|
|
230
|
+
*/
|
|
231
|
+
|
|
232
|
+
interface TokenUsage$1 {
|
|
233
|
+
readonly input: number;
|
|
234
|
+
readonly output: number;
|
|
235
|
+
readonly cacheCreationInput?: number | undefined;
|
|
236
|
+
readonly cacheReadInput?: number | undefined;
|
|
237
|
+
}
|
|
238
|
+
/**
|
|
239
|
+
* Common Anthropic stop reasons. The `(string & {})` pattern preserves
|
|
240
|
+
* autocomplete for the documented values while still allowing arbitrary
|
|
241
|
+
* strings from third-party proxies that may use other vocabularies.
|
|
242
|
+
*/
|
|
243
|
+
type StopReason = 'end_turn' | 'tool_use' | 'max_tokens' | 'stop_sequence' | 'refusal' | 'pause_turn' | (string & Record<never, never>);
|
|
244
|
+
type NormalizedEvent = {
|
|
245
|
+
readonly type: 'text';
|
|
246
|
+
readonly index: number;
|
|
247
|
+
readonly text: string;
|
|
248
|
+
} | {
|
|
249
|
+
readonly type: 'tool_use';
|
|
250
|
+
readonly index: number;
|
|
251
|
+
readonly id: string;
|
|
252
|
+
readonly name: string;
|
|
253
|
+
readonly input: unknown;
|
|
254
|
+
} | {
|
|
255
|
+
readonly type: 'thinking';
|
|
256
|
+
readonly index: number;
|
|
257
|
+
readonly thinking: string;
|
|
258
|
+
} | {
|
|
259
|
+
readonly type: 'message_stop';
|
|
260
|
+
readonly stopReason: StopReason;
|
|
261
|
+
readonly usage: TokenUsage$1;
|
|
262
|
+
};
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* Orchestrator types — plan schema, step results, retry policy,
|
|
266
|
+
* orchestrator config, and the public OrchestratorResult.
|
|
267
|
+
*/
|
|
268
|
+
|
|
269
|
+
declare const PlanStepSchema: z.ZodObject<{
|
|
270
|
+
id: z.ZodString;
|
|
271
|
+
description: z.ZodString;
|
|
272
|
+
action: z.ZodEnum<["research", "implement", "verify", "review", "custom"]>;
|
|
273
|
+
files: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
274
|
+
spec: z.ZodOptional<z.ZodString>;
|
|
275
|
+
dependsOn: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
276
|
+
}, "strip", z.ZodTypeAny, {
|
|
277
|
+
id: string;
|
|
278
|
+
description: string;
|
|
279
|
+
action: "custom" | "research" | "implement" | "verify" | "review";
|
|
280
|
+
files?: string[] | undefined;
|
|
281
|
+
spec?: string | undefined;
|
|
282
|
+
dependsOn?: string[] | undefined;
|
|
283
|
+
}, {
|
|
284
|
+
id: string;
|
|
285
|
+
description: string;
|
|
286
|
+
action: "custom" | "research" | "implement" | "verify" | "review";
|
|
287
|
+
files?: string[] | undefined;
|
|
288
|
+
spec?: string | undefined;
|
|
289
|
+
dependsOn?: string[] | undefined;
|
|
290
|
+
}>;
|
|
291
|
+
declare const PlanSchema: z.ZodObject<{
|
|
292
|
+
summary: z.ZodString;
|
|
293
|
+
steps: z.ZodArray<z.ZodObject<{
|
|
294
|
+
id: z.ZodString;
|
|
295
|
+
description: z.ZodString;
|
|
296
|
+
action: z.ZodEnum<["research", "implement", "verify", "review", "custom"]>;
|
|
297
|
+
files: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
298
|
+
spec: z.ZodOptional<z.ZodString>;
|
|
299
|
+
dependsOn: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
300
|
+
}, "strip", z.ZodTypeAny, {
|
|
301
|
+
id: string;
|
|
302
|
+
description: string;
|
|
303
|
+
action: "custom" | "research" | "implement" | "verify" | "review";
|
|
304
|
+
files?: string[] | undefined;
|
|
305
|
+
spec?: string | undefined;
|
|
306
|
+
dependsOn?: string[] | undefined;
|
|
307
|
+
}, {
|
|
308
|
+
id: string;
|
|
309
|
+
description: string;
|
|
310
|
+
action: "custom" | "research" | "implement" | "verify" | "review";
|
|
311
|
+
files?: string[] | undefined;
|
|
312
|
+
spec?: string | undefined;
|
|
313
|
+
dependsOn?: string[] | undefined;
|
|
314
|
+
}>, "many">;
|
|
315
|
+
}, "strip", z.ZodTypeAny, {
|
|
316
|
+
summary: string;
|
|
317
|
+
steps: {
|
|
318
|
+
id: string;
|
|
319
|
+
description: string;
|
|
320
|
+
action: "custom" | "research" | "implement" | "verify" | "review";
|
|
321
|
+
files?: string[] | undefined;
|
|
322
|
+
spec?: string | undefined;
|
|
323
|
+
dependsOn?: string[] | undefined;
|
|
324
|
+
}[];
|
|
325
|
+
}, {
|
|
326
|
+
summary: string;
|
|
327
|
+
steps: {
|
|
328
|
+
id: string;
|
|
329
|
+
description: string;
|
|
330
|
+
action: "custom" | "research" | "implement" | "verify" | "review";
|
|
331
|
+
files?: string[] | undefined;
|
|
332
|
+
spec?: string | undefined;
|
|
333
|
+
dependsOn?: string[] | undefined;
|
|
334
|
+
}[];
|
|
335
|
+
}>;
|
|
336
|
+
type PlanStep = z.infer<typeof PlanStepSchema>;
|
|
337
|
+
type Plan = z.infer<typeof PlanSchema>;
|
|
338
|
+
type StepStatus = 'done' | 'failed' | 'skipped' | 'reverted';
|
|
339
|
+
interface StepResult {
|
|
340
|
+
readonly stepId: string;
|
|
341
|
+
readonly action: string;
|
|
342
|
+
readonly status: StepStatus;
|
|
343
|
+
readonly output?: string | undefined;
|
|
344
|
+
readonly error?: string | undefined;
|
|
345
|
+
readonly attempts: number;
|
|
346
|
+
readonly agentId?: string | undefined;
|
|
347
|
+
}
|
|
348
|
+
interface FileSnapshot {
|
|
349
|
+
readonly path: string;
|
|
350
|
+
/** null = file didn't exist before (delete on revert). */
|
|
351
|
+
readonly content: string | null;
|
|
352
|
+
}
|
|
353
|
+
interface RetryPolicy {
|
|
354
|
+
readonly maxAttempts: number;
|
|
355
|
+
readonly backoffMs: number;
|
|
356
|
+
readonly onExhausted: 'revert' | 'skip' | 'fail';
|
|
357
|
+
}
|
|
358
|
+
interface ResolvedRetryPolicies {
|
|
359
|
+
readonly plan: RetryPolicy;
|
|
360
|
+
readonly research: RetryPolicy;
|
|
361
|
+
readonly implement: RetryPolicy;
|
|
362
|
+
readonly verify: RetryPolicy;
|
|
363
|
+
readonly review: RetryPolicy;
|
|
364
|
+
}
|
|
365
|
+
interface ResolvedOrchestratorConfig {
|
|
366
|
+
readonly enabled: boolean;
|
|
367
|
+
readonly retries: ResolvedRetryPolicies;
|
|
368
|
+
readonly maxParallelResearchers: number;
|
|
369
|
+
readonly enableReview: boolean;
|
|
370
|
+
readonly enableRollback: boolean;
|
|
371
|
+
readonly maxPlanSteps: number;
|
|
372
|
+
/** Max turns per internal agent run (planner, researcher, implementer, verifier). Default: 15. */
|
|
373
|
+
readonly agentMaxTurns: number;
|
|
374
|
+
}
|
|
375
|
+
interface OrchestratorResult {
|
|
376
|
+
readonly status: 'done' | 'partial' | 'failed';
|
|
377
|
+
readonly output: string;
|
|
378
|
+
readonly plan: Plan | null;
|
|
379
|
+
readonly steps: readonly StepResult[];
|
|
380
|
+
readonly tokensUsed: TokenUsage$1;
|
|
381
|
+
readonly agentRuns: number;
|
|
382
|
+
readonly reverted: readonly string[];
|
|
383
|
+
readonly durationMs: number;
|
|
384
|
+
}
|
|
385
|
+
interface OrchestrateOptions {
|
|
386
|
+
readonly runId: string;
|
|
387
|
+
readonly nodeId: string;
|
|
388
|
+
readonly task: string;
|
|
389
|
+
/** Optional pre-built plan — skip the Planner agent. */
|
|
390
|
+
readonly plan?: Plan | undefined;
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
/**
|
|
394
|
+
* Tool contract — the shape every tool implements, plus a registry to
|
|
395
|
+
* collect them by name.
|
|
396
|
+
*
|
|
397
|
+
* The contract is intentionally minimal:
|
|
398
|
+
*
|
|
399
|
+
* 1. A tool has a `name`, a `description`, and a Zod `inputSchema`.
|
|
400
|
+
* Zod gives both runtime validation and a TypeScript-inferred
|
|
401
|
+
* input type for `execute()`.
|
|
402
|
+
* 2. `execute(input, context)` returns a `ToolResult` (or rejects).
|
|
403
|
+
* The runtime catches rejections and turns them into error
|
|
404
|
+
* results — a misbehaving tool can never crash the agent loop.
|
|
405
|
+
* 3. The runtime supplies a `ToolContext` carrying at least an
|
|
406
|
+
* `AbortSignal` for cancellation.
|
|
407
|
+
*
|
|
408
|
+
* Phase 5 ships the types and the registry. Phase 6 fills the registry
|
|
409
|
+
* with the core tool set (Bash, Read, Write, Edit, Glob, Grep, WebFetch).
|
|
410
|
+
* Custom tools come from `config.tools.custom` at engine init time.
|
|
411
|
+
*/
|
|
412
|
+
|
|
413
|
+
/**
|
|
414
|
+
* The execution context passed to every tool. Phase 5 keeps this
|
|
415
|
+
* minimal — only an AbortSignal — so the contract stays portable.
|
|
416
|
+
* Later phases extend with storage, run/node ids, hook handles, etc.
|
|
417
|
+
*/
|
|
418
|
+
interface ToolContext {
|
|
419
|
+
readonly signal: AbortSignal;
|
|
420
|
+
}
|
|
421
|
+
/**
|
|
422
|
+
* The result of a single tool invocation. The runtime wraps thrown
|
|
423
|
+
* errors in this shape so the agent loop only ever sees success/error
|
|
424
|
+
* as data, not exceptions.
|
|
425
|
+
*/
|
|
426
|
+
interface ToolResult {
|
|
427
|
+
/** Human- and model-readable output. The agent's next turn sees this. */
|
|
428
|
+
readonly content: string;
|
|
429
|
+
/** True when the tool failed. */
|
|
430
|
+
readonly isError?: boolean;
|
|
431
|
+
/** Optional structured metadata for hooks / observability. */
|
|
432
|
+
readonly metadata?: Readonly<Record<string, unknown>>;
|
|
433
|
+
}
|
|
434
|
+
/**
|
|
435
|
+
* A tool definition. Generic on a Zod schema so that `execute()`'s
|
|
436
|
+
* input parameter is statically typed via `z.infer<TSchema>`.
|
|
437
|
+
*
|
|
438
|
+
* Tool authors normally use `defineTool()` to get the inference
|
|
439
|
+
* without writing the generic by hand.
|
|
440
|
+
*/
|
|
441
|
+
interface Tool$1<TSchema extends z.ZodTypeAny = z.ZodTypeAny> {
|
|
442
|
+
readonly name: string;
|
|
443
|
+
readonly description: string;
|
|
444
|
+
readonly inputSchema: TSchema;
|
|
445
|
+
/**
|
|
446
|
+
* Optional raw JSON Schema to send to the Anthropic API as
|
|
447
|
+
* `input_schema`, bypassing Zod-to-JSON-Schema conversion. Used by
|
|
448
|
+
* MCP-sourced tools whose schema is authored in JSON Schema directly
|
|
449
|
+
* and whose Zod schema is a `z.unknown()` passthrough.
|
|
450
|
+
*
|
|
451
|
+
* When set, `toAnthropicTool()` in the agent loop prefers this over
|
|
452
|
+
* running `zodToJsonSchema(inputSchema)`. Normal in-process tools
|
|
453
|
+
* defined via `defineTool()` should leave this undefined.
|
|
454
|
+
*/
|
|
455
|
+
readonly anthropicSchemaOverride?: Record<string, unknown>;
|
|
456
|
+
/**
|
|
457
|
+
* Returns true if this tool is safe to run concurrently with other
|
|
458
|
+
* concurrency-safe tools. Read-only tools (Read, Glob, Grep, etc.)
|
|
459
|
+
* should return true; mutating tools (Write, Edit, Bash) should
|
|
460
|
+
* return false. Default: false (fail-closed).
|
|
461
|
+
*
|
|
462
|
+
* The agent loop uses this to batch consecutive safe tool calls
|
|
463
|
+
* into a single `Promise.all()` for parallel execution, while
|
|
464
|
+
* running unsafe tools serially.
|
|
465
|
+
*
|
|
466
|
+
* Input is typed as `unknown` (not `z.infer<TSchema>`) so that
|
|
467
|
+
* `Tool<ZodObject<...>>` remains assignable to `Tool<ZodTypeAny>`
|
|
468
|
+
* under `exactOptionalPropertyTypes`.
|
|
469
|
+
*/
|
|
470
|
+
isConcurrencySafe?: ((input: unknown) => boolean) | undefined;
|
|
471
|
+
execute(input: z.infer<TSchema>, context: ToolContext): Promise<ToolResult>;
|
|
472
|
+
}
|
|
473
|
+
/**
|
|
474
|
+
* Identity helper that locks in the schema generic so callers don't have
|
|
475
|
+
* to write it. Use this whenever defining a tool — it costs nothing at
|
|
476
|
+
* runtime and gives you full inference inside `execute()`.
|
|
477
|
+
*
|
|
478
|
+
* @example
|
|
479
|
+
* ```ts
|
|
480
|
+
* const Echo = defineTool({
|
|
481
|
+
* name: 'Echo',
|
|
482
|
+
* description: 'Echoes its input back',
|
|
483
|
+
* inputSchema: z.object({ msg: z.string() }),
|
|
484
|
+
* execute: async ({ msg }) => ({ content: msg }),
|
|
485
|
+
* })
|
|
486
|
+
* ```
|
|
487
|
+
*/
|
|
488
|
+
declare function defineTool<TSchema extends z.ZodTypeAny>(tool: Tool$1<TSchema>): Tool$1<TSchema>;
|
|
489
|
+
/**
|
|
490
|
+
* In-memory registry of tools by name. Used by the engine's tool runtime
|
|
491
|
+
* to dispatch tool calls. Re-registering the same name throws so typos
|
|
492
|
+
* surface immediately rather than silently shadowing.
|
|
493
|
+
*/
|
|
494
|
+
declare class ToolRegistry {
|
|
495
|
+
private readonly tools;
|
|
496
|
+
register(tool: Tool$1): void;
|
|
497
|
+
registerAll(tools: ReadonlyArray<Tool$1>): void;
|
|
498
|
+
unregister(name: string): void;
|
|
499
|
+
get(name: string): Tool$1 | undefined;
|
|
500
|
+
has(name: string): boolean;
|
|
501
|
+
list(): Tool$1[];
|
|
502
|
+
count(): number;
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
/**
|
|
506
|
+
* Hook event payloads + handler types.
|
|
507
|
+
*
|
|
508
|
+
* Six lifecycle slots match the `config.hooks` shape from Phase 1:
|
|
509
|
+
*
|
|
510
|
+
* preRun — fires once before the agent loop starts
|
|
511
|
+
* postRun — fires once when the run exits (done | paused | failed)
|
|
512
|
+
* preTurn — fires before each turn's API call
|
|
513
|
+
* postTurn — fires after each turn's tool dispatches complete
|
|
514
|
+
* preToolCall — fires before each tool execution
|
|
515
|
+
* postToolCall — fires after each tool execution (always — even on error)
|
|
516
|
+
*
|
|
517
|
+
* Hooks are functions: `(event) => void | Promise<void>`. They run in
|
|
518
|
+
* registration order, sequentially. Errors thrown by a hook are
|
|
519
|
+
* isolated — they never abort the run.
|
|
520
|
+
*/
|
|
521
|
+
|
|
522
|
+
interface PreRunEvent {
|
|
523
|
+
readonly runId: string;
|
|
524
|
+
readonly nodeId: string;
|
|
525
|
+
readonly task: string;
|
|
526
|
+
}
|
|
527
|
+
interface PostRunEvent {
|
|
528
|
+
readonly runId: string;
|
|
529
|
+
readonly nodeId: string;
|
|
530
|
+
readonly status: 'done' | 'paused' | 'failed';
|
|
531
|
+
readonly tokensUsed: TokenUsage$1;
|
|
532
|
+
readonly turnsUsed: number;
|
|
533
|
+
readonly output?: string;
|
|
534
|
+
readonly error?: Error;
|
|
535
|
+
/** Number of tool calls dispatched during the run. */
|
|
536
|
+
readonly toolCallCount?: number;
|
|
537
|
+
/** Transcript path for this run (e.g. "projects/{runId}/nodes/{nodeId}"). */
|
|
538
|
+
readonly transcriptPath?: string;
|
|
539
|
+
}
|
|
540
|
+
interface PreTurnEvent {
|
|
541
|
+
readonly runId: string;
|
|
542
|
+
readonly nodeId: string;
|
|
543
|
+
readonly turnIndex: number;
|
|
544
|
+
readonly messageCount: number;
|
|
545
|
+
}
|
|
546
|
+
interface PostTurnEvent {
|
|
547
|
+
readonly runId: string;
|
|
548
|
+
readonly nodeId: string;
|
|
549
|
+
readonly turnIndex: number;
|
|
550
|
+
readonly tokensUsed: TokenUsage$1;
|
|
551
|
+
}
|
|
552
|
+
/**
|
|
553
|
+
* Stop hook event — fired after each turn completes (tool dispatch or
|
|
554
|
+
* terminal). Ported from La-Machina's stopHooks.ts. The hook can
|
|
555
|
+
* inspect the full turn state and optionally prevent continuation.
|
|
556
|
+
*/
|
|
557
|
+
interface StopHookEvent {
|
|
558
|
+
readonly runId: string;
|
|
559
|
+
readonly nodeId: string;
|
|
560
|
+
readonly turnIndex: number;
|
|
561
|
+
readonly tokensUsed: TokenUsage$1;
|
|
562
|
+
readonly lastAssistantText: string;
|
|
563
|
+
readonly toolsCalled: readonly string[];
|
|
564
|
+
readonly status: 'tool_use' | 'end_turn';
|
|
565
|
+
}
|
|
566
|
+
/**
|
|
567
|
+
* Stop hook result. Return `{ preventContinuation: true }` to stop
|
|
568
|
+
* the run gracefully after this turn. The run returns status 'done'
|
|
569
|
+
* with the last assistant output. If multiple stop hooks run, any
|
|
570
|
+
* one returning preventContinuation stops the run.
|
|
571
|
+
*/
|
|
572
|
+
interface StopHookResult {
|
|
573
|
+
readonly preventContinuation?: boolean;
|
|
574
|
+
readonly reason?: string;
|
|
575
|
+
}
|
|
576
|
+
interface PreToolCallEvent$1 {
|
|
577
|
+
readonly toolName: string;
|
|
578
|
+
readonly input: unknown;
|
|
579
|
+
}
|
|
580
|
+
interface PostToolCallEvent$1 {
|
|
581
|
+
readonly toolName: string;
|
|
582
|
+
readonly input: unknown;
|
|
583
|
+
readonly result: ToolResult;
|
|
584
|
+
readonly durationMs: number;
|
|
585
|
+
}
|
|
586
|
+
/**
|
|
587
|
+
* Gate decision returned by `gateBeforeTool`. `allow: true` lets the
|
|
588
|
+
* dispatch proceed; `allow: false` pauses the run with `reason` captured
|
|
589
|
+
* in the transcript snapshot.
|
|
590
|
+
*/
|
|
591
|
+
interface GateDecision$1 {
|
|
592
|
+
readonly allow: boolean;
|
|
593
|
+
readonly reason?: string;
|
|
594
|
+
}
|
|
595
|
+
type PreRunHook = (event: PreRunEvent) => void | Promise<void>;
|
|
596
|
+
type PostRunHook = (event: PostRunEvent) => void | Promise<void>;
|
|
597
|
+
type PreTurnHook = (event: PreTurnEvent) => void | Promise<void>;
|
|
598
|
+
type PostTurnHook = (event: PostTurnEvent) => void | Promise<void>;
|
|
599
|
+
type PreToolCallHook = (event: PreToolCallEvent$1) => void | Promise<void>;
|
|
600
|
+
type PostToolCallHook = (event: PostToolCallEvent$1) => void | Promise<void>;
|
|
601
|
+
/**
|
|
602
|
+
* Stop hook — fires after each turn. Can return `{ preventContinuation: true }`
|
|
603
|
+
* to stop the run gracefully. Ported from La-Machina's executeStopHooks().
|
|
604
|
+
*/
|
|
605
|
+
type StopHook = (event: StopHookEvent) => StopHookResult | void | Promise<StopHookResult | void>;
|
|
606
|
+
/**
|
|
607
|
+
* Gate hook signature — synchronous or async. Receives the tool name and
|
|
608
|
+
* the validated input, returns a `GateDecision`. Distinct from the other
|
|
609
|
+
* hook slots because its return value drives pause/resume behavior.
|
|
610
|
+
*/
|
|
611
|
+
type GateBeforeToolHook = (toolName: string, input: unknown) => GateDecision$1 | Promise<GateDecision$1>;
|
|
612
|
+
|
|
613
|
+
/**
|
|
614
|
+
* MCP config types.
|
|
615
|
+
*
|
|
616
|
+
* Three transport variants (discriminated on `type`):
|
|
617
|
+
* - `stdio` — subprocess spawning, most common
|
|
618
|
+
* - `http` — Streamable HTTP (modern MCP spec, preferred for cloud)
|
|
619
|
+
* - `sse` — Server-Sent Events (legacy, still in use by many servers)
|
|
620
|
+
*/
|
|
621
|
+
interface McpToolDef {
|
|
622
|
+
readonly name: string;
|
|
623
|
+
readonly description: string;
|
|
624
|
+
readonly inputSchema: Record<string, unknown>;
|
|
625
|
+
}
|
|
626
|
+
interface McpCallResult {
|
|
627
|
+
readonly content: string;
|
|
628
|
+
readonly isError?: boolean;
|
|
629
|
+
}
|
|
630
|
+
type McpServerState = 'uninitialized' | 'connecting' | 'connected' | 'failed' | 'disconnected';
|
|
631
|
+
interface ResolvedMcpStdioServerConfig {
|
|
632
|
+
readonly type: 'stdio';
|
|
633
|
+
readonly command: string;
|
|
634
|
+
readonly args: readonly string[];
|
|
635
|
+
readonly env: Readonly<Record<string, string>> | undefined;
|
|
636
|
+
readonly cwd: string | undefined;
|
|
637
|
+
readonly isolateEnv: boolean;
|
|
638
|
+
/** See `ResolvedMcpHttpServerConfig.allowSampling`. */
|
|
639
|
+
readonly allowSampling?: boolean | undefined;
|
|
640
|
+
}
|
|
641
|
+
interface ResolvedMcpHttpServerConfig {
|
|
642
|
+
readonly type: 'http';
|
|
643
|
+
/** Full URL of the MCP server endpoint, e.g. `https://mcp.example.com/v1`. */
|
|
644
|
+
readonly url: string;
|
|
645
|
+
/** Optional headers sent on every request (auth tokens, API keys). */
|
|
646
|
+
readonly headers: Readonly<Record<string, string>> | undefined;
|
|
647
|
+
/**
|
|
648
|
+
* Use the Workers-friendly plain-POST transport (`BindingHttpTransport`)
|
|
649
|
+
* instead of the default `StreamableHTTPClientTransport`. Enable when
|
|
650
|
+
* running on Cloudflare Workers — the SDK's streaming transport can
|
|
651
|
+
* hang on Workers after `initialize`. Default: false.
|
|
652
|
+
*/
|
|
653
|
+
readonly preferBindingTransport?: boolean | undefined;
|
|
654
|
+
/**
|
|
655
|
+
* Per-request dynamic header provider. When set, called before every
|
|
656
|
+
* MCP send and its result is merged OVER the static `headers`. Use
|
|
657
|
+
* for OAuth tokens that need refresh. On HTTP 401 the engine invokes
|
|
658
|
+
* the provider a second time and retries the request once.
|
|
659
|
+
*
|
|
660
|
+
* Must be idempotent — may be called many times per run. Not called
|
|
661
|
+
* for stdio servers (their env is immutable post-spawn).
|
|
662
|
+
*/
|
|
663
|
+
readonly headersProvider?: (() => Promise<Readonly<Record<string, string>>>) | undefined;
|
|
664
|
+
/**
|
|
665
|
+
* Allow this server to request LLM completions via MCP's
|
|
666
|
+
* `sampling/createMessage`. OFF by default — sampling consumes the
|
|
667
|
+
* engine's LLM budget, so opt-in is explicit per server. See Plan 018 §2.
|
|
668
|
+
*/
|
|
669
|
+
readonly allowSampling?: boolean | undefined;
|
|
670
|
+
}
|
|
671
|
+
interface ResolvedMcpSseServerConfig {
|
|
672
|
+
readonly type: 'sse';
|
|
673
|
+
/** Full URL of the SSE endpoint, e.g. `https://mcp.example.com/sse`. */
|
|
674
|
+
readonly url: string;
|
|
675
|
+
/** Optional headers sent on every request. */
|
|
676
|
+
readonly headers: Readonly<Record<string, string>> | undefined;
|
|
677
|
+
/** See `ResolvedMcpHttpServerConfig.headersProvider`. */
|
|
678
|
+
readonly headersProvider?: (() => Promise<Readonly<Record<string, string>>>) | undefined;
|
|
679
|
+
/** See `ResolvedMcpHttpServerConfig.allowSampling`. */
|
|
680
|
+
readonly allowSampling?: boolean | undefined;
|
|
681
|
+
}
|
|
682
|
+
type ResolvedMcpServerConfig = ResolvedMcpStdioServerConfig | ResolvedMcpHttpServerConfig | ResolvedMcpSseServerConfig;
|
|
683
|
+
interface ResolvedMcpConfig {
|
|
684
|
+
readonly servers: Readonly<Record<string, ResolvedMcpServerConfig>>;
|
|
685
|
+
readonly connectTimeoutMs: number;
|
|
686
|
+
readonly callTimeoutMs: number;
|
|
687
|
+
/** Timeout for shutdownAll() before force-killing servers. Default: 5000. */
|
|
688
|
+
readonly shutdownTimeoutMs: number;
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
/**
|
|
692
|
+
* Permission system types.
|
|
693
|
+
*
|
|
694
|
+
* Three modes:
|
|
695
|
+
* - `'open'` — every tool is allowed (v0.1 behavior, default)
|
|
696
|
+
* - `'rules'` — evaluate rule strings in order, first match wins
|
|
697
|
+
* - `'locked'` — only safe-allowlist tools pass, everything else denied
|
|
698
|
+
*
|
|
699
|
+
* Rule syntax:
|
|
700
|
+
* - `'allow:Read'` — allow the Read tool
|
|
701
|
+
* - `'deny:Bash'` — deny the Bash tool
|
|
702
|
+
* - `'allow:mcp__fs__*'` — glob: allow all tools from the fs MCP server
|
|
703
|
+
* - `'deny:*'` — deny everything not explicitly allowed above
|
|
704
|
+
*
|
|
705
|
+
* Rules are evaluated top-to-bottom. The first matching rule wins. If no
|
|
706
|
+
* rule matches, the decision falls to the mode's default:
|
|
707
|
+
* - `'rules'` mode → deny (fall-closed)
|
|
708
|
+
* - `'open'` mode → allow (rules are informational only)
|
|
709
|
+
* - `'locked'` mode → rules field is ignored entirely
|
|
710
|
+
*/
|
|
711
|
+
type PermissionMode = 'open' | 'rules' | 'locked';
|
|
712
|
+
type PermissionAction = 'allow' | 'deny';
|
|
713
|
+
interface PermissionRule {
|
|
714
|
+
readonly action: PermissionAction;
|
|
715
|
+
readonly pattern: string;
|
|
716
|
+
}
|
|
717
|
+
type PermissionDecision = {
|
|
718
|
+
readonly allowed: true;
|
|
719
|
+
} | {
|
|
720
|
+
readonly allowed: false;
|
|
721
|
+
readonly reason: string;
|
|
722
|
+
};
|
|
723
|
+
interface ResolvedPermissionsConfig {
|
|
724
|
+
readonly mode: PermissionMode;
|
|
725
|
+
readonly rules: readonly string[];
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
/** Placeholder for the Phase 5 tool contract. */
|
|
729
|
+
type Tool = unknown;
|
|
730
|
+
/**
|
|
731
|
+
* Structured log record emitted by the engine to `config.logging.sink`.
|
|
732
|
+
* `ts` is an ISO-8601 timestamp. `meta` is any caller-supplied context.
|
|
733
|
+
*/
|
|
734
|
+
interface LogEntry {
|
|
735
|
+
readonly level: LogLevel;
|
|
736
|
+
readonly msg: string;
|
|
737
|
+
readonly ts: string;
|
|
738
|
+
readonly meta?: Record<string, unknown>;
|
|
739
|
+
}
|
|
740
|
+
type ModelProvider = 'anthropic' | 'openai' | 'google' | 'openai-compatible' | 'bedrock' | 'vertex' | 'proxy';
|
|
741
|
+
type StorageProvider = 'local' | 'r2' | 'r2-binding';
|
|
742
|
+
type MemoryMode = 'off' | 'read-only' | 'read-write';
|
|
743
|
+
type MemoryScope = 'workspace' | 'global';
|
|
744
|
+
type FlushPolicy = 'turn-end' | 'entry' | 'manual';
|
|
745
|
+
type LogLevel = 'silent' | 'error' | 'warn' | 'info' | 'debug';
|
|
746
|
+
type LogSink = 'stderr' | 'none' | ((entry: LogEntry) => void);
|
|
747
|
+
interface ResolvedR2Config {
|
|
748
|
+
readonly bucket: string;
|
|
749
|
+
readonly region: string;
|
|
750
|
+
readonly accessKeyId: string;
|
|
751
|
+
readonly secretAccessKey: string;
|
|
752
|
+
readonly endpoint?: string | undefined;
|
|
753
|
+
}
|
|
754
|
+
interface ResolvedModelConfig {
|
|
755
|
+
readonly provider: ModelProvider;
|
|
756
|
+
readonly modelId: string;
|
|
757
|
+
readonly apiKey: string;
|
|
758
|
+
readonly baseURL?: string | undefined;
|
|
759
|
+
readonly maxTokens: number;
|
|
760
|
+
readonly temperature: number;
|
|
761
|
+
/** Max retries on transient API errors (429, 500). 0 = no retry. Default: 2. */
|
|
762
|
+
readonly maxRetries: number;
|
|
763
|
+
}
|
|
764
|
+
interface ResolvedStorageConfig {
|
|
765
|
+
readonly provider: StorageProvider;
|
|
766
|
+
readonly rootPath: string;
|
|
767
|
+
readonly workspaceId: string;
|
|
768
|
+
readonly r2?: ResolvedR2Config | undefined;
|
|
769
|
+
/**
|
|
770
|
+
* Cloudflare R2 binding object (env.BUCKET) — required when
|
|
771
|
+
* provider === 'r2-binding'. Structurally: { head, get, put, delete, list }.
|
|
772
|
+
* Not serializable — kept as-is and used only inside the factory.
|
|
773
|
+
*/
|
|
774
|
+
readonly r2Binding?: R2BucketBinding | undefined;
|
|
775
|
+
}
|
|
776
|
+
interface ResolvedMemoryConfig {
|
|
777
|
+
readonly mode: MemoryMode;
|
|
778
|
+
readonly scope: MemoryScope;
|
|
779
|
+
}
|
|
780
|
+
interface ResolvedToolsConfig {
|
|
781
|
+
readonly enabled: readonly string[];
|
|
782
|
+
readonly disabled: readonly string[];
|
|
783
|
+
readonly custom: readonly Tool[];
|
|
784
|
+
}
|
|
785
|
+
interface ResolvedAgentsConfig {
|
|
786
|
+
readonly builtins: readonly string[];
|
|
787
|
+
readonly customPath?: string | undefined;
|
|
788
|
+
}
|
|
789
|
+
interface ResolvedSkillsConfig {
|
|
790
|
+
readonly path?: string | undefined;
|
|
791
|
+
readonly autoload: boolean;
|
|
792
|
+
/**
|
|
793
|
+
* SSRF allowlist for per-run `InlineSkillSource` URL fetches.
|
|
794
|
+
* When undefined / empty, any URL is allowed (development default).
|
|
795
|
+
* Production deployments should set this to their CDN / R2 host(s).
|
|
796
|
+
*/
|
|
797
|
+
readonly allowedHosts?: readonly string[] | undefined;
|
|
798
|
+
}
|
|
799
|
+
interface ResolvedExecutionConfig {
|
|
800
|
+
readonly maxTurns: number;
|
|
801
|
+
readonly maxSubagentDepth: number;
|
|
802
|
+
readonly turnTimeoutMs: number;
|
|
803
|
+
readonly runTimeoutMs: number;
|
|
804
|
+
/** Context window size in tokens. Used by compaction to decide when to compact. Default: 200_000. */
|
|
805
|
+
readonly contextLimit: number;
|
|
806
|
+
/** Max number of concurrency-safe tools to execute in parallel. Default: 10. */
|
|
807
|
+
readonly maxToolConcurrency: number;
|
|
808
|
+
}
|
|
809
|
+
interface ResolvedTranscriptConfig {
|
|
810
|
+
readonly enabled: boolean;
|
|
811
|
+
readonly flushPolicy: FlushPolicy;
|
|
812
|
+
readonly idleFlushMs: number;
|
|
813
|
+
}
|
|
814
|
+
interface ResolvedHooksConfig {
|
|
815
|
+
readonly preRun: readonly PreRunHook[];
|
|
816
|
+
readonly postRun: readonly PostRunHook[];
|
|
817
|
+
readonly preTurn: readonly PreTurnHook[];
|
|
818
|
+
readonly postTurn: readonly PostTurnHook[];
|
|
819
|
+
readonly preToolCall: readonly PreToolCallHook[];
|
|
820
|
+
readonly postToolCall: readonly PostToolCallHook[];
|
|
821
|
+
/**
|
|
822
|
+
* Optional gate called before every tool dispatch. Returning
|
|
823
|
+
* `{ allow: false, reason? }` pauses the run with the pending tool
|
|
824
|
+
* captured in the snapshot. `undefined` means no gate — the default.
|
|
825
|
+
*
|
|
826
|
+
* This is the only real pause pathway in v0.1. Setting this enables
|
|
827
|
+
* human-in-the-loop workflows without bypassing `initEngine()`.
|
|
828
|
+
*/
|
|
829
|
+
readonly gateBeforeTool: GateBeforeToolHook | undefined;
|
|
830
|
+
/**
|
|
831
|
+
* When true, the parent's gate propagates into every spawned
|
|
832
|
+
* subagent's inner loop. A gate denial inside the child pauses the
|
|
833
|
+
* parent run with `RunSnapshot.pendingSubagent` populated so
|
|
834
|
+
* observers can see which subagent tool was blocked. Default: false.
|
|
835
|
+
*
|
|
836
|
+
* Only the parent engine reads this flag; subagents don't re-read it.
|
|
837
|
+
* The `Agent` tool factory receives the effective gate callback at
|
|
838
|
+
* construction time.
|
|
839
|
+
*/
|
|
840
|
+
readonly propagateGateToSubagents?: boolean | undefined;
|
|
841
|
+
/**
|
|
842
|
+
* Stop hooks — fire after each turn. Can return `{ preventContinuation: true }`
|
|
843
|
+
* to stop the run gracefully. Ported from La-Machina's executeStopHooks().
|
|
844
|
+
*/
|
|
845
|
+
readonly stopHooks: readonly StopHook[];
|
|
846
|
+
}
|
|
847
|
+
interface ResolvedLoggingConfig {
|
|
848
|
+
readonly level: LogLevel;
|
|
849
|
+
readonly sink: LogSink;
|
|
850
|
+
}
|
|
851
|
+
interface ResolvedConfig {
|
|
852
|
+
readonly model: ResolvedModelConfig;
|
|
853
|
+
readonly storage: ResolvedStorageConfig;
|
|
854
|
+
readonly memory: ResolvedMemoryConfig;
|
|
855
|
+
readonly tools: ResolvedToolsConfig;
|
|
856
|
+
readonly agents: ResolvedAgentsConfig;
|
|
857
|
+
readonly skills: ResolvedSkillsConfig;
|
|
858
|
+
readonly execution: ResolvedExecutionConfig;
|
|
859
|
+
readonly transcript: ResolvedTranscriptConfig;
|
|
860
|
+
readonly hooks: ResolvedHooksConfig;
|
|
861
|
+
readonly logging: ResolvedLoggingConfig;
|
|
862
|
+
readonly mcp: ResolvedMcpConfig;
|
|
863
|
+
readonly permissions: ResolvedPermissionsConfig;
|
|
864
|
+
readonly compaction: ResolvedCompactionConfig;
|
|
865
|
+
readonly coordinator: ResolvedCoordinatorConfig;
|
|
866
|
+
readonly orchestrator: ResolvedOrchestratorConfig;
|
|
867
|
+
}
|
|
868
|
+
|
|
869
|
+
type DeepPartial<T> = T extends readonly unknown[] ? T : T extends object ? {
|
|
870
|
+
-readonly [K in keyof T]?: DeepPartial<T[K]> | undefined;
|
|
871
|
+
} : T;
|
|
872
|
+
type UserConfig = DeepPartial<ResolvedConfig>;
|
|
873
|
+
|
|
874
|
+
/**
|
|
875
|
+
* ModelAdapter — the interface every LLM provider must implement.
|
|
876
|
+
*
|
|
877
|
+
* Same pattern as StorageAdapter. The engine calls `streamMessage()`
|
|
878
|
+
* and gets back a normalized async generator of events. The adapter
|
|
879
|
+
* handles provider-specific HTTP translation behind the scenes.
|
|
880
|
+
*
|
|
881
|
+
* Two implementations ship:
|
|
882
|
+
* - AnthropicAdapter → @anthropic-ai/sdk (existing, default)
|
|
883
|
+
* - AISdkAdapter → Vercel AI SDK (75+ providers)
|
|
884
|
+
*
|
|
885
|
+
* NormalizedEvent and TokenUsage types come from api/streaming.ts —
|
|
886
|
+
* the canonical definitions. Adapters produce these; the agent loop
|
|
887
|
+
* consumes them. No duplicate type definitions.
|
|
888
|
+
*/
|
|
889
|
+
|
|
890
|
+
interface ModelToolDefinition {
|
|
891
|
+
readonly name: string;
|
|
892
|
+
readonly description: string;
|
|
893
|
+
readonly inputSchema: Record<string, unknown>;
|
|
894
|
+
}
|
|
895
|
+
interface ModelMessage {
|
|
896
|
+
readonly role: 'user' | 'assistant';
|
|
897
|
+
readonly content: unknown;
|
|
898
|
+
}
|
|
899
|
+
interface StreamRequest {
|
|
900
|
+
readonly messages: readonly ModelMessage[];
|
|
901
|
+
readonly system?: string | undefined;
|
|
902
|
+
readonly tools?: readonly ModelToolDefinition[] | undefined;
|
|
903
|
+
readonly maxTokens?: number | undefined;
|
|
904
|
+
readonly temperature?: number | undefined;
|
|
905
|
+
}
|
|
906
|
+
|
|
907
|
+
interface ModelAdapter {
|
|
908
|
+
streamMessage(request: StreamRequest): AsyncGenerator<NormalizedEvent, void, void>;
|
|
909
|
+
}
|
|
910
|
+
|
|
911
|
+
/**
|
|
912
|
+
* MCP sampling — Plan 018 §2.
|
|
913
|
+
*
|
|
914
|
+
* MCP servers can issue `sampling/createMessage` requests asking the
|
|
915
|
+
* *client* to run an LLM completion on their behalf. The canonical use
|
|
916
|
+
* case is a tool that needs to summarize / classify / structure some
|
|
917
|
+
* text internally without carrying its own API key.
|
|
918
|
+
*
|
|
919
|
+
* This module provides:
|
|
920
|
+
*
|
|
921
|
+
* - `SamplingHandler` — the function callers can install via
|
|
922
|
+
* `EngineInternals.samplingHandler` to customize routing.
|
|
923
|
+
* - `defaultSamplingHandler(modelAdapter)` — a ready-to-use default
|
|
924
|
+
* that routes every request to the engine's own model adapter.
|
|
925
|
+
* Same model, same API key, same billing.
|
|
926
|
+
* - Request/response shapes mirroring MCP's schema closely enough to
|
|
927
|
+
* translate both directions without leaking SDK internals into the
|
|
928
|
+
* rest of the engine.
|
|
929
|
+
*
|
|
930
|
+
* Safety: calling sampling is OFF by default per MCP server
|
|
931
|
+
* (`allowSampling: false`). When a server attempts sampling and the
|
|
932
|
+
* flag is off, the McpClient responds with a protocol error instead
|
|
933
|
+
* of invoking the handler. The handler itself is only called after
|
|
934
|
+
* the gate passes.
|
|
935
|
+
*/
|
|
936
|
+
|
|
937
|
+
/** Content block shape accepted from / sent to the MCP server. */
|
|
938
|
+
interface SamplingTextBlock {
|
|
939
|
+
readonly type: 'text';
|
|
940
|
+
readonly text: string;
|
|
941
|
+
}
|
|
942
|
+
interface SamplingMessage {
|
|
943
|
+
readonly role: 'user' | 'assistant';
|
|
944
|
+
readonly content: SamplingTextBlock | ReadonlyArray<SamplingTextBlock>;
|
|
945
|
+
}
|
|
946
|
+
interface SamplingModelPreferences {
|
|
947
|
+
readonly hints?: ReadonlyArray<{
|
|
948
|
+
readonly name?: string;
|
|
949
|
+
}>;
|
|
950
|
+
readonly costPriority?: number;
|
|
951
|
+
readonly speedPriority?: number;
|
|
952
|
+
readonly intelligencePriority?: number;
|
|
953
|
+
}
|
|
954
|
+
interface SamplingRequest {
|
|
955
|
+
readonly messages: ReadonlyArray<SamplingMessage>;
|
|
956
|
+
readonly systemPrompt?: string;
|
|
957
|
+
readonly maxTokens: number;
|
|
958
|
+
readonly temperature?: number;
|
|
959
|
+
readonly stopSequences?: ReadonlyArray<string>;
|
|
960
|
+
readonly modelPreferences?: SamplingModelPreferences;
|
|
961
|
+
readonly includeContext?: 'none' | 'thisServer' | 'allServers';
|
|
962
|
+
readonly metadata?: Readonly<Record<string, unknown>>;
|
|
963
|
+
}
|
|
964
|
+
interface SamplingResponse {
|
|
965
|
+
readonly role: 'assistant';
|
|
966
|
+
readonly model: string;
|
|
967
|
+
/** The assistant text. */
|
|
968
|
+
readonly content: SamplingTextBlock;
|
|
969
|
+
readonly stopReason?: 'endTurn' | 'stopSequence' | 'maxTokens' | 'error';
|
|
970
|
+
}
|
|
971
|
+
interface SamplingContext {
|
|
972
|
+
/** MCP server that issued the request. */
|
|
973
|
+
readonly serverName: string;
|
|
974
|
+
/** Engine runId for the run this handler was invoked inside. */
|
|
975
|
+
readonly runId?: string;
|
|
976
|
+
/** Engine nodeId for the run. */
|
|
977
|
+
readonly nodeId?: string;
|
|
978
|
+
/**
|
|
979
|
+
* Nesting depth — incremented when sampling itself triggers another
|
|
980
|
+
* sampling request. Guards against loops: handlers MAY refuse when
|
|
981
|
+
* `depth` exceeds a threshold (default implementation refuses at 3).
|
|
982
|
+
*/
|
|
983
|
+
readonly depth: number;
|
|
984
|
+
}
|
|
985
|
+
type SamplingHandler = (request: SamplingRequest, context: SamplingContext) => Promise<SamplingResponse>;
|
|
986
|
+
/** Max depth the default handler will recurse through. */
|
|
987
|
+
declare const DEFAULT_SAMPLING_MAX_DEPTH = 3;
|
|
988
|
+
/**
|
|
989
|
+
* Build a default handler that routes every sampling request to the
|
|
990
|
+
* provided `ModelAdapter`. Consumes the stream, concatenates the text
|
|
991
|
+
* blocks, and maps the stop reason into the MCP vocabulary.
|
|
992
|
+
*
|
|
993
|
+
* If you want a different model per server, provide a custom
|
|
994
|
+
* handler via `EngineInternals.samplingHandler`.
|
|
995
|
+
*/
|
|
996
|
+
declare function defaultSamplingHandler(modelAdapter: ModelAdapter): SamplingHandler;
|
|
997
|
+
|
|
998
|
+
/**
|
|
999
|
+
* SubagentRegistry — tracks parent→child relationships across a single
|
|
1000
|
+
* `engine.run()`. Mints unique `agentId`s, computes spawn depth, and
|
|
1001
|
+
* enforces `maxDepth` so a runaway loop can't recursively spawn forever.
|
|
1002
|
+
*
|
|
1003
|
+
* The registry's lifetime is one engine run. A fresh instance is built
|
|
1004
|
+
* inside `engine.run()` and shared with every Agent tool invocation
|
|
1005
|
+
* during that run, including children that themselves spawn grandchildren.
|
|
1006
|
+
*
|
|
1007
|
+
* Tracking is in-memory only. We don't persist parent→child relationships
|
|
1008
|
+
* to the transcript here — that's the runner's job, which writes
|
|
1009
|
+
* `subagent_spawn` and `subagent_done` entries to the parent's transcript.
|
|
1010
|
+
*/
|
|
1011
|
+
interface SubagentRegistryOptions {
|
|
1012
|
+
/** Maximum spawn depth. Depth 1 = a direct child of the root run. */
|
|
1013
|
+
readonly maxDepth: number;
|
|
1014
|
+
}
|
|
1015
|
+
interface SpawnResult {
|
|
1016
|
+
readonly agentId: string;
|
|
1017
|
+
readonly depth: number;
|
|
1018
|
+
}
|
|
1019
|
+
/** Result from a background agent, stored on completion. */
|
|
1020
|
+
interface BackgroundAgentResult {
|
|
1021
|
+
readonly agentId: string;
|
|
1022
|
+
readonly output: string;
|
|
1023
|
+
readonly isError: boolean;
|
|
1024
|
+
readonly description?: string;
|
|
1025
|
+
}
|
|
1026
|
+
/** Serialized form for persistence alongside transcripts. */
|
|
1027
|
+
interface SerializedAgent {
|
|
1028
|
+
readonly agentId: string;
|
|
1029
|
+
readonly parentId: string | null;
|
|
1030
|
+
readonly depth: number;
|
|
1031
|
+
readonly name?: string;
|
|
1032
|
+
readonly status: 'running' | 'stopped' | 'completed';
|
|
1033
|
+
}
|
|
1034
|
+
declare class SubagentRegistry {
|
|
1035
|
+
private readonly maxDepth;
|
|
1036
|
+
private readonly agents;
|
|
1037
|
+
constructor(options: SubagentRegistryOptions);
|
|
1038
|
+
/**
|
|
1039
|
+
* Mint a new subagent under `parentAgentId` (null = direct child of
|
|
1040
|
+
* the root run). Throws if the resulting depth would exceed `maxDepth`.
|
|
1041
|
+
*/
|
|
1042
|
+
spawn(parentAgentId: string | null): SpawnResult;
|
|
1043
|
+
getDepth(agentId: string): number;
|
|
1044
|
+
getParent(agentId: string): string | null;
|
|
1045
|
+
count(): number;
|
|
1046
|
+
/** Set a human-readable name for routing (e.g. subagent_type or task description). */
|
|
1047
|
+
setName(agentId: string, name: string): void;
|
|
1048
|
+
/** Find an agent by its human-readable name. Returns the first match. */
|
|
1049
|
+
findByName(name: string): string | undefined;
|
|
1050
|
+
setStatus(agentId: string, status: 'running' | 'stopped' | 'completed'): void;
|
|
1051
|
+
getStatus(agentId: string): 'running' | 'stopped' | 'completed';
|
|
1052
|
+
/** Queue a message for a running agent. */
|
|
1053
|
+
queueMessage(agentId: string, message: string): void;
|
|
1054
|
+
/** Drain all pending messages. Returns empty array if none. */
|
|
1055
|
+
drainMessages(agentId: string): string[];
|
|
1056
|
+
/** Mark an agent as background (fire-and-forget). */
|
|
1057
|
+
setBackground(agentId: string): void;
|
|
1058
|
+
/** Track a background agent's Promise for drain-before-pause. */
|
|
1059
|
+
setBackgroundPromise(agentId: string, promise: Promise<void>): void;
|
|
1060
|
+
/** Store a background agent's result on completion. */
|
|
1061
|
+
setBackgroundResult(agentId: string, result: BackgroundAgentResult): void;
|
|
1062
|
+
/**
|
|
1063
|
+
* Drain all completed background agent results. Returns them and
|
|
1064
|
+
* clears the stored results so they're only delivered once.
|
|
1065
|
+
* Called at the top of each turn in agentLoop.
|
|
1066
|
+
*/
|
|
1067
|
+
drainCompletedBackgroundResults(): BackgroundAgentResult[];
|
|
1068
|
+
/**
|
|
1069
|
+
* Wait for all running background agents to complete (with timeout).
|
|
1070
|
+
* Called before pause to drain in-flight work. Returns results from
|
|
1071
|
+
* agents that finished within the timeout.
|
|
1072
|
+
*/
|
|
1073
|
+
awaitBackgroundAgents(timeoutMs?: number): Promise<BackgroundAgentResult[]>;
|
|
1074
|
+
/** Serialize all agents for sidecar persistence. */
|
|
1075
|
+
toJSON(): SerializedAgent[];
|
|
1076
|
+
/** Rebuild registry from serialized data (e.g. on resume). */
|
|
1077
|
+
static fromJSON(data: SerializedAgent[], maxDepth: number): SubagentRegistry;
|
|
1078
|
+
private requireAgent;
|
|
1079
|
+
}
|
|
1080
|
+
|
|
1081
|
+
/**
|
|
1082
|
+
* Smart memory type definitions.
|
|
1083
|
+
*
|
|
1084
|
+
* The Engram is the fundamental unit of memory — named for Karl Lashley's
|
|
1085
|
+
* concept of the physical substrate of a memory trace. Each engram has a
|
|
1086
|
+
* kind (always / never / when / lesson / profile), a scope (global vs.
|
|
1087
|
+
* workspace), a confidence level, and a topic.
|
|
1088
|
+
*/
|
|
1089
|
+
type EngramKind = 'always' | 'never' | 'when' | 'lesson' | 'profile';
|
|
1090
|
+
type EngramScope = 'global' | 'workspace';
|
|
1091
|
+
type EngramConfidence = 'high' | 'medium' | 'low';
|
|
1092
|
+
type EngramSource = 'user' | 'consolidation' | 'llm';
|
|
1093
|
+
interface Engram {
|
|
1094
|
+
readonly text: string;
|
|
1095
|
+
readonly kind: EngramKind;
|
|
1096
|
+
readonly scope: EngramScope;
|
|
1097
|
+
readonly confidence: EngramConfidence;
|
|
1098
|
+
readonly topic: string;
|
|
1099
|
+
readonly source: EngramSource;
|
|
1100
|
+
}
|
|
1101
|
+
/**
|
|
1102
|
+
* Episodic memory entry — a single turn snapshot. JSONL-encoded for
|
|
1103
|
+
* append-only logging via the storage adapter.
|
|
1104
|
+
*/
|
|
1105
|
+
interface Episode {
|
|
1106
|
+
readonly ts: string;
|
|
1107
|
+
readonly session: string;
|
|
1108
|
+
readonly turn: number;
|
|
1109
|
+
readonly role: 'user' | 'assistant' | 'tool_call' | 'tool_result';
|
|
1110
|
+
readonly content: string;
|
|
1111
|
+
readonly meta: Readonly<Record<string, unknown>>;
|
|
1112
|
+
}
|
|
1113
|
+
|
|
1114
|
+
/**
|
|
1115
|
+
* EpisodicMemory — append-only JSONL log of every turn in a run.
|
|
1116
|
+
*
|
|
1117
|
+
* Ported from La-Machina with TS-strict cleanup. The episodic log
|
|
1118
|
+
* captures the raw timeline of conversation turns (user input,
|
|
1119
|
+
* assistant text, tool calls, tool results) for later inspection,
|
|
1120
|
+
* search, or training data extraction.
|
|
1121
|
+
*
|
|
1122
|
+
* Design rules:
|
|
1123
|
+
* - **Fire-and-forget**: `logTurn()` returns synchronously and never
|
|
1124
|
+
* throws. Storage errors are swallowed so logging cannot break the
|
|
1125
|
+
* agent loop.
|
|
1126
|
+
* - **Disabled = total no-op**: when `enabled` is false, no file is
|
|
1127
|
+
* created and `logTurn()` does nothing. Used to implement
|
|
1128
|
+
* `memory.mode === 'off'` at the engine config layer.
|
|
1129
|
+
* - **Per-session file**: each `startSession()` mints a fresh JSONL
|
|
1130
|
+
* under `{subdir}/{sessionId}.jsonl`. Format `YYYYMMDD_HHMMSS`.
|
|
1131
|
+
* - **Truncation**: tool_call and tool_result content is capped at
|
|
1132
|
+
* 2000 characters to keep individual entries small.
|
|
1133
|
+
*/
|
|
1134
|
+
|
|
1135
|
+
declare class EpisodicMemory {
|
|
1136
|
+
private readonly storage;
|
|
1137
|
+
private readonly subdir;
|
|
1138
|
+
private _enabled;
|
|
1139
|
+
private _sessionId;
|
|
1140
|
+
private _filePath;
|
|
1141
|
+
/**
|
|
1142
|
+
* Serialization chain — every log() chains its appendFile onto the
|
|
1143
|
+
* previous one so on-disk order matches call order. Without this,
|
|
1144
|
+
* two concurrent unawaited appendFile calls can land in arbitrary
|
|
1145
|
+
* order even though each individual O_APPEND is atomic.
|
|
1146
|
+
*/
|
|
1147
|
+
private writeChain;
|
|
1148
|
+
constructor(storage: StorageAdapter, subdir?: string, enabled?: boolean);
|
|
1149
|
+
get enabled(): boolean;
|
|
1150
|
+
set enabled(value: boolean);
|
|
1151
|
+
get currentSessionId(): string | null;
|
|
1152
|
+
/**
|
|
1153
|
+
* Start a new session — mints a timestamp-based id and creates an
|
|
1154
|
+
* empty JSONL file under `{subdir}/`. Returns the session id, or
|
|
1155
|
+
* empty string if disabled.
|
|
1156
|
+
*/
|
|
1157
|
+
startSession(): Promise<string>;
|
|
1158
|
+
resumeSession(sessionId: string): Promise<string>;
|
|
1159
|
+
/**
|
|
1160
|
+
* Log a single episode entry. Fire-and-forget — never blocks, never
|
|
1161
|
+
* throws. Disabled mode and missing session both produce silent
|
|
1162
|
+
* no-ops so callers don't have to guard.
|
|
1163
|
+
*/
|
|
1164
|
+
log(episode: Episode): void;
|
|
1165
|
+
/** Convenience wrapper that builds an Episode and logs it. */
|
|
1166
|
+
logTurn(turn: number, role: Episode['role'], content: string, meta?: Readonly<Record<string, unknown>>): void;
|
|
1167
|
+
}
|
|
1168
|
+
|
|
1169
|
+
/**
|
|
1170
|
+
* Transcript entry types and schemas.
|
|
1171
|
+
*
|
|
1172
|
+
* A transcript is a sequence of `Entry` records persisted as newline-
|
|
1173
|
+
* delimited JSON (NDJSON) sharded across numbered `.jsonl` files. This
|
|
1174
|
+
* module defines the entry type union, their Zod schemas, and the
|
|
1175
|
+
* serialize/parse helpers used by the writer and reader.
|
|
1176
|
+
*
|
|
1177
|
+
* The entry vocabulary is deliberately minimal — seven types. Everything
|
|
1178
|
+
* CLI-UX-specific in La-Machina's 19-type union has been dropped. Only
|
|
1179
|
+
* what a workflow engine needs to replay, audit, and learn from:
|
|
1180
|
+
*
|
|
1181
|
+
* user — user message (prompt, tool result from the client)
|
|
1182
|
+
* assistant — assistant message (text + tool_use blocks)
|
|
1183
|
+
* tool_result — result of a tool call dispatched by the engine
|
|
1184
|
+
* subagent_spawn — a child agent was created
|
|
1185
|
+
* subagent_done — a child agent returned its final output
|
|
1186
|
+
* meta — session-scoped metadata (title, tags, workflow ctx)
|
|
1187
|
+
* error — engine-level failure (tool crash, stream break, etc.)
|
|
1188
|
+
*/
|
|
1189
|
+
|
|
1190
|
+
declare const EntrySchema: z.ZodDiscriminatedUnion<"type", [z.ZodObject<{
|
|
1191
|
+
message: z.ZodObject<{
|
|
1192
|
+
role: z.ZodEnum<["user", "assistant"]>;
|
|
1193
|
+
content: z.ZodArray<z.ZodUnknown, "many">;
|
|
1194
|
+
}, "strict", z.ZodTypeAny, {
|
|
1195
|
+
role: "user" | "assistant";
|
|
1196
|
+
content: unknown[];
|
|
1197
|
+
}, {
|
|
1198
|
+
role: "user" | "assistant";
|
|
1199
|
+
content: unknown[];
|
|
1200
|
+
}>;
|
|
1201
|
+
uuid: z.ZodString;
|
|
1202
|
+
parentUuid: z.ZodNullable<z.ZodString>;
|
|
1203
|
+
ts: z.ZodString;
|
|
1204
|
+
type: z.ZodLiteral<"user">;
|
|
1205
|
+
}, "strict", z.ZodTypeAny, {
|
|
1206
|
+
type: "user";
|
|
1207
|
+
message: {
|
|
1208
|
+
role: "user" | "assistant";
|
|
1209
|
+
content: unknown[];
|
|
1210
|
+
};
|
|
1211
|
+
uuid: string;
|
|
1212
|
+
parentUuid: string | null;
|
|
1213
|
+
ts: string;
|
|
1214
|
+
}, {
|
|
1215
|
+
type: "user";
|
|
1216
|
+
message: {
|
|
1217
|
+
role: "user" | "assistant";
|
|
1218
|
+
content: unknown[];
|
|
1219
|
+
};
|
|
1220
|
+
uuid: string;
|
|
1221
|
+
parentUuid: string | null;
|
|
1222
|
+
ts: string;
|
|
1223
|
+
}>, z.ZodObject<{
|
|
1224
|
+
message: z.ZodObject<{
|
|
1225
|
+
role: z.ZodEnum<["user", "assistant"]>;
|
|
1226
|
+
content: z.ZodArray<z.ZodUnknown, "many">;
|
|
1227
|
+
}, "strict", z.ZodTypeAny, {
|
|
1228
|
+
role: "user" | "assistant";
|
|
1229
|
+
content: unknown[];
|
|
1230
|
+
}, {
|
|
1231
|
+
role: "user" | "assistant";
|
|
1232
|
+
content: unknown[];
|
|
1233
|
+
}>;
|
|
1234
|
+
uuid: z.ZodString;
|
|
1235
|
+
parentUuid: z.ZodNullable<z.ZodString>;
|
|
1236
|
+
ts: z.ZodString;
|
|
1237
|
+
type: z.ZodLiteral<"assistant">;
|
|
1238
|
+
}, "strict", z.ZodTypeAny, {
|
|
1239
|
+
type: "assistant";
|
|
1240
|
+
message: {
|
|
1241
|
+
role: "user" | "assistant";
|
|
1242
|
+
content: unknown[];
|
|
1243
|
+
};
|
|
1244
|
+
uuid: string;
|
|
1245
|
+
parentUuid: string | null;
|
|
1246
|
+
ts: string;
|
|
1247
|
+
}, {
|
|
1248
|
+
type: "assistant";
|
|
1249
|
+
message: {
|
|
1250
|
+
role: "user" | "assistant";
|
|
1251
|
+
content: unknown[];
|
|
1252
|
+
};
|
|
1253
|
+
uuid: string;
|
|
1254
|
+
parentUuid: string | null;
|
|
1255
|
+
ts: string;
|
|
1256
|
+
}>, z.ZodObject<{
|
|
1257
|
+
toolUseId: z.ZodString;
|
|
1258
|
+
content: z.ZodUnknown;
|
|
1259
|
+
isError: z.ZodOptional<z.ZodBoolean>;
|
|
1260
|
+
uuid: z.ZodString;
|
|
1261
|
+
parentUuid: z.ZodNullable<z.ZodString>;
|
|
1262
|
+
ts: z.ZodString;
|
|
1263
|
+
type: z.ZodLiteral<"tool_result">;
|
|
1264
|
+
}, "strict", z.ZodTypeAny, {
|
|
1265
|
+
type: "tool_result";
|
|
1266
|
+
uuid: string;
|
|
1267
|
+
toolUseId: string;
|
|
1268
|
+
parentUuid: string | null;
|
|
1269
|
+
ts: string;
|
|
1270
|
+
content?: unknown;
|
|
1271
|
+
isError?: boolean | undefined;
|
|
1272
|
+
}, {
|
|
1273
|
+
type: "tool_result";
|
|
1274
|
+
uuid: string;
|
|
1275
|
+
toolUseId: string;
|
|
1276
|
+
parentUuid: string | null;
|
|
1277
|
+
ts: string;
|
|
1278
|
+
content?: unknown;
|
|
1279
|
+
isError?: boolean | undefined;
|
|
1280
|
+
}>, z.ZodObject<{
|
|
1281
|
+
agentId: z.ZodString;
|
|
1282
|
+
agentType: z.ZodString;
|
|
1283
|
+
uuid: z.ZodString;
|
|
1284
|
+
parentUuid: z.ZodNullable<z.ZodString>;
|
|
1285
|
+
ts: z.ZodString;
|
|
1286
|
+
type: z.ZodLiteral<"subagent_spawn">;
|
|
1287
|
+
}, "strict", z.ZodTypeAny, {
|
|
1288
|
+
type: "subagent_spawn";
|
|
1289
|
+
uuid: string;
|
|
1290
|
+
agentId: string;
|
|
1291
|
+
parentUuid: string | null;
|
|
1292
|
+
ts: string;
|
|
1293
|
+
agentType: string;
|
|
1294
|
+
}, {
|
|
1295
|
+
type: "subagent_spawn";
|
|
1296
|
+
uuid: string;
|
|
1297
|
+
agentId: string;
|
|
1298
|
+
parentUuid: string | null;
|
|
1299
|
+
ts: string;
|
|
1300
|
+
agentType: string;
|
|
1301
|
+
}>, z.ZodObject<{
|
|
1302
|
+
agentId: z.ZodString;
|
|
1303
|
+
output: z.ZodString;
|
|
1304
|
+
uuid: z.ZodString;
|
|
1305
|
+
parentUuid: z.ZodNullable<z.ZodString>;
|
|
1306
|
+
ts: z.ZodString;
|
|
1307
|
+
type: z.ZodLiteral<"subagent_done">;
|
|
1308
|
+
}, "strict", z.ZodTypeAny, {
|
|
1309
|
+
type: "subagent_done";
|
|
1310
|
+
uuid: string;
|
|
1311
|
+
output: string;
|
|
1312
|
+
agentId: string;
|
|
1313
|
+
parentUuid: string | null;
|
|
1314
|
+
ts: string;
|
|
1315
|
+
}, {
|
|
1316
|
+
type: "subagent_done";
|
|
1317
|
+
uuid: string;
|
|
1318
|
+
output: string;
|
|
1319
|
+
agentId: string;
|
|
1320
|
+
parentUuid: string | null;
|
|
1321
|
+
ts: string;
|
|
1322
|
+
}>, z.ZodObject<{
|
|
1323
|
+
key: z.ZodString;
|
|
1324
|
+
value: z.ZodUnknown;
|
|
1325
|
+
uuid: z.ZodString;
|
|
1326
|
+
ts: z.ZodString;
|
|
1327
|
+
type: z.ZodLiteral<"meta">;
|
|
1328
|
+
}, "strict", z.ZodTypeAny, {
|
|
1329
|
+
type: "meta";
|
|
1330
|
+
uuid: string;
|
|
1331
|
+
ts: string;
|
|
1332
|
+
key: string;
|
|
1333
|
+
value?: unknown;
|
|
1334
|
+
}, {
|
|
1335
|
+
type: "meta";
|
|
1336
|
+
uuid: string;
|
|
1337
|
+
ts: string;
|
|
1338
|
+
key: string;
|
|
1339
|
+
value?: unknown;
|
|
1340
|
+
}>, z.ZodObject<{
|
|
1341
|
+
error: z.ZodObject<{
|
|
1342
|
+
code: z.ZodString;
|
|
1343
|
+
message: z.ZodString;
|
|
1344
|
+
stack: z.ZodOptional<z.ZodString>;
|
|
1345
|
+
}, "strict", z.ZodTypeAny, {
|
|
1346
|
+
code: string;
|
|
1347
|
+
message: string;
|
|
1348
|
+
stack?: string | undefined;
|
|
1349
|
+
}, {
|
|
1350
|
+
code: string;
|
|
1351
|
+
message: string;
|
|
1352
|
+
stack?: string | undefined;
|
|
1353
|
+
}>;
|
|
1354
|
+
uuid: z.ZodString;
|
|
1355
|
+
ts: z.ZodString;
|
|
1356
|
+
type: z.ZodLiteral<"error">;
|
|
1357
|
+
}, "strict", z.ZodTypeAny, {
|
|
1358
|
+
type: "error";
|
|
1359
|
+
error: {
|
|
1360
|
+
code: string;
|
|
1361
|
+
message: string;
|
|
1362
|
+
stack?: string | undefined;
|
|
1363
|
+
};
|
|
1364
|
+
uuid: string;
|
|
1365
|
+
ts: string;
|
|
1366
|
+
}, {
|
|
1367
|
+
type: "error";
|
|
1368
|
+
error: {
|
|
1369
|
+
code: string;
|
|
1370
|
+
message: string;
|
|
1371
|
+
stack?: string | undefined;
|
|
1372
|
+
};
|
|
1373
|
+
uuid: string;
|
|
1374
|
+
ts: string;
|
|
1375
|
+
}>]>;
|
|
1376
|
+
type Entry = z.infer<typeof EntrySchema>;
|
|
1377
|
+
|
|
1378
|
+
/**
|
|
1379
|
+
* TranscriptMeta — the small sidecar at `{logPath}/meta.json` that the
|
|
1380
|
+
* writer updates on every flush.
|
|
1381
|
+
*
|
|
1382
|
+
* The meta document serves two purposes:
|
|
1383
|
+
* 1. **Fast session listing**: upstream callers (Nikaido, dashboard)
|
|
1384
|
+
* can stat this file to know turn count, status, and last update
|
|
1385
|
+
* without reading the transcript shards.
|
|
1386
|
+
* 2. **Resume anchor**: on pause/resume, the snapshot points to the
|
|
1387
|
+
* last shard index recorded here, letting the reader skip straight
|
|
1388
|
+
* to where it left off.
|
|
1389
|
+
*
|
|
1390
|
+
* The document is tiny (<1KB) and written frequently, so the cost of
|
|
1391
|
+
* overwriting on every flush is negligible. Writes go through the
|
|
1392
|
+
* storage adapter's atomic `writeFile`.
|
|
1393
|
+
*/
|
|
1394
|
+
|
|
1395
|
+
type TranscriptStatus = 'pending' | 'running' | 'paused' | 'done' | 'failed';
|
|
1396
|
+
declare const TranscriptMetaSchema: z.ZodObject<{
|
|
1397
|
+
version: z.ZodLiteral<1>;
|
|
1398
|
+
status: z.ZodEnum<["pending", "running", "paused", "done", "failed"]>;
|
|
1399
|
+
startedAt: z.ZodString;
|
|
1400
|
+
updatedAt: z.ZodString;
|
|
1401
|
+
turnCount: z.ZodNumber;
|
|
1402
|
+
messageCount: z.ZodNumber;
|
|
1403
|
+
lastShardIndex: z.ZodNullable<z.ZodNumber>;
|
|
1404
|
+
shardCount: z.ZodNumber;
|
|
1405
|
+
}, "strict", z.ZodTypeAny, {
|
|
1406
|
+
status: "paused" | "done" | "failed" | "running" | "pending";
|
|
1407
|
+
version: 1;
|
|
1408
|
+
messageCount: number;
|
|
1409
|
+
lastShardIndex: number | null;
|
|
1410
|
+
startedAt: string;
|
|
1411
|
+
updatedAt: string;
|
|
1412
|
+
turnCount: number;
|
|
1413
|
+
shardCount: number;
|
|
1414
|
+
}, {
|
|
1415
|
+
status: "paused" | "done" | "failed" | "running" | "pending";
|
|
1416
|
+
version: 1;
|
|
1417
|
+
messageCount: number;
|
|
1418
|
+
lastShardIndex: number | null;
|
|
1419
|
+
startedAt: string;
|
|
1420
|
+
updatedAt: string;
|
|
1421
|
+
turnCount: number;
|
|
1422
|
+
shardCount: number;
|
|
1423
|
+
}>;
|
|
1424
|
+
type TranscriptMeta = z.infer<typeof TranscriptMetaSchema>;
|
|
1425
|
+
|
|
1426
|
+
/**
|
|
1427
|
+
* Permission evaluator — the decision pipeline.
|
|
1428
|
+
*
|
|
1429
|
+
* Pipeline (evaluated in order, first match wins):
|
|
1430
|
+
*
|
|
1431
|
+
* 1. Mode check:
|
|
1432
|
+
* - `'open'` → allow immediately (no further evaluation)
|
|
1433
|
+
* - `'locked'` → check allowlist only (skip rules entirely)
|
|
1434
|
+
* - `'rules'` → proceed to step 2
|
|
1435
|
+
*
|
|
1436
|
+
* 2. Safe-tool allowlist fast-path:
|
|
1437
|
+
* - If the tool is on the safe allowlist → allow (skip rules)
|
|
1438
|
+
*
|
|
1439
|
+
* 3. Rule evaluation (top-to-bottom):
|
|
1440
|
+
* - First matching rule wins
|
|
1441
|
+
* - `allow:X` → allow
|
|
1442
|
+
* - `deny:X` → deny with reason
|
|
1443
|
+
*
|
|
1444
|
+
* 4. Fall-closed:
|
|
1445
|
+
* - If no rule matched → deny ("no matching rule, mode is rules")
|
|
1446
|
+
*
|
|
1447
|
+
* The evaluator is a pure function: no side effects, no LLM calls.
|
|
1448
|
+
* The gate hook (`hooks.gateBeforeTool`) runs AFTER this evaluation in
|
|
1449
|
+
* the engine's tool dispatch — it can override a permission allow with
|
|
1450
|
+
* a pause, but it cannot override a permission deny.
|
|
1451
|
+
*/
|
|
1452
|
+
|
|
1453
|
+
/**
|
|
1454
|
+
* Compiled permission policy — call `check(toolName)` to get a decision.
|
|
1455
|
+
* Built once at engine construction time from the resolved config.
|
|
1456
|
+
*/
|
|
1457
|
+
interface PermissionPolicy {
|
|
1458
|
+
check(toolName: string): PermissionDecision;
|
|
1459
|
+
}
|
|
1460
|
+
/**
|
|
1461
|
+
* Build a `PermissionPolicy` from a resolved config. The returned
|
|
1462
|
+
* object is reusable across all runs on the same engine instance.
|
|
1463
|
+
*/
|
|
1464
|
+
declare function buildPermissionPolicy(config: ResolvedPermissionsConfig): PermissionPolicy;
|
|
1465
|
+
|
|
1466
|
+
/**
|
|
1467
|
+
* ToolExecutor — runs tools by name with input validation, timeouts,
|
|
1468
|
+
* cancellation, error isolation, and lifecycle hooks.
|
|
1469
|
+
*
|
|
1470
|
+
* Responsibilities:
|
|
1471
|
+
* 1. Resolve a tool by name from a `ToolRegistry`. Unknown name →
|
|
1472
|
+
* typed error result, never an exception.
|
|
1473
|
+
* 2. Validate the caller-supplied input against the tool's Zod schema.
|
|
1474
|
+
* Invalid input → typed error result.
|
|
1475
|
+
* 3. Build a `ToolContext` with an `AbortSignal` that fires on either
|
|
1476
|
+
* timeout or external cancellation.
|
|
1477
|
+
* 4. Race the tool's `execute()` against `timeoutMs` (when set). On
|
|
1478
|
+
* timeout, return an error result and abort the context signal.
|
|
1479
|
+
* 5. Catch any synchronous or asynchronous throw from the tool and
|
|
1480
|
+
* wrap it as `{ content, isError: true }`. **A misbehaving tool
|
|
1481
|
+
* must never crash the agent loop.**
|
|
1482
|
+
* 6. Fire `preToolCall` and `postToolCall` hooks. `postToolCall` runs
|
|
1483
|
+
* in a finally semantic — even on error, even on timeout. Hook
|
|
1484
|
+
* failures are isolated; they never propagate.
|
|
1485
|
+
*
|
|
1486
|
+
* The executor is stateless except for the registry reference; a
|
|
1487
|
+
* single instance per engine run is fine.
|
|
1488
|
+
*/
|
|
1489
|
+
|
|
1490
|
+
interface PreToolCallEvent {
|
|
1491
|
+
readonly toolName: string;
|
|
1492
|
+
readonly input: unknown;
|
|
1493
|
+
}
|
|
1494
|
+
interface PostToolCallEvent {
|
|
1495
|
+
readonly toolName: string;
|
|
1496
|
+
readonly input: unknown;
|
|
1497
|
+
readonly result: ToolResult;
|
|
1498
|
+
readonly durationMs: number;
|
|
1499
|
+
}
|
|
1500
|
+
interface ToolExecutorHooks {
|
|
1501
|
+
readonly preToolCall?: (event: PreToolCallEvent) => void | Promise<void>;
|
|
1502
|
+
readonly postToolCall?: (event: PostToolCallEvent) => void | Promise<void>;
|
|
1503
|
+
}
|
|
1504
|
+
interface ToolExecutorOptions {
|
|
1505
|
+
readonly registry: ToolRegistry;
|
|
1506
|
+
/** Per-call timeout in milliseconds. Omit or 0 to disable. */
|
|
1507
|
+
readonly timeoutMs?: number;
|
|
1508
|
+
readonly hooks?: ToolExecutorHooks;
|
|
1509
|
+
/** Permission policy. When set, every tool dispatch is checked before
|
|
1510
|
+
* execution. Denied tools get an isError result without running. */
|
|
1511
|
+
readonly permissions?: PermissionPolicy;
|
|
1512
|
+
}
|
|
1513
|
+
interface ExecuteOptions {
|
|
1514
|
+
/** Optional external AbortSignal that can cancel the tool early. */
|
|
1515
|
+
readonly signal?: AbortSignal;
|
|
1516
|
+
}
|
|
1517
|
+
declare class ToolExecutor {
|
|
1518
|
+
private readonly registry;
|
|
1519
|
+
private readonly timeoutMs;
|
|
1520
|
+
private readonly hooks;
|
|
1521
|
+
private readonly permissions;
|
|
1522
|
+
constructor(options: ToolExecutorOptions);
|
|
1523
|
+
execute(toolName: string, rawInput: unknown, options?: ExecuteOptions): Promise<ToolResult>;
|
|
1524
|
+
private executeInner;
|
|
1525
|
+
private runWithTimeout;
|
|
1526
|
+
private firePreHook;
|
|
1527
|
+
private firePostHook;
|
|
1528
|
+
}
|
|
1529
|
+
|
|
1530
|
+
/**
|
|
1531
|
+
* SkillSource — abstraction over skill discovery and lazy body loading.
|
|
1532
|
+
*
|
|
1533
|
+
* Two implementations live alongside this file:
|
|
1534
|
+
* - `StorageSkillSource` (storageSkillSource.ts) — reads `SKILL.md`
|
|
1535
|
+
* files from a storage adapter (the current default behavior).
|
|
1536
|
+
* - `InlineSkillSource` (inlineSkillSource.ts) — takes an explicit
|
|
1537
|
+
* list of overrides passed per-run via `RunOptions.skills`.
|
|
1538
|
+
*
|
|
1539
|
+
* Downstream code (prompt builder, SkillPage tool) accepts `SkillSource`
|
|
1540
|
+
* rather than reaching into storage directly, so the two paths share
|
|
1541
|
+
* every contract: model sees the same catalogue, `SkillPage` returns
|
|
1542
|
+
* bodies the same way, and errors surface the same error shape.
|
|
1543
|
+
*/
|
|
1544
|
+
/**
|
|
1545
|
+
* Minimal descriptor the system prompt needs to list a skill. The full
|
|
1546
|
+
* body stays unloaded until `getSkillFile` / `getPage` is invoked.
|
|
1547
|
+
*/
|
|
1548
|
+
interface ResolvedSkill {
|
|
1549
|
+
/** Unique identifier — `[a-zA-Z0-9_-]+`. */
|
|
1550
|
+
readonly name: string;
|
|
1551
|
+
/** One-line human description. Rendered in the system prompt catalogue. */
|
|
1552
|
+
readonly description: string;
|
|
1553
|
+
/** True when supplemental `pages/*.md` are available. */
|
|
1554
|
+
readonly hasPages: boolean;
|
|
1555
|
+
}
|
|
1556
|
+
/**
|
|
1557
|
+
* Pluggable skill source used by the engine at run time.
|
|
1558
|
+
*
|
|
1559
|
+
* - `list()` runs once at run start. Cheap — names + descriptions only.
|
|
1560
|
+
* - `getSkillFile()` / `getPage()` are invoked by the `SkillPage` tool
|
|
1561
|
+
* when the model decides a skill is relevant. They MAY fetch from the
|
|
1562
|
+
* network; they SHOULD cache within a run.
|
|
1563
|
+
*
|
|
1564
|
+
* Implementations MUST return `null` for unknown skills / pages (never
|
|
1565
|
+
* throw for "not found"). They MAY throw for infrastructure failures
|
|
1566
|
+
* (fetch errors, disallowed hosts) — the `SkillPage` tool converts
|
|
1567
|
+
* those to tool errors.
|
|
1568
|
+
*/
|
|
1569
|
+
interface SkillSource {
|
|
1570
|
+
/** Enumerate available skills. Safe to call multiple times per run. */
|
|
1571
|
+
list(): Promise<ReadonlyArray<ResolvedSkill>>;
|
|
1572
|
+
/** Load the main `SKILL.md` body for a skill. `null` if unknown. */
|
|
1573
|
+
getSkillFile(skill: string): Promise<string | null>;
|
|
1574
|
+
/** Load a named page for a skill. `null` if skill or page is unknown. */
|
|
1575
|
+
getPage(skill: string, page: string): Promise<string | null>;
|
|
1576
|
+
/**
|
|
1577
|
+
* Return the list of page names available for a skill. Used by the
|
|
1578
|
+
* `SkillPage` tool to render a "did you mean?" suggestion when a page
|
|
1579
|
+
* name misses. Returns `[]` for unknown skills or skills with no pages.
|
|
1580
|
+
*/
|
|
1581
|
+
listPages(skill: string): Promise<ReadonlyArray<string>>;
|
|
1582
|
+
}
|
|
1583
|
+
/**
|
|
1584
|
+
* Shape the caller passes to `RunOptions.skills` / `ResumeOptions.skills`
|
|
1585
|
+
* to override disk-based discovery for exactly this run.
|
|
1586
|
+
*
|
|
1587
|
+
* Rules:
|
|
1588
|
+
* - `name` must match `/^[a-zA-Z0-9_-]+$/` — rejected at construction.
|
|
1589
|
+
* - `description` is required (needed for the prompt catalogue without fetch).
|
|
1590
|
+
* - Either `body` OR `url` for the main file. `body` wins when both set.
|
|
1591
|
+
* Omitting both is valid — the skill is listed but reading it errors.
|
|
1592
|
+
* - `pages` follow the same `body`/`url` rules per-page.
|
|
1593
|
+
* - `headers` travel with both the main fetch and every page fetch.
|
|
1594
|
+
*/
|
|
1595
|
+
interface SkillOverride {
|
|
1596
|
+
readonly name: string;
|
|
1597
|
+
readonly description: string;
|
|
1598
|
+
readonly body?: string;
|
|
1599
|
+
readonly url?: string;
|
|
1600
|
+
readonly pages?: Readonly<Record<string, SkillPageOverride>>;
|
|
1601
|
+
readonly headers?: Readonly<Record<string, string>>;
|
|
1602
|
+
}
|
|
1603
|
+
interface SkillPageOverride {
|
|
1604
|
+
readonly body?: string;
|
|
1605
|
+
readonly url?: string;
|
|
1606
|
+
}
|
|
1607
|
+
|
|
1608
|
+
/**
|
|
1609
|
+
* Engine-facing types for `run()` and `resume()`.
|
|
1610
|
+
*
|
|
1611
|
+
* Phase 1 defines the interface shapes. Actual behavior (streaming, tool
|
|
1612
|
+
* calls, pause/resume logic) arrives in Phases 7–10. These types are
|
|
1613
|
+
* exported from the public API so callers can compile against them.
|
|
1614
|
+
*/
|
|
1615
|
+
interface RunOptions {
|
|
1616
|
+
/** Unique run identifier. Optional — auto-generated as `run_<uuid>` if omitted. */
|
|
1617
|
+
readonly runId?: string;
|
|
1618
|
+
readonly nodeId: string;
|
|
1619
|
+
readonly task: string;
|
|
1620
|
+
readonly tools?: readonly string[] | undefined;
|
|
1621
|
+
readonly context?: Readonly<Record<string, unknown>> | undefined;
|
|
1622
|
+
/** Override maxTurns for this run only. Used by the orchestrator for per-phase budgets. */
|
|
1623
|
+
readonly maxTurns?: number | undefined;
|
|
1624
|
+
/**
|
|
1625
|
+
* Token budget for this run. When total tokens used (input + output)
|
|
1626
|
+
* exceeds this value, the run stops gracefully with the last assistant
|
|
1627
|
+
* output. Ported from La-Machina's task_budget enforcement.
|
|
1628
|
+
*/
|
|
1629
|
+
readonly tokenBudget?: number | undefined;
|
|
1630
|
+
/**
|
|
1631
|
+
* Output format. Default: 'text'.
|
|
1632
|
+
* - 'text': model responds freely, result.output is raw string
|
|
1633
|
+
* - 'json': engine injects schema instructions into system prompt,
|
|
1634
|
+
* parses the model's final response as JSON, validates against
|
|
1635
|
+
* outputSchema if provided, retries once on failure.
|
|
1636
|
+
* result.data contains the parsed+validated value.
|
|
1637
|
+
*/
|
|
1638
|
+
readonly outputFormat?: 'text' | 'json' | undefined;
|
|
1639
|
+
/**
|
|
1640
|
+
* Zod schema for structured output. Only used when outputFormat is 'json'.
|
|
1641
|
+
* Injected into system prompt as JSON Schema description, used to
|
|
1642
|
+
* validate the model's response. If validation fails, retries once.
|
|
1643
|
+
*/
|
|
1644
|
+
readonly outputSchema?: zod.ZodTypeAny | undefined;
|
|
1645
|
+
/**
|
|
1646
|
+
* Per-run skill override (Plan 017). When present, the engine IGNORES
|
|
1647
|
+
* `config.skills.autoload` + `config.skills.path` and uses this list
|
|
1648
|
+
* instead. Each override carries a description (always required) plus
|
|
1649
|
+
* either an inline `body` or an HTTPS `url` that is fetched on demand
|
|
1650
|
+
* by the `SkillPage` tool.
|
|
1651
|
+
*/
|
|
1652
|
+
readonly skills?: ReadonlyArray<SkillOverride> | undefined;
|
|
1653
|
+
}
|
|
1654
|
+
interface TokenUsage {
|
|
1655
|
+
readonly input: number;
|
|
1656
|
+
readonly output: number;
|
|
1657
|
+
readonly cacheCreationInput?: number | undefined;
|
|
1658
|
+
readonly cacheReadInput?: number | undefined;
|
|
1659
|
+
}
|
|
1660
|
+
/**
|
|
1661
|
+
* Why a run paused.
|
|
1662
|
+
*
|
|
1663
|
+
* v0.1 only ever produces `'gate_required'` — that's the one path the agent
|
|
1664
|
+
* loop uses to pause (`gateBeforeTool` callback returning `{ allow: false }`).
|
|
1665
|
+
*
|
|
1666
|
+
* The other three values are reserved for v0.2:
|
|
1667
|
+
* - `'max_turns'` — currently returns `{ status: 'failed', error: ERR_MAX_TURNS }`
|
|
1668
|
+
* instead. See `agentLoop.ts` + tests at
|
|
1669
|
+
* `test/unit/engine/agentLoop.test.ts` and
|
|
1670
|
+
* `test/integration/engine/run-with-mockapi.test.ts`.
|
|
1671
|
+
* - `'timeout'` — `execution.runTimeoutMs` is declared but not enforced.
|
|
1672
|
+
* - `'explicit'` — no explicit-pause API exists yet.
|
|
1673
|
+
*
|
|
1674
|
+
* Kept in the union so the v0.2 change is backwards-compatible (no type
|
|
1675
|
+
* narrowing in callers that already switch on the full set).
|
|
1676
|
+
*/
|
|
1677
|
+
type PauseReason = 'gate_required' | 'subagent_gate_required' | 'max_turns' | 'explicit' | 'timeout';
|
|
1678
|
+
interface RunSnapshot {
|
|
1679
|
+
readonly version: 1;
|
|
1680
|
+
readonly status: 'paused';
|
|
1681
|
+
readonly runId: string;
|
|
1682
|
+
readonly nodeId: string;
|
|
1683
|
+
readonly pausedAt: string;
|
|
1684
|
+
readonly pauseReason: PauseReason;
|
|
1685
|
+
readonly messageCount: number;
|
|
1686
|
+
readonly lastShardIndex: number;
|
|
1687
|
+
readonly lastMessageUuid: string;
|
|
1688
|
+
readonly pendingToolCall?: {
|
|
1689
|
+
toolName: string;
|
|
1690
|
+
toolUseId: string;
|
|
1691
|
+
input: unknown;
|
|
1692
|
+
calledAt: string;
|
|
1693
|
+
} | undefined;
|
|
1694
|
+
/**
|
|
1695
|
+
* Nested-pause pointer set when a subagent's own gate fired. The parent
|
|
1696
|
+
* is paused on its Agent tool call; this field records the child's
|
|
1697
|
+
* snapshot so observers can inspect which inner tool was blocked.
|
|
1698
|
+
* Populated only when `hooks.propagateGateToSubagents: true`.
|
|
1699
|
+
*/
|
|
1700
|
+
readonly pendingSubagent?: {
|
|
1701
|
+
subagentType: string;
|
|
1702
|
+
parentToolUseId: string;
|
|
1703
|
+
childSnapshot: RunSnapshot;
|
|
1704
|
+
} | undefined;
|
|
1705
|
+
readonly tokensUsedSoFar: TokenUsage;
|
|
1706
|
+
readonly turnsUsed: number;
|
|
1707
|
+
}
|
|
1708
|
+
interface TranscriptLocation {
|
|
1709
|
+
readonly path: string;
|
|
1710
|
+
readonly lastShardIndex: number;
|
|
1711
|
+
}
|
|
1712
|
+
type RunResult = {
|
|
1713
|
+
readonly status: 'done';
|
|
1714
|
+
readonly output: string;
|
|
1715
|
+
/** Parsed + validated data when outputFormat is 'json'. Undefined for 'text' or if parsing failed. */
|
|
1716
|
+
readonly data?: unknown;
|
|
1717
|
+
readonly tokensUsed: TokenUsage;
|
|
1718
|
+
readonly turns: number;
|
|
1719
|
+
} | {
|
|
1720
|
+
readonly status: 'paused';
|
|
1721
|
+
readonly snapshot: RunSnapshot;
|
|
1722
|
+
readonly reason: PauseReason;
|
|
1723
|
+
} | {
|
|
1724
|
+
readonly status: 'failed';
|
|
1725
|
+
readonly error: Error;
|
|
1726
|
+
readonly transcript: TranscriptLocation;
|
|
1727
|
+
};
|
|
1728
|
+
interface ResumeOptions {
|
|
1729
|
+
/** The runId of the paused run. Engine loads snapshot from storage. */
|
|
1730
|
+
readonly runId: string;
|
|
1731
|
+
/** Optional nodeId to disambiguate if multiple nodes have paused under this runId. */
|
|
1732
|
+
readonly nodeId?: string;
|
|
1733
|
+
readonly gateAnswer?: unknown;
|
|
1734
|
+
readonly outputFormat?: 'text' | 'json' | undefined;
|
|
1735
|
+
readonly outputSchema?: zod.ZodTypeAny | undefined;
|
|
1736
|
+
/**
|
|
1737
|
+
* Optional: pass snapshot directly instead of loading from storage.
|
|
1738
|
+
* Used internally and for advanced cases. Most clients should just pass runId.
|
|
1739
|
+
*/
|
|
1740
|
+
readonly snapshot?: RunSnapshot;
|
|
1741
|
+
/**
|
|
1742
|
+
* Per-resume skill override (Plan 017). Mirrors `RunOptions.skills` —
|
|
1743
|
+
* pass the same list you used on start() to keep tool availability
|
|
1744
|
+
* consistent. Overrides are not persisted in the snapshot.
|
|
1745
|
+
*/
|
|
1746
|
+
readonly skills?: ReadonlyArray<SkillOverride> | undefined;
|
|
1747
|
+
}
|
|
1748
|
+
|
|
1749
|
+
/**
|
|
1750
|
+
* Result of `gateBeforeTool` callback. `allow: true` runs the tool;
|
|
1751
|
+
* `allow: false` pauses the run with the given reason. The pending
|
|
1752
|
+
* tool call is captured in the snapshot.
|
|
1753
|
+
*/
|
|
1754
|
+
interface GateDecision {
|
|
1755
|
+
readonly allow: boolean;
|
|
1756
|
+
readonly reason?: string;
|
|
1757
|
+
}
|
|
1758
|
+
type GateBeforeTool = (toolName: string, input: unknown) => GateDecision | Promise<GateDecision>;
|
|
1759
|
+
/**
|
|
1760
|
+
* Progress payload fired by `onProgress` at turn boundaries. Maps
|
|
1761
|
+
* directly to `RunStateManager.RunProgress` on the engine side.
|
|
1762
|
+
*/
|
|
1763
|
+
interface LoopProgress {
|
|
1764
|
+
/** Current turn index (1-based). */
|
|
1765
|
+
readonly turns: number;
|
|
1766
|
+
/** Cumulative token usage across all turns so far. */
|
|
1767
|
+
readonly tokensUsed: TokenUsage$1;
|
|
1768
|
+
/** What the loop is doing right now. */
|
|
1769
|
+
readonly currentActivity: 'idle' | 'streaming' | 'tool_dispatch' | 'compacting';
|
|
1770
|
+
/** Most recent tool name (when `currentActivity === 'tool_dispatch'`). */
|
|
1771
|
+
readonly lastTool?: string;
|
|
1772
|
+
}
|
|
1773
|
+
interface ToolBatch {
|
|
1774
|
+
concurrent: boolean;
|
|
1775
|
+
calls: Array<{
|
|
1776
|
+
id: string;
|
|
1777
|
+
name: string;
|
|
1778
|
+
input: unknown;
|
|
1779
|
+
}>;
|
|
1780
|
+
}
|
|
1781
|
+
/**
|
|
1782
|
+
* Partition tool calls into batches: consecutive concurrency-safe tools
|
|
1783
|
+
* are grouped together for parallel execution; each unsafe tool becomes
|
|
1784
|
+
* its own serial batch. Mirrors La-Machina's toolOrchestration.ts pattern.
|
|
1785
|
+
*/
|
|
1786
|
+
declare function partitionToolCalls(calls: ReadonlyArray<{
|
|
1787
|
+
id: string;
|
|
1788
|
+
name: string;
|
|
1789
|
+
input: unknown;
|
|
1790
|
+
}>, registry?: ToolRegistry): ToolBatch[];
|
|
1791
|
+
|
|
1792
|
+
/**
|
|
1793
|
+
* EngineResponse — unified response shape for the public API.
|
|
1794
|
+
*
|
|
1795
|
+
* Every engine.run() and engine.resume() returns this single flat shape.
|
|
1796
|
+
* Internal code uses RunResult (the discriminated union); this module
|
|
1797
|
+
* converts it to the client-facing format.
|
|
1798
|
+
*
|
|
1799
|
+
* {
|
|
1800
|
+
* status: 'done' | 'paused' | 'failed',
|
|
1801
|
+
* data: any, // user-defined — text or JSON from outputSchema
|
|
1802
|
+
* meta: { ... }, // tokens, turns, timing, snapshot, transcript
|
|
1803
|
+
* errors: [], // structured error array
|
|
1804
|
+
* timestamp: number, // unix ms
|
|
1805
|
+
* }
|
|
1806
|
+
*/
|
|
1807
|
+
|
|
1808
|
+
interface ResponseError {
|
|
1809
|
+
readonly code: string;
|
|
1810
|
+
readonly message: string;
|
|
1811
|
+
readonly details?: unknown;
|
|
1812
|
+
}
|
|
1813
|
+
interface ResponseMeta {
|
|
1814
|
+
readonly nodeId: string;
|
|
1815
|
+
readonly turns?: number;
|
|
1816
|
+
readonly tokensUsed?: TokenUsage;
|
|
1817
|
+
readonly durationMs?: number;
|
|
1818
|
+
readonly output?: string;
|
|
1819
|
+
readonly snapshot?: RunSnapshot;
|
|
1820
|
+
readonly pendingToolCall?: RunSnapshot['pendingToolCall'];
|
|
1821
|
+
readonly pauseReason?: PauseReason;
|
|
1822
|
+
readonly transcript?: TranscriptLocation;
|
|
1823
|
+
/** Additional context-specific fields (e.g., activity, lastTool, cancelled). */
|
|
1824
|
+
readonly [key: string]: unknown;
|
|
1825
|
+
}
|
|
1826
|
+
interface EngineResponse {
|
|
1827
|
+
/** Unique run identifier — client's primary handle. Use this for resume. */
|
|
1828
|
+
readonly runId: string;
|
|
1829
|
+
readonly status: 'done' | 'paused' | 'failed' | 'queued' | 'running' | 'cancelled' | 'not_found';
|
|
1830
|
+
readonly data: unknown;
|
|
1831
|
+
readonly meta: ResponseMeta;
|
|
1832
|
+
readonly errors: readonly ResponseError[];
|
|
1833
|
+
readonly timestamp: number;
|
|
1834
|
+
}
|
|
1835
|
+
/**
|
|
1836
|
+
* Convert internal RunResult to the unified EngineResponse shape.
|
|
1837
|
+
*/
|
|
1838
|
+
declare function toResponse(result: RunResult, extra: {
|
|
1839
|
+
runId: string;
|
|
1840
|
+
nodeId: string;
|
|
1841
|
+
durationMs: number;
|
|
1842
|
+
logPath?: string;
|
|
1843
|
+
}): EngineResponse;
|
|
1844
|
+
|
|
1845
|
+
/**
|
|
1846
|
+
* RunState — durable per-run state stored in state.json.
|
|
1847
|
+
*
|
|
1848
|
+
* Lives alongside transcript shards and snapshot.json:
|
|
1849
|
+
* projects/{runId}/nodes/{nodeId}/state.json
|
|
1850
|
+
*
|
|
1851
|
+
* Enables:
|
|
1852
|
+
* - Async run tracking (getStatus, waitFor)
|
|
1853
|
+
* - Crash recovery (detect orphaned running runs via stale heartbeat)
|
|
1854
|
+
* - Webhook delivery history (retry tracking)
|
|
1855
|
+
* - Full response persistence (data + meta + errors) — getStatus returns
|
|
1856
|
+
* the same shape as sync engine.run()
|
|
1857
|
+
*
|
|
1858
|
+
* All pure JS — no `node:` imports, Workers-compatible.
|
|
1859
|
+
*/
|
|
1860
|
+
|
|
1861
|
+
type RunStatus = 'queued' | 'running' | 'paused' | 'done' | 'failed' | 'cancelled';
|
|
1862
|
+
interface RunProgress {
|
|
1863
|
+
readonly turns: number;
|
|
1864
|
+
readonly tokensUsed: TokenUsage;
|
|
1865
|
+
readonly currentActivity: 'idle' | 'streaming' | 'tool_dispatch' | 'compacting';
|
|
1866
|
+
readonly lastTool?: string;
|
|
1867
|
+
}
|
|
1868
|
+
type WebhookEvent = 'paused' | 'done' | 'failed';
|
|
1869
|
+
interface WebhookDelivery {
|
|
1870
|
+
readonly id: string;
|
|
1871
|
+
readonly event: WebhookEvent;
|
|
1872
|
+
readonly attempt: number;
|
|
1873
|
+
readonly scheduledAt: number;
|
|
1874
|
+
readonly deliveredAt?: number;
|
|
1875
|
+
readonly status: 'pending' | 'delivered' | 'failed';
|
|
1876
|
+
readonly httpCode?: number;
|
|
1877
|
+
readonly error?: string;
|
|
1878
|
+
}
|
|
1879
|
+
interface WebhookConfig {
|
|
1880
|
+
readonly url: string;
|
|
1881
|
+
readonly events: readonly WebhookEvent[];
|
|
1882
|
+
readonly secret?: string;
|
|
1883
|
+
readonly headers?: Readonly<Record<string, string>>;
|
|
1884
|
+
readonly deliveries: readonly WebhookDelivery[];
|
|
1885
|
+
}
|
|
1886
|
+
interface RunState {
|
|
1887
|
+
readonly version: 1;
|
|
1888
|
+
readonly runId: string;
|
|
1889
|
+
readonly nodeId: string;
|
|
1890
|
+
readonly status: RunStatus;
|
|
1891
|
+
readonly startedAt: number;
|
|
1892
|
+
readonly lastHeartbeat: number;
|
|
1893
|
+
readonly progress: RunProgress;
|
|
1894
|
+
readonly response: EngineResponse | null;
|
|
1895
|
+
readonly webhook?: WebhookConfig;
|
|
1896
|
+
}
|
|
1897
|
+
/**
|
|
1898
|
+
* Reads, writes, and updates state.json files via the storage adapter.
|
|
1899
|
+
* Concurrent writes are NOT synchronized — callers are responsible for
|
|
1900
|
+
* serializing updates (in practice: only one process writes per run).
|
|
1901
|
+
*/
|
|
1902
|
+
declare class RunStateManager {
|
|
1903
|
+
private readonly storage;
|
|
1904
|
+
constructor(storage: StorageAdapter);
|
|
1905
|
+
private path;
|
|
1906
|
+
write(state: RunState): Promise<void>;
|
|
1907
|
+
read(runId: string, nodeId: string): Promise<RunState | null>;
|
|
1908
|
+
/**
|
|
1909
|
+
* Merge patch into current state and write back. Returns the new state.
|
|
1910
|
+
* If no state exists, throws — use `write()` for initial creation.
|
|
1911
|
+
*/
|
|
1912
|
+
update(runId: string, nodeId: string, patch: Partial<RunState>): Promise<RunState>;
|
|
1913
|
+
/**
|
|
1914
|
+
* Update just the heartbeat + progress (cheap, called every turn).
|
|
1915
|
+
*/
|
|
1916
|
+
heartbeat(runId: string, nodeId: string, progress: Partial<RunProgress>): Promise<void>;
|
|
1917
|
+
/**
|
|
1918
|
+
* Mark terminal state with response. Used at end of run/resume.
|
|
1919
|
+
*/
|
|
1920
|
+
finalize(runId: string, nodeId: string, response: EngineResponse): Promise<RunState>;
|
|
1921
|
+
/**
|
|
1922
|
+
* List all state files under a runId (one per node). Returns empty array
|
|
1923
|
+
* if the runId directory doesn't exist.
|
|
1924
|
+
*/
|
|
1925
|
+
scanRun(runId: string): Promise<RunState[]>;
|
|
1926
|
+
/**
|
|
1927
|
+
* Scan all state files and return those with stale heartbeats.
|
|
1928
|
+
* Used by recoverOrphanedRuns().
|
|
1929
|
+
*/
|
|
1930
|
+
findOrphaned(staleThresholdMs: number): Promise<RunState[]>;
|
|
1931
|
+
/** Helper to build a fresh state for a new run. */
|
|
1932
|
+
static initial(runId: string, nodeId: string, webhook?: WebhookConfig): RunState;
|
|
1933
|
+
}
|
|
1934
|
+
|
|
1935
|
+
/**
|
|
1936
|
+
* BackgroundExecutor — abstracts how async runs are dispatched.
|
|
1937
|
+
*
|
|
1938
|
+
* Node.js: NodeBackgroundExecutor uses `void Promise` fire-and-forget.
|
|
1939
|
+
* Cloudflare Workers: user provides a DurableObjectExecutor (Worker-side
|
|
1940
|
+
* code that dispatches to a DO with a longer execution budget).
|
|
1941
|
+
*
|
|
1942
|
+
* All pure JS — no `node:` imports, Workers-compatible.
|
|
1943
|
+
*/
|
|
1944
|
+
/**
|
|
1945
|
+
* Contract for async run dispatch. Engine calls `schedule()` with a work
|
|
1946
|
+
* function; executor is responsible for running it eventually.
|
|
1947
|
+
*/
|
|
1948
|
+
interface BackgroundExecutor {
|
|
1949
|
+
/**
|
|
1950
|
+
* Schedule a work function to run in the background. Must return
|
|
1951
|
+
* immediately — the function will be invoked asynchronously.
|
|
1952
|
+
*
|
|
1953
|
+
* The returned AbortSignal fires if `cancel(runId)` is called.
|
|
1954
|
+
* Work functions should pass this to agentLoop for early termination.
|
|
1955
|
+
*/
|
|
1956
|
+
schedule(runId: string, work: (signal: AbortSignal) => Promise<void>): void;
|
|
1957
|
+
/**
|
|
1958
|
+
* Cancel a scheduled/running execution. Fires the AbortSignal so the
|
|
1959
|
+
* work function can exit gracefully.
|
|
1960
|
+
*/
|
|
1961
|
+
cancel(runId: string): Promise<void>;
|
|
1962
|
+
/** True if this executor is currently tracking a run. */
|
|
1963
|
+
isActive(runId: string): boolean;
|
|
1964
|
+
}
|
|
1965
|
+
/**
|
|
1966
|
+
* Default Node.js implementation — fire-and-forget via `void` Promise.
|
|
1967
|
+
* Works in Node, Bun, Deno. Does NOT work on Cloudflare Workers because
|
|
1968
|
+
* the Worker instance terminates when the request handler returns.
|
|
1969
|
+
*/
|
|
1970
|
+
declare class NodeBackgroundExecutor implements BackgroundExecutor {
|
|
1971
|
+
private readonly running;
|
|
1972
|
+
schedule(runId: string, work: (signal: AbortSignal) => Promise<void>): void;
|
|
1973
|
+
cancel(runId: string): Promise<void>;
|
|
1974
|
+
isActive(runId: string): boolean;
|
|
1975
|
+
/** For tests/observability — current count of running tasks. */
|
|
1976
|
+
size(): number;
|
|
1977
|
+
}
|
|
1978
|
+
|
|
1979
|
+
/** Config for webhook delivery when using async APIs. */
|
|
1980
|
+
interface WebhookOptions {
|
|
1981
|
+
readonly url: string;
|
|
1982
|
+
readonly secret?: string;
|
|
1983
|
+
readonly events?: readonly WebhookEvent[];
|
|
1984
|
+
readonly headers?: Record<string, string>;
|
|
1985
|
+
}
|
|
1986
|
+
/** Options for engine.start() — extends RunOptions with webhook. */
|
|
1987
|
+
interface StartOptions extends RunOptions {
|
|
1988
|
+
readonly webhook?: WebhookOptions;
|
|
1989
|
+
}
|
|
1990
|
+
/** Options for engine.resumeAsync() — extends ResumeOptions with webhook. */
|
|
1991
|
+
interface ResumeAsyncOptions extends ResumeOptions {
|
|
1992
|
+
readonly webhook?: WebhookOptions;
|
|
1993
|
+
}
|
|
1994
|
+
/** Options for engine.waitFor() — blocks until terminal state. */
|
|
1995
|
+
interface WaitForOptions {
|
|
1996
|
+
readonly nodeId?: string;
|
|
1997
|
+
readonly timeoutMs?: number;
|
|
1998
|
+
readonly pollIntervalMs?: number;
|
|
1999
|
+
}
|
|
2000
|
+
interface EngineInternals {
|
|
2001
|
+
/** Override the fetch implementation used by the API client. Tests only. */
|
|
2002
|
+
readonly fetch?: typeof globalThis.fetch;
|
|
2003
|
+
/**
|
|
2004
|
+
* Optional pre-tool gate. When set, every tool dispatch is checked
|
|
2005
|
+
* by this callback. Returning `{ allow: false }` pauses the run.
|
|
2006
|
+
*/
|
|
2007
|
+
readonly gateBeforeTool?: GateBeforeTool;
|
|
2008
|
+
/**
|
|
2009
|
+
* Background executor for async runs (engine.start, engine.resumeAsync).
|
|
2010
|
+
* Defaults to NodeBackgroundExecutor. On Cloudflare Workers, provide a
|
|
2011
|
+
* DurableObjectExecutor that dispatches to a DO.
|
|
2012
|
+
*/
|
|
2013
|
+
readonly backgroundExecutor?: BackgroundExecutor;
|
|
2014
|
+
/**
|
|
2015
|
+
* Override the storage factory. When set, the engine calls this
|
|
2016
|
+
* instead of `createEngineStorage(config.storage)` for every run.
|
|
2017
|
+
*
|
|
2018
|
+
* Use this on Cloudflare Workers to build an `EngineStorage` pair
|
|
2019
|
+
* backed by `R2BindingStorageAdapter` (which uses the native R2
|
|
2020
|
+
* binding, not the S3 SDK). Example:
|
|
2021
|
+
*
|
|
2022
|
+
* new Engine(config, {
|
|
2023
|
+
* buildStorage: async () => ({
|
|
2024
|
+
* global: new R2BindingStorageAdapter(env.BUCKET, 'root/.claude'),
|
|
2025
|
+
* workspace: new R2BindingStorageAdapter(env.BUCKET, `root/workspaces/${ws}/.claude`),
|
|
2026
|
+
* }),
|
|
2027
|
+
* })
|
|
2028
|
+
*/
|
|
2029
|
+
readonly buildStorage?: () => Promise<EngineStorage>;
|
|
2030
|
+
/**
|
|
2031
|
+
* Handler invoked when an MCP server with `allowSampling: true`
|
|
2032
|
+
* requests an LLM completion via `sampling/createMessage`. Defaults
|
|
2033
|
+
* to a handler that routes through the engine's own ModelAdapter
|
|
2034
|
+
* (same API key, same billing). Override to route to a cheaper
|
|
2035
|
+
* model, enforce budget limits, or refuse specific servers.
|
|
2036
|
+
*/
|
|
2037
|
+
readonly samplingHandler?: SamplingHandler;
|
|
2038
|
+
}
|
|
2039
|
+
declare class Engine {
|
|
2040
|
+
readonly config: ResolvedConfig;
|
|
2041
|
+
private readonly internals;
|
|
2042
|
+
private readonly mcpManager;
|
|
2043
|
+
private readonly permissionPolicy;
|
|
2044
|
+
private readonly backgroundExecutor;
|
|
2045
|
+
private readonly webhookDispatcher;
|
|
2046
|
+
constructor(config: ResolvedConfig, internals?: EngineInternals);
|
|
2047
|
+
run(options: RunOptions): Promise<EngineResponse>;
|
|
2048
|
+
resume(options: ResumeOptions): Promise<EngineResponse>;
|
|
2049
|
+
/**
|
|
2050
|
+
* Load a paused snapshot from storage using runId (and optional nodeId).
|
|
2051
|
+
* If nodeId is not provided, finds the most recently paused node.
|
|
2052
|
+
*/
|
|
2053
|
+
private loadSnapshot;
|
|
2054
|
+
/**
|
|
2055
|
+
* Start a run asynchronously. Writes initial state, schedules the run
|
|
2056
|
+
* in the background, and returns immediately. Clients should track the
|
|
2057
|
+
* returned runId and poll via `getStatus(runId)` or wait for a webhook.
|
|
2058
|
+
*
|
|
2059
|
+
* Workers compatibility: provide a DurableObjectExecutor via
|
|
2060
|
+
* EngineInternals.backgroundExecutor — the default NodeBackgroundExecutor
|
|
2061
|
+
* uses fire-and-forget Promises which won't survive Worker request exit.
|
|
2062
|
+
*/
|
|
2063
|
+
start(options: StartOptions): Promise<{
|
|
2064
|
+
runId: string;
|
|
2065
|
+
nodeId: string;
|
|
2066
|
+
status: RunStatus;
|
|
2067
|
+
}>;
|
|
2068
|
+
/**
|
|
2069
|
+
* Resume a paused run asynchronously. Equivalent to engine.resume() but
|
|
2070
|
+
* dispatched via the background executor. Returns immediately.
|
|
2071
|
+
*/
|
|
2072
|
+
resumeAsync(options: ResumeAsyncOptions): Promise<{
|
|
2073
|
+
runId: string;
|
|
2074
|
+
nodeId: string;
|
|
2075
|
+
status: RunStatus;
|
|
2076
|
+
}>;
|
|
2077
|
+
/**
|
|
2078
|
+
* Read the current status of a run. Returns the full EngineResponse once
|
|
2079
|
+
* terminal; returns a provisional response with status='running'|'queued'
|
|
2080
|
+
* otherwise. If nodeId is omitted, picks the most recently updated node.
|
|
2081
|
+
*/
|
|
2082
|
+
getStatus(runId: string, nodeId?: string): Promise<EngineResponse>;
|
|
2083
|
+
/**
|
|
2084
|
+
* Poll until the run reaches a terminal state (done | failed | paused |
|
|
2085
|
+
* cancelled) or the timeout expires. Returns the final EngineResponse.
|
|
2086
|
+
*/
|
|
2087
|
+
waitFor(runId: string, opts?: WaitForOptions): Promise<EngineResponse>;
|
|
2088
|
+
/**
|
|
2089
|
+
* Cancel an async run. Aborts the background executor and marks the
|
|
2090
|
+
* state as cancelled. Idempotent — safe to call on already-terminal runs.
|
|
2091
|
+
*/
|
|
2092
|
+
cancelRun(runId: string, nodeId?: string): Promise<void>;
|
|
2093
|
+
/**
|
|
2094
|
+
* Retry a specific webhook delivery. Useful when the client acknowledges
|
|
2095
|
+
* they missed an event (e.g., after downtime).
|
|
2096
|
+
*/
|
|
2097
|
+
retryWebhook(runId: string, deliveryId: string, nodeId?: string): Promise<void>;
|
|
2098
|
+
/**
|
|
2099
|
+
* Scan all runs for stale heartbeats and mark them as failed. Clients
|
|
2100
|
+
* should call this on startup to recover from crashes.
|
|
2101
|
+
*
|
|
2102
|
+
* @param opts.staleThresholdMs - Heartbeat age after which a running run
|
|
2103
|
+
* is considered orphaned. Default: 5 minutes.
|
|
2104
|
+
*/
|
|
2105
|
+
recoverOrphanedRuns(opts?: {
|
|
2106
|
+
staleThresholdMs?: number;
|
|
2107
|
+
}): Promise<readonly RunState[]>;
|
|
2108
|
+
private maybeFireWebhook;
|
|
2109
|
+
private dispatchWebhookWithRetries;
|
|
2110
|
+
/**
|
|
2111
|
+
* Shut down engine-owned background resources — currently just the
|
|
2112
|
+
* `McpManager`'s connection pool. Long-running hosts (servers,
|
|
2113
|
+
* daemons) SHOULD call this before dropping their engine reference,
|
|
2114
|
+
* otherwise MCP subprocesses leak until the parent process exits.
|
|
2115
|
+
*
|
|
2116
|
+
* Idempotent. Safe to call multiple times.
|
|
2117
|
+
*/
|
|
2118
|
+
shutdown(): Promise<void>;
|
|
2119
|
+
/**
|
|
2120
|
+
* Structured orchestration — decomposes a task into a plan, executes
|
|
2121
|
+
* each step with specialized agents (planner, researcher, implementer,
|
|
2122
|
+
* verifier, reviewer, finalizer), enforces retry policies, and reverts
|
|
2123
|
+
* failed implementations.
|
|
2124
|
+
*
|
|
2125
|
+
* Unlike `run()` (single agent, best effort), `orchestrate()` guarantees
|
|
2126
|
+
* a result: done (all steps passed), partial (some failed + reverted),
|
|
2127
|
+
* or failed (planning failed entirely).
|
|
2128
|
+
*
|
|
2129
|
+
* Requires `config.orchestrator.enabled = true` or the method throws.
|
|
2130
|
+
* The orchestrator calls `engine.run()` internally for each phase —
|
|
2131
|
+
* it's a state machine layer above the existing agent loop.
|
|
2132
|
+
*
|
|
2133
|
+
* Pure JS — works on both Node.js and Cloudflare Workers.
|
|
2134
|
+
*/
|
|
2135
|
+
orchestrate(options: OrchestrateOptions): Promise<OrchestratorResult>;
|
|
2136
|
+
/**
|
|
2137
|
+
* Emit a terminal log line per run. `done` is info; `paused` is info;
|
|
2138
|
+
* `failed` is error (with the error message in `meta.error`).
|
|
2139
|
+
*/
|
|
2140
|
+
private logRunEnd;
|
|
2141
|
+
/**
|
|
2142
|
+
* Collect the names of all tools that will be registered — used to
|
|
2143
|
+
* populate the system prompt's tool-specific instructions BEFORE
|
|
2144
|
+
* building the registry itself (the prompt goes into the registry's
|
|
2145
|
+
* Agent tool as the child system prompt, so the prompt must exist
|
|
2146
|
+
* first).
|
|
2147
|
+
*/
|
|
2148
|
+
private collectToolNames;
|
|
2149
|
+
/**
|
|
2150
|
+
* Resolve the subagent catalogue the Agent tool will dispatch against.
|
|
2151
|
+
*
|
|
2152
|
+
* Merge semantics:
|
|
2153
|
+
* 1. Start with `config.agents.builtins` — each name becomes a
|
|
2154
|
+
* placeholder definition that inherits the parent's system prompt.
|
|
2155
|
+
* (The only builtin v0.1 knows about is `'general-purpose'`.)
|
|
2156
|
+
* 2. If `config.agents.customPath` is set, load any `.md` files from
|
|
2157
|
+
* that directory (relative to the workspace adapter) and append as
|
|
2158
|
+
* full `AgentDefinition` records with their own system prompts.
|
|
2159
|
+
* 3. Custom-loaded agents with the same name as a builtin REPLACE the
|
|
2160
|
+
* builtin — last-wins dedup happens inside `createAgentTool`.
|
|
2161
|
+
*
|
|
2162
|
+
* Throws `ConfigError` if the merged list is empty.
|
|
2163
|
+
*/
|
|
2164
|
+
private resolveAgents;
|
|
2165
|
+
/**
|
|
2166
|
+
* Pick the effective gate callback:
|
|
2167
|
+
* 1. `EngineInternals.gateBeforeTool` (test seam) — highest priority
|
|
2168
|
+
* 2. `config.hooks.gateBeforeTool` (user config) — the public path
|
|
2169
|
+
* 3. `undefined` (no gate) — default
|
|
2170
|
+
*/
|
|
2171
|
+
private resolveGate;
|
|
2172
|
+
/**
|
|
2173
|
+
* Start the run-level timeout. Returns an AbortController signal if
|
|
2174
|
+
* `runTimeoutMs > 0`, otherwise returns an empty handle. The caller
|
|
2175
|
+
* MUST invoke `clear()` in a finally block to cancel the timer.
|
|
2176
|
+
*/
|
|
2177
|
+
private startRunTimeout;
|
|
2178
|
+
private firePostRunHook;
|
|
2179
|
+
private finalizeResult;
|
|
2180
|
+
/**
|
|
2181
|
+
* Pick the effective `SkillSource` for a run.
|
|
2182
|
+
*
|
|
2183
|
+
* - Per-run override (`options.skills`) → `InlineSkillSource`
|
|
2184
|
+
* - else `config.skills.autoload === true` → `StorageSkillSource`
|
|
2185
|
+
* - else → undefined (SkillPage tool not registered)
|
|
2186
|
+
*/
|
|
2187
|
+
private resolveSkillSource;
|
|
2188
|
+
/**
|
|
2189
|
+
* Build a throttled heartbeat callback for agentLoop's `onProgress` hook.
|
|
2190
|
+
*
|
|
2191
|
+
* Only writes to `state.json` if one already exists for this run
|
|
2192
|
+
* (i.e., the run was started via `engine.start()` or `resumeAsync()`).
|
|
2193
|
+
* Sync `engine.run()` callers without a pre-existing state file get a
|
|
2194
|
+
* no-op heartbeat.
|
|
2195
|
+
*
|
|
2196
|
+
* Throttled to at most one write per 500ms AND only when activity
|
|
2197
|
+
* changes, so R2 writes stay cheap even for long runs.
|
|
2198
|
+
*/
|
|
2199
|
+
private buildHeartbeat;
|
|
2200
|
+
/**
|
|
2201
|
+
* Build the engine's storage pair. Uses the `EngineInternals.buildStorage`
|
|
2202
|
+
* override when provided (for Workers / custom adapters), otherwise
|
|
2203
|
+
* defers to the standard `createEngineStorage()` factory.
|
|
2204
|
+
*/
|
|
2205
|
+
private buildStorage;
|
|
2206
|
+
private buildClient;
|
|
2207
|
+
}
|
|
2208
|
+
|
|
2209
|
+
/**
|
|
2210
|
+
* providerSelector — translate a `ResolvedModelConfig` into a typed
|
|
2211
|
+
* `ProviderPlan` describing what API client to construct and how.
|
|
2212
|
+
*
|
|
2213
|
+
* The plan is a thin, validated intermediate between user config and the
|
|
2214
|
+
* actual SDK construction so that:
|
|
2215
|
+
* 1. Invalid combinations (proxy provider without baseURL, anthropic
|
|
2216
|
+
* without apiKey) are rejected at this layer, not at runtime inside
|
|
2217
|
+
* the SDK's message send
|
|
2218
|
+
* 2. Deferred backends (Bedrock, Vertex, Foundry) can be flagged here
|
|
2219
|
+
* with a clear `NotImplementedError` pointing at the v0.2 milestone
|
|
2220
|
+
* 3. Tests can exercise selection logic independently of actual client
|
|
2221
|
+
* construction
|
|
2222
|
+
*/
|
|
2223
|
+
|
|
2224
|
+
interface AnthropicProviderPlan {
|
|
2225
|
+
readonly kind: 'anthropic';
|
|
2226
|
+
readonly modelId: string;
|
|
2227
|
+
readonly apiKey: string;
|
|
2228
|
+
readonly baseURL: string | undefined;
|
|
2229
|
+
readonly maxTokens: number;
|
|
2230
|
+
readonly temperature: number;
|
|
2231
|
+
readonly maxRetries: number;
|
|
2232
|
+
}
|
|
2233
|
+
interface ProxyProviderPlan {
|
|
2234
|
+
readonly kind: 'proxy';
|
|
2235
|
+
readonly modelId: string;
|
|
2236
|
+
readonly apiKey: string;
|
|
2237
|
+
readonly baseURL: string;
|
|
2238
|
+
readonly maxTokens: number;
|
|
2239
|
+
readonly temperature: number;
|
|
2240
|
+
readonly maxRetries: number;
|
|
2241
|
+
}
|
|
2242
|
+
type ProviderPlan = AnthropicProviderPlan | ProxyProviderPlan;
|
|
2243
|
+
|
|
2244
|
+
/**
|
|
2245
|
+
* AnthropicClient — thin wrapper around `@anthropic-ai/sdk`.
|
|
2246
|
+
*
|
|
2247
|
+
* Responsibilities:
|
|
2248
|
+
* 1. Construct an `Anthropic` instance from a `ProviderPlan` (anthropic
|
|
2249
|
+
* first-party or proxy). Passes a custom `fetch` when supplied so
|
|
2250
|
+
* tests can drive the client with a scripted fetch without any real
|
|
2251
|
+
* network I/O.
|
|
2252
|
+
* 2. Expose `streamMessage(request)` as an async generator of
|
|
2253
|
+
* `NormalizedEvent`, with Anthropic SDK stream events passed through
|
|
2254
|
+
* `normalizeStream` on the way out.
|
|
2255
|
+
* 3. Translate SDK-level errors into the engine's typed error taxonomy
|
|
2256
|
+
* (`AuthError`, `RateLimitError`, `ApiError`, etc.) so callers can
|
|
2257
|
+
* discriminate without instanceof-checking third-party classes.
|
|
2258
|
+
*
|
|
2259
|
+
* The client is stateless — each `streamMessage` call is independent.
|
|
2260
|
+
* Callers should reuse a single `AnthropicClient` per run; the
|
|
2261
|
+
* underlying SDK already pools HTTP connections internally.
|
|
2262
|
+
*/
|
|
2263
|
+
|
|
2264
|
+
interface AnthropicClientOptions {
|
|
2265
|
+
readonly plan: ProviderPlan;
|
|
2266
|
+
/** Custom fetch for tests. Defaults to globalThis.fetch. */
|
|
2267
|
+
readonly fetch?: typeof globalThis.fetch;
|
|
2268
|
+
}
|
|
2269
|
+
|
|
2270
|
+
/**
|
|
2271
|
+
* AnthropicAdapter — wraps the existing @anthropic-ai/sdk client as a
|
|
2272
|
+
* ModelAdapter. Default provider.
|
|
2273
|
+
*/
|
|
2274
|
+
|
|
2275
|
+
declare class AnthropicAdapter implements ModelAdapter {
|
|
2276
|
+
private readonly client;
|
|
2277
|
+
constructor(options: AnthropicClientOptions);
|
|
2278
|
+
streamMessage(request: StreamRequest): AsyncGenerator<NormalizedEvent, void, void>;
|
|
2279
|
+
}
|
|
2280
|
+
|
|
2281
|
+
/**
|
|
2282
|
+
* AISdkAdapter — ModelAdapter using the Vercel AI SDK for 75+ providers.
|
|
2283
|
+
*
|
|
2284
|
+
* Properly converts Anthropic-format messages to AI SDK v4 ModelMessage
|
|
2285
|
+
* format, including tool_use/tool_result round-trips. The key is that
|
|
2286
|
+
* AI SDK v4 requires tool result output as { type: 'text', value: '...' }
|
|
2287
|
+
* (not a plain string or generic object).
|
|
2288
|
+
*/
|
|
2289
|
+
|
|
2290
|
+
interface AISdkAdapterOptions {
|
|
2291
|
+
readonly provider: string;
|
|
2292
|
+
readonly modelId: string;
|
|
2293
|
+
readonly apiKey: string;
|
|
2294
|
+
readonly baseURL?: string | undefined;
|
|
2295
|
+
readonly maxRetries?: number | undefined;
|
|
2296
|
+
}
|
|
2297
|
+
declare class AISdkAdapter implements ModelAdapter {
|
|
2298
|
+
private readonly options;
|
|
2299
|
+
private model;
|
|
2300
|
+
constructor(options: AISdkAdapterOptions);
|
|
2301
|
+
streamMessage(request: StreamRequest): AsyncGenerator<NormalizedEvent, void, void>;
|
|
2302
|
+
private getModel;
|
|
2303
|
+
}
|
|
2304
|
+
|
|
2305
|
+
/**
|
|
2306
|
+
* Model adapter factory — routes config to the right adapter.
|
|
2307
|
+
*
|
|
2308
|
+
* Same pattern as storage/factory.ts:
|
|
2309
|
+
* createEngineStorage(config.storage) → StorageAdapter
|
|
2310
|
+
* createModelAdapter(config.model) → ModelAdapter
|
|
2311
|
+
*/
|
|
2312
|
+
|
|
2313
|
+
interface CreateModelAdapterOptions {
|
|
2314
|
+
/** Custom fetch for the Anthropic adapter (OpenRouter auth override). */
|
|
2315
|
+
readonly fetch?: typeof globalThis.fetch | undefined;
|
|
2316
|
+
}
|
|
2317
|
+
/**
|
|
2318
|
+
* Create a ModelAdapter from the resolved model config.
|
|
2319
|
+
*
|
|
2320
|
+
* Routing:
|
|
2321
|
+
* - 'anthropic' (no sdk override) → AnthropicAdapter (@anthropic-ai/sdk)
|
|
2322
|
+
* - 'proxy' → AnthropicAdapter with baseURL
|
|
2323
|
+
* - 'openai', 'google', 'openai-compatible' → AISdkAdapter
|
|
2324
|
+
* - 'anthropic' + sdk: 'ai-sdk' → AISdkAdapter with @ai-sdk/anthropic
|
|
2325
|
+
*/
|
|
2326
|
+
declare function createModelAdapter(config: ResolvedModelConfig, options?: CreateModelAdapterOptions): ModelAdapter;
|
|
2327
|
+
|
|
2328
|
+
/**
|
|
2329
|
+
* WebhookDispatcher — delivers status changes to client URLs.
|
|
2330
|
+
*
|
|
2331
|
+
* Features:
|
|
2332
|
+
* - HMAC-SHA256 signing via Web Crypto API (works everywhere)
|
|
2333
|
+
* - Exponential backoff retries: 10s → 60s → 5min → 30min → give up
|
|
2334
|
+
* - HTTP 4xx → no retry (client bug); 5xx/network → retry
|
|
2335
|
+
* - HTTP 410 Gone → give up immediately (resource deliberately removed)
|
|
2336
|
+
* - Delivery tracking: each attempt recorded in state.json
|
|
2337
|
+
*
|
|
2338
|
+
* All pure JS — no `node:` imports, Workers-compatible.
|
|
2339
|
+
*/
|
|
2340
|
+
|
|
2341
|
+
/** Milliseconds to wait before attempt N (1-indexed). */
|
|
2342
|
+
declare const RETRY_DELAYS_MS: readonly [0, 10000, 60000, number, number];
|
|
2343
|
+
declare const MAX_ATTEMPTS: 5;
|
|
2344
|
+
/**
|
|
2345
|
+
* Sign a webhook payload with HMAC-SHA256 using Web Crypto API.
|
|
2346
|
+
* Returns the full header value: `sha256=<hex>`.
|
|
2347
|
+
*/
|
|
2348
|
+
declare function signPayload(secret: string, timestamp: number, body: string): Promise<string>;
|
|
2349
|
+
interface DeliverOptions {
|
|
2350
|
+
readonly url: string;
|
|
2351
|
+
readonly event: WebhookEvent;
|
|
2352
|
+
readonly payload: EngineResponse;
|
|
2353
|
+
readonly secret?: string;
|
|
2354
|
+
readonly headers?: Readonly<Record<string, string>>;
|
|
2355
|
+
readonly attempt?: number;
|
|
2356
|
+
readonly deliveryId?: string;
|
|
2357
|
+
readonly timeoutMs?: number;
|
|
2358
|
+
readonly fetch?: typeof globalThis.fetch;
|
|
2359
|
+
}
|
|
2360
|
+
interface DeliverResult {
|
|
2361
|
+
readonly delivery: WebhookDelivery;
|
|
2362
|
+
readonly shouldRetry: boolean;
|
|
2363
|
+
readonly nextRetryDelayMs?: number;
|
|
2364
|
+
}
|
|
2365
|
+
declare class WebhookDispatcher {
|
|
2366
|
+
private readonly fetchImpl;
|
|
2367
|
+
constructor(fetchImpl?: typeof globalThis.fetch);
|
|
2368
|
+
/**
|
|
2369
|
+
* Attempt one delivery. Returns a delivery record and whether to retry.
|
|
2370
|
+
*/
|
|
2371
|
+
deliver(options: DeliverOptions): Promise<DeliverResult>;
|
|
2372
|
+
}
|
|
2373
|
+
|
|
2374
|
+
/**
|
|
2375
|
+
* Error taxonomy for la-machina-engine.
|
|
2376
|
+
*
|
|
2377
|
+
* Every thrown error must be a subclass of `EngineError`, so callers can
|
|
2378
|
+
* `instanceof` discriminate at the library boundary. Phase 1 defines only
|
|
2379
|
+
* the errors needed for config resolution and unimplemented methods.
|
|
2380
|
+
* Later phases extend this hierarchy (see plan 002 §4 Phase 4).
|
|
2381
|
+
*/
|
|
2382
|
+
declare class EngineError extends Error {
|
|
2383
|
+
readonly code: string;
|
|
2384
|
+
constructor(code: string, message: string);
|
|
2385
|
+
}
|
|
2386
|
+
/**
|
|
2387
|
+
* Thrown when config is invalid or incomplete. Raised by `initEngine()` when
|
|
2388
|
+
* `apiKey` cannot be resolved, or when schema validation fails.
|
|
2389
|
+
*/
|
|
2390
|
+
declare class ConfigError extends EngineError {
|
|
2391
|
+
constructor(message: string);
|
|
2392
|
+
}
|
|
2393
|
+
/**
|
|
2394
|
+
* Thrown when a method is called in a phase where its implementation has
|
|
2395
|
+
* not yet landed. Used by `engine.run()` and `engine.resume()` during
|
|
2396
|
+
* Phases 1–6 before the loop is wired, and by provider selectors for
|
|
2397
|
+
* backends deferred to v0.2 (Bedrock, Vertex, Foundry).
|
|
2398
|
+
*/
|
|
2399
|
+
declare class NotImplementedError extends EngineError {
|
|
2400
|
+
constructor(feature: string, target: string);
|
|
2401
|
+
}
|
|
2402
|
+
/**
|
|
2403
|
+
* Thrown when the provider rejects a request with HTTP 401/403 (missing
|
|
2404
|
+
* or invalid API key). Callers should not retry.
|
|
2405
|
+
*/
|
|
2406
|
+
declare class AuthError extends EngineError {
|
|
2407
|
+
constructor(message: string);
|
|
2408
|
+
}
|
|
2409
|
+
/**
|
|
2410
|
+
* Thrown when the provider returns HTTP 429 (rate limited). Callers
|
|
2411
|
+
* should back off and retry. `retryAfterMs` reflects the provider's
|
|
2412
|
+
* hint when available, otherwise null.
|
|
2413
|
+
*/
|
|
2414
|
+
declare class RateLimitError extends EngineError {
|
|
2415
|
+
readonly retryAfterMs: number | null;
|
|
2416
|
+
constructor(message: string, retryAfterMs?: number | null);
|
|
2417
|
+
}
|
|
2418
|
+
/**
|
|
2419
|
+
* Thrown when a streaming response can't be parsed into a normalized
|
|
2420
|
+
* event — typically malformed SSE or a JSON parse failure in a delta.
|
|
2421
|
+
* This is a hard error; the stream is not recoverable.
|
|
2422
|
+
*/
|
|
2423
|
+
declare class StreamParseError extends EngineError {
|
|
2424
|
+
constructor(message: string);
|
|
2425
|
+
}
|
|
2426
|
+
/**
|
|
2427
|
+
* Thrown when a streaming response ends without a terminal event
|
|
2428
|
+
* (`message_stop`). The caller cannot know whether the assistant
|
|
2429
|
+
* actually finished — almost always safer to fail the run and retry.
|
|
2430
|
+
*/
|
|
2431
|
+
declare class StreamIncompleteError extends EngineError {
|
|
2432
|
+
constructor(message: string);
|
|
2433
|
+
}
|
|
2434
|
+
/**
|
|
2435
|
+
* Generic provider-side API error — non-auth, non-rate-limit failures
|
|
2436
|
+
* like 500s, network drops, and unrecognized shapes. `status` is the
|
|
2437
|
+
* HTTP status code when available, otherwise null.
|
|
2438
|
+
*/
|
|
2439
|
+
declare class ApiError extends EngineError {
|
|
2440
|
+
readonly status: number | null;
|
|
2441
|
+
constructor(message: string, status?: number | null);
|
|
2442
|
+
}
|
|
2443
|
+
/**
|
|
2444
|
+
* Thrown when a run exceeds `execution.runTimeoutMs`. The agent loop is
|
|
2445
|
+
* aborted at the next checkpoint (top of the turn loop or after a tool
|
|
2446
|
+
* dispatch). The transcript so far is preserved.
|
|
2447
|
+
*/
|
|
2448
|
+
declare class RunTimeoutError extends EngineError {
|
|
2449
|
+
readonly timeoutMs: number;
|
|
2450
|
+
constructor(timeoutMs: number);
|
|
2451
|
+
}
|
|
2452
|
+
|
|
2453
|
+
/**
|
|
2454
|
+
* Structured JSON output — schema injection, parsing, validation.
|
|
2455
|
+
*
|
|
2456
|
+
* When `outputFormat: 'json'` is set on RunOptions:
|
|
2457
|
+
* 1. buildSchemaPrompt() injects output instructions into the system prompt
|
|
2458
|
+
* 2. tryParseJSON() extracts JSON from the model's response (handles fences, preamble)
|
|
2459
|
+
* 3. validateOutput() checks against the Zod schema
|
|
2460
|
+
*
|
|
2461
|
+
* All pure JS — no `node:` imports, Workers-compatible.
|
|
2462
|
+
*/
|
|
2463
|
+
|
|
2464
|
+
/**
|
|
2465
|
+
* Build the output format section to append to the system prompt.
|
|
2466
|
+
* Called when outputFormat is 'json'.
|
|
2467
|
+
*/
|
|
2468
|
+
declare function buildSchemaPrompt(schema?: ZodTypeAny): string;
|
|
2469
|
+
interface ParseResult {
|
|
2470
|
+
readonly ok: boolean;
|
|
2471
|
+
readonly value?: unknown;
|
|
2472
|
+
readonly error?: string;
|
|
2473
|
+
}
|
|
2474
|
+
/**
|
|
2475
|
+
* Extract JSON from model output. Tries multiple strategies:
|
|
2476
|
+
* 1. Raw parse (entire text is JSON)
|
|
2477
|
+
* 2. Strip markdown fences (```json ... ```)
|
|
2478
|
+
* 3. Find first { ... } or [ ... ] block
|
|
2479
|
+
*/
|
|
2480
|
+
declare function tryParseJSON(text: string): ParseResult;
|
|
2481
|
+
/**
|
|
2482
|
+
* Validate parsed JSON against a Zod schema.
|
|
2483
|
+
* Returns the validated data or an error message.
|
|
2484
|
+
*/
|
|
2485
|
+
declare function validateOutput(value: unknown, schema: ZodTypeAny): {
|
|
2486
|
+
ok: true;
|
|
2487
|
+
data: unknown;
|
|
2488
|
+
} | {
|
|
2489
|
+
ok: false;
|
|
2490
|
+
error: string;
|
|
2491
|
+
};
|
|
2492
|
+
|
|
2493
|
+
/**
|
|
2494
|
+
* StreamingToolExecutor — stateful tool queue with ordered result yielding,
|
|
2495
|
+
* concurrency control, and sibling error cascading.
|
|
2496
|
+
*
|
|
2497
|
+
* Ported 1:1 from La-Machina's StreamingToolExecutor.ts. Key behaviors:
|
|
2498
|
+
*
|
|
2499
|
+
* - Tools are enqueued via `addTool()` and executed as they become eligible
|
|
2500
|
+
* - `canExecute()` enforces: concurrent-safe tools run in parallel; unsafe
|
|
2501
|
+
* tools run alone (exclusive access)
|
|
2502
|
+
* - Results are yielded in **original enqueue order** regardless of
|
|
2503
|
+
* completion order (preserves tool_result ordering for the API)
|
|
2504
|
+
* - On **Bash error**, `siblingAbortController.abort()` cancels all queued
|
|
2505
|
+
* siblings with synthetic error results. Only Bash cascades — Read/Grep/
|
|
2506
|
+
* WebFetch errors are independent.
|
|
2507
|
+
* - All pure JS — no `node:` imports, Workers-compatible.
|
|
2508
|
+
*/
|
|
2509
|
+
|
|
2510
|
+
declare class StreamingToolExecutor {
|
|
2511
|
+
private readonly tools;
|
|
2512
|
+
private readonly executor;
|
|
2513
|
+
private readonly siblingAbort;
|
|
2514
|
+
private bashErrorDesc;
|
|
2515
|
+
constructor(executor: ToolExecutor);
|
|
2516
|
+
/**
|
|
2517
|
+
* Enqueue a tool call. Eligible tools start executing immediately.
|
|
2518
|
+
*/
|
|
2519
|
+
addTool(id: string, name: string, input: unknown, isConcurrencySafe: boolean): void;
|
|
2520
|
+
/**
|
|
2521
|
+
* Yield results in original enqueue order. Waits for each tool to
|
|
2522
|
+
* complete before yielding — guarantees the API sees tool_result
|
|
2523
|
+
* blocks in the same order as the tool_use blocks.
|
|
2524
|
+
*/
|
|
2525
|
+
results(): AsyncGenerator<{
|
|
2526
|
+
id: string;
|
|
2527
|
+
result: ToolResult;
|
|
2528
|
+
}>;
|
|
2529
|
+
/**
|
|
2530
|
+
* Can this tool start now? Mirrors La-Machina's canExecuteTool():
|
|
2531
|
+
* - If nothing is executing → yes
|
|
2532
|
+
* - If only concurrent-safe tools are executing AND this tool is safe → yes
|
|
2533
|
+
* - Otherwise → no (wait for exclusive access)
|
|
2534
|
+
*/
|
|
2535
|
+
private canExecute;
|
|
2536
|
+
/**
|
|
2537
|
+
* Walk the queue and start any eligible tools. Called after enqueue
|
|
2538
|
+
* and after each tool completes (to unblock the next batch).
|
|
2539
|
+
*/
|
|
2540
|
+
private processQueue;
|
|
2541
|
+
private startTool;
|
|
2542
|
+
private executeTool;
|
|
2543
|
+
}
|
|
2544
|
+
|
|
2545
|
+
/**
|
|
2546
|
+
* SendMessage tool — inter-agent communication within a single run.
|
|
2547
|
+
*
|
|
2548
|
+
* Routes messages to agents tracked in the SubagentRegistry:
|
|
2549
|
+
*
|
|
2550
|
+
* - **Running agent**: Message is queued via `registry.queueMessage()`.
|
|
2551
|
+
* The child's agentLoop drains queued messages at the start of
|
|
2552
|
+
* each turn (injected as a user message).
|
|
2553
|
+
*
|
|
2554
|
+
* - **Stopped agent**: Returns an error — auto-resume is deferred to v0.5.
|
|
2555
|
+
*
|
|
2556
|
+
* - **Completed agent**: Returns an error — can't send to finished agents.
|
|
2557
|
+
*
|
|
2558
|
+
* Ported from La-Machina's SendMessageTool.ts (918 lines). The engine
|
|
2559
|
+
* version omits mailbox/UDS/bridge routing (CLI-only) and structured
|
|
2560
|
+
* messages (shutdown_request, plan_approval) — headless mode doesn't
|
|
2561
|
+
* need them.
|
|
2562
|
+
*/
|
|
2563
|
+
|
|
2564
|
+
declare const inputSchema$1: z.ZodObject<{
|
|
2565
|
+
to: z.ZodString;
|
|
2566
|
+
message: z.ZodString;
|
|
2567
|
+
}, "strip", z.ZodTypeAny, {
|
|
2568
|
+
message: string;
|
|
2569
|
+
to: string;
|
|
2570
|
+
}, {
|
|
2571
|
+
message: string;
|
|
2572
|
+
to: string;
|
|
2573
|
+
}>;
|
|
2574
|
+
interface SendMessageToolOptions {
|
|
2575
|
+
readonly registry: SubagentRegistry;
|
|
2576
|
+
}
|
|
2577
|
+
declare function createSendMessageTool(options: SendMessageToolOptions): Tool$1<typeof inputSchema$1>;
|
|
2578
|
+
|
|
2579
|
+
/**
|
|
2580
|
+
* Fork subagent — child inherits parent's full message context.
|
|
2581
|
+
*
|
|
2582
|
+
* Ported 1:1 from La-Machina's forkSubagent.ts. When `subagent_type`
|
|
2583
|
+
* is omitted, the Agent tool forks a child that sees the parent's
|
|
2584
|
+
* entire conversation history + a directive. This enables "continue
|
|
2585
|
+
* this but focus on X" delegation without starting from scratch.
|
|
2586
|
+
*
|
|
2587
|
+
* Key behaviors:
|
|
2588
|
+
* - Placeholder tool_results are byte-identical across all forks
|
|
2589
|
+
* (enables prompt cache sharing)
|
|
2590
|
+
* - Recursion guard via `<fork-boilerplate>` tag detection
|
|
2591
|
+
* - Fork children are instructed NOT to spawn sub-agents
|
|
2592
|
+
*
|
|
2593
|
+
* All pure JS — no `node:` imports, Workers-compatible.
|
|
2594
|
+
*/
|
|
2595
|
+
|
|
2596
|
+
/**
|
|
2597
|
+
* Detect whether the current message history contains a fork boilerplate
|
|
2598
|
+
* tag — prevents recursive forking (child can't re-fork).
|
|
2599
|
+
*/
|
|
2600
|
+
declare function isInForkChild(messages: readonly MessageParam[]): boolean;
|
|
2601
|
+
/**
|
|
2602
|
+
* Build the messages a forked child sees. The child gets:
|
|
2603
|
+
* 1. All of the parent's existing messages (passed separately)
|
|
2604
|
+
* 2. A cloned assistant message with tool_use blocks
|
|
2605
|
+
* 3. Placeholder tool_results + fork directive
|
|
2606
|
+
*
|
|
2607
|
+
* The placeholder text is identical across all forks so the LLM
|
|
2608
|
+
* provider can cache-share the prefix.
|
|
2609
|
+
*/
|
|
2610
|
+
declare function buildForkedMessages(directive: string, assistantContent: readonly ContentBlockParam[]): MessageParam[];
|
|
2611
|
+
|
|
2612
|
+
/**
|
|
2613
|
+
* LocalStorageAdapter — filesystem-backed `StorageAdapter`.
|
|
2614
|
+
*
|
|
2615
|
+
* Ported from La-Machina's production implementation. Wraps Node
|
|
2616
|
+
* `fs/promises` with:
|
|
2617
|
+
*
|
|
2618
|
+
* - **Atomic writes** (temp file + rename pattern)
|
|
2619
|
+
* - **Path safety** via `validateRelativePath` — blocks traversal, absolute
|
|
2620
|
+
* paths, null bytes at every entrypoint
|
|
2621
|
+
* - **Null-on-missing semantics** — `readFile`/`stat` return `null` instead
|
|
2622
|
+
* of throwing `ENOENT`; `listDir` returns `[]` for missing directories
|
|
2623
|
+
* - **Recursive directory creation** on all write operations
|
|
2624
|
+
*
|
|
2625
|
+
* Errors from the underlying `fs` module are wrapped in `StorageError` with
|
|
2626
|
+
* the original error preserved as `.cause`.
|
|
2627
|
+
*/
|
|
2628
|
+
|
|
2629
|
+
declare class LocalStorageAdapter implements StorageAdapter {
|
|
2630
|
+
private readonly root;
|
|
2631
|
+
constructor(root: string);
|
|
2632
|
+
private resolve;
|
|
2633
|
+
readFile(rel: string): Promise<string | null>;
|
|
2634
|
+
writeFile(rel: string, content: string): Promise<void>;
|
|
2635
|
+
appendFile(rel: string, content: string): Promise<void>;
|
|
2636
|
+
deleteFile(rel: string): Promise<void>;
|
|
2637
|
+
exists(rel: string): Promise<boolean>;
|
|
2638
|
+
listDir(rel: string): Promise<string[]>;
|
|
2639
|
+
mkdir(rel: string): Promise<void>;
|
|
2640
|
+
isDirectory(rel: string): Promise<boolean>;
|
|
2641
|
+
stat(rel: string): Promise<FileStat | null>;
|
|
2642
|
+
getRoot(): string;
|
|
2643
|
+
rootExists(): Promise<boolean>;
|
|
2644
|
+
}
|
|
2645
|
+
|
|
2646
|
+
/**
|
|
2647
|
+
* R2StorageAdapter — Cloudflare R2 (S3-compatible) implementation.
|
|
2648
|
+
*
|
|
2649
|
+
* Ported from La-Machina. Uses `@aws-sdk/client-s3` since R2 speaks the S3
|
|
2650
|
+
* protocol. Path semantics:
|
|
2651
|
+
*
|
|
2652
|
+
* - Files become objects keyed at `${rootPrefix}/${relativePath}`
|
|
2653
|
+
* - Directories are synthetic — S3 has no real directory concept. `mkdir`
|
|
2654
|
+
* is a no-op; `listDir` uses `Delimiter: '/'` to separate files from
|
|
2655
|
+
* "subdirectories" (common prefixes).
|
|
2656
|
+
* - `appendFile` is read-modify-write (S3 has no native append primitive).
|
|
2657
|
+
* OK for small files (JSONL episode logs); prohibitively expensive for
|
|
2658
|
+
* large files.
|
|
2659
|
+
* - `writeFile` is a single atomic PutObject — no temp+rename dance.
|
|
2660
|
+
*
|
|
2661
|
+
* The backing S3Client is constructed from config. Tests use
|
|
2662
|
+
* `aws-sdk-client-mock` to intercept `.send()` without needing DI.
|
|
2663
|
+
*/
|
|
2664
|
+
|
|
2665
|
+
interface R2ClientConfig {
|
|
2666
|
+
readonly bucket: string;
|
|
2667
|
+
readonly region: string;
|
|
2668
|
+
readonly accessKeyId: string;
|
|
2669
|
+
readonly secretAccessKey: string;
|
|
2670
|
+
readonly endpoint?: string | undefined;
|
|
2671
|
+
}
|
|
2672
|
+
declare class R2StorageAdapter implements StorageAdapter {
|
|
2673
|
+
private readonly client;
|
|
2674
|
+
private readonly bucket;
|
|
2675
|
+
private readonly rootPrefix;
|
|
2676
|
+
constructor(config: R2ClientConfig, rootPrefix: string);
|
|
2677
|
+
private key;
|
|
2678
|
+
readFile(rel: string): Promise<string | null>;
|
|
2679
|
+
writeFile(rel: string, content: string): Promise<void>;
|
|
2680
|
+
appendFile(rel: string, content: string): Promise<void>;
|
|
2681
|
+
deleteFile(rel: string): Promise<void>;
|
|
2682
|
+
exists(rel: string): Promise<boolean>;
|
|
2683
|
+
listDir(rel: string): Promise<string[]>;
|
|
2684
|
+
mkdir(rel: string): Promise<void>;
|
|
2685
|
+
isDirectory(rel: string): Promise<boolean>;
|
|
2686
|
+
stat(rel: string): Promise<FileStat | null>;
|
|
2687
|
+
}
|
|
2688
|
+
|
|
2689
|
+
/**
|
|
2690
|
+
* Hippocampus — memory encoding and retrieval engine.
|
|
2691
|
+
*
|
|
2692
|
+
* Ported from La-Machina. Named for the brain's hippocampus, which
|
|
2693
|
+
* encodes new traces (writing) and pattern-completes partial cues into
|
|
2694
|
+
* full memories (reading).
|
|
2695
|
+
*
|
|
2696
|
+
* Each Hippocampus instance manages one scope's files via a
|
|
2697
|
+
* StorageAdapter:
|
|
2698
|
+
* - profile.md → identity (full rewrite, not append)
|
|
2699
|
+
* - rules.md → behavioral gates (always / never / when sections)
|
|
2700
|
+
* - lessons.md → semantic facts (append + dedup, token-budgeted recall)
|
|
2701
|
+
* - topics/*.md → per-topic deep dives
|
|
2702
|
+
*
|
|
2703
|
+
* The storage backend is fully abstracted — the same instance works
|
|
2704
|
+
* against local fs or R2. Read-modify-write paths use the adapter's
|
|
2705
|
+
* atomic `writeFile` so concurrent writers see one or the other, never
|
|
2706
|
+
* a partial.
|
|
2707
|
+
*/
|
|
2708
|
+
|
|
2709
|
+
declare class Hippocampus {
|
|
2710
|
+
private readonly storage;
|
|
2711
|
+
private readonly subdir;
|
|
2712
|
+
private readonly profilePath;
|
|
2713
|
+
private readonly rulesPath;
|
|
2714
|
+
private readonly lessonsPath;
|
|
2715
|
+
private readonly topicsDir;
|
|
2716
|
+
constructor(storage: StorageAdapter, subdir?: string);
|
|
2717
|
+
recallIdentity(): Promise<string>;
|
|
2718
|
+
recallRules(): Promise<string>;
|
|
2719
|
+
/**
|
|
2720
|
+
* Load semantic knowledge most recent first, within a token budget
|
|
2721
|
+
* (rough heuristic: 4 chars per token). The header lines (# Lessons,
|
|
2722
|
+
* blank lines) always survive; entry lines fill the remaining budget
|
|
2723
|
+
* starting from the most recent.
|
|
2724
|
+
*/
|
|
2725
|
+
recallLessons(tokenBudget?: number): Promise<string>;
|
|
2726
|
+
recallTopic(slug: string): Promise<string>;
|
|
2727
|
+
encodeRule(text: string, kind: 'always' | 'never' | 'when', confidence?: EngramConfidence, source?: EngramSource): Promise<void>;
|
|
2728
|
+
encodeLesson(text: string, topic?: string, source?: EngramSource): Promise<void>;
|
|
2729
|
+
/**
|
|
2730
|
+
* Replace the identity snapshot — full rewrite, not append. Identity
|
|
2731
|
+
* is a coherent snapshot, not an append log.
|
|
2732
|
+
*/
|
|
2733
|
+
rewriteIdentity(entries: ReadonlyArray<string>): Promise<void>;
|
|
2734
|
+
entryCount(): Promise<number>;
|
|
2735
|
+
getStorage(): StorageAdapter;
|
|
2736
|
+
get rulesPathRelative(): string;
|
|
2737
|
+
get lessonsPathRelative(): string;
|
|
2738
|
+
get subdirPath(): string;
|
|
2739
|
+
/**
|
|
2740
|
+
* Extract normalized entry texts from a markdown memory file.
|
|
2741
|
+
* Strips the "- " prefix and trailing `<!-- ... -->` metadata so
|
|
2742
|
+
* dedup works regardless of the comment payload.
|
|
2743
|
+
*/
|
|
2744
|
+
static extractEntryTexts(content: string): Set<string>;
|
|
2745
|
+
/** Sanitize a topic name into a safe file slug. */
|
|
2746
|
+
static sanitizeSlug(name: string): string;
|
|
2747
|
+
}
|
|
2748
|
+
|
|
2749
|
+
/**
|
|
2750
|
+
* memoryConfig — applies the engine's `memory.mode` and `memory.scope`
|
|
2751
|
+
* gates to a wrapped Hippocampus + EpisodicMemory pair.
|
|
2752
|
+
*
|
|
2753
|
+
* Three modes from `ResolvedMemoryConfig`:
|
|
2754
|
+
*
|
|
2755
|
+
* - `'off'` — All reads return empty, all writes are no-ops.
|
|
2756
|
+
* The wrapped Hippocampus is built but never touched.
|
|
2757
|
+
* - `'read-only'` — Reads pass through to the wrapped Hippocampus,
|
|
2758
|
+
* writes are silent no-ops.
|
|
2759
|
+
* - `'read-write'` — Both reads and writes pass through.
|
|
2760
|
+
*
|
|
2761
|
+
* Two scopes:
|
|
2762
|
+
* - `'workspace'` — Hippocampus reads/writes the workspace adapter.
|
|
2763
|
+
* - `'global'` — Hippocampus reads/writes the global adapter.
|
|
2764
|
+
*
|
|
2765
|
+
* The factory returns a thin façade that exposes Hippocampus's read +
|
|
2766
|
+
* write surface plus the EpisodicMemory instance. The agent loop and
|
|
2767
|
+
* tools talk to this façade, never the raw Hippocampus.
|
|
2768
|
+
*/
|
|
2769
|
+
|
|
2770
|
+
interface SmartMemoryConfig {
|
|
2771
|
+
readonly mode: MemoryMode;
|
|
2772
|
+
readonly scope: MemoryScope;
|
|
2773
|
+
}
|
|
2774
|
+
interface SmartMemoryOptions {
|
|
2775
|
+
readonly storage: EngineStorage;
|
|
2776
|
+
readonly config: SmartMemoryConfig;
|
|
2777
|
+
}
|
|
2778
|
+
interface SmartMemory {
|
|
2779
|
+
readonly mode: MemoryMode;
|
|
2780
|
+
readonly scope: MemoryScope;
|
|
2781
|
+
readonly episodes: EpisodicMemory;
|
|
2782
|
+
recallIdentity(): Promise<string>;
|
|
2783
|
+
recallRules(): Promise<string>;
|
|
2784
|
+
recallLessons(tokenBudget?: number): Promise<string>;
|
|
2785
|
+
recallTopic(slug: string): Promise<string>;
|
|
2786
|
+
encodeRule(text: string, kind: 'always' | 'never' | 'when', confidence?: EngramConfidence, source?: EngramSource): Promise<void>;
|
|
2787
|
+
encodeLesson(text: string, topic?: string, source?: EngramSource): Promise<void>;
|
|
2788
|
+
rewriteIdentity(entries: ReadonlyArray<string>): Promise<void>;
|
|
2789
|
+
}
|
|
2790
|
+
declare function createSmartMemory(options: SmartMemoryOptions): SmartMemory;
|
|
2791
|
+
|
|
2792
|
+
/**
|
|
2793
|
+
* Skills loader — discovers `SKILL.md` files under a base directory.
|
|
2794
|
+
*
|
|
2795
|
+
* The skill format is intentionally minimal:
|
|
2796
|
+
*
|
|
2797
|
+
* skills/
|
|
2798
|
+
* ├── alpha/
|
|
2799
|
+
* │ ├── SKILL.md ← required (name + description)
|
|
2800
|
+
* │ └── pages/ ← optional (multi-page skill)
|
|
2801
|
+
* │ ├── intro.md
|
|
2802
|
+
* │ └── advanced.md
|
|
2803
|
+
* └── beta/
|
|
2804
|
+
* └── SKILL.md
|
|
2805
|
+
*
|
|
2806
|
+
* The loader returns one `LoadedSkill` per discovered `SKILL.md`. The
|
|
2807
|
+
* description is extracted from the first non-empty body line (anything
|
|
2808
|
+
* after the title heading) — falling back to the heading text itself if
|
|
2809
|
+
* the file has no body.
|
|
2810
|
+
*
|
|
2811
|
+
* The result feeds into:
|
|
2812
|
+
* - the system prompt builder (Phase 12+) which injects skill names +
|
|
2813
|
+
* descriptions so the model knows what's available
|
|
2814
|
+
* - the SkillPage tool, which lets the model load specific pages on
|
|
2815
|
+
* demand for multi-page skills
|
|
2816
|
+
*/
|
|
2817
|
+
|
|
2818
|
+
interface LoadedSkill {
|
|
2819
|
+
/** Folder name (also used as the lookup key in SkillPage). */
|
|
2820
|
+
readonly name: string;
|
|
2821
|
+
/** Human-readable description, extracted from SKILL.md. */
|
|
2822
|
+
readonly description: string;
|
|
2823
|
+
/** Whether the skill has a pages/ subdirectory with content. */
|
|
2824
|
+
readonly hasPages: boolean;
|
|
2825
|
+
/** Full relative path to the skill folder, e.g. "skills/alpha". */
|
|
2826
|
+
readonly path: string;
|
|
2827
|
+
/** Full relative path to the SKILL.md file. */
|
|
2828
|
+
readonly skillFilePath: string;
|
|
2829
|
+
}
|
|
2830
|
+
/**
|
|
2831
|
+
* Walk the given base directory and return one entry per discovered
|
|
2832
|
+
* skill. Returns an empty array when the base directory does not exist
|
|
2833
|
+
* or contains no skills.
|
|
2834
|
+
*/
|
|
2835
|
+
declare function loadSkills(storage: StorageAdapter, baseDir: string): Promise<LoadedSkill[]>;
|
|
2836
|
+
|
|
2837
|
+
/**
|
|
2838
|
+
* SkillPage tool — loads skill content on demand via a `SkillSource`.
|
|
2839
|
+
*
|
|
2840
|
+
* The tool is pluggable: the engine constructs it with either
|
|
2841
|
+
* `StorageSkillSource` (disk-backed, default) or `InlineSkillSource`
|
|
2842
|
+
* (per-run override). The tool itself only knows the interface.
|
|
2843
|
+
*
|
|
2844
|
+
* Input:
|
|
2845
|
+
* { skill: string, page?: string }
|
|
2846
|
+
*
|
|
2847
|
+
* - When `page` is omitted, the main `SKILL.md` body is returned.
|
|
2848
|
+
* - When `page` is set, the supplemental page body is returned.
|
|
2849
|
+
*
|
|
2850
|
+
* Errors surface as `{ isError: true, content }`:
|
|
2851
|
+
* - invalid skill / page name (path traversal shape)
|
|
2852
|
+
* - unknown skill (returned null from the source)
|
|
2853
|
+
* - unknown page
|
|
2854
|
+
* - upstream source throws (network error, SSRF block, etc.)
|
|
2855
|
+
*/
|
|
2856
|
+
|
|
2857
|
+
declare const inputSchema: z.ZodObject<{
|
|
2858
|
+
skill: z.ZodString;
|
|
2859
|
+
page: z.ZodOptional<z.ZodString>;
|
|
2860
|
+
}, "strip", z.ZodTypeAny, {
|
|
2861
|
+
skill: string;
|
|
2862
|
+
page?: string | undefined;
|
|
2863
|
+
}, {
|
|
2864
|
+
skill: string;
|
|
2865
|
+
page?: string | undefined;
|
|
2866
|
+
}>;
|
|
2867
|
+
interface SkillPageToolOptions {
|
|
2868
|
+
/** The skill source the tool reads through. */
|
|
2869
|
+
readonly source: SkillSource;
|
|
2870
|
+
}
|
|
2871
|
+
declare function createSkillPageTool(options: SkillPageToolOptions): Tool$1<typeof inputSchema>;
|
|
2872
|
+
|
|
2873
|
+
/**
|
|
2874
|
+
* StorageSkillSource — the default `SkillSource`.
|
|
2875
|
+
*
|
|
2876
|
+
* Reads `SKILL.md` files (and optional `pages/*.md`) from a storage
|
|
2877
|
+
* adapter pair. Lookup precedence is workspace-first, global-fallback —
|
|
2878
|
+
* matching the pre-017 behavior so existing deployments see no change.
|
|
2879
|
+
*
|
|
2880
|
+
* Empty/missing `baseDir` → `list()` returns `[]` (equivalent to "no
|
|
2881
|
+
* skills configured"). `autoload: false` at the config layer is handled
|
|
2882
|
+
* upstream — this source does not read that flag.
|
|
2883
|
+
*/
|
|
2884
|
+
|
|
2885
|
+
interface StorageSkillSourceOptions {
|
|
2886
|
+
readonly storage: EngineStorage;
|
|
2887
|
+
/** Directory under each scope's adapter root. Default: `'skills'`. */
|
|
2888
|
+
readonly baseDir?: string;
|
|
2889
|
+
}
|
|
2890
|
+
declare class StorageSkillSource implements SkillSource {
|
|
2891
|
+
private readonly storage;
|
|
2892
|
+
private readonly baseDir;
|
|
2893
|
+
constructor(options: StorageSkillSourceOptions);
|
|
2894
|
+
list(): Promise<ReadonlyArray<ResolvedSkill>>;
|
|
2895
|
+
getSkillFile(skill: string): Promise<string | null>;
|
|
2896
|
+
getPage(skill: string, page: string): Promise<string | null>;
|
|
2897
|
+
listPages(skill: string): Promise<ReadonlyArray<string>>;
|
|
2898
|
+
private listPagesIn;
|
|
2899
|
+
private findFile;
|
|
2900
|
+
private readIfExists;
|
|
2901
|
+
}
|
|
2902
|
+
|
|
2903
|
+
/**
|
|
2904
|
+
* InlineSkillSource — the per-run override `SkillSource`.
|
|
2905
|
+
*
|
|
2906
|
+
* Takes a list of `SkillOverride` records and resolves bodies from
|
|
2907
|
+
* inline `body` (fast path, no network) or `url` (HTTPS fetch on
|
|
2908
|
+
* demand, cached per instance). Designed for Cloudflare Workers —
|
|
2909
|
+
* zero `node:*` imports, plain `fetch`.
|
|
2910
|
+
*
|
|
2911
|
+
* Security:
|
|
2912
|
+
* - Optional `allowedHosts` gate. When set, URL fetches outside the
|
|
2913
|
+
* allowlist throw before making the request. When unset (default),
|
|
2914
|
+
* any URL is allowed — fine for local dev, risky for production.
|
|
2915
|
+
* - `timeoutMs` aborts stuck fetches (default: 15s).
|
|
2916
|
+
*
|
|
2917
|
+
* Caching:
|
|
2918
|
+
* - Bodies (main + each page) are memoized within the instance, so
|
|
2919
|
+
* repeated `SkillPage` calls within one run never refetch.
|
|
2920
|
+
* - No cross-run cache. Each run constructs a fresh InlineSkillSource.
|
|
2921
|
+
*/
|
|
2922
|
+
|
|
2923
|
+
interface InlineSkillSourceOptions {
|
|
2924
|
+
/** Fetch implementation. Defaults to `globalThis.fetch`. */
|
|
2925
|
+
readonly fetch?: typeof globalThis.fetch;
|
|
2926
|
+
/**
|
|
2927
|
+
* Host allowlist for URL fetches. When empty/undefined, any URL is
|
|
2928
|
+
* allowed. When set, each URL's host must exactly match an entry or
|
|
2929
|
+
* be a subdomain of one (`api.example.com` matches `example.com`).
|
|
2930
|
+
*/
|
|
2931
|
+
readonly allowedHosts?: ReadonlyArray<string>;
|
|
2932
|
+
/** Per-request fetch timeout. Default 15_000 ms. */
|
|
2933
|
+
readonly timeoutMs?: number;
|
|
2934
|
+
}
|
|
2935
|
+
declare class InlineSkillSource implements SkillSource {
|
|
2936
|
+
private readonly byName;
|
|
2937
|
+
private readonly cache;
|
|
2938
|
+
private readonly fetchImpl;
|
|
2939
|
+
private readonly allowedHosts;
|
|
2940
|
+
private readonly timeoutMs;
|
|
2941
|
+
constructor(overrides: ReadonlyArray<SkillOverride>, options?: InlineSkillSourceOptions);
|
|
2942
|
+
list(): Promise<ReadonlyArray<ResolvedSkill>>;
|
|
2943
|
+
getSkillFile(skill: string): Promise<string | null>;
|
|
2944
|
+
getPage(skill: string, page: string): Promise<string | null>;
|
|
2945
|
+
listPages(skill: string): Promise<ReadonlyArray<string>>;
|
|
2946
|
+
private resolve;
|
|
2947
|
+
private assertUrlAllowed;
|
|
2948
|
+
}
|
|
2949
|
+
|
|
2950
|
+
/**
|
|
2951
|
+
* Runtime detection — determines whether the engine is running in
|
|
2952
|
+
* Node.js or a restricted runtime (Cloudflare Workers, etc.)
|
|
2953
|
+
*
|
|
2954
|
+
* Detection strategy:
|
|
2955
|
+
* - Node.js has `process.versions.node` set to a version string
|
|
2956
|
+
* - Workers have `process` (polyfilled) but `process.versions` is
|
|
2957
|
+
* undefined or `process.versions.node` is undefined
|
|
2958
|
+
* - As a fallback, probe whether child_process.spawn is functional
|
|
2959
|
+
*/
|
|
2960
|
+
type RuntimeKind = 'node' | 'worker';
|
|
2961
|
+
/**
|
|
2962
|
+
* Detect the runtime. Result is cached after first call.
|
|
2963
|
+
*/
|
|
2964
|
+
declare function detectRuntime(): RuntimeKind;
|
|
2965
|
+
/**
|
|
2966
|
+
* Whether subprocess spawning (Bash, ripgrep, MCP stdio) is available.
|
|
2967
|
+
*/
|
|
2968
|
+
declare function canSpawnProcesses(): boolean;
|
|
2969
|
+
/**
|
|
2970
|
+
* Whether process lifecycle hooks (process.on('exit')) are available.
|
|
2971
|
+
*/
|
|
2972
|
+
declare function hasProcessLifecycle(): boolean;
|
|
2973
|
+
|
|
2974
|
+
/**
|
|
2975
|
+
* Logger — structured log emission gated by `config.logging.level` and
|
|
2976
|
+
* routed to `config.logging.sink`.
|
|
2977
|
+
*
|
|
2978
|
+
* Levels (ascending verbosity):
|
|
2979
|
+
*
|
|
2980
|
+
* silent < error < warn < info < debug
|
|
2981
|
+
*
|
|
2982
|
+
* `silent` suppresses everything (no-op Logger). Any other level passes
|
|
2983
|
+
* records at that rank OR ANY RANK ABOVE IT through to the sink.
|
|
2984
|
+
*
|
|
2985
|
+
* Sinks:
|
|
2986
|
+
* - `'stderr'` — `console.error(JSON.stringify(entry))` per record.
|
|
2987
|
+
* Structured output that's cheap to grep/pipe through `jq`.
|
|
2988
|
+
* - `'none'` — true no-op. Use when embedding the engine inside a
|
|
2989
|
+
* host that has its own observability stack and doesn't want the
|
|
2990
|
+
* engine yelling into stderr.
|
|
2991
|
+
* - `(entry) => void` — callable sink. The engine hands the full
|
|
2992
|
+
* `LogEntry` to the caller on every emission. Use this to route into
|
|
2993
|
+
* OpenTelemetry, Pino, Winston, Sentry, a worker queue, etc.
|
|
2994
|
+
*
|
|
2995
|
+
* The returned logger is intentionally small: `{ error, warn, info, debug }`.
|
|
2996
|
+
* Each method takes a short message and an optional structured `meta`
|
|
2997
|
+
* object. The engine calls these at known checkpoints (run start/end,
|
|
2998
|
+
* turn boundaries, tool dispatch, pauses, failures) — see engine.ts for
|
|
2999
|
+
* the full emission schedule.
|
|
3000
|
+
*/
|
|
3001
|
+
|
|
3002
|
+
interface Logger {
|
|
3003
|
+
error(msg: string, meta?: Record<string, unknown>): void;
|
|
3004
|
+
warn(msg: string, meta?: Record<string, unknown>): void;
|
|
3005
|
+
info(msg: string, meta?: Record<string, unknown>): void;
|
|
3006
|
+
debug(msg: string, meta?: Record<string, unknown>): void;
|
|
3007
|
+
}
|
|
3008
|
+
/**
|
|
3009
|
+
* Build a Logger from a resolved logging config.
|
|
3010
|
+
*
|
|
3011
|
+
* Silent-level shortcuts to a true no-op so hot paths don't even
|
|
3012
|
+
* allocate the meta object. Non-silent loggers filter per-call against
|
|
3013
|
+
* the level rank before dispatching to the sink.
|
|
3014
|
+
*/
|
|
3015
|
+
declare function createLogger(config: ResolvedLoggingConfig): Logger;
|
|
3016
|
+
|
|
3017
|
+
/**
|
|
3018
|
+
* Orchestrate — the main state machine.
|
|
3019
|
+
*
|
|
3020
|
+
* Pure JS. No platform APIs. Calls engine.run() internally for each
|
|
3021
|
+
* agent phase. Enforces retry policies, file snapshots for rollback,
|
|
3022
|
+
* dependency ordering, and parallel research execution.
|
|
3023
|
+
*
|
|
3024
|
+
* Flow:
|
|
3025
|
+
* 1. Plan — Planner agent produces a JSON plan (or use caller-supplied plan)
|
|
3026
|
+
* 2. Execute — for each step in dependency order:
|
|
3027
|
+
* - research: spawn N researchers in parallel
|
|
3028
|
+
* - implement: synthesize spec from research → run implementer with retry
|
|
3029
|
+
* - verify: run verifier → on fail, retry implementer or revert
|
|
3030
|
+
* - review: optional code review
|
|
3031
|
+
* 3. Finalize — Finalizer agent assembles the report
|
|
3032
|
+
* 4. Return — OrchestratorResult with full provenance
|
|
3033
|
+
*/
|
|
3034
|
+
|
|
3035
|
+
/**
|
|
3036
|
+
* Execute a structured orchestration. This is the internal implementation
|
|
3037
|
+
* called by engine.orchestrate().
|
|
3038
|
+
*/
|
|
3039
|
+
declare function orchestrate(engine: Engine, options: OrchestrateOptions, config: ResolvedOrchestratorConfig, storage: StorageAdapter, logger: Logger): Promise<OrchestratorResult>;
|
|
3040
|
+
|
|
3041
|
+
/**
|
|
3042
|
+
* Plan parser — extract and validate a JSON plan from LLM output.
|
|
3043
|
+
*
|
|
3044
|
+
* The Planner agent may wrap JSON in markdown fences, include preamble
|
|
3045
|
+
* text, or produce slightly malformed JSON. This parser is tolerant:
|
|
3046
|
+
* 1. Strips markdown ```json ... ``` fences
|
|
3047
|
+
* 2. Finds the first { ... } block in the text
|
|
3048
|
+
* 3. Parses as JSON
|
|
3049
|
+
* 4. Validates against PlanSchema with Zod
|
|
3050
|
+
*/
|
|
3051
|
+
|
|
3052
|
+
/**
|
|
3053
|
+
* Extract a valid Plan from raw LLM output. Returns null if the output
|
|
3054
|
+
* can't be parsed into a valid plan.
|
|
3055
|
+
*/
|
|
3056
|
+
declare function parsePlan(raw: string, maxSteps: number): Plan | null;
|
|
3057
|
+
|
|
3058
|
+
/**
|
|
3059
|
+
* Generic retry-with-backoff wrapper. Pure JS — no platform APIs
|
|
3060
|
+
* beyond setTimeout.
|
|
3061
|
+
*/
|
|
3062
|
+
|
|
3063
|
+
interface RetryResult<T> {
|
|
3064
|
+
readonly success: boolean;
|
|
3065
|
+
readonly value?: T;
|
|
3066
|
+
readonly error?: string;
|
|
3067
|
+
readonly attempts: number;
|
|
3068
|
+
}
|
|
3069
|
+
/**
|
|
3070
|
+
* Execute `fn` up to `policy.maxAttempts` times with exponential backoff.
|
|
3071
|
+
* Returns the first successful result or the last error.
|
|
3072
|
+
*/
|
|
3073
|
+
declare function runWithRetry<T>(fn: () => Promise<T>, policy: RetryPolicy): Promise<RetryResult<T>>;
|
|
3074
|
+
|
|
3075
|
+
/**
|
|
3076
|
+
* File snapshot + restore — rollback mechanism for implementation failures.
|
|
3077
|
+
* Operates through the StorageAdapter abstraction — works on both
|
|
3078
|
+
* local filesystem and R2.
|
|
3079
|
+
*/
|
|
3080
|
+
|
|
3081
|
+
/**
|
|
3082
|
+
* Read and store the current content of a list of files. Files that
|
|
3083
|
+
* don't exist get `content: null` — restoring them will delete the file
|
|
3084
|
+
* (reversing a create).
|
|
3085
|
+
*/
|
|
3086
|
+
declare function snapshotFiles(storage: StorageAdapter, paths: readonly string[]): Promise<FileSnapshot[]>;
|
|
3087
|
+
/**
|
|
3088
|
+
* Restore files to their snapshotted state. Files that didn't exist
|
|
3089
|
+
* (content === null) are deleted. Files that existed are overwritten
|
|
3090
|
+
* with their original content.
|
|
3091
|
+
*/
|
|
3092
|
+
declare function restoreSnapshot(storage: StorageAdapter, snapshots: readonly FileSnapshot[]): Promise<string[]>;
|
|
3093
|
+
|
|
3094
|
+
/**
|
|
3095
|
+
* Synthesize — combine research findings into an implementation spec.
|
|
3096
|
+
*
|
|
3097
|
+
* This is CODE, not an LLM call. It takes the research step results
|
|
3098
|
+
* for a given implementation step and produces a combined spec string
|
|
3099
|
+
* that the Implementer agent receives as its task.
|
|
3100
|
+
*
|
|
3101
|
+
* The synthesis is deliberately simple: concatenate research findings
|
|
3102
|
+
* with the step's description and any file paths. The Implementer
|
|
3103
|
+
* LLM does the actual reasoning — the synthesizer just assembles context.
|
|
3104
|
+
*/
|
|
3105
|
+
|
|
3106
|
+
/**
|
|
3107
|
+
* Build an implementation spec from the step's description, prior
|
|
3108
|
+
* research results, and file list.
|
|
3109
|
+
*/
|
|
3110
|
+
declare function synthesizeSpec(step: PlanStep, allResults: readonly StepResult[]): string;
|
|
3111
|
+
|
|
3112
|
+
/**
|
|
3113
|
+
* Agent tool — lets the parent assistant spawn a child agent.
|
|
3114
|
+
*
|
|
3115
|
+
* The Agent tool is a closure factory that captures the parent's
|
|
3116
|
+
* subsystems (storage, API client, registry, subagent registry,
|
|
3117
|
+
* config) at engine.run() time. When the parent calls it via the
|
|
3118
|
+
* usual tool dispatch mechanism, the closure runs `runAgent` to drive
|
|
3119
|
+
* a child loop and returns the child's final answer as the
|
|
3120
|
+
* tool_result content.
|
|
3121
|
+
*
|
|
3122
|
+
* Depth and uniqueness are enforced via `SubagentRegistry`. If the
|
|
3123
|
+
* spawn would exceed `maxSubagentDepth`, the tool returns an error
|
|
3124
|
+
* result instead of throwing — the parent assistant can read the
|
|
3125
|
+
* error and adapt.
|
|
3126
|
+
*
|
|
3127
|
+
* The factory's `parentAgentId` parameter is null at the top level
|
|
3128
|
+
* (root run) and set to the parent's agentId for nested invocations.
|
|
3129
|
+
*/
|
|
3130
|
+
|
|
3131
|
+
/**
|
|
3132
|
+
* A subagent definition the parent can dispatch to via `Agent({ subagent_type })`.
|
|
3133
|
+
*
|
|
3134
|
+
* `systemPrompt` overrides the parent's system prompt when the child runs.
|
|
3135
|
+
* Leave it undefined to inherit the parent's prompt — which is what the
|
|
3136
|
+
* `'general-purpose'` builtin does.
|
|
3137
|
+
*/
|
|
3138
|
+
interface AgentDefinition {
|
|
3139
|
+
readonly name: string;
|
|
3140
|
+
readonly description: string;
|
|
3141
|
+
readonly systemPrompt?: string;
|
|
3142
|
+
/**
|
|
3143
|
+
* When true, marks this agent as safe for parallel execution
|
|
3144
|
+
* alongside other readOnly agents. The Agent tool uses this via
|
|
3145
|
+
* `isConcurrencySafe` so the batch dispatcher can run multiple
|
|
3146
|
+
* research agents concurrently. Default: false.
|
|
3147
|
+
*/
|
|
3148
|
+
readonly readOnly?: boolean;
|
|
3149
|
+
}
|
|
3150
|
+
|
|
3151
|
+
/**
|
|
3152
|
+
* Agents loader — discovers custom subagent definitions under a directory.
|
|
3153
|
+
*
|
|
3154
|
+
* Format (intentionally minimal, no YAML dependency):
|
|
3155
|
+
*
|
|
3156
|
+
* {customPath}/
|
|
3157
|
+
* ├── researcher.md
|
|
3158
|
+
* └── reviewer.md
|
|
3159
|
+
*
|
|
3160
|
+
* Each `.md` file defines one agent:
|
|
3161
|
+
*
|
|
3162
|
+
* # researcher
|
|
3163
|
+
*
|
|
3164
|
+
* One-line description. This is what the parent model sees in the Agent
|
|
3165
|
+
* tool's description catalogue.
|
|
3166
|
+
*
|
|
3167
|
+
* ---
|
|
3168
|
+
*
|
|
3169
|
+
* You are a research subagent. You have access to the web and filesystem...
|
|
3170
|
+
*
|
|
3171
|
+
* Parsing rules:
|
|
3172
|
+
*
|
|
3173
|
+
* - The **filename** (sans `.md`) is the agent's `name`. It must match
|
|
3174
|
+
* `^[a-zA-Z0-9_-]+$` or the file is skipped.
|
|
3175
|
+
* - The **first non-empty line** (after stripping leading `# `) is the
|
|
3176
|
+
* agent's `description`. One liner.
|
|
3177
|
+
* - The **rest of the file after the `---` separator** is the agent's
|
|
3178
|
+
* `systemPrompt`. If no `---` separator exists, the entire body after
|
|
3179
|
+
* the description is the system prompt. If there is no body at all,
|
|
3180
|
+
* `systemPrompt` is undefined and the agent inherits the parent's prompt.
|
|
3181
|
+
*/
|
|
3182
|
+
|
|
3183
|
+
/**
|
|
3184
|
+
* Walk `baseDir` under the given storage adapter and return one
|
|
3185
|
+
* `AgentDefinition` per valid `.md` file. Returns an empty array when
|
|
3186
|
+
* the directory is missing or empty.
|
|
3187
|
+
*/
|
|
3188
|
+
declare function loadAgents(storage: StorageAdapter, baseDir: string): Promise<AgentDefinition[]>;
|
|
3189
|
+
|
|
3190
|
+
/**
|
|
3191
|
+
* Coordinator mode — resolves config, builds the worker agent definition,
|
|
3192
|
+
* and determines the effective system prompt and tool restrictions.
|
|
3193
|
+
*/
|
|
3194
|
+
|
|
3195
|
+
/**
|
|
3196
|
+
* Check whether the engine is running in coordinator mode.
|
|
3197
|
+
*/
|
|
3198
|
+
declare function isCoordinatorMode(config: ResolvedConfig): boolean;
|
|
3199
|
+
/**
|
|
3200
|
+
* Build the worker agent definition. Workers get a restricted tool set
|
|
3201
|
+
* and a simple system prompt. The coordinator dispatches to them via
|
|
3202
|
+
* `Agent({ subagent_type: 'worker', ... })`.
|
|
3203
|
+
*/
|
|
3204
|
+
declare function buildWorkerAgent(config: ResolvedCoordinatorConfig): AgentDefinition;
|
|
3205
|
+
/**
|
|
3206
|
+
* Get the coordinator's system prompt override. Used by the engine's
|
|
3207
|
+
* prompt builder when coordinator mode is active.
|
|
3208
|
+
*/
|
|
3209
|
+
declare function getCoordinatorBasePrompt(): string;
|
|
3210
|
+
|
|
3211
|
+
/**
|
|
3212
|
+
* Coordinator system prompt — ported from La-Machina's coordinatorMode.ts.
|
|
3213
|
+
*
|
|
3214
|
+
* 370+ lines of behavioral instructions that transform the agent from
|
|
3215
|
+
* an implementer into an orchestrator. The prompt enforces:
|
|
3216
|
+
* - Research → Synthesis → Implementation → Verification phases
|
|
3217
|
+
* - Anti-lazy-delegation (must synthesize before directing workers)
|
|
3218
|
+
* - Parallel reads, serial writes
|
|
3219
|
+
* - Worker prompt quality standards
|
|
3220
|
+
*
|
|
3221
|
+
* Adapted for headless: no slash commands, no terminal UI, no interactive
|
|
3222
|
+
* approval dialogs. Tool names are parameterized.
|
|
3223
|
+
*/
|
|
3224
|
+
declare function getCoordinatorSystemPrompt(): string;
|
|
3225
|
+
|
|
3226
|
+
interface McpClientOptions {
|
|
3227
|
+
readonly serverName: string;
|
|
3228
|
+
readonly config: ResolvedMcpServerConfig;
|
|
3229
|
+
readonly connectTimeoutMs: number;
|
|
3230
|
+
readonly callTimeoutMs: number;
|
|
3231
|
+
readonly logger: Logger;
|
|
3232
|
+
readonly transportOverride?: Transport;
|
|
3233
|
+
/**
|
|
3234
|
+
* Handler invoked when this server issues a `sampling/createMessage`
|
|
3235
|
+
* request. Only called when `config.allowSampling === true`. When
|
|
3236
|
+
* absent, sampling requests are refused with a protocol error.
|
|
3237
|
+
*/
|
|
3238
|
+
readonly samplingHandler?: SamplingHandler;
|
|
3239
|
+
}
|
|
3240
|
+
declare class McpClient {
|
|
3241
|
+
readonly serverName: string;
|
|
3242
|
+
private readonly options;
|
|
3243
|
+
private sdkClient;
|
|
3244
|
+
private toolCache;
|
|
3245
|
+
private connected;
|
|
3246
|
+
/** PID of the child process (stdio only). Used for synchronous orphan kill. */
|
|
3247
|
+
private childPid;
|
|
3248
|
+
constructor(options: McpClientOptions);
|
|
3249
|
+
get isConnected(): boolean;
|
|
3250
|
+
/** PID of the spawned child process (stdio only). Null for HTTP/SSE. */
|
|
3251
|
+
get pid(): number | null;
|
|
3252
|
+
connect(): Promise<void>;
|
|
3253
|
+
listTools(): readonly McpToolDef[];
|
|
3254
|
+
callTool(toolName: string, input: unknown): Promise<McpCallResult>;
|
|
3255
|
+
close(): Promise<void>;
|
|
3256
|
+
/**
|
|
3257
|
+
* Synchronously kill the child process (stdio only). Used by
|
|
3258
|
+
* McpManager's process.on('exit') handler where async work is not
|
|
3259
|
+
* possible. No-op for HTTP/SSE transports.
|
|
3260
|
+
*/
|
|
3261
|
+
killSync(): void;
|
|
3262
|
+
/**
|
|
3263
|
+
* Register the engine-side handler for MCP `sampling/createMessage`.
|
|
3264
|
+
* Runs user-supplied SamplingHandler, converts result to the MCP
|
|
3265
|
+
* `CreateMessageResult` shape.
|
|
3266
|
+
*/
|
|
3267
|
+
private installSamplingHandler;
|
|
3268
|
+
private buildTransport;
|
|
3269
|
+
}
|
|
3270
|
+
|
|
3271
|
+
/**
|
|
3272
|
+
* BindingHttpTransport — plain-POST JSON-RPC MCP transport for Cloudflare
|
|
3273
|
+
* Workers.
|
|
3274
|
+
*
|
|
3275
|
+
* The upstream `StreamableHTTPClientTransport` opens a long-lived `fetch()`
|
|
3276
|
+
* with a streaming SSE response for server-push notifications. On the
|
|
3277
|
+
* Workers runtime that stream can hang unpredictably after `initialize`,
|
|
3278
|
+
* causing `tools/list` and `tools/call` to stall. This transport sidesteps
|
|
3279
|
+
* that entirely: every message is a short, stateless POST; the response
|
|
3280
|
+
* is parsed once and handed to `onmessage`. No persistent reader, no SSE.
|
|
3281
|
+
*
|
|
3282
|
+
* Trade-off: server-push notifications (logs, progress) don't work. That's
|
|
3283
|
+
* fine for the tool-calling use case — `tools/list` and `tools/call` are
|
|
3284
|
+
* request/response only.
|
|
3285
|
+
*
|
|
3286
|
+
* Implements just enough of the MCP transport contract for the engine's
|
|
3287
|
+
* `McpClient` to use. All pure JS — no `node:*` imports, Workers-safe.
|
|
3288
|
+
*/
|
|
3289
|
+
|
|
3290
|
+
interface BindingHttpTransportOptions {
|
|
3291
|
+
/** Full URL of the MCP Streamable-HTTP endpoint (e.g. https://mcp.example.com/mcp). */
|
|
3292
|
+
readonly url: string;
|
|
3293
|
+
/** Extra headers sent on every POST (auth tokens, API keys). */
|
|
3294
|
+
readonly headers?: Readonly<Record<string, string>>;
|
|
3295
|
+
/**
|
|
3296
|
+
* Per-request dynamic header factory. When set, called before every
|
|
3297
|
+
* send; its result is merged OVER the static `headers`. On HTTP 401
|
|
3298
|
+
* the transport calls the factory again (invalidating any token
|
|
3299
|
+
* cache inside) and retries the request once.
|
|
3300
|
+
*/
|
|
3301
|
+
readonly resolveHeaders?: () => Promise<Readonly<Record<string, string>>>;
|
|
3302
|
+
/** Override fetch (tests only). Defaults to globalThis.fetch. */
|
|
3303
|
+
readonly fetch?: typeof globalThis.fetch;
|
|
3304
|
+
/** Per-request timeout in milliseconds. Default: 30_000. */
|
|
3305
|
+
readonly timeoutMs?: number;
|
|
3306
|
+
}
|
|
3307
|
+
/**
|
|
3308
|
+
* MCP transport that speaks JSON-RPC 2.0 over bare POST. No persistent
|
|
3309
|
+
* connection. Not suitable for servers that rely on async notifications.
|
|
3310
|
+
*/
|
|
3311
|
+
declare class BindingHttpTransport implements Transport {
|
|
3312
|
+
readonly url: string;
|
|
3313
|
+
private readonly headers;
|
|
3314
|
+
private readonly resolveHeaders;
|
|
3315
|
+
private readonly fetchImpl;
|
|
3316
|
+
private readonly timeoutMs;
|
|
3317
|
+
private started;
|
|
3318
|
+
private closed;
|
|
3319
|
+
sessionId?: string;
|
|
3320
|
+
onclose?: () => void;
|
|
3321
|
+
onerror?: (error: Error) => void;
|
|
3322
|
+
onmessage?: (message: JSONRPCMessage) => void;
|
|
3323
|
+
constructor(options: BindingHttpTransportOptions);
|
|
3324
|
+
start(): Promise<void>;
|
|
3325
|
+
send(message: JSONRPCMessage): Promise<void>;
|
|
3326
|
+
close(): Promise<void>;
|
|
3327
|
+
/**
|
|
3328
|
+
* Perform a single POST, applying current headers (static + dynamic).
|
|
3329
|
+
* Does not retry — the 401-refresh loop lives in `send()`.
|
|
3330
|
+
*/
|
|
3331
|
+
private doFetch;
|
|
3332
|
+
}
|
|
3333
|
+
|
|
3334
|
+
interface McpInstructionDelta {
|
|
3335
|
+
readonly connected: string[];
|
|
3336
|
+
readonly disconnected: string[];
|
|
3337
|
+
}
|
|
3338
|
+
declare class McpManager {
|
|
3339
|
+
private readonly logger;
|
|
3340
|
+
private readonly servers;
|
|
3341
|
+
private readonly shutdownTimeoutMs;
|
|
3342
|
+
private exitHandler;
|
|
3343
|
+
/** Tracks which servers connected/disconnected since last delta retrieval. */
|
|
3344
|
+
private pendingConnected;
|
|
3345
|
+
private pendingDisconnected;
|
|
3346
|
+
constructor(config: ResolvedMcpConfig, logger: Logger, options?: {
|
|
3347
|
+
samplingHandler?: SamplingHandler;
|
|
3348
|
+
});
|
|
3349
|
+
get connectedCount(): number;
|
|
3350
|
+
/**
|
|
3351
|
+
* Lazily connect every configured server and return the full set of
|
|
3352
|
+
* tools. Detects dead connections and retries them. Per-server failures
|
|
3353
|
+
* are isolated.
|
|
3354
|
+
*/
|
|
3355
|
+
getTools(): Promise<ReadonlyArray<Tool$1>>;
|
|
3356
|
+
/**
|
|
3357
|
+
* Retrieve and clear pending instruction deltas (connected/disconnected
|
|
3358
|
+
* servers since last call). Used by agentLoop to announce MCP changes
|
|
3359
|
+
* to the model. Ported from La-Machina's mcpInstructionsDelta.ts.
|
|
3360
|
+
*/
|
|
3361
|
+
getInstructionDeltas(): McpInstructionDelta;
|
|
3362
|
+
/**
|
|
3363
|
+
* Graceful shutdown with a timeout. Attempts `client.close()` for
|
|
3364
|
+
* every active server within `SHUTDOWN_TIMEOUT_MS`. Servers that don't
|
|
3365
|
+
* respond in time are force-killed (SIGKILL for stdio, no-op for HTTP/SSE).
|
|
3366
|
+
*
|
|
3367
|
+
* Removes the process.on('exit') handler after cleanup.
|
|
3368
|
+
*/
|
|
3369
|
+
shutdownAll(): Promise<void>;
|
|
3370
|
+
/**
|
|
3371
|
+
* Synchronously SIGKILL every known child PID. Used by the
|
|
3372
|
+
* process.on('exit') handler (where async is not possible) and by the
|
|
3373
|
+
* shutdown timeout fallback.
|
|
3374
|
+
*/
|
|
3375
|
+
private killAllSync;
|
|
3376
|
+
private removeExitHandler;
|
|
3377
|
+
private connectServer;
|
|
3378
|
+
private doConnect;
|
|
3379
|
+
}
|
|
3380
|
+
|
|
3381
|
+
/**
|
|
3382
|
+
* MCP error taxonomy.
|
|
3383
|
+
*
|
|
3384
|
+
* Three error classes, all subclasses of `EngineError`:
|
|
3385
|
+
*
|
|
3386
|
+
* - `McpConnectionError` — a server failed to spawn, initialize, or list
|
|
3387
|
+
* tools. Swallowed by `McpManager`: logged as warn, server's tools are
|
|
3388
|
+
* absent from the registry, run proceeds.
|
|
3389
|
+
*
|
|
3390
|
+
* - `McpProtocolError` — a server returned a malformed JSON-RPC response
|
|
3391
|
+
* or an unexpected capability mismatch. Usually a server bug. Surfaces
|
|
3392
|
+
* in `tool_result` as `isError: true` when it happens during a call.
|
|
3393
|
+
*
|
|
3394
|
+
* - `McpTimeoutError` — a tool call exceeded `mcp.callTimeoutMs`.
|
|
3395
|
+
* Surfaces in `tool_result` as `isError: true` so the agent loop can
|
|
3396
|
+
* recover (retry, use a different tool, report back).
|
|
3397
|
+
*
|
|
3398
|
+
* The principle: MCP is best-effort. A run must never fail because one
|
|
3399
|
+
* of its MCP servers went wrong.
|
|
3400
|
+
*/
|
|
3401
|
+
|
|
3402
|
+
declare class McpConnectionError extends EngineError {
|
|
3403
|
+
readonly serverName: string;
|
|
3404
|
+
constructor(serverName: string, cause: string);
|
|
3405
|
+
}
|
|
3406
|
+
declare class McpProtocolError extends EngineError {
|
|
3407
|
+
readonly serverName: string;
|
|
3408
|
+
constructor(serverName: string, cause: string);
|
|
3409
|
+
}
|
|
3410
|
+
declare class McpTimeoutError extends EngineError {
|
|
3411
|
+
readonly serverName: string;
|
|
3412
|
+
readonly toolName: string;
|
|
3413
|
+
readonly timeoutMs: number;
|
|
3414
|
+
constructor(serverName: string, toolName: string, timeoutMs: number);
|
|
3415
|
+
}
|
|
3416
|
+
|
|
3417
|
+
/**
|
|
3418
|
+
* adaptMcpTool — convert an `McpToolDef` into an engine `Tool`.
|
|
3419
|
+
*
|
|
3420
|
+
* The returned tool:
|
|
3421
|
+
*
|
|
3422
|
+
* - Has its name prefixed with `mcp__{serverName}__` so it cannot
|
|
3423
|
+
* collide with built-in or custom tools.
|
|
3424
|
+
* - Carries the server's description verbatim (the model sees this
|
|
3425
|
+
* as the tool's "what does it do" text).
|
|
3426
|
+
* - Uses `z.unknown()` as the engine-side input schema. MCP tools
|
|
3427
|
+
* author their validation in JSON Schema, not Zod, and the server
|
|
3428
|
+
* itself validates on receipt. Rather than run a lossy
|
|
3429
|
+
* JSON-Schema → Zod conversion at adapter time, we let the server
|
|
3430
|
+
* be the source of truth and pass input through unchanged.
|
|
3431
|
+
* - Sets `anthropicSchemaOverride` to the raw MCP JSON Schema — this
|
|
3432
|
+
* is what the engine ships to the Anthropic API so the model sees
|
|
3433
|
+
* a proper `input_schema` with typed parameters.
|
|
3434
|
+
* - Dispatches to `McpClient.callTool()`, turning `McpCallResult`
|
|
3435
|
+
* into the engine's `ToolResult` and catching any thrown MCP errors
|
|
3436
|
+
* into `{ isError: true }` results so the agent loop can recover.
|
|
3437
|
+
*
|
|
3438
|
+
* Why not use `z.any().passthrough()` instead of `z.unknown()`?
|
|
3439
|
+
* `z.unknown()` validates nothing (matches any value, including
|
|
3440
|
+
* `undefined`). `z.any()` is semantically equivalent but linters
|
|
3441
|
+
* frown on it. Either works; we pick `unknown` for stricter types.
|
|
3442
|
+
*/
|
|
3443
|
+
|
|
3444
|
+
/** Build the engine-side tool name for an MCP tool. */
|
|
3445
|
+
declare function mcpToolName(serverName: string, toolName: string): string;
|
|
3446
|
+
declare function adaptMcpTool(client: McpClient, serverName: string, def: McpToolDef): Tool$1<z.ZodUnknown>;
|
|
3447
|
+
|
|
3448
|
+
/**
|
|
3449
|
+
* buildSystemPrompt — assemble the full system prompt from static
|
|
3450
|
+
* sections, dynamic per-run sections, and optional memory/skill recall.
|
|
3451
|
+
*
|
|
3452
|
+
* Section order (matches La-Machina's cacheable/dynamic split):
|
|
3453
|
+
*
|
|
3454
|
+
* ── STATIC (cacheable prefix — identical across runs) ──
|
|
3455
|
+
* 1. Base identity + capabilities
|
|
3456
|
+
* 2. Doing tasks (behavioral rules)
|
|
3457
|
+
* 3. Executing actions with care (reversibility, blast radius)
|
|
3458
|
+
* 4. Using your tools (tool-specific instructions)
|
|
3459
|
+
* 5. Tone and style + output efficiency
|
|
3460
|
+
*
|
|
3461
|
+
* ── DYNAMIC (changes per-run) ──
|
|
3462
|
+
* 6. Environment (OS, CWD, model, date)
|
|
3463
|
+
* 7. MCP tools (when config.mcp.servers is populated)
|
|
3464
|
+
* 8. Identity (smart memory profile.md)
|
|
3465
|
+
* 9. Rules (smart memory rules.md)
|
|
3466
|
+
* 10. Lessons (smart memory lessons.md, token-budgeted)
|
|
3467
|
+
* 11. Skills (loadSkills() output as name/description list)
|
|
3468
|
+
* 12. Custom base (caller-supplied override, appended last)
|
|
3469
|
+
*
|
|
3470
|
+
* The function is async because memory recall and skill discovery both
|
|
3471
|
+
* involve storage adapter reads. It's pure-ish — no writes, no side
|
|
3472
|
+
* effects beyond the reads themselves.
|
|
3473
|
+
*/
|
|
3474
|
+
|
|
3475
|
+
interface BuildSystemPromptOptions {
|
|
3476
|
+
/** Custom base instruction appended to the system prompt. */
|
|
3477
|
+
readonly base?: string;
|
|
3478
|
+
/** SmartMemory façade — reads are gated by memory mode. */
|
|
3479
|
+
readonly memory: SmartMemory;
|
|
3480
|
+
/** Storage adapter pair — used to enumerate skills. */
|
|
3481
|
+
readonly storage: EngineStorage;
|
|
3482
|
+
/** Whether to scan and inject the skill index. */
|
|
3483
|
+
readonly skillsAutoload: boolean;
|
|
3484
|
+
/** Directory to scan for skills (relative to each scope's adapter root). */
|
|
3485
|
+
readonly skillsDir?: string;
|
|
3486
|
+
/**
|
|
3487
|
+
* Pre-resolved skill catalogue. When set, takes precedence over the
|
|
3488
|
+
* disk scan (`skillsAutoload` + `skillsDir`). Used by the engine when
|
|
3489
|
+
* `RunOptions.skills` supplies an inline list via `InlineSkillSource`.
|
|
3490
|
+
*/
|
|
3491
|
+
readonly skillList?: ReadonlyArray<{
|
|
3492
|
+
name: string;
|
|
3493
|
+
description: string;
|
|
3494
|
+
}>;
|
|
3495
|
+
/** Token budget for the lessons section (default: 1000). */
|
|
3496
|
+
readonly lessonsTokenBudget?: number;
|
|
3497
|
+
/** Model ID for the environment section. */
|
|
3498
|
+
readonly modelId?: string;
|
|
3499
|
+
/** Provider name for the environment section. */
|
|
3500
|
+
readonly provider?: string;
|
|
3501
|
+
/** Working directory override for the environment section. */
|
|
3502
|
+
readonly cwd?: string;
|
|
3503
|
+
/** Registered tool names — drives tool-specific prompt instructions. */
|
|
3504
|
+
readonly registeredToolNames?: ReadonlySet<string>;
|
|
3505
|
+
/** MCP-sourced tools — listed in a dedicated prompt section. */
|
|
3506
|
+
readonly mcpTools?: ReadonlyArray<Tool$1>;
|
|
3507
|
+
/** When true, skip normal static sections (base/doingTasks/etc.) —
|
|
3508
|
+
* the coordinator prompt replaces them via `base`. */
|
|
3509
|
+
readonly coordinatorMode?: boolean;
|
|
3510
|
+
}
|
|
3511
|
+
declare function buildSystemPrompt(options: BuildSystemPromptOptions): Promise<string>;
|
|
3512
|
+
|
|
3513
|
+
/**
|
|
3514
|
+
* Safe tool allowlist — tools that are always safe to run without
|
|
3515
|
+
* human approval or rule evaluation.
|
|
3516
|
+
*
|
|
3517
|
+
* Ported from La-Machina's `SAFE_YOLO_ALLOWLISTED_TOOLS`. These tools
|
|
3518
|
+
* are read-only or purely informational — they cannot modify the
|
|
3519
|
+
* filesystem, send messages, or cause side effects beyond reading state.
|
|
3520
|
+
*
|
|
3521
|
+
* Used by `'locked'` mode: only these tools pass, everything else is
|
|
3522
|
+
* denied. Also used as a fast-path in `'rules'` mode: allowlisted tools
|
|
3523
|
+
* skip rule evaluation entirely.
|
|
3524
|
+
*/
|
|
3525
|
+
declare const SAFE_TOOL_ALLOWLIST: ReadonlySet<string>;
|
|
3526
|
+
/**
|
|
3527
|
+
* Check if a tool name is on the safe allowlist.
|
|
3528
|
+
*/
|
|
3529
|
+
declare function isSafeTool(toolName: string): boolean;
|
|
3530
|
+
|
|
3531
|
+
/**
|
|
3532
|
+
* Rule parser + matcher.
|
|
3533
|
+
*
|
|
3534
|
+
* Rule string format: `"action:pattern"`
|
|
3535
|
+
* - action: `allow` or `deny`
|
|
3536
|
+
* - pattern: exact tool name or glob with `*` wildcard
|
|
3537
|
+
*
|
|
3538
|
+
* Examples:
|
|
3539
|
+
* - `"allow:Read"` → allow the Read tool
|
|
3540
|
+
* - `"deny:Bash"` → deny the Bash tool
|
|
3541
|
+
* - `"allow:mcp__fs__*"` → allow all tools from the fs MCP server
|
|
3542
|
+
* - `"deny:mcp__*"` → deny all MCP tools
|
|
3543
|
+
* - `"deny:*"` → deny everything (catch-all)
|
|
3544
|
+
* - `"allow:*"` → allow everything (catch-all)
|
|
3545
|
+
*
|
|
3546
|
+
* Invalid rule strings are silently skipped (logged at warn level by
|
|
3547
|
+
* the evaluator).
|
|
3548
|
+
*/
|
|
3549
|
+
|
|
3550
|
+
/**
|
|
3551
|
+
* Parse a rule string into a structured `PermissionRule`. Returns null
|
|
3552
|
+
* if the string is malformed.
|
|
3553
|
+
*/
|
|
3554
|
+
declare function parseRule(raw: string): PermissionRule | null;
|
|
3555
|
+
/**
|
|
3556
|
+
* Parse an array of rule strings into structured rules, skipping
|
|
3557
|
+
* malformed entries.
|
|
3558
|
+
*/
|
|
3559
|
+
declare function parseRules(raw: readonly string[]): readonly PermissionRule[];
|
|
3560
|
+
/**
|
|
3561
|
+
* Check if a tool name matches a rule's pattern. Uses picomatch for
|
|
3562
|
+
* glob support (same library the engine uses for Glob/Grep tools).
|
|
3563
|
+
*/
|
|
3564
|
+
declare function matchesRule(toolName: string, rule: PermissionRule): boolean;
|
|
3565
|
+
|
|
3566
|
+
/**
|
|
3567
|
+
* la-machina-engine — public API.
|
|
3568
|
+
*
|
|
3569
|
+
* The single entry point is `initEngine()`. Every config option has a
|
|
3570
|
+
* default; `initEngine()` with no arguments must work, so long as either
|
|
3571
|
+
* `config.model.apiKey` is set OR `ANTHROPIC_API_KEY` is in the environment.
|
|
3572
|
+
*
|
|
3573
|
+
* See plans/002-la-machina-engine.md for the full spec.
|
|
3574
|
+
*/
|
|
3575
|
+
|
|
3576
|
+
declare const ENGINE_VERSION = "0.0.0";
|
|
3577
|
+
/**
|
|
3578
|
+
* Initialize an Engine with (optional) user config merged over defaults.
|
|
3579
|
+
*
|
|
3580
|
+
* @param user Partial config. Every field is optional.
|
|
3581
|
+
* @returns A fully-configured Engine ready to `run()` (once Phase 7 lands).
|
|
3582
|
+
* @throws {ConfigError} If config validation fails or `apiKey` cannot be
|
|
3583
|
+
* resolved from either `config.model.apiKey` or `ANTHROPIC_API_KEY` env.
|
|
3584
|
+
*/
|
|
3585
|
+
declare function initEngine(user?: UserConfig): Engine;
|
|
3586
|
+
|
|
3587
|
+
export { AISdkAdapter, type AgentDefinition, AnthropicAdapter, ApiError, AuthError, type BackgroundAgentResult, type BackgroundExecutor, BindingHttpTransport, type BuildSystemPromptOptions, ConfigError, DEFAULT_SAMPLING_MAX_DEPTH, ENGINE_VERSION, Engine, EngineError, type EngineInternals, type EngineResponse, type EngineStorage, type Engram, type EngramConfidence, type EngramKind, type EngramScope, type EngramSource, type Entry, type Episode, EpisodicMemory, type FileSnapshot, type FileStat, type FlushPolicy, type GateBeforeToolHook, type GateDecision$1 as GateDecision, Hippocampus, InlineSkillSource, type LoadedSkill, LocalStorageAdapter, type LogEntry, type LogLevel, type LogSink, type Logger, type LoopProgress, MAX_ATTEMPTS, type McpCallResult, McpClient, McpConnectionError, type McpInstructionDelta, McpManager, McpProtocolError, type McpServerState, McpTimeoutError, type McpToolDef, type MemoryMode, type MemoryScope, type ModelAdapter, type ModelMessage, type ModelProvider, type ModelToolDefinition, NodeBackgroundExecutor, type NormalizedEvent, NotImplementedError, type OrchestrateOptions, type OrchestratorResult, type PauseReason, type PermissionAction, type PermissionDecision, type PermissionMode, type PermissionPolicy, type PermissionRule, type Plan, PlanSchema, type PlanStep, PlanStepSchema, type PostRunEvent, type PostRunHook, type PostToolCallEvent$1 as PostToolCallEvent, type PostToolCallHook, type PostTurnEvent, type PostTurnHook, type PreRunEvent, type PreRunHook, type PreToolCallEvent$1 as PreToolCallEvent, type PreToolCallHook, type PreTurnEvent, type PreTurnHook, R2BindingStorageAdapter, type R2BucketBinding, type R2ClientConfig, R2StorageAdapter, RETRY_DELAYS_MS, RateLimitError, type ResolvedAgentsConfig, type ResolvedConfig, type ResolvedCoordinatorConfig, type ResolvedExecutionConfig, type ResolvedHooksConfig, type ResolvedLoggingConfig, type ResolvedMcpConfig, type ResolvedMcpHttpServerConfig, type ResolvedMcpServerConfig, type ResolvedMcpSseServerConfig, type ResolvedMcpStdioServerConfig, type ResolvedMemoryConfig, type ResolvedModelConfig, type ResolvedOrchestratorConfig, type ResolvedPermissionsConfig, type ResolvedR2Config, type ResolvedSkill, type ResolvedSkillsConfig, type ResolvedStorageConfig, type ResolvedToolsConfig, type ResolvedTranscriptConfig, type ResponseError, type ResponseMeta, type ResumeAsyncOptions, type ResumeOptions, type RetryPolicy, type RunOptions, type RunProgress, type RunResult, type RunSnapshot, type RunState, RunStateManager, type RunStatus, RunTimeoutError, type RuntimeKind, SAFE_TOOL_ALLOWLIST, type SamplingContext, type SamplingHandler, type SamplingMessage, type SamplingModelPreferences, type SamplingRequest, type SamplingResponse, type SamplingTextBlock, type SerializedAgent, type SkillOverride, type SkillPageOverride, type SkillSource, type SmartMemory, type SmartMemoryConfig, type SpawnResult, type StartOptions, type StepResult, type StepStatus, type StopHook, type StopHookEvent, type StopHookResult, type StorageAdapter, StorageError, type StorageProvider, StorageSkillSource, StreamIncompleteError, StreamParseError, type StreamRequest, StreamingToolExecutor, SubagentRegistry, type SubagentRegistryOptions, type TokenUsage, type Tool$1 as Tool, type ToolContext, ToolRegistry, type ToolResult, type TranscriptLocation, type TranscriptMeta, type TranscriptStatus, type UserConfig, type WaitForOptions, type WebhookConfig, type WebhookDelivery, WebhookDispatcher, type WebhookEvent, type WebhookOptions, adaptMcpTool, buildForkedMessages, buildPermissionPolicy, buildSchemaPrompt, buildSystemPrompt, buildWorkerAgent, canSpawnProcesses, createLogger, createModelAdapter, createSendMessageTool, createSkillPageTool, createSmartMemory, defaultSamplingHandler, defineTool, detectRuntime, getCoordinatorBasePrompt, getCoordinatorSystemPrompt, hasProcessLifecycle, initEngine, isCoordinatorMode, isInForkChild, isSafeTool, loadAgents, loadSkills, matchesRule, mcpToolName, orchestrate, parsePlan, parseRule, parseRules, partitionToolCalls, restoreSnapshot, runWithRetry, signPayload, snapshotFiles, synthesizeSpec, toResponse, tryParseJSON, validateOutput };
|