agent.libx.js 0.86.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,255 @@
1
+ import { IFilesystem, CommandExecutor } from '@livx.cc/wcli/core';
2
+
3
+ /**
4
+ * Wire types mirroring ai.libx.js (OpenAI-style chat). We type the transport
5
+ * structurally via `ChatLike`, so an ai.libx.js `AIClient` is a drop-in — and a
6
+ * `FakeAIClient` works in tests — with no hard runtime dependency on ai.libx.js.
7
+ */
8
+ type Role = 'system' | 'user' | 'assistant' | 'tool';
9
+ interface ToolCall {
10
+ id: string;
11
+ type: 'function';
12
+ function: {
13
+ name: string;
14
+ arguments: string;
15
+ };
16
+ }
17
+ /** One part of a multimodal message (mirrors ai.libx.js ContentPart) — text or an image URL/data-URI. */
18
+ interface ContentPart {
19
+ type: 'text' | 'image_url';
20
+ text?: string;
21
+ image_url?: {
22
+ url: string;
23
+ };
24
+ }
25
+ /** A message's content is either plain text or an array of multimodal parts (images + text). */
26
+ type MessageContent = string | ContentPart[];
27
+ interface Message {
28
+ role: Role;
29
+ content: MessageContent;
30
+ name?: string;
31
+ tool_call_id?: string;
32
+ tool_calls?: ToolCall[];
33
+ }
34
+ /** Flatten any message content to its text (string as-is; parts → concatenated text) — for length
35
+ * estimation, summaries, and display. Non-text parts (images) contribute a short placeholder. */
36
+ declare function contentText(content: MessageContent | undefined): string;
37
+ /** Build an image content part from a data-URI or http(s) URL. */
38
+ declare function imagePart(url: string): ContentPart;
39
+ interface Tool {
40
+ type: 'function';
41
+ function: {
42
+ name: string;
43
+ description?: string;
44
+ parameters: object;
45
+ };
46
+ }
47
+ interface ChatResponse {
48
+ content: string;
49
+ finishReason?: string;
50
+ toolCalls?: ToolCall[];
51
+ model?: string;
52
+ usage?: {
53
+ promptTokens: number;
54
+ completionTokens: number;
55
+ totalTokens: number;
56
+ };
57
+ }
58
+ /**
59
+ * One incremental event from a streamed `chat({stream:true})` call — mirrors
60
+ * ai.libx.js's `StreamChunk` (OpenAI-style): each chunk carries a `content`
61
+ * text delta; the terminal chunk carries `finishReason` and the accumulated
62
+ * `toolCalls`. Consuming the stream and folding the deltas reconstructs the
63
+ * same `ChatResponse` the non-stream path returns.
64
+ */
65
+ interface StreamChunk {
66
+ content: string;
67
+ finishReason?: string;
68
+ index?: number;
69
+ toolCalls?: ToolCall[];
70
+ reasoningContent?: string;
71
+ usage?: {
72
+ promptTokens: number;
73
+ completionTokens: number;
74
+ totalTokens: number;
75
+ };
76
+ }
77
+ interface ChatOptions {
78
+ model: string;
79
+ messages: Message[];
80
+ tools?: Tool[];
81
+ toolChoice?: unknown;
82
+ stream?: boolean;
83
+ /** Cancel the request/stream. Forwarded to providers that honor it; the Agent also stops consuming on abort. */
84
+ signal?: AbortSignal;
85
+ [k: string]: unknown;
86
+ }
87
+ /** Minimal shape of an ai.libx.js AIClient that the Agent drives. */
88
+ interface ChatLike {
89
+ chat(options: ChatOptions): Promise<ChatResponse | AsyncIterable<StreamChunk>>;
90
+ }
91
+
92
+ /** A single planning item. `in_progress` should mark exactly the one task currently being worked on. */
93
+ interface TodoItem {
94
+ content: string;
95
+ status: 'pending' | 'in_progress' | 'completed';
96
+ }
97
+ /**
98
+ * `TodoWrite` tool — the agent's planning scratchpad for multi-step tasks.
99
+ * Use to lay out steps up front and keep them current (mark one `in_progress`,
100
+ * flip finished ones to `completed`). Each call REPLACES the whole list.
101
+ */
102
+ declare const todoWriteTool: AgentTool;
103
+
104
+ /**
105
+ * Sandbox background jobs — the in-process twin of `ShellJobRegistry` (src/tools.shell.ts), but for work
106
+ * that runs *inside* the sandbox rather than as an OS child process. Backs `bash({background:true})` and
107
+ * (later) background sub-agents.
108
+ *
109
+ * IMPORTANT — this is concurrency via async-I/O interleaving, NOT parallelism. The runtime is single
110
+ * threaded: a CPU-bound JS command still blocks. Real overlap only happens when a job *awaits* — i.e. a
111
+ * remote VFS backend (BodDbFilesystem, network per read) or a sub-agent (model calls). Over a pure in-mem
112
+ * MemFilesystem, backgrounding a fast command buys nothing (the tool description says so).
113
+ *
114
+ * The registry is kind-AGNOSTIC: it tracks any async producer's lifecycle + tail output. Overlay isolation
115
+ * and commit-on-success are the *producer's* responsibility (IoC), so the registry stays a pure tracker.
116
+ */
117
+ type JobStatus = 'running' | 'done' | 'error' | 'killed';
118
+ /** A producer: does the actual work, may stream progress via `onChunk`, returns its final text. */
119
+ interface JobFn {
120
+ (ctx: {
121
+ signal: AbortSignal;
122
+ onChunk: (s: string) => void;
123
+ }): Promise<string>;
124
+ }
125
+ /** Per-session registry of in-process background jobs. Bounded tail buffer; abortable; drainable at turn end. */
126
+ declare class SandboxJobRegistry {
127
+ private maxBuffer;
128
+ private jobs;
129
+ private seq;
130
+ constructor(maxBuffer?: number);
131
+ /** Start `fn` in the background and return a job id immediately (does not await completion). */
132
+ start(fn: JobFn, opts?: {
133
+ kind?: string;
134
+ label?: string;
135
+ }): string;
136
+ /** Tail output for a job (null = no such job, '' = job exists but no output yet). */
137
+ output(id: string): string | null;
138
+ status(id: string): {
139
+ status: JobStatus;
140
+ bytes: number;
141
+ } | null;
142
+ list(): Array<{
143
+ id: string;
144
+ kind: string;
145
+ label: string;
146
+ status: JobStatus;
147
+ }>;
148
+ /** Signal cancel (the producer decides what abort means — e.g. skip the overlay commit). */
149
+ kill(id: string): boolean;
150
+ killAll(): void;
151
+ /** Await every still-running job. Called at turn end so a job's committed writes aren't lost from view
152
+ * just because the model didn't poll it. Returns the ids that were still running when drain began. */
153
+ drain(): Promise<string[]>;
154
+ }
155
+ /** Companion tools (JobOutput / JobStatus / JobKill) over a SandboxJobRegistry. */
156
+ declare function makeJobTools(registry: SandboxJobRegistry): AgentTool[];
157
+
158
+ /** A structured multiple-choice question the model can pose to a human. */
159
+ interface UserQuestion {
160
+ question: string;
161
+ header?: string;
162
+ options: {
163
+ label: string;
164
+ description?: string;
165
+ }[];
166
+ multiSelect?: boolean;
167
+ }
168
+ /**
169
+ * The host / human-in-the-loop seam (the "third seam" beyond LLM + filesystem).
170
+ * Injected per host: a CLI reads stdin, a browser renders a dialog, edge/headless
171
+ * omits it. Unifies user-questions, permission/plan approvals, and notifications.
172
+ */
173
+ type HostEvent = {
174
+ kind: 'text_delta';
175
+ message: string;
176
+ } | {
177
+ kind: 'thinking_delta';
178
+ message: string;
179
+ } | {
180
+ kind: 'tool_use';
181
+ id: string;
182
+ name: string;
183
+ input: unknown;
184
+ } | {
185
+ kind: 'tool_result';
186
+ id: string;
187
+ output: string;
188
+ isError?: boolean;
189
+ } | {
190
+ kind: 'tool_result_image';
191
+ id: string;
192
+ dataUrl: string;
193
+ } | {
194
+ kind: string;
195
+ message: string;
196
+ data?: unknown;
197
+ };
198
+ interface HostBridge {
199
+ /** Ask the user a structured question; resolve to the chosen label(s) / free text. */
200
+ ask?(q: UserQuestion): Promise<string>;
201
+ /** Request approval for a sensitive action (permission 'ask' / plan approval). */
202
+ confirm?(prompt: string, meta?: {
203
+ tool: string;
204
+ input: unknown;
205
+ }): Promise<boolean>;
206
+ /** Emit a progress / notification event to the host UI (non-blocking). */
207
+ notify?(event: HostEvent): void;
208
+ }
209
+ interface ToolContext {
210
+ fs: IFilesystem;
211
+ exec: CommandExecutor;
212
+ /** path -> content snapshot at last Read/Edit; powers the read-before-edit staleness guard. */
213
+ readState: Map<string, string>;
214
+ /** optional host interaction channel; absent => autonomous/headless. */
215
+ host?: HostBridge;
216
+ /** optional run-cancellation signal (mirrors AgentOptions.signal); lets abort-aware tools
217
+ * (e.g. the real shell) kill in-flight work when the run is cancelled. */
218
+ signal?: AbortSignal;
219
+ /** the agent's working todo list (TodoWrite planning aid); replaced wholesale per call. */
220
+ todos: TodoItem[];
221
+ /** optional syntax guardrail: if set, write-class tools refuse to persist a broken result. */
222
+ lint?: (path: string, content: string) => string | null;
223
+ /** optional model handle for tools that run their own LLM pass (e.g. Review, a self-critique).
224
+ * Populated by the Agent from its own ai/model; absent => such tools degrade to a no-op. */
225
+ ai?: ChatLike;
226
+ model?: string;
227
+ /** optional sandbox background-job registry; enables `bash({background:true})`. Absent => no backgrounding. */
228
+ jobs?: SandboxJobRegistry;
229
+ }
230
+ interface AgentTool {
231
+ name: string;
232
+ description: string;
233
+ parameters: object;
234
+ run(args: any, ctx: ToolContext): Promise<string>;
235
+ }
236
+ /** Build a tool context bound to a filesystem backend (Mem / Disk / …) and an optional host. */
237
+ declare function makeContext(fs: IFilesystem, host?: HostBridge): ToolContext;
238
+ /** Convert AgentTools into the ai.libx.js `tools` array for chat(). */
239
+ declare function toWireTools(tools: AgentTool[]): Tool[];
240
+ /** Run any shell command line over the VFS (ls/cat/grep/find/head/tail/echo/mkdir/rm/mv/wc, pipes, redirects, &&/||/;). */
241
+ declare const bashTool: AgentTool;
242
+ /** Read a text file as 1-indexed numbered lines; arms the staleness guard for Edit. */
243
+ declare const readTool: AgentTool;
244
+ /** Replace an exact, unique substring; requires a prior Read and guards against stale edits. */
245
+ declare const editTool: AgentTool;
246
+ declare function defaultTools(): AgentTool[];
247
+ /**
248
+ * The full catalog of selectable tools, keyed by name. The evolve loop's mutation
249
+ * surface picks from this registry; embedders can build a custom tool set by name.
250
+ */
251
+ declare function toolRegistry(): Record<string, AgentTool>;
252
+ /** Resolve a list of tool names against the registry (unknown names throw). */
253
+ declare function toolsByName(names: string[]): AgentTool[];
254
+
255
+ export { type AgentTool as A, type ChatLike as C, type HostBridge as H, type Message as M, type Role as R, SandboxJobRegistry as S, type TodoItem as T, type UserQuestion as U, type ChatOptions as a, type ChatResponse as b, type ContentPart as c, type HostEvent as d, type MessageContent as e, type StreamChunk as f, type Tool as g, type ToolCall as h, type ToolContext as i, bashTool as j, contentText as k, defaultTools as l, editTool as m, imagePart as n, makeContext as o, makeJobTools as p, todoWriteTool as q, readTool as r, toolRegistry as s, toWireTools as t, toolsByName as u };
@@ -0,0 +1,80 @@
1
+ import { A as AgentTool } from './tools-Ch-OzOU8.js';
2
+ import '@livx.cc/wcli/core';
3
+
4
+ /** The slice of node's `child_process.spawn` we depend on — injectable so tests supply a fake. */
5
+ type SpawnFn = (command: string, args: string[], options: {
6
+ cwd?: string;
7
+ env?: Record<string, string | undefined>;
8
+ signal?: AbortSignal;
9
+ }) => SpawnedProcess;
10
+ /** Minimal `ChildProcess` surface this tool uses. */
11
+ interface SpawnedProcess {
12
+ stdout?: {
13
+ on(ev: 'data', cb: (chunk: any) => void): void;
14
+ } | null;
15
+ stderr?: {
16
+ on(ev: 'data', cb: (chunk: any) => void): void;
17
+ } | null;
18
+ on(ev: 'close', cb: (code: number | null) => void): void;
19
+ on(ev: 'error', cb: (err: Error) => void): void;
20
+ kill(signal?: string): void;
21
+ }
22
+ interface RealShellOptions {
23
+ /** Working directory the shell is bound to (typically a NodeDiskFilesystem `baseDir`). Required. */
24
+ cwd: string;
25
+ /** Override the spawner (tests inject a fake; default lazily imports node:child_process). */
26
+ spawn?: SpawnFn;
27
+ /** Per-command wall-clock cap (kill on overrun). Default 120s. */
28
+ timeoutMs?: number;
29
+ /** Extra env merged over the (optionally scrubbed) base env for the child. */
30
+ env?: Record<string, string>;
31
+ /** Strip likely-secret vars (API keys, tokens, cloud creds) from the child's env. Default ON.
32
+ * The FS jail does NOT contain a real process, so this is the seam that keeps `echo $ANTHROPIC_API_KEY`
33
+ * from leaking the host's secrets to a spawned command. `false` passes `process.env` through verbatim. */
34
+ redactEnv?: boolean;
35
+ /** Job registry enabling `Shell({background:true})` (long-running processes). Pair with `makeShellJobTools`. */
36
+ registry?: ShellJobRegistry;
37
+ }
38
+ type JobStatus = 'running' | 'exited' | 'killed' | 'error';
39
+ interface ShellJobConfig {
40
+ cwd: string;
41
+ spawn?: SpawnFn;
42
+ env?: Record<string, string>;
43
+ redactEnv?: boolean;
44
+ /** Tail buffer cap per job (bytes); older output is dropped. Default 256 KB. */
45
+ maxBuffer?: number;
46
+ /** Kill all jobs on process exit (the CLI sets this; tests leave it off to avoid global handlers). */
47
+ killOnExit?: boolean;
48
+ }
49
+ /**
50
+ * Per-session registry of background `/bin/sh` jobs. Backs `Shell({background:true})` and the
51
+ * `ShellOutput`/`ShellStatus`/`ShellKill` tools. Output accumulates into a tail-capped ring so a
52
+ * chatty process can't OOM. Bounded + killable; the CLI wires `killOnExit` so children are reaped.
53
+ */
54
+ declare class ShellJobRegistry {
55
+ private cfg;
56
+ private jobs;
57
+ private seq;
58
+ constructor(cfg: ShellJobConfig);
59
+ start(command: string): Promise<string>;
60
+ /** Current tail output for a job (null = no such job). */
61
+ output(id: string): string | null;
62
+ status(id: string): {
63
+ status: JobStatus;
64
+ exitCode?: number;
65
+ bytes: number;
66
+ } | null;
67
+ list(): Array<{
68
+ id: string;
69
+ command: string;
70
+ status: JobStatus;
71
+ }>;
72
+ kill(id: string): boolean;
73
+ killAll(): void;
74
+ }
75
+ /** Build an opt-in real-shell tool bound to `options.cwd`. */
76
+ declare function makeRealShellTool(options: RealShellOptions): AgentTool;
77
+ /** Build the background-job companion tools (ShellOutput / ShellStatus / ShellKill) over a registry. */
78
+ declare function makeShellJobTools(registry: ShellJobRegistry): AgentTool[];
79
+
80
+ export { type JobStatus, type RealShellOptions, type ShellJobConfig, ShellJobRegistry, type SpawnFn, type SpawnedProcess, makeRealShellTool, makeShellJobTools };