@1kbirds/chidori 3.6.0 → 3.7.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/README.md +78 -3
- package/dist/agent.d.ts +479 -47
- package/dist/agent.js +28 -0
- package/dist/index.d.ts +131 -7
- package/dist/index.js +207 -24
- package/package.json +2 -1
- package/src/agent.ts +539 -55
- package/src/index.ts +258 -22
package/src/agent.ts
CHANGED
|
@@ -15,6 +15,27 @@ export type AgentJson =
|
|
|
15
15
|
|
|
16
16
|
export type JsonObject = { [key: string]: AgentJson };
|
|
17
17
|
|
|
18
|
+
/**
|
|
19
|
+
* Structured fields for `chidori.log`. Values are JSON; `undefined` entries are
|
|
20
|
+
* accepted (and simply dropped on serialization) so optional data can be logged
|
|
21
|
+
* without `?? null` dances.
|
|
22
|
+
*/
|
|
23
|
+
export type LogFields = { [key: string]: AgentJson | undefined };
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* What an agent handler may return: JSON, except object members may be
|
|
27
|
+
* `undefined` (they are dropped when the output is serialized). This is what
|
|
28
|
+
* lets outcome shapes with optional fields — {@link BranchOutcome},
|
|
29
|
+
* {@link ActorOutcome} — be returned from an agent without `?? null` dances.
|
|
30
|
+
*/
|
|
31
|
+
export type AgentOutput =
|
|
32
|
+
| null
|
|
33
|
+
| boolean
|
|
34
|
+
| number
|
|
35
|
+
| string
|
|
36
|
+
| AgentOutput[]
|
|
37
|
+
| { [key: string]: AgentOutput | undefined };
|
|
38
|
+
|
|
18
39
|
export interface JsonSchema {
|
|
19
40
|
type?: "object" | "array" | "string" | "number" | "integer" | "boolean" | "null";
|
|
20
41
|
description?: string;
|
|
@@ -33,6 +54,43 @@ export interface ToolDefinition {
|
|
|
33
54
|
parameters: JsonSchema & { type: "object" };
|
|
34
55
|
}
|
|
35
56
|
|
|
57
|
+
/**
|
|
58
|
+
* A tool is nothing more than a function with a documented signature: a
|
|
59
|
+
* `name`, a `description`, and a JSON-schema for its `parameters`, wrapped
|
|
60
|
+
* around the `run` function the model actually calls. That's the whole
|
|
61
|
+
* contract — the signature is what the model reads to decide when and how to
|
|
62
|
+
* call it; the function is what runs. {@link defineTool} builds one as a
|
|
63
|
+
* plain object you import (or define inline) like any other code — no
|
|
64
|
+
* `tools/` directory, no registry.
|
|
65
|
+
*
|
|
66
|
+
* Pass handles directly in `prompt({ tools: [...] })`,
|
|
67
|
+
* `conversation({ tools: [...] })`, or `context().tools([...])`, freely
|
|
68
|
+
* mixed with registered tool names.
|
|
69
|
+
*
|
|
70
|
+
* The `run` function executes in the AGENT'S OWN VM, so closures over agent
|
|
71
|
+
* state and ordinary imports all work, and its side effects (fetch,
|
|
72
|
+
* workspace, node:fs, ...) are the captured effects every agent already
|
|
73
|
+
* has — journaled and deterministically replayable for free. Each model
|
|
74
|
+
* turn of the loop is a durable `respond()` call; each invocation is
|
|
75
|
+
* journaled as a `mark("tool:<name>")` record for the trace.
|
|
76
|
+
*/
|
|
77
|
+
export interface ToolHandle<Args extends JsonObject = JsonObject> {
|
|
78
|
+
readonly __chidoriTool: true;
|
|
79
|
+
/** The name the model calls the tool by. */
|
|
80
|
+
readonly name: string;
|
|
81
|
+
/** What the tool does — the model reads this to decide when to call it. */
|
|
82
|
+
readonly description: string;
|
|
83
|
+
/** JSON-schema for the arguments: the documented shape of the call. */
|
|
84
|
+
readonly parameters: JsonSchema & { type: "object" };
|
|
85
|
+
/**
|
|
86
|
+
* The function that runs when the tool is called. The `chidori` runtime
|
|
87
|
+
* handle is passed in by the tool loop, but tool bodies usually just close
|
|
88
|
+
* over the imported `chidori`, so it is optional when you call `run`
|
|
89
|
+
* directly (`tool.run(args)`).
|
|
90
|
+
*/
|
|
91
|
+
run(args: Args, chidori?: Chidori): AgentOutput | Promise<AgentOutput>;
|
|
92
|
+
}
|
|
93
|
+
|
|
36
94
|
export type PromptStreamType = "progress" | "draft" | "subagent" | "final" | (string & {});
|
|
37
95
|
|
|
38
96
|
/** Provider prompt-cache lifetime for a cached prefix. */
|
|
@@ -43,12 +101,33 @@ export interface PromptOptions {
|
|
|
43
101
|
system?: string;
|
|
44
102
|
model?: string;
|
|
45
103
|
maxTokens?: number;
|
|
46
|
-
max_tokens?: number;
|
|
47
104
|
maxTurns?: number;
|
|
48
|
-
max_turns?: number;
|
|
49
105
|
temperature?: number;
|
|
50
|
-
tools?: string
|
|
106
|
+
tools?: Array<string | ToolHandle>;
|
|
107
|
+
/**
|
|
108
|
+
* `"json"` parses the reply as JSON (a single wrapping markdown fence is
|
|
109
|
+
* tolerated) and, by default, THROWS when the reply is not valid JSON — a
|
|
110
|
+
* truncated reply (e.g. a reasoning model spending the whole `maxTokens`
|
|
111
|
+
* budget on hidden reasoning) can never masquerade as a successful
|
|
112
|
+
* structured result. Set `strict: false` for the lenient raw-string
|
|
113
|
+
* fallback.
|
|
114
|
+
*/
|
|
51
115
|
format?: "json" | (string & {});
|
|
116
|
+
/**
|
|
117
|
+
* Applies to `format: "json"`. `true` (the default): unparseable output
|
|
118
|
+
* throws with the parse error and the reply head. `false`: fall back to
|
|
119
|
+
* returning the raw reply string.
|
|
120
|
+
*/
|
|
121
|
+
strict?: boolean;
|
|
122
|
+
/**
|
|
123
|
+
* Opt-in guard for plain-text prompts: `true` THROWS a catchable error when
|
|
124
|
+
* the reply is empty after trimming whitespace — a response truncated to
|
|
125
|
+
* nothing (e.g. a reasoning model spending the whole `maxTokens` budget on
|
|
126
|
+
* hidden reasoning) can never flow on as a silently empty result. Has no
|
|
127
|
+
* effect with `format: "json"`, where strict parsing already throws on an
|
|
128
|
+
* empty reply. Off by default: an empty reply resolves to `""`.
|
|
129
|
+
*/
|
|
130
|
+
nonEmpty?: boolean;
|
|
52
131
|
stream?: boolean;
|
|
53
132
|
/**
|
|
54
133
|
* Prompt-cache posture. Defaults to on (`"5m"`): the runtime marks the
|
|
@@ -64,12 +143,19 @@ export interface PromptOptions {
|
|
|
64
143
|
export interface LlmResponseJson {
|
|
65
144
|
content: string;
|
|
66
145
|
blocks: AgentJson[];
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
146
|
+
/** Tool-use requests from the model. `input` is always a JSON object, so it
|
|
147
|
+
* can be passed straight to `chidori.tool(call.name, call.input)`. */
|
|
148
|
+
toolCalls: { id: string; name: string; input: JsonObject }[];
|
|
149
|
+
stopReason: string;
|
|
150
|
+
inputTokens: number;
|
|
151
|
+
outputTokens: number;
|
|
152
|
+
cacheCreationTokens: number;
|
|
153
|
+
cacheReadTokens: number;
|
|
154
|
+
/** Hidden reasoning text from reasoning models (OpenAI-compatible
|
|
155
|
+
* `reasoning_content`, e.g. DeepSeek). Present only when the provider
|
|
156
|
+
* reported it; never part of the conversation. Useful for inspecting why
|
|
157
|
+
* a model spent output budget before its visible answer. */
|
|
158
|
+
reasoning?: string;
|
|
73
159
|
}
|
|
74
160
|
|
|
75
161
|
/** Options for `Context.compact()` — explicit, opt-in window compaction. */
|
|
@@ -117,7 +203,7 @@ export interface CompactOptions {
|
|
|
117
203
|
export interface Context {
|
|
118
204
|
system(text: string): Context;
|
|
119
205
|
/** Expose registered tools (by name, resolved like `prompt({ tools })`). */
|
|
120
|
-
tools(
|
|
206
|
+
tools(list: Array<string | ToolHandle>): Context;
|
|
121
207
|
/** A large stable reference block, labelled for the trace. */
|
|
122
208
|
doc(label: string, text: string): Context;
|
|
123
209
|
user(text: string): Context;
|
|
@@ -150,18 +236,20 @@ export interface Context {
|
|
|
150
236
|
estimateTokens(): number;
|
|
151
237
|
}
|
|
152
238
|
|
|
153
|
-
/** One recorded exchange in a {@link Conversation} transcript.
|
|
154
|
-
|
|
239
|
+
/** One recorded exchange in a {@link Conversation} transcript. A type alias
|
|
240
|
+
* (not an interface) so transcripts stay assignable to {@link AgentJson} and
|
|
241
|
+
* can be returned as agent output directly. */
|
|
242
|
+
export type ConversationTurn = {
|
|
155
243
|
role: "user" | "assistant";
|
|
156
244
|
text: string;
|
|
157
|
-
}
|
|
245
|
+
};
|
|
158
246
|
|
|
159
247
|
/** Options for `chidori.conversation()`. */
|
|
160
248
|
export interface ConversationOptions {
|
|
161
249
|
/** System prompt — frozen once as the conversation's cacheable prefix. */
|
|
162
250
|
system?: string;
|
|
163
251
|
/** Tool names available on every turn (resolved like `prompt({ tools })`). */
|
|
164
|
-
tools?: string
|
|
252
|
+
tools?: Array<string | ToolHandle>;
|
|
165
253
|
/** Default stream label for each turn's prompt (default `"final"`). */
|
|
166
254
|
type?: PromptStreamType;
|
|
167
255
|
/** Default model for each turn (a per-turn override still wins). */
|
|
@@ -246,18 +334,29 @@ export interface InputOptions {
|
|
|
246
334
|
type?: string;
|
|
247
335
|
default?: string;
|
|
248
336
|
choices?: string[];
|
|
337
|
+
/**
|
|
338
|
+
* The artifact under review — a draft, a diff, a report — shown to the
|
|
339
|
+
* human alongside the question so an approval gate is never blind. The CLI
|
|
340
|
+
* renders it above the prompt; over HTTP it is surfaced as the paused
|
|
341
|
+
* session's `pending_details`. Never part of the durable record, so adding
|
|
342
|
+
* or editing it does not diverge an existing checkpoint.
|
|
343
|
+
*/
|
|
344
|
+
details?: string;
|
|
249
345
|
}
|
|
250
346
|
|
|
251
347
|
/**
|
|
252
348
|
* Who delivered a signal. `kind` distinguishes a human participant from a peer
|
|
253
349
|
* agent; `id` is the participant identity; `runId` is set when an agent sends
|
|
254
350
|
* (its own run id), so agent-to-agent coordination is attributable in the trace.
|
|
351
|
+
*
|
|
352
|
+
* A type alias (not an interface) so it stays assignable to {@link AgentJson} —
|
|
353
|
+
* senders routinely flow into `chidori.log` fields and agent outputs.
|
|
255
354
|
*/
|
|
256
|
-
export
|
|
355
|
+
export type SignalSender = {
|
|
257
356
|
kind: "human" | "agent";
|
|
258
357
|
id: string;
|
|
259
358
|
runId?: string;
|
|
260
|
-
}
|
|
359
|
+
};
|
|
261
360
|
|
|
262
361
|
/**
|
|
263
362
|
* A named message delivered into a run mid-flight (`docs/signals.md`). The
|
|
@@ -265,11 +364,13 @@ export interface SignalSender {
|
|
|
265
364
|
* `{ name, payload, from }` at an agent-declared listen point. Every signal is
|
|
266
365
|
* recorded in the call log, so the multiplayer session replays deterministically.
|
|
267
366
|
*/
|
|
268
|
-
export
|
|
367
|
+
export type Signal<T = AgentJson> = {
|
|
269
368
|
name: string;
|
|
270
369
|
payload: T;
|
|
271
370
|
from: SignalSender;
|
|
272
|
-
}
|
|
371
|
+
/** Never set on a delivered signal — the discriminant against {@link SignalTimeout}, so `if (result.timedOut)` narrows. */
|
|
372
|
+
timedOut?: undefined;
|
|
373
|
+
};
|
|
273
374
|
|
|
274
375
|
export interface SignalOptions {
|
|
275
376
|
/**
|
|
@@ -285,14 +386,14 @@ export interface SignalOptions {
|
|
|
285
386
|
* The sentinel a `timeoutMs` listen point resolves to when the deadline passes
|
|
286
387
|
* with no matching delivery — a timeout resolves to this sentinel rather than
|
|
287
388
|
* rejecting (`docs/signals.md`). `name` is the single awaited name, or `null`
|
|
288
|
-
* for a multi-name
|
|
389
|
+
* for a multi-name fan-in listen.
|
|
289
390
|
*/
|
|
290
|
-
export
|
|
391
|
+
export type SignalTimeout = {
|
|
291
392
|
name: string | null;
|
|
292
393
|
payload: null;
|
|
293
394
|
from: null;
|
|
294
395
|
timedOut: true;
|
|
295
|
-
}
|
|
396
|
+
};
|
|
296
397
|
|
|
297
398
|
export interface ParallelOptions {
|
|
298
399
|
concurrency?: number;
|
|
@@ -314,8 +415,9 @@ export interface BranchVariant {
|
|
|
314
415
|
|
|
315
416
|
export type BranchStatus = "completed" | "paused" | "failed";
|
|
316
417
|
|
|
317
|
-
/** The result of one branch sub-run, returned for comparison (not merged).
|
|
318
|
-
|
|
418
|
+
/** The result of one branch sub-run, returned for comparison (not merged).
|
|
419
|
+
* A type alias so outcomes can flow straight into agent output. */
|
|
420
|
+
export type BranchOutcome<T extends AgentJson = AgentJson> = {
|
|
319
421
|
label: string;
|
|
320
422
|
/**
|
|
321
423
|
* `<parent run id>-op<branch seq>-branch-<k>` — identifies the branch
|
|
@@ -330,7 +432,7 @@ export interface BranchOutcome<T extends AgentJson = AgentJson> {
|
|
|
330
432
|
pendingPrompt?: string;
|
|
331
433
|
/** The failure message, when `status` is `"failed"`. */
|
|
332
434
|
error?: string;
|
|
333
|
-
}
|
|
435
|
+
};
|
|
334
436
|
|
|
335
437
|
export interface BranchOptions {
|
|
336
438
|
/**
|
|
@@ -347,6 +449,301 @@ export interface RetryOptions {
|
|
|
347
449
|
backoff?: "fixed" | "exponential";
|
|
348
450
|
}
|
|
349
451
|
|
|
452
|
+
/**
|
|
453
|
+
* What the runtime does when an actor iteration fails (`docs/actors.md`):
|
|
454
|
+
* - `"never"` (default) — the failure is the actor's final outcome;
|
|
455
|
+
* - `"clean"` — re-run the actor's module from scratch (fresh log, the
|
|
456
|
+
* spawn-time filesystem anchor, the original input);
|
|
457
|
+
* - `"resume"` — replay the actor's accumulated call log with the trailing
|
|
458
|
+
* failed records stripped, so completed work returns from cache and the
|
|
459
|
+
* failing call itself retries live.
|
|
460
|
+
*/
|
|
461
|
+
export type ActorRestartStrategy = "never" | "clean" | "resume";
|
|
462
|
+
|
|
463
|
+
export interface SpawnActorOptions {
|
|
464
|
+
/** Register the actor under a name for `actors.lookup`/`actors.send` addressing. */
|
|
465
|
+
name?: string;
|
|
466
|
+
/** Restart strategy applied when an iteration fails. Default `"never"`. */
|
|
467
|
+
restart?: ActorRestartStrategy;
|
|
468
|
+
/** Restart intensity cap (default 3). */
|
|
469
|
+
maxRestarts?: number;
|
|
470
|
+
/** Base delay between restarts, doubling per attempt (default 0). */
|
|
471
|
+
backoffMs?: number;
|
|
472
|
+
/**
|
|
473
|
+
* How long the actor may sit at an empty-mailbox listen point with no
|
|
474
|
+
* explicit `timeoutMs` before the runtime parks it as a `paused` outcome
|
|
475
|
+
* (default 300 000 ms).
|
|
476
|
+
*/
|
|
477
|
+
idleTimeoutMs?: number;
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
/** How an actor's supervision loop settled. */
|
|
481
|
+
export type ActorOutcomeStatus = "completed" | "failed" | "paused" | "stopped";
|
|
482
|
+
|
|
483
|
+
/** The settlement `actors.join()`/`actors.stop()` resolve to.
|
|
484
|
+
* A type alias so outcomes can flow straight into agent output. */
|
|
485
|
+
export type ActorOutcome<T extends AgentJson = AgentJson> = {
|
|
486
|
+
pid: string;
|
|
487
|
+
status: ActorOutcomeStatus;
|
|
488
|
+
/** The actor's return value, when `status` is `"completed"`. */
|
|
489
|
+
output?: T;
|
|
490
|
+
/** The final failure, when `status` is `"failed"`. */
|
|
491
|
+
error?: string;
|
|
492
|
+
/** What the actor is parked on, when `status` is `"paused"`. */
|
|
493
|
+
pendingPrompt?: string;
|
|
494
|
+
/** How many supervised restarts the actor consumed. */
|
|
495
|
+
restarts: number;
|
|
496
|
+
};
|
|
497
|
+
|
|
498
|
+
/**
|
|
499
|
+
* What a `join`/`stop` with `timeoutMs` resolves to when the deadline passes
|
|
500
|
+
* before the actor settles: a snapshot, not a settlement — nothing merged,
|
|
501
|
+
* join again later. Discriminate with `outcome.status === "running"`.
|
|
502
|
+
*/
|
|
503
|
+
export type ActorStillRunning = {
|
|
504
|
+
pid: string;
|
|
505
|
+
status: "running";
|
|
506
|
+
restarts: number;
|
|
507
|
+
};
|
|
508
|
+
|
|
509
|
+
export type ActorStatus = {
|
|
510
|
+
pid: string;
|
|
511
|
+
status: ActorOutcomeStatus | "running" | "waiting" | "unknown";
|
|
512
|
+
restarts?: number;
|
|
513
|
+
/** Messages queued and not yet consumed. */
|
|
514
|
+
mailbox?: number;
|
|
515
|
+
/** The listen-point names a `"waiting"` actor is parked on. */
|
|
516
|
+
waitingFor?: string[];
|
|
517
|
+
};
|
|
518
|
+
|
|
519
|
+
/** A message consumed from a mailbox. */
|
|
520
|
+
export type ActorMessage<T = AgentJson> = {
|
|
521
|
+
name: string;
|
|
522
|
+
payload: T;
|
|
523
|
+
/** Sender identity: `id` is the sending actor's pid, or `"run"`. */
|
|
524
|
+
from: SignalSender;
|
|
525
|
+
/** Never set on a delivered message — the discriminant against {@link SignalTimeout}. */
|
|
526
|
+
timedOut?: undefined;
|
|
527
|
+
};
|
|
528
|
+
|
|
529
|
+
export interface ReceiveOptions {
|
|
530
|
+
/** Resolve to the `{ timedOut: true }` sentinel after this many ms. */
|
|
531
|
+
timeoutMs?: number;
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
export interface JoinActorOptions {
|
|
535
|
+
/**
|
|
536
|
+
* Give up waiting after this many ms: the join resolves to
|
|
537
|
+
* {@link ActorStillRunning} without settling the actor — join again later.
|
|
538
|
+
*/
|
|
539
|
+
timeoutMs?: number;
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
/**
|
|
543
|
+
* A spawned actor's handle: its address plus its lifecycle methods, so code
|
|
544
|
+
* reads `worker.send(...)` / `worker.join()` instead of threading pid strings.
|
|
545
|
+
* Pure sugar — every method is one recorded durable host call, identical to
|
|
546
|
+
* the string-addressed forms on {@link Actors}.
|
|
547
|
+
*/
|
|
548
|
+
export interface ActorHandle {
|
|
549
|
+
pid: string;
|
|
550
|
+
/** The registered name, when the spawn options provided one. */
|
|
551
|
+
name: string | null;
|
|
552
|
+
send<T extends AgentJson = AgentJson>(name: string, payload?: T): Promise<{ delivered: boolean }>;
|
|
553
|
+
join<T extends AgentJson = AgentJson>(): Promise<ActorOutcome<T>>;
|
|
554
|
+
join<T extends AgentJson = AgentJson>(
|
|
555
|
+
options: JoinActorOptions,
|
|
556
|
+
): Promise<ActorOutcome<T> | ActorStillRunning>;
|
|
557
|
+
stop<T extends AgentJson = AgentJson>(): Promise<ActorOutcome<T>>;
|
|
558
|
+
stop<T extends AgentJson = AgentJson>(
|
|
559
|
+
options: JoinActorOptions,
|
|
560
|
+
): Promise<ActorOutcome<T> | ActorStillRunning>;
|
|
561
|
+
status(): Promise<ActorStatus>;
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
/**
|
|
565
|
+
* `chidori.actors` — supervised, message-passing actor processes
|
|
566
|
+
* (`docs/actors.md`). `spawn` starts another agent module as a concurrent,
|
|
567
|
+
* addressable sub-run with a durable mailbox and a restart policy; actors can
|
|
568
|
+
* spawn their own children, forming a supervision tree (settling is
|
|
569
|
+
* owner-only, `"parent"` addresses the owner, and supervisors reap their
|
|
570
|
+
* children). Every method is a recorded durable call: a replay re-runs
|
|
571
|
+
* nothing.
|
|
572
|
+
*/
|
|
573
|
+
export interface Actors {
|
|
574
|
+
spawn<TInput extends AgentJson = JsonObject>(
|
|
575
|
+
source: string,
|
|
576
|
+
input?: TInput,
|
|
577
|
+
options?: SpawnActorOptions,
|
|
578
|
+
): Promise<ActorHandle>;
|
|
579
|
+
/**
|
|
580
|
+
* Deliver a named message to a mailbox. `to` is a pid, a registered name,
|
|
581
|
+
* or `"parent"` (the sender's spawner: its owning actor, or the run for a
|
|
582
|
+
* top-level actor). Never blocks; `delivered` is false once the target has
|
|
583
|
+
* settled.
|
|
584
|
+
*/
|
|
585
|
+
send<T extends AgentJson = AgentJson>(
|
|
586
|
+
to: string,
|
|
587
|
+
name: string,
|
|
588
|
+
payload?: T,
|
|
589
|
+
): Promise<{ delivered: boolean }>;
|
|
590
|
+
/** String-addressed `join` — see {@link ActorHandle.join}. Owner-only. */
|
|
591
|
+
join<T extends AgentJson = AgentJson>(target: string): Promise<ActorOutcome<T>>;
|
|
592
|
+
join<T extends AgentJson = AgentJson>(
|
|
593
|
+
target: string,
|
|
594
|
+
options: JoinActorOptions,
|
|
595
|
+
): Promise<ActorOutcome<T> | ActorStillRunning>;
|
|
596
|
+
/**
|
|
597
|
+
* Request a cooperative stop (honored between iterations, at mailbox waits,
|
|
598
|
+
* and during restart backoff — a live LLM/tool call finishes first), then
|
|
599
|
+
* settle and merge exactly like `join`. Owner-only.
|
|
600
|
+
*/
|
|
601
|
+
stop<T extends AgentJson = AgentJson>(target: string): Promise<ActorOutcome<T>>;
|
|
602
|
+
stop<T extends AgentJson = AgentJson>(
|
|
603
|
+
target: string,
|
|
604
|
+
options: JoinActorOptions,
|
|
605
|
+
): Promise<ActorOutcome<T> | ActorStillRunning>;
|
|
606
|
+
/** A durable snapshot of an actor's lifecycle. */
|
|
607
|
+
status(target: string): Promise<ActorStatus>;
|
|
608
|
+
/** Registry lookup: a handle for the actor registered under `name`, or `null`. */
|
|
609
|
+
lookup(name: string): Promise<ActorHandle | null>;
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
export interface SpawnAgentOptions {
|
|
613
|
+
/** Register the agent under a name in the durable registry; generated when omitted. */
|
|
614
|
+
name?: string;
|
|
615
|
+
/** Restart strategy applied when an iteration fails. Default `"resume"`. */
|
|
616
|
+
restart?: ActorRestartStrategy;
|
|
617
|
+
/** Restart intensity cap (default 3). */
|
|
618
|
+
maxRestarts?: number;
|
|
619
|
+
/** Base delay between restarts, doubling per attempt (default 0). */
|
|
620
|
+
backoffMs?: number;
|
|
621
|
+
/**
|
|
622
|
+
* Default model for the agent's prompts. Defaults to the spawner's
|
|
623
|
+
* resolved model and travels with the agent's durable descriptor, so a
|
|
624
|
+
* wake in a differently-configured process (another server, another env)
|
|
625
|
+
* keeps running on the same model.
|
|
626
|
+
*/
|
|
627
|
+
model?: string;
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
/**
|
|
631
|
+
* A detached agent's lifecycle status (`docs/detached-agents.md`).
|
|
632
|
+
* `hibernating` means parked at a listen point holding no thread and no VM,
|
|
633
|
+
* waiting on a mailbox delivery or an alarm deadline.
|
|
634
|
+
*/
|
|
635
|
+
export type DetachedAgentStatus =
|
|
636
|
+
| "running"
|
|
637
|
+
| "hibernating"
|
|
638
|
+
| "paused"
|
|
639
|
+
| "completed"
|
|
640
|
+
| "failed"
|
|
641
|
+
| "stopped";
|
|
642
|
+
|
|
643
|
+
/** The snapshot `agents.status()`/`agents.join()` resolve to.
|
|
644
|
+
* A type alias so outcomes can flow straight into agent output. */
|
|
645
|
+
export type DetachedAgentOutcome<T extends AgentJson = AgentJson> = {
|
|
646
|
+
name: string;
|
|
647
|
+
runId: string;
|
|
648
|
+
status: DetachedAgentStatus;
|
|
649
|
+
output?: T;
|
|
650
|
+
error?: string;
|
|
651
|
+
restarts: number;
|
|
652
|
+
/** The listen-point names a hibernating agent wakes on. */
|
|
653
|
+
waitingFor?: string[] | null;
|
|
654
|
+
/** The alarm deadline, when the current wait carries one (ISO timestamp). */
|
|
655
|
+
deadline?: string | null;
|
|
656
|
+
};
|
|
657
|
+
|
|
658
|
+
/**
|
|
659
|
+
* A detached agent's handle — same sugar as {@link ActorHandle}, addressed by
|
|
660
|
+
* the agent's registered name. Every method is one recorded durable call.
|
|
661
|
+
*/
|
|
662
|
+
export interface DetachedAgentHandle {
|
|
663
|
+
name: string;
|
|
664
|
+
runId: string | null;
|
|
665
|
+
send<T extends AgentJson = AgentJson>(name: string, payload?: T): Promise<{ delivered: boolean }>;
|
|
666
|
+
join<T extends AgentJson = AgentJson>(
|
|
667
|
+
options?: JoinActorOptions,
|
|
668
|
+
): Promise<DetachedAgentOutcome<T>>;
|
|
669
|
+
stop<T extends AgentJson = AgentJson>(options?: JoinActorOptions): Promise<DetachedAgentOutcome<T>>;
|
|
670
|
+
status<T extends AgentJson = AgentJson>(): Promise<DetachedAgentOutcome<T>>;
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
/**
|
|
674
|
+
* `chidori.agents` — detached, durable, addressable agent processes
|
|
675
|
+
* (`docs/detached-agents.md`). Where an actor lives inside its spawning run,
|
|
676
|
+
* a detached agent is its OWN durable run: it has its own journal, a
|
|
677
|
+
* registered name that outlives the spawner, a durable mailbox, and a
|
|
678
|
+
* hibernate/wake lifecycle that survives process restarts. Requires
|
|
679
|
+
* persistence.
|
|
680
|
+
*/
|
|
681
|
+
export interface DetachedAgents {
|
|
682
|
+
spawn<TInput extends AgentJson = JsonObject>(
|
|
683
|
+
source: string,
|
|
684
|
+
input?: TInput,
|
|
685
|
+
options?: SpawnAgentOptions,
|
|
686
|
+
): Promise<DetachedAgentHandle>;
|
|
687
|
+
/** Durable delivery into the agent's mailbox; wakes a hibernating agent on a matching name. */
|
|
688
|
+
send<T extends AgentJson = AgentJson>(
|
|
689
|
+
to: string,
|
|
690
|
+
name: string,
|
|
691
|
+
payload?: T,
|
|
692
|
+
): Promise<{ delivered: boolean }>;
|
|
693
|
+
/** Wait for the agent to settle; with `timeoutMs`, resolves to a status snapshot on expiry. */
|
|
694
|
+
join<T extends AgentJson = AgentJson>(
|
|
695
|
+
target: string,
|
|
696
|
+
options?: JoinActorOptions,
|
|
697
|
+
): Promise<DetachedAgentOutcome<T>>;
|
|
698
|
+
/** Cooperative stop (a live LLM call finishes first). */
|
|
699
|
+
stop<T extends AgentJson = AgentJson>(
|
|
700
|
+
target: string,
|
|
701
|
+
options?: JoinActorOptions,
|
|
702
|
+
): Promise<DetachedAgentOutcome<T>>;
|
|
703
|
+
/** Point-in-time snapshot; never blocks. */
|
|
704
|
+
status<T extends AgentJson = AgentJson>(target: string): Promise<DetachedAgentOutcome<T>>;
|
|
705
|
+
/** Registry lookup: a handle for the agent registered under `name`, or `null`. */
|
|
706
|
+
lookup(name: string): Promise<DetachedAgentHandle | null>;
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
/**
|
|
710
|
+
* `chidori.memory` — the persistent, namespaced key-value store. Values are
|
|
711
|
+
* JSON; `options.namespace` scopes keys (default `"default"`).
|
|
712
|
+
*/
|
|
713
|
+
export interface MemoryStore {
|
|
714
|
+
set<T extends AgentJson = AgentJson>(key: string, value: T, options?: JsonObject): Promise<void>;
|
|
715
|
+
get<T extends AgentJson = AgentJson>(key: string, options?: JsonObject): Promise<T | null>;
|
|
716
|
+
delete(key: string, options?: JsonObject): Promise<void>;
|
|
717
|
+
/** List entries, optionally filtered with `options.prefix`. */
|
|
718
|
+
list(options?: JsonObject): Promise<AgentJson[]>;
|
|
719
|
+
clear(options?: JsonObject): Promise<void>;
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
/**
|
|
723
|
+
* `chidori.util` — in-VM control-flow helpers. Unlike everything else on the
|
|
724
|
+
* host object these are pure JavaScript and record NOTHING: only the durable
|
|
725
|
+
* calls made inside your callbacks appear in the journal.
|
|
726
|
+
*/
|
|
727
|
+
export interface ChidoriUtil {
|
|
728
|
+
parallel<TTasks extends readonly (() => Promise<unknown>)[]>(
|
|
729
|
+
tasks: TTasks,
|
|
730
|
+
options?: ParallelOptions,
|
|
731
|
+
): Promise<{ [Index in keyof TTasks]: Awaited<ReturnType<TTasks[Index]>> }>;
|
|
732
|
+
retry<T>(fn: () => Promise<T>, options?: RetryOptions): Promise<T>;
|
|
733
|
+
tryCall<T>(fn: () => Promise<T>): Promise<TryCallResult<T>>;
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
/**
|
|
737
|
+
* `chidori.appData` — the agent-run application-data store (generative-UI
|
|
738
|
+
* runs). Writes and queries execute through the host boundary (the agent
|
|
739
|
+
* never holds a credential) and are journaled like every other effect;
|
|
740
|
+
* `params` bind server-side (`$1`, `$2`, …), never string-concatenated.
|
|
741
|
+
*/
|
|
742
|
+
export interface AppData {
|
|
743
|
+
write(sql: string, params?: AgentJson[]): Promise<AgentJson>;
|
|
744
|
+
query(sql: string, params?: AgentJson[]): Promise<AgentJson>;
|
|
745
|
+
}
|
|
746
|
+
|
|
350
747
|
export interface TryCallResult<T> {
|
|
351
748
|
ok: boolean;
|
|
352
749
|
value?: T;
|
|
@@ -357,7 +754,6 @@ export interface TemplateOptions {
|
|
|
357
754
|
source?: "file" | "inline";
|
|
358
755
|
}
|
|
359
756
|
|
|
360
|
-
export type MemoryAction = "get" | "set" | "delete" | "list" | "clear";
|
|
361
757
|
|
|
362
758
|
export type WorkspaceFileStatus = "complete" | "writing" | "failed";
|
|
363
759
|
|
|
@@ -409,13 +805,21 @@ export interface RuntimePolicyConfig {
|
|
|
409
805
|
export interface Chidori {
|
|
410
806
|
workspace: WorkspaceHost;
|
|
411
807
|
/** Start an immutable multi-turn prompt context (optionally seeded). */
|
|
412
|
-
context(seed?: { system?: string; tools?: string
|
|
808
|
+
context(seed?: { system?: string; tools?: Array<string | ToolHandle> }): Context;
|
|
413
809
|
/**
|
|
414
810
|
* Start a multi-turn chat assistant — a stateful wrapper over `context()`
|
|
415
811
|
* that owns the running dialogue. Send turns with `chat.say(message)` or drive
|
|
416
812
|
* an interactive `input()` loop with `chat.loop()`.
|
|
417
813
|
*/
|
|
418
814
|
conversation(options?: ConversationOptions): Conversation;
|
|
815
|
+
/**
|
|
816
|
+
* `format: "json"` parses the reply as JSON before returning it, so the
|
|
817
|
+
* resolved value is structured data (`AgentJson`), not a string. Narrow it
|
|
818
|
+
* to your shape with a single cast: `await chidori.prompt(p, { format:
|
|
819
|
+
* "json" }) as Triage`. (With `strict: false` an unparseable reply falls
|
|
820
|
+
* back to the raw string — still assignable to `AgentJson`.)
|
|
821
|
+
*/
|
|
822
|
+
prompt(text: string, options: PromptOptions & { format: "json" }): Promise<AgentJson>;
|
|
419
823
|
prompt(text: string, options?: PromptOptions): Promise<string>;
|
|
420
824
|
input(message: string, options?: InputOptions): Promise<string>;
|
|
421
825
|
/**
|
|
@@ -423,10 +827,14 @@ export interface Chidori {
|
|
|
423
827
|
* is already queued in the durable mailbox), then resolve to
|
|
424
828
|
* `{ name, payload, from }`. The inverse of `input()`: the run idles cheaply
|
|
425
829
|
* on disk and an outside party delivers via `POST /sessions/{id}/signal`.
|
|
830
|
+
* Pass an array for fan-in — the resolved signal's `name` says which fired;
|
|
831
|
+
* pre-arrived candidates are consumed in delivery order. Reach for `signal`
|
|
832
|
+
* when the delivery comes from OUTSIDE the process; use `receive` for actor
|
|
833
|
+
* messages.
|
|
426
834
|
*/
|
|
427
|
-
signal<T = AgentJson>(name: string): Promise<Signal<T>>;
|
|
835
|
+
signal<T = AgentJson>(name: string | string[]): Promise<Signal<T>>;
|
|
428
836
|
signal<T = AgentJson>(
|
|
429
|
-
name: string,
|
|
837
|
+
name: string | string[],
|
|
430
838
|
options: SignalOptions,
|
|
431
839
|
): Promise<Signal<T> | SignalTimeout>;
|
|
432
840
|
/**
|
|
@@ -435,17 +843,6 @@ export interface Chidori {
|
|
|
435
843
|
* deterministic.
|
|
436
844
|
*/
|
|
437
845
|
pollSignal<T = AgentJson>(name: string): Promise<Signal<T> | null>;
|
|
438
|
-
/**
|
|
439
|
-
* Fan-in: pause until ANY of the named signals is delivered (or one is
|
|
440
|
-
* already queued in the durable mailbox). Resolves to the bare consumed
|
|
441
|
-
* signal — its `name` says which fired. Pre-arrived candidates are consumed
|
|
442
|
-
* in delivery order (lowest `delivery_seq` across the whole name set).
|
|
443
|
-
*/
|
|
444
|
-
signalAny<T = AgentJson>(names: string[]): Promise<Signal<T>>;
|
|
445
|
-
signalAny<T = AgentJson>(
|
|
446
|
-
names: string[],
|
|
447
|
-
options: SignalOptions,
|
|
448
|
-
): Promise<Signal<T> | SignalTimeout>;
|
|
449
846
|
/**
|
|
450
847
|
* Durable value checkpoint: run `fn` once and journal its JSON-serializable
|
|
451
848
|
* result; on replay/resume the recorded value (or error) is returned without
|
|
@@ -453,8 +850,19 @@ export interface Chidori {
|
|
|
453
850
|
* resumed run does not re-pay it. The callback must be pure, synchronous
|
|
454
851
|
* compute — host effects (`chidori.*`), captured randomness, filesystem
|
|
455
852
|
* writes, timers, and async callbacks are refused inside a step.
|
|
853
|
+
*
|
|
854
|
+
* Type the result with a `type` alias, not an `interface`: interfaces lack
|
|
855
|
+
* the index signature `AgentJson` requires, so `interface Commit {...}`
|
|
856
|
+
* fails to satisfy `T extends AgentJson` while the structurally identical
|
|
857
|
+
* `type Commit = {...}` works.
|
|
456
858
|
*/
|
|
457
859
|
step<T extends AgentJson = AgentJson>(name: string, fn: () => T): Promise<T>;
|
|
860
|
+
/**
|
|
861
|
+
* Run another agent module inline and return its output — a synchronous
|
|
862
|
+
* child call sharing this run's context and log. Of the three module
|
|
863
|
+
* runners: need the answer inline → `callAgent`; comparing strategies →
|
|
864
|
+
* `branch`; coordinating concurrent processes → `actors.spawn`.
|
|
865
|
+
*/
|
|
458
866
|
callAgent<TInput extends AgentJson = JsonObject, TOutput extends AgentJson = AgentJson>(
|
|
459
867
|
path: string,
|
|
460
868
|
input?: TInput,
|
|
@@ -470,28 +878,68 @@ export interface Chidori {
|
|
|
470
878
|
variants: BranchVariant[],
|
|
471
879
|
options?: BranchOptions,
|
|
472
880
|
): Promise<BranchOutcome<T>[]>;
|
|
881
|
+
/**
|
|
882
|
+
* Supervised actor processes: spawn agent modules as concurrent,
|
|
883
|
+
* addressable sub-runs with durable mailboxes, supervision trees, and
|
|
884
|
+
* restart policies (`docs/actors.md`).
|
|
885
|
+
*/
|
|
886
|
+
actors: Actors;
|
|
887
|
+
/**
|
|
888
|
+
* Detached durable agent processes: spawn agent modules as long-lived,
|
|
889
|
+
* named runs that outlive the spawner, hibernate at listen points holding
|
|
890
|
+
* no thread and no VM, and wake on mailbox deliveries or alarm deadlines
|
|
891
|
+
* (`docs/detached-agents.md`).
|
|
892
|
+
*/
|
|
893
|
+
agents: DetachedAgents;
|
|
894
|
+
/**
|
|
895
|
+
* A durable timer: hibernate until the deadline, surviving process
|
|
896
|
+
* restarts (the deadline is persisted and re-armed at boot). Resolves to
|
|
897
|
+
* the `{ timedOut: true }` sentinel when the alarm fires.
|
|
898
|
+
*/
|
|
899
|
+
alarm(ms: number): Promise<SignalTimeout>;
|
|
900
|
+
/**
|
|
901
|
+
* Blocking, in-place message consumption: inside an actor, drains the
|
|
902
|
+
* actor's own mailbox; in the spawning run, drains messages actors sent to
|
|
903
|
+
* `"parent"` (and any pre-queued external signals). Unlike `signal()` —
|
|
904
|
+
* which pauses the whole run so an external party can resume it — `receive`
|
|
905
|
+
* waits in place and is woken directly by in-process senders. Reach for
|
|
906
|
+
* `receive` for actor messages; use `signal` for external deliveries.
|
|
907
|
+
*
|
|
908
|
+
* Actor death is observable: an actor that settles `failed` (restart
|
|
909
|
+
* budget spent) or `paused` delivers a monitor message named
|
|
910
|
+
* `"__chidori.down__"` to its owner's mailbox with payload
|
|
911
|
+
* `{ pid, name, status, error?, pendingPrompt?, restarts }` — include it in
|
|
912
|
+
* a fan-in (`receive(["result", "__chidori.down__"])`) so a collection
|
|
913
|
+
* loop reacts to worker death instead of waiting out its timeout. As a
|
|
914
|
+
* backstop, a `receive` (even with `timeoutMs`) fails fast once every
|
|
915
|
+
* spawned actor has settled and nothing matching is queued — nothing
|
|
916
|
+
* in-process can deliver anymore, so blocking would be pure starvation.
|
|
917
|
+
*/
|
|
918
|
+
receive<T = AgentJson>(names: string | string[]): Promise<ActorMessage<T>>;
|
|
919
|
+
receive<T = AgentJson>(
|
|
920
|
+
names: string | string[],
|
|
921
|
+
options: ReceiveOptions,
|
|
922
|
+
): Promise<ActorMessage<T> | SignalTimeout>;
|
|
473
923
|
tool<TArgs extends JsonObject = JsonObject, TResult extends AgentJson = AgentJson>(
|
|
474
924
|
name: string,
|
|
475
925
|
args?: TArgs,
|
|
476
926
|
): Promise<TResult>;
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
options?: ParallelOptions,
|
|
480
|
-
): Promise<{ [Index in keyof TTasks]: Awaited<ReturnType<TTasks[Index]>> }>;
|
|
481
|
-
retry<T>(fn: () => Promise<T>, options?: RetryOptions): Promise<T>;
|
|
482
|
-
tryCall<T>(fn: () => Promise<T>): Promise<TryCallResult<T>>;
|
|
927
|
+
/** In-VM control-flow helpers (`parallel`, `retry`, `tryCall`) — pure JS, never recorded. */
|
|
928
|
+
util: ChidoriUtil;
|
|
483
929
|
template(pathOrText: string, vars?: JsonObject, options?: TemplateOptions): Promise<string>;
|
|
484
|
-
log(message: string, fields?:
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
930
|
+
log(message: string, fields?: LogFields): Promise<void>;
|
|
931
|
+
/** The persistent, namespaced key-value store. */
|
|
932
|
+
memory: MemoryStore;
|
|
933
|
+
/** The journaled agent-run application-data store (generative-UI runs). */
|
|
934
|
+
appData: AppData;
|
|
935
|
+
/**
|
|
936
|
+
* Record a labelled trace marker in the call log — an annotation, nothing
|
|
937
|
+
* more. (The durable VALUE checkpoint is `chidori.step`.)
|
|
938
|
+
*/
|
|
939
|
+
mark(label?: string, data?: AgentJson): Promise<void>;
|
|
492
940
|
}
|
|
493
941
|
|
|
494
|
-
export type AgentFunction<TInput extends AgentJson = JsonObject, TOutput extends
|
|
942
|
+
export type AgentFunction<TInput extends AgentJson = JsonObject, TOutput extends AgentOutput = AgentOutput> = (
|
|
495
943
|
input: TInput,
|
|
496
944
|
) => TOutput | Promise<TOutput>;
|
|
497
945
|
|
|
@@ -537,7 +985,7 @@ export const chidori: Chidori = new Proxy({} as Chidori, {
|
|
|
537
985
|
* run(async (input) => ({ greeting: `hello ${input.name}` }));
|
|
538
986
|
* ```
|
|
539
987
|
*/
|
|
540
|
-
export function run<TInput extends AgentJson = JsonObject, TOutput extends
|
|
988
|
+
export function run<TInput extends AgentJson = JsonObject, TOutput extends AgentOutput = AgentOutput>(
|
|
541
989
|
handler: AgentFunction<TInput, TOutput>,
|
|
542
990
|
): void {
|
|
543
991
|
void handler;
|
|
@@ -546,3 +994,39 @@ export function run<TInput extends AgentJson = JsonObject, TOutput extends Agent
|
|
|
546
994
|
"replaced when an agent runs under chidori.",
|
|
547
995
|
);
|
|
548
996
|
}
|
|
997
|
+
|
|
998
|
+
/**
|
|
999
|
+
* Wrap a function into a {@link ToolHandle} by attaching its documented
|
|
1000
|
+
* signature — a `name`, a `description`, and a JSON-schema for its
|
|
1001
|
+
* `parameters`. That signature is all the model needs to call the function;
|
|
1002
|
+
* the function itself is just code. No `tools/` directory, no registry —
|
|
1003
|
+
* import (or define inline) the handle like any other value. `parameters`
|
|
1004
|
+
* defaults to an empty object schema; `description` to `""`. Inside the
|
|
1005
|
+
* runtime this import is replaced by the live implementation, exactly like
|
|
1006
|
+
* `run` and `chidori`.
|
|
1007
|
+
*
|
|
1008
|
+
* ```ts
|
|
1009
|
+
* import { chidori, run, defineTool } from "chidori:agent";
|
|
1010
|
+
*
|
|
1011
|
+
* const search = defineTool({
|
|
1012
|
+
* name: "search",
|
|
1013
|
+
* description: "Search the corpus.",
|
|
1014
|
+
* parameters: { type: "object", properties: { q: { type: "string" } }, required: ["q"] },
|
|
1015
|
+
* run: async ({ q }) => (await fetch(BASE + q)).json(),
|
|
1016
|
+
* });
|
|
1017
|
+
*
|
|
1018
|
+
* run(async () => chidori.prompt("find chidori", { tools: [search], maxTurns: 6 }));
|
|
1019
|
+
* ```
|
|
1020
|
+
*/
|
|
1021
|
+
export function defineTool<Args extends JsonObject = JsonObject>(def: {
|
|
1022
|
+
name: string;
|
|
1023
|
+
description?: string;
|
|
1024
|
+
parameters?: JsonSchema & { type: "object" };
|
|
1025
|
+
run(args: Args, chidori?: Chidori): AgentOutput | Promise<AgentOutput>;
|
|
1026
|
+
}): ToolHandle<Args> {
|
|
1027
|
+
void def;
|
|
1028
|
+
throw new Error(
|
|
1029
|
+
"defineTool() is only available inside the chidori runtime; this import " +
|
|
1030
|
+
"is replaced when an agent runs under chidori.",
|
|
1031
|
+
);
|
|
1032
|
+
}
|