@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.
Files changed (81) hide show
  1. package/dist_ts/00_commitinfo_data.js +3 -3
  2. package/dist_ts/index.d.ts +9 -12
  3. package/dist_ts/index.js +9 -20
  4. package/dist_ts/plugins.d.ts +8 -9
  5. package/dist_ts/plugins.js +10 -12
  6. package/dist_ts/smartagent.classes.agent.d.ts +2 -0
  7. package/dist_ts/smartagent.classes.agent.js +173 -0
  8. package/dist_ts/smartagent.classes.toolregistry.d.ts +12 -0
  9. package/dist_ts/smartagent.classes.toolregistry.js +17 -0
  10. package/dist_ts/smartagent.interfaces.d.ts +47 -231
  11. package/dist_ts/smartagent.interfaces.js +6 -7
  12. package/dist_ts/smartagent.utils.truncation.d.ts +10 -0
  13. package/dist_ts/smartagent.utils.truncation.js +26 -0
  14. package/dist_ts_compaction/index.d.ts +1 -0
  15. package/dist_ts_compaction/index.js +2 -0
  16. package/dist_ts_compaction/plugins.d.ts +4 -0
  17. package/dist_ts_compaction/plugins.js +3 -0
  18. package/dist_ts_compaction/smartagent.compaction.d.ts +10 -0
  19. package/dist_ts_compaction/smartagent.compaction.js +46 -0
  20. package/dist_ts_tools/index.d.ts +8 -0
  21. package/dist_ts_tools/index.js +6 -0
  22. package/dist_ts_tools/plugins.d.ts +15 -0
  23. package/dist_ts_tools/plugins.js +19 -0
  24. package/dist_ts_tools/tool.filesystem.d.ts +6 -0
  25. package/dist_ts_tools/tool.filesystem.js +102 -0
  26. package/dist_ts_tools/tool.http.d.ts +2 -0
  27. package/dist_ts_tools/tool.http.js +65 -0
  28. package/dist_ts_tools/tool.json.d.ts +2 -0
  29. package/dist_ts_tools/tool.json.js +47 -0
  30. package/dist_ts_tools/tool.shell.d.ts +8 -0
  31. package/dist_ts_tools/tool.shell.js +40 -0
  32. package/npmextra.json +1 -1
  33. package/package.json +30 -18
  34. package/readme.hints.md +43 -42
  35. package/readme.md +257 -526
  36. package/ts/00_commitinfo_data.ts +2 -2
  37. package/ts/index.ts +11 -31
  38. package/ts/plugins.ts +22 -21
  39. package/ts/smartagent.classes.agent.ts +198 -0
  40. package/ts/smartagent.classes.toolregistry.ts +20 -0
  41. package/ts/smartagent.interfaces.ts +51 -303
  42. package/ts/smartagent.utils.truncation.ts +39 -0
  43. package/ts_compaction/index.ts +1 -0
  44. package/ts_compaction/plugins.ts +6 -0
  45. package/ts_compaction/smartagent.compaction.ts +51 -0
  46. package/ts_tools/index.ts +8 -0
  47. package/ts_tools/plugins.ts +30 -0
  48. package/ts_tools/tool.filesystem.ts +131 -0
  49. package/ts_tools/tool.http.ts +78 -0
  50. package/ts_tools/tool.json.ts +53 -0
  51. package/ts_tools/tool.shell.ts +62 -0
  52. package/dist_ts/smartagent.classes.driveragent.d.ts +0 -134
  53. package/dist_ts/smartagent.classes.driveragent.js +0 -671
  54. package/dist_ts/smartagent.classes.dualagent.d.ts +0 -79
  55. package/dist_ts/smartagent.classes.dualagent.js +0 -583
  56. package/dist_ts/smartagent.classes.guardianagent.d.ts +0 -46
  57. package/dist_ts/smartagent.classes.guardianagent.js +0 -201
  58. package/dist_ts/smartagent.tools.base.d.ts +0 -52
  59. package/dist_ts/smartagent.tools.base.js +0 -42
  60. package/dist_ts/smartagent.tools.browser.d.ts +0 -17
  61. package/dist_ts/smartagent.tools.browser.js +0 -229
  62. package/dist_ts/smartagent.tools.deno.d.ts +0 -21
  63. package/dist_ts/smartagent.tools.deno.js +0 -191
  64. package/dist_ts/smartagent.tools.filesystem.d.ts +0 -40
  65. package/dist_ts/smartagent.tools.filesystem.js +0 -801
  66. package/dist_ts/smartagent.tools.http.d.ts +0 -16
  67. package/dist_ts/smartagent.tools.http.js +0 -264
  68. package/dist_ts/smartagent.tools.json.d.ts +0 -24
  69. package/dist_ts/smartagent.tools.json.js +0 -202
  70. package/dist_ts/smartagent.tools.shell.d.ts +0 -17
  71. package/dist_ts/smartagent.tools.shell.js +0 -202
  72. package/ts/smartagent.classes.driveragent.ts +0 -775
  73. package/ts/smartagent.classes.dualagent.ts +0 -657
  74. package/ts/smartagent.classes.guardianagent.ts +0 -241
  75. package/ts/smartagent.tools.base.ts +0 -83
  76. package/ts/smartagent.tools.browser.ts +0 -253
  77. package/ts/smartagent.tools.deno.ts +0 -230
  78. package/ts/smartagent.tools.filesystem.ts +0 -885
  79. package/ts/smartagent.tools.http.ts +0 -283
  80. package/ts/smartagent.tools.json.ts +0 -224
  81. 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 dual-agent agentic framework with **Driver** and **Guardian** agents for safe, policy-controlled AI task execution. 🤖🛡️
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
- SmartAgent implements a **dual-agent architecture** where AI safety isn't just an afterthoughtit's baked into the core design:
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
- - **🎯 Driver Agent**: The executor. Reasons about goals, plans steps, and proposes tool calls
22
- - **🛡️ Guardian Agent**: The gatekeeper. Evaluates every tool call against your policy, approving or rejecting with feedback
19
+ ```typescript
20
+ import { runAgent, tool, z } from '@push.rocks/smartagent';
21
+ import { getModel } from '@push.rocks/smartai';
23
22
 
24
- This design ensures safe tool use through **AI-based policy evaluation** rather than rigid programmatic rules. The Guardian can understand context, nuance, and intent—catching dangerous operations that simple regex or allowlists would miss.
23
+ const model = getModel({
24
+ provider: 'anthropic',
25
+ model: 'claude-sonnet-4-5-20250929',
26
+ apiKey: process.env.ANTHROPIC_TOKEN,
27
+ });
25
28
 
26
- ### Why Dual-Agent?
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
- Traditional AI agents have a fundamental problem: they're given tools and expected to use them responsibly. SmartAgent adds a second AI specifically trained to evaluate whether each action is safe and appropriate. Think of it as separation of concerns, but for AI safety.
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
- ## Quick Start
62
-
63
- ```typescript
64
- import { DualAgentOrchestrator } from '@push.rocks/smartagent';
65
-
66
- // Create orchestrator with Guardian policy
67
- const orchestrator = new DualAgentOrchestrator({
68
- openaiToken: 'sk-...',
69
- defaultProvider: 'openai',
70
- guardianPolicyPrompt: `
71
- FILE SYSTEM POLICY:
72
- - ONLY allow reading/writing within /tmp or the current working directory
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
- ## Standard Tools
101
-
102
- SmartAgent comes with five battle-tested tools out of the box via `registerStandardTools()`:
73
+ **Key features:**
103
74
 
104
- ### 🗂️ FilesystemTool
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
- File and directory operations powered by `@push.rocks/smartfs`.
82
+ ## Core API
107
83
 
108
- **Actions**: `read`, `write`, `append`, `list`, `delete`, `exists`, `stat`, `copy`, `move`, `mkdir`
84
+ ### `runAgent(options): Promise<IAgentRunResult>`
109
85
 
110
- ```typescript
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
- ```typescript
123
- // Only allow access within a specific directory
124
- orchestrator.registerScopedFilesystemTool('/home/user/workspace');
125
-
126
- // With exclusion patterns (glob syntax)
127
- orchestrator.registerScopedFilesystemTool('/home/user/workspace', [
128
- '.nogit/**',
129
- 'node_modules/**',
130
- '*.secret',
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
- **Line-range Reading**: Read specific portions of large files:
101
+ ### `IAgentRunResult`
135
102
 
136
103
  ```typescript
137
- <tool_call>
138
- <tool>filesystem</tool>
139
- <action>read</action>
140
- <params>{"path": "/var/log/app.log", "startLine": 100, "endLine": 150}</params>
141
- <reasoning>Reading only the relevant log section to avoid token overload</reasoning>
142
- </tool_call>
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
- ### 🌐 HttpTool
146
-
147
- HTTP requests using `@push.rocks/smartrequest`.
117
+ ## Defining Tools 🛠️
148
118
 
149
- **Actions**: `get`, `post`, `put`, `patch`, `delete`
119
+ Tools use Vercel AI SDK's `tool()` helper with Zod schemas:
150
120
 
151
121
  ```typescript
152
- <tool_call>
153
- <tool>http</tool>
154
- <action>get</action>
155
- <params>{"url": "https://api.example.com/data", "headers": {"Authorization": "Bearer token"}}</params>
156
- <reasoning>Fetching data from the API endpoint</reasoning>
157
- </tool_call>
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
- ### 💻 ShellTool
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
- <tool_call>
168
- <tool>shell</tool>
169
- <action>execute</action>
170
- <params>{"command": "ls", "args": ["-la", "/tmp"]}</params>
171
- <reasoning>Listing directory contents to find relevant files</reasoning>
172
- </tool_call>
140
+ await runAgent({
141
+ model,
142
+ prompt: 'Do the thing',
143
+ tools: { myTool, anotherTool },
144
+ maxSteps: 10,
145
+ });
173
146
  ```
174
147
 
175
- > 🔒 **Security Note**: The shell tool uses `execSpawn` with `shell: false`, meaning command and arguments are passed separately. This makes shell injection attacks impossible.
176
-
177
- ### 🌍 BrowserTool
148
+ ## ToolRegistry
178
149
 
179
- Web page interaction using `@push.rocks/smartbrowser` (Puppeteer-based).
180
-
181
- **Actions**: `screenshot`, `pdf`, `evaluate`, `getPageContent`
150
+ A lightweight helper for collecting tools:
182
151
 
183
152
  ```typescript
184
- <tool_call>
185
- <tool>browser</tool>
186
- <action>getPageContent</action>
187
- <params>{"url": "https://example.com"}</params>
188
- <reasoning>Extracting text content from the webpage</reasoning>
189
- </tool_call>
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
- ### 🦕 DenoTool
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
- **Permissions**: `all`, `env`, `ffi`, `hrtime`, `net`, `read`, `run`, `sys`, `write`
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
- // Simple code execution (sandboxed, no permissions)
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
- ## Additional Tools
234
-
235
- ### 📋 JsonValidatorTool
190
+ ### `filesystemTool(options?)`
236
191
 
237
- Validate and format JSON data. Perfect for agents to self-check their JSON output before completing tasks.
238
-
239
- **Actions**: `validate`, `format`
192
+ Returns: `read_file`, `write_file`, `list_directory`, `delete_file`
240
193
 
241
194
  ```typescript
242
- import { JsonValidatorTool } from '@push.rocks/smartagent';
195
+ const tools = filesystemTool({ rootDir: '/home/user/workspace' });
243
196
 
244
- // Register the JSON validator tool (not included in registerStandardTools)
245
- orchestrator.registerTool(new JsonValidatorTool());
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
- ```typescript
249
- // Validate JSON with required field checking
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
- ## 🎥 Streaming Support
208
+ ### `shellTool(options?)`
270
209
 
271
- SmartAgent supports token-by-token streaming for real-time output during LLM generation:
210
+ Returns: `run_command`
272
211
 
273
212
  ```typescript
274
- const orchestrator = new DualAgentOrchestrator({
275
- openaiToken: 'sk-...',
276
- defaultProvider: 'openai',
277
- guardianPolicyPrompt: '...',
278
-
279
- // Token streaming callback
280
- onToken: (token, source) => {
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
- This is perfect for CLI applications or UIs that need to show progress as the agent thinks.
223
+ Options:
224
+ - `cwd` — working directory for commands
225
+ - `allowedCommands` — whitelist of allowed commands (if set, others are rejected)
288
226
 
289
- ## 🖼️ Vision Support
227
+ ### `httpTool()`
290
228
 
291
- Pass images to vision-capable models for multimodal tasks:
229
+ Returns: `http_get`, `http_post`
292
230
 
293
231
  ```typescript
294
- import { readFileSync } from 'fs';
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
- ```typescript
311
- const orchestrator = new DualAgentOrchestrator({
312
- openaiToken: 'sk-...',
313
- guardianPolicyPrompt: '...',
314
- logPrefix: '[MyAgent]', // Optional prefix for log messages
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
- **Event Types**: `task_started`, `iteration_started`, `tool_proposed`, `guardian_evaluating`, `tool_approved`, `tool_rejected`, `tool_executing`, `tool_completed`, `task_completed`, `clarification_needed`, `max_iterations`, `max_rejections`
340
-
341
- ## 🔧 Native Tool Calling
242
+ ### `jsonTool()`
342
243
 
343
- For providers that support native tool calling (like Ollama with certain models), SmartAgent can use the provider's built-in tool calling API instead of XML parsing:
244
+ Returns: `json_validate`, `json_transform`
344
245
 
345
246
  ```typescript
346
- const orchestrator = new DualAgentOrchestrator({
347
- ollamaToken: 'http://localhost:11434', // Ollama endpoint
348
- defaultProvider: 'ollama',
349
- guardianPolicyPrompt: '...',
247
+ const tools = jsonTool();
350
248
 
351
- // Enable native tool calling
352
- useNativeToolCalling: true,
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
- When `useNativeToolCalling` is enabled:
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
- The Guardian's power comes from your policy. Here are battle-tested examples:
367
-
368
- ### 🔐 Strict Security Policy
259
+ Monitor the agent in real-time:
369
260
 
370
261
  ```typescript
371
- const securityPolicy = `
372
- SECURITY POLICY:
373
- 1. REJECT any file operations outside /home/user/workspace
374
- 2. REJECT any shell commands that could modify system state
375
- 3. REJECT any HTTP requests to internal/private IP ranges
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
- ### 🛠️ Development Environment Policy
268
+ // Token-by-token streaming
269
+ onToken: (delta) => process.stdout.write(delta),
386
270
 
387
- ```typescript
388
- const devPolicy = `
389
- DEVELOPMENT POLICY:
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
- ### 🦕 Deno Code Execution Policy
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
- ## Configuration Options
278
+ For long-running agents that might exceed the model's context window, use the compaction subpath:
426
279
 
427
280
  ```typescript
428
- interface IDualAgentOptions {
429
- // Provider tokens (from @push.rocks/smartai)
430
- openaiToken?: string;
431
- anthropicToken?: string;
432
- perplexityToken?: string;
433
- groqToken?: string;
434
- xaiToken?: string;
435
- ollamaToken?: string; // URL for Ollama endpoint
436
-
437
- // Use existing SmartAi instance (optional - avoids duplicate providers)
438
- smartAiInstance?: SmartAi;
439
-
440
- // Provider selection
441
- defaultProvider?: TProvider; // For both Driver and Guardian
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
- ## Custom Tools
297
+ ## Output Truncation ✂️
492
298
 
493
- Create custom tools by extending `BaseToolWrapper`:
299
+ Prevent large tool outputs from consuming too much context:
494
300
 
495
301
  ```typescript
496
- import { BaseToolWrapper, IToolAction, IToolExecutionResult } from '@push.rocks/smartagent';
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
- // Register custom tool
548
- orchestrator.registerTool(new MyCustomTool());
304
+ const { content, truncated, notice } = truncateOutput(hugeOutput, {
305
+ maxLines: 2000, // default
306
+ maxBytes: 50_000, // default
307
+ });
549
308
  ```
550
309
 
551
- ## Reusing SmartAi Instances
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
- ```typescript
556
- import { SmartAi } from '@push.rocks/smartai';
557
- import { DualAgentOrchestrator } from '@push.rocks/smartagent';
312
+ ## Multi-Turn Conversations 💬
558
313
 
559
- const smartai = new SmartAi({ openaiToken: 'sk-...' });
560
- await smartai.start();
314
+ Pass the returned `messages` back for multi-turn interactions:
561
315
 
562
- const orchestrator = new DualAgentOrchestrator({
563
- smartAiInstance: smartai, // Reuse existing instance
564
- guardianPolicyPrompt: '...',
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
- await orchestrator.start();
568
- // ... use orchestrator ...
569
- await orchestrator.stop();
570
-
571
- // SmartAi instance lifecycle is managed separately
572
- await smartai.stop();
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
- ## Supported Providers
576
-
577
- SmartAgent supports all providers from `@push.rocks/smartai`:
335
+ ## Exports
578
336
 
579
- | Provider | Driver | Guardian |
580
- |----------|:------:|:--------:|
581
- | OpenAI | ✅ | ✅ |
582
- | Anthropic | ✅ | ✅ |
583
- | Perplexity | ✅ | ✅ |
584
- | Groq | ✅ | ✅ |
585
- | Ollama | ✅ | ✅ |
586
- | XAI | ✅ | ✅ |
587
- | Exo | ✅ | ✅ |
337
+ ### Main (`@push.rocks/smartagent`)
588
338
 
589
- **💡 Pro tip**: Use a faster/cheaper model for Guardian (like Groq) and a more capable model for Driver:
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
- ```typescript
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
- ## API Reference
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
- ### DualAgentOrchestrator
359
+ ### Compaction (`@push.rocks/smartagent/compaction`)
604
360
 
605
- | Method | Description |
606
- |--------|-------------|
607
- | `start()` | Initialize all tools and AI providers |
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
- ### Exports
365
+ ## Dependencies
620
366
 
621
- ```typescript
622
- // Main classes
623
- export { DualAgentOrchestrator } from '@push.rocks/smartagent';
624
- export { DriverAgent } from '@push.rocks/smartagent';
625
- export { GuardianAgent } from '@push.rocks/smartagent';
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