@swarmclawai/swarmclaw 0.7.7 → 0.8.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/README.md +12 -14
- package/next.config.ts +13 -2
- package/package.json +4 -2
- package/src/app/api/agents/[id]/thread/route.ts +9 -0
- package/src/app/api/agents/route.ts +4 -0
- package/src/app/api/agents/thread-route.test.ts +133 -0
- package/src/app/api/approvals/route.test.ts +148 -0
- package/src/app/api/canvas/[sessionId]/route.ts +3 -1
- package/src/app/api/chatrooms/[id]/chat/route.ts +4 -2
- package/src/app/api/chats/[id]/devserver/route.ts +48 -7
- package/src/app/api/chats/[id]/messages/route.ts +42 -18
- package/src/app/api/chats/[id]/route.ts +1 -1
- package/src/app/api/chats/[id]/stop/route.ts +5 -4
- package/src/app/api/chats/route.ts +23 -2
- package/src/app/api/clawhub/install/route.ts +28 -8
- package/src/app/api/connectors/[id]/route.ts +46 -3
- package/src/app/api/connectors/route.ts +12 -8
- package/src/app/api/external-agents/route.test.ts +165 -0
- package/src/app/api/gateways/[id]/health/route.ts +27 -12
- package/src/app/api/gateways/[id]/route.ts +2 -0
- package/src/app/api/gateways/health-route.test.ts +135 -0
- package/src/app/api/gateways/route.ts +2 -0
- package/src/app/api/mcp-servers/route.test.ts +130 -0
- package/src/app/api/openclaw/deploy/route.ts +38 -5
- package/src/app/api/plugins/install/route.ts +46 -6
- package/src/app/api/plugins/marketplace/route.ts +48 -15
- package/src/app/api/preview-server/route.ts +26 -11
- package/src/app/api/projects/[id]/route.ts +6 -2
- package/src/app/api/projects/route.ts +4 -3
- package/src/app/api/schedules/[id]/run/route.ts +4 -0
- package/src/app/api/schedules/route.test.ts +86 -0
- package/src/app/api/schedules/route.ts +6 -1
- package/src/app/api/secrets/[id]/route.ts +1 -0
- package/src/app/api/secrets/route.ts +2 -1
- package/src/app/api/settings/route.ts +2 -0
- package/src/app/api/setup/check-provider/route.test.ts +19 -0
- package/src/app/api/setup/check-provider/route.ts +40 -10
- package/src/app/api/skills/[id]/route.ts +12 -0
- package/src/app/api/skills/import/route.ts +14 -12
- package/src/app/api/skills/route.ts +13 -1
- package/src/app/api/tasks/[id]/route.ts +10 -1
- package/src/app/api/tasks/import/github/route.test.ts +65 -0
- package/src/app/api/tasks/import/github/route.ts +337 -0
- package/src/app/api/wallets/[id]/approve/route.ts +17 -3
- package/src/app/api/wallets/[id]/route.ts +79 -33
- package/src/app/api/wallets/[id]/send/route.ts +19 -33
- package/src/app/api/wallets/route.ts +78 -61
- package/src/app/api/webhooks/[id]/route.ts +33 -6
- package/src/app/api/webhooks/route.test.ts +272 -0
- package/src/cli/index.js +1 -0
- package/src/cli/spec.js +1 -0
- package/src/components/agents/agent-card.tsx +9 -2
- package/src/components/agents/agent-chat-list.tsx +18 -2
- package/src/components/agents/agent-list.tsx +1 -0
- package/src/components/agents/agent-sheet.tsx +257 -38
- package/src/components/agents/inspector-panel.tsx +41 -0
- package/src/components/canvas/canvas-panel.tsx +236 -65
- package/src/components/chat/chat-area.tsx +36 -19
- package/src/components/chat/chat-card.tsx +36 -13
- package/src/components/chat/chat-header.tsx +48 -16
- package/src/components/chat/chat-list.tsx +28 -4
- package/src/components/chat/checkpoint-timeline.tsx +50 -34
- package/src/components/chat/delegation-banner.test.ts +14 -1
- package/src/components/chat/delegation-banner.tsx +1 -1
- package/src/components/chat/message-bubble.tsx +208 -145
- package/src/components/chat/message-list.tsx +48 -19
- package/src/components/chatrooms/chatroom-message.tsx +2 -2
- package/src/components/chatrooms/chatroom-sheet.tsx +16 -2
- package/src/components/connectors/connector-health.tsx +1 -1
- package/src/components/connectors/connector-list.tsx +7 -2
- package/src/components/connectors/connector-sheet.tsx +337 -148
- package/src/components/gateways/gateway-sheet.tsx +2 -2
- package/src/components/layout/app-layout.tsx +40 -23
- package/src/components/mcp-servers/mcp-server-list.tsx +26 -5
- package/src/components/mcp-servers/mcp-server-sheet.tsx +19 -2
- package/src/components/openclaw/openclaw-deploy-panel.tsx +269 -21
- package/src/components/plugins/plugin-list.tsx +45 -9
- package/src/components/plugins/plugin-sheet.tsx +55 -7
- package/src/components/projects/project-detail.tsx +217 -0
- package/src/components/projects/project-sheet.tsx +176 -4
- package/src/components/providers/provider-list.tsx +2 -1
- package/src/components/providers/provider-sheet.tsx +21 -2
- package/src/components/schedules/schedule-card.tsx +25 -1
- package/src/components/schedules/schedule-sheet.tsx +44 -2
- package/src/components/secrets/secret-sheet.tsx +21 -2
- package/src/components/shared/agent-switch-dialog.tsx +12 -1
- package/src/components/shared/bottom-sheet.tsx +13 -3
- package/src/components/shared/command-palette.tsx +8 -1
- package/src/components/shared/confirm-dialog.tsx +19 -4
- package/src/components/shared/connector-platform-icon.test.ts +28 -0
- package/src/components/shared/connector-platform-icon.tsx +39 -6
- package/src/components/shared/settings/plugin-manager.tsx +29 -6
- package/src/components/shared/settings/section-capability-policy.tsx +45 -3
- package/src/components/shared/settings/section-voice.tsx +11 -3
- package/src/components/skills/skill-list.tsx +25 -0
- package/src/components/skills/skill-sheet.tsx +84 -12
- package/src/components/tasks/approvals-panel.tsx +289 -34
- package/src/components/tasks/task-board.tsx +410 -25
- package/src/components/tasks/task-card.tsx +66 -8
- package/src/components/tasks/task-sheet.tsx +16 -4
- package/src/components/ui/dialog.tsx +2 -2
- package/src/components/wallets/wallet-approval-dialog.tsx +4 -2
- package/src/components/wallets/wallet-panel.tsx +435 -90
- package/src/components/wallets/wallet-section.tsx +198 -48
- package/src/components/webhooks/webhook-sheet.tsx +22 -2
- package/src/lib/approval-display.ts +20 -0
- package/src/lib/canvas-content.ts +198 -0
- package/src/lib/chat-artifact-summary.ts +165 -0
- package/src/lib/chat-display.test.ts +91 -0
- package/src/lib/chat-display.ts +58 -0
- package/src/lib/chat-streaming-state.test.ts +47 -1
- package/src/lib/chat-streaming-state.ts +42 -0
- package/src/lib/ollama-model.ts +10 -0
- package/src/lib/openclaw-endpoint.test.ts +8 -0
- package/src/lib/openclaw-endpoint.ts +6 -1
- package/src/lib/plugin-install-cors.ts +46 -0
- package/src/lib/plugin-sources.test.ts +43 -0
- package/src/lib/plugin-sources.ts +77 -0
- package/src/lib/providers/ollama.ts +16 -6
- package/src/lib/providers/openclaw.test.ts +54 -0
- package/src/lib/providers/openclaw.ts +127 -11
- package/src/lib/schedule-dedupe-advanced.test.ts +1335 -0
- package/src/lib/schedule-dedupe.test.ts +66 -1
- package/src/lib/schedule-dedupe.ts +169 -12
- package/src/lib/schedule-origin.test.ts +20 -0
- package/src/lib/schedule-origin.ts +15 -0
- package/src/lib/server/__fixtures__/fake-mcp-stdio-server.mjs +27 -0
- package/src/lib/server/agent-availability.ts +16 -0
- package/src/lib/server/agent-runtime-config.ts +12 -4
- package/src/lib/server/agent-thread-session.test.ts +51 -0
- package/src/lib/server/agent-thread-session.ts +7 -0
- package/src/lib/server/approval-match.ts +205 -0
- package/src/lib/server/approvals-auto-approve.test.ts +538 -1
- package/src/lib/server/approvals.ts +214 -1
- package/src/lib/server/assistant-control.test.ts +29 -0
- package/src/lib/server/assistant-control.ts +23 -0
- package/src/lib/server/build-llm.test.ts +79 -0
- package/src/lib/server/build-llm.ts +14 -4
- package/src/lib/server/canvas-content.test.ts +32 -0
- package/src/lib/server/canvas-content.ts +6 -0
- package/src/lib/server/capability-router.test.ts +33 -0
- package/src/lib/server/capability-router.ts +80 -19
- package/src/lib/server/chat-execution-advanced.test.ts +651 -0
- package/src/lib/server/chat-execution-disabled.test.ts +94 -0
- package/src/lib/server/chat-execution-tool-events.test.ts +157 -0
- package/src/lib/server/chat-execution.ts +378 -73
- package/src/lib/server/clawhub-client.test.ts +14 -8
- package/src/lib/server/connectors/manager-reconnect.test.ts +47 -0
- package/src/lib/server/connectors/manager.test.ts +1147 -0
- package/src/lib/server/connectors/manager.ts +461 -137
- package/src/lib/server/connectors/pairing.ts +26 -5
- package/src/lib/server/connectors/types.ts +2 -0
- package/src/lib/server/connectors/whatsapp.test.ts +134 -0
- package/src/lib/server/connectors/whatsapp.ts +271 -47
- package/src/lib/server/context-manager.ts +6 -1
- package/src/lib/server/daemon-state.ts +84 -47
- package/src/lib/server/data-dir.test.ts +37 -0
- package/src/lib/server/data-dir.ts +20 -1
- package/src/lib/server/delegation-jobs-advanced.test.ts +513 -0
- package/src/lib/server/devserver-launch.test.ts +60 -0
- package/src/lib/server/devserver-launch.ts +85 -0
- package/src/lib/server/elevenlabs.test.ts +247 -1
- package/src/lib/server/elevenlabs.ts +147 -43
- package/src/lib/server/ethereum.ts +590 -0
- package/src/lib/server/eval/agent-regression-advanced.test.ts +302 -0
- package/src/lib/server/eval/agent-regression.test.ts +18 -1
- package/src/lib/server/eval/agent-regression.ts +383 -11
- package/src/lib/server/evm-swap.ts +475 -0
- package/src/lib/server/execution-log.ts +1 -0
- package/src/lib/server/heartbeat-service-timer.test.ts +173 -0
- package/src/lib/server/heartbeat-service.ts +20 -11
- package/src/lib/server/heartbeat-wake.test.ts +112 -0
- package/src/lib/server/heartbeat-wake.ts +338 -57
- package/src/lib/server/main-agent-loop-advanced.test.ts +538 -0
- package/src/lib/server/main-agent-loop.test.ts +260 -0
- package/src/lib/server/main-agent-loop.ts +559 -14
- package/src/lib/server/mcp-client.test.ts +16 -0
- package/src/lib/server/mcp-client.ts +25 -0
- package/src/lib/server/memory-integration.test.ts +719 -0
- package/src/lib/server/memory-policy.test.ts +43 -0
- package/src/lib/server/memory-policy.ts +132 -0
- package/src/lib/server/memory-tiers.test.ts +60 -0
- package/src/lib/server/memory-tiers.ts +16 -0
- package/src/lib/server/ollama-runtime.ts +58 -0
- package/src/lib/server/openclaw-deploy.test.ts +109 -1
- package/src/lib/server/openclaw-deploy.ts +557 -81
- package/src/lib/server/openclaw-gateway.test.ts +131 -0
- package/src/lib/server/openclaw-gateway.ts +10 -4
- package/src/lib/server/openclaw-health.test.ts +35 -0
- package/src/lib/server/openclaw-health.ts +215 -47
- package/src/lib/server/orchestrator-lg.ts +3 -2
- package/src/lib/server/orchestrator.ts +2 -0
- package/src/lib/server/plugins-advanced.test.ts +351 -0
- package/src/lib/server/plugins.ts +211 -6
- package/src/lib/server/project-context.ts +162 -0
- package/src/lib/server/project-utils.ts +150 -0
- package/src/lib/server/queue-advanced.test.ts +528 -0
- package/src/lib/server/queue-followups.test.ts +409 -2
- package/src/lib/server/queue-reconcile.test.ts +128 -0
- package/src/lib/server/queue.ts +527 -68
- package/src/lib/server/scheduler.ts +29 -1
- package/src/lib/server/session-note.test.ts +36 -0
- package/src/lib/server/session-note.ts +42 -0
- package/src/lib/server/session-run-manager.ts +83 -4
- package/src/lib/server/session-tools/canvas.ts +14 -12
- package/src/lib/server/session-tools/connector-inputs.test.ts +37 -0
- package/src/lib/server/session-tools/connector.test.ts +138 -0
- package/src/lib/server/session-tools/connector.ts +366 -54
- package/src/lib/server/session-tools/context.ts +17 -3
- package/src/lib/server/session-tools/crud.ts +484 -84
- package/src/lib/server/session-tools/delegate-fallback.test.ts +103 -0
- package/src/lib/server/session-tools/delegate-resume.test.ts +50 -0
- package/src/lib/server/session-tools/delegate.ts +102 -10
- package/src/lib/server/session-tools/discovery-approvals.test.ts +142 -0
- package/src/lib/server/session-tools/discovery.ts +80 -12
- package/src/lib/server/session-tools/file-normalize.test.ts +36 -0
- package/src/lib/server/session-tools/file.ts +43 -4
- package/src/lib/server/session-tools/human-loop.ts +35 -5
- package/src/lib/server/session-tools/index.ts +44 -9
- package/src/lib/server/session-tools/manage-connectors.test.ts +139 -0
- package/src/lib/server/session-tools/manage-schedules-advanced.test.ts +564 -0
- package/src/lib/server/session-tools/manage-schedules.test.ts +283 -0
- package/src/lib/server/session-tools/manage-tasks-advanced.test.ts +852 -0
- package/src/lib/server/session-tools/manage-tasks.test.ts +114 -0
- package/src/lib/server/session-tools/memory.test.ts +93 -0
- package/src/lib/server/session-tools/memory.ts +554 -75
- package/src/lib/server/session-tools/normalize-tool-args.ts +1 -1
- package/src/lib/server/session-tools/platform-access.test.ts +58 -0
- package/src/lib/server/session-tools/platform.ts +60 -19
- package/src/lib/server/session-tools/plugin-creator.ts +57 -1
- package/src/lib/server/session-tools/primitive-tools.test.ts +6 -0
- package/src/lib/server/session-tools/schedule.ts +6 -1
- package/src/lib/server/session-tools/shell-normalize.test.ts +25 -1
- package/src/lib/server/session-tools/shell.ts +22 -3
- package/src/lib/server/session-tools/wallet-tool.test.ts +254 -0
- package/src/lib/server/session-tools/wallet.ts +1374 -139
- package/src/lib/server/session-tools/web-inputs.test.ts +178 -0
- package/src/lib/server/session-tools/web.ts +621 -70
- package/src/lib/server/skill-discovery.ts +128 -0
- package/src/lib/server/skill-eligibility.test.ts +84 -0
- package/src/lib/server/skill-eligibility.ts +95 -0
- package/src/lib/server/skill-prompt-budget.test.ts +102 -0
- package/src/lib/server/skill-prompt-budget.ts +125 -0
- package/src/lib/server/skills-normalize.test.ts +54 -0
- package/src/lib/server/skills-normalize.ts +372 -26
- package/src/lib/server/solana.ts +214 -29
- package/src/lib/server/storage.ts +65 -36
- package/src/lib/server/stream-agent-chat.test.ts +437 -2
- package/src/lib/server/stream-agent-chat.ts +957 -79
- package/src/lib/server/system-events.ts +1 -1
- package/src/lib/server/tool-aliases.ts +2 -0
- package/src/lib/server/tool-capability-policy-advanced.test.ts +502 -0
- package/src/lib/server/tool-capability-policy.test.ts +24 -0
- package/src/lib/server/tool-capability-policy.ts +29 -1
- package/src/lib/server/tool-loop-detection.test.ts +105 -0
- package/src/lib/server/tool-loop-detection.ts +260 -0
- package/src/lib/server/tool-planning.test.ts +44 -0
- package/src/lib/server/tool-planning.ts +271 -0
- package/src/lib/server/wallet-execution.test.ts +198 -0
- package/src/lib/server/wallet-portfolio.test.ts +98 -0
- package/src/lib/server/wallet-portfolio.ts +724 -0
- package/src/lib/server/wallet-service.test.ts +57 -0
- package/src/lib/server/wallet-service.ts +213 -0
- package/src/lib/server/watch-jobs-advanced.test.ts +594 -0
- package/src/lib/server/watch-jobs.ts +17 -2
- package/src/lib/server/workspace-context.ts +111 -0
- package/src/lib/skill-save-payload.test.ts +39 -0
- package/src/lib/skill-save-payload.ts +37 -0
- package/src/lib/tasks.ts +28 -0
- package/src/lib/tool-definitions.ts +2 -1
- package/src/lib/tool-event-summary.test.ts +30 -0
- package/src/lib/tool-event-summary.ts +37 -0
- package/src/lib/validation/schemas.ts +1 -0
- package/src/lib/wallet-transactions.test.ts +75 -0
- package/src/lib/wallet-transactions.ts +43 -0
- package/src/lib/wallet.test.ts +17 -0
- package/src/lib/wallet.ts +183 -0
- package/src/proxy.test.ts +31 -0
- package/src/proxy.ts +34 -2
- package/src/stores/use-chat-store.ts +15 -1
- package/src/types/index.ts +249 -14
|
@@ -33,6 +33,57 @@ function runWithTempDataDir(script: string) {
|
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
describe('approval auto-approve', () => {
|
|
36
|
+
it('defaults new installs to approvals auto-run', () => {
|
|
37
|
+
const output = runWithTempDataDir(`
|
|
38
|
+
const storageMod = await import('./src/lib/server/storage.ts')
|
|
39
|
+
const approvalsMod = await import('./src/lib/server/approvals.ts')
|
|
40
|
+
const storage = storageMod.default || storageMod
|
|
41
|
+
const approvals = approvalsMod.default || approvalsMod
|
|
42
|
+
|
|
43
|
+
const now = Date.now()
|
|
44
|
+
storage.saveSessions({
|
|
45
|
+
session_default: {
|
|
46
|
+
id: 'session_default',
|
|
47
|
+
name: 'Default Approval Policy Test',
|
|
48
|
+
cwd: process.cwd(),
|
|
49
|
+
user: 'tester',
|
|
50
|
+
provider: 'openai',
|
|
51
|
+
model: 'gpt-test',
|
|
52
|
+
credentialId: null,
|
|
53
|
+
apiEndpoint: null,
|
|
54
|
+
claudeSessionId: null,
|
|
55
|
+
codexThreadId: null,
|
|
56
|
+
opencodeSessionId: null,
|
|
57
|
+
delegateResumeIds: { claudeCode: null, codex: null, opencode: null, gemini: null },
|
|
58
|
+
messages: [],
|
|
59
|
+
createdAt: now,
|
|
60
|
+
lastActiveAt: now,
|
|
61
|
+
sessionType: 'human',
|
|
62
|
+
agentId: 'default',
|
|
63
|
+
plugins: [],
|
|
64
|
+
},
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
const settings = storage.loadSettings()
|
|
68
|
+
const approval = await approvals.requestApprovalMaybeAutoApprove({
|
|
69
|
+
category: 'tool_access',
|
|
70
|
+
title: 'Enable Plugin: shell',
|
|
71
|
+
description: 'Need shell access for a task.',
|
|
72
|
+
data: { toolId: 'shell', pluginId: 'shell' },
|
|
73
|
+
sessionId: 'session_default',
|
|
74
|
+
agentId: 'default',
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
console.log(JSON.stringify({
|
|
78
|
+
approvalsEnabled: settings.approvalsEnabled,
|
|
79
|
+
approvalStatus: approval.status,
|
|
80
|
+
}))
|
|
81
|
+
`)
|
|
82
|
+
|
|
83
|
+
assert.equal(output.approvalsEnabled, false)
|
|
84
|
+
assert.equal(output.approvalStatus, 'approved')
|
|
85
|
+
})
|
|
86
|
+
|
|
36
87
|
it('auto-approves tool access and plugin scaffolds when configured', () => {
|
|
37
88
|
const output = runWithTempDataDir(`
|
|
38
89
|
const storageMod = await import('./src/lib/server/storage.ts')
|
|
@@ -115,8 +166,34 @@ describe('approval auto-approve', () => {
|
|
|
115
166
|
const output = runWithTempDataDir(`
|
|
116
167
|
const storageMod = await import('./src/lib/server/storage.ts')
|
|
117
168
|
const approvalsMod = await import('./src/lib/server/approvals.ts')
|
|
169
|
+
const sessionRunsMod = await import('./src/lib/server/session-run-manager.ts')
|
|
118
170
|
const storage = storageMod.default || storageMod
|
|
119
171
|
const approvals = approvalsMod.default || approvalsMod
|
|
172
|
+
const sessionRuns = sessionRunsMod.default || sessionRunsMod
|
|
173
|
+
|
|
174
|
+
const now = Date.now()
|
|
175
|
+
storage.saveSessions({
|
|
176
|
+
session_disabled: {
|
|
177
|
+
id: 'session_disabled',
|
|
178
|
+
name: 'Disabled Approval Session',
|
|
179
|
+
cwd: process.cwd(),
|
|
180
|
+
user: 'tester',
|
|
181
|
+
provider: 'openai',
|
|
182
|
+
model: 'gpt-test',
|
|
183
|
+
credentialId: null,
|
|
184
|
+
apiEndpoint: null,
|
|
185
|
+
claudeSessionId: null,
|
|
186
|
+
codexThreadId: null,
|
|
187
|
+
opencodeSessionId: null,
|
|
188
|
+
delegateResumeIds: { claudeCode: null, codex: null, opencode: null, gemini: null },
|
|
189
|
+
messages: [],
|
|
190
|
+
createdAt: now,
|
|
191
|
+
lastActiveAt: now,
|
|
192
|
+
sessionType: 'human',
|
|
193
|
+
agentId: 'default',
|
|
194
|
+
plugins: [],
|
|
195
|
+
},
|
|
196
|
+
})
|
|
120
197
|
|
|
121
198
|
storage.saveSettings({
|
|
122
199
|
approvalsEnabled: false,
|
|
@@ -128,18 +205,22 @@ describe('approval auto-approve', () => {
|
|
|
128
205
|
description: 'Should be auto-approved because approvals are disabled platform-wide.',
|
|
129
206
|
data: { question: 'Proceed?' },
|
|
130
207
|
agentId: 'default',
|
|
131
|
-
sessionId:
|
|
208
|
+
sessionId: 'session_disabled',
|
|
132
209
|
})
|
|
133
210
|
|
|
211
|
+
await new Promise((resolve) => setTimeout(resolve, 500))
|
|
212
|
+
|
|
134
213
|
const stored = storage.loadApprovals()[approval.id]
|
|
135
214
|
console.log(JSON.stringify({
|
|
136
215
|
approvalStatus: approval.status,
|
|
137
216
|
storedStatus: stored?.status || null,
|
|
217
|
+
runCount: sessionRuns.listRuns({ sessionId: 'session_disabled', limit: 10 }).length,
|
|
138
218
|
}))
|
|
139
219
|
`)
|
|
140
220
|
|
|
141
221
|
assert.equal(output.approvalStatus, 'approved')
|
|
142
222
|
assert.equal(output.storedStatus, 'approved')
|
|
223
|
+
assert.equal(output.runCount, 0)
|
|
143
224
|
})
|
|
144
225
|
|
|
145
226
|
it('adds a pending approval request message to the chat session when approvals are enabled', () => {
|
|
@@ -153,6 +234,7 @@ describe('approval auto-approve', () => {
|
|
|
153
234
|
storage.saveSettings({
|
|
154
235
|
approvalsEnabled: true,
|
|
155
236
|
approvalAutoApproveCategories: [],
|
|
237
|
+
approvalConnectorNotifyDelaySec: 30,
|
|
156
238
|
})
|
|
157
239
|
storage.saveSessions({
|
|
158
240
|
session_chat: {
|
|
@@ -203,8 +285,270 @@ describe('approval auto-approve', () => {
|
|
|
203
285
|
assert.match(output.lastMessage.text, /\"pluginId\":\"shell\"/)
|
|
204
286
|
})
|
|
205
287
|
|
|
288
|
+
it('injects approval guidance only from enabled plugins', () => {
|
|
289
|
+
const output = runWithTempDataDir(`
|
|
290
|
+
process.on('unhandledRejection', () => {})
|
|
291
|
+
await import('./src/lib/server/session-tools/wallet.ts')
|
|
292
|
+
const storageMod = await import('./src/lib/server/storage.ts')
|
|
293
|
+
const approvalsMod = await import('./src/lib/server/approvals.ts')
|
|
294
|
+
const storage = storageMod.default || storageMod
|
|
295
|
+
const approvals = approvalsMod.default || approvalsMod
|
|
296
|
+
|
|
297
|
+
const now = Date.now()
|
|
298
|
+
storage.saveAgents({
|
|
299
|
+
agent_wallet: {
|
|
300
|
+
id: 'agent_wallet',
|
|
301
|
+
name: 'Wallet Agent',
|
|
302
|
+
description: 'Tests plugin approval guidance',
|
|
303
|
+
systemPrompt: 'test',
|
|
304
|
+
provider: 'openai',
|
|
305
|
+
model: 'gpt-test',
|
|
306
|
+
plugins: ['wallet'],
|
|
307
|
+
createdAt: now,
|
|
308
|
+
updatedAt: now,
|
|
309
|
+
},
|
|
310
|
+
agent_plain: {
|
|
311
|
+
id: 'agent_plain',
|
|
312
|
+
name: 'Plain Agent',
|
|
313
|
+
description: 'No wallet plugin',
|
|
314
|
+
systemPrompt: 'test',
|
|
315
|
+
provider: 'openai',
|
|
316
|
+
model: 'gpt-test',
|
|
317
|
+
plugins: [],
|
|
318
|
+
createdAt: now,
|
|
319
|
+
updatedAt: now,
|
|
320
|
+
},
|
|
321
|
+
})
|
|
322
|
+
storage.saveSettings({
|
|
323
|
+
approvalsEnabled: true,
|
|
324
|
+
approvalAutoApproveCategories: [],
|
|
325
|
+
})
|
|
326
|
+
storage.saveSessions({
|
|
327
|
+
session_wallet_guidance: {
|
|
328
|
+
id: 'session_wallet_guidance',
|
|
329
|
+
name: 'Wallet Guidance Session',
|
|
330
|
+
cwd: process.cwd(),
|
|
331
|
+
user: 'tester',
|
|
332
|
+
provider: 'openai',
|
|
333
|
+
model: 'gpt-test',
|
|
334
|
+
credentialId: null,
|
|
335
|
+
apiEndpoint: null,
|
|
336
|
+
claudeSessionId: null,
|
|
337
|
+
codexThreadId: null,
|
|
338
|
+
opencodeSessionId: null,
|
|
339
|
+
delegateResumeIds: { claudeCode: null, codex: null, opencode: null, gemini: null },
|
|
340
|
+
messages: [],
|
|
341
|
+
createdAt: now,
|
|
342
|
+
lastActiveAt: now,
|
|
343
|
+
sessionType: 'human',
|
|
344
|
+
agentId: 'agent_wallet',
|
|
345
|
+
plugins: ['wallet'],
|
|
346
|
+
connectorContext: {
|
|
347
|
+
connectorId: 'connector_wallet',
|
|
348
|
+
channelId: 'chan_wallet',
|
|
349
|
+
},
|
|
350
|
+
},
|
|
351
|
+
session_plain_guidance: {
|
|
352
|
+
id: 'session_plain_guidance',
|
|
353
|
+
name: 'Plain Guidance Session',
|
|
354
|
+
cwd: process.cwd(),
|
|
355
|
+
user: 'tester',
|
|
356
|
+
provider: 'openai',
|
|
357
|
+
model: 'gpt-test',
|
|
358
|
+
credentialId: null,
|
|
359
|
+
apiEndpoint: null,
|
|
360
|
+
claudeSessionId: null,
|
|
361
|
+
codexThreadId: null,
|
|
362
|
+
opencodeSessionId: null,
|
|
363
|
+
delegateResumeIds: { claudeCode: null, codex: null, opencode: null, gemini: null },
|
|
364
|
+
messages: [],
|
|
365
|
+
createdAt: now,
|
|
366
|
+
lastActiveAt: now,
|
|
367
|
+
sessionType: 'human',
|
|
368
|
+
agentId: 'agent_plain',
|
|
369
|
+
plugins: [],
|
|
370
|
+
},
|
|
371
|
+
})
|
|
372
|
+
|
|
373
|
+
const walletApproval = await approvals.requestApprovalMaybeAutoApprove({
|
|
374
|
+
category: 'wallet_action',
|
|
375
|
+
title: 'Wallet action: send transaction',
|
|
376
|
+
description: 'Broadcast an Ethereum transaction on Arbitrum.',
|
|
377
|
+
data: {
|
|
378
|
+
action: 'send_transaction',
|
|
379
|
+
chain: 'ethereum',
|
|
380
|
+
network: 'arbitrum',
|
|
381
|
+
summary: 'swap 1 USDC',
|
|
382
|
+
transaction: {
|
|
383
|
+
to: '0x000000000000000000000000000000000000dEaD',
|
|
384
|
+
data: '0x1234',
|
|
385
|
+
value: '0',
|
|
386
|
+
},
|
|
387
|
+
},
|
|
388
|
+
sessionId: 'session_wallet_guidance',
|
|
389
|
+
agentId: 'agent_wallet',
|
|
390
|
+
})
|
|
391
|
+
|
|
392
|
+
const plainApproval = await approvals.requestApprovalMaybeAutoApprove({
|
|
393
|
+
category: 'wallet_action',
|
|
394
|
+
title: 'Wallet action: send transaction',
|
|
395
|
+
description: 'Broadcast an Ethereum transaction on Arbitrum.',
|
|
396
|
+
data: {
|
|
397
|
+
action: 'send_transaction',
|
|
398
|
+
chain: 'ethereum',
|
|
399
|
+
network: 'arbitrum',
|
|
400
|
+
summary: 'swap 1 USDC',
|
|
401
|
+
transaction: {
|
|
402
|
+
to: '0x000000000000000000000000000000000000dEaD',
|
|
403
|
+
data: '0x1234',
|
|
404
|
+
value: '0',
|
|
405
|
+
},
|
|
406
|
+
},
|
|
407
|
+
sessionId: 'session_plain_guidance',
|
|
408
|
+
agentId: 'agent_plain',
|
|
409
|
+
})
|
|
410
|
+
|
|
411
|
+
const walletMessage = JSON.parse(storage.loadSessions().session_wallet_guidance.messages.at(-1).text)
|
|
412
|
+
const plainMessage = JSON.parse(storage.loadSessions().session_plain_guidance.messages.at(-1).text)
|
|
413
|
+
console.log(JSON.stringify({
|
|
414
|
+
walletGuidance: walletMessage.guidance || [],
|
|
415
|
+
plainGuidance: plainMessage.guidance || [],
|
|
416
|
+
}))
|
|
417
|
+
`)
|
|
418
|
+
|
|
419
|
+
assert.equal(Array.isArray(output.walletGuidance), true)
|
|
420
|
+
assert.equal(output.walletGuidance.some((line: string) => /wallet_tool/.test(line)), true)
|
|
421
|
+
assert.deepEqual(output.plainGuidance, [])
|
|
422
|
+
})
|
|
423
|
+
|
|
424
|
+
it('derives tool-access approval guidance from the requested plugin metadata', () => {
|
|
425
|
+
const output = runWithTempDataDir(`
|
|
426
|
+
process.on('unhandledRejection', () => {})
|
|
427
|
+
await import('./src/lib/server/session-tools/http.ts')
|
|
428
|
+
const storageMod = await import('./src/lib/server/storage.ts')
|
|
429
|
+
const approvalsMod = await import('./src/lib/server/approvals.ts')
|
|
430
|
+
const storage = storageMod.default || storageMod
|
|
431
|
+
const approvals = approvalsMod.default || approvalsMod
|
|
432
|
+
|
|
433
|
+
const now = Date.now()
|
|
434
|
+
storage.saveSettings({
|
|
435
|
+
approvalsEnabled: true,
|
|
436
|
+
approvalAutoApproveCategories: [],
|
|
437
|
+
})
|
|
438
|
+
storage.saveSessions({
|
|
439
|
+
session_http_guidance: {
|
|
440
|
+
id: 'session_http_guidance',
|
|
441
|
+
name: 'HTTP Guidance Session',
|
|
442
|
+
cwd: process.cwd(),
|
|
443
|
+
user: 'tester',
|
|
444
|
+
provider: 'openai',
|
|
445
|
+
model: 'gpt-test',
|
|
446
|
+
credentialId: null,
|
|
447
|
+
apiEndpoint: null,
|
|
448
|
+
claudeSessionId: null,
|
|
449
|
+
codexThreadId: null,
|
|
450
|
+
opencodeSessionId: null,
|
|
451
|
+
delegateResumeIds: { claudeCode: null, codex: null, opencode: null, gemini: null },
|
|
452
|
+
messages: [],
|
|
453
|
+
createdAt: now,
|
|
454
|
+
lastActiveAt: now,
|
|
455
|
+
sessionType: 'human',
|
|
456
|
+
agentId: 'agent_http',
|
|
457
|
+
plugins: [],
|
|
458
|
+
},
|
|
459
|
+
})
|
|
460
|
+
|
|
461
|
+
await approvals.requestApprovalMaybeAutoApprove({
|
|
462
|
+
category: 'tool_access',
|
|
463
|
+
title: 'Enable Plugin: http',
|
|
464
|
+
description: 'Need HTTP access for an API task.',
|
|
465
|
+
data: { toolId: 'http', pluginId: 'http' },
|
|
466
|
+
sessionId: 'session_http_guidance',
|
|
467
|
+
agentId: 'agent_http',
|
|
468
|
+
})
|
|
469
|
+
|
|
470
|
+
const message = JSON.parse(storage.loadSessions().session_http_guidance.messages.at(-1).text)
|
|
471
|
+
console.log(JSON.stringify({ guidance: message.guidance || [] }))
|
|
472
|
+
`)
|
|
473
|
+
|
|
474
|
+
assert.equal(Array.isArray(output.guidance), true)
|
|
475
|
+
assert.equal(output.guidance.some((line: string) => /http_request/.test(line)), true)
|
|
476
|
+
})
|
|
477
|
+
|
|
478
|
+
it('injects plugin-owned scaffold guidance for plugin creator approvals', () => {
|
|
479
|
+
const output = runWithTempDataDir(`
|
|
480
|
+
process.on('unhandledRejection', () => {})
|
|
481
|
+
await import('./src/lib/server/session-tools/plugin-creator.ts')
|
|
482
|
+
const storageMod = await import('./src/lib/server/storage.ts')
|
|
483
|
+
const approvalsMod = await import('./src/lib/server/approvals.ts')
|
|
484
|
+
const storage = storageMod.default || storageMod
|
|
485
|
+
const approvals = approvalsMod.default || approvalsMod
|
|
486
|
+
|
|
487
|
+
const now = Date.now()
|
|
488
|
+
storage.saveAgents({
|
|
489
|
+
agent_plugins: {
|
|
490
|
+
id: 'agent_plugins',
|
|
491
|
+
name: 'Plugin Agent',
|
|
492
|
+
description: 'Tests plugin creator approval guidance',
|
|
493
|
+
systemPrompt: 'test',
|
|
494
|
+
provider: 'openai',
|
|
495
|
+
model: 'gpt-test',
|
|
496
|
+
plugins: ['plugin_creator'],
|
|
497
|
+
createdAt: now,
|
|
498
|
+
updatedAt: now,
|
|
499
|
+
},
|
|
500
|
+
})
|
|
501
|
+
storage.saveSettings({
|
|
502
|
+
approvalsEnabled: true,
|
|
503
|
+
approvalAutoApproveCategories: [],
|
|
504
|
+
})
|
|
505
|
+
storage.saveSessions({
|
|
506
|
+
session_plugin_creator_guidance: {
|
|
507
|
+
id: 'session_plugin_creator_guidance',
|
|
508
|
+
name: 'Plugin Creator Guidance Session',
|
|
509
|
+
cwd: process.cwd(),
|
|
510
|
+
user: 'tester',
|
|
511
|
+
provider: 'openai',
|
|
512
|
+
model: 'gpt-test',
|
|
513
|
+
credentialId: null,
|
|
514
|
+
apiEndpoint: null,
|
|
515
|
+
claudeSessionId: null,
|
|
516
|
+
codexThreadId: null,
|
|
517
|
+
opencodeSessionId: null,
|
|
518
|
+
delegateResumeIds: { claudeCode: null, codex: null, opencode: null, gemini: null },
|
|
519
|
+
messages: [],
|
|
520
|
+
createdAt: now,
|
|
521
|
+
lastActiveAt: now,
|
|
522
|
+
sessionType: 'human',
|
|
523
|
+
agentId: 'agent_plugins',
|
|
524
|
+
plugins: ['plugin_creator'],
|
|
525
|
+
},
|
|
526
|
+
})
|
|
527
|
+
|
|
528
|
+
await approvals.requestApprovalMaybeAutoApprove({
|
|
529
|
+
category: 'plugin_scaffold',
|
|
530
|
+
title: 'Scaffold Plugin: test-plugin.js',
|
|
531
|
+
description: 'Create a test plugin file.',
|
|
532
|
+
data: {
|
|
533
|
+
filename: 'test-plugin.js',
|
|
534
|
+
code: 'module.exports = { name: "Test Plugin" }',
|
|
535
|
+
packageManager: 'npm',
|
|
536
|
+
},
|
|
537
|
+
sessionId: 'session_plugin_creator_guidance',
|
|
538
|
+
agentId: 'agent_plugins',
|
|
539
|
+
})
|
|
540
|
+
|
|
541
|
+
const message = JSON.parse(storage.loadSessions().session_plugin_creator_guidance.messages.at(-1).text)
|
|
542
|
+
console.log(JSON.stringify({ guidance: message.guidance || [] }))
|
|
543
|
+
`)
|
|
544
|
+
|
|
545
|
+
assert.equal(Array.isArray(output.guidance), true)
|
|
546
|
+
assert.equal(output.guidance.some((line: string) => /plugin_creator_tool/.test(line)), true)
|
|
547
|
+
})
|
|
548
|
+
|
|
206
549
|
it('applies tool access after a manual approval decision', () => {
|
|
207
550
|
const output = runWithTempDataDir(`
|
|
551
|
+
process.on('unhandledRejection', () => {})
|
|
208
552
|
const storageMod = await import('./src/lib/server/storage.ts')
|
|
209
553
|
const approvalsMod = await import('./src/lib/server/approvals.ts')
|
|
210
554
|
const storage = storageMod.default || storageMod
|
|
@@ -261,4 +605,197 @@ describe('approval auto-approve', () => {
|
|
|
261
605
|
assert.equal(output.finalStatus, 'approved')
|
|
262
606
|
assert.equal(output.plugins.includes('shell'), true)
|
|
263
607
|
})
|
|
608
|
+
|
|
609
|
+
it('wakes the blocked session after a manual approval decision', () => {
|
|
610
|
+
const output = runWithTempDataDir(`
|
|
611
|
+
process.on('unhandledRejection', () => {})
|
|
612
|
+
const storageMod = await import('./src/lib/server/storage.ts')
|
|
613
|
+
const approvalsMod = await import('./src/lib/server/approvals.ts')
|
|
614
|
+
const sessionRunsMod = await import('./src/lib/server/session-run-manager.ts')
|
|
615
|
+
const storage = storageMod.default || storageMod
|
|
616
|
+
const approvals = approvalsMod.default || approvalsMod
|
|
617
|
+
const sessionRuns = sessionRunsMod.default || sessionRunsMod
|
|
618
|
+
|
|
619
|
+
const now = Date.now()
|
|
620
|
+
storage.saveSettings({
|
|
621
|
+
approvalsEnabled: true,
|
|
622
|
+
approvalAutoApproveCategories: [],
|
|
623
|
+
})
|
|
624
|
+
storage.saveSessions({
|
|
625
|
+
session_resume: {
|
|
626
|
+
id: 'session_resume',
|
|
627
|
+
name: 'Resume Approval Test',
|
|
628
|
+
cwd: process.cwd(),
|
|
629
|
+
user: 'tester',
|
|
630
|
+
provider: 'openai',
|
|
631
|
+
model: 'gpt-test',
|
|
632
|
+
credentialId: null,
|
|
633
|
+
apiEndpoint: null,
|
|
634
|
+
claudeSessionId: null,
|
|
635
|
+
codexThreadId: null,
|
|
636
|
+
opencodeSessionId: null,
|
|
637
|
+
delegateResumeIds: { claudeCode: null, codex: null, opencode: null, gemini: null },
|
|
638
|
+
messages: [],
|
|
639
|
+
createdAt: now,
|
|
640
|
+
lastActiveAt: now,
|
|
641
|
+
sessionType: 'human',
|
|
642
|
+
agentId: 'default',
|
|
643
|
+
plugins: [],
|
|
644
|
+
},
|
|
645
|
+
})
|
|
646
|
+
|
|
647
|
+
const approval = await approvals.requestApprovalMaybeAutoApprove({
|
|
648
|
+
category: 'wallet_action',
|
|
649
|
+
title: 'Wallet action: sign transaction',
|
|
650
|
+
description: 'Sign an Ethereum transaction on Arbitrum.',
|
|
651
|
+
data: {
|
|
652
|
+
action: 'sign_transaction',
|
|
653
|
+
chain: 'ethereum',
|
|
654
|
+
network: 'arbitrum',
|
|
655
|
+
},
|
|
656
|
+
sessionId: 'session_resume',
|
|
657
|
+
agentId: 'default',
|
|
658
|
+
})
|
|
659
|
+
|
|
660
|
+
await approvals.submitDecision(approval.id, true)
|
|
661
|
+
await approvals.submitDecision(approval.id, true)
|
|
662
|
+
await new Promise((resolve) => setTimeout(resolve, 600))
|
|
663
|
+
|
|
664
|
+
const runs = sessionRuns.listRuns({ sessionId: 'session_resume', limit: 10 })
|
|
665
|
+
console.log(JSON.stringify({
|
|
666
|
+
finalStatus: storage.loadApprovals()[approval.id]?.status || null,
|
|
667
|
+
runCount: runs.length,
|
|
668
|
+
runSources: runs.map((run) => run.source),
|
|
669
|
+
runStatuses: runs.map((run) => run.status),
|
|
670
|
+
}))
|
|
671
|
+
`)
|
|
672
|
+
|
|
673
|
+
assert.equal(output.finalStatus, 'approved')
|
|
674
|
+
assert.equal(output.runCount >= 1, true)
|
|
675
|
+
assert.equal(output.runSources.filter((source) => source === 'approval-decision').length, 1)
|
|
676
|
+
})
|
|
677
|
+
|
|
678
|
+
it('reuses equivalent wallet approvals instead of creating duplicates', () => {
|
|
679
|
+
const output = runWithTempDataDir(`
|
|
680
|
+
process.on('unhandledRejection', () => {})
|
|
681
|
+
const storageMod = await import('./src/lib/server/storage.ts')
|
|
682
|
+
const approvalsMod = await import('./src/lib/server/approvals.ts')
|
|
683
|
+
const storage = storageMod.default || storageMod
|
|
684
|
+
const approvals = approvalsMod.default || approvalsMod
|
|
685
|
+
|
|
686
|
+
const now = Date.now()
|
|
687
|
+
storage.saveSettings({
|
|
688
|
+
approvalsEnabled: true,
|
|
689
|
+
approvalAutoApproveCategories: [],
|
|
690
|
+
})
|
|
691
|
+
storage.saveSessions({
|
|
692
|
+
session_wallet: {
|
|
693
|
+
id: 'session_wallet',
|
|
694
|
+
name: 'Wallet Approval Dedupe Test',
|
|
695
|
+
cwd: process.cwd(),
|
|
696
|
+
user: 'tester',
|
|
697
|
+
provider: 'openai',
|
|
698
|
+
model: 'gpt-test',
|
|
699
|
+
credentialId: null,
|
|
700
|
+
apiEndpoint: null,
|
|
701
|
+
claudeSessionId: null,
|
|
702
|
+
codexThreadId: null,
|
|
703
|
+
opencodeSessionId: null,
|
|
704
|
+
delegateResumeIds: { claudeCode: null, codex: null, opencode: null, gemini: null },
|
|
705
|
+
messages: [],
|
|
706
|
+
createdAt: now,
|
|
707
|
+
lastActiveAt: now,
|
|
708
|
+
sessionType: 'human',
|
|
709
|
+
agentId: 'default',
|
|
710
|
+
plugins: [],
|
|
711
|
+
},
|
|
712
|
+
})
|
|
713
|
+
|
|
714
|
+
const requestParams = {
|
|
715
|
+
category: 'wallet_action',
|
|
716
|
+
title: 'Wallet action: sign transaction',
|
|
717
|
+
description: 'Sign an Ethereum transaction on Arbitrum.',
|
|
718
|
+
data: {
|
|
719
|
+
action: 'sign_transaction',
|
|
720
|
+
chain: 'ethereum',
|
|
721
|
+
network: 'arbitrum',
|
|
722
|
+
transaction: {
|
|
723
|
+
to: '0xaf88d065e77c8cC2239327C5EDb3A432268e5831',
|
|
724
|
+
data: '0x095ea7b3000000000000000000000000216b4b4ba9f3e719726886d34a177484278bfcae00000000000000000000000000000000000000000000000000000000000f4240',
|
|
725
|
+
value: '0',
|
|
726
|
+
},
|
|
727
|
+
},
|
|
728
|
+
sessionId: 'session_wallet',
|
|
729
|
+
agentId: 'default',
|
|
730
|
+
}
|
|
731
|
+
|
|
732
|
+
const first = await approvals.requestApprovalMaybeAutoApprove(requestParams)
|
|
733
|
+
const second = await approvals.requestApprovalMaybeAutoApprove(requestParams)
|
|
734
|
+
await approvals.submitDecision(first.id, true)
|
|
735
|
+
const third = await approvals.requestApprovalMaybeAutoApprove(requestParams)
|
|
736
|
+
|
|
737
|
+
const allApprovals = storage.loadApprovals()
|
|
738
|
+
console.log(JSON.stringify({
|
|
739
|
+
firstId: first.id,
|
|
740
|
+
secondId: second.id,
|
|
741
|
+
thirdId: third.id,
|
|
742
|
+
secondStatus: second.status,
|
|
743
|
+
thirdStatus: third.status,
|
|
744
|
+
approvalCount: Object.keys(allApprovals).length,
|
|
745
|
+
}))
|
|
746
|
+
`)
|
|
747
|
+
|
|
748
|
+
assert.equal(output.firstId, output.secondId)
|
|
749
|
+
assert.equal(output.firstId, output.thirdId)
|
|
750
|
+
assert.equal(output.secondStatus, 'pending')
|
|
751
|
+
assert.equal(output.thirdStatus, 'approved')
|
|
752
|
+
assert.equal(output.approvalCount, 1)
|
|
753
|
+
})
|
|
754
|
+
|
|
755
|
+
it('reuses approved tool-access decisions across sessions for the same agent', () => {
|
|
756
|
+
const output = runWithTempDataDir(`
|
|
757
|
+
const storageMod = await import('./src/lib/server/storage.ts')
|
|
758
|
+
const approvalsMod = await import('./src/lib/server/approvals.ts')
|
|
759
|
+
const storage = storageMod.default || storageMod
|
|
760
|
+
const approvals = approvalsMod.default || approvalsMod
|
|
761
|
+
|
|
762
|
+
const now = Date.now()
|
|
763
|
+
storage.saveSettings({
|
|
764
|
+
approvalsEnabled: true,
|
|
765
|
+
approvalAutoApproveCategories: [],
|
|
766
|
+
})
|
|
767
|
+
|
|
768
|
+
const first = await approvals.requestApprovalMaybeAutoApprove({
|
|
769
|
+
category: 'tool_access',
|
|
770
|
+
title: 'Enable connector tool',
|
|
771
|
+
description: 'Grant connector messaging',
|
|
772
|
+
data: { toolId: 'connector_message_tool', pluginId: 'connector_message_tool' },
|
|
773
|
+
agentId: 'agent_1',
|
|
774
|
+
})
|
|
775
|
+
await approvals.submitDecision(first.id, true)
|
|
776
|
+
|
|
777
|
+
const approvalsStore = storage.loadApprovals()
|
|
778
|
+
approvalsStore[first.id].updatedAt = now - (24 * 60 * 60 * 1000)
|
|
779
|
+
storage.upsertApproval(first.id, approvalsStore[first.id])
|
|
780
|
+
|
|
781
|
+
const second = await approvals.requestApprovalMaybeAutoApprove({
|
|
782
|
+
category: 'tool_access',
|
|
783
|
+
title: 'Enable connector tool',
|
|
784
|
+
description: 'Grant connector messaging again',
|
|
785
|
+
data: { toolId: 'connector_message_tool', pluginId: 'connector_message_tool' },
|
|
786
|
+
agentId: 'agent_1',
|
|
787
|
+
})
|
|
788
|
+
|
|
789
|
+
console.log(JSON.stringify({
|
|
790
|
+
firstId: first.id,
|
|
791
|
+
secondId: second.id,
|
|
792
|
+
secondStatus: second.status,
|
|
793
|
+
approvalCount: Object.keys(storage.loadApprovals()).length,
|
|
794
|
+
}))
|
|
795
|
+
`)
|
|
796
|
+
|
|
797
|
+
assert.equal(output.firstId, output.secondId)
|
|
798
|
+
assert.equal(output.secondStatus, 'approved')
|
|
799
|
+
assert.equal(output.approvalCount, 1)
|
|
800
|
+
})
|
|
264
801
|
})
|