@amodalai/runtime 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/dist/.last_build +0 -0
- package/dist/src/agent/agent-runner.d.ts +13 -0
- package/dist/src/agent/agent-runner.js +827 -0
- package/dist/src/agent/agent-runner.js.map +1 -0
- package/dist/src/agent/agent-runner.test.d.ts +6 -0
- package/dist/src/agent/agent-runner.test.js +552 -0
- package/dist/src/agent/agent-runner.test.js.map +1 -0
- package/dist/src/agent/agent-types.d.ts +57 -0
- package/dist/src/agent/agent-types.js +17 -0
- package/dist/src/agent/agent-types.js.map +1 -0
- package/dist/src/agent/agent-types.test.d.ts +6 -0
- package/dist/src/agent/agent-types.test.js +44 -0
- package/dist/src/agent/agent-types.test.js.map +1 -0
- package/dist/src/agent/automation-bridge.d.ts +24 -0
- package/dist/src/agent/automation-bridge.js +24 -0
- package/dist/src/agent/automation-bridge.js.map +1 -0
- package/dist/src/agent/automation-bridge.test.d.ts +6 -0
- package/dist/src/agent/automation-bridge.test.js +67 -0
- package/dist/src/agent/automation-bridge.test.js.map +1 -0
- package/dist/src/agent/config-watcher.d.ts +20 -0
- package/dist/src/agent/config-watcher.js +68 -0
- package/dist/src/agent/config-watcher.js.map +1 -0
- package/dist/src/agent/config-watcher.test.d.ts +6 -0
- package/dist/src/agent/config-watcher.test.js +83 -0
- package/dist/src/agent/config-watcher.test.js.map +1 -0
- package/dist/src/agent/custom-tools-e2e.test.d.ts +6 -0
- package/dist/src/agent/custom-tools-e2e.test.js +566 -0
- package/dist/src/agent/custom-tools-e2e.test.js.map +1 -0
- package/dist/src/agent/local-server.d.ts +15 -0
- package/dist/src/agent/local-server.js +158 -0
- package/dist/src/agent/local-server.js.map +1 -0
- package/dist/src/agent/local-server.test.d.ts +6 -0
- package/dist/src/agent/local-server.test.js +126 -0
- package/dist/src/agent/local-server.test.js.map +1 -0
- package/dist/src/agent/proactive/delivery.d.ts +21 -0
- package/dist/src/agent/proactive/delivery.js +68 -0
- package/dist/src/agent/proactive/delivery.js.map +1 -0
- package/dist/src/agent/proactive/delivery.test.d.ts +6 -0
- package/dist/src/agent/proactive/delivery.test.js +65 -0
- package/dist/src/agent/proactive/delivery.test.js.map +1 -0
- package/dist/src/agent/proactive/proactive-runner.d.ts +76 -0
- package/dist/src/agent/proactive/proactive-runner.js +201 -0
- package/dist/src/agent/proactive/proactive-runner.js.map +1 -0
- package/dist/src/agent/proactive/proactive-runner.test.d.ts +6 -0
- package/dist/src/agent/proactive/proactive-runner.test.js +265 -0
- package/dist/src/agent/proactive/proactive-runner.test.js.map +1 -0
- package/dist/src/agent/request-helper.d.ts +16 -0
- package/dist/src/agent/request-helper.js +87 -0
- package/dist/src/agent/request-helper.js.map +1 -0
- package/dist/src/agent/routes/automations.d.ts +19 -0
- package/dist/src/agent/routes/automations.js +58 -0
- package/dist/src/agent/routes/automations.js.map +1 -0
- package/dist/src/agent/routes/automations.test.d.ts +6 -0
- package/dist/src/agent/routes/automations.test.js +117 -0
- package/dist/src/agent/routes/automations.test.js.map +1 -0
- package/dist/src/agent/routes/chat.d.ts +35 -0
- package/dist/src/agent/routes/chat.js +88 -0
- package/dist/src/agent/routes/chat.js.map +1 -0
- package/dist/src/agent/routes/chat.test.d.ts +6 -0
- package/dist/src/agent/routes/chat.test.js +115 -0
- package/dist/src/agent/routes/chat.test.js.map +1 -0
- package/dist/src/agent/routes/inspect.d.ts +12 -0
- package/dist/src/agent/routes/inspect.js +40 -0
- package/dist/src/agent/routes/inspect.js.map +1 -0
- package/dist/src/agent/routes/inspect.test.d.ts +6 -0
- package/dist/src/agent/routes/inspect.test.js +80 -0
- package/dist/src/agent/routes/inspect.test.js.map +1 -0
- package/dist/src/agent/routes/stores.d.ts +20 -0
- package/dist/src/agent/routes/stores.js +137 -0
- package/dist/src/agent/routes/stores.js.map +1 -0
- package/dist/src/agent/routes/stores.test.d.ts +6 -0
- package/dist/src/agent/routes/stores.test.js +191 -0
- package/dist/src/agent/routes/stores.test.js.map +1 -0
- package/dist/src/agent/routes/task.d.ts +11 -0
- package/dist/src/agent/routes/task.js +116 -0
- package/dist/src/agent/routes/task.js.map +1 -0
- package/dist/src/agent/routes/task.test.d.ts +6 -0
- package/dist/src/agent/routes/task.test.js +91 -0
- package/dist/src/agent/routes/task.test.js.map +1 -0
- package/dist/src/agent/routes/webhooks.d.ts +17 -0
- package/dist/src/agent/routes/webhooks.js +53 -0
- package/dist/src/agent/routes/webhooks.js.map +1 -0
- package/dist/src/agent/routes/webhooks.test.d.ts +6 -0
- package/dist/src/agent/routes/webhooks.test.js +100 -0
- package/dist/src/agent/routes/webhooks.test.js.map +1 -0
- package/dist/src/agent/session-manager.d.ts +72 -0
- package/dist/src/agent/session-manager.js +214 -0
- package/dist/src/agent/session-manager.js.map +1 -0
- package/dist/src/agent/session-manager.test.d.ts +6 -0
- package/dist/src/agent/session-manager.test.js +145 -0
- package/dist/src/agent/session-manager.test.js.map +1 -0
- package/dist/src/agent/shell-executor-local.d.ts +16 -0
- package/dist/src/agent/shell-executor-local.js +51 -0
- package/dist/src/agent/shell-executor-local.js.map +1 -0
- package/dist/src/agent/shell-executor-local.test.d.ts +6 -0
- package/dist/src/agent/shell-executor-local.test.js +46 -0
- package/dist/src/agent/shell-executor-local.test.js.map +1 -0
- package/dist/src/agent/snapshot-server.d.ts +38 -0
- package/dist/src/agent/snapshot-server.js +114 -0
- package/dist/src/agent/snapshot-server.js.map +1 -0
- package/dist/src/agent/stores-e2e.test.d.ts +6 -0
- package/dist/src/agent/stores-e2e.test.js +433 -0
- package/dist/src/agent/stores-e2e.test.js.map +1 -0
- package/dist/src/agent/tool-context-builder.d.ts +11 -0
- package/dist/src/agent/tool-context-builder.js +84 -0
- package/dist/src/agent/tool-context-builder.js.map +1 -0
- package/dist/src/agent/tool-context-builder.test.d.ts +6 -0
- package/dist/src/agent/tool-context-builder.test.js +152 -0
- package/dist/src/agent/tool-context-builder.test.js.map +1 -0
- package/dist/src/agent/tool-executor-local.d.ts +15 -0
- package/dist/src/agent/tool-executor-local.js +74 -0
- package/dist/src/agent/tool-executor-local.js.map +1 -0
- package/dist/src/agent/tool-executor-local.test.d.ts +6 -0
- package/dist/src/agent/tool-executor-local.test.js +116 -0
- package/dist/src/agent/tool-executor-local.test.js.map +1 -0
- package/dist/src/agent/tool-harness-template.d.ts +23 -0
- package/dist/src/agent/tool-harness-template.js +94 -0
- package/dist/src/agent/tool-harness-template.js.map +1 -0
- package/dist/src/agent/user-context-fetcher.d.ts +25 -0
- package/dist/src/agent/user-context-fetcher.js +79 -0
- package/dist/src/agent/user-context-fetcher.js.map +1 -0
- package/dist/src/agent/user-context-fetcher.test.d.ts +6 -0
- package/dist/src/agent/user-context-fetcher.test.js +121 -0
- package/dist/src/agent/user-context-fetcher.test.js.map +1 -0
- package/dist/src/audit/audit-client.d.ts +46 -0
- package/dist/src/audit/audit-client.js +83 -0
- package/dist/src/audit/audit-client.js.map +1 -0
- package/dist/src/cron/heartbeat-runner.d.ts +24 -0
- package/dist/src/cron/heartbeat-runner.js +87 -0
- package/dist/src/cron/heartbeat-runner.js.map +1 -0
- package/dist/src/cron/heartbeat-runner.test.d.ts +6 -0
- package/dist/src/cron/heartbeat-runner.test.js +120 -0
- package/dist/src/cron/heartbeat-runner.test.js.map +1 -0
- package/dist/src/cron/heartbeat-scheduler.d.ts +26 -0
- package/dist/src/cron/heartbeat-scheduler.js +54 -0
- package/dist/src/cron/heartbeat-scheduler.js.map +1 -0
- package/dist/src/cron/heartbeat-scheduler.test.d.ts +6 -0
- package/dist/src/cron/heartbeat-scheduler.test.js +61 -0
- package/dist/src/cron/heartbeat-scheduler.test.js.map +1 -0
- package/dist/src/index.d.ts +24 -0
- package/dist/src/index.js +118 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/middleware/auth.d.ts +40 -0
- package/dist/src/middleware/auth.js +135 -0
- package/dist/src/middleware/auth.js.map +1 -0
- package/dist/src/middleware/auth.test.d.ts +6 -0
- package/dist/src/middleware/auth.test.js +268 -0
- package/dist/src/middleware/auth.test.js.map +1 -0
- package/dist/src/middleware/error-handler.d.ts +20 -0
- package/dist/src/middleware/error-handler.js +48 -0
- package/dist/src/middleware/error-handler.js.map +1 -0
- package/dist/src/middleware/error-handler.test.d.ts +6 -0
- package/dist/src/middleware/error-handler.test.js +68 -0
- package/dist/src/middleware/error-handler.test.js.map +1 -0
- package/dist/src/middleware/request-validation.d.ts +13 -0
- package/dist/src/middleware/request-validation.js +26 -0
- package/dist/src/middleware/request-validation.js.map +1 -0
- package/dist/src/middleware/request-validation.test.d.ts +6 -0
- package/dist/src/middleware/request-validation.test.js +57 -0
- package/dist/src/middleware/request-validation.test.js.map +1 -0
- package/dist/src/output/email-output.d.ts +10 -0
- package/dist/src/output/email-output.js +12 -0
- package/dist/src/output/email-output.js.map +1 -0
- package/dist/src/output/output-router.d.ts +12 -0
- package/dist/src/output/output-router.js +36 -0
- package/dist/src/output/output-router.js.map +1 -0
- package/dist/src/output/output-router.test.d.ts +6 -0
- package/dist/src/output/output-router.test.js +132 -0
- package/dist/src/output/output-router.test.js.map +1 -0
- package/dist/src/output/slack-output.d.ts +10 -0
- package/dist/src/output/slack-output.js +54 -0
- package/dist/src/output/slack-output.js.map +1 -0
- package/dist/src/output/webhook-output.d.ts +10 -0
- package/dist/src/output/webhook-output.js +25 -0
- package/dist/src/output/webhook-output.js.map +1 -0
- package/dist/src/routes/ai-stream.d.ts +159 -0
- package/dist/src/routes/ai-stream.js +309 -0
- package/dist/src/routes/ai-stream.js.map +1 -0
- package/dist/src/routes/ai-stream.test.d.ts +6 -0
- package/dist/src/routes/ai-stream.test.js +586 -0
- package/dist/src/routes/ai-stream.test.js.map +1 -0
- package/dist/src/routes/ask-user-response.d.ts +30 -0
- package/dist/src/routes/ask-user-response.js +61 -0
- package/dist/src/routes/ask-user-response.js.map +1 -0
- package/dist/src/routes/ask-user-response.test.d.ts +6 -0
- package/dist/src/routes/ask-user-response.test.js +88 -0
- package/dist/src/routes/ask-user-response.test.js.map +1 -0
- package/dist/src/routes/chat-stream.d.ts +14 -0
- package/dist/src/routes/chat-stream.js +84 -0
- package/dist/src/routes/chat-stream.js.map +1 -0
- package/dist/src/routes/chat-stream.test.d.ts +6 -0
- package/dist/src/routes/chat-stream.test.js +155 -0
- package/dist/src/routes/chat-stream.test.js.map +1 -0
- package/dist/src/routes/chat.d.ts +13 -0
- package/dist/src/routes/chat.js +55 -0
- package/dist/src/routes/chat.js.map +1 -0
- package/dist/src/routes/chat.test.d.ts +6 -0
- package/dist/src/routes/chat.test.js +99 -0
- package/dist/src/routes/chat.test.js.map +1 -0
- package/dist/src/routes/health.d.ts +13 -0
- package/dist/src/routes/health.js +23 -0
- package/dist/src/routes/health.js.map +1 -0
- package/dist/src/routes/health.test.d.ts +6 -0
- package/dist/src/routes/health.test.js +45 -0
- package/dist/src/routes/health.test.js.map +1 -0
- package/dist/src/routes/sessions.d.ts +14 -0
- package/dist/src/routes/sessions.js +82 -0
- package/dist/src/routes/sessions.js.map +1 -0
- package/dist/src/routes/webhooks.d.ts +13 -0
- package/dist/src/routes/webhooks.js +43 -0
- package/dist/src/routes/webhooks.js.map +1 -0
- package/dist/src/routes/webhooks.test.d.ts +6 -0
- package/dist/src/routes/webhooks.test.js +80 -0
- package/dist/src/routes/webhooks.test.js.map +1 -0
- package/dist/src/routes/widget-actions.d.ts +49 -0
- package/dist/src/routes/widget-actions.js +78 -0
- package/dist/src/routes/widget-actions.js.map +1 -0
- package/dist/src/server.d.ts +31 -0
- package/dist/src/server.js +129 -0
- package/dist/src/server.js.map +1 -0
- package/dist/src/server.test.d.ts +6 -0
- package/dist/src/server.test.js +153 -0
- package/dist/src/server.test.js.map +1 -0
- package/dist/src/session/history-converter.d.ts +21 -0
- package/dist/src/session/history-converter.js +59 -0
- package/dist/src/session/history-converter.js.map +1 -0
- package/dist/src/session/history-converter.test.d.ts +6 -0
- package/dist/src/session/history-converter.test.js +130 -0
- package/dist/src/session/history-converter.test.js.map +1 -0
- package/dist/src/session/session-manager.d.ts +117 -0
- package/dist/src/session/session-manager.js +480 -0
- package/dist/src/session/session-manager.js.map +1 -0
- package/dist/src/session/session-manager.test.d.ts +6 -0
- package/dist/src/session/session-manager.test.js +586 -0
- package/dist/src/session/session-manager.test.js.map +1 -0
- package/dist/src/session/session-runner.d.ts +30 -0
- package/dist/src/session/session-runner.js +771 -0
- package/dist/src/session/session-runner.js.map +1 -0
- package/dist/src/session/session-runner.test.d.ts +6 -0
- package/dist/src/session/session-runner.test.js +842 -0
- package/dist/src/session/session-runner.test.js.map +1 -0
- package/dist/src/stores/index.d.ts +8 -0
- package/dist/src/stores/index.js +9 -0
- package/dist/src/stores/index.js.map +1 -0
- package/dist/src/stores/key-resolver.d.ts +21 -0
- package/dist/src/stores/key-resolver.js +30 -0
- package/dist/src/stores/key-resolver.js.map +1 -0
- package/dist/src/stores/key-resolver.test.d.ts +6 -0
- package/dist/src/stores/key-resolver.test.js +31 -0
- package/dist/src/stores/key-resolver.test.js.map +1 -0
- package/dist/src/stores/pglite-store-backend.d.ts +36 -0
- package/dist/src/stores/pglite-store-backend.js +227 -0
- package/dist/src/stores/pglite-store-backend.js.map +1 -0
- package/dist/src/stores/pglite-store-backend.test.d.ts +6 -0
- package/dist/src/stores/pglite-store-backend.test.js +150 -0
- package/dist/src/stores/pglite-store-backend.test.js.map +1 -0
- package/dist/src/stores/ttl-resolver.d.ts +24 -0
- package/dist/src/stores/ttl-resolver.js +64 -0
- package/dist/src/stores/ttl-resolver.js.map +1 -0
- package/dist/src/stores/ttl-resolver.test.d.ts +6 -0
- package/dist/src/stores/ttl-resolver.test.js +68 -0
- package/dist/src/stores/ttl-resolver.test.js.map +1 -0
- package/dist/src/types.d.ts +227 -0
- package/dist/src/types.js +50 -0
- package/dist/src/types.js.map +1 -0
- package/dist/src/types.test.d.ts +6 -0
- package/dist/src/types.test.js +68 -0
- package/dist/src/types.test.js.map +1 -0
- package/dist/src/utils/jwt-verify.d.ts +20 -0
- package/dist/src/utils/jwt-verify.js +34 -0
- package/dist/src/utils/jwt-verify.js.map +1 -0
- package/dist/src/utils/jwt-verify.test.d.ts +6 -0
- package/dist/src/utils/jwt-verify.test.js +156 -0
- package/dist/src/utils/jwt-verify.test.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/package.json +51 -0
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2025 Amodal Labs, Inc.
|
|
4
|
+
* SPDX-License-Identifier: MIT
|
|
5
|
+
*/
|
|
6
|
+
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
7
|
+
import { buildToolContext } from './tool-context-builder.js';
|
|
8
|
+
// Mock the request-helper module
|
|
9
|
+
const { mockMakeApiRequest } = vi.hoisted(() => ({
|
|
10
|
+
mockMakeApiRequest: vi.fn().mockResolvedValue({ output: '{"ok":true}' }),
|
|
11
|
+
}));
|
|
12
|
+
vi.mock('./request-helper.js', () => ({
|
|
13
|
+
makeApiRequest: mockMakeApiRequest,
|
|
14
|
+
}));
|
|
15
|
+
function makeTool(overrides = {}) {
|
|
16
|
+
return {
|
|
17
|
+
name: 'test_tool',
|
|
18
|
+
description: 'Test tool',
|
|
19
|
+
parameters: {},
|
|
20
|
+
confirm: false,
|
|
21
|
+
timeout: 5000,
|
|
22
|
+
env: ['ALLOWED_KEY'],
|
|
23
|
+
handlerPath: '/tmp/handler.ts',
|
|
24
|
+
location: '/tmp',
|
|
25
|
+
hasPackageJson: false,
|
|
26
|
+
hasDockerfile: false,
|
|
27
|
+
hasSetupScript: false,
|
|
28
|
+
hasRequirementsTxt: false,
|
|
29
|
+
sandboxLanguage: 'typescript',
|
|
30
|
+
...overrides,
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
function makeSession() {
|
|
34
|
+
return {
|
|
35
|
+
id: 'test-session',
|
|
36
|
+
runtime: {
|
|
37
|
+
repo: {
|
|
38
|
+
config: { name: 'test', version: '1.0.0', models: { main: { provider: 'anthropic', model: 'test' } } },
|
|
39
|
+
connections: new Map(),
|
|
40
|
+
skills: [],
|
|
41
|
+
agents: {},
|
|
42
|
+
automations: [],
|
|
43
|
+
knowledge: [],
|
|
44
|
+
evals: [],
|
|
45
|
+
tools: [],
|
|
46
|
+
},
|
|
47
|
+
connectionsMap: {},
|
|
48
|
+
userRoles: ['analyst'],
|
|
49
|
+
fieldScrubber: null,
|
|
50
|
+
telemetry: { logScrub: vi.fn() },
|
|
51
|
+
actionGate: { evaluate: vi.fn() },
|
|
52
|
+
outputPipeline: { process: vi.fn() },
|
|
53
|
+
compiledContext: { systemPrompt: '', tokenUsage: { total: 0, used: 0, remaining: 0, sectionBreakdown: {} }, sections: [] },
|
|
54
|
+
},
|
|
55
|
+
tenantId: 'test-tenant',
|
|
56
|
+
conversationHistory: [],
|
|
57
|
+
createdAt: Date.now(),
|
|
58
|
+
lastAccessedAt: Date.now(),
|
|
59
|
+
planModeManager: { isActive: vi.fn(() => false), enter: vi.fn(), exit: vi.fn(), getPlanningReminder: vi.fn(), getApprovedPlanContext: vi.fn() },
|
|
60
|
+
exploreConfig: { model: { provider: 'anthropic', model: 'test' }, maxTurns: 5, maxDepth: 2, systemPrompt: '' },
|
|
61
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
describe('buildToolContext', () => {
|
|
65
|
+
const signal = AbortSignal.timeout(10000);
|
|
66
|
+
beforeEach(() => {
|
|
67
|
+
vi.clearAllMocks();
|
|
68
|
+
});
|
|
69
|
+
it('provides user roles', () => {
|
|
70
|
+
const session = makeSession();
|
|
71
|
+
const ctx = buildToolContext(session, makeTool(), signal);
|
|
72
|
+
expect(ctx.user.roles).toEqual(['analyst']);
|
|
73
|
+
});
|
|
74
|
+
it('provides env() that respects allowlist', () => {
|
|
75
|
+
process.env['ALLOWED_KEY'] = 'secret-value';
|
|
76
|
+
process.env['BLOCKED_KEY'] = 'blocked-value';
|
|
77
|
+
const ctx = buildToolContext(makeSession(), makeTool(), signal);
|
|
78
|
+
expect(ctx.env('ALLOWED_KEY')).toBe('secret-value');
|
|
79
|
+
expect(ctx.env('BLOCKED_KEY')).toBeUndefined();
|
|
80
|
+
delete process.env['ALLOWED_KEY'];
|
|
81
|
+
delete process.env['BLOCKED_KEY'];
|
|
82
|
+
});
|
|
83
|
+
it('provides log() that writes to stderr', () => {
|
|
84
|
+
const stderrSpy = vi.spyOn(process.stderr, 'write').mockImplementation(() => true);
|
|
85
|
+
const ctx = buildToolContext(makeSession(), makeTool(), signal);
|
|
86
|
+
ctx.log('hello world');
|
|
87
|
+
expect(stderrSpy).toHaveBeenCalledWith(expect.stringContaining('hello world'));
|
|
88
|
+
stderrSpy.mockRestore();
|
|
89
|
+
});
|
|
90
|
+
it('provides an abort signal', () => {
|
|
91
|
+
const ctx = buildToolContext(makeSession(), makeTool(), signal);
|
|
92
|
+
expect(ctx.signal).toBeDefined();
|
|
93
|
+
expect(ctx.signal.aborted).toBe(false);
|
|
94
|
+
});
|
|
95
|
+
it('rejects non-GET requests when confirm is false', async () => {
|
|
96
|
+
const ctx = buildToolContext(makeSession(), makeTool({ confirm: false }), signal);
|
|
97
|
+
await expect(ctx.request('crm', '/deals', { method: 'POST' })).rejects.toThrow(/only GET requests are allowed/);
|
|
98
|
+
});
|
|
99
|
+
it('allows non-GET requests when confirm is true', async () => {
|
|
100
|
+
mockMakeApiRequest.mockResolvedValueOnce({ output: '{"ok":true}' });
|
|
101
|
+
const ctx = buildToolContext(makeSession(), makeTool({ confirm: true }), signal);
|
|
102
|
+
const result = await ctx.request('crm', '/deals', { method: 'POST' });
|
|
103
|
+
expect(result).toEqual({ ok: true });
|
|
104
|
+
expect(mockMakeApiRequest).toHaveBeenCalledOnce();
|
|
105
|
+
});
|
|
106
|
+
it('allows GET requests when confirm is false', async () => {
|
|
107
|
+
mockMakeApiRequest.mockResolvedValueOnce({ output: '{"ok":true}' });
|
|
108
|
+
const ctx = buildToolContext(makeSession(), makeTool({ confirm: false }), signal);
|
|
109
|
+
const result = await ctx.request('crm', '/deals');
|
|
110
|
+
expect(result).toEqual({ ok: true });
|
|
111
|
+
expect(mockMakeApiRequest).toHaveBeenCalledOnce();
|
|
112
|
+
});
|
|
113
|
+
it('provides exec() that runs shell commands', async () => {
|
|
114
|
+
const ctx = buildToolContext(makeSession(), makeTool(), signal);
|
|
115
|
+
const result = await ctx.exec('echo "hello from exec"');
|
|
116
|
+
expect(result.exitCode).toBe(0);
|
|
117
|
+
expect(result.stdout.trim()).toBe('hello from exec');
|
|
118
|
+
});
|
|
119
|
+
it('exec() captures non-zero exit codes', async () => {
|
|
120
|
+
const ctx = buildToolContext(makeSession(), makeTool(), signal);
|
|
121
|
+
const result = await ctx.exec('exit 42');
|
|
122
|
+
expect(result.exitCode).toBe(42);
|
|
123
|
+
});
|
|
124
|
+
it('exec() uses tool location as default cwd', async () => {
|
|
125
|
+
const ctx = buildToolContext(makeSession(), makeTool({ location: '/tmp' }), signal);
|
|
126
|
+
const result = await ctx.exec('pwd');
|
|
127
|
+
// macOS: /tmp is a symlink to /private/tmp
|
|
128
|
+
expect(result.stdout.trim()).toMatch(/\/?tmp$/);
|
|
129
|
+
});
|
|
130
|
+
it('exec() delegates to session.shellExecutor when present', async () => {
|
|
131
|
+
const mockShellExecutor = {
|
|
132
|
+
exec: vi.fn().mockResolvedValue({ stdout: 'sandbox output', stderr: '', exitCode: 0 }),
|
|
133
|
+
};
|
|
134
|
+
const session = makeSession();
|
|
135
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
136
|
+
session.shellExecutor = mockShellExecutor;
|
|
137
|
+
const ctx = buildToolContext(session, makeTool(), signal);
|
|
138
|
+
const result = await ctx.exec('echo hello');
|
|
139
|
+
expect(result.stdout).toBe('sandbox output');
|
|
140
|
+
expect(result.exitCode).toBe(0);
|
|
141
|
+
expect(mockShellExecutor.exec).toHaveBeenCalledWith('echo hello', 5000, expect.any(AbortSignal));
|
|
142
|
+
});
|
|
143
|
+
it('exec() falls back to local when no shellExecutor', async () => {
|
|
144
|
+
const session = makeSession();
|
|
145
|
+
// No shellExecutor set
|
|
146
|
+
const ctx = buildToolContext(session, makeTool(), signal);
|
|
147
|
+
const result = await ctx.exec('echo "local fallback"');
|
|
148
|
+
expect(result.exitCode).toBe(0);
|
|
149
|
+
expect(result.stdout.trim()).toBe('local fallback');
|
|
150
|
+
});
|
|
151
|
+
});
|
|
152
|
+
//# sourceMappingURL=tool-context-builder.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tool-context-builder.test.js","sourceRoot":"","sources":["../../../src/agent/tool-context-builder.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2025 Amodal Labs, Inc.
|
|
4
|
+
* SPDX-License-Identifier: MIT
|
|
5
|
+
*/
|
|
6
|
+
import type { CustomToolExecutor, CustomToolContext, LoadedTool } from '@amodalai/core';
|
|
7
|
+
/**
|
|
8
|
+
* Executes custom tool handlers locally via dynamic import.
|
|
9
|
+
*/
|
|
10
|
+
export declare class LocalToolExecutor implements CustomToolExecutor {
|
|
11
|
+
private readonly handlerCache;
|
|
12
|
+
execute(tool: LoadedTool, params: Record<string, unknown>, ctx: CustomToolContext): Promise<unknown>;
|
|
13
|
+
dispose(): void;
|
|
14
|
+
private loadHandler;
|
|
15
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2025 Amodal Labs, Inc.
|
|
4
|
+
* SPDX-License-Identifier: MIT
|
|
5
|
+
*/
|
|
6
|
+
import { pathToFileURL } from 'node:url';
|
|
7
|
+
/**
|
|
8
|
+
* Executes custom tool handlers locally via dynamic import.
|
|
9
|
+
*/
|
|
10
|
+
export class LocalToolExecutor {
|
|
11
|
+
handlerCache = new Map();
|
|
12
|
+
async execute(tool, params, ctx) {
|
|
13
|
+
try {
|
|
14
|
+
const handler = await this.loadHandler(tool);
|
|
15
|
+
const fn = resolveHandlerFn(handler, tool.name);
|
|
16
|
+
const result = await fn(params, ctx);
|
|
17
|
+
// Wrap non-object results
|
|
18
|
+
if (result === null || result === undefined) {
|
|
19
|
+
return { result: null };
|
|
20
|
+
}
|
|
21
|
+
if (typeof result !== 'object' || Array.isArray(result)) {
|
|
22
|
+
return { result };
|
|
23
|
+
}
|
|
24
|
+
return result;
|
|
25
|
+
}
|
|
26
|
+
catch (err) {
|
|
27
|
+
if (ctx.signal.aborted) {
|
|
28
|
+
return { error: 'Tool execution aborted' };
|
|
29
|
+
}
|
|
30
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
31
|
+
return { error: message };
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
dispose() {
|
|
35
|
+
this.handlerCache.clear();
|
|
36
|
+
}
|
|
37
|
+
async loadHandler(tool) {
|
|
38
|
+
const cached = this.handlerCache.get(tool.handlerPath);
|
|
39
|
+
if (cached) {
|
|
40
|
+
return cached;
|
|
41
|
+
}
|
|
42
|
+
const moduleUrl = pathToFileURL(tool.handlerPath).href;
|
|
43
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- dynamic import returns unknown
|
|
44
|
+
const mod = await import(moduleUrl);
|
|
45
|
+
if (typeof mod.default !== 'function' && !isDefineToolResult(mod.default)) {
|
|
46
|
+
throw new Error(`Tool "${tool.name}" handler must export a default function or use defineToolHandler()`);
|
|
47
|
+
}
|
|
48
|
+
this.handlerCache.set(tool.handlerPath, mod);
|
|
49
|
+
return mod;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Check if a default export is a defineToolHandler result.
|
|
54
|
+
*/
|
|
55
|
+
function isDefineToolResult(value) {
|
|
56
|
+
return (typeof value === 'object' &&
|
|
57
|
+
value !== null &&
|
|
58
|
+
'__toolHandler' in value &&
|
|
59
|
+
value['__toolHandler'] === true);
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Resolve the actual handler function from a module's default export.
|
|
63
|
+
* Handles both plain functions and defineToolHandler results.
|
|
64
|
+
*/
|
|
65
|
+
function resolveHandlerFn(mod, toolName) {
|
|
66
|
+
if (typeof mod.default === 'function') {
|
|
67
|
+
return mod.default;
|
|
68
|
+
}
|
|
69
|
+
if (isDefineToolResult(mod.default)) {
|
|
70
|
+
return mod.default.handler;
|
|
71
|
+
}
|
|
72
|
+
throw new Error(`Tool "${toolName}" handler must export a default function or use defineToolHandler()`);
|
|
73
|
+
}
|
|
74
|
+
//# sourceMappingURL=tool-executor-local.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tool-executor-local.js","sourceRoot":"","sources":["../../../src/agent/tool-executor-local.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2025 Amodal Labs, Inc.
|
|
4
|
+
* SPDX-License-Identifier: MIT
|
|
5
|
+
*/
|
|
6
|
+
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
7
|
+
import { mkdtempSync, mkdirSync, writeFileSync, rmSync } from 'node:fs';
|
|
8
|
+
import { join } from 'node:path';
|
|
9
|
+
import { tmpdir } from 'node:os';
|
|
10
|
+
import { LocalToolExecutor } from './tool-executor-local.js';
|
|
11
|
+
describe('LocalToolExecutor', () => {
|
|
12
|
+
let tempDir;
|
|
13
|
+
let executor;
|
|
14
|
+
beforeEach(() => {
|
|
15
|
+
tempDir = mkdtempSync(join(tmpdir(), 'tool-executor-test-'));
|
|
16
|
+
executor = new LocalToolExecutor();
|
|
17
|
+
});
|
|
18
|
+
afterEach(() => {
|
|
19
|
+
executor.dispose();
|
|
20
|
+
rmSync(tempDir, { recursive: true, force: true });
|
|
21
|
+
});
|
|
22
|
+
function createHandler(name, code) {
|
|
23
|
+
const handlerDir = join(tempDir, name);
|
|
24
|
+
mkdirSync(handlerDir, { recursive: true });
|
|
25
|
+
// Write as .mjs so dynamic import works without TS compilation
|
|
26
|
+
const handlerPath = join(handlerDir, 'handler.mjs');
|
|
27
|
+
writeFileSync(handlerPath, code);
|
|
28
|
+
return handlerPath;
|
|
29
|
+
}
|
|
30
|
+
function makeTool(handlerPath, overrides = {}) {
|
|
31
|
+
return {
|
|
32
|
+
name: 'test_tool',
|
|
33
|
+
description: 'Test tool',
|
|
34
|
+
parameters: {},
|
|
35
|
+
confirm: false,
|
|
36
|
+
timeout: 5000,
|
|
37
|
+
env: [],
|
|
38
|
+
handlerPath,
|
|
39
|
+
location: tempDir,
|
|
40
|
+
hasPackageJson: false,
|
|
41
|
+
hasDockerfile: false,
|
|
42
|
+
hasSetupScript: false,
|
|
43
|
+
hasRequirementsTxt: false,
|
|
44
|
+
sandboxLanguage: 'typescript',
|
|
45
|
+
...overrides,
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
function makeCtx() {
|
|
49
|
+
return {
|
|
50
|
+
exec: async () => ({ stdout: '', stderr: '', exitCode: 0 }),
|
|
51
|
+
request: async () => ({}),
|
|
52
|
+
env: () => undefined,
|
|
53
|
+
log: () => { },
|
|
54
|
+
user: { roles: [] },
|
|
55
|
+
signal: AbortSignal.timeout(10000),
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
it('executes a handler that returns an object', async () => {
|
|
59
|
+
const handlerPath = createHandler('obj-handler', 'export default async (params) => ({ value: params.x * 2 });');
|
|
60
|
+
const tool = makeTool(handlerPath);
|
|
61
|
+
const result = await executor.execute(tool, { x: 5 }, makeCtx());
|
|
62
|
+
expect(result).toEqual({ value: 10 });
|
|
63
|
+
});
|
|
64
|
+
it('wraps non-object return values', async () => {
|
|
65
|
+
const handlerPath = createHandler('num-handler', 'export default async () => 42;');
|
|
66
|
+
const tool = makeTool(handlerPath);
|
|
67
|
+
const result = await executor.execute(tool, {}, makeCtx());
|
|
68
|
+
expect(result).toEqual({ result: 42 });
|
|
69
|
+
});
|
|
70
|
+
it('wraps null return values', async () => {
|
|
71
|
+
const handlerPath = createHandler('null-handler', 'export default async () => null;');
|
|
72
|
+
const tool = makeTool(handlerPath);
|
|
73
|
+
const result = await executor.execute(tool, {}, makeCtx());
|
|
74
|
+
expect(result).toEqual({ result: null });
|
|
75
|
+
});
|
|
76
|
+
it('wraps array return values', async () => {
|
|
77
|
+
const handlerPath = createHandler('arr-handler', 'export default async () => [1, 2, 3];');
|
|
78
|
+
const tool = makeTool(handlerPath);
|
|
79
|
+
const result = await executor.execute(tool, {}, makeCtx());
|
|
80
|
+
expect(result).toEqual({ result: [1, 2, 3] });
|
|
81
|
+
});
|
|
82
|
+
it('catches handler errors', async () => {
|
|
83
|
+
const handlerPath = createHandler('err-handler', 'export default async () => { throw new Error("boom"); };');
|
|
84
|
+
const tool = makeTool(handlerPath);
|
|
85
|
+
const result = await executor.execute(tool, {}, makeCtx());
|
|
86
|
+
expect(result).toEqual({ error: 'boom' });
|
|
87
|
+
});
|
|
88
|
+
it('caches imported modules', async () => {
|
|
89
|
+
const handlerPath = createHandler('cache-handler', 'let count = 0; export default async () => ({ count: ++count });');
|
|
90
|
+
const tool = makeTool(handlerPath);
|
|
91
|
+
const ctx = makeCtx();
|
|
92
|
+
const r1 = await executor.execute(tool, {}, ctx);
|
|
93
|
+
const r2 = await executor.execute(tool, {}, ctx);
|
|
94
|
+
// Both calls use the same module (count increments)
|
|
95
|
+
expect(r1).toEqual({ count: 1 });
|
|
96
|
+
expect(r2).toEqual({ count: 2 });
|
|
97
|
+
});
|
|
98
|
+
it('clears cache on dispose', async () => {
|
|
99
|
+
const handlerPath = createHandler('dispose-handler', 'export default async () => ({ ok: true });');
|
|
100
|
+
const tool = makeTool(handlerPath);
|
|
101
|
+
await executor.execute(tool, {}, makeCtx());
|
|
102
|
+
executor.dispose();
|
|
103
|
+
// After dispose, a new executor needs to reimport
|
|
104
|
+
const executor2 = new LocalToolExecutor();
|
|
105
|
+
const result = await executor2.execute(tool, {}, makeCtx());
|
|
106
|
+
expect(result).toEqual({ ok: true });
|
|
107
|
+
executor2.dispose();
|
|
108
|
+
});
|
|
109
|
+
it('throws for handler without default export', async () => {
|
|
110
|
+
const handlerPath = createHandler('no-default', 'export const handler = async () => ({});');
|
|
111
|
+
const tool = makeTool(handlerPath);
|
|
112
|
+
const result = await executor.execute(tool, {}, makeCtx());
|
|
113
|
+
expect(result).toHaveProperty('error');
|
|
114
|
+
});
|
|
115
|
+
});
|
|
116
|
+
//# sourceMappingURL=tool-executor-local.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tool-executor-local.test.js","sourceRoot":"","sources":["../../../src/agent/tool-executor-local.test.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAC,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,UAAU,EAAE,SAAS,EAAC,MAAM,QAAQ,CAAC;AACnE,OAAO,EAAC,WAAW,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,EAAC,MAAM,SAAS,CAAC;AACtE,OAAO,EAAC,IAAI,EAAC,MAAM,WAAW,CAAC;AAC/B,OAAO,EAAC,MAAM,EAAC,MAAM,SAAS,CAAC;AAE/B,OAAO,EAAC,iBAAiB,EAAC,MAAM,0BAA0B,CAAC;AAE3D,QAAQ,CAAC,mBAAmB,EAAE,GAAG,EAAE;IACjC,IAAI,OAAe,CAAC;IACpB,IAAI,QAA2B,CAAC;IAEhC,UAAU,CAAC,GAAG,EAAE;QACd,OAAO,GAAG,WAAW,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,qBAAqB,CAAC,CAAC,CAAC;QAC7D,QAAQ,GAAG,IAAI,iBAAiB,EAAE,CAAC;IACrC,CAAC,CAAC,CAAC;IAEH,SAAS,CAAC,GAAG,EAAE;QACb,QAAQ,CAAC,OAAO,EAAE,CAAC;QACnB,MAAM,CAAC,OAAO,EAAE,EAAC,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAC,CAAC,CAAC;IAClD,CAAC,CAAC,CAAC;IAEH,SAAS,aAAa,CAAC,IAAY,EAAE,IAAY;QAC/C,MAAM,UAAU,GAAG,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;QACvC,SAAS,CAAC,UAAU,EAAE,EAAC,SAAS,EAAE,IAAI,EAAC,CAAC,CAAC;QACzC,+DAA+D;QAC/D,MAAM,WAAW,GAAG,IAAI,CAAC,UAAU,EAAE,aAAa,CAAC,CAAC;QACpD,aAAa,CAAC,WAAW,EAAE,IAAI,CAAC,CAAC;QACjC,OAAO,WAAW,CAAC;IACrB,CAAC;IAED,SAAS,QAAQ,CAAC,WAAmB,EAAE,YAAiC,EAAE;QACxE,OAAO;YACL,IAAI,EAAE,WAAW;YACjB,WAAW,EAAE,WAAW;YACxB,UAAU,EAAE,EAAE;YACd,OAAO,EAAE,KAAK;YACd,OAAO,EAAE,IAAI;YACb,GAAG,EAAE,EAAE;YACP,WAAW;YACX,QAAQ,EAAE,OAAO;YACjB,cAAc,EAAE,KAAK;YACrB,aAAa,EAAE,KAAK;YACpB,cAAc,EAAE,KAAK;YACrB,kBAAkB,EAAE,KAAK;YACzB,eAAe,EAAE,YAAY;YAC7B,GAAG,SAAS;SACb,CAAC;IACJ,CAAC;IAED,SAAS,OAAO;QACd,OAAO;YACL,IAAI,EAAE,KAAK,IAAI,EAAE,CAAC,CAAC,EAAC,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,QAAQ,EAAE,CAAC,EAAC,CAAC;YACzD,OAAO,EAAE,KAAK,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC;YACzB,GAAG,EAAE,GAAG,EAAE,CAAC,SAAS;YACpB,GAAG,EAAE,GAAG,EAAE,GAAE,CAAC;YACb,IAAI,EAAE,EAAC,KAAK,EAAE,EAAE,EAAC;YACjB,MAAM,EAAE,WAAW,CAAC,OAAO,CAAC,KAAK,CAAC;SACnC,CAAC;IACJ,CAAC;IAED,EAAE,CAAC,2CAA2C,EAAE,KAAK,IAAI,EAAE;QACzD,MAAM,WAAW,GAAG,aAAa,CAAC,aAAa,EAC7C,6DAA6D,CAC9D,CAAC;QACF,MAAM,IAAI,GAAG,QAAQ,CAAC,WAAW,CAAC,CAAC;QACnC,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,OAAO,CAAC,IAAI,EAAE,EAAC,CAAC,EAAE,CAAC,EAAC,EAAE,OAAO,EAAE,CAAC,CAAC;QAC/D,MAAM,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,EAAC,KAAK,EAAE,EAAE,EAAC,CAAC,CAAC;IACtC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,gCAAgC,EAAE,KAAK,IAAI,EAAE;QAC9C,MAAM,WAAW,GAAG,aAAa,CAAC,aAAa,EAC7C,gCAAgC,CACjC,CAAC;QACF,MAAM,IAAI,GAAG,QAAQ,CAAC,WAAW,CAAC,CAAC;QACnC,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,EAAE,OAAO,EAAE,CAAC,CAAC;QAC3D,MAAM,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,EAAC,MAAM,EAAE,EAAE,EAAC,CAAC,CAAC;IACvC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,0BAA0B,EAAE,KAAK,IAAI,EAAE;QACxC,MAAM,WAAW,GAAG,aAAa,CAAC,cAAc,EAC9C,kCAAkC,CACnC,CAAC;QACF,MAAM,IAAI,GAAG,QAAQ,CAAC,WAAW,CAAC,CAAC;QACnC,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,EAAE,OAAO,EAAE,CAAC,CAAC;QAC3D,MAAM,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,EAAC,MAAM,EAAE,IAAI,EAAC,CAAC,CAAC;IACzC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,2BAA2B,EAAE,KAAK,IAAI,EAAE;QACzC,MAAM,WAAW,GAAG,aAAa,CAAC,aAAa,EAC7C,uCAAuC,CACxC,CAAC;QACF,MAAM,IAAI,GAAG,QAAQ,CAAC,WAAW,CAAC,CAAC;QACnC,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,EAAE,OAAO,EAAE,CAAC,CAAC;QAC3D,MAAM,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,EAAC,MAAM,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,EAAC,CAAC,CAAC;IAC9C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,wBAAwB,EAAE,KAAK,IAAI,EAAE;QACtC,MAAM,WAAW,GAAG,aAAa,CAAC,aAAa,EAC7C,0DAA0D,CAC3D,CAAC;QACF,MAAM,IAAI,GAAG,QAAQ,CAAC,WAAW,CAAC,CAAC;QACnC,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,EAAE,OAAO,EAAE,CAAC,CAAC;QAC3D,MAAM,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,EAAC,KAAK,EAAE,MAAM,EAAC,CAAC,CAAC;IAC1C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,yBAAyB,EAAE,KAAK,IAAI,EAAE;QACvC,MAAM,WAAW,GAAG,aAAa,CAAC,eAAe,EAC/C,iEAAiE,CAClE,CAAC;QACF,MAAM,IAAI,GAAG,QAAQ,CAAC,WAAW,CAAC,CAAC;QACnC,MAAM,GAAG,GAAG,OAAO,EAAE,CAAC;QAEtB,MAAM,EAAE,GAAG,MAAM,QAAQ,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,EAAE,GAAG,CAAC,CAAC;QACjD,MAAM,EAAE,GAAG,MAAM,QAAQ,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,EAAE,GAAG,CAAC,CAAC;QAEjD,oDAAoD;QACpD,MAAM,CAAC,EAAE,CAAC,CAAC,OAAO,CAAC,EAAC,KAAK,EAAE,CAAC,EAAC,CAAC,CAAC;QAC/B,MAAM,CAAC,EAAE,CAAC,CAAC,OAAO,CAAC,EAAC,KAAK,EAAE,CAAC,EAAC,CAAC,CAAC;IACjC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,yBAAyB,EAAE,KAAK,IAAI,EAAE;QACvC,MAAM,WAAW,GAAG,aAAa,CAAC,iBAAiB,EACjD,4CAA4C,CAC7C,CAAC;QACF,MAAM,IAAI,GAAG,QAAQ,CAAC,WAAW,CAAC,CAAC;QAEnC,MAAM,QAAQ,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,EAAE,OAAO,EAAE,CAAC,CAAC;QAC5C,QAAQ,CAAC,OAAO,EAAE,CAAC;QAEnB,kDAAkD;QAClD,MAAM,SAAS,GAAG,IAAI,iBAAiB,EAAE,CAAC;QAC1C,MAAM,MAAM,GAAG,MAAM,SAAS,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,EAAE,OAAO,EAAE,CAAC,CAAC;QAC5D,MAAM,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,EAAC,EAAE,EAAE,IAAI,EAAC,CAAC,CAAC;QACnC,SAAS,CAAC,OAAO,EAAE,CAAC;IACtB,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,2CAA2C,EAAE,KAAK,IAAI,EAAE;QACzD,MAAM,WAAW,GAAG,aAAa,CAAC,YAAY,EAC5C,0CAA0C,CAC3C,CAAC;QACF,MAAM,IAAI,GAAG,QAAQ,CAAC,WAAW,CAAC,CAAC;QACnC,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,EAAE,OAAO,EAAE,CAAC,CAAC;QAC3D,MAAM,CAAC,MAAM,CAAC,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC;IACzC,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2025 Amodal Labs, Inc.
|
|
4
|
+
* SPDX-License-Identifier: MIT
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Generates the entry.js content for a sandbox tool wrapper.
|
|
8
|
+
*
|
|
9
|
+
* This script runs inside a Daytona sandbox workspace:
|
|
10
|
+
* 1. Reads invocation payload from /tmp/invocation.json
|
|
11
|
+
* 2. Imports the tool handler
|
|
12
|
+
* 3. Creates a proxy ToolContext (where request() and exec() delegate to the host)
|
|
13
|
+
* 4. Executes the handler
|
|
14
|
+
* 5. Writes JSON result to stdout
|
|
15
|
+
*/
|
|
16
|
+
export declare function generateToolHarnessEntry(handlerRelativePath: string): string;
|
|
17
|
+
/**
|
|
18
|
+
* Generates a default Dockerfile for tools without a custom one.
|
|
19
|
+
*
|
|
20
|
+
* The image contains Node 22 (Alpine), installs npm deps if present,
|
|
21
|
+
* and copies the full tool directory into /tool.
|
|
22
|
+
*/
|
|
23
|
+
export declare function generateDefaultDockerfile(hasPackageJson: boolean): string;
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2025 Amodal Labs, Inc.
|
|
4
|
+
* SPDX-License-Identifier: MIT
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Generates the entry.js content for a sandbox tool wrapper.
|
|
8
|
+
*
|
|
9
|
+
* This script runs inside a Daytona sandbox workspace:
|
|
10
|
+
* 1. Reads invocation payload from /tmp/invocation.json
|
|
11
|
+
* 2. Imports the tool handler
|
|
12
|
+
* 3. Creates a proxy ToolContext (where request() and exec() delegate to the host)
|
|
13
|
+
* 4. Executes the handler
|
|
14
|
+
* 5. Writes JSON result to stdout
|
|
15
|
+
*/
|
|
16
|
+
export function generateToolHarnessEntry(handlerRelativePath) {
|
|
17
|
+
return `
|
|
18
|
+
import {readFileSync} from 'node:fs';
|
|
19
|
+
import {execSync} from 'node:child_process';
|
|
20
|
+
import handler from '${handlerRelativePath}';
|
|
21
|
+
|
|
22
|
+
const payload = JSON.parse(readFileSync('/tmp/invocation.json', 'utf-8'));
|
|
23
|
+
const {params, callbackUrl} = payload;
|
|
24
|
+
|
|
25
|
+
// Resolve handler function — supports both plain functions and defineToolHandler
|
|
26
|
+
const fn = typeof handler === 'function'
|
|
27
|
+
? handler
|
|
28
|
+
: (handler.__toolHandler ? handler.handler : handler);
|
|
29
|
+
|
|
30
|
+
const ctx = {
|
|
31
|
+
async request(connection, endpoint, options) {
|
|
32
|
+
const res = await fetch(callbackUrl + '/request', {
|
|
33
|
+
method: 'POST',
|
|
34
|
+
headers: {'Content-Type': 'application/json'},
|
|
35
|
+
body: JSON.stringify({connection, endpoint, ...options}),
|
|
36
|
+
});
|
|
37
|
+
return res.json();
|
|
38
|
+
},
|
|
39
|
+
exec(command, options) {
|
|
40
|
+
try {
|
|
41
|
+
const stdout = execSync(command, {
|
|
42
|
+
cwd: options?.cwd ?? '/tool',
|
|
43
|
+
timeout: options?.timeout ?? payload.timeout ?? 30000,
|
|
44
|
+
maxBuffer: 1024 * 1024,
|
|
45
|
+
encoding: 'utf-8',
|
|
46
|
+
});
|
|
47
|
+
return { stdout, stderr: '', exitCode: 0 };
|
|
48
|
+
} catch (err) {
|
|
49
|
+
return {
|
|
50
|
+
stdout: err.stdout ?? '',
|
|
51
|
+
stderr: err.stderr ?? err.message,
|
|
52
|
+
exitCode: typeof err.status === 'number' ? err.status : 1,
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
},
|
|
56
|
+
env(name) {
|
|
57
|
+
return process.env[name];
|
|
58
|
+
},
|
|
59
|
+
log(message) {
|
|
60
|
+
process.stderr.write('[tool] ' + message + '\\n');
|
|
61
|
+
},
|
|
62
|
+
user: payload.user ?? {roles: []},
|
|
63
|
+
signal: AbortSignal.timeout(payload.timeout ?? 30000),
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
try {
|
|
67
|
+
const result = await fn(params, ctx);
|
|
68
|
+
process.stdout.write(JSON.stringify({result}) + '\\n');
|
|
69
|
+
} catch (err) {
|
|
70
|
+
process.stdout.write(JSON.stringify({error: err.message ?? String(err)}) + '\\n');
|
|
71
|
+
process.exit(1);
|
|
72
|
+
}
|
|
73
|
+
`.trim();
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Generates a default Dockerfile for tools without a custom one.
|
|
77
|
+
*
|
|
78
|
+
* The image contains Node 22 (Alpine), installs npm deps if present,
|
|
79
|
+
* and copies the full tool directory into /tool.
|
|
80
|
+
*/
|
|
81
|
+
export function generateDefaultDockerfile(hasPackageJson) {
|
|
82
|
+
const lines = [
|
|
83
|
+
'FROM node:22-alpine',
|
|
84
|
+
'WORKDIR /tool',
|
|
85
|
+
];
|
|
86
|
+
if (hasPackageJson) {
|
|
87
|
+
lines.push('COPY package.json package-lock.json* ./');
|
|
88
|
+
lines.push('RUN npm install --production');
|
|
89
|
+
}
|
|
90
|
+
lines.push('COPY . .');
|
|
91
|
+
lines.push('');
|
|
92
|
+
return lines.join('\n');
|
|
93
|
+
}
|
|
94
|
+
//# sourceMappingURL=tool-harness-template.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tool-harness-template.js","sourceRoot":"","sources":["../../../src/agent/tool-harness-template.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH;;;;;;;;;GASG;AACH,MAAM,UAAU,wBAAwB,CAAC,mBAA2B;IAClE,OAAO;;;uBAGc,mBAAmB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAqDzC,CAAC,IAAI,EAAE,CAAC;AACT,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,yBAAyB,CAAC,cAAuB;IAC/D,MAAM,KAAK,GAAG;QACZ,qBAAqB;QACrB,eAAe;KAChB,CAAC;IAEF,IAAI,cAAc,EAAE,CAAC;QACnB,KAAK,CAAC,IAAI,CAAC,yCAAyC,CAAC,CAAC;QACtD,KAAK,CAAC,IAAI,CAAC,8BAA8B,CAAC,CAAC;IAC7C,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;IACvB,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAEf,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2025 Amodal Labs, Inc.
|
|
4
|
+
* SPDX-License-Identifier: MIT
|
|
5
|
+
*/
|
|
6
|
+
import type { AmodalRepo } from '@amodalai/core';
|
|
7
|
+
import type { ConnectionsMap } from '@amodalai/core';
|
|
8
|
+
/**
|
|
9
|
+
* Fetches user context from a configured endpoint.
|
|
10
|
+
*
|
|
11
|
+
* The `config.userContext` value is a string like "GET crm/users/me"
|
|
12
|
+
* (method connection/path). The connection auth is resolved from
|
|
13
|
+
* the connections map.
|
|
14
|
+
*/
|
|
15
|
+
export declare function fetchUserContext(repo: AmodalRepo, tenantToken: string, connectionsMap: ConnectionsMap): Promise<Record<string, unknown>>;
|
|
16
|
+
interface ParsedSpec {
|
|
17
|
+
method: string;
|
|
18
|
+
connection: string;
|
|
19
|
+
path: string;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Parse "GET crm/users/me" → {method: "GET", connection: "crm", path: "users/me"}
|
|
23
|
+
*/
|
|
24
|
+
declare function parseUserContextSpec(spec: string): ParsedSpec | null;
|
|
25
|
+
export { parseUserContextSpec as _parseUserContextSpecForTesting };
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2025 Amodal Labs, Inc.
|
|
4
|
+
* SPDX-License-Identifier: MIT
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Fetches user context from a configured endpoint.
|
|
8
|
+
*
|
|
9
|
+
* The `config.userContext` value is a string like "GET crm/users/me"
|
|
10
|
+
* (method connection/path). The connection auth is resolved from
|
|
11
|
+
* the connections map.
|
|
12
|
+
*/
|
|
13
|
+
export async function fetchUserContext(repo, tenantToken, connectionsMap) {
|
|
14
|
+
const spec = repo.config.userContext;
|
|
15
|
+
if (!spec) {
|
|
16
|
+
return {};
|
|
17
|
+
}
|
|
18
|
+
const parsed = parseUserContextSpec(spec);
|
|
19
|
+
if (!parsed) {
|
|
20
|
+
return {};
|
|
21
|
+
}
|
|
22
|
+
const connConfig = connectionsMap[parsed.connection];
|
|
23
|
+
if (!connConfig) {
|
|
24
|
+
return {};
|
|
25
|
+
}
|
|
26
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- SDK boundary: connections map values are records
|
|
27
|
+
const baseUrl = connConfig['base_url'];
|
|
28
|
+
if (!baseUrl) {
|
|
29
|
+
return {};
|
|
30
|
+
}
|
|
31
|
+
const url = `${baseUrl.replace(/\/$/, '')}/${parsed.path.replace(/^\//, '')}`;
|
|
32
|
+
const headers = {
|
|
33
|
+
'Content-Type': 'application/json',
|
|
34
|
+
'Authorization': `Bearer ${tenantToken}`,
|
|
35
|
+
};
|
|
36
|
+
const controller = new AbortController();
|
|
37
|
+
const timeout = setTimeout(() => controller.abort(), 5000);
|
|
38
|
+
try {
|
|
39
|
+
const response = await fetch(url, {
|
|
40
|
+
method: parsed.method,
|
|
41
|
+
headers,
|
|
42
|
+
signal: controller.signal,
|
|
43
|
+
});
|
|
44
|
+
if (!response.ok) {
|
|
45
|
+
return {};
|
|
46
|
+
}
|
|
47
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- SDK boundary: expecting JSON object from API
|
|
48
|
+
const data = (await response.json());
|
|
49
|
+
return data;
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
return {};
|
|
53
|
+
}
|
|
54
|
+
finally {
|
|
55
|
+
clearTimeout(timeout);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Parse "GET crm/users/me" → {method: "GET", connection: "crm", path: "users/me"}
|
|
60
|
+
*/
|
|
61
|
+
function parseUserContextSpec(spec) {
|
|
62
|
+
const parts = spec.trim().split(/\s+/);
|
|
63
|
+
if (parts.length < 2) {
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
const method = parts[0]?.toUpperCase() ?? '';
|
|
67
|
+
const fullPath = parts[1] ?? '';
|
|
68
|
+
const slashIdx = fullPath.indexOf('/');
|
|
69
|
+
if (slashIdx === -1) {
|
|
70
|
+
return { method, connection: fullPath, path: '/' };
|
|
71
|
+
}
|
|
72
|
+
return {
|
|
73
|
+
method,
|
|
74
|
+
connection: fullPath.substring(0, slashIdx),
|
|
75
|
+
path: fullPath.substring(slashIdx),
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
export { parseUserContextSpec as _parseUserContextSpecForTesting };
|
|
79
|
+
//# sourceMappingURL=user-context-fetcher.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"user-context-fetcher.js","sourceRoot":"","sources":["../../../src/agent/user-context-fetcher.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAKH;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB,CACpC,IAAgB,EAChB,WAAmB,EACnB,cAA8B;IAE9B,MAAM,IAAI,GAAG,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC;IACrC,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,MAAM,MAAM,GAAG,oBAAoB,CAAC,IAAI,CAAC,CAAC;IAC1C,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,MAAM,UAAU,GAAG,cAAc,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;IACrD,IAAI,CAAC,UAAU,EAAE,CAAC;QAChB,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,2HAA2H;IAC3H,MAAM,OAAO,GAAI,UAAsC,CAAC,UAAU,CAAuB,CAAC;IAC1F,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,MAAM,GAAG,GAAG,GAAG,OAAO,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,IAAI,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,EAAE,CAAC;IAE9E,MAAM,OAAO,GAA2B;QACtC,cAAc,EAAE,kBAAkB;QAClC,eAAe,EAAE,UAAU,WAAW,EAAE;KACzC,CAAC;IAEF,MAAM,UAAU,GAAG,IAAI,eAAe,EAAE,CAAC;IACzC,MAAM,OAAO,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,UAAU,CAAC,KAAK,EAAE,EAAE,IAAI,CAAC,CAAC;IAE3D,IAAI,CAAC;QACH,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE;YAChC,MAAM,EAAE,MAAM,CAAC,MAAM;YACrB,OAAO;YACP,MAAM,EAAE,UAAU,CAAC,MAAM;SAC1B,CAAC,CAAC;QAEH,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;YACjB,OAAO,EAAE,CAAC;QACZ,CAAC;QAED,uHAAuH;QACvH,MAAM,IAAI,GAAG,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAA4B,CAAC;QAChE,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;YAAS,CAAC;QACT,YAAY,CAAC,OAAO,CAAC,CAAC;IACxB,CAAC;AACH,CAAC;AAQD;;GAEG;AACH,SAAS,oBAAoB,CAAC,IAAY;IACxC,MAAM,KAAK,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;IACvC,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACrB,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM,MAAM,GAAG,KAAK,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC;IAC7C,MAAM,QAAQ,GAAG,KAAK,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;IAEhC,MAAM,QAAQ,GAAG,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IACvC,IAAI,QAAQ,KAAK,CAAC,CAAC,EAAE,CAAC;QACpB,OAAO,EAAC,MAAM,EAAE,UAAU,EAAE,QAAQ,EAAE,IAAI,EAAE,GAAG,EAAC,CAAC;IACnD,CAAC;IAED,OAAO;QACL,MAAM;QACN,UAAU,EAAE,QAAQ,CAAC,SAAS,CAAC,CAAC,EAAE,QAAQ,CAAC;QAC3C,IAAI,EAAE,QAAQ,CAAC,SAAS,CAAC,QAAQ,CAAC;KACnC,CAAC;AACJ,CAAC;AAED,OAAO,EAAC,oBAAoB,IAAI,+BAA+B,EAAC,CAAC"}
|