diff --git a/changelog.md b/changelog.md
index 09f0bdd..36ee37a 100644
--- a/changelog.md
+++ b/changelog.md
@@ -1,5 +1,16 @@
# Changelog
+## 2025-12-02 - 1.1.0 - feat(deno)
+Add Deno tool and smartdeno integration; export and register DenoTool; update docs and tests
+
+- Introduce DenoTool wrapper (ts/smartagent.tools.deno.ts) to run TypeScript/JavaScript in a sandboxed Deno environment with permission controls
+- Add @push.rocks/smartdeno dependency to package.json
+- Import and re-export smartdeno in ts/plugins.ts
+- Export DenoTool and TDenoPermission from ts/index.ts
+- Register DenoTool in DualAgentOrchestrator.registerStandardTools() so it's available as a standard tool
+- Update README architecture diagram and docs to include Deno as a standard tool
+- Add tests for DenoTool in test/test.ts (exports, instantiation, call summary, permission display)
+
## 2025-12-02 - 1.0.2 - fix(core)
Bump version to 1.0.2 (patch release)
diff --git a/package.json b/package.json
index 1bf7178..904b748 100644
--- a/package.json
+++ b/package.json
@@ -23,6 +23,7 @@
"dependencies": {
"@push.rocks/smartai": "^0.8.0",
"@push.rocks/smartbrowser": "^2.0.8",
+ "@push.rocks/smartdeno": "^1.2.0",
"@push.rocks/smartfs": "^1.2.0",
"@push.rocks/smartrequest": "^5.0.1",
"@push.rocks/smartshell": "^3.3.0"
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 7f14f25..f1927ec 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -14,6 +14,9 @@ importers:
'@push.rocks/smartbrowser':
specifier: ^2.0.8
version: 2.0.8(typescript@5.9.3)
+ '@push.rocks/smartdeno':
+ specifier: ^1.2.0
+ version: 1.2.0
'@push.rocks/smartfs':
specifier: ^1.2.0
version: 1.2.0
@@ -831,6 +834,9 @@ packages:
'@push.rocks/smartarchive@4.2.4':
resolution: {integrity: sha512-uiqVAXPxmr8G5rv3uZvZFMOCt8l7cZC3nzvsy4YQqKf/VkPhKIEX+b7LkAeNlxPSYUiBQUkNRoawg9+5BaMcHg==}
+ '@push.rocks/smartarchive@5.0.1':
+ resolution: {integrity: sha512-x4bie9IIdL9BZqBZLc8Pemp8xZOJGa6mXSVgKJRL4/Rw+E5N4rVHjQOYGRV75nC2mAMJh9GIbixuxLnWjj77ag==}
+
'@push.rocks/smartarray@1.1.0':
resolution: {integrity: sha512-b5YgBmUdglOJH8zeUf2ZWdPCoqySgwvkycRi2BhA9zVZHkpASh39Ej0q0fxFJetlUVyYqGfVoMVjbVrLFfFV7g==}
@@ -868,6 +874,9 @@ packages:
'@push.rocks/smartdelay@3.0.5':
resolution: {integrity: sha512-mUuI7kj2f7ztjpic96FvRIlf2RsKBa5arw81AHNsndbxO6asRcxuWL8dTVxouEIK8YsBUlj0AsrCkHhMbLQdHw==}
+ '@push.rocks/smartdeno@1.2.0':
+ resolution: {integrity: sha512-6S1plCaMUVOZiRSflfoz9Fqk9phACCuKmc7Z6SfTvfl+p9VcPUmewKgaa/0QiLOpiI6ksfxdfmkS5Rw5HpYeIA==}
+
'@push.rocks/smartdns@7.6.1':
resolution: {integrity: sha512-nnP5+A2GOt0WsHrYhtKERmjdEHUchc+QbCCBEqlyeQTn+mNfx2WZvKVI1DFRJt8lamvzxP6Hr/BSe3WHdh4Snw==}
@@ -5161,6 +5170,26 @@ snapshots:
- react-native-b4a
- supports-color
+ '@push.rocks/smartarchive@5.0.1':
+ dependencies:
+ '@push.rocks/smartdelay': 3.0.5
+ '@push.rocks/smartfile': 13.1.0
+ '@push.rocks/smartpath': 6.0.0
+ '@push.rocks/smartpromise': 4.2.3
+ '@push.rocks/smartrequest': 4.4.2
+ '@push.rocks/smartrx': 3.0.10
+ '@push.rocks/smartstream': 3.2.5
+ '@push.rocks/smartunique': 3.0.9
+ '@push.rocks/smarturl': 3.1.0
+ '@types/tar-stream': 3.1.4
+ fflate: 0.8.2
+ file-type: 21.1.1
+ tar-stream: 3.1.7
+ transitivePeerDependencies:
+ - bare-abort-controller
+ - react-native-b4a
+ - supports-color
+
'@push.rocks/smartarray@1.1.0': {}
'@push.rocks/smartbrowser@2.0.8(typescript@5.9.3)':
@@ -5284,6 +5313,18 @@ snapshots:
dependencies:
'@push.rocks/smartpromise': 4.2.3
+ '@push.rocks/smartdeno@1.2.0':
+ dependencies:
+ '@push.rocks/smartarchive': 5.0.1
+ '@push.rocks/smartfs': 1.2.0
+ '@push.rocks/smartpath': 6.0.0
+ '@push.rocks/smartshell': 3.3.0
+ '@push.rocks/smartunique': 3.0.9
+ transitivePeerDependencies:
+ - bare-abort-controller
+ - react-native-b4a
+ - supports-color
+
'@push.rocks/smartdns@7.6.1':
dependencies:
'@push.rocks/smartdelay': 3.0.5
diff --git a/readme.md b/readme.md
index 5a1799b..d9cd610 100644
--- a/readme.md
+++ b/readme.md
@@ -19,26 +19,33 @@ This design ensures safe tool use through AI-based policy evaluation rather than
## Architecture
-```
-User Task + Guardian Policy Prompt
- |
- +---------------------------------------+
- | DualAgentOrchestrator |
- | |
- | +--------+ +------------+ |
- | | Driver |-------> | Guardian | |
- | | Agent | tool | Agent | |
- | | | call | | |
- | | Reason |<--------| Evaluate | |
- | | + Plan | approve | against | |
- | +--------+ /reject | policy | |
- | | +feedback+-----------+ |
- | v (if approved) |
- | +-----------------------------------+|
- | | Standard Tools ||
- | | Filesystem | HTTP | Shell | Browser|
- | +-----------------------------------+|
- +---------------------------------------+
+```mermaid
+flowchart TB
+ subgraph Input
+ Task["User Task"]
+ Policy["Guardian Policy Prompt"]
+ end
+
+ subgraph Orchestrator["DualAgentOrchestrator"]
+ Driver["Driver Agent
Reason + Plan"]
+ Guardian["Guardian Agent
Evaluate against policy"]
+
+ Driver -->|"tool call proposal"| Guardian
+ Guardian -->|"approve / reject + feedback"| Driver
+ end
+
+ subgraph Tools["Standard Tools"]
+ FS["Filesystem"]
+ HTTP["HTTP"]
+ Shell["Shell"]
+ Browser["Browser"]
+ Deno["Deno"]
+ end
+
+ Task --> Orchestrator
+ Policy --> Guardian
+ Driver -->|"execute
(if approved)"| Tools
+ Tools -->|"result"| Driver
```
## Quick Start
@@ -139,6 +146,46 @@ Web page interaction using `@push.rocks/smartbrowser` (Puppeteer-based).
```
+### DenoTool
+Execute TypeScript/JavaScript code in a sandboxed Deno environment using `@push.rocks/smartdeno`.
+
+**Actions**: `execute`, `executeWithResult`
+
+**Permissions**: `all`, `env`, `ffi`, `hrtime`, `net`, `read`, `run`, `sys`, `write`
+
+By default, code runs fully sandboxed with no permissions. Permissions must be explicitly requested.
+
+```typescript
+// Simple code execution
+
+ deno
+ execute
+ {"code": "console.log('Hello from Deno!')"}
+ Running a simple script to verify the environment
+
+
+// Code with network permission
+
+ deno
+ execute
+ {
+ "code": "const resp = await fetch('https://api.example.com/data'); console.log(await resp.json());",
+ "permissions": ["net"]
+ }
+ Fetching data from API using Deno's fetch
+
+
+// Execute and parse JSON result
+
+ deno
+ executeWithResult
+ {
+ "code": "const result = { sum: 2 + 2, date: new Date().toISOString() }; console.log(JSON.stringify(result));"
+ }
+ Computing values and returning structured data
+
+```
+
## Guardian Policy Examples
### Strict Security Policy
@@ -174,6 +221,27 @@ Always verify:
`;
```
+### Deno Code Execution Policy
+```typescript
+const denoPolicy = `
+DENO CODE EXECUTION POLICY:
+- ONLY allow 'read' permission for files within the workspace
+- REJECT 'all' permission unless explicitly justified for the task
+- REJECT 'run' permission (subprocess execution) without specific justification
+- REJECT code that attempts to:
+ - Access credentials or environment secrets (even with 'env' permission)
+ - Make network requests to internal/private IP ranges
+ - Write to system directories
+- FLAG obfuscated or encoded code (base64, eval with dynamic strings)
+- Prefer sandboxed execution (no permissions) when possible
+
+When evaluating code:
+- Review the actual code content, not just permissions
+- Consider what data the code could exfiltrate
+- Verify network endpoints are legitimate public APIs
+`;
+```
+
## Configuration Options
```typescript
diff --git a/test/test.ts b/test/test.ts
index d6f4f2f..1828362 100644
--- a/test/test.ts
+++ b/test/test.ts
@@ -35,6 +35,10 @@ tap.test('should export BrowserTool class', async () => {
expect(smartagent.BrowserTool).toBeTypeOf('function');
});
+tap.test('should export DenoTool class', async () => {
+ expect(smartagent.DenoTool).toBeTypeOf('function');
+});
+
// Test tool instantiation
tap.test('should be able to instantiate FilesystemTool', async () => {
const fsTool = new smartagent.FilesystemTool();
@@ -61,6 +65,12 @@ tap.test('should be able to instantiate BrowserTool', async () => {
expect(browserTool.actions).toBeTypeOf('object');
});
+tap.test('should be able to instantiate DenoTool', async () => {
+ const denoTool = new smartagent.DenoTool();
+ expect(denoTool.name).toEqual('deno');
+ expect(denoTool.actions).toBeTypeOf('object');
+});
+
// Test tool descriptions
tap.test('FilesystemTool should have required actions', async () => {
const fsTool = new smartagent.FilesystemTool();
@@ -97,6 +107,13 @@ tap.test('BrowserTool should have required actions', async () => {
expect(actionNames).toContain('getPageContent');
});
+tap.test('DenoTool should have required actions', async () => {
+ const denoTool = new smartagent.DenoTool();
+ const actionNames = denoTool.actions.map((a) => a.name);
+ expect(actionNames).toContain('execute');
+ expect(actionNames).toContain('executeWithResult');
+});
+
// Test getCallSummary
tap.test('FilesystemTool should generate call summaries', async () => {
const fsTool = new smartagent.FilesystemTool();
@@ -112,4 +129,22 @@ tap.test('HttpTool should generate call summaries', async () => {
expect(summary).toInclude('example.com');
});
+tap.test('DenoTool should generate call summaries', async () => {
+ const denoTool = new smartagent.DenoTool();
+ const summary = denoTool.getCallSummary('execute', { code: 'console.log("hello");' });
+ expect(summary).toBeTypeOf('string');
+ expect(summary).toInclude('sandboxed');
+});
+
+tap.test('DenoTool should show permissions in call summary', async () => {
+ const denoTool = new smartagent.DenoTool();
+ const summary = denoTool.getCallSummary('execute', {
+ code: 'console.log("hello");',
+ permissions: ['net', 'read']
+ });
+ expect(summary).toBeTypeOf('string');
+ expect(summary).toInclude('permissions');
+ expect(summary).toInclude('net');
+});
+
export default tap.start();
diff --git a/ts/00_commitinfo_data.ts b/ts/00_commitinfo_data.ts
index df7d3bd..e6fe23e 100644
--- a/ts/00_commitinfo_data.ts
+++ b/ts/00_commitinfo_data.ts
@@ -3,6 +3,6 @@
*/
export const commitinfo = {
name: '@push.rocks/smartagent',
- version: '1.0.2',
+ version: '1.1.0',
description: 'an agentic framework built on top of @push.rocks/smartai'
}
diff --git a/ts/index.ts b/ts/index.ts
index 141aff3..7e05ae1 100644
--- a/ts/index.ts
+++ b/ts/index.ts
@@ -15,6 +15,7 @@ export { FilesystemTool } from './smartagent.tools.filesystem.js';
export { HttpTool } from './smartagent.tools.http.js';
export { ShellTool } from './smartagent.tools.shell.js';
export { BrowserTool } from './smartagent.tools.browser.js';
+export { DenoTool, type TDenoPermission } from './smartagent.tools.deno.js';
// Export all interfaces
export * from './smartagent.interfaces.js';
diff --git a/ts/plugins.ts b/ts/plugins.ts
index 6103d77..bbca2ad 100644
--- a/ts/plugins.ts
+++ b/ts/plugins.ts
@@ -1,5 +1,6 @@
// @push.rocks scope
import * as smartai from '@push.rocks/smartai';
+import * as smartdeno from '@push.rocks/smartdeno';
import * as smartfs from '@push.rocks/smartfs';
import * as smartrequest from '@push.rocks/smartrequest';
import * as smartbrowser from '@push.rocks/smartbrowser';
@@ -7,6 +8,7 @@ import * as smartshell from '@push.rocks/smartshell';
export {
smartai,
+ smartdeno,
smartfs,
smartrequest,
smartbrowser,
diff --git a/ts/smartagent.classes.dualagent.ts b/ts/smartagent.classes.dualagent.ts
index cc20722..e02be61 100644
--- a/ts/smartagent.classes.dualagent.ts
+++ b/ts/smartagent.classes.dualagent.ts
@@ -7,6 +7,7 @@ import { FilesystemTool } from './smartagent.tools.filesystem.js';
import { HttpTool } from './smartagent.tools.http.js';
import { ShellTool } from './smartagent.tools.shell.js';
import { BrowserTool } from './smartagent.tools.browser.js';
+import { DenoTool } from './smartagent.tools.deno.js';
/**
* DualAgentOrchestrator - Coordinates Driver and Guardian agents
@@ -87,6 +88,7 @@ export class DualAgentOrchestrator {
new HttpTool(),
new ShellTool(),
new BrowserTool(),
+ new DenoTool(),
];
for (const tool of standardTools) {
diff --git a/ts/smartagent.tools.deno.ts b/ts/smartagent.tools.deno.ts
new file mode 100644
index 0000000..1320fde
--- /dev/null
+++ b/ts/smartagent.tools.deno.ts
@@ -0,0 +1,191 @@
+import * as plugins from './plugins.js';
+import * as interfaces from './smartagent.interfaces.js';
+import { BaseToolWrapper } from './smartagent.tools.base.js';
+
+/**
+ * Deno permission types for sandboxed code execution
+ */
+export type TDenoPermission =
+ | 'all'
+ | 'env'
+ | 'ffi'
+ | 'hrtime'
+ | 'net'
+ | 'read'
+ | 'run'
+ | 'sys'
+ | 'write';
+
+/**
+ * Deno tool for executing TypeScript/JavaScript code in a sandboxed environment
+ * Wraps @push.rocks/smartdeno
+ */
+export class DenoTool extends BaseToolWrapper {
+ public name = 'deno';
+ public description =
+ 'Execute TypeScript/JavaScript code in a sandboxed Deno environment with fine-grained permission control';
+
+ public actions: interfaces.IToolAction[] = [
+ {
+ name: 'execute',
+ description:
+ 'Execute TypeScript/JavaScript code and return stdout/stderr. Code runs in Deno sandbox with specified permissions.',
+ parameters: {
+ type: 'object',
+ properties: {
+ code: {
+ type: 'string',
+ description: 'TypeScript/JavaScript code to execute',
+ },
+ permissions: {
+ type: 'array',
+ items: {
+ type: 'string',
+ enum: ['all', 'env', 'ffi', 'hrtime', 'net', 'read', 'run', 'sys', 'write'],
+ },
+ description:
+ 'Deno permissions to grant. Default: none (fully sandboxed). Options: all, env, net, read, write, run, sys, ffi, hrtime',
+ },
+ },
+ required: ['code'],
+ },
+ },
+ {
+ name: 'executeWithResult',
+ description:
+ 'Execute code that outputs JSON on the last line of stdout. The JSON is parsed and returned as the result.',
+ parameters: {
+ type: 'object',
+ properties: {
+ code: {
+ type: 'string',
+ description:
+ 'Code that console.logs a JSON value on the final line. This JSON will be parsed and returned.',
+ },
+ permissions: {
+ type: 'array',
+ items: {
+ type: 'string',
+ enum: ['all', 'env', 'ffi', 'hrtime', 'net', 'read', 'run', 'sys', 'write'],
+ },
+ description: 'Deno permissions to grant',
+ },
+ },
+ required: ['code'],
+ },
+ },
+ ];
+
+ private smartdeno!: plugins.smartdeno.SmartDeno;
+
+ public async initialize(): Promise {
+ this.smartdeno = new plugins.smartdeno.SmartDeno();
+ await this.smartdeno.start();
+ this.isInitialized = true;
+ }
+
+ public async cleanup(): Promise {
+ if (this.smartdeno) {
+ await this.smartdeno.stop();
+ }
+ this.isInitialized = false;
+ }
+
+ public async execute(
+ action: string,
+ params: Record
+ ): Promise {
+ this.validateAction(action);
+ this.ensureInitialized();
+
+ try {
+ const code = params.code as string;
+ const permissions = (params.permissions as TDenoPermission[]) || [];
+
+ // Execute the script
+ const result = await this.smartdeno.executeScript(code, {
+ permissions,
+ });
+
+ switch (action) {
+ case 'execute': {
+ return {
+ success: result.exitCode === 0,
+ result: {
+ exitCode: result.exitCode,
+ stdout: result.stdout,
+ stderr: result.stderr,
+ permissions,
+ },
+ };
+ }
+
+ case 'executeWithResult': {
+ if (result.exitCode !== 0) {
+ return {
+ success: false,
+ error: `Script failed with exit code ${result.exitCode}: ${result.stderr}`,
+ };
+ }
+
+ // Parse the last line of stdout as JSON
+ const lines = result.stdout.trim().split('\n');
+ const lastLine = lines[lines.length - 1];
+
+ try {
+ const parsedResult = JSON.parse(lastLine);
+ return {
+ success: true,
+ result: {
+ parsed: parsedResult,
+ stdout: result.stdout,
+ stderr: result.stderr,
+ },
+ };
+ } catch (parseError) {
+ return {
+ success: false,
+ error: `Failed to parse JSON from last line of output: ${lastLine}`,
+ };
+ }
+ }
+
+ default:
+ return {
+ success: false,
+ error: `Unknown action: ${action}`,
+ };
+ }
+ } catch (error) {
+ return {
+ success: false,
+ error: error instanceof Error ? error.message : String(error),
+ };
+ }
+ }
+
+ public getCallSummary(action: string, params: Record): string {
+ const code = params.code as string;
+ const permissions = (params.permissions as string[]) || [];
+
+ // Create a preview of the code (first 100 chars)
+ const codePreview = code.length > 100 ? code.substring(0, 100) + '...' : code;
+ // Escape newlines for single-line display
+ const cleanPreview = codePreview.replace(/\n/g, '\\n');
+
+ const permissionInfo = permissions.length > 0
+ ? ` [permissions: ${permissions.join(', ')}]`
+ : ' [sandboxed - no permissions]';
+
+ switch (action) {
+ case 'execute':
+ return `Execute Deno code${permissionInfo}: "${cleanPreview}"`;
+
+ case 'executeWithResult':
+ return `Execute Deno code and parse JSON result${permissionInfo}: "${cleanPreview}"`;
+
+ default:
+ return `Unknown action: ${action}`;
+ }
+ }
+}