@molecule/api-ai-agents-llm 1.0.0 → 1.0.2
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 +203 -0
- package/package.json +17 -10
package/README.md
ADDED
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
AUTO-GENERATED — DO NOT EDIT THIS FILE.
|
|
3
|
+
Generated by `mlcl sync-docs` from the package's src/index.ts JSDoc + mlcl/registry.json.
|
|
4
|
+
Edits here are overwritten on the next commit (molecule's pre-commit hook regenerates).
|
|
5
|
+
To change this document, edit the module-level JSDoc in src/index.ts.
|
|
6
|
+
Generated: 2026-08-04T01:47:27.608Z
|
|
7
|
+
-->
|
|
8
|
+
|
|
9
|
+
# @molecule/api-ai-agents-llm
|
|
10
|
+
|
|
11
|
+
> **Auto-generated, AI-first package reference** for the [molecule.dev](https://molecule.dev) ecosystem.
|
|
12
|
+
> It is written to be read by coding agents as much as by people, and is generated from this
|
|
13
|
+
> package's source — edit `src/index.ts` JSDoc, not this file.
|
|
14
|
+
|
|
15
|
+
LLM-backed AI agents provider for molecule.dev — the batteries-included
|
|
16
|
+
tool-calling loop that implements the `@molecule/api-ai-agents` core contract.
|
|
17
|
+
|
|
18
|
+
Ships a default `provider` (`name: 'llm'`) that drives a model↔tool loop over
|
|
19
|
+
the swappable `ai` chat bond (`@molecule/api-ai`): it calls the model,
|
|
20
|
+
executes any tools the model requests via each `AITool.execute()`, feeds the
|
|
21
|
+
results back, and repeats until the model stops calling tools (or the
|
|
22
|
+
`maxSteps` budget is exhausted). Bond it once at startup, then drive it from
|
|
23
|
+
anywhere via the `@molecule/api-ai-agents` accessor.
|
|
24
|
+
|
|
25
|
+
## Quick Start
|
|
26
|
+
|
|
27
|
+
```typescript
|
|
28
|
+
import { bond } from '@molecule/api-bond'
|
|
29
|
+
import { requireProvider } from '@molecule/api-ai-agents'
|
|
30
|
+
import { provider as agents } from '@molecule/api-ai-agents-llm'
|
|
31
|
+
import type { AITool } from '@molecule/api-ai'
|
|
32
|
+
|
|
33
|
+
// Wire at startup (an `ai` provider must already be bonded).
|
|
34
|
+
bond('ai-agents', agents)
|
|
35
|
+
|
|
36
|
+
const myTool: AITool = {
|
|
37
|
+
name: 'add',
|
|
38
|
+
description: 'Add two numbers',
|
|
39
|
+
parameters: {
|
|
40
|
+
type: 'object',
|
|
41
|
+
properties: { a: { type: 'number' }, b: { type: 'number' } },
|
|
42
|
+
required: ['a', 'b'],
|
|
43
|
+
},
|
|
44
|
+
execute: async (input) => {
|
|
45
|
+
const { a, b } = input as { a: number; b: number }
|
|
46
|
+
return a + b
|
|
47
|
+
},
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const result = await requireProvider().run({
|
|
51
|
+
task: 'What is 1 + 2? Use the add tool.',
|
|
52
|
+
tools: [myTool],
|
|
53
|
+
})
|
|
54
|
+
console.log(result.output) // final assistant answer
|
|
55
|
+
console.log(result.steps) // intermediate tool calls + results
|
|
56
|
+
console.log(result.usage) // token usage summed across all model calls
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Type
|
|
60
|
+
|
|
61
|
+
`provider`
|
|
62
|
+
|
|
63
|
+
## Installation
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
npm install @molecule/api-ai-agents-llm @molecule/api-ai @molecule/api-ai-agents @molecule/api-i18n
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## API
|
|
70
|
+
|
|
71
|
+
### Classes
|
|
72
|
+
|
|
73
|
+
#### `AgentRunError`
|
|
74
|
+
|
|
75
|
+
Thrown by `run()` when the agentic loop fails partway through — a provider
|
|
76
|
+
API error (rate limit, auth, overload, network), an abort, or any other
|
|
77
|
+
exception raised while draining a model turn or executing a tool.
|
|
78
|
+
|
|
79
|
+
### Constants
|
|
80
|
+
|
|
81
|
+
#### `provider`
|
|
82
|
+
|
|
83
|
+
The LLM-backed AI agents provider: a tool-calling loop over the bonded `ai`
|
|
84
|
+
chat provider.
|
|
85
|
+
|
|
86
|
+
Bond it with `bond('ai-agents', provider)` and drive it with
|
|
87
|
+
`requireProvider().run({ task, tools })`. Requires a bonded `ai` provider
|
|
88
|
+
whose model supports tool use.
|
|
89
|
+
|
|
90
|
+
```typescript
|
|
91
|
+
const provider: AIAgentsProvider
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
## Core Interface
|
|
95
|
+
|
|
96
|
+
Implements `@molecule/api-ai-agents` interface.
|
|
97
|
+
|
|
98
|
+
## Bond Wiring
|
|
99
|
+
|
|
100
|
+
Setup function to register this provider with the core interface:
|
|
101
|
+
|
|
102
|
+
```typescript
|
|
103
|
+
import { setProvider } from '@molecule/api-ai-agents'
|
|
104
|
+
import { provider } from '@molecule/api-ai-agents-llm'
|
|
105
|
+
|
|
106
|
+
export function setupAiAgentsLlm(): void {
|
|
107
|
+
setProvider(provider)
|
|
108
|
+
}
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
## Injection Notes
|
|
112
|
+
|
|
113
|
+
### Requirements
|
|
114
|
+
|
|
115
|
+
Peer dependencies:
|
|
116
|
+
|
|
117
|
+
- `@molecule/api-ai` ^1.0.1
|
|
118
|
+
- `@molecule/api-ai-agents` ^1.0.1
|
|
119
|
+
- `@molecule/api-i18n` ^1.0.1
|
|
120
|
+
|
|
121
|
+
### Runtime Dependencies
|
|
122
|
+
|
|
123
|
+
- `@molecule/api-ai`
|
|
124
|
+
- `@molecule/api-ai-agents`
|
|
125
|
+
- `@molecule/api-i18n`
|
|
126
|
+
|
|
127
|
+
**Requires a bonded `ai` provider** (`bond('ai', provider)` or a named
|
|
128
|
+
provider selected via `run({ provider: 'anthropic' })`) whose model supports
|
|
129
|
+
tool use — this agent has no model of its own; it orchestrates the `ai` bond.
|
|
130
|
+
|
|
131
|
+
The loop executes your tools: each model `tool_use` runs the matching
|
|
132
|
+
`AITool.execute(input)` and the return value is fed back as a `tool_result`.
|
|
133
|
+
A thrown tool error or an unknown tool name becomes an error `tool_result`
|
|
134
|
+
(recorded with `isError: true`) instead of aborting the run, so the model can
|
|
135
|
+
recover. `run()` requires exactly one of `task` (a single user turn) or
|
|
136
|
+
`messages` (a full history); it throws if neither — or both — is supplied.
|
|
137
|
+
|
|
138
|
+
Every model turn streams by default (`input.stream` — set `false` to force
|
|
139
|
+
non-streaming) and, when `input.cacheControl` is set, is passed through to
|
|
140
|
+
`ai.chat()` on every turn so a provider that supports prompt caching (e.g.
|
|
141
|
+
`@molecule/api-ai-anthropic`) doesn't re-bill the identical `system` + `tools`
|
|
142
|
+
prefix on each of up to `maxSteps` turns. Note `onEvent` is only an
|
|
143
|
+
INCREMENTAL live hook while streaming — with `stream: false` it fires once
|
|
144
|
+
per turn with the whole response.
|
|
145
|
+
|
|
146
|
+
Failure semantics (how to tell failure modes apart):
|
|
147
|
+
|
|
148
|
+
- **Tool failure** (throwing `execute()`, unknown tool name) — NON-fatal:
|
|
149
|
+
recorded on the step with `isError: true` and fed back to the model.
|
|
150
|
+
- **AI provider API failure** (rate limit, bad key, overload, network — the
|
|
151
|
+
`ai` bond emits an in-band `error` ChatEvent), **an abort, or any other
|
|
152
|
+
mid-run exception** — FATAL: `run()` rejects with an `AgentRunError`
|
|
153
|
+
(message text unchanged from a plain `Error`) carrying the `usage` and
|
|
154
|
+
`steps` accumulated across every turn that completed before the failure —
|
|
155
|
+
a caller that meters spend can book that partial usage instead of losing
|
|
156
|
+
it. `run()` never resolves with a silently empty `output`, so an empty
|
|
157
|
+
`result.output` means the model genuinely produced no text, not that the
|
|
158
|
+
API failed.
|
|
159
|
+
- **Step budget exhausted** — resolves normally; `output` is the last
|
|
160
|
+
assistant text or an explicit "step budget exhausted" note.
|
|
161
|
+
|
|
162
|
+
Swappable like any bond: replace this LLM agent with your own
|
|
163
|
+
`AIAgentsProvider` via `bond('ai-agents', myProvider)` — nothing else changes.
|
|
164
|
+
|
|
165
|
+
## E2E Tests
|
|
166
|
+
|
|
167
|
+
Integration checklist — drive the real UI (live preview, no mocks), give the
|
|
168
|
+
agent a task that genuinely needs several tool calls, adapt each item to this
|
|
169
|
+
app's actual agent surface + its registered tools, and check every box off one
|
|
170
|
+
by one. A box you can't check is an integration bug to fix — not a skip:
|
|
171
|
+
|
|
172
|
+
- [ ] A multi-step task drives a real agentic LOOP, not one shot: `run()`
|
|
173
|
+
records more than one entry in `result.steps`, and each `toolCalls[]` names a
|
|
174
|
+
tool the agent was actually given. Confirm each tool's `execute()` truly RAN
|
|
175
|
+
via its own side effect / log / the resource it touched — not that the model
|
|
176
|
+
narrated calling it — and that `result.output` is a final answer that USES
|
|
177
|
+
those tool results to complete the task.
|
|
178
|
+
- [ ] The loop TERMINATES cleanly: it either reaches a done state (the model
|
|
179
|
+
stops calling tools, so `run()` returns the final `output`) or hits the
|
|
180
|
+
`maxSteps` cap (default 10) and stops with the step-budget note as `output`.
|
|
181
|
+
It never spins forever or ping-pongs the same tool endlessly — a step cap
|
|
182
|
+
exists and is honored: `result.steps.length <= maxSteps`, and a task built to
|
|
183
|
+
loop past the budget stops AT the cap rather than running away.
|
|
184
|
+
- [ ] A tool that fails mid-loop is handled, not fatal: when a tool's
|
|
185
|
+
`execute()` throws (or the model names a tool that was never registered), that
|
|
186
|
+
call is caught and fed back to the model as an error tool result — the
|
|
187
|
+
matching `toolCalls[]` has `isError: true`, the loop continues, and the run
|
|
188
|
+
still returns a result. The request never crashes. (Only an `ai`-provider
|
|
189
|
+
failure rejects — as `AgentRunError` carrying the partial `usage`/`steps` —
|
|
190
|
+
and it surfaces as a handled error, not an unhandled throw.)
|
|
191
|
+
- [ ] If the run streams (the default), the UI shows progress INCREMENTALLY:
|
|
192
|
+
the `onEvent` hook fires per `ChatEvent` (thinking `text`, `tool_use`, `done`)
|
|
193
|
+
as they arrive, so the user watches the agent think + call tools live — not a
|
|
194
|
+
long freeze then one blob at the end.
|
|
195
|
+
- [ ] SECURITY — the agent can call ONLY the tools handed to this run: a tool
|
|
196
|
+
name the model invents (or one injected via the task or a tool's own output)
|
|
197
|
+
that was never in `input.tools` is refused as an `unknown tool` error, not
|
|
198
|
+
executed. Tool execution stays server-side under its backend's guards (e.g.
|
|
199
|
+
the `@molecule/api-ai-tools` `pathGuards`/`redactSecrets`/`blockCommand`), and
|
|
200
|
+
the `ai` provider key lives on the API, never the client. Feed a
|
|
201
|
+
prompt-injected instruction (in the task, or in data a tool returns) telling
|
|
202
|
+
the agent to escape the workspace, run a privileged command, or exfiltrate a
|
|
203
|
+
secret, and confirm it CANNOT do anything the user couldn't do directly.
|
package/package.json
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@molecule/api-ai-agents-llm",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.2",
|
|
4
4
|
"description": "LLM-backed AI agents provider for molecule.dev — a tool-calling loop over the swappable @molecule/api-ai chat bond",
|
|
5
|
+
"homepage": "https://www.molecule.dev/packages/api-ai-agents-llm",
|
|
5
6
|
"type": "module",
|
|
6
7
|
"main": "dist/index.js",
|
|
7
8
|
"types": "dist/index.d.ts",
|
|
@@ -17,7 +18,8 @@
|
|
|
17
18
|
}
|
|
18
19
|
},
|
|
19
20
|
"files": [
|
|
20
|
-
"dist"
|
|
21
|
+
"dist",
|
|
22
|
+
"README.md"
|
|
21
23
|
],
|
|
22
24
|
"keywords": [
|
|
23
25
|
"molecule",
|
|
@@ -28,18 +30,23 @@
|
|
|
28
30
|
"llm"
|
|
29
31
|
],
|
|
30
32
|
"license": "Apache-2.0",
|
|
33
|
+
"repository": {
|
|
34
|
+
"type": "git",
|
|
35
|
+
"url": "https://github.com/molecule-dev/molecule.git",
|
|
36
|
+
"directory": "packages/api/bonds/ai-agents/llm"
|
|
37
|
+
},
|
|
31
38
|
"peerDependencies": {
|
|
32
|
-
"@molecule/api-ai": "^1.0.
|
|
33
|
-
"@molecule/api-ai-agents": "^1.0.
|
|
34
|
-
"@molecule/api-i18n": "^1.0.
|
|
39
|
+
"@molecule/api-ai": "^1.0.1",
|
|
40
|
+
"@molecule/api-ai-agents": "^1.0.1",
|
|
41
|
+
"@molecule/api-i18n": "^1.0.1"
|
|
35
42
|
},
|
|
36
43
|
"devDependencies": {
|
|
37
|
-
"@molecule/api-ai": "1.
|
|
38
|
-
"@molecule/api-ai-agents": "1.0.
|
|
39
|
-
"@molecule/api-bond": "1.0.
|
|
40
|
-
"@molecule/api-i18n": "1.0.
|
|
44
|
+
"@molecule/api-ai": "1.2.1",
|
|
45
|
+
"@molecule/api-ai-agents": "1.0.2",
|
|
46
|
+
"@molecule/api-bond": "1.0.2",
|
|
47
|
+
"@molecule/api-i18n": "1.0.3",
|
|
41
48
|
"@types/node": "26.1.2",
|
|
42
49
|
"typescript": "6.0.3",
|
|
43
|
-
"vitest": "4.1.
|
|
50
|
+
"vitest": "4.1.11"
|
|
44
51
|
}
|
|
45
52
|
}
|