agentic-assistant 0.1.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md ADDED
@@ -0,0 +1,37 @@
1
+ # Changelog
2
+
3
+ ## 0.3.0
4
+
5
+ Added
6
+
7
+ - `agentic-assistant/client`, a typed fetch-based client for the HTTP API: `health`, `tools`, `chat` (an async generator of events), `approvals`, `approve`, and `deny`. It has an injectable `fetch`, a base URL, and a typed `AssistantApiError`.
8
+ - A demo Worker entry (`dist/demo.js`, built from `src/demo.ts`), `wrangler.demo.jsonc`, and `npm run deploy:demo`. It serves a demo page, `/health`, and the `/api/v1` routes with bearer auth. With no model key, chat answers 503 `not_configured`.
9
+ - `createStaticTokenAuth`, an `agentic-identity` `Auth` for a fixed map of bearer tokens.
10
+ - Chat responses carry the header `x-ai-generated: true`.
11
+ - A small schema checker for hosts that refuse code generation, such as Workers. It fails closed on keywords it does not support.
12
+
13
+ Removed
14
+
15
+ - The default export of `agentic-assistant/worker` (a stub that answered `/health` only). Use `createWorker`.
16
+
17
+ Changed
18
+
19
+ - The package needs Node.js 22 or newer. `agentic-identity` needs Node 22, so `engines.node` is now `>=22`. It was `>=18`.
20
+ - The `agentic-identity` dependency is now `^0.4.0`. It was `^0.3.0`. The exported names did not change.
21
+
22
+ ## 0.2.0
23
+
24
+ Added
25
+
26
+ - AI transparency. Each tool result whose tool ran, and each run that got a model answer, carries a `provenance` record with `generator: "ai"`, model, provider, time, assistant id, the person it acts for, the tool and its risk, and the approver with a `kind`. A denied call has no record.
27
+ - `createProvenance`, `provenanceMarker` (JSON-LD with the IPTC `trainedAlgorithmicMedia` source type) and `provenanceLabel`.
28
+ - The record is in SDK events, the HTTP chat stream, `--json` output of the CLI, and `assistant_chat` results of the MCP server. The MCP result also holds `label` and `marker`. The CLI prints the label after a run.
29
+ - Approval decisions may carry `approverKind` (`human`, `agent`, or `host`). A decision without it is recorded as `unknown`. `QueueApprovalGate.approve(id, decidedBy, approverKind)` takes it as a third argument: pass it, or every record shows `unknown`. The CLI sets `host` for a pre-approved call and `human` for a typed yes. The HTTP API sets `human` for a person and `agent` for any other caller.
30
+
31
+ Changed
32
+
33
+ - A provider that gives an empty model or provider name now ends the run with `provider_error` before any tool runs.
34
+
35
+ ## 0.1.0
36
+
37
+ First release.
package/README.md CHANGED
@@ -7,8 +7,9 @@
7
7
 
8
8
  An AI assistant that acts inside your apps with the user's own permissions.
9
9
 
10
- > Status: version 0.1.0, the first release. The core loop, the tool sources, approvals, and the React panel work.
11
- > The chat CLI and the MCP server are not built yet. The `agentic-assistant` command only prints help and version.
10
+ > Status: early. The core loop, the tool sources, approvals, the React panel, the CLI, the MCP server, and the hosted HTTP API work.
11
+
12
+ A demo Worker runs at https://agentic-assistant-demo.hello-fd1.workers.dev. `GET /` shows a demo page and `GET /health` answers 200. The `/api/v1` routes need a bearer key, so without one they answer 401. The demo has no model key set. A signed-in `POST /api/v1/chat` answers 503 with `not_configured`. The demo never fakes an answer.
12
13
 
13
14
  ## Install
14
15
 
@@ -16,7 +17,7 @@ An AI assistant that acts inside your apps with the user's own permissions.
16
17
  npm install agentic-assistant
17
18
  ```
18
19
 
19
- You need Node.js 18 or newer. The React panel needs `react` 18 or newer.
20
+ You need Node.js 22 or newer. The React panel needs `react` 18 or newer.
20
21
 
21
22
  To build from source:
22
23
 
@@ -120,6 +121,127 @@ The panel streams the answer, shows each tool call as a card, and shows an appro
120
121
 
121
122
  The look comes from plain CSS variables (`--aa-bg`, `--aa-fg`, `--aa-accent`, and others). Set them on `.aa-panel` or a parent. The sheet loads no font, image, or file from another host. A prompt that the run left open (after Stop or an error) is closed and marked expired. Closing the panel stops the run.
122
123
 
124
+ ## Use the CLI
125
+
126
+ ```sh
127
+ export ANTHROPIC_API_KEY=...
128
+ agentic-assistant chat \
129
+ --tools https://notes.example.eu/mcp --tools-header-env authorization=NOTES_TOKEN \
130
+ --scope notes:read --tool-scope notes:read --tool-risk safe \
131
+ --use-default-endpoint \
132
+ "What does note 7 say?"
133
+ ```
134
+
135
+ The assistant prints its answer on stdout. The answer starts with `Assistant (machine-made):`. Tool calls and approval requests on stderr start with `[machine-made]`, and so does the question that `--approve ask` shows. Control characters are removed from all text it prints for a person, so a tool cannot move your cursor or change your terminal title. `--json` events are not changed: they carry the `actor` with `machineMade: true` on each action, and the text events belong to the labeled run. Add `--json` to get one event per line on stdout: that is the form for agents and tests. The exit code is 0 when the run ends normally, 1 when it stops for another reason (a cap, an error), and 2 for a usage error.
136
+
137
+ - `--tools` takes an `https` MCP URL, or a command that starts a server on stdio (words split on spaces, or a JSON list like `'["node","server.js"]'`).
138
+ - `--scope` is what the assistant may hold. `--tool-scope` is what each tool needs. Both are required, and the CLI guesses neither.
139
+ - `--tool-risk` defaults to `risky`. A risky call needs approval. Approve one exact call ahead of time with `--approve-call '{"tool":"notes_write","input":{"id":"7","text":"hi"}}'`. The call must match the tool and the arguments, and each approval is used once. On a terminal, `--approve ask` asks you.
140
+ - Keys come from the environment: `ANTHROPIC_API_KEY`, or the variable named by `--api-key-env`. There is no flag that takes a key.
141
+ - For an EU-hosted model, use `--provider openai-compatible --base-url https://models.example.eu/v1 --model <id> --api-key-env EU_KEY`. Add `--price-in` and `--price-out` to use the cost cap.
142
+ - A message on stdin may be up to 1 MB.
143
+ - Limits: `--max-turns`, `--max-tokens`, `--max-cost`, `--max-tool-calls`.
144
+ - `--provider scripted --script turns.json` replays fixed turns. It is for tests.
145
+
146
+ ## Run it as an MCP server
147
+
148
+ ```sh
149
+ agentic-assistant mcp --tools https://notes.example.eu/mcp --scope notes:read --tool-scope notes:read --tool-risk safe --use-default-endpoint
150
+ ```
151
+
152
+ Add this to an MCP client as a stdio server. It offers one tool, `assistant_chat`, with one argument, `message`. Each call starts a new conversation. The reply is JSON with `machineMade: true`, the answer, the stop reason, and the tool calls. A run that stops on a cap is an error result. The caps (`--max-turns`, `--max-cost`) apply to one call, and the server has no total limit, so set a limit in the client that calls it. The server stops when the client closes its input. A call that is still running is cancelled and dropped: the client gets no reply for it. The tool servers are closed. The server cannot ask a person, so a risky call runs only when you started the server with a matching `--approve-call`.
153
+
154
+ ## Run it as a hosted HTTP API
155
+
156
+ ```ts
157
+ // src/index.ts of your Worker. `store`, `registry`, and `Env` are yours.
158
+ import { createWorker } from "agentic-assistant/worker";
159
+ import { AnthropicProvider } from "agentic-assistant";
160
+ import { createStoreAuth } from "agentic-identity";
161
+
162
+ let worker: ReturnType<typeof createWorker> | undefined;
163
+
164
+ export default {
165
+ fetch(request: Request, env: Env) {
166
+ // Build the worker once. It keeps the pending approvals, so a new one per request would lose them.
167
+ worker ??= createWorker({
168
+ auth: createStoreAuth(store(env)),
169
+ registry,
170
+ provider: () => new AnthropicProvider({ baseUrl: env.MODEL_URL, apiKey: env.MODEL_KEY }),
171
+ });
172
+ return worker.fetch(request, env);
173
+ },
174
+ };
175
+ ```
176
+
177
+ To deploy the demo Worker yourself, set the secrets `ASSISTANT_TOKENS` (a JSON map of a bearer token of 32 bytes or more to `{"id":"ana","scopes":["demo:read"]}`) and `MODEL_KEY`, set the var `MODEL_URL` (or `MODEL_USE_DEFAULT_ENDPOINT=1` to call the US-hosted `api.anthropic.com`), then run `npm run deploy:demo`. Without `MODEL_KEY`, chat answers 503. `agentic-assistant/worker` also exports `createStaticTokenAuth`, an `agentic-identity` `Auth` that reads bearer tokens from a fixed map.
178
+
179
+ Every route is under `/api/v1` and needs `Authorization: Bearer <key>`. The key resolves to an `agentic-identity` principal. A person, an agent, and a token that acts for a user all work. A missing, bad, or odd credential is a 401. Every chat response has the header `x-ai-generated: true`.
180
+
181
+ | Route | What it does |
182
+ |---|---|
183
+ | `GET /tools` | The tools inside the caller's scopes: name, description, scope, risk, input schema. |
184
+ | `POST /chat` `{"message":"..."}` | One new conversation. The answer is a stream of server-sent events (`event: <type>`, `data: <json>`), the same events as `runAssistant`, ending with `done`. |
185
+ | `GET /approvals` | Pending risky calls of the same owner, with the digest and `canDecide`. |
186
+ | `POST /approvals/:id/approve` `{"digest":"..."}` | Approve the exact call. A wrong digest is a 409. |
187
+ | `POST /approvals/:id/deny` `{"reason":"..."}` | Refuse the call. |
188
+
189
+ The assistant holds the caller's scopes and no more. A chat turn waits at a risky call until another principal decides. The stream stays open for that time. A principal that asked for the turn never decides on its own proposal: an agent gets a 403 for both approve and deny. The same holds for an agent it made and for the agent that made it. A person who asks may decide, because the assistant proposed the call. Another agent may decide only when it holds `assistant:admin` and the scope of the tool. A principal with another owner sees a 404. The list hides a call whose tool the caller has no scope for. If a client hangs up, the run stops and its pending calls go. If the key is revoked or its scopes change during a run, the run stops before its next tool call.
190
+
191
+ ## Use the HTTP client
192
+
193
+ `agentic-assistant/client` is a typed client for the hosted API routes. It uses only `fetch`, so it runs in Workers and Node. In a browser it works on the same origin only, because the API sends no CORS headers.
194
+
195
+ ```ts
196
+ import { createClient, AssistantApiError } from "agentic-assistant/client";
197
+
198
+ const assistant = createClient({ baseUrl: "https://assistant.example.com", token });
199
+ const tools = await assistant.tools();
200
+ for await (const event of assistant.chat("Delete note n1")) {
201
+ if (event.type === "text_delta") process.stdout.write(event.text);
202
+ }
203
+ try {
204
+ const [pending] = await assistant.approvals();
205
+ if (pending?.canDecide) await assistant.approve(pending.id, pending.digest);
206
+ } catch (error) {
207
+ if (error instanceof AssistantApiError) console.error(error.code, error.status);
208
+ }
209
+ ```
210
+
211
+ - The methods match the routes: `health`, `tools`, `chat`, `approvals`, `approve`, `deny`. Each takes an optional `{ signal }` as its last argument.
212
+ - `chat` is an async generator of the same events as `runAssistant`, ending with `done`. It sends the request on the first read. A run that fails on the server ends with an `error` event. A stream that ends with no `done` or `error` throws `UNKNOWN`: the run did not finish. Stop reading to stop the run. The stream stays open while a risky call waits, so decide from another call.
213
+ - `baseUrl` is the Worker URL: `https`, or `http` on `localhost`, `127.0.0.1`, or `[::1]`, with no query, hash, or credentials. A path prefix is for a host that strips it before the Worker. Pass `fetch` to use your own. `health` sends no token.
214
+ - A failed call throws `AssistantApiError`. `code` is `INVALID_INPUT`, `UNAUTHENTICATED`, `FORBIDDEN`, `NOT_FOUND`, `METHOD_NOT_ALLOWED`, `CONFLICT`, `RATE_LIMITED`, `NOT_CONFIGURED`, `INTERNAL`, or `UNKNOWN`. `status` is the HTTP status. `retryAfter` is set for a 429. A `200` reply with the wrong shape fails as `UNKNOWN`.
215
+ - The client never follows a redirect and never retries. A decision that you abort after the client sends it can still apply: read `approvals()` before you try again.
216
+
217
+ ## AI transparency
218
+
219
+ The package marks what the assistant makes as AI-generated. This follows the idea of EU AI Act Article 50.
220
+
221
+ Each tool result and each finished run carries a `provenance` record: `generator: "ai"`, the model, the provider, the time, the assistant id, the person it acts for, and the tool, its risk, and the approver when someone approved the call. The approver has a `kind`: `human`, `agent`, `host` (a rule you set), or `unknown`. Say "a person approved" only for `human`. After a fallback, the record names the model that answered. A denied call has no record, because nothing ran.
222
+
223
+ ```ts
224
+ import { provenanceLabel, provenanceMarker } from "agentic-assistant";
225
+
226
+ for await (const event of runAssistant(options)) {
227
+ if (event.type === "tool_result" && event.provenance) {
228
+ provenanceLabel(event.provenance); // "AI-generated by model-1 (acme), approved by ana"
229
+ provenanceMarker(event.provenance); // JSON-LD with the IPTC trainedAlgorithmicMedia source type
230
+ }
231
+ }
232
+ ```
233
+
234
+ The record is in the SDK events, in the HTTP chat stream, in `--json` output of the CLI, and in the `assistant_chat` result of the MCP server. The MCP result also holds `label` and `marker`. The CLI prints the label after each run.
235
+
236
+ The host must still do these things:
237
+
238
+ - Store the marker with every document or file the assistant changes.
239
+ - Show the label to people where they read the content.
240
+ - Label chat text that the assistant makes, if you show it outside this package.
241
+ - Keep records as long as the law needs, and get legal advice. This package does not make a deployment compliant.
242
+
243
+ The record says who made the content. It does not say the content is true.
244
+
123
245
  ## Features
124
246
 
125
247
  - **Model port.** `ModelProvider` has two adapters. `AnthropicProvider` needs a `baseUrl`, or `useDefaultEndpoint: true` to call `api.anthropic.com`, which is a US-hosted endpoint. There is no silent default. `OpenAICompatibleProvider` calls any server that speaks the chat-completions stream, so you can use an EU-hosted model. Both adapters need an `https` URL, except for `localhost`. If you pass your own `client` to `AnthropicProvider`, you own its endpoint. The OpenAI-compatible adapter needs an API key for any other host and cuts a response at a size limit.
@@ -128,7 +250,8 @@ The look comes from plain CSS variables (`--aa-bg`, `--aa-fg`, `--aa-accent`, an
128
250
  - **Input validation.** The loop checks each call against the tool's JSON Schema before the handler runs. Nothing is coerced. A bad call returns an error to the model and never reaches the tool.
129
251
  - **Tool output is data.** Each result is quoted in `<tool_output>` tags. Every `<` in the output is escaped. Every request carries a notice that tool output is never an instruction. This lowers the risk of prompt injection. It does not remove it: keep tools narrow and scopes small.
130
252
  - **Approvals.** Every tool has a required `risk`: `safe`, `risky`, or `irreversible`. The loop asks the `ApprovalGate` before a risky or irreversible call. With no gate, those tools are not offered and are refused. The text a person writes when they decline goes to the event, not to the model.
131
- - **React panel.** `agentic-assistant/react` has `AssistantPanel` and `useAssistant`. It meets the WCAG 2.2 AA points that code can check: named controls, a live conversation log, keyboard use, focus that moves to an approval prompt and then to the message box, buttons that are at least 32 px, a visible focus ring, and a color palette checked for contrast.
253
+ - **React panel.** `agentic-assistant/react` has `AssistantPanel` and `useAssistant`. It is built to these WCAG 2.2 AA points: named controls, a live conversation log, keyboard use, focus that moves to an approval prompt and then to the message box, buttons that are at least 32 px, a visible focus ring, and a color palette checked for contrast.
254
+ - **CLI and MCP server.** `chat` and `mcp` take the host choices as flags and refuse to guess a scope.
132
255
  - **Fixed principal.** The assistant always acts as `agent_of_human`. `createPrincipal` throws when the kind is missing or different.
133
256
  - **Caps.** A max-turn cap, a total-token cap, and a cost cap. A cap that trips stops the run. No tool runs after that. A tool result is cut at `maxToolResultChars`. A request that is sure to pass the token cap is not sent. The cost cap also limits the output of each request. A turn with more than `maxToolCallsPerTurn` calls (default 16) runs none of them.
134
257
  - **Stops that fail closed.** An expired or revoked principal stops the run. So does a revocation check that throws or returns anything but `false`. The loop checks the principal again at the start of each run. A provider error ends the run with `reason: "provider_error"` and keeps the transcript. Truncated or refused model output never runs its tool calls.
@@ -137,16 +260,19 @@ The look comes from plain CSS variables (`--aa-bg`, `--aa-fg`, `--aa-accent`, an
137
260
 
138
261
  ## Limits
139
262
 
140
- - The chat CLI and the MCP server are not built. The `agentic-assistant` command prints help and version only.
263
+ - The hosted API keeps pending approvals in the memory of one Worker instance. A decision must reach the instance that holds the chat stream. Run it on one instance or behind sticky routing until a shared queue exists. It has no per-IP limit, only a cap of four chat turns at once per principal.
264
+ - The tests run the API under Node with a fake model. They do not run it under `wrangler dev`. The demo Worker was checked by hand under `wrangler dev` and on workers.dev.
265
+ - Workers refuse code generation, so there a tool schema is checked by a small built-in checker, not by Ajv. It supports type, properties, required, additionalProperties, enum, const, items, length and size bounds, and anyOf, oneOf, allOf. It accepts annotation keywords such as `description` and `format` but does not check `format`, as the Ajv setup does not. It refuses a tool whose schema has any other keyword, such as `$ref`.
266
+ - The CLI reads one message, or a short conversation on a terminal. It does not keep a conversation between runs.
141
267
  - The panel is tested in a simulated browser (jsdom). Contrast is checked from the CSS values. Run a screen reader and a real browser before you ship it to people who need it.
142
268
  - The panel shows plain text, not Markdown.
143
269
  - The gate trusts the name that `decidedBy` carries. Your gate must check who the person is. `QueueApprovalGate` does not.
144
270
  - An MCP server is trusted for its own output only. The assistant does not sandbox the server process. Run stdio servers you trust.
145
271
  - Token and cost caps need the provider to report usage. The OpenAI-compatible adapter throws when the server sends none.
146
- - A cost is an estimate from the `pricing` you give. Cache discounts are not applied. The built-in Anthropic prices are from 2026-10-06. Check them.
272
+ - A cost is an estimate from the `pricing` you give. Cache discounts are not applied. The built-in Anthropic prices were checked on 2026-10-29 (`PRICING_CHECKED_ON`). A test fails when that date is over 120 days old. Haiku has no built-in price, because its rate changes with the prompt size. Pass `pricing` for it. Check the prices before you rely on a cap.
147
273
  - The token pre-check guesses three characters per token. It is an estimate.
148
274
  - A tool error text goes to the `detail` field of the event. The model sees only `tool failed`.
149
- - A model refusal stops the run. No automatic fallback model is set up.
275
+ - A model refusal stops the run, unless you set a fallback model. `FallbackProvider(primary, fallback)` or `--fallback-model <id>` asks the second model once when the first refuses or fails. Each `turn_end` event has `answeredBy` with the model that answered. The text of the first model is held until its turn ends, so a turn with a fallback does not stream word by word. A refusal from the fallback stops the run. The fallback gets only the output room that the first ask left, and the caps count the input of both asks, so a token or cost cap holds. A first ask that failed after it streamed is counted by an estimate. If the fallback itself fails after it streamed, the totals of that run leave out the first ask. Its error text is not kept.
150
276
 
151
277
  ## Develop
152
278
 
@@ -155,6 +281,12 @@ npm ci
155
281
  npm run ci:local
156
282
  ```
157
283
 
284
+ The live smoke test calls the real Anthropic API. It costs a few cents, so it runs only when you ask:
285
+
286
+ ```sh
287
+ AGENTIC_ASSISTANT_LIVE=1 ANTHROPIC_API_KEY=... npx vitest run src/live.smoke.test.ts
288
+ ```
289
+
158
290
  ## License
159
291
 
160
292
  MIT.
package/SKILL.md CHANGED
@@ -27,6 +27,23 @@ registry.registerAll(mcp.tools);
27
27
 
28
28
  Give each tool a `risk`. Pass `approvals: new QueueApprovalGate()` to `runAssistant`. On an `approval_request` event, show the exact arguments. Then call `approve(id, personId)` or `reject(id, personId)`.
29
29
 
30
+ ## Serve the HTTP API
31
+
32
+ ```ts
33
+ import { createWorker } from "agentic-assistant/worker";
34
+ const worker = createWorker({ auth, registry, provider: () => model });
35
+ ```
36
+
37
+ Routes: `GET /api/v1/tools`, `POST /api/v1/chat` (server-sent events), `GET /api/v1/approvals`, `POST /api/v1/approvals/:id/approve` with the digest, and `/deny`. The caller that asked for a turn never decides on it, if it is an agent.
38
+
39
+ ## Use the CLI
40
+
41
+ ```sh
42
+ agentic-assistant chat --tools <mcp-url> --scope notes:read --tool-scope notes:read --tool-risk safe --json "message"
43
+ ```
44
+
45
+ Set the key in the environment. Approve one exact risky call with `--approve-call`. Read the exit code: 0 normal, 1 stopped or failed, 2 usage.
46
+
30
47
  ## Show a panel
31
48
 
32
49
  ```tsx
@@ -0,0 +1,120 @@
1
+ import { S as Scope, u as ToolRisk, s as ToolDefinition, o as RunLimits, t as ToolRegistry, g as ApprovalGate, A as AssistantEvent } from './loop-D4qWbRu6.js';
2
+ import { P as Pricing, M as ModelProvider, c as Message } from './types-DGH8FS56.js';
3
+
4
+ type McpTransportConfig = {
5
+ type: "stdio";
6
+ command: string;
7
+ args?: string[];
8
+ env?: Record<string, string>;
9
+ cwd?: string;
10
+ } | {
11
+ type: "http";
12
+ url: string;
13
+ headers?: Record<string, string>;
14
+ };
15
+ interface McpToolInfo {
16
+ name: string;
17
+ description?: string;
18
+ annotations?: {
19
+ readOnlyHint?: boolean;
20
+ destructiveHint?: boolean;
21
+ idempotentHint?: boolean;
22
+ openWorldHint?: boolean;
23
+ };
24
+ }
25
+ interface ConnectMcpOptions {
26
+ transport: McpTransportConfig;
27
+ /** The scope each tool needs. There is no default. A function can choose per tool. */
28
+ scope: Scope | ((tool: McpToolInfo) => Scope);
29
+ /** How risky each tool is. There is no default. A function can choose per tool. Do not trust the server's hints. */
30
+ risk: ToolRisk | ((tool: McpToolInfo) => ToolRisk);
31
+ /** Put before each tool name, to keep names from two servers apart. */
32
+ prefix?: string;
33
+ /** Most characters of tool output to keep. Default 32768. */
34
+ maxOutputChars?: number;
35
+ /** Most tools to load. Default 200. */
36
+ maxTools?: number;
37
+ /** Longest tool description to pass to the model. Default 1000. */
38
+ maxDescriptionChars?: number;
39
+ /** Time to wait for one tool call. Default 30000. */
40
+ callTimeoutMs?: number;
41
+ /** Time to connect and list every tool, all pages together. Default 30000. */
42
+ loadTimeoutMs?: number;
43
+ /** Most bytes to read from one HTTP response. Default 4000000. Stdio is limited by the SDK. */
44
+ maxResponseBytes?: number;
45
+ }
46
+ interface McpTools {
47
+ tools: ToolDefinition[];
48
+ /** Tools that were not loaded, with the reason. */
49
+ skipped: {
50
+ name: string;
51
+ reason: string;
52
+ }[];
53
+ close(): Promise<void>;
54
+ }
55
+ /**
56
+ * Connects to an MCP server and turns its tools into assistant tools. The host
57
+ * names the scope each tool needs. Output text is capped, and non-text content is
58
+ * left out. Tools with a bad name, schema, or scope are skipped, not loaded.
59
+ */
60
+ declare function connectMcpTools(options: ConnectMcpOptions): Promise<McpTools>;
61
+
62
+ declare class ConfigError extends Error {
63
+ constructor(message: string);
64
+ }
65
+ interface ChatConfig {
66
+ /** An http(s) URL of an MCP server, or a command line that starts one on stdio. */
67
+ tools: string;
68
+ /** Names of environment variables that hold HTTP headers for the MCP server: `header=ENV_NAME`. */
69
+ toolHeaderEnv: string[];
70
+ scopes: string[];
71
+ toolScope: string;
72
+ toolRisk: ToolRisk;
73
+ provider: "anthropic" | "openai-compatible" | "scripted";
74
+ model?: string;
75
+ /** A second model on the same provider. It answers once when the first one refuses or fails. */
76
+ fallbackModel?: string;
77
+ baseUrl?: string;
78
+ useDefaultEndpoint: boolean;
79
+ apiKeyEnv?: string;
80
+ pricing?: Pricing;
81
+ script?: string;
82
+ system?: string;
83
+ user: string;
84
+ limits: RunLimits;
85
+ /** Exact calls a person approved before the run. Each is used once. */
86
+ preApproved: {
87
+ tool: string;
88
+ input: unknown;
89
+ }[];
90
+ approve: "deny" | "ask";
91
+ }
92
+ interface ChatIo {
93
+ env: Record<string, string | undefined>;
94
+ readFile(path: string): string;
95
+ /** Ask a person a question and return the answer. Only used for `--approve ask`. */
96
+ ask?(question: string): Promise<string>;
97
+ }
98
+ interface Setup {
99
+ provider: ModelProvider;
100
+ registry: ToolRegistry;
101
+ gate: ApprovalGate;
102
+ principalOptions: {
103
+ id: string;
104
+ parentId: string;
105
+ scopes: string[];
106
+ };
107
+ limits: RunLimits;
108
+ system?: string;
109
+ skipped: {
110
+ name: string;
111
+ reason: string;
112
+ }[];
113
+ close(): Promise<void>;
114
+ }
115
+ /** An http(s) URL is an HTTP server. A JSON array of strings, or words split on spaces, is a command. */
116
+ declare function parseTarget(target: string, headerEnv: string[], env: ChatIo["env"]): McpTransportConfig;
117
+ declare function buildSetup(config: ChatConfig, io: ChatIo): Promise<Setup>;
118
+ declare function runChat(setup: Setup, messages: Message[], signal?: AbortSignal): AsyncGenerator<AssistantEvent>;
119
+
120
+ export { type ChatConfig as C, type McpToolInfo as M, type Setup as S, type ChatIo as a, ConfigError as b, type ConnectMcpOptions as c, type McpTools as d, type McpTransportConfig as e, buildSetup as f, connectMcpTools as g, parseTarget as p, runChat as r };