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