@swarmclawai/swarmclaw 0.7.2 → 0.7.4
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/README.md +116 -50
- package/bin/package-manager.js +157 -0
- package/bin/package-manager.test.js +90 -0
- package/bin/server-cmd.js +38 -7
- package/bin/swarmclaw.js +54 -4
- package/bin/update-cmd.js +48 -10
- package/bin/update-cmd.test.js +55 -0
- package/package.json +8 -3
- package/scripts/postinstall.mjs +26 -0
- package/src/app/api/agents/[id]/route.ts +43 -0
- package/src/app/api/agents/[id]/thread/route.ts +39 -8
- package/src/app/api/agents/route.ts +35 -2
- package/src/app/api/auth/route.ts +77 -8
- package/src/app/api/chatrooms/[id]/chat/route.ts +22 -6
- package/src/app/api/chatrooms/[id]/pins/route.ts +2 -1
- package/src/app/api/chatrooms/[id]/reactions/route.ts +2 -1
- package/src/app/api/chatrooms/[id]/route.ts +6 -0
- package/src/app/api/chats/[id]/browser/route.ts +5 -1
- package/src/app/api/chats/[id]/chat/route.ts +7 -3
- package/src/app/api/chats/[id]/messages/route.ts +19 -13
- package/src/app/api/chats/[id]/route.ts +30 -0
- package/src/app/api/chats/[id]/stop/route.ts +6 -1
- package/src/app/api/chats/heartbeat/route.ts +2 -1
- package/src/app/api/chats/route.ts +23 -1
- package/src/app/api/connectors/[id]/doctor/route.ts +26 -0
- package/src/app/api/connectors/doctor/route.ts +13 -0
- package/src/app/api/external-agents/[id]/heartbeat/route.ts +33 -0
- package/src/app/api/external-agents/[id]/route.ts +31 -0
- package/src/app/api/external-agents/register/route.ts +3 -0
- package/src/app/api/external-agents/route.ts +66 -0
- package/src/app/api/files/open/route.ts +16 -14
- package/src/app/api/gateways/[id]/health/route.ts +28 -0
- package/src/app/api/gateways/[id]/route.ts +79 -0
- package/src/app/api/gateways/route.ts +57 -0
- package/src/app/api/memory/maintenance/route.ts +11 -1
- package/src/app/api/openclaw/agent-files/route.ts +27 -4
- package/src/app/api/openclaw/gateway/route.ts +10 -7
- package/src/app/api/openclaw/skills/route.ts +12 -4
- package/src/app/api/plugins/dependencies/route.ts +24 -0
- package/src/app/api/plugins/install/route.ts +15 -92
- package/src/app/api/plugins/route.ts +3 -26
- package/src/app/api/plugins/settings/route.ts +17 -12
- package/src/app/api/plugins/ui/route.ts +1 -0
- package/src/app/api/providers/[id]/discover-models/route.ts +27 -0
- package/src/app/api/schedules/[id]/route.ts +38 -9
- package/src/app/api/schedules/route.ts +51 -28
- package/src/app/api/settings/route.ts +55 -17
- package/src/app/api/setup/doctor/route.ts +6 -4
- package/src/app/api/tasks/[id]/route.ts +16 -6
- package/src/app/api/tasks/bulk/route.ts +3 -3
- package/src/app/api/tasks/route.ts +9 -4
- package/src/app/api/webhooks/[id]/route.ts +8 -1
- package/src/app/page.tsx +135 -17
- package/src/cli/binary.test.js +142 -0
- package/src/cli/index.js +38 -11
- package/src/cli/index.test.js +195 -0
- package/src/cli/index.ts +21 -12
- package/src/cli/server-cmd.test.js +59 -0
- package/src/cli/spec.js +20 -2
- package/src/components/agents/agent-card.tsx +15 -12
- package/src/components/agents/agent-chat-list.tsx +101 -1
- package/src/components/agents/agent-list.tsx +46 -9
- package/src/components/agents/agent-sheet.tsx +456 -23
- package/src/components/agents/inspector-panel.tsx +110 -49
- package/src/components/agents/sandbox-env-panel.tsx +4 -1
- package/src/components/auth/access-key-gate.tsx +36 -97
- package/src/components/auth/setup-wizard.tsx +970 -275
- package/src/components/chat/chat-area.tsx +70 -27
- package/src/components/chat/chat-card.tsx +6 -21
- package/src/components/chat/chat-header.tsx +263 -366
- package/src/components/chat/chat-list.tsx +62 -26
- package/src/components/chat/checkpoint-timeline.tsx +1 -1
- package/src/components/chat/message-list.tsx +145 -19
- package/src/components/chatrooms/chatroom-input.tsx +96 -33
- package/src/components/chatrooms/chatroom-list.tsx +141 -72
- package/src/components/chatrooms/chatroom-message.tsx +7 -6
- package/src/components/chatrooms/chatroom-sheet.tsx +13 -1
- package/src/components/chatrooms/chatroom-tool-request-banner.tsx +5 -2
- package/src/components/chatrooms/chatroom-view.tsx +422 -209
- package/src/components/chatrooms/reaction-picker.tsx +38 -33
- package/src/components/connectors/connector-list.tsx +265 -127
- package/src/components/connectors/connector-sheet.tsx +217 -0
- package/src/components/gateways/gateway-sheet.tsx +567 -0
- package/src/components/home/home-view.tsx +128 -4
- package/src/components/input/chat-input.tsx +135 -86
- package/src/components/layout/app-layout.tsx +385 -194
- package/src/components/layout/mobile-header.tsx +26 -8
- package/src/components/memory/memory-browser.tsx +71 -6
- package/src/components/memory/memory-card.tsx +18 -0
- package/src/components/memory/memory-detail.tsx +58 -31
- package/src/components/memory/memory-sheet.tsx +32 -4
- package/src/components/plugins/plugin-list.tsx +15 -3
- package/src/components/plugins/plugin-sheet.tsx +118 -9
- package/src/components/projects/project-detail.tsx +189 -1
- package/src/components/providers/provider-list.tsx +158 -2
- package/src/components/providers/provider-sheet.tsx +81 -70
- package/src/components/shared/agent-picker-list.tsx +2 -2
- package/src/components/shared/bottom-sheet.tsx +31 -15
- package/src/components/shared/command-palette.tsx +111 -24
- package/src/components/shared/confirm-dialog.tsx +45 -30
- package/src/components/shared/model-combobox.tsx +90 -8
- package/src/components/shared/settings/plugin-manager.tsx +20 -4
- package/src/components/shared/settings/section-capability-policy.tsx +105 -0
- package/src/components/shared/settings/section-heartbeat.tsx +88 -6
- package/src/components/shared/settings/section-orchestrator.tsx +6 -3
- package/src/components/shared/settings/section-runtime-loop.tsx +5 -5
- package/src/components/shared/settings/section-secrets.tsx +6 -6
- package/src/components/shared/settings/section-user-preferences.tsx +1 -1
- package/src/components/shared/settings/section-voice.tsx +5 -1
- package/src/components/shared/settings/section-web-search.tsx +10 -2
- package/src/components/shared/settings/settings-page.tsx +248 -47
- package/src/components/tasks/approvals-panel.tsx +211 -18
- package/src/components/tasks/task-board.tsx +242 -46
- package/src/components/ui/dialog.tsx +2 -2
- package/src/components/usage/metrics-dashboard.tsx +74 -1
- package/src/components/wallets/wallet-approval-dialog.tsx +59 -54
- package/src/components/wallets/wallet-panel.tsx +17 -5
- package/src/components/webhooks/webhook-sheet.tsx +7 -7
- package/src/lib/auth.ts +17 -0
- package/src/lib/chat-streaming-state.test.ts +108 -0
- package/src/lib/chat-streaming-state.ts +108 -0
- package/src/lib/heartbeat-defaults.ts +48 -0
- package/src/lib/memory-presentation.ts +59 -0
- package/src/lib/openclaw-agent-id.test.ts +14 -0
- package/src/lib/openclaw-agent-id.ts +31 -0
- package/src/lib/provider-model-discovery-client.ts +29 -0
- package/src/lib/providers/index.ts +12 -5
- package/src/lib/runtime-loop.ts +105 -3
- package/src/lib/safe-storage.ts +6 -1
- package/src/lib/server/agent-assignment.test.ts +112 -0
- package/src/lib/server/agent-assignment.ts +169 -0
- package/src/lib/server/agent-runtime-config.test.ts +141 -0
- package/src/lib/server/agent-runtime-config.ts +277 -0
- package/src/lib/server/approval-connector-notify.test.ts +253 -0
- package/src/lib/server/approvals-auto-approve.test.ts +264 -0
- package/src/lib/server/approvals.ts +483 -75
- package/src/lib/server/autonomy-runtime.test.ts +341 -0
- package/src/lib/server/browser-state.test.ts +118 -0
- package/src/lib/server/browser-state.ts +123 -0
- package/src/lib/server/build-llm.test.ts +44 -0
- package/src/lib/server/build-llm.ts +11 -4
- package/src/lib/server/builtin-plugins.ts +34 -0
- package/src/lib/server/chat-execution-heartbeat.test.ts +40 -0
- package/src/lib/server/chat-execution-tool-events.test.ts +219 -0
- package/src/lib/server/chat-execution.ts +402 -125
- package/src/lib/server/chatroom-health.test.ts +26 -0
- package/src/lib/server/chatroom-health.ts +2 -3
- package/src/lib/server/chatroom-helpers.test.ts +74 -2
- package/src/lib/server/chatroom-helpers.ts +144 -11
- package/src/lib/server/chatroom-session-persistence.test.ts +87 -0
- package/src/lib/server/connectors/discord.ts +175 -11
- package/src/lib/server/connectors/doctor.test.ts +80 -0
- package/src/lib/server/connectors/doctor.ts +116 -0
- package/src/lib/server/connectors/manager.ts +994 -130
- package/src/lib/server/connectors/policy.test.ts +222 -0
- package/src/lib/server/connectors/policy.ts +452 -0
- package/src/lib/server/connectors/slack.ts +189 -10
- package/src/lib/server/connectors/telegram.ts +65 -15
- package/src/lib/server/connectors/thread-context.test.ts +44 -0
- package/src/lib/server/connectors/thread-context.ts +72 -0
- package/src/lib/server/connectors/types.ts +41 -11
- package/src/lib/server/daemon-state.ts +62 -3
- package/src/lib/server/data-dir.ts +13 -0
- package/src/lib/server/delegation-jobs.test.ts +140 -0
- package/src/lib/server/delegation-jobs.ts +248 -0
- package/src/lib/server/document-utils.test.ts +47 -0
- package/src/lib/server/document-utils.ts +397 -0
- package/src/lib/server/eval/agent-regression.test.ts +47 -0
- package/src/lib/server/eval/agent-regression.ts +1742 -0
- package/src/lib/server/eval/runner.ts +11 -1
- package/src/lib/server/eval/store.ts +2 -1
- package/src/lib/server/heartbeat-service.ts +23 -43
- package/src/lib/server/heartbeat-source.test.ts +22 -0
- package/src/lib/server/heartbeat-source.ts +7 -0
- package/src/lib/server/identity-continuity.test.ts +77 -0
- package/src/lib/server/identity-continuity.ts +127 -0
- package/src/lib/server/mailbox-utils.ts +347 -0
- package/src/lib/server/main-agent-loop.ts +31 -964
- package/src/lib/server/memory-db.ts +4 -6
- package/src/lib/server/memory-tiers.ts +40 -0
- package/src/lib/server/openclaw-agent-resolver.test.ts +70 -0
- package/src/lib/server/openclaw-agent-resolver.ts +128 -0
- package/src/lib/server/openclaw-exec-config.ts +6 -5
- package/src/lib/server/openclaw-gateway.ts +123 -36
- package/src/lib/server/openclaw-skills-normalize.test.ts +56 -0
- package/src/lib/server/openclaw-skills-normalize.ts +136 -0
- package/src/lib/server/openclaw-sync.ts +3 -2
- package/src/lib/server/orchestrator-lg.ts +18 -8
- package/src/lib/server/orchestrator.ts +5 -4
- package/src/lib/server/playwright-proxy.mjs +27 -3
- package/src/lib/server/plugins.test.ts +215 -0
- package/src/lib/server/plugins.ts +832 -69
- package/src/lib/server/provider-health.ts +33 -3
- package/src/lib/server/provider-model-discovery.ts +481 -0
- package/src/lib/server/queue.ts +4 -21
- package/src/lib/server/runtime-settings.test.ts +119 -0
- package/src/lib/server/runtime-settings.ts +12 -92
- package/src/lib/server/schedule-normalization.ts +187 -0
- package/src/lib/server/scheduler.ts +2 -0
- package/src/lib/server/session-archive-memory.test.ts +85 -0
- package/src/lib/server/session-archive-memory.ts +230 -0
- package/src/lib/server/session-mailbox.ts +8 -18
- package/src/lib/server/session-reset-policy.test.ts +99 -0
- package/src/lib/server/session-reset-policy.ts +311 -0
- package/src/lib/server/session-run-manager.ts +33 -80
- package/src/lib/server/session-tools/autonomy-tools.test.ts +128 -0
- package/src/lib/server/session-tools/calendar.ts +2 -12
- package/src/lib/server/session-tools/connector.ts +109 -8
- package/src/lib/server/session-tools/context.ts +14 -2
- package/src/lib/server/session-tools/crawl.ts +447 -0
- package/src/lib/server/session-tools/crud.ts +96 -34
- package/src/lib/server/session-tools/delegate-fallback.test.ts +219 -0
- package/src/lib/server/session-tools/delegate.ts +406 -20
- package/src/lib/server/session-tools/discovery-approvals.test.ts +170 -0
- package/src/lib/server/session-tools/discovery.ts +40 -12
- package/src/lib/server/session-tools/document.ts +283 -0
- package/src/lib/server/session-tools/email.ts +1 -3
- package/src/lib/server/session-tools/extract.ts +137 -0
- package/src/lib/server/session-tools/file-normalize.test.ts +98 -0
- package/src/lib/server/session-tools/file-send.test.ts +84 -1
- package/src/lib/server/session-tools/file.ts +243 -24
- package/src/lib/server/session-tools/http.ts +9 -3
- package/src/lib/server/session-tools/human-loop.ts +227 -0
- package/src/lib/server/session-tools/image-gen.ts +1 -3
- package/src/lib/server/session-tools/index.ts +87 -2
- package/src/lib/server/session-tools/mailbox.ts +276 -0
- package/src/lib/server/session-tools/manage-schedules.test.ts +137 -0
- package/src/lib/server/session-tools/memory.ts +35 -3
- package/src/lib/server/session-tools/monitor.ts +162 -12
- package/src/lib/server/session-tools/normalize-tool-args.ts +17 -14
- package/src/lib/server/session-tools/openclaw-nodes.test.ts +111 -0
- package/src/lib/server/session-tools/openclaw-nodes.ts +86 -20
- package/src/lib/server/session-tools/platform-normalize.test.ts +142 -0
- package/src/lib/server/session-tools/platform.ts +142 -4
- package/src/lib/server/session-tools/plugin-creator.ts +95 -25
- package/src/lib/server/session-tools/primitive-tools.test.ts +257 -0
- package/src/lib/server/session-tools/replicate.ts +1 -3
- package/src/lib/server/session-tools/sandbox.ts +51 -92
- package/src/lib/server/session-tools/schedule.ts +20 -10
- package/src/lib/server/session-tools/session-info.ts +58 -4
- package/src/lib/server/session-tools/session-tools-wiring.test.ts +54 -17
- package/src/lib/server/session-tools/shell.ts +2 -2
- package/src/lib/server/session-tools/subagent.ts +195 -27
- package/src/lib/server/session-tools/table.ts +587 -0
- package/src/lib/server/session-tools/wallet.ts +13 -10
- package/src/lib/server/session-tools/web-browser-config.test.ts +39 -0
- package/src/lib/server/session-tools/web.ts +947 -108
- package/src/lib/server/storage.ts +255 -10
- package/src/lib/server/stream-agent-chat.test.ts +61 -0
- package/src/lib/server/stream-agent-chat.ts +185 -25
- package/src/lib/server/structured-extract.test.ts +72 -0
- package/src/lib/server/structured-extract.ts +373 -0
- package/src/lib/server/task-mention.test.ts +16 -2
- package/src/lib/server/task-mention.ts +61 -11
- package/src/lib/server/tool-aliases.ts +80 -12
- package/src/lib/server/tool-capability-policy.ts +7 -1
- package/src/lib/server/tool-retry.ts +2 -0
- package/src/lib/server/watch-jobs.test.ts +173 -0
- package/src/lib/server/watch-jobs.ts +532 -0
- package/src/lib/server/ws-hub.ts +5 -3
- package/src/lib/setup-defaults.ts +352 -11
- package/src/lib/tool-definitions.ts +3 -4
- package/src/lib/validation/schemas.test.ts +26 -0
- package/src/lib/validation/schemas.ts +62 -1
- package/src/lib/ws-client.ts +14 -12
- package/src/proxy.ts +5 -5
- package/src/stores/use-app-store.ts +43 -7
- package/src/stores/use-chat-store.ts +31 -2
- package/src/stores/use-chatroom-store.ts +153 -26
- package/src/types/index.ts +470 -44
- package/src/app/api/chats/[id]/main-loop/route.ts +0 -94
- package/src/components/chat/new-chat-sheet.tsx +0 -253
- package/src/lib/server/main-session.ts +0 -17
- package/src/lib/server/session-run-manager.test.ts +0 -26
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
import assert from 'node:assert/strict'
|
|
2
|
+
import fs from 'node:fs'
|
|
3
|
+
import os from 'node:os'
|
|
4
|
+
import path from 'node:path'
|
|
5
|
+
import { spawnSync } from 'node:child_process'
|
|
6
|
+
import { describe, it } from 'node:test'
|
|
7
|
+
|
|
8
|
+
const repoRoot = path.resolve(path.dirname(new URL(import.meta.url).pathname), '../../../..')
|
|
9
|
+
|
|
10
|
+
function runWithTempDataDir(script: string) {
|
|
11
|
+
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'swarmclaw-discovery-approval-'))
|
|
12
|
+
try {
|
|
13
|
+
const result = spawnSync(process.execPath, ['--import', 'tsx', '--input-type=module', '--eval', script], {
|
|
14
|
+
cwd: repoRoot,
|
|
15
|
+
env: {
|
|
16
|
+
...process.env,
|
|
17
|
+
DATA_DIR: path.join(tempDir, 'data'),
|
|
18
|
+
WORKSPACE_DIR: path.join(tempDir, 'workspace'),
|
|
19
|
+
},
|
|
20
|
+
encoding: 'utf-8',
|
|
21
|
+
})
|
|
22
|
+
assert.equal(result.status, 0, result.stderr || result.stdout || 'subprocess failed')
|
|
23
|
+
const lines = (result.stdout || '')
|
|
24
|
+
.trim()
|
|
25
|
+
.split('\n')
|
|
26
|
+
.map((line) => line.trim())
|
|
27
|
+
.filter(Boolean)
|
|
28
|
+
const jsonLine = [...lines].reverse().find((line) => line.startsWith('{'))
|
|
29
|
+
return JSON.parse(jsonLine || '{}')
|
|
30
|
+
} finally {
|
|
31
|
+
fs.rmSync(tempDir, { recursive: true, force: true })
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
describe('discovery approval flows', () => {
|
|
36
|
+
it('request_tool_access creates a real approval and grants the tool when auto-approved', () => {
|
|
37
|
+
const output = runWithTempDataDir(`
|
|
38
|
+
const storageMod = await import('./src/lib/server/storage.ts')
|
|
39
|
+
const toolsMod = await import('./src/lib/server/session-tools/index.ts')
|
|
40
|
+
const storage = storageMod.default || storageMod
|
|
41
|
+
const toolsApi = toolsMod.default || toolsMod
|
|
42
|
+
|
|
43
|
+
storage.saveSettings({ approvalsEnabled: false })
|
|
44
|
+
|
|
45
|
+
const now = Date.now()
|
|
46
|
+
storage.saveSessions({
|
|
47
|
+
session_tools: {
|
|
48
|
+
id: 'session_tools',
|
|
49
|
+
name: 'Tool Access Test',
|
|
50
|
+
cwd: process.env.WORKSPACE_DIR,
|
|
51
|
+
user: 'tester',
|
|
52
|
+
provider: 'openai',
|
|
53
|
+
model: 'gpt-test',
|
|
54
|
+
claudeSessionId: null,
|
|
55
|
+
messages: [],
|
|
56
|
+
createdAt: now,
|
|
57
|
+
lastActiveAt: now,
|
|
58
|
+
sessionType: 'human',
|
|
59
|
+
agentId: 'default',
|
|
60
|
+
plugins: [],
|
|
61
|
+
},
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
const built = await toolsApi.buildSessionTools(process.env.WORKSPACE_DIR, [], {
|
|
65
|
+
sessionId: 'session_tools',
|
|
66
|
+
agentId: 'default',
|
|
67
|
+
platformAssignScope: 'self',
|
|
68
|
+
})
|
|
69
|
+
const tool = built.tools.find((entry) => entry.name === 'request_tool_access')
|
|
70
|
+
const raw = await tool.invoke({ toolId: 'shell', reason: 'Need terminal access.' })
|
|
71
|
+
const approvals = storage.loadApprovals()
|
|
72
|
+
const session = storage.loadSessions().session_tools
|
|
73
|
+
console.log(JSON.stringify({
|
|
74
|
+
raw,
|
|
75
|
+
approvalCount: Object.keys(approvals).length,
|
|
76
|
+
plugins: session.plugins || [],
|
|
77
|
+
}))
|
|
78
|
+
`)
|
|
79
|
+
|
|
80
|
+
assert.match(String(output.raw), /auto-approved|granted/i)
|
|
81
|
+
assert.equal(output.approvalCount, 1)
|
|
82
|
+
assert.equal(output.plugins.includes('shell'), true)
|
|
83
|
+
})
|
|
84
|
+
|
|
85
|
+
it('manage_capabilities request_access accepts query aliases for pluginId', () => {
|
|
86
|
+
const output = runWithTempDataDir(`
|
|
87
|
+
const storageMod = await import('./src/lib/server/storage.ts')
|
|
88
|
+
const toolsMod = await import('./src/lib/server/session-tools/index.ts')
|
|
89
|
+
const storage = storageMod.default || storageMod
|
|
90
|
+
const toolsApi = toolsMod.default || toolsMod
|
|
91
|
+
|
|
92
|
+
storage.saveSettings({ approvalsEnabled: false })
|
|
93
|
+
|
|
94
|
+
const now = Date.now()
|
|
95
|
+
storage.saveSessions({
|
|
96
|
+
session_caps: {
|
|
97
|
+
id: 'session_caps',
|
|
98
|
+
name: 'Capabilities Test',
|
|
99
|
+
cwd: process.env.WORKSPACE_DIR,
|
|
100
|
+
user: 'tester',
|
|
101
|
+
provider: 'openai',
|
|
102
|
+
model: 'gpt-test',
|
|
103
|
+
claudeSessionId: null,
|
|
104
|
+
messages: [],
|
|
105
|
+
createdAt: now,
|
|
106
|
+
lastActiveAt: now,
|
|
107
|
+
sessionType: 'human',
|
|
108
|
+
agentId: 'default',
|
|
109
|
+
plugins: [],
|
|
110
|
+
},
|
|
111
|
+
})
|
|
112
|
+
|
|
113
|
+
const built = await toolsApi.buildSessionTools(process.env.WORKSPACE_DIR, [], {
|
|
114
|
+
sessionId: 'session_caps',
|
|
115
|
+
agentId: 'default',
|
|
116
|
+
platformAssignScope: 'self',
|
|
117
|
+
})
|
|
118
|
+
const tool = built.tools.find((entry) => entry.name === 'manage_capabilities')
|
|
119
|
+
const raw = await tool.invoke({ action: 'request_access', query: 'shell', reason: 'Need terminal access.' })
|
|
120
|
+
const session = storage.loadSessions().session_caps
|
|
121
|
+
console.log(JSON.stringify({
|
|
122
|
+
raw,
|
|
123
|
+
plugins: session.plugins || [],
|
|
124
|
+
}))
|
|
125
|
+
`)
|
|
126
|
+
|
|
127
|
+
assert.match(String(output.raw), /auto-approved|granted/i)
|
|
128
|
+
assert.equal(output.plugins.includes('shell'), true)
|
|
129
|
+
})
|
|
130
|
+
|
|
131
|
+
it('granting manage_schedules does not surface the manage_platform umbrella tool', () => {
|
|
132
|
+
const output = runWithTempDataDir(`
|
|
133
|
+
const storageMod = await import('./src/lib/server/storage.ts')
|
|
134
|
+
const toolsMod = await import('./src/lib/server/session-tools/index.ts')
|
|
135
|
+
const storage = storageMod.default || storageMod
|
|
136
|
+
const toolsApi = toolsMod.default || toolsMod
|
|
137
|
+
|
|
138
|
+
const now = Date.now()
|
|
139
|
+
storage.saveSessions({
|
|
140
|
+
session_sched: {
|
|
141
|
+
id: 'session_sched',
|
|
142
|
+
name: 'Schedule Tool Isolation',
|
|
143
|
+
cwd: process.env.WORKSPACE_DIR,
|
|
144
|
+
user: 'tester',
|
|
145
|
+
provider: 'openai',
|
|
146
|
+
model: 'gpt-test',
|
|
147
|
+
claudeSessionId: null,
|
|
148
|
+
messages: [],
|
|
149
|
+
createdAt: now,
|
|
150
|
+
lastActiveAt: now,
|
|
151
|
+
sessionType: 'human',
|
|
152
|
+
agentId: 'default',
|
|
153
|
+
plugins: ['manage_schedules'],
|
|
154
|
+
},
|
|
155
|
+
})
|
|
156
|
+
|
|
157
|
+
const built = await toolsApi.buildSessionTools(process.env.WORKSPACE_DIR, ['manage_schedules'], {
|
|
158
|
+
sessionId: 'session_sched',
|
|
159
|
+
agentId: 'default',
|
|
160
|
+
platformAssignScope: 'self',
|
|
161
|
+
})
|
|
162
|
+
console.log(JSON.stringify({
|
|
163
|
+
toolNames: built.tools.map((entry) => entry.name).sort(),
|
|
164
|
+
}))
|
|
165
|
+
`)
|
|
166
|
+
|
|
167
|
+
assert.equal(output.toolNames.includes('manage_schedules'), true)
|
|
168
|
+
assert.equal(output.toolNames.includes('manage_platform'), false)
|
|
169
|
+
})
|
|
170
|
+
})
|
|
@@ -15,15 +15,24 @@ async function executeDiscoveryAction(args: Record<string, unknown>, bctx?: Tool
|
|
|
15
15
|
const normalized = normalizeToolInputArgs(args)
|
|
16
16
|
const action = normalized.action
|
|
17
17
|
const approved = normalized.approved
|
|
18
|
-
const
|
|
18
|
+
const explicitPluginId = typeof normalized.pluginId === 'string'
|
|
19
19
|
? normalized.pluginId.trim()
|
|
20
20
|
: typeof normalized.plugin_id === 'string'
|
|
21
21
|
? normalized.plugin_id.trim()
|
|
22
|
-
:
|
|
22
|
+
: typeof normalized.toolId === 'string'
|
|
23
|
+
? normalized.toolId.trim()
|
|
24
|
+
: typeof normalized.tool_id === 'string'
|
|
25
|
+
? normalized.tool_id.trim()
|
|
26
|
+
: typeof normalized.tool === 'string'
|
|
27
|
+
? normalized.tool.trim()
|
|
28
|
+
: typeof normalized.name === 'string'
|
|
29
|
+
? normalized.name.trim()
|
|
30
|
+
: undefined
|
|
23
31
|
const url = typeof normalized.url === 'string' ? normalized.url.trim() : undefined
|
|
24
32
|
const reason = normalized.reason as string | undefined
|
|
25
33
|
const manager = getPluginManager()
|
|
26
34
|
const q = typeof normalized.query === 'string' ? normalized.query : ''
|
|
35
|
+
const pluginId = explicitPluginId || (action === 'request_access' ? q.trim() : '')
|
|
27
36
|
|
|
28
37
|
console.log('[discovery] Executing action:', action, { query: q, pluginId })
|
|
29
38
|
|
|
@@ -88,7 +97,8 @@ async function executeDiscoveryAction(args: Record<string, unknown>, bctx?: Tool
|
|
|
88
97
|
if (bctx?.ctx?.sessionId) {
|
|
89
98
|
const allSessions = loadSessions()
|
|
90
99
|
const currentSession = allSessions[bctx.ctx.sessionId]
|
|
91
|
-
|
|
100
|
+
const grantedTools = currentSession?.plugins || currentSession?.tools || []
|
|
101
|
+
if (currentSession && pluginIdMatches(grantedTools, pluginId)) {
|
|
92
102
|
return JSON.stringify({
|
|
93
103
|
alreadyGranted: true,
|
|
94
104
|
pluginId,
|
|
@@ -96,8 +106,8 @@ async function executeDiscoveryAction(args: Record<string, unknown>, bctx?: Tool
|
|
|
96
106
|
})
|
|
97
107
|
}
|
|
98
108
|
}
|
|
99
|
-
const {
|
|
100
|
-
|
|
109
|
+
const { requestApprovalMaybeAutoApprove } = await import('../approvals')
|
|
110
|
+
const approval = await requestApprovalMaybeAutoApprove({
|
|
101
111
|
category: 'tool_access',
|
|
102
112
|
title: `Enable Plugin: ${pluginId}`,
|
|
103
113
|
description: reason || `Agent is requesting access to the "${pluginId}" plugin.`,
|
|
@@ -105,6 +115,15 @@ async function executeDiscoveryAction(args: Record<string, unknown>, bctx?: Tool
|
|
|
105
115
|
agentId: bctx?.ctx?.agentId,
|
|
106
116
|
sessionId: bctx?.ctx?.sessionId,
|
|
107
117
|
})
|
|
118
|
+
if (approval.status === 'approved') {
|
|
119
|
+
return JSON.stringify({
|
|
120
|
+
alreadyGranted: true,
|
|
121
|
+
pluginId,
|
|
122
|
+
toolId: pluginId,
|
|
123
|
+
autoApproved: true,
|
|
124
|
+
message: `Access to "${pluginId}" was auto-approved and granted. Proceed to use it directly.`,
|
|
125
|
+
})
|
|
126
|
+
}
|
|
108
127
|
return JSON.stringify({
|
|
109
128
|
type: 'plugin_request',
|
|
110
129
|
pluginId,
|
|
@@ -118,8 +137,8 @@ async function executeDiscoveryAction(args: Record<string, unknown>, bctx?: Tool
|
|
|
118
137
|
return JSON.stringify({ error: 'url is required for install_request.' })
|
|
119
138
|
}
|
|
120
139
|
if (approved !== true) {
|
|
121
|
-
const {
|
|
122
|
-
|
|
140
|
+
const { requestApprovalMaybeAutoApprove } = await import('../approvals')
|
|
141
|
+
const approval = await requestApprovalMaybeAutoApprove({
|
|
123
142
|
category: 'plugin_install',
|
|
124
143
|
title: `Install Plugin${pluginId ? `: ${pluginId}` : ' from URL'}`,
|
|
125
144
|
description: reason || `Agent wants to install a plugin${url ? ` from ${url}` : ''}.`,
|
|
@@ -127,6 +146,15 @@ async function executeDiscoveryAction(args: Record<string, unknown>, bctx?: Tool
|
|
|
127
146
|
agentId: bctx?.ctx?.agentId,
|
|
128
147
|
sessionId: bctx?.ctx?.sessionId,
|
|
129
148
|
})
|
|
149
|
+
if (approval.status === 'approved') {
|
|
150
|
+
return JSON.stringify({
|
|
151
|
+
type: 'plugin_install_request',
|
|
152
|
+
url,
|
|
153
|
+
pluginId,
|
|
154
|
+
autoApproved: true,
|
|
155
|
+
message: `Plugin install from ${url} was auto-approved and has been applied.`,
|
|
156
|
+
})
|
|
157
|
+
}
|
|
130
158
|
return JSON.stringify({
|
|
131
159
|
type: 'plugin_install_request',
|
|
132
160
|
url,
|
|
@@ -158,13 +186,13 @@ const DiscoveryPlugin: Plugin = {
|
|
|
158
186
|
tools: [
|
|
159
187
|
{
|
|
160
188
|
name: 'manage_capabilities',
|
|
161
|
-
description: '
|
|
189
|
+
description: 'Discover currently available tools, search marketplaces, or request access to a direct tool/plugin name with action="request_access" (for example "shell", "manage_schedules", or "delegate").',
|
|
162
190
|
parameters: {
|
|
163
191
|
type: 'object',
|
|
164
192
|
properties: {
|
|
165
193
|
action: { type: 'string', enum: ['discover', 'search_marketplace', 'request_access', 'install_request'] },
|
|
166
|
-
query: { type: 'string', description: 'Search term for marketplace' },
|
|
167
|
-
pluginId: { type: 'string', description: 'The
|
|
194
|
+
query: { type: 'string', description: 'Search term for marketplace, or the direct tool/plugin name for request_access' },
|
|
195
|
+
pluginId: { type: 'string', description: 'The exact tool/plugin name to request, such as "shell" or "manage_schedules"' },
|
|
168
196
|
url: { type: 'string', description: 'URL for new plugin install request' },
|
|
169
197
|
reason: { type: 'string', description: 'Why you need this capability' }
|
|
170
198
|
},
|
|
@@ -187,8 +215,8 @@ export function buildDiscoveryTools(bctx: ToolBuildContext): StructuredToolInter
|
|
|
187
215
|
description: DiscoveryPlugin.tools![0].description,
|
|
188
216
|
schema: z.object({
|
|
189
217
|
action: z.enum(['discover', 'search_marketplace', 'request_access', 'install_request']).describe('The discovery action to perform'),
|
|
190
|
-
query: z.string().optional().describe('The
|
|
191
|
-
pluginId: z.string().optional(),
|
|
218
|
+
query: z.string().optional().describe('The marketplace query, or the direct tool/plugin name to request access to'),
|
|
219
|
+
pluginId: z.string().optional().describe('The exact tool/plugin name to request, such as "shell" or "manage_schedules"'),
|
|
192
220
|
url: z.string().optional(),
|
|
193
221
|
reason: z.string().describe('Why you need to perform this discovery action')
|
|
194
222
|
})
|
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
import path from 'path'
|
|
2
|
+
import { z } from 'zod'
|
|
3
|
+
import { tool, type StructuredToolInterface } from '@langchain/core/tools'
|
|
4
|
+
import { genId } from '@/lib/id'
|
|
5
|
+
import type { DocumentEntry, Plugin, PluginHooks } from '@/types'
|
|
6
|
+
import { getPluginManager } from '../plugins'
|
|
7
|
+
import { loadDocuments, saveDocuments } from '../storage'
|
|
8
|
+
import { extractDocumentArtifact } from '../document-utils'
|
|
9
|
+
import type { ToolBuildContext } from './context'
|
|
10
|
+
import { findBinaryOnPath, safePath } from './context'
|
|
11
|
+
import { normalizeToolInputArgs } from './normalize-tool-args'
|
|
12
|
+
|
|
13
|
+
function parseMetadataInput(value: unknown): Record<string, unknown> {
|
|
14
|
+
if (!value) return {}
|
|
15
|
+
if (typeof value === 'object' && !Array.isArray(value)) return value as Record<string, unknown>
|
|
16
|
+
if (typeof value === 'string' && value.trim()) {
|
|
17
|
+
try {
|
|
18
|
+
const parsed = JSON.parse(value)
|
|
19
|
+
if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) return parsed as Record<string, unknown>
|
|
20
|
+
} catch {
|
|
21
|
+
return {}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
return {}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function resolveFilePath(cwd: string, value: unknown): string {
|
|
28
|
+
if (typeof value !== 'string' || !value.trim()) throw new Error('filePath is required.')
|
|
29
|
+
return path.isAbsolute(value) ? path.resolve(value) : safePath(cwd, value)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function previewTables(tables: Awaited<ReturnType<typeof extractDocumentArtifact>>['tables']) {
|
|
33
|
+
return tables.map((table) => ({
|
|
34
|
+
name: table.name,
|
|
35
|
+
headers: table.headers,
|
|
36
|
+
rowCount: table.rowCount,
|
|
37
|
+
rows: table.rows.slice(0, 20),
|
|
38
|
+
truncated: table.rowCount > 20,
|
|
39
|
+
}))
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function searchStoredDocuments(documents: Record<string, DocumentEntry>, query: string, limit: number) {
|
|
43
|
+
const terms = query.toLowerCase().split(/\s+/).filter(Boolean)
|
|
44
|
+
return Object.values(documents)
|
|
45
|
+
.map((doc) => {
|
|
46
|
+
const hay = `${doc.title}\n${doc.fileName}\n${doc.content}`.toLowerCase()
|
|
47
|
+
if (!terms.every((term) => hay.includes(term))) return null
|
|
48
|
+
let score = hay.includes(query.toLowerCase()) ? 10 : 0
|
|
49
|
+
for (const term of terms) {
|
|
50
|
+
let at = hay.indexOf(term)
|
|
51
|
+
while (at !== -1) {
|
|
52
|
+
score += 1
|
|
53
|
+
at = hay.indexOf(term, at + term.length)
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
const firstTerm = terms[0] || query
|
|
57
|
+
const at = hay.indexOf(firstTerm.toLowerCase())
|
|
58
|
+
const start = at >= 0 ? Math.max(0, at - 120) : 0
|
|
59
|
+
const end = Math.min(doc.content.length, start + 360)
|
|
60
|
+
return {
|
|
61
|
+
id: doc.id,
|
|
62
|
+
title: doc.title,
|
|
63
|
+
fileName: doc.fileName,
|
|
64
|
+
score,
|
|
65
|
+
snippet: doc.content.slice(start, end).replace(/\s+/g, ' ').trim(),
|
|
66
|
+
updatedAt: doc.updatedAt,
|
|
67
|
+
}
|
|
68
|
+
})
|
|
69
|
+
.filter((entry): entry is NonNullable<typeof entry> => !!entry)
|
|
70
|
+
.sort((a, b) => b.score - a.score)
|
|
71
|
+
.slice(0, limit)
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
async function executeDocumentAction(
|
|
75
|
+
args: Record<string, unknown>,
|
|
76
|
+
bctx: { cwd: string; sessionId?: string | null; agentId?: string | null },
|
|
77
|
+
) {
|
|
78
|
+
const normalized = normalizeToolInputArgs(args)
|
|
79
|
+
const action = String(normalized.action || 'status').trim().toLowerCase()
|
|
80
|
+
|
|
81
|
+
try {
|
|
82
|
+
if (action === 'status') {
|
|
83
|
+
return JSON.stringify({
|
|
84
|
+
pdftotext: findBinaryOnPath('pdftotext') || null,
|
|
85
|
+
textutil: findBinaryOnPath('textutil') || null,
|
|
86
|
+
tesseract: findBinaryOnPath('tesseract') || null,
|
|
87
|
+
supports: ['read', 'metadata', 'ocr', 'extract_tables', 'store', 'list', 'search', 'get', 'delete'],
|
|
88
|
+
})
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if (action === 'list' || action === 'list_stored') {
|
|
92
|
+
const documents = loadDocuments() as Record<string, DocumentEntry>
|
|
93
|
+
const limit = typeof normalized.limit === 'number' ? Math.max(1, Math.min(normalized.limit, 200)) : 50
|
|
94
|
+
return JSON.stringify(
|
|
95
|
+
Object.values(documents)
|
|
96
|
+
.sort((a, b) => (b.updatedAt || 0) - (a.updatedAt || 0))
|
|
97
|
+
.slice(0, limit)
|
|
98
|
+
.map((doc) => ({
|
|
99
|
+
id: doc.id,
|
|
100
|
+
title: doc.title,
|
|
101
|
+
fileName: doc.fileName,
|
|
102
|
+
sourcePath: doc.sourcePath,
|
|
103
|
+
textLength: doc.textLength,
|
|
104
|
+
method: doc.method,
|
|
105
|
+
metadata: doc.metadata || {},
|
|
106
|
+
createdAt: doc.createdAt,
|
|
107
|
+
updatedAt: doc.updatedAt,
|
|
108
|
+
})),
|
|
109
|
+
)
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
if (action === 'search' || action === 'search_stored') {
|
|
113
|
+
const query = typeof normalized.query === 'string' ? normalized.query.trim() : ''
|
|
114
|
+
if (!query) return 'Error: query is required.'
|
|
115
|
+
const documents = loadDocuments() as Record<string, DocumentEntry>
|
|
116
|
+
const limit = typeof normalized.limit === 'number' ? Math.max(1, Math.min(normalized.limit, 50)) : 10
|
|
117
|
+
const matches = searchStoredDocuments(documents, query, limit)
|
|
118
|
+
return JSON.stringify({ query, total: matches.length, matches })
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
if (action === 'get' || action === 'get_stored') {
|
|
122
|
+
const id = typeof normalized.id === 'string' ? normalized.id.trim() : ''
|
|
123
|
+
if (!id) return 'Error: id is required.'
|
|
124
|
+
const documents = loadDocuments() as Record<string, DocumentEntry>
|
|
125
|
+
const doc = documents[id]
|
|
126
|
+
if (!doc) return `Error: document "${id}" not found.`
|
|
127
|
+
return JSON.stringify({
|
|
128
|
+
...doc,
|
|
129
|
+
content: doc.content.length > 80_000 ? `${doc.content.slice(0, 80_000)}\n... [truncated]` : doc.content,
|
|
130
|
+
})
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
if (action === 'delete' || action === 'delete_stored') {
|
|
134
|
+
const id = typeof normalized.id === 'string' ? normalized.id.trim() : ''
|
|
135
|
+
if (!id) return 'Error: id is required.'
|
|
136
|
+
const documents = loadDocuments() as Record<string, DocumentEntry>
|
|
137
|
+
if (!documents[id]) return `Error: document "${id}" not found.`
|
|
138
|
+
delete documents[id]
|
|
139
|
+
saveDocuments(documents)
|
|
140
|
+
return JSON.stringify({ ok: true, id })
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
const filePath = resolveFilePath(bctx.cwd, normalized.filePath ?? normalized.path)
|
|
144
|
+
const artifact = await extractDocumentArtifact(filePath, {
|
|
145
|
+
preferOcr: action === 'ocr' || normalized.preferOcr === true,
|
|
146
|
+
maxChars: typeof normalized.maxChars === 'number' ? Math.max(5_000, normalized.maxChars) : undefined,
|
|
147
|
+
})
|
|
148
|
+
|
|
149
|
+
if (action === 'metadata') {
|
|
150
|
+
return JSON.stringify({
|
|
151
|
+
filePath: artifact.filePath,
|
|
152
|
+
fileName: artifact.fileName,
|
|
153
|
+
ext: artifact.ext,
|
|
154
|
+
method: artifact.method,
|
|
155
|
+
metadata: artifact.metadata,
|
|
156
|
+
textLength: artifact.text.length,
|
|
157
|
+
tableCount: artifact.tables.length,
|
|
158
|
+
})
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
if (action === 'extract_tables') {
|
|
162
|
+
return JSON.stringify({
|
|
163
|
+
filePath: artifact.filePath,
|
|
164
|
+
fileName: artifact.fileName,
|
|
165
|
+
tableCount: artifact.tables.length,
|
|
166
|
+
tables: previewTables(artifact.tables),
|
|
167
|
+
})
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
if (action === 'store') {
|
|
171
|
+
if (!artifact.text.trim()) return 'Error: extracted document text is empty.'
|
|
172
|
+
const documents = loadDocuments() as Record<string, DocumentEntry>
|
|
173
|
+
const now = Date.now()
|
|
174
|
+
const docId = genId(8)
|
|
175
|
+
const entry: DocumentEntry = {
|
|
176
|
+
id: docId,
|
|
177
|
+
title: typeof normalized.title === 'string' && normalized.title.trim() ? normalized.title.trim() : artifact.fileName,
|
|
178
|
+
fileName: artifact.fileName,
|
|
179
|
+
sourcePath: artifact.filePath,
|
|
180
|
+
content: artifact.text,
|
|
181
|
+
method: artifact.method,
|
|
182
|
+
textLength: artifact.text.length,
|
|
183
|
+
metadata: {
|
|
184
|
+
...artifact.metadata,
|
|
185
|
+
...parseMetadataInput(normalized.metadata),
|
|
186
|
+
ext: artifact.ext,
|
|
187
|
+
tableCount: artifact.tables.length,
|
|
188
|
+
storedByAgentId: bctx.agentId || null,
|
|
189
|
+
storedInSessionId: bctx.sessionId || null,
|
|
190
|
+
},
|
|
191
|
+
createdAt: now,
|
|
192
|
+
updatedAt: now,
|
|
193
|
+
}
|
|
194
|
+
documents[entry.id] = entry
|
|
195
|
+
saveDocuments(documents)
|
|
196
|
+
return JSON.stringify({
|
|
197
|
+
id: entry.id,
|
|
198
|
+
title: entry.title,
|
|
199
|
+
fileName: entry.fileName,
|
|
200
|
+
textLength: entry.textLength,
|
|
201
|
+
method: entry.method,
|
|
202
|
+
metadata: entry.metadata,
|
|
203
|
+
})
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
if (action === 'read' || action === 'ocr') {
|
|
207
|
+
return JSON.stringify({
|
|
208
|
+
filePath: artifact.filePath,
|
|
209
|
+
fileName: artifact.fileName,
|
|
210
|
+
ext: artifact.ext,
|
|
211
|
+
method: artifact.method,
|
|
212
|
+
text: artifact.text,
|
|
213
|
+
textLength: artifact.text.length,
|
|
214
|
+
metadata: artifact.metadata,
|
|
215
|
+
tableCount: artifact.tables.length,
|
|
216
|
+
tables: previewTables(artifact.tables),
|
|
217
|
+
})
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
return `Error: Unknown action "${action}".`
|
|
221
|
+
} catch (err: unknown) {
|
|
222
|
+
return `Error: ${err instanceof Error ? err.message : String(err)}`
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
const DocumentPlugin: Plugin = {
|
|
227
|
+
name: 'Document',
|
|
228
|
+
enabledByDefault: false,
|
|
229
|
+
description: 'Extract text/tables/OCR from local documents and optionally store them for later retrieval.',
|
|
230
|
+
hooks: {
|
|
231
|
+
getCapabilityDescription: () =>
|
|
232
|
+
'I can parse local documents with `document`, including PDFs, office docs, OCR-able images, CSV/XLSX tables, and stored document search.',
|
|
233
|
+
} as PluginHooks,
|
|
234
|
+
tools: [
|
|
235
|
+
{
|
|
236
|
+
name: 'document',
|
|
237
|
+
description: 'Document parsing tool. Actions: status, read, metadata, ocr, extract_tables, store, list, list_stored, search, search_stored, get, get_stored, delete, delete_stored.',
|
|
238
|
+
parameters: {
|
|
239
|
+
type: 'object',
|
|
240
|
+
properties: {
|
|
241
|
+
action: {
|
|
242
|
+
type: 'string',
|
|
243
|
+
enum: ['status', 'read', 'metadata', 'ocr', 'extract_tables', 'store', 'list', 'list_stored', 'search', 'search_stored', 'get', 'get_stored', 'delete', 'delete_stored'],
|
|
244
|
+
},
|
|
245
|
+
filePath: { type: 'string' },
|
|
246
|
+
id: { type: 'string' },
|
|
247
|
+
title: { type: 'string' },
|
|
248
|
+
query: { type: 'string' },
|
|
249
|
+
metadata: {},
|
|
250
|
+
limit: { type: 'number' },
|
|
251
|
+
maxChars: { type: 'number' },
|
|
252
|
+
preferOcr: { type: 'boolean' },
|
|
253
|
+
},
|
|
254
|
+
required: ['action'],
|
|
255
|
+
},
|
|
256
|
+
execute: async (args, context) => executeDocumentAction(args, {
|
|
257
|
+
cwd: context.session.cwd || process.cwd(),
|
|
258
|
+
sessionId: context.session.id,
|
|
259
|
+
agentId: context.session.agentId || null,
|
|
260
|
+
}),
|
|
261
|
+
},
|
|
262
|
+
],
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
getPluginManager().registerBuiltin('document', DocumentPlugin)
|
|
266
|
+
|
|
267
|
+
export function buildDocumentTools(bctx: ToolBuildContext): StructuredToolInterface[] {
|
|
268
|
+
if (!bctx.hasPlugin('document')) return []
|
|
269
|
+
return [
|
|
270
|
+
tool(
|
|
271
|
+
async (args) => executeDocumentAction(args, {
|
|
272
|
+
cwd: bctx.cwd,
|
|
273
|
+
sessionId: bctx.ctx?.sessionId || null,
|
|
274
|
+
agentId: bctx.ctx?.agentId || null,
|
|
275
|
+
}),
|
|
276
|
+
{
|
|
277
|
+
name: 'document',
|
|
278
|
+
description: DocumentPlugin.tools![0].description,
|
|
279
|
+
schema: z.object({}).passthrough(),
|
|
280
|
+
},
|
|
281
|
+
),
|
|
282
|
+
]
|
|
283
|
+
}
|
|
@@ -3,7 +3,6 @@ import { tool, type StructuredToolInterface } from '@langchain/core/tools'
|
|
|
3
3
|
import type { Plugin, PluginHooks } from '@/types'
|
|
4
4
|
import { getPluginManager } from '../plugins'
|
|
5
5
|
import { normalizeToolInputArgs } from './normalize-tool-args'
|
|
6
|
-
import { loadSettings } from '../storage'
|
|
7
6
|
import type { ToolBuildContext } from './context'
|
|
8
7
|
|
|
9
8
|
interface SmtpConfig {
|
|
@@ -17,8 +16,7 @@ interface SmtpConfig {
|
|
|
17
16
|
}
|
|
18
17
|
|
|
19
18
|
function getSmtpConfig(): SmtpConfig {
|
|
20
|
-
const
|
|
21
|
-
const ps = (settings.pluginSettings as Record<string, Record<string, unknown>> | undefined)?.email ?? {}
|
|
19
|
+
const ps = getPluginManager().getPluginSettings('email')
|
|
22
20
|
return {
|
|
23
21
|
host: (ps.host as string) || '',
|
|
24
22
|
port: Number(ps.port) || 587,
|