@push.rocks/smartagent 1.7.0 → 3.0.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_ts/00_commitinfo_data.js +3 -3
- package/dist_ts/index.d.ts +9 -12
- package/dist_ts/index.js +9 -20
- package/dist_ts/plugins.d.ts +8 -9
- package/dist_ts/plugins.js +10 -12
- package/dist_ts/smartagent.classes.agent.d.ts +2 -0
- package/dist_ts/smartagent.classes.agent.js +173 -0
- package/dist_ts/smartagent.classes.toolregistry.d.ts +12 -0
- package/dist_ts/smartagent.classes.toolregistry.js +17 -0
- package/dist_ts/smartagent.interfaces.d.ts +47 -231
- package/dist_ts/smartagent.interfaces.js +6 -7
- package/dist_ts/smartagent.utils.truncation.d.ts +10 -0
- package/dist_ts/smartagent.utils.truncation.js +26 -0
- package/dist_ts_compaction/index.d.ts +1 -0
- package/dist_ts_compaction/index.js +2 -0
- package/dist_ts_compaction/plugins.d.ts +4 -0
- package/dist_ts_compaction/plugins.js +3 -0
- package/dist_ts_compaction/smartagent.compaction.d.ts +10 -0
- package/dist_ts_compaction/smartagent.compaction.js +46 -0
- package/dist_ts_tools/index.d.ts +8 -0
- package/dist_ts_tools/index.js +6 -0
- package/dist_ts_tools/plugins.d.ts +15 -0
- package/dist_ts_tools/plugins.js +19 -0
- package/dist_ts_tools/tool.filesystem.d.ts +6 -0
- package/dist_ts_tools/tool.filesystem.js +102 -0
- package/dist_ts_tools/tool.http.d.ts +2 -0
- package/dist_ts_tools/tool.http.js +65 -0
- package/dist_ts_tools/tool.json.d.ts +2 -0
- package/dist_ts_tools/tool.json.js +47 -0
- package/dist_ts_tools/tool.shell.d.ts +8 -0
- package/dist_ts_tools/tool.shell.js +40 -0
- package/npmextra.json +1 -1
- package/package.json +30 -18
- package/readme.hints.md +43 -42
- package/readme.md +257 -526
- package/ts/00_commitinfo_data.ts +2 -2
- package/ts/index.ts +11 -31
- package/ts/plugins.ts +22 -21
- package/ts/smartagent.classes.agent.ts +198 -0
- package/ts/smartagent.classes.toolregistry.ts +20 -0
- package/ts/smartagent.interfaces.ts +51 -303
- package/ts/smartagent.utils.truncation.ts +39 -0
- package/ts_compaction/index.ts +1 -0
- package/ts_compaction/plugins.ts +6 -0
- package/ts_compaction/smartagent.compaction.ts +51 -0
- package/ts_tools/index.ts +8 -0
- package/ts_tools/plugins.ts +30 -0
- package/ts_tools/tool.filesystem.ts +131 -0
- package/ts_tools/tool.http.ts +78 -0
- package/ts_tools/tool.json.ts +53 -0
- package/ts_tools/tool.shell.ts +62 -0
- package/dist_ts/smartagent.classes.driveragent.d.ts +0 -134
- package/dist_ts/smartagent.classes.driveragent.js +0 -671
- package/dist_ts/smartagent.classes.dualagent.d.ts +0 -79
- package/dist_ts/smartagent.classes.dualagent.js +0 -583
- package/dist_ts/smartagent.classes.guardianagent.d.ts +0 -46
- package/dist_ts/smartagent.classes.guardianagent.js +0 -201
- package/dist_ts/smartagent.tools.base.d.ts +0 -52
- package/dist_ts/smartagent.tools.base.js +0 -42
- package/dist_ts/smartagent.tools.browser.d.ts +0 -17
- package/dist_ts/smartagent.tools.browser.js +0 -229
- package/dist_ts/smartagent.tools.deno.d.ts +0 -21
- package/dist_ts/smartagent.tools.deno.js +0 -191
- package/dist_ts/smartagent.tools.filesystem.d.ts +0 -40
- package/dist_ts/smartagent.tools.filesystem.js +0 -801
- package/dist_ts/smartagent.tools.http.d.ts +0 -16
- package/dist_ts/smartagent.tools.http.js +0 -264
- package/dist_ts/smartagent.tools.json.d.ts +0 -24
- package/dist_ts/smartagent.tools.json.js +0 -202
- package/dist_ts/smartagent.tools.shell.d.ts +0 -17
- package/dist_ts/smartagent.tools.shell.js +0 -202
- package/ts/smartagent.classes.driveragent.ts +0 -775
- package/ts/smartagent.classes.dualagent.ts +0 -657
- package/ts/smartagent.classes.guardianagent.ts +0 -241
- package/ts/smartagent.tools.base.ts +0 -83
- package/ts/smartagent.tools.browser.ts +0 -253
- package/ts/smartagent.tools.deno.ts +0 -230
- package/ts/smartagent.tools.filesystem.ts +0 -885
- package/ts/smartagent.tools.http.ts +0 -283
- package/ts/smartagent.tools.json.ts +0 -224
- package/ts/smartagent.tools.shell.ts +0 -230
package/readme.md
CHANGED
|
@@ -1,12 +1,10 @@
|
|
|
1
1
|
# @push.rocks/smartagent
|
|
2
2
|
|
|
3
|
-
A
|
|
3
|
+
A lightweight agentic loop built on **Vercel AI SDK v6** via `@push.rocks/smartai`. Register tools, get a model, call `runAgent()` — done. 🚀
|
|
4
4
|
|
|
5
5
|
## Install
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
|
-
npm install @push.rocks/smartagent
|
|
9
|
-
# or
|
|
10
8
|
pnpm install @push.rocks/smartagent
|
|
11
9
|
```
|
|
12
10
|
|
|
@@ -16,629 +14,362 @@ For reporting bugs, issues, or security vulnerabilities, please visit [community
|
|
|
16
14
|
|
|
17
15
|
## Overview
|
|
18
16
|
|
|
19
|
-
|
|
17
|
+
`@push.rocks/smartagent` wraps the AI SDK's `streamText` with `stopWhen: stepCountIs(n)` for **parallel multi-step tool execution**. No classes to instantiate, no lifecycle to manage — just one async function:
|
|
20
18
|
|
|
21
|
-
|
|
22
|
-
|
|
19
|
+
```typescript
|
|
20
|
+
import { runAgent, tool, z } from '@push.rocks/smartagent';
|
|
21
|
+
import { getModel } from '@push.rocks/smartai';
|
|
23
22
|
|
|
24
|
-
|
|
23
|
+
const model = getModel({
|
|
24
|
+
provider: 'anthropic',
|
|
25
|
+
model: 'claude-sonnet-4-5-20250929',
|
|
26
|
+
apiKey: process.env.ANTHROPIC_TOKEN,
|
|
27
|
+
});
|
|
25
28
|
|
|
26
|
-
|
|
29
|
+
const result = await runAgent({
|
|
30
|
+
model,
|
|
31
|
+
prompt: 'What is 7 + 35?',
|
|
32
|
+
system: 'You are a helpful assistant. Use tools when asked.',
|
|
33
|
+
tools: {
|
|
34
|
+
calculator: tool({
|
|
35
|
+
description: 'Perform arithmetic',
|
|
36
|
+
inputSchema: z.object({
|
|
37
|
+
operation: z.enum(['add', 'subtract', 'multiply', 'divide']),
|
|
38
|
+
a: z.number(),
|
|
39
|
+
b: z.number(),
|
|
40
|
+
}),
|
|
41
|
+
execute: async ({ operation, a, b }) => {
|
|
42
|
+
const ops = { add: a + b, subtract: a - b, multiply: a * b, divide: a / b };
|
|
43
|
+
return String(ops[operation]);
|
|
44
|
+
},
|
|
45
|
+
}),
|
|
46
|
+
},
|
|
47
|
+
maxSteps: 10,
|
|
48
|
+
});
|
|
27
49
|
|
|
28
|
-
|
|
50
|
+
console.log(result.text); // "7 + 35 = 42"
|
|
51
|
+
console.log(result.steps); // number of agentic steps taken
|
|
52
|
+
console.log(result.usage); // { promptTokens, completionTokens, totalTokens }
|
|
53
|
+
```
|
|
29
54
|
|
|
30
55
|
## Architecture
|
|
31
56
|
|
|
32
|
-
```mermaid
|
|
33
|
-
flowchart TB
|
|
34
|
-
subgraph Input
|
|
35
|
-
Task["User Task"]
|
|
36
|
-
Policy["Guardian Policy Prompt"]
|
|
37
|
-
end
|
|
38
|
-
|
|
39
|
-
subgraph Orchestrator["DualAgentOrchestrator"]
|
|
40
|
-
Driver["Driver Agent<br/><i>Reason + Plan</i>"]
|
|
41
|
-
Guardian["Guardian Agent<br/><i>Evaluate against policy</i>"]
|
|
42
|
-
|
|
43
|
-
Driver -->|"tool call proposal"| Guardian
|
|
44
|
-
Guardian -->|"approve / reject + feedback"| Driver
|
|
45
|
-
end
|
|
46
|
-
|
|
47
|
-
subgraph Tools["Standard Tools"]
|
|
48
|
-
FS["Filesystem"]
|
|
49
|
-
HTTP["HTTP"]
|
|
50
|
-
Shell["Shell"]
|
|
51
|
-
Browser["Browser"]
|
|
52
|
-
Deno["Deno"]
|
|
53
|
-
end
|
|
54
|
-
|
|
55
|
-
Task --> Orchestrator
|
|
56
|
-
Policy --> Guardian
|
|
57
|
-
Driver -->|"execute<br/>(if approved)"| Tools
|
|
58
|
-
Tools -->|"result"| Driver
|
|
59
57
|
```
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
- REJECT operations on system directories or sensitive files
|
|
74
|
-
|
|
75
|
-
SHELL POLICY:
|
|
76
|
-
- Allow read-only commands (ls, cat, grep, echo)
|
|
77
|
-
- REJECT destructive commands (rm, mv, chmod) without explicit justification
|
|
78
|
-
|
|
79
|
-
FLAG any attempt to expose secrets or credentials.
|
|
80
|
-
`,
|
|
81
|
-
});
|
|
82
|
-
|
|
83
|
-
// Register standard tools
|
|
84
|
-
orchestrator.registerStandardTools();
|
|
85
|
-
|
|
86
|
-
// Start the orchestrator (initializes all tools)
|
|
87
|
-
await orchestrator.start();
|
|
88
|
-
|
|
89
|
-
// Run a task
|
|
90
|
-
const result = await orchestrator.run('List all TypeScript files in the current directory');
|
|
91
|
-
|
|
92
|
-
console.log('Success:', result.success);
|
|
93
|
-
console.log('Result:', result.result);
|
|
94
|
-
console.log('Iterations:', result.iterations);
|
|
95
|
-
|
|
96
|
-
// Cleanup
|
|
97
|
-
await orchestrator.stop();
|
|
58
|
+
┌─────────────────────────────────────────────────┐
|
|
59
|
+
│ runAgent({ model, prompt, tools, maxSteps }) │
|
|
60
|
+
│ │
|
|
61
|
+
│ ┌───────────┐ ┌──────────┐ ┌───────────┐ │
|
|
62
|
+
│ │ Messages │──▶│ streamText│──▶│ Tools │ │
|
|
63
|
+
│ │ (history) │◀──│ (AI SDK) │◀──│ (ToolSet) │ │
|
|
64
|
+
│ └───────────┘ └──────────┘ └───────────┘ │
|
|
65
|
+
│ │
|
|
66
|
+
│ stopWhen: stepCountIs(maxSteps) │
|
|
67
|
+
│ + retry with backoff on 429/529/503 │
|
|
68
|
+
│ + context overflow detection & recovery │
|
|
69
|
+
│ + tool call repair (case-insensitive matching) │
|
|
70
|
+
└─────────────────────────────────────────────────┘
|
|
98
71
|
```
|
|
99
72
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
SmartAgent comes with five battle-tested tools out of the box via `registerStandardTools()`:
|
|
73
|
+
**Key features:**
|
|
103
74
|
|
|
104
|
-
|
|
75
|
+
- 🔄 **Multi-step agentic loop** — the model calls tools, sees results, and continues reasoning until done
|
|
76
|
+
- ⚡ **Parallel tool execution** — multiple tool calls in a single step are executed concurrently
|
|
77
|
+
- 🔧 **Auto-retry with backoff** — handles 429/529/503 errors with header-aware retry delays
|
|
78
|
+
- 🩹 **Tool call repair** — case-insensitive name matching + invalid tool sink prevents crashes
|
|
79
|
+
- 📊 **Token streaming** — `onToken` and `onToolCall` callbacks for real-time progress
|
|
80
|
+
- 💥 **Context overflow handling** — detects overflow and invokes your `onContextOverflow` callback
|
|
105
81
|
|
|
106
|
-
|
|
82
|
+
## Core API
|
|
107
83
|
|
|
108
|
-
|
|
84
|
+
### `runAgent(options): Promise<IAgentRunResult>`
|
|
109
85
|
|
|
110
|
-
|
|
111
|
-
// Example tool call by Driver
|
|
112
|
-
<tool_call>
|
|
113
|
-
<tool>filesystem</tool>
|
|
114
|
-
<action>read</action>
|
|
115
|
-
<params>{"path": "/tmp/config.json"}</params>
|
|
116
|
-
<reasoning>Need to read the configuration file to understand the settings</reasoning>
|
|
117
|
-
</tool_call>
|
|
118
|
-
```
|
|
119
|
-
|
|
120
|
-
**Scoped Filesystem**: Lock file operations to a specific directory with optional exclusion patterns:
|
|
86
|
+
The single entry point. Options:
|
|
121
87
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
88
|
+
| Option | Type | Default | Description |
|
|
89
|
+
|--------|------|---------|-------------|
|
|
90
|
+
| `model` | `LanguageModelV3` | *required* | Model from `@push.rocks/smartai`'s `getModel()` |
|
|
91
|
+
| `prompt` | `string` | *required* | The user's task/question |
|
|
92
|
+
| `system` | `string` | `undefined` | System prompt |
|
|
93
|
+
| `tools` | `ToolSet` | `{}` | Tools the agent can call |
|
|
94
|
+
| `maxSteps` | `number` | `20` | Max agentic steps before stopping |
|
|
95
|
+
| `messages` | `ModelMessage[]` | `[]` | Conversation history (for multi-turn) |
|
|
96
|
+
| `maxRetries` | `number` | `5` | Max retries on rate-limit/server errors |
|
|
97
|
+
| `onToken` | `(delta: string) => void` | — | Streaming token callback |
|
|
98
|
+
| `onToolCall` | `(name: string) => void` | — | Called when a tool is invoked |
|
|
99
|
+
| `onContextOverflow` | `(messages) => messages` | — | Handle context overflow (e.g., compact messages) |
|
|
133
100
|
|
|
134
|
-
|
|
101
|
+
### `IAgentRunResult`
|
|
135
102
|
|
|
136
103
|
```typescript
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
104
|
+
interface IAgentRunResult {
|
|
105
|
+
text: string; // Final response text
|
|
106
|
+
finishReason: string; // 'stop', 'tool-calls', 'length', etc.
|
|
107
|
+
steps: number; // Number of agentic steps taken
|
|
108
|
+
messages: ModelMessage[]; // Full conversation for multi-turn
|
|
109
|
+
usage: {
|
|
110
|
+
promptTokens: number;
|
|
111
|
+
completionTokens: number;
|
|
112
|
+
totalTokens: number;
|
|
113
|
+
};
|
|
114
|
+
}
|
|
143
115
|
```
|
|
144
116
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
HTTP requests using `@push.rocks/smartrequest`.
|
|
117
|
+
## Defining Tools 🛠️
|
|
148
118
|
|
|
149
|
-
|
|
119
|
+
Tools use Vercel AI SDK's `tool()` helper with Zod schemas:
|
|
150
120
|
|
|
151
121
|
```typescript
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
122
|
+
import { tool, z } from '@push.rocks/smartagent';
|
|
123
|
+
|
|
124
|
+
const myTool = tool({
|
|
125
|
+
description: 'Describe what this tool does',
|
|
126
|
+
inputSchema: z.object({
|
|
127
|
+
param1: z.string().describe('What this parameter is for'),
|
|
128
|
+
param2: z.number().optional(),
|
|
129
|
+
}),
|
|
130
|
+
execute: async ({ param1, param2 }) => {
|
|
131
|
+
// Do work, return a string
|
|
132
|
+
return `Result: ${param1}`;
|
|
133
|
+
},
|
|
134
|
+
});
|
|
158
135
|
```
|
|
159
136
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
Secure shell command execution using `@push.rocks/smartshell` with `execSpawn` (no shell injection possible).
|
|
163
|
-
|
|
164
|
-
**Actions**: `execute`, `which`
|
|
137
|
+
Pass tools as a flat object to `runAgent()`:
|
|
165
138
|
|
|
166
139
|
```typescript
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
140
|
+
await runAgent({
|
|
141
|
+
model,
|
|
142
|
+
prompt: 'Do the thing',
|
|
143
|
+
tools: { myTool, anotherTool },
|
|
144
|
+
maxSteps: 10,
|
|
145
|
+
});
|
|
173
146
|
```
|
|
174
147
|
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
### 🌍 BrowserTool
|
|
148
|
+
## ToolRegistry
|
|
178
149
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
**Actions**: `screenshot`, `pdf`, `evaluate`, `getPageContent`
|
|
150
|
+
A lightweight helper for collecting tools:
|
|
182
151
|
|
|
183
152
|
```typescript
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
153
|
+
import { ToolRegistry, tool, z } from '@push.rocks/smartagent';
|
|
154
|
+
|
|
155
|
+
const registry = new ToolRegistry();
|
|
156
|
+
|
|
157
|
+
registry.register('random_number', tool({
|
|
158
|
+
description: 'Generate a random integer between min and max',
|
|
159
|
+
inputSchema: z.object({
|
|
160
|
+
min: z.number(),
|
|
161
|
+
max: z.number(),
|
|
162
|
+
}),
|
|
163
|
+
execute: async ({ min, max }) => {
|
|
164
|
+
return String(Math.floor(Math.random() * (max - min + 1)) + min);
|
|
165
|
+
},
|
|
166
|
+
}));
|
|
167
|
+
|
|
168
|
+
registry.register('is_even', tool({
|
|
169
|
+
description: 'Check if a number is even',
|
|
170
|
+
inputSchema: z.object({ number: z.number() }),
|
|
171
|
+
execute: async ({ number: n }) => n % 2 === 0 ? 'Yes' : 'No',
|
|
172
|
+
}));
|
|
173
|
+
|
|
174
|
+
const result = await runAgent({
|
|
175
|
+
model,
|
|
176
|
+
prompt: 'Generate a random number and tell me if it is even',
|
|
177
|
+
tools: registry.getTools(),
|
|
178
|
+
maxSteps: 10,
|
|
179
|
+
});
|
|
190
180
|
```
|
|
191
181
|
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
Execute TypeScript/JavaScript code in a **sandboxed Deno environment** with fine-grained permission control.
|
|
195
|
-
|
|
196
|
-
**Actions**: `execute`, `executeWithResult`
|
|
182
|
+
## Built-in Tool Factories 🧰
|
|
197
183
|
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
By default, code runs **fully sandboxed with no permissions**. Permissions must be explicitly requested and are subject to Guardian approval.
|
|
184
|
+
Import from the `@push.rocks/smartagent/tools` subpath:
|
|
201
185
|
|
|
202
186
|
```typescript
|
|
203
|
-
|
|
204
|
-
<tool_call>
|
|
205
|
-
<tool>deno</tool>
|
|
206
|
-
<action>execute</action>
|
|
207
|
-
<params>{"code": "console.log('Hello from Deno!')"}</params>
|
|
208
|
-
<reasoning>Running a simple script to verify the environment</reasoning>
|
|
209
|
-
</tool_call>
|
|
210
|
-
|
|
211
|
-
// Code with network permission
|
|
212
|
-
<tool_call>
|
|
213
|
-
<tool>deno</tool>
|
|
214
|
-
<action>execute</action>
|
|
215
|
-
<params>{
|
|
216
|
-
"code": "const resp = await fetch('https://api.example.com/data'); console.log(await resp.json());",
|
|
217
|
-
"permissions": ["net"]
|
|
218
|
-
}</params>
|
|
219
|
-
<reasoning>Fetching data from API using Deno's fetch</reasoning>
|
|
220
|
-
</tool_call>
|
|
221
|
-
|
|
222
|
-
// Execute and parse JSON result
|
|
223
|
-
<tool_call>
|
|
224
|
-
<tool>deno</tool>
|
|
225
|
-
<action>executeWithResult</action>
|
|
226
|
-
<params>{
|
|
227
|
-
"code": "const result = { sum: 2 + 2, date: new Date().toISOString() }; console.log(JSON.stringify(result));"
|
|
228
|
-
}</params>
|
|
229
|
-
<reasoning>Computing values and returning structured data</reasoning>
|
|
230
|
-
</tool_call>
|
|
187
|
+
import { filesystemTool, shellTool, httpTool, jsonTool } from '@push.rocks/smartagent/tools';
|
|
231
188
|
```
|
|
232
189
|
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
### 📋 JsonValidatorTool
|
|
190
|
+
### `filesystemTool(options?)`
|
|
236
191
|
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
**Actions**: `validate`, `format`
|
|
192
|
+
Returns: `read_file`, `write_file`, `list_directory`, `delete_file`
|
|
240
193
|
|
|
241
194
|
```typescript
|
|
242
|
-
|
|
195
|
+
const tools = filesystemTool({ rootDir: '/home/user/workspace' });
|
|
243
196
|
|
|
244
|
-
|
|
245
|
-
|
|
197
|
+
await runAgent({
|
|
198
|
+
model,
|
|
199
|
+
prompt: 'Create a file called hello.txt with "Hello World"',
|
|
200
|
+
tools,
|
|
201
|
+
maxSteps: 5,
|
|
202
|
+
});
|
|
246
203
|
```
|
|
247
204
|
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
<tool_call>
|
|
251
|
-
<tool>json</tool>
|
|
252
|
-
<action>validate</action>
|
|
253
|
-
<params>{
|
|
254
|
-
"jsonString": "{\"name\": \"test\", \"version\": \"1.0.0\"}",
|
|
255
|
-
"requiredFields": ["name", "version", "description"]
|
|
256
|
-
}</params>
|
|
257
|
-
<reasoning>Ensuring the config has all required fields before saving</reasoning>
|
|
258
|
-
</tool_call>
|
|
259
|
-
|
|
260
|
-
// Pretty-print JSON
|
|
261
|
-
<tool_call>
|
|
262
|
-
<tool>json</tool>
|
|
263
|
-
<action>format</action>
|
|
264
|
-
<params>{"jsonString": "{\"compact\":true,\"data\":[1,2,3]}"}</params>
|
|
265
|
-
<reasoning>Formatting JSON for readable output</reasoning>
|
|
266
|
-
</tool_call>
|
|
267
|
-
```
|
|
205
|
+
Options:
|
|
206
|
+
- `rootDir` — restrict all file operations to this directory. Paths outside it throw `Access denied`.
|
|
268
207
|
|
|
269
|
-
|
|
208
|
+
### `shellTool(options?)`
|
|
270
209
|
|
|
271
|
-
|
|
210
|
+
Returns: `run_command`
|
|
272
211
|
|
|
273
212
|
```typescript
|
|
274
|
-
const
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
// source is 'driver' or 'guardian'
|
|
282
|
-
process.stdout.write(token);
|
|
283
|
-
},
|
|
213
|
+
const tools = shellTool({ cwd: '/tmp', allowedCommands: ['ls', 'echo', 'cat'] });
|
|
214
|
+
|
|
215
|
+
await runAgent({
|
|
216
|
+
model,
|
|
217
|
+
prompt: 'List all files in /tmp',
|
|
218
|
+
tools,
|
|
219
|
+
maxSteps: 5,
|
|
284
220
|
});
|
|
285
221
|
```
|
|
286
222
|
|
|
287
|
-
|
|
223
|
+
Options:
|
|
224
|
+
- `cwd` — working directory for commands
|
|
225
|
+
- `allowedCommands` — whitelist of allowed commands (if set, others are rejected)
|
|
288
226
|
|
|
289
|
-
|
|
227
|
+
### `httpTool()`
|
|
290
228
|
|
|
291
|
-
|
|
229
|
+
Returns: `http_get`, `http_post`
|
|
292
230
|
|
|
293
231
|
```typescript
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
// Load image as base64
|
|
297
|
-
const imageBase64 = readFileSync('screenshot.png').toString('base64');
|
|
298
|
-
|
|
299
|
-
// Run task with images
|
|
300
|
-
const result = await orchestrator.run(
|
|
301
|
-
'Analyze this UI screenshot and describe any usability issues',
|
|
302
|
-
{ images: [imageBase64] }
|
|
303
|
-
);
|
|
304
|
-
```
|
|
305
|
-
|
|
306
|
-
## 📊 Progress Events
|
|
307
|
-
|
|
308
|
-
Get real-time feedback on task execution with the `onProgress` callback:
|
|
232
|
+
const tools = httpTool();
|
|
309
233
|
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
onProgress: (event) => {
|
|
317
|
-
// Pre-formatted log message ready for output
|
|
318
|
-
console.log(event.logMessage);
|
|
319
|
-
|
|
320
|
-
// Or handle specific event types
|
|
321
|
-
switch (event.type) {
|
|
322
|
-
case 'tool_proposed':
|
|
323
|
-
console.log(`Proposing: ${event.toolName}.${event.action}`);
|
|
324
|
-
break;
|
|
325
|
-
case 'tool_approved':
|
|
326
|
-
console.log(`✓ Approved`);
|
|
327
|
-
break;
|
|
328
|
-
case 'tool_rejected':
|
|
329
|
-
console.log(`✗ Rejected: ${event.reason}`);
|
|
330
|
-
break;
|
|
331
|
-
case 'task_completed':
|
|
332
|
-
console.log(`Done in ${event.iteration} iterations`);
|
|
333
|
-
break;
|
|
334
|
-
}
|
|
335
|
-
},
|
|
234
|
+
await runAgent({
|
|
235
|
+
model,
|
|
236
|
+
prompt: 'Fetch the data from https://api.example.com/status',
|
|
237
|
+
tools,
|
|
238
|
+
maxSteps: 5,
|
|
336
239
|
});
|
|
337
240
|
```
|
|
338
241
|
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
## 🔧 Native Tool Calling
|
|
242
|
+
### `jsonTool()`
|
|
342
243
|
|
|
343
|
-
|
|
244
|
+
Returns: `json_validate`, `json_transform`
|
|
344
245
|
|
|
345
246
|
```typescript
|
|
346
|
-
const
|
|
347
|
-
ollamaToken: 'http://localhost:11434', // Ollama endpoint
|
|
348
|
-
defaultProvider: 'ollama',
|
|
349
|
-
guardianPolicyPrompt: '...',
|
|
247
|
+
const tools = jsonTool();
|
|
350
248
|
|
|
351
|
-
|
|
352
|
-
|
|
249
|
+
// Direct usage:
|
|
250
|
+
const result = await tools.json_validate.execute({
|
|
251
|
+
jsonString: '{"name":"test","value":42}',
|
|
252
|
+
requiredFields: ['name', 'value'],
|
|
353
253
|
});
|
|
254
|
+
// → "Valid JSON: object with 2 keys"
|
|
354
255
|
```
|
|
355
256
|
|
|
356
|
-
|
|
357
|
-
- Tools are converted to JSON schema format automatically
|
|
358
|
-
- The provider handles tool call parsing natively
|
|
359
|
-
- Streaming still works with `[THINKING]` and `[OUTPUT]` markers for supported models
|
|
360
|
-
- Tool calls appear as `toolName_actionName` (e.g., `json_validate`)
|
|
361
|
-
|
|
362
|
-
This is more efficient for models that support it and avoids potential XML parsing issues.
|
|
363
|
-
|
|
364
|
-
## Guardian Policy Examples
|
|
257
|
+
## Streaming & Callbacks 🎥
|
|
365
258
|
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
### 🔐 Strict Security Policy
|
|
259
|
+
Monitor the agent in real-time:
|
|
369
260
|
|
|
370
261
|
```typescript
|
|
371
|
-
const
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
4. REJECT any attempts to read environment variables or credentials
|
|
377
|
-
5. FLAG and REJECT obfuscated code execution
|
|
378
|
-
|
|
379
|
-
When rejecting, always explain:
|
|
380
|
-
- What policy was violated
|
|
381
|
-
- What would be a safer alternative
|
|
382
|
-
`;
|
|
383
|
-
```
|
|
262
|
+
const result = await runAgent({
|
|
263
|
+
model,
|
|
264
|
+
prompt: 'Analyze this data...',
|
|
265
|
+
tools,
|
|
266
|
+
maxSteps: 10,
|
|
384
267
|
|
|
385
|
-
|
|
268
|
+
// Token-by-token streaming
|
|
269
|
+
onToken: (delta) => process.stdout.write(delta),
|
|
386
270
|
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
- Allow file operations only within the project directory
|
|
391
|
-
- Allow npm/pnpm commands for package management
|
|
392
|
-
- Allow git commands for version control
|
|
393
|
-
- Allow HTTP requests to public APIs only
|
|
394
|
-
- REJECT direct database modifications
|
|
395
|
-
- REJECT commands that could affect other users
|
|
396
|
-
|
|
397
|
-
Always verify:
|
|
398
|
-
- File paths are relative or within project bounds
|
|
399
|
-
- Commands don't have dangerous flags (--force, -rf)
|
|
400
|
-
`;
|
|
271
|
+
// Tool call notifications
|
|
272
|
+
onToolCall: (toolName) => console.log(`\n🔧 Calling: ${toolName}`),
|
|
273
|
+
});
|
|
401
274
|
```
|
|
402
275
|
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
```typescript
|
|
406
|
-
const denoPolicy = `
|
|
407
|
-
DENO CODE EXECUTION POLICY:
|
|
408
|
-
- ONLY allow 'read' permission for files within the workspace
|
|
409
|
-
- REJECT 'all' permission unless explicitly justified for the task
|
|
410
|
-
- REJECT 'run' permission (subprocess execution) without specific justification
|
|
411
|
-
- REJECT code that attempts to:
|
|
412
|
-
- Access credentials or environment secrets (even with 'env' permission)
|
|
413
|
-
- Make network requests to internal/private IP ranges
|
|
414
|
-
- Write to system directories
|
|
415
|
-
- FLAG obfuscated or encoded code (base64, eval with dynamic strings)
|
|
416
|
-
- Prefer sandboxed execution (no permissions) when possible
|
|
417
|
-
|
|
418
|
-
When evaluating code:
|
|
419
|
-
- Review the actual code content, not just permissions
|
|
420
|
-
- Consider what data the code could exfiltrate
|
|
421
|
-
- Verify network endpoints are legitimate public APIs
|
|
422
|
-
`;
|
|
423
|
-
```
|
|
276
|
+
## Context Overflow Handling 💥
|
|
424
277
|
|
|
425
|
-
|
|
278
|
+
For long-running agents that might exceed the model's context window, use the compaction subpath:
|
|
426
279
|
|
|
427
280
|
```typescript
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
guardianProvider?: TProvider; // Optional: separate provider for Guardian
|
|
443
|
-
|
|
444
|
-
// Agent configuration
|
|
445
|
-
driverSystemMessage?: string; // Custom system message for Driver
|
|
446
|
-
guardianPolicyPrompt: string; // REQUIRED: Policy for Guardian to enforce
|
|
447
|
-
name?: string; // Agent system name
|
|
448
|
-
verbose?: boolean; // Enable verbose logging
|
|
449
|
-
|
|
450
|
-
// Native tool calling
|
|
451
|
-
useNativeToolCalling?: boolean; // Use provider's native tool calling API (default: false)
|
|
452
|
-
|
|
453
|
-
// Limits
|
|
454
|
-
maxIterations?: number; // Max task iterations (default: 20)
|
|
455
|
-
maxConsecutiveRejections?: number; // Abort after N rejections (default: 3)
|
|
456
|
-
maxResultChars?: number; // Max chars for tool results before truncation (default: 15000)
|
|
457
|
-
maxHistoryMessages?: number; // Max history messages for API (default: 20)
|
|
458
|
-
|
|
459
|
-
// Callbacks
|
|
460
|
-
onProgress?: (event: IProgressEvent) => void; // Progress event callback
|
|
461
|
-
onToken?: (token: string, source: 'driver' | 'guardian') => void; // Streaming callback
|
|
462
|
-
logPrefix?: string; // Prefix for log messages
|
|
463
|
-
}
|
|
464
|
-
```
|
|
465
|
-
|
|
466
|
-
## Result Interface
|
|
467
|
-
|
|
468
|
-
```typescript
|
|
469
|
-
interface IDualAgentRunResult {
|
|
470
|
-
success: boolean; // Whether task completed successfully
|
|
471
|
-
completed: boolean; // Task completion status
|
|
472
|
-
result: string; // Final result or response
|
|
473
|
-
iterations: number; // Number of iterations taken
|
|
474
|
-
history: IAgentMessage[]; // Full conversation history
|
|
475
|
-
status: TDualAgentRunStatus; // 'completed' | 'max_iterations_reached' | etc.
|
|
476
|
-
toolCallCount?: number; // Number of tool calls made
|
|
477
|
-
rejectionCount?: number; // Number of Guardian rejections
|
|
478
|
-
toolLog?: IToolExecutionLog[]; // Detailed tool execution log
|
|
479
|
-
error?: string; // Error message if status is 'error'
|
|
480
|
-
}
|
|
481
|
-
|
|
482
|
-
type TDualAgentRunStatus =
|
|
483
|
-
| 'completed'
|
|
484
|
-
| 'in_progress'
|
|
485
|
-
| 'max_iterations_reached'
|
|
486
|
-
| 'max_rejections_reached'
|
|
487
|
-
| 'clarification_needed'
|
|
488
|
-
| 'error';
|
|
281
|
+
import { runAgent } from '@push.rocks/smartagent';
|
|
282
|
+
import { compactMessages } from '@push.rocks/smartagent/compaction';
|
|
283
|
+
|
|
284
|
+
const result = await runAgent({
|
|
285
|
+
model,
|
|
286
|
+
prompt: 'Process all 500 files...',
|
|
287
|
+
tools,
|
|
288
|
+
maxSteps: 100,
|
|
289
|
+
|
|
290
|
+
onContextOverflow: async (messages) => {
|
|
291
|
+
// Summarize the conversation to free up context space
|
|
292
|
+
return await compactMessages(model, messages);
|
|
293
|
+
},
|
|
294
|
+
});
|
|
489
295
|
```
|
|
490
296
|
|
|
491
|
-
##
|
|
297
|
+
## Output Truncation ✂️
|
|
492
298
|
|
|
493
|
-
|
|
299
|
+
Prevent large tool outputs from consuming too much context:
|
|
494
300
|
|
|
495
301
|
```typescript
|
|
496
|
-
import {
|
|
497
|
-
|
|
498
|
-
class MyCustomTool extends BaseToolWrapper {
|
|
499
|
-
public name = 'custom';
|
|
500
|
-
public description = 'My custom tool for specific operations';
|
|
501
|
-
|
|
502
|
-
public actions: IToolAction[] = [
|
|
503
|
-
{
|
|
504
|
-
name: 'myAction',
|
|
505
|
-
description: 'Performs a custom action',
|
|
506
|
-
parameters: {
|
|
507
|
-
type: 'object',
|
|
508
|
-
properties: {
|
|
509
|
-
input: { type: 'string', description: 'Input for the action' },
|
|
510
|
-
},
|
|
511
|
-
required: ['input'],
|
|
512
|
-
},
|
|
513
|
-
},
|
|
514
|
-
];
|
|
515
|
-
|
|
516
|
-
public async initialize(): Promise<void> {
|
|
517
|
-
// Setup your tool (called when orchestrator.start() runs)
|
|
518
|
-
this.isInitialized = true;
|
|
519
|
-
}
|
|
520
|
-
|
|
521
|
-
public async cleanup(): Promise<void> {
|
|
522
|
-
// Cleanup resources (called when orchestrator.stop() runs)
|
|
523
|
-
this.isInitialized = false;
|
|
524
|
-
}
|
|
525
|
-
|
|
526
|
-
public async execute(action: string, params: Record<string, unknown>): Promise<IToolExecutionResult> {
|
|
527
|
-
this.validateAction(action);
|
|
528
|
-
this.ensureInitialized();
|
|
529
|
-
|
|
530
|
-
if (action === 'myAction') {
|
|
531
|
-
return {
|
|
532
|
-
success: true,
|
|
533
|
-
result: { processed: params.input },
|
|
534
|
-
summary: `Processed input: ${params.input}`, // Optional human-readable summary
|
|
535
|
-
};
|
|
536
|
-
}
|
|
537
|
-
|
|
538
|
-
return { success: false, error: 'Unknown action' };
|
|
539
|
-
}
|
|
540
|
-
|
|
541
|
-
// Human-readable summary for Guardian evaluation
|
|
542
|
-
public getCallSummary(action: string, params: Record<string, unknown>): string {
|
|
543
|
-
return `Custom action "${action}" with input "${params.input}"`;
|
|
544
|
-
}
|
|
545
|
-
}
|
|
302
|
+
import { truncateOutput } from '@push.rocks/smartagent';
|
|
546
303
|
|
|
547
|
-
|
|
548
|
-
|
|
304
|
+
const { content, truncated, notice } = truncateOutput(hugeOutput, {
|
|
305
|
+
maxLines: 2000, // default
|
|
306
|
+
maxBytes: 50_000, // default
|
|
307
|
+
});
|
|
549
308
|
```
|
|
550
309
|
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
If you already have a `@push.rocks/smartai` instance, you can share it:
|
|
310
|
+
The built-in tool factories use `truncateOutput` internally.
|
|
554
311
|
|
|
555
|
-
|
|
556
|
-
import { SmartAi } from '@push.rocks/smartai';
|
|
557
|
-
import { DualAgentOrchestrator } from '@push.rocks/smartagent';
|
|
312
|
+
## Multi-Turn Conversations 💬
|
|
558
313
|
|
|
559
|
-
|
|
560
|
-
await smartai.start();
|
|
314
|
+
Pass the returned `messages` back for multi-turn interactions:
|
|
561
315
|
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
316
|
+
```typescript
|
|
317
|
+
// First turn
|
|
318
|
+
const turn1 = await runAgent({
|
|
319
|
+
model,
|
|
320
|
+
prompt: 'Create a project structure',
|
|
321
|
+
tools,
|
|
322
|
+
maxSteps: 10,
|
|
565
323
|
});
|
|
566
324
|
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
325
|
+
// Second turn — continues the conversation
|
|
326
|
+
const turn2 = await runAgent({
|
|
327
|
+
model,
|
|
328
|
+
prompt: 'Now add a README to the project',
|
|
329
|
+
tools,
|
|
330
|
+
maxSteps: 10,
|
|
331
|
+
messages: turn1.messages, // pass history
|
|
332
|
+
});
|
|
573
333
|
```
|
|
574
334
|
|
|
575
|
-
##
|
|
576
|
-
|
|
577
|
-
SmartAgent supports all providers from `@push.rocks/smartai`:
|
|
335
|
+
## Exports
|
|
578
336
|
|
|
579
|
-
|
|
580
|
-
|----------|:------:|:--------:|
|
|
581
|
-
| OpenAI | ✅ | ✅ |
|
|
582
|
-
| Anthropic | ✅ | ✅ |
|
|
583
|
-
| Perplexity | ✅ | ✅ |
|
|
584
|
-
| Groq | ✅ | ✅ |
|
|
585
|
-
| Ollama | ✅ | ✅ |
|
|
586
|
-
| XAI | ✅ | ✅ |
|
|
587
|
-
| Exo | ✅ | ✅ |
|
|
337
|
+
### Main (`@push.rocks/smartagent`)
|
|
588
338
|
|
|
589
|
-
|
|
339
|
+
| Export | Type | Description |
|
|
340
|
+
|--------|------|-------------|
|
|
341
|
+
| `runAgent` | function | Core agentic loop |
|
|
342
|
+
| `ToolRegistry` | class | Tool collection helper |
|
|
343
|
+
| `truncateOutput` | function | Output truncation utility |
|
|
344
|
+
| `ContextOverflowError` | class | Error type for context overflow |
|
|
345
|
+
| `tool` | function | Re-exported from `@push.rocks/smartai` |
|
|
346
|
+
| `z` | object | Re-exported Zod for schema definitions |
|
|
347
|
+
| `stepCountIs` | function | Re-exported from AI SDK |
|
|
348
|
+
| `jsonSchema` | function | Re-exported from `@push.rocks/smartai` |
|
|
590
349
|
|
|
591
|
-
|
|
592
|
-
const orchestrator = new DualAgentOrchestrator({
|
|
593
|
-
openaiToken: 'sk-...',
|
|
594
|
-
groqToken: 'gsk-...',
|
|
595
|
-
defaultProvider: 'openai', // Driver uses OpenAI
|
|
596
|
-
guardianProvider: 'groq', // Guardian uses Groq (faster, cheaper)
|
|
597
|
-
guardianPolicyPrompt: '...',
|
|
598
|
-
});
|
|
599
|
-
```
|
|
350
|
+
### Tools (`@push.rocks/smartagent/tools`)
|
|
600
351
|
|
|
601
|
-
|
|
352
|
+
| Export | Type | Description |
|
|
353
|
+
|--------|------|-------------|
|
|
354
|
+
| `filesystemTool` | factory | File operations (read, write, list, delete) |
|
|
355
|
+
| `shellTool` | factory | Shell command execution |
|
|
356
|
+
| `httpTool` | factory | HTTP GET/POST requests |
|
|
357
|
+
| `jsonTool` | factory | JSON validation and transformation |
|
|
602
358
|
|
|
603
|
-
###
|
|
359
|
+
### Compaction (`@push.rocks/smartagent/compaction`)
|
|
604
360
|
|
|
605
|
-
|
|
|
606
|
-
|
|
607
|
-
| `
|
|
608
|
-
| `stop()` | Cleanup all tools and resources |
|
|
609
|
-
| `run(task, options?)` | Execute a task with optional images for vision |
|
|
610
|
-
| `continueTask(input)` | Continue a task with user input |
|
|
611
|
-
| `registerTool(tool)` | Register a custom tool |
|
|
612
|
-
| `registerStandardTools()` | Register all built-in tools (Filesystem, HTTP, Shell, Browser, Deno) |
|
|
613
|
-
| `registerScopedFilesystemTool(basePath, excludePatterns?)` | Register filesystem tool with path restriction |
|
|
614
|
-
| `setGuardianPolicy(policy)` | Update Guardian policy at runtime |
|
|
615
|
-
| `getHistory()` | Get conversation history |
|
|
616
|
-
| `getToolNames()` | Get list of registered tool names |
|
|
617
|
-
| `isActive()` | Check if orchestrator is running |
|
|
361
|
+
| Export | Type | Description |
|
|
362
|
+
|--------|------|-------------|
|
|
363
|
+
| `compactMessages` | function | Summarize message history to free context |
|
|
618
364
|
|
|
619
|
-
|
|
365
|
+
## Dependencies
|
|
620
366
|
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
// Tools
|
|
628
|
-
export { BaseToolWrapper } from '@push.rocks/smartagent';
|
|
629
|
-
export { FilesystemTool, type IFilesystemToolOptions } from '@push.rocks/smartagent';
|
|
630
|
-
export { HttpTool } from '@push.rocks/smartagent';
|
|
631
|
-
export { ShellTool } from '@push.rocks/smartagent';
|
|
632
|
-
export { BrowserTool } from '@push.rocks/smartagent';
|
|
633
|
-
export { DenoTool, type TDenoPermission } from '@push.rocks/smartagent';
|
|
634
|
-
export { JsonValidatorTool } from '@push.rocks/smartagent';
|
|
635
|
-
|
|
636
|
-
// Types and interfaces
|
|
637
|
-
export * from '@push.rocks/smartagent'; // All interfaces
|
|
638
|
-
|
|
639
|
-
// Re-exported from @push.rocks/smartai
|
|
640
|
-
export { type ISmartAiOptions, type TProvider, type ChatMessage, type ChatOptions, type ChatResponse };
|
|
641
|
-
```
|
|
367
|
+
- **[`@push.rocks/smartai`](https://code.foss.global/push.rocks/smartai)** — Provider registry, `getModel()`, re-exports `tool`/`jsonSchema`
|
|
368
|
+
- **[`ai`](https://www.npmjs.com/package/ai)** v6 — Vercel AI SDK (`streamText`, `stepCountIs`, `ModelMessage`)
|
|
369
|
+
- **[`zod`](https://www.npmjs.com/package/zod)** — Tool input schema definitions
|
|
370
|
+
- **[`@push.rocks/smartfs`](https://code.foss.global/push.rocks/smartfs)** — Filesystem tool implementation
|
|
371
|
+
- **[`@push.rocks/smartshell`](https://code.foss.global/push.rocks/smartshell)** — Shell tool implementation
|
|
372
|
+
- **[`@push.rocks/smartrequest`](https://code.foss.global/push.rocks/smartrequest)** — HTTP tool implementation
|
|
642
373
|
|
|
643
374
|
## License and Legal Information
|
|
644
375
|
|