@1kbirds/chidori 0.1.26 → 3.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/README.md +113 -6
- package/dist/agent.d.ts +487 -0
- package/dist/agent.js +46 -0
- package/dist/index.d.ts +332 -0
- package/dist/index.js +318 -0
- package/package.json +27 -33
- package/src/agent.ts +548 -0
- package/src/index.ts +580 -0
- package/CHANGELOG.md +0 -33
- package/Cargo.toml +0 -83
- package/build.rs +0 -7
- package/package_node/index.d.ts +0 -0
- package/package_node/index.js +0 -130
- package/pyproject.toml +0 -35
- package/src/lib.rs +0 -23
- package/src/translations/mod.rs +0 -9
- package/src/translations/nodejs.rs +0 -739
- package/src/translations/python.rs +0 -921
- package/src/translations/rust.rs +0 -737
- package/src/translations/shared.rs +0 -49
- package/src/translations/wasm.rs +0 -1
- package/tests/nodejs/chidori.test.js +0 -41
- package/tests/nodejs/nodeHandle.test.js +0 -15
- package/tests/python/test_chidori.py +0 -30
package/README.md
CHANGED
|
@@ -1,10 +1,117 @@
|
|
|
1
|
-
#
|
|
1
|
+
# chidori TypeScript SDK
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
Zero-dependency TypeScript client for a running `chidori serve` instance.
|
|
4
|
+
Uses the global `fetch` (Node 18+, browsers). Mirrors the Python SDK.
|
|
5
5
|
|
|
6
|
-
##
|
|
7
|
-
- npm run build-release
|
|
6
|
+
## Install
|
|
8
7
|
|
|
8
|
+
The package is published to npm as
|
|
9
|
+
[`@1kbirds/chidori`](https://www.npmjs.com/package/@1kbirds/chidori) (the
|
|
10
|
+
unscoped `chidori` npm name belongs to an unrelated project). Installing it
|
|
11
|
+
under the `chidori` alias keeps the historical import spelling working:
|
|
9
12
|
|
|
10
|
-
|
|
13
|
+
```bash
|
|
14
|
+
npm install chidori@npm:@1kbirds/chidori
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
```ts
|
|
18
|
+
import { AgentClient, Checkpoint } from "chidori";
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Or install under the scoped name directly — the Chidori runtime accepts both
|
|
22
|
+
`"chidori"` and `"@1kbirds/chidori"` as the SDK module specifier in agent
|
|
23
|
+
files:
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
npm install @1kbirds/chidori
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
To build from source instead:
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
cd sdk/typescript
|
|
33
|
+
npm install
|
|
34
|
+
npm run build
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Agent and tool authoring types are also exported:
|
|
38
|
+
|
|
39
|
+
```ts
|
|
40
|
+
import type { Chidori, ToolDefinition } from "chidori";
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Usage
|
|
44
|
+
|
|
45
|
+
```ts
|
|
46
|
+
import { AgentClient, Checkpoint, isSignalQueued } from "chidori";
|
|
47
|
+
|
|
48
|
+
const client = new AgentClient("http://localhost:8080");
|
|
49
|
+
|
|
50
|
+
// Run an agent
|
|
51
|
+
const session = await client.run({ document: "Rust is a systems language." });
|
|
52
|
+
console.log(session.output);
|
|
53
|
+
|
|
54
|
+
// Save and replay a checkpoint — zero LLM calls on replay
|
|
55
|
+
const checkpoint = await session.checkpoint();
|
|
56
|
+
const replayed = await client.replay(checkpoint);
|
|
57
|
+
|
|
58
|
+
// Durable TypeScript runs may include snapshot metadata in the checkpoint.
|
|
59
|
+
// The manifest is safe to inspect; raw VM snapshot bytes remain server-side.
|
|
60
|
+
if (checkpoint.snapshotManifest) {
|
|
61
|
+
console.log(checkpoint.snapshotManifest.pending?.kind);
|
|
62
|
+
console.log(checkpoint.snapshotManifest.abi.engine_fork);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const manifest = await client.getSnapshotManifest(session.id);
|
|
66
|
+
console.log(manifest.policy.typescript_imports);
|
|
67
|
+
|
|
68
|
+
// Live streaming: host calls, prompt stream deltas, then `done`
|
|
69
|
+
for await (const evt of client.stream({ document: "hi" })) {
|
|
70
|
+
if (evt.type === "call") console.log(evt.record.function);
|
|
71
|
+
if (evt.type === "prompt_delta" && evt.prompt_type === "progress") {
|
|
72
|
+
process.stdout.write(evt.delta);
|
|
73
|
+
}
|
|
74
|
+
if (evt.type === "done") console.log(evt.status, evt.output);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Paused sessions (from input())
|
|
78
|
+
if (session.status === "paused") {
|
|
79
|
+
const resumed = await client.resume(session.id, "yes");
|
|
80
|
+
console.log(resumed.output);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Multiplayer signals (from chidori.signal / pollSignal): deliver
|
|
84
|
+
// { name, payload?, from? } to a run.
|
|
85
|
+
const result = await client.signal(session.id, {
|
|
86
|
+
name: "review",
|
|
87
|
+
payload: { decision: "approve", notes: "LGTM" },
|
|
88
|
+
from: { kind: "human", id: "mara" },
|
|
89
|
+
});
|
|
90
|
+
if (isSignalQueued(result)) {
|
|
91
|
+
// run wasn't paused-waiting on this name → enqueued in the durable mailbox (202)
|
|
92
|
+
console.log("queued at delivery_seq", result.delivery_seq);
|
|
93
|
+
} else {
|
|
94
|
+
// run was paused-waiting on this name → resolved + resumed (200)
|
|
95
|
+
console.log(result.status, result.output);
|
|
96
|
+
}
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
See the top-level `sdk/python/chidori` for the Python equivalent.
|
|
100
|
+
|
|
101
|
+
## Snapshot-aware checkpoints
|
|
102
|
+
|
|
103
|
+
`Checkpoint` contains the replay call log plus optional `snapshotManifest`
|
|
104
|
+
metadata. The manifest records the runtime ABI, deterministic policy, source
|
|
105
|
+
hashes, pending host operation, and snapshot file name. Clients can use it to
|
|
106
|
+
display durable-resume state or diagnose why resume is blocked without handling
|
|
107
|
+
the raw `runtime.snapshot` VM bytes.
|
|
108
|
+
|
|
109
|
+
`client.replay(checkpoint)` uses the call log for deterministic replay. Durable
|
|
110
|
+
resume is exposed through `client.resume(sessionId, response)` for paused
|
|
111
|
+
sessions, recovering through persisted host-promise metadata and the replay
|
|
112
|
+
journal. Replay **is** the resume mechanism by design — the QuickJS live-VM
|
|
113
|
+
snapshot path was removed in #39, not merely deferred, so the manifest carries
|
|
114
|
+
journal/scaffold metadata rather than serialized VM bytes.
|
|
115
|
+
|
|
116
|
+
Use `client.getSnapshotManifest(sessionId)` when a UI needs only snapshot
|
|
117
|
+
metadata. The endpoint never returns the binary VM snapshot.
|
package/dist/agent.d.ts
ADDED
|
@@ -0,0 +1,487 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Type declarations for TypeScript agents executed inside the Chidori runtime.
|
|
3
|
+
*
|
|
4
|
+
* These are authoring-time types only. The runtime injects the concrete
|
|
5
|
+
* `chidori` host object when it evaluates an agent or tool module.
|
|
6
|
+
*/
|
|
7
|
+
export type AgentJson = null | boolean | number | string | AgentJson[] | {
|
|
8
|
+
[key: string]: AgentJson;
|
|
9
|
+
};
|
|
10
|
+
export type JsonObject = {
|
|
11
|
+
[key: string]: AgentJson;
|
|
12
|
+
};
|
|
13
|
+
export interface JsonSchema {
|
|
14
|
+
type?: "object" | "array" | "string" | "number" | "integer" | "boolean" | "null";
|
|
15
|
+
description?: string;
|
|
16
|
+
properties?: Record<string, JsonSchema>;
|
|
17
|
+
items?: JsonSchema;
|
|
18
|
+
required?: string[];
|
|
19
|
+
enum?: AgentJson[];
|
|
20
|
+
default?: AgentJson;
|
|
21
|
+
additionalProperties?: boolean | JsonSchema;
|
|
22
|
+
[keyword: string]: unknown;
|
|
23
|
+
}
|
|
24
|
+
export interface ToolDefinition {
|
|
25
|
+
name: string;
|
|
26
|
+
description: string;
|
|
27
|
+
parameters: JsonSchema & {
|
|
28
|
+
type: "object";
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
export type PromptStreamType = "progress" | "draft" | "subagent" | "final" | (string & {});
|
|
32
|
+
/** Provider prompt-cache lifetime for a cached prefix. */
|
|
33
|
+
export type CacheTtl = "5m" | "1h";
|
|
34
|
+
export interface PromptOptions {
|
|
35
|
+
type?: PromptStreamType;
|
|
36
|
+
system?: string;
|
|
37
|
+
model?: string;
|
|
38
|
+
maxTokens?: number;
|
|
39
|
+
max_tokens?: number;
|
|
40
|
+
maxTurns?: number;
|
|
41
|
+
max_turns?: number;
|
|
42
|
+
temperature?: number;
|
|
43
|
+
tools?: string[];
|
|
44
|
+
format?: "json" | (string & {});
|
|
45
|
+
stream?: boolean;
|
|
46
|
+
/**
|
|
47
|
+
* Prompt-cache posture. Defaults to on (`"5m"`): the runtime marks the
|
|
48
|
+
* stable request head (system, tools, conversation prefix) so providers
|
|
49
|
+
* bill repeated prefixes at the cached rate. `false` disables marking for
|
|
50
|
+
* this call; `"1h"` (or `{ ttl: "1h" }`) requests the extended TTL.
|
|
51
|
+
* Caching never changes a response — only how it is billed.
|
|
52
|
+
*/
|
|
53
|
+
cache?: boolean | CacheTtl | {
|
|
54
|
+
ttl?: CacheTtl;
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
/** Structured response from `Context.respond()` — mirrors the provider turn. */
|
|
58
|
+
export interface LlmResponseJson {
|
|
59
|
+
content: string;
|
|
60
|
+
blocks: AgentJson[];
|
|
61
|
+
tool_calls: {
|
|
62
|
+
id: string;
|
|
63
|
+
name: string;
|
|
64
|
+
input: AgentJson;
|
|
65
|
+
}[];
|
|
66
|
+
stop_reason: string;
|
|
67
|
+
input_tokens: number;
|
|
68
|
+
output_tokens: number;
|
|
69
|
+
cache_creation_tokens: number;
|
|
70
|
+
cache_read_tokens: number;
|
|
71
|
+
}
|
|
72
|
+
/** Options for `Context.compact()` — explicit, opt-in window compaction. */
|
|
73
|
+
export interface CompactOptions {
|
|
74
|
+
/** How many of the newest conversation turns to keep verbatim (default 2). */
|
|
75
|
+
keepTurns?: number;
|
|
76
|
+
/**
|
|
77
|
+
* Skip compaction (pure no-op, no host call) while `estimateTokens()` is at
|
|
78
|
+
* or under this budget — lets a loop call `compact()` unconditionally.
|
|
79
|
+
*/
|
|
80
|
+
budgetTokens?: number;
|
|
81
|
+
/** Model for the summarization prompt (defaults like `prompt()`). */
|
|
82
|
+
model?: string;
|
|
83
|
+
/** System instructions for the summarizer (a faithful-brief default). */
|
|
84
|
+
instructions?: string;
|
|
85
|
+
/** `maxTokens` for the summarization prompt. */
|
|
86
|
+
maxTokens?: number;
|
|
87
|
+
/** Cache posture for the summarization prompt (see `PromptOptions.cache`). */
|
|
88
|
+
cache?: boolean | CacheTtl | {
|
|
89
|
+
ttl?: CacheTtl;
|
|
90
|
+
};
|
|
91
|
+
/** TTL of the fresh cache breakpoint placed on the summary (default "5m"). */
|
|
92
|
+
ttl?: CacheTtl;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* An immutable, content-addressed, turn-structured prompt context.
|
|
96
|
+
*
|
|
97
|
+
* Builder methods return a NEW context that structurally shares this one's
|
|
98
|
+
* segments — `base.user("a")` and `base.user("b")` are independent and share
|
|
99
|
+
* `base`'s prefix — which keeps cache prefixes stable and makes forks cheap.
|
|
100
|
+
* Only `prompt()` / `respond()` perform a durable host call.
|
|
101
|
+
*
|
|
102
|
+
* ```ts
|
|
103
|
+
* const base = chidori.context()
|
|
104
|
+
* .system("You are a policy analyst.")
|
|
105
|
+
* .doc("corpus", corpusText)
|
|
106
|
+
* .cacheBreakpoint("1h");
|
|
107
|
+
* let ctx = base;
|
|
108
|
+
* for (const q of questions) {
|
|
109
|
+
* ctx = ctx.user(q);
|
|
110
|
+
* const { text, context } = await ctx.prompt();
|
|
111
|
+
* ctx = context; // assistant turn appended, prefix still shared
|
|
112
|
+
* }
|
|
113
|
+
* ```
|
|
114
|
+
*/
|
|
115
|
+
export interface Context {
|
|
116
|
+
system(text: string): Context;
|
|
117
|
+
/** Expose registered tools (by name, resolved like `prompt({ tools })`). */
|
|
118
|
+
tools(names: string[]): Context;
|
|
119
|
+
/** A large stable reference block, labelled for the trace. */
|
|
120
|
+
doc(label: string, text: string): Context;
|
|
121
|
+
user(text: string): Context;
|
|
122
|
+
assistant(text: string): Context;
|
|
123
|
+
toolResult(id: string, content: string, isError?: boolean): Context;
|
|
124
|
+
/**
|
|
125
|
+
* Freeze everything appended so far as a cacheable prefix (one provider
|
|
126
|
+
* cache breakpoint). Providers cap breakpoints, so marks are coalesced —
|
|
127
|
+
* latest wins. Most authors never need this: stable heads are auto-marked.
|
|
128
|
+
*/
|
|
129
|
+
cacheBreakpoint(ttl?: CacheTtl): Context;
|
|
130
|
+
/** Send this context; returns the text and the context extended with the
|
|
131
|
+
* assistant turn (including any internal tool-use exchange). */
|
|
132
|
+
prompt(options?: PromptOptions): Promise<{
|
|
133
|
+
text: string;
|
|
134
|
+
context: Context;
|
|
135
|
+
}>;
|
|
136
|
+
/** Single structured turn for author-driven tool loops. */
|
|
137
|
+
respond(options?: PromptOptions): Promise<{
|
|
138
|
+
response: LlmResponseJson;
|
|
139
|
+
context: Context;
|
|
140
|
+
}>;
|
|
141
|
+
/**
|
|
142
|
+
* Summarize the older conversation turns into one durable summary segment
|
|
143
|
+
* (via a recorded `prompt` host call, so it replays deterministically) and
|
|
144
|
+
* return a new context: stable head + summary + fresh cache breakpoint +
|
|
145
|
+
* the kept newest turns. Never automatic — compaction changes what the
|
|
146
|
+
* model sees, so it is always an explicit author decision. Returns this
|
|
147
|
+
* context unchanged (without a host call) when there is nothing to compact
|
|
148
|
+
* or the context is within `budgetTokens`.
|
|
149
|
+
*/
|
|
150
|
+
compact(options?: CompactOptions): Promise<Context>;
|
|
151
|
+
/** Stable content hash of the request this context would assemble. */
|
|
152
|
+
digest(options?: PromptOptions): string;
|
|
153
|
+
/** Rough local token estimate for window budgeting. */
|
|
154
|
+
estimateTokens(): number;
|
|
155
|
+
}
|
|
156
|
+
/** One recorded exchange in a {@link Conversation} transcript. */
|
|
157
|
+
export interface ConversationTurn {
|
|
158
|
+
role: "user" | "assistant";
|
|
159
|
+
text: string;
|
|
160
|
+
}
|
|
161
|
+
/** Options for `chidori.conversation()`. */
|
|
162
|
+
export interface ConversationOptions {
|
|
163
|
+
/** System prompt — frozen once as the conversation's cacheable prefix. */
|
|
164
|
+
system?: string;
|
|
165
|
+
/** Tool names available on every turn (resolved like `prompt({ tools })`). */
|
|
166
|
+
tools?: string[];
|
|
167
|
+
/** Default stream label for each turn's prompt (default `"final"`). */
|
|
168
|
+
type?: PromptStreamType;
|
|
169
|
+
/** Default model for each turn (a per-turn override still wins). */
|
|
170
|
+
model?: string;
|
|
171
|
+
/** Default output token cap for each turn. */
|
|
172
|
+
maxTokens?: number;
|
|
173
|
+
/** Default sampling temperature for each turn. */
|
|
174
|
+
temperature?: number;
|
|
175
|
+
/** Default cache posture for each turn (see {@link PromptOptions.cache}). */
|
|
176
|
+
cache?: boolean | CacheTtl | {
|
|
177
|
+
ttl?: CacheTtl;
|
|
178
|
+
};
|
|
179
|
+
/** TTL of the cache breakpoint frozen over the system/tools head. */
|
|
180
|
+
cacheTtl?: CacheTtl;
|
|
181
|
+
/**
|
|
182
|
+
* Opt-in window management: when set, each turn first runs the same budgeted
|
|
183
|
+
* `Context.compact()` — a pure no-op until the running tail exceeds budget,
|
|
184
|
+
* then the older turns fold into one recorded summary segment.
|
|
185
|
+
*/
|
|
186
|
+
compact?: CompactOptions;
|
|
187
|
+
}
|
|
188
|
+
/** Options for `Conversation.loop()` — an interactive `input()`-driven loop. */
|
|
189
|
+
export interface ConversationLoopOptions {
|
|
190
|
+
/** Prompt shown to the human each turn (or a function of the turn index). */
|
|
191
|
+
prompt?: string | ((turn: number) => string);
|
|
192
|
+
/** Extra options forwarded to `chidori.input()` (defaults to `{ type: "message" }`). */
|
|
193
|
+
inputOptions?: InputOptions;
|
|
194
|
+
/** Words that end the loop, case-insensitive (default `["exit", "quit"]`). */
|
|
195
|
+
exit?: string | string[];
|
|
196
|
+
/** Hard cap on the number of exchanges before returning. */
|
|
197
|
+
maxTurns?: number;
|
|
198
|
+
/** Skip blank input lines instead of sending them (default `true`). */
|
|
199
|
+
skipEmpty?: boolean;
|
|
200
|
+
/** Per-turn prompt options applied to every `say()` in the loop. */
|
|
201
|
+
turn?: PromptOptions;
|
|
202
|
+
/** Called with the assistant reply (and the user message) after each turn. */
|
|
203
|
+
onReply?: (reply: string, message: string) => void | Promise<void>;
|
|
204
|
+
/** Return `true` after a turn to end the loop (checked after `onReply`). */
|
|
205
|
+
until?: (message: string, reply: string) => boolean;
|
|
206
|
+
}
|
|
207
|
+
/**
|
|
208
|
+
* A small stateful wrapper over {@link Context} for the most common shape — a
|
|
209
|
+
* multi-turn chat assistant. It owns the running context (system + tools frozen
|
|
210
|
+
* as a cacheable prefix) and threads each turn through it, so you write
|
|
211
|
+
* `chat.say(message)` instead of re-plumbing `ctx = (await
|
|
212
|
+
* ctx.user(message).prompt()).context` by hand. Every turn is still one durable
|
|
213
|
+
* `prompt`/`respond` host call that replays for free.
|
|
214
|
+
*
|
|
215
|
+
* ```ts
|
|
216
|
+
* const chat = chidori.conversation({ system: "You are concise." });
|
|
217
|
+
* const a = await chat.say("Hi, who are you?");
|
|
218
|
+
* const b = await chat.say("What can you help with?");
|
|
219
|
+
* // or drive it interactively:
|
|
220
|
+
* const transcript = await chat.loop({ prompt: "you>" });
|
|
221
|
+
* ```
|
|
222
|
+
*/
|
|
223
|
+
export interface Conversation {
|
|
224
|
+
/** The underlying immutable context, for dropping to the lower-level API. */
|
|
225
|
+
readonly context: Context;
|
|
226
|
+
/** Number of completed exchanges (user+assistant pairs) so far. */
|
|
227
|
+
readonly length: number;
|
|
228
|
+
/** The transcript so far as plain `{ role, text }` entries. */
|
|
229
|
+
history(): ConversationTurn[];
|
|
230
|
+
/** Send one user message; resolves to the assistant's reply text. */
|
|
231
|
+
say(message: string, options?: PromptOptions): Promise<string>;
|
|
232
|
+
/**
|
|
233
|
+
* Like `say()`, but resolves to the structured response (`tool_calls`,
|
|
234
|
+
* `blocks`) for author-driven tool loops. Append tool results with
|
|
235
|
+
* `chat.context.toolResult(...)`, then call `say()` again.
|
|
236
|
+
*/
|
|
237
|
+
respond(message: string, options?: PromptOptions): Promise<LlmResponseJson>;
|
|
238
|
+
/**
|
|
239
|
+
* Drive an interactive loop: read a human message via `chidori.input()`
|
|
240
|
+
* (terminal stdin under `chidori run`, a paused session resume under `chidori
|
|
241
|
+
* serve`), reply with `say()`, and repeat until the user types an exit word
|
|
242
|
+
* or `until` returns true. Resolves to the full transcript.
|
|
243
|
+
*/
|
|
244
|
+
loop(options?: ConversationLoopOptions): Promise<ConversationTurn[]>;
|
|
245
|
+
}
|
|
246
|
+
export interface InputOptions {
|
|
247
|
+
type?: string;
|
|
248
|
+
default?: string;
|
|
249
|
+
choices?: string[];
|
|
250
|
+
}
|
|
251
|
+
/**
|
|
252
|
+
* Who delivered a signal. `kind` distinguishes a human participant from a peer
|
|
253
|
+
* agent; `id` is the participant identity; `runId` is set when an agent sends
|
|
254
|
+
* (its own run id), so agent-to-agent coordination is attributable in the trace.
|
|
255
|
+
*/
|
|
256
|
+
export interface SignalSender {
|
|
257
|
+
kind: "human" | "agent";
|
|
258
|
+
id: string;
|
|
259
|
+
runId?: string;
|
|
260
|
+
}
|
|
261
|
+
/**
|
|
262
|
+
* A named message delivered into a run mid-flight (`docs/signals.md` §6.1). The
|
|
263
|
+
* inverse of `input()`: an outside party (human or agent) pushes
|
|
264
|
+
* `{ name, payload, from }` at an agent-declared listen point. Every signal is
|
|
265
|
+
* recorded in the call log, so the multiplayer session replays deterministically.
|
|
266
|
+
*/
|
|
267
|
+
export interface Signal<T = AgentJson> {
|
|
268
|
+
name: string;
|
|
269
|
+
payload: T;
|
|
270
|
+
from: SignalSender;
|
|
271
|
+
}
|
|
272
|
+
export interface SignalOptions {
|
|
273
|
+
/**
|
|
274
|
+
* Resolve to a {@link SignalTimeout} sentinel after this many milliseconds
|
|
275
|
+
* instead of waiting forever. The deadline is enforced by the supervising
|
|
276
|
+
* server while the run idles; the recorded result (signal or sentinel)
|
|
277
|
+
* replays deterministically. Discriminate with `"timedOut" in result`.
|
|
278
|
+
*/
|
|
279
|
+
timeoutMs?: number;
|
|
280
|
+
}
|
|
281
|
+
/**
|
|
282
|
+
* 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`.
|
|
286
|
+
*/
|
|
287
|
+
export interface SignalTimeout {
|
|
288
|
+
name: string | null;
|
|
289
|
+
payload: null;
|
|
290
|
+
from: null;
|
|
291
|
+
timedOut: true;
|
|
292
|
+
}
|
|
293
|
+
export interface ParallelOptions {
|
|
294
|
+
concurrency?: number;
|
|
295
|
+
}
|
|
296
|
+
/**
|
|
297
|
+
* One `chidori.branch` variant (`docs/branching-execution.md` §6.1). A branch
|
|
298
|
+
* runs its own continuation source module from the parent's anchored state —
|
|
299
|
+
* not a re-run of the parent agent — so `source` is required.
|
|
300
|
+
*/
|
|
301
|
+
export interface BranchVariant {
|
|
302
|
+
/** Branch label, shown in outcomes and the trace. Defaults to `branch-<k>`. */
|
|
303
|
+
label?: string;
|
|
304
|
+
/** Branch source module path, resolved like `callAgent` paths. */
|
|
305
|
+
source: string;
|
|
306
|
+
/** State handed to the branch as its run input. Defaults to `{}`. */
|
|
307
|
+
input?: AgentJson;
|
|
308
|
+
}
|
|
309
|
+
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> {
|
|
312
|
+
label: string;
|
|
313
|
+
/**
|
|
314
|
+
* `<parent run id>-op<branch seq>-branch-<k>` — identifies the branch
|
|
315
|
+
* sub-run, including for out-of-band `chidori branch-resume` /
|
|
316
|
+
* `branch-rerun` against its persisted store.
|
|
317
|
+
*/
|
|
318
|
+
branchId: string;
|
|
319
|
+
status: BranchStatus;
|
|
320
|
+
/** The branch's output, when `status` is `"completed"`. */
|
|
321
|
+
output?: T;
|
|
322
|
+
/** What the branch is waiting on, when `status` is `"paused"`. */
|
|
323
|
+
pendingPrompt?: string;
|
|
324
|
+
/** The failure message, when `status` is `"failed"`. */
|
|
325
|
+
error?: string;
|
|
326
|
+
}
|
|
327
|
+
export interface BranchOptions {
|
|
328
|
+
/**
|
|
329
|
+
* Maximum branches running live at once (cost cap). Defaults to 1 —
|
|
330
|
+
* sequential. Higher values run variants in concurrent waves; outcome
|
|
331
|
+
* order always follows variant order.
|
|
332
|
+
*/
|
|
333
|
+
concurrency?: number;
|
|
334
|
+
}
|
|
335
|
+
export interface RetryOptions {
|
|
336
|
+
attempts?: number;
|
|
337
|
+
delayMs?: number;
|
|
338
|
+
backoff?: "fixed" | "exponential";
|
|
339
|
+
}
|
|
340
|
+
export interface TryCallResult<T> {
|
|
341
|
+
ok: boolean;
|
|
342
|
+
value?: T;
|
|
343
|
+
error?: string;
|
|
344
|
+
}
|
|
345
|
+
export interface TemplateOptions {
|
|
346
|
+
source?: "file" | "inline";
|
|
347
|
+
}
|
|
348
|
+
export type MemoryAction = "get" | "set" | "delete" | "list" | "clear";
|
|
349
|
+
export type WorkspaceFileStatus = "complete" | "writing" | "failed";
|
|
350
|
+
export interface WorkspaceEntry {
|
|
351
|
+
path: string;
|
|
352
|
+
status: WorkspaceFileStatus;
|
|
353
|
+
sha256: string;
|
|
354
|
+
bytes: number;
|
|
355
|
+
language?: string | null;
|
|
356
|
+
attempt?: number | null;
|
|
357
|
+
updatedAt?: string | null;
|
|
358
|
+
}
|
|
359
|
+
export interface WorkspaceListOptions {
|
|
360
|
+
completeOnly?: boolean;
|
|
361
|
+
}
|
|
362
|
+
export interface WorkspaceWriteOptions {
|
|
363
|
+
language?: string;
|
|
364
|
+
}
|
|
365
|
+
export interface WorkspaceHost {
|
|
366
|
+
list(options?: WorkspaceListOptions): Promise<WorkspaceEntry[]>;
|
|
367
|
+
read(path: string): Promise<string>;
|
|
368
|
+
write(path: string, content: string, options?: WorkspaceWriteOptions): Promise<WorkspaceEntry>;
|
|
369
|
+
delete(path: string, reason?: string): Promise<void>;
|
|
370
|
+
remove(path: string, reason?: string): Promise<void>;
|
|
371
|
+
manifest(): Promise<AgentJson>;
|
|
372
|
+
}
|
|
373
|
+
export type TypeScriptImportPolicy = "none" | "relative" | "project";
|
|
374
|
+
export type DatePolicy = "disabled" | "fixed" | "host";
|
|
375
|
+
export type RandomPolicy = "disabled" | "seeded" | "host";
|
|
376
|
+
export type MapSetSnapshotPolicy = "reject" | "serialize";
|
|
377
|
+
export interface RuntimePolicyConfig {
|
|
378
|
+
typescript?: {
|
|
379
|
+
imports?: TypeScriptImportPolicy;
|
|
380
|
+
};
|
|
381
|
+
runtime?: {
|
|
382
|
+
date?: DatePolicy;
|
|
383
|
+
random?: RandomPolicy;
|
|
384
|
+
};
|
|
385
|
+
snapshot?: {
|
|
386
|
+
mapsSets?: MapSetSnapshotPolicy;
|
|
387
|
+
};
|
|
388
|
+
}
|
|
389
|
+
export interface Chidori {
|
|
390
|
+
workspace: WorkspaceHost;
|
|
391
|
+
/** Start an immutable multi-turn prompt context (optionally seeded). */
|
|
392
|
+
context(seed?: {
|
|
393
|
+
system?: string;
|
|
394
|
+
tools?: string[];
|
|
395
|
+
}): Context;
|
|
396
|
+
/**
|
|
397
|
+
* Start a multi-turn chat assistant — a stateful wrapper over `context()`
|
|
398
|
+
* that owns the running dialogue. Send turns with `chat.say(message)` or drive
|
|
399
|
+
* an interactive `input()` loop with `chat.loop()`.
|
|
400
|
+
*/
|
|
401
|
+
conversation(options?: ConversationOptions): Conversation;
|
|
402
|
+
prompt(text: string, options?: PromptOptions): Promise<string>;
|
|
403
|
+
input(message: string, options?: InputOptions): Promise<string>;
|
|
404
|
+
/**
|
|
405
|
+
* Pause at a named listen point until a matching signal is delivered (or one
|
|
406
|
+
* is already queued in the durable mailbox), then resolve to
|
|
407
|
+
* `{ name, payload, from }`. The inverse of `input()`: the run idles cheaply
|
|
408
|
+
* on disk and an outside party delivers via `POST /sessions/{id}/signal`.
|
|
409
|
+
*/
|
|
410
|
+
signal<T = AgentJson>(name: string): Promise<Signal<T>>;
|
|
411
|
+
signal<T = AgentJson>(name: string, options: SignalOptions): Promise<Signal<T> | SignalTimeout>;
|
|
412
|
+
/**
|
|
413
|
+
* Non-blocking: consume a queued signal of this name if present, else resolve
|
|
414
|
+
* to `null`. Records the result (value or null) at this seq so replay is
|
|
415
|
+
* deterministic.
|
|
416
|
+
*/
|
|
417
|
+
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
|
+
/**
|
|
427
|
+
* Durable value checkpoint: run `fn` once and journal its JSON-serializable
|
|
428
|
+
* result; on replay/resume the recorded value (or error) is returned without
|
|
429
|
+
* re-running `fn`. Wrap expensive deterministic computation in a step so a
|
|
430
|
+
* resumed run does not re-pay it. The callback must be pure, synchronous
|
|
431
|
+
* compute — host effects (`chidori.*`), captured randomness, filesystem
|
|
432
|
+
* writes, timers, and async callbacks are refused inside a step.
|
|
433
|
+
*/
|
|
434
|
+
step<T extends AgentJson = AgentJson>(name: string, fn: () => T): Promise<T>;
|
|
435
|
+
callAgent<TInput extends AgentJson = JsonObject, TOutput extends AgentJson = AgentJson>(path: string, input?: TInput): Promise<TOutput>;
|
|
436
|
+
/**
|
|
437
|
+
* Fork the run into one sub-run per variant from the current anchored state
|
|
438
|
+
* (the VFS plus each variant's explicit `input`), run each variant's own
|
|
439
|
+
* source module, and return every outcome so the agent can compare and pick.
|
|
440
|
+
* The whole fan-out is one recorded durable call: a replay of this run
|
|
441
|
+
* returns the outcomes from cache without re-running the branches.
|
|
442
|
+
*/
|
|
443
|
+
branch<T extends AgentJson = AgentJson>(variants: BranchVariant[], options?: BranchOptions): Promise<BranchOutcome<T>[]>;
|
|
444
|
+
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>>;
|
|
450
|
+
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>;
|
|
454
|
+
}
|
|
455
|
+
export type AgentFunction<TInput extends AgentJson = JsonObject, TOutput extends AgentJson = AgentJson> = (input: TInput) => TOutput | Promise<TOutput>;
|
|
456
|
+
export type ToolFunction<TArgs extends JsonObject = JsonObject, TResult extends AgentJson = AgentJson> = (args: TArgs) => TResult | Promise<TResult>;
|
|
457
|
+
/**
|
|
458
|
+
* The chidori host object — the durable surface your agents and tools call
|
|
459
|
+
* (`chidori.log`, `chidori.prompt`, `chidori.tool`, `chidori.input`, …).
|
|
460
|
+
*
|
|
461
|
+
* Import it for typed access; the runtime strips this import and supplies the
|
|
462
|
+
* real object at execution time, so there's no actual module dependency (and no
|
|
463
|
+
* need for a `(input, chidori)` second parameter):
|
|
464
|
+
*
|
|
465
|
+
* ```ts
|
|
466
|
+
* import { chidori, run } from "chidori";
|
|
467
|
+
* run(async (input: { topic: string }) => {
|
|
468
|
+
* await chidori.log("starting", { topic: input.topic });
|
|
469
|
+
* return { ok: true };
|
|
470
|
+
* });
|
|
471
|
+
* ```
|
|
472
|
+
*
|
|
473
|
+
* Accessing it from a plain import outside the runtime throws.
|
|
474
|
+
*/
|
|
475
|
+
export declare const chidori: Chidori;
|
|
476
|
+
/**
|
|
477
|
+
* Define the agent entrypoint. Call it once at the top level of an agent module
|
|
478
|
+
* with your handler; the runtime invokes the handler with the run input and
|
|
479
|
+
* uses its return value as the output. This replaces the old "export a function
|
|
480
|
+
* named `agent`" convention.
|
|
481
|
+
*
|
|
482
|
+
* ```ts
|
|
483
|
+
* import { run } from "chidori";
|
|
484
|
+
* run(async (input) => ({ greeting: `hello ${input.name}` }));
|
|
485
|
+
* ```
|
|
486
|
+
*/
|
|
487
|
+
export declare function run<TInput extends AgentJson = JsonObject, TOutput extends AgentJson = AgentJson>(handler: AgentFunction<TInput, TOutput>): void;
|
package/dist/agent.js
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Type declarations for TypeScript agents executed inside the Chidori runtime.
|
|
3
|
+
*
|
|
4
|
+
* These are authoring-time types only. The runtime injects the concrete
|
|
5
|
+
* `chidori` host object when it evaluates an agent or tool module.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* The chidori host object — the durable surface your agents and tools call
|
|
9
|
+
* (`chidori.log`, `chidori.prompt`, `chidori.tool`, `chidori.input`, …).
|
|
10
|
+
*
|
|
11
|
+
* Import it for typed access; the runtime strips this import and supplies the
|
|
12
|
+
* real object at execution time, so there's no actual module dependency (and no
|
|
13
|
+
* need for a `(input, chidori)` second parameter):
|
|
14
|
+
*
|
|
15
|
+
* ```ts
|
|
16
|
+
* import { chidori, run } from "chidori";
|
|
17
|
+
* run(async (input: { topic: string }) => {
|
|
18
|
+
* await chidori.log("starting", { topic: input.topic });
|
|
19
|
+
* return { ok: true };
|
|
20
|
+
* });
|
|
21
|
+
* ```
|
|
22
|
+
*
|
|
23
|
+
* Accessing it from a plain import outside the runtime throws.
|
|
24
|
+
*/
|
|
25
|
+
export const chidori = new Proxy({}, {
|
|
26
|
+
get(_target, prop) {
|
|
27
|
+
throw new Error(`chidori.${String(prop)} is only available inside the chidori runtime; ` +
|
|
28
|
+
`this import is replaced when an agent runs under chidori.`);
|
|
29
|
+
},
|
|
30
|
+
});
|
|
31
|
+
/**
|
|
32
|
+
* Define the agent entrypoint. Call it once at the top level of an agent module
|
|
33
|
+
* with your handler; the runtime invokes the handler with the run input and
|
|
34
|
+
* uses its return value as the output. This replaces the old "export a function
|
|
35
|
+
* named `agent`" convention.
|
|
36
|
+
*
|
|
37
|
+
* ```ts
|
|
38
|
+
* import { run } from "chidori";
|
|
39
|
+
* run(async (input) => ({ greeting: `hello ${input.name}` }));
|
|
40
|
+
* ```
|
|
41
|
+
*/
|
|
42
|
+
export function run(handler) {
|
|
43
|
+
void handler;
|
|
44
|
+
throw new Error("run() is only available inside the chidori runtime; this import is " +
|
|
45
|
+
"replaced when an agent runs under chidori.");
|
|
46
|
+
}
|