@push.rocks/smartagent 1.8.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 (87) hide show
  1. package/dist_ts/00_commitinfo_data.js +3 -3
  2. package/dist_ts/index.d.ts +8 -14
  3. package/dist_ts/index.js +8 -24
  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 +7 -70
  9. package/dist_ts/smartagent.classes.toolregistry.js +11 -155
  10. package/dist_ts/smartagent.interfaces.d.ts +47 -283
  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 +38 -84
  35. package/readme.md +254 -682
  36. package/ts/00_commitinfo_data.ts +2 -2
  37. package/ts/index.ts +10 -37
  38. package/ts/plugins.ts +22 -21
  39. package/ts/smartagent.classes.agent.ts +198 -0
  40. package/ts/smartagent.classes.toolregistry.ts +11 -179
  41. package/ts/smartagent.interfaces.ts +51 -363
  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 -93
  55. package/dist_ts/smartagent.classes.dualagent.js +0 -614
  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.expert.d.ts +0 -27
  65. package/dist_ts/smartagent.tools.expert.js +0 -126
  66. package/dist_ts/smartagent.tools.filesystem.d.ts +0 -40
  67. package/dist_ts/smartagent.tools.filesystem.js +0 -801
  68. package/dist_ts/smartagent.tools.http.d.ts +0 -16
  69. package/dist_ts/smartagent.tools.http.js +0 -264
  70. package/dist_ts/smartagent.tools.json.d.ts +0 -24
  71. package/dist_ts/smartagent.tools.json.js +0 -202
  72. package/dist_ts/smartagent.tools.search.d.ts +0 -29
  73. package/dist_ts/smartagent.tools.search.js +0 -215
  74. package/dist_ts/smartagent.tools.shell.d.ts +0 -17
  75. package/dist_ts/smartagent.tools.shell.js +0 -202
  76. package/ts/smartagent.classes.driveragent.ts +0 -775
  77. package/ts/smartagent.classes.dualagent.ts +0 -692
  78. package/ts/smartagent.classes.guardianagent.ts +0 -241
  79. package/ts/smartagent.tools.base.ts +0 -83
  80. package/ts/smartagent.tools.browser.ts +0 -253
  81. package/ts/smartagent.tools.deno.ts +0 -230
  82. package/ts/smartagent.tools.expert.ts +0 -144
  83. package/ts/smartagent.tools.filesystem.ts +0 -885
  84. package/ts/smartagent.tools.http.ts +0 -283
  85. package/ts/smartagent.tools.json.ts +0 -224
  86. package/ts/smartagent.tools.search.ts +0 -237
  87. 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,788 +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
- Registry["ToolRegistry<br/><i>Visibility & Lifecycle</i>"]
41
- Driver["Driver Agent<br/><i>Reason + Plan</i>"]
42
- Guardian["Guardian Agent<br/><i>Evaluate against policy</i>"]
43
-
44
- Driver -->|"tool call proposal"| Guardian
45
- Guardian -->|"approve / reject + feedback"| Driver
46
- Registry -->|"visible tools"| Driver
47
- end
48
-
49
- subgraph Tools["Tools"]
50
- Initial["Initial Tools<br/><i>Always visible</i>"]
51
- OnDemand["On-Demand Tools<br/><i>Discoverable via search</i>"]
52
- Experts["Expert SubAgents<br/><i>Specialized agents as tools</i>"]
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
73
+ **Key features:**
101
74
 
102
- SmartAgent comes with five battle-tested tools out of the box via `registerStandardTools()`:
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
103
81
 
104
- ### 🗂️ FilesystemTool
82
+ ## Core API
105
83
 
106
- File and directory operations powered by `@push.rocks/smartfs`.
84
+ ### `runAgent(options): Promise<IAgentRunResult>`
107
85
 
108
- **Actions**: `read`, `write`, `append`, `list`, `delete`, `exists`, `stat`, `copy`, `move`, `mkdir`
86
+ The single entry point. Options:
109
87
 
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
- ```
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) |
119
100
 
120
- **Scoped Filesystem**: Lock file operations to a specific directory with optional exclusion patterns:
101
+ ### `IAgentRunResult`
121
102
 
122
103
  ```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
- ```
133
-
134
- **Line-range Reading**: Read specific portions of large files:
135
-
136
- ```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
117
+ ## Defining Tools 🛠️
146
118
 
147
- HTTP requests using `@push.rocks/smartrequest`.
148
-
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.
182
+ ## Built-in Tool Factories 🧰
195
183
 
196
- **Actions**: `execute`, `executeWithResult`
197
-
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
236
-
237
- Validate and format JSON data. Perfect for agents to self-check their JSON output before completing tasks.
190
+ ### `filesystemTool(options?)`
238
191
 
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());
246
- ```
247
-
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>
197
+ await runAgent({
198
+ model,
199
+ prompt: 'Create a file called hello.txt with "Hello World"',
200
+ tools,
201
+ maxSteps: 5,
202
+ });
267
203
  ```
268
204
 
269
- ### 🔍 ToolSearchTool
205
+ Options:
206
+ - `rootDir` — restrict all file operations to this directory. Paths outside it throw `Access denied`.
270
207
 
271
- Enable the Driver to discover and activate on-demand tools at runtime.
208
+ ### `shellTool(options?)`
272
209
 
273
- **Actions**: `search`, `list`, `activate`, `details`
210
+ Returns: `run_command`
274
211
 
275
212
  ```typescript
276
- // Enable tool search (adds the 'tools' tool)
277
- orchestrator.enableToolSearch();
278
- ```
213
+ const tools = shellTool({ cwd: '/tmp', allowedCommands: ['ls', 'echo', 'cat'] });
279
214
 
280
- ```typescript
281
- // Search for tools by capability
282
- <tool_call>
283
- <tool>tools</tool>
284
- <action>search</action>
285
- <params>{"query": "database"}</params>
286
- </tool_call>
287
-
288
- // List all available tools
289
- <tool_call>
290
- <tool>tools</tool>
291
- <action>list</action>
292
- <params>{}</params>
293
- </tool_call>
294
-
295
- // Activate an on-demand tool
296
- <tool_call>
297
- <tool>tools</tool>
298
- <action>activate</action>
299
- <params>{"name": "database_expert"}</params>
300
- </tool_call>
301
-
302
- // Get detailed information about a tool
303
- <tool_call>
304
- <tool>tools</tool>
305
- <action>details</action>
306
- <params>{"name": "filesystem"}</params>
307
- </tool_call>
308
- ```
309
-
310
- ### 🧠 ExpertTool (SubAgents)
311
-
312
- Create specialized sub-agents that can be invoked as tools. Experts are complete `DualAgentOrchestrator` instances wrapped as tools, enabling hierarchical agent architectures.
313
-
314
- **Actions**: `consult`
315
-
316
- ```typescript
317
- // Register an expert for code review
318
- orchestrator.registerExpert({
319
- name: 'code_reviewer',
320
- description: 'Reviews code for quality, bugs, and best practices',
321
- systemMessage: `You are an expert code reviewer. Analyze code for:
322
- - Bugs and potential issues
323
- - Code style and best practices
324
- - Performance concerns
325
- - Security vulnerabilities`,
326
- guardianPolicy: 'Allow read-only file access within the workspace',
327
- tools: [new FilesystemTool()],
328
- visibility: 'on-demand', // Only available via tool search
329
- tags: ['code', 'review', 'quality'],
330
- category: 'expert',
215
+ await runAgent({
216
+ model,
217
+ prompt: 'List all files in /tmp',
218
+ tools,
219
+ maxSteps: 5,
331
220
  });
332
221
  ```
333
222
 
334
- ```typescript
335
- // Consult an expert
336
- <tool_call>
337
- <tool>code_reviewer</tool>
338
- <action>consult</action>
339
- <params>{
340
- "task": "Review this function for potential issues",
341
- "context": "This is a user authentication handler"
342
- }</params>
343
- </tool_call>
344
- ```
345
-
346
- ## 🎯 Tool Visibility System
347
-
348
- SmartAgent supports **tool visibility modes** for scalable agent architectures:
349
-
350
- - **`initial`** (default): Tool is visible to the Driver from the start, included in the system prompt
351
- - **`on-demand`**: Tool is hidden until explicitly activated via `tools.activate()`
352
-
353
- This enables you to have many specialized tools/experts without overwhelming the Driver's context.
354
-
355
- ```typescript
356
- // Register a tool with on-demand visibility
357
- orchestrator.registerTool(new MySpecializedTool(), {
358
- visibility: 'on-demand',
359
- tags: ['specialized', 'database'],
360
- category: 'data',
361
- });
362
-
363
- // Enable tool search so Driver can discover and activate on-demand tools
364
- orchestrator.enableToolSearch();
223
+ Options:
224
+ - `cwd` working directory for commands
225
+ - `allowedCommands` — whitelist of allowed commands (if set, others are rejected)
365
226
 
366
- // The Driver can now:
367
- // 1. tools.search({"query": "database"}) -> finds MySpecializedTool
368
- // 2. tools.activate({"name": "myspecialized"}) -> enables it
369
- // 3. myspecialized.action({...}) -> use the tool
370
- ```
227
+ ### `httpTool()`
371
228
 
372
- ### Expert SubAgent Example
229
+ Returns: `http_get`, `http_post`
373
230
 
374
231
  ```typescript
375
- const orchestrator = new DualAgentOrchestrator({
376
- openaiToken: 'sk-...',
377
- defaultProvider: 'openai',
378
- guardianPolicyPrompt: 'Allow safe operations...',
379
- });
380
-
381
- orchestrator.registerStandardTools();
382
- orchestrator.enableToolSearch();
383
-
384
- // Initial expert (always visible)
385
- orchestrator.registerExpert({
386
- name: 'code_assistant',
387
- description: 'Helps with coding tasks and code generation',
388
- systemMessage: 'You are a helpful coding assistant...',
389
- guardianPolicy: 'Allow read-only file access',
390
- tools: [new FilesystemTool()],
391
- });
392
-
393
- // On-demand experts (discoverable via search)
394
- orchestrator.registerExpert({
395
- name: 'database_expert',
396
- description: 'Database design, optimization, and query analysis',
397
- systemMessage: 'You are a database expert...',
398
- guardianPolicy: 'Allow read-only operations',
399
- visibility: 'on-demand',
400
- tags: ['database', 'sql', 'optimization'],
401
- });
232
+ const tools = httpTool();
402
233
 
403
- orchestrator.registerExpert({
404
- name: 'security_auditor',
405
- description: 'Security vulnerability assessment and best practices',
406
- systemMessage: 'You are a security expert...',
407
- guardianPolicy: 'Allow read-only file access',
408
- visibility: 'on-demand',
409
- tags: ['security', 'audit', 'vulnerabilities'],
234
+ await runAgent({
235
+ model,
236
+ prompt: 'Fetch the data from https://api.example.com/status',
237
+ tools,
238
+ maxSteps: 5,
410
239
  });
411
-
412
- await orchestrator.start();
413
-
414
- // Now the Driver can:
415
- // - Use code_assistant directly
416
- // - Search for "database" and activate database_expert when needed
417
- // - Search for "security" and activate security_auditor when needed
418
240
  ```
419
241
 
420
- ## 🎥 Streaming Support
242
+ ### `jsonTool()`
421
243
 
422
- SmartAgent supports token-by-token streaming for real-time output during LLM generation:
244
+ Returns: `json_validate`, `json_transform`
423
245
 
424
246
  ```typescript
425
- const orchestrator = new DualAgentOrchestrator({
426
- openaiToken: 'sk-...',
427
- defaultProvider: 'openai',
428
- guardianPolicyPrompt: '...',
429
-
430
- // Token streaming callback
431
- onToken: (token, source) => {
432
- // source is 'driver' or 'guardian'
433
- process.stdout.write(token);
434
- },
247
+ const tools = jsonTool();
248
+
249
+ // Direct usage:
250
+ const result = await tools.json_validate.execute({
251
+ jsonString: '{"name":"test","value":42}',
252
+ requiredFields: ['name', 'value'],
435
253
  });
254
+ // → "Valid JSON: object with 2 keys"
436
255
  ```
437
256
 
438
- This is perfect for CLI applications or UIs that need to show progress as the agent thinks.
257
+ ## Streaming & Callbacks 🎥
439
258
 
440
- ## 🖼️ Vision Support
441
-
442
- Pass images to vision-capable models for multimodal tasks:
259
+ Monitor the agent in real-time:
443
260
 
444
261
  ```typescript
445
- import { readFileSync } from 'fs';
262
+ const result = await runAgent({
263
+ model,
264
+ prompt: 'Analyze this data...',
265
+ tools,
266
+ maxSteps: 10,
446
267
 
447
- // Load image as base64
448
- const imageBase64 = readFileSync('screenshot.png').toString('base64');
268
+ // Token-by-token streaming
269
+ onToken: (delta) => process.stdout.write(delta),
449
270
 
450
- // Run task with images
451
- const result = await orchestrator.run(
452
- 'Analyze this UI screenshot and describe any usability issues',
453
- { images: [imageBase64] }
454
- );
271
+ // Tool call notifications
272
+ onToolCall: (toolName) => console.log(`\n🔧 Calling: ${toolName}`),
273
+ });
455
274
  ```
456
275
 
457
- ## 📊 Progress Events
276
+ ## Context Overflow Handling 💥
458
277
 
459
- Get real-time feedback on task execution with the `onProgress` callback:
278
+ For long-running agents that might exceed the model's context window, use the compaction subpath:
460
279
 
461
280
  ```typescript
462
- const orchestrator = new DualAgentOrchestrator({
463
- openaiToken: 'sk-...',
464
- guardianPolicyPrompt: '...',
465
- logPrefix: '[MyAgent]', // Optional prefix for log messages
466
-
467
- onProgress: (event) => {
468
- // Pre-formatted log message ready for output
469
- console.log(event.logMessage);
470
-
471
- // Or handle specific event types
472
- switch (event.type) {
473
- case 'tool_proposed':
474
- console.log(`Proposing: ${event.toolName}.${event.action}`);
475
- break;
476
- case 'tool_approved':
477
- console.log(`✓ Approved`);
478
- break;
479
- case 'tool_rejected':
480
- console.log(`✗ Rejected: ${event.reason}`);
481
- break;
482
- case 'task_completed':
483
- console.log(`Done in ${event.iteration} iterations`);
484
- break;
485
- }
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);
486
293
  },
487
294
  });
488
295
  ```
489
296
 
490
- **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`
491
-
492
- ## 🔧 Native Tool Calling
297
+ ## Output Truncation ✂️
493
298
 
494
- 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:
299
+ Prevent large tool outputs from consuming too much context:
495
300
 
496
301
  ```typescript
497
- const orchestrator = new DualAgentOrchestrator({
498
- ollamaToken: 'http://localhost:11434', // Ollama endpoint
499
- defaultProvider: 'ollama',
500
- guardianPolicyPrompt: '...',
302
+ import { truncateOutput } from '@push.rocks/smartagent';
501
303
 
502
- // Enable native tool calling
503
- useNativeToolCalling: true,
304
+ const { content, truncated, notice } = truncateOutput(hugeOutput, {
305
+ maxLines: 2000, // default
306
+ maxBytes: 50_000, // default
504
307
  });
505
308
  ```
506
309
 
507
- When `useNativeToolCalling` is enabled:
508
- - Tools are converted to JSON schema format automatically
509
- - The provider handles tool call parsing natively
510
- - Streaming still works with `[THINKING]` and `[OUTPUT]` markers for supported models
511
- - Tool calls appear as `toolName_actionName` (e.g., `json_validate`)
512
-
513
- This is more efficient for models that support it and avoids potential XML parsing issues.
514
-
515
- ## Guardian Policy Examples
516
-
517
- The Guardian's power comes from your policy. Here are battle-tested examples:
518
-
519
- ### 🔐 Strict Security Policy
520
-
521
- ```typescript
522
- const securityPolicy = `
523
- SECURITY POLICY:
524
- 1. REJECT any file operations outside /home/user/workspace
525
- 2. REJECT any shell commands that could modify system state
526
- 3. REJECT any HTTP requests to internal/private IP ranges
527
- 4. REJECT any attempts to read environment variables or credentials
528
- 5. FLAG and REJECT obfuscated code execution
529
-
530
- When rejecting, always explain:
531
- - What policy was violated
532
- - What would be a safer alternative
533
- `;
534
- ```
535
-
536
- ### 🛠️ Development Environment Policy
537
-
538
- ```typescript
539
- const devPolicy = `
540
- DEVELOPMENT POLICY:
541
- - Allow file operations only within the project directory
542
- - Allow npm/pnpm commands for package management
543
- - Allow git commands for version control
544
- - Allow HTTP requests to public APIs only
545
- - REJECT direct database modifications
546
- - REJECT commands that could affect other users
547
-
548
- Always verify:
549
- - File paths are relative or within project bounds
550
- - Commands don't have dangerous flags (--force, -rf)
551
- `;
552
- ```
553
-
554
- ### 🦕 Deno Code Execution Policy
555
-
556
- ```typescript
557
- const denoPolicy = `
558
- DENO CODE EXECUTION POLICY:
559
- - ONLY allow 'read' permission for files within the workspace
560
- - REJECT 'all' permission unless explicitly justified for the task
561
- - REJECT 'run' permission (subprocess execution) without specific justification
562
- - REJECT code that attempts to:
563
- - Access credentials or environment secrets (even with 'env' permission)
564
- - Make network requests to internal/private IP ranges
565
- - Write to system directories
566
- - FLAG obfuscated or encoded code (base64, eval with dynamic strings)
567
- - Prefer sandboxed execution (no permissions) when possible
568
-
569
- When evaluating code:
570
- - Review the actual code content, not just permissions
571
- - Consider what data the code could exfiltrate
572
- - Verify network endpoints are legitimate public APIs
573
- `;
574
- ```
575
-
576
- ## Configuration Options
310
+ The built-in tool factories use `truncateOutput` internally.
577
311
 
578
- ```typescript
579
- interface IDualAgentOptions {
580
- // Provider tokens (from @push.rocks/smartai)
581
- openaiToken?: string;
582
- anthropicToken?: string;
583
- perplexityToken?: string;
584
- groqToken?: string;
585
- xaiToken?: string;
586
- ollamaToken?: string; // URL for Ollama endpoint
587
-
588
- // Use existing SmartAi instance (optional - avoids duplicate providers)
589
- smartAiInstance?: SmartAi;
590
-
591
- // Provider selection
592
- defaultProvider?: TProvider; // For both Driver and Guardian
593
- guardianProvider?: TProvider; // Optional: separate provider for Guardian
594
-
595
- // Agent configuration
596
- driverSystemMessage?: string; // Custom system message for Driver
597
- guardianPolicyPrompt: string; // REQUIRED: Policy for Guardian to enforce
598
- name?: string; // Agent system name
599
- verbose?: boolean; // Enable verbose logging
600
-
601
- // Native tool calling
602
- useNativeToolCalling?: boolean; // Use provider's native tool calling API (default: false)
603
-
604
- // Limits
605
- maxIterations?: number; // Max task iterations (default: 20)
606
- maxConsecutiveRejections?: number; // Abort after N rejections (default: 3)
607
- maxResultChars?: number; // Max chars for tool results before truncation (default: 15000)
608
- maxHistoryMessages?: number; // Max history messages for API (default: 20)
609
-
610
- // Callbacks
611
- onProgress?: (event: IProgressEvent) => void; // Progress event callback
612
- onToken?: (token: string, source: 'driver' | 'guardian') => void; // Streaming callback
613
- logPrefix?: string; // Prefix for log messages
614
- }
615
- ```
312
+ ## Multi-Turn Conversations 💬
616
313
 
617
- ## Result Interface
314
+ Pass the returned `messages` back for multi-turn interactions:
618
315
 
619
316
  ```typescript
620
- interface IDualAgentRunResult {
621
- success: boolean; // Whether task completed successfully
622
- completed: boolean; // Task completion status
623
- result: string; // Final result or response
624
- iterations: number; // Number of iterations taken
625
- history: IAgentMessage[]; // Full conversation history
626
- status: TDualAgentRunStatus; // 'completed' | 'max_iterations_reached' | etc.
627
- toolCallCount?: number; // Number of tool calls made
628
- rejectionCount?: number; // Number of Guardian rejections
629
- toolLog?: IToolExecutionLog[]; // Detailed tool execution log
630
- error?: string; // Error message if status is 'error'
631
- }
632
-
633
- type TDualAgentRunStatus =
634
- | 'completed'
635
- | 'in_progress'
636
- | 'max_iterations_reached'
637
- | 'max_rejections_reached'
638
- | 'clarification_needed'
639
- | 'error';
640
- ```
641
-
642
- ## Custom Tools
643
-
644
- Create custom tools by extending `BaseToolWrapper`:
645
-
646
- ```typescript
647
- import { BaseToolWrapper, IToolAction, IToolExecutionResult } from '@push.rocks/smartagent';
648
-
649
- class MyCustomTool extends BaseToolWrapper {
650
- public name = 'custom';
651
- public description = 'My custom tool for specific operations';
652
-
653
- public actions: IToolAction[] = [
654
- {
655
- name: 'myAction',
656
- description: 'Performs a custom action',
657
- parameters: {
658
- type: 'object',
659
- properties: {
660
- input: { type: 'string', description: 'Input for the action' },
661
- },
662
- required: ['input'],
663
- },
664
- },
665
- ];
666
-
667
- public async initialize(): Promise<void> {
668
- // Setup your tool (called when orchestrator.start() runs)
669
- this.isInitialized = true;
670
- }
671
-
672
- public async cleanup(): Promise<void> {
673
- // Cleanup resources (called when orchestrator.stop() runs)
674
- this.isInitialized = false;
675
- }
676
-
677
- public async execute(action: string, params: Record<string, unknown>): Promise<IToolExecutionResult> {
678
- this.validateAction(action);
679
- this.ensureInitialized();
680
-
681
- if (action === 'myAction') {
682
- return {
683
- success: true,
684
- result: { processed: params.input },
685
- summary: `Processed input: ${params.input}`, // Optional human-readable summary
686
- };
687
- }
688
-
689
- return { success: false, error: 'Unknown action' };
690
- }
691
-
692
- // Human-readable summary for Guardian evaluation
693
- public getCallSummary(action: string, params: Record<string, unknown>): string {
694
- return `Custom action "${action}" with input "${params.input}"`;
695
- }
696
- }
697
-
698
- // Register custom tool
699
- orchestrator.registerTool(new MyCustomTool());
700
- ```
701
-
702
- ## Reusing SmartAi Instances
703
-
704
- If you already have a `@push.rocks/smartai` instance, you can share it:
705
-
706
- ```typescript
707
- import { SmartAi } from '@push.rocks/smartai';
708
- import { DualAgentOrchestrator } from '@push.rocks/smartagent';
709
-
710
- const smartai = new SmartAi({ openaiToken: 'sk-...' });
711
- await smartai.start();
317
+ // First turn
318
+ const turn1 = await runAgent({
319
+ model,
320
+ prompt: 'Create a project structure',
321
+ tools,
322
+ maxSteps: 10,
323
+ });
712
324
 
713
- const orchestrator = new DualAgentOrchestrator({
714
- smartAiInstance: smartai, // Reuse existing instance
715
- guardianPolicyPrompt: '...',
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
716
332
  });
333
+ ```
717
334
 
718
- await orchestrator.start();
719
- // ... use orchestrator ...
720
- await orchestrator.stop();
335
+ ## Exports
721
336
 
722
- // SmartAi instance lifecycle is managed separately
723
- await smartai.stop();
724
- ```
337
+ ### Main (`@push.rocks/smartagent`)
725
338
 
726
- ## Supported Providers
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` |
727
349
 
728
- SmartAgent supports all providers from `@push.rocks/smartai`:
350
+ ### Tools (`@push.rocks/smartagent/tools`)
729
351
 
730
- | Provider | Driver | Guardian |
731
- |----------|:------:|:--------:|
732
- | OpenAI | | |
733
- | Anthropic | | |
734
- | Perplexity | | |
735
- | Groq | | |
736
- | Ollama | ✅ | ✅ |
737
- | XAI | ✅ | ✅ |
738
- | Exo | ✅ | ✅ |
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 |
739
358
 
740
- **💡 Pro tip**: Use a faster/cheaper model for Guardian (like Groq) and a more capable model for Driver:
359
+ ### Compaction (`@push.rocks/smartagent/compaction`)
741
360
 
742
- ```typescript
743
- const orchestrator = new DualAgentOrchestrator({
744
- openaiToken: 'sk-...',
745
- groqToken: 'gsk-...',
746
- defaultProvider: 'openai', // Driver uses OpenAI
747
- guardianProvider: 'groq', // Guardian uses Groq (faster, cheaper)
748
- guardianPolicyPrompt: '...',
749
- });
750
- ```
361
+ | Export | Type | Description |
362
+ |--------|------|-------------|
363
+ | `compactMessages` | function | Summarize message history to free context |
751
364
 
752
- ## API Reference
753
-
754
- ### DualAgentOrchestrator
755
-
756
- | Method | Description |
757
- |--------|-------------|
758
- | `start()` | Initialize all tools and AI providers |
759
- | `stop()` | Cleanup all tools and resources |
760
- | `run(task, options?)` | Execute a task with optional images for vision |
761
- | `continueTask(input)` | Continue a task with user input |
762
- | `registerTool(tool, options?)` | Register a custom tool with optional visibility settings |
763
- | `registerStandardTools()` | Register all built-in tools (Filesystem, HTTP, Shell, Browser, Deno) |
764
- | `registerScopedFilesystemTool(basePath, excludePatterns?)` | Register filesystem tool with path restriction |
765
- | `registerExpert(config)` | Register a specialized sub-agent as a tool |
766
- | `enableToolSearch()` | Enable tool discovery and activation for the Driver |
767
- | `setGuardianPolicy(policy)` | Update Guardian policy at runtime |
768
- | `getHistory()` | Get conversation history |
769
- | `getToolNames()` | Get list of registered tool names |
770
- | `getRegistry()` | Get the ToolRegistry for advanced operations |
771
- | `isActive()` | Check if orchestrator is running |
772
-
773
- ### Exports
365
+ ## Dependencies
774
366
 
775
- ```typescript
776
- // Main classes
777
- export { DualAgentOrchestrator } from '@push.rocks/smartagent';
778
- export { DriverAgent } from '@push.rocks/smartagent';
779
- export { GuardianAgent } from '@push.rocks/smartagent';
780
-
781
- // Tool Registry
782
- export { ToolRegistry } from '@push.rocks/smartagent';
783
-
784
- // Tools
785
- export { BaseToolWrapper } from '@push.rocks/smartagent';
786
- export { FilesystemTool, type IFilesystemToolOptions } from '@push.rocks/smartagent';
787
- export { HttpTool } from '@push.rocks/smartagent';
788
- export { ShellTool } from '@push.rocks/smartagent';
789
- export { BrowserTool } from '@push.rocks/smartagent';
790
- export { DenoTool, type TDenoPermission } from '@push.rocks/smartagent';
791
- export { JsonValidatorTool } from '@push.rocks/smartagent';
792
- export { ToolSearchTool } from '@push.rocks/smartagent';
793
- export { ExpertTool } from '@push.rocks/smartagent';
794
-
795
- // Types and interfaces
796
- export * from '@push.rocks/smartagent'; // All interfaces (IExpertConfig, IToolMetadata, etc.)
797
-
798
- // Re-exported from @push.rocks/smartai
799
- export { type ISmartAiOptions, type TProvider, type ChatMessage, type ChatOptions, type ChatResponse };
800
- ```
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
801
373
 
802
374
  ## License and Legal Information
803
375