@swarmclawai/swarmclaw 0.7.8 → 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 -15
- 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 +22 -2
- package/src/app/api/clawhub/install/route.ts +28 -8
- package/src/app/api/connectors/[id]/route.ts +26 -1
- 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/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/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 +73 -24
- package/src/components/agents/inspector-panel.tsx +41 -0
- package/src/components/canvas/canvas-panel.tsx +236 -65
- package/src/components/chat/chat-card.tsx +36 -13
- package/src/components/chat/chat-header.tsx +44 -16
- package/src/components/chat/chat-list.tsx +28 -4
- package/src/components/chat/checkpoint-timeline.tsx +50 -34
- 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/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/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 +7 -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 +191 -95
- package/src/components/tasks/task-board.tsx +273 -2
- package/src/components/tasks/task-card.tsx +38 -9
- 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 +11 -0
- package/src/lib/server/capability-router.ts +26 -1
- 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 +353 -72
- package/src/lib/server/clawhub-client.test.ts +14 -8
- package/src/lib/server/connectors/manager.test.ts +1147 -0
- package/src/lib/server/connectors/manager.ts +362 -63
- 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 +1 -1
- 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 +189 -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 +15 -10
- 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/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 +2 -2
- package/src/lib/server/plugins-advanced.test.ts +351 -0
- package/src/lib/server/plugins.ts +205 -5
- package/src/lib/server/queue-advanced.test.ts +528 -0
- package/src/lib/server/queue-followups.test.ts +262 -0
- package/src/lib/server/queue-reconcile.test.ts +128 -0
- package/src/lib/server/queue.ts +293 -61
- 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 +52 -4
- package/src/lib/server/session-tools/canvas.ts +14 -12
- package/src/lib/server/session-tools/connector.test.ts +138 -0
- package/src/lib/server/session-tools/connector.ts +348 -61
- package/src/lib/server/session-tools/context.ts +12 -3
- package/src/lib/server/session-tools/crud.ts +221 -10
- package/src/lib/server/session-tools/delegate-fallback.test.ts +103 -0
- package/src/lib/server/session-tools/delegate.ts +64 -8
- 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/memory.test.ts +93 -0
- package/src/lib/server/session-tools/memory.ts +546 -79
- package/src/lib/server/session-tools/normalize-tool-args.ts +1 -1
- 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 +162 -1
- package/src/lib/server/session-tools/web.ts +468 -64
- 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 +419 -9
- package/src/lib/server/stream-agent-chat.ts +887 -83
- package/src/lib/server/system-events.ts +1 -1
- package/src/lib/server/tool-capability-policy-advanced.test.ts +502 -0
- 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.ts +4 -2
- 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-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 +210 -14
|
@@ -219,7 +219,10 @@ export function cancelAllHeartbeatRuns(reason = 'Heartbeat disabled globally'):
|
|
|
219
219
|
async function drainExecution(executionKey: string): Promise<void> {
|
|
220
220
|
if (state.runningByExecution.has(executionKey)) return
|
|
221
221
|
const q = queueForExecution(executionKey)
|
|
222
|
-
|
|
222
|
+
// Priority: user (non-heartbeat) runs go first. If a heartbeat is queued
|
|
223
|
+
// behind a user run, the user run takes priority.
|
|
224
|
+
const userIdx = q.findIndex(e => !isInternalHeartbeatRun(e.run.internal, e.run.source))
|
|
225
|
+
const next = userIdx >= 0 ? q.splice(userIdx, 1)[0] : q.shift()
|
|
223
226
|
if (!next) return
|
|
224
227
|
|
|
225
228
|
state.runningByExecution.set(executionKey, next)
|
|
@@ -416,6 +419,18 @@ export function enqueueSessionRun(input: EnqueueSessionRunInput): EnqueueSession
|
|
|
416
419
|
cancelPendingForSession(input.sessionId, 'Cancelled by steer mode')
|
|
417
420
|
}
|
|
418
421
|
|
|
422
|
+
// Heartbeat preemption: if a user chat arrives while a heartbeat is running,
|
|
423
|
+
// abort the heartbeat so the user doesn't wait. The heartbeat will retry
|
|
424
|
+
// on the next tick.
|
|
425
|
+
if (!internal && source === 'chat') {
|
|
426
|
+
const running = state.runningByExecution.get(executionKey)
|
|
427
|
+
if (running && isInternalHeartbeatRun(running.run.internal, running.run.source)) {
|
|
428
|
+
log.info('session-run', `Preempting heartbeat ${running.run.id} for user chat on ${input.sessionId}`)
|
|
429
|
+
abortSessionRuntime(running, 'Preempted by user chat')
|
|
430
|
+
state.runningByExecution.delete(executionKey)
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
|
|
419
434
|
const running = state.runningByExecution.get(executionKey)
|
|
420
435
|
const q = queueForExecution(executionKey)
|
|
421
436
|
if (mode === 'collect' && !input.imagePath && !input.imageUrl && !input.attachedFiles?.length) {
|
|
@@ -506,15 +521,48 @@ export function enqueueSessionRun(input: EnqueueSessionRunInput): EnqueueSession
|
|
|
506
521
|
export function getSessionRunState(sessionId: string): {
|
|
507
522
|
runningRunId?: string
|
|
508
523
|
queueLength: number
|
|
524
|
+
} {
|
|
525
|
+
const summary = getSessionExecutionState(sessionId)
|
|
526
|
+
return {
|
|
527
|
+
runningRunId: summary.runningRunId,
|
|
528
|
+
queueLength: summary.queueLength,
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
export function getSessionExecutionState(sessionId: string): {
|
|
533
|
+
runningRunId?: string
|
|
534
|
+
queueLength: number
|
|
535
|
+
hasRunning: boolean
|
|
536
|
+
hasQueued: boolean
|
|
537
|
+
hasRunningHeartbeat: boolean
|
|
538
|
+
hasQueuedHeartbeat: boolean
|
|
539
|
+
hasRunningNonHeartbeat: boolean
|
|
540
|
+
hasQueuedNonHeartbeat: boolean
|
|
509
541
|
} {
|
|
510
542
|
const executionKey = executionKeyForSession(sessionId)
|
|
511
543
|
const running = state.runningByExecution.get(executionKey)
|
|
512
|
-
const
|
|
544
|
+
const runningMatchesSession = running?.run.sessionId === sessionId
|
|
545
|
+
const runningHeartbeat = Boolean(
|
|
546
|
+
runningMatchesSession
|
|
547
|
+
&& isInternalHeartbeatRun(running.run.internal, running.run.source),
|
|
548
|
+
)
|
|
549
|
+
const runningNonHeartbeat = Boolean(runningMatchesSession && !runningHeartbeat)
|
|
550
|
+
const queuedEntries = queueForExecution(executionKey).filter((entry) => entry.run.sessionId === sessionId)
|
|
551
|
+
const queuedHeartbeat = queuedEntries.filter((entry) =>
|
|
552
|
+
isInternalHeartbeatRun(entry.run.internal, entry.run.source),
|
|
553
|
+
).length
|
|
554
|
+
const queuedNonHeartbeat = queuedEntries.length - queuedHeartbeat
|
|
513
555
|
return {
|
|
514
|
-
runningRunId: (
|
|
556
|
+
runningRunId: (runningMatchesSession && running?.run.status === 'running')
|
|
515
557
|
? running.run.id
|
|
516
558
|
: undefined,
|
|
517
|
-
queueLength:
|
|
559
|
+
queueLength: queuedEntries.length,
|
|
560
|
+
hasRunning: Boolean(runningMatchesSession),
|
|
561
|
+
hasQueued: queuedEntries.length > 0,
|
|
562
|
+
hasRunningHeartbeat: runningHeartbeat,
|
|
563
|
+
hasQueuedHeartbeat: queuedHeartbeat > 0,
|
|
564
|
+
hasRunningNonHeartbeat: runningNonHeartbeat,
|
|
565
|
+
hasQueuedNonHeartbeat: queuedNonHeartbeat > 0,
|
|
518
566
|
}
|
|
519
567
|
}
|
|
520
568
|
|
|
@@ -6,6 +6,7 @@ import type { ToolBuildContext } from './context'
|
|
|
6
6
|
import type { Plugin, PluginHooks } from '@/types'
|
|
7
7
|
import { getPluginManager } from '../plugins'
|
|
8
8
|
import { normalizeToolInputArgs } from './normalize-tool-args'
|
|
9
|
+
import { normalizeCanvasContent, summarizeCanvasContent } from '@/lib/canvas-content'
|
|
9
10
|
|
|
10
11
|
/**
|
|
11
12
|
* Core Canvas Execution Logic
|
|
@@ -14,6 +15,7 @@ async function executeCanvasAction(args: Record<string, unknown>, context: { ses
|
|
|
14
15
|
const normalized = normalizeToolInputArgs(args)
|
|
15
16
|
const action = normalized.action as string
|
|
16
17
|
const content = normalized.content as string | undefined
|
|
18
|
+
const document = normalized.document
|
|
17
19
|
try {
|
|
18
20
|
const sessionId = context.sessionId
|
|
19
21
|
if (!sessionId) return 'Error: no active session for canvas.'
|
|
@@ -23,13 +25,18 @@ async function executeCanvasAction(args: Record<string, unknown>, context: { ses
|
|
|
23
25
|
if (!session) return 'Error: session not found.'
|
|
24
26
|
|
|
25
27
|
if (action === 'present') {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
+
const nextContent = normalizeCanvasContent(document ?? content)
|
|
29
|
+
if (!nextContent) return 'Error: content or document is required for present action.'
|
|
30
|
+
;(session as Record<string, unknown>).canvasContent = nextContent
|
|
28
31
|
session.lastActiveAt = Date.now()
|
|
29
32
|
sessions[sessionId] = session
|
|
30
33
|
saveSessions(sessions)
|
|
31
34
|
notify(`canvas:${sessionId}`)
|
|
32
|
-
return JSON.stringify({
|
|
35
|
+
return JSON.stringify({
|
|
36
|
+
ok: true,
|
|
37
|
+
action: 'present',
|
|
38
|
+
...summarizeCanvasContent(nextContent),
|
|
39
|
+
})
|
|
33
40
|
}
|
|
34
41
|
|
|
35
42
|
if (action === 'hide') {
|
|
@@ -42,14 +49,8 @@ async function executeCanvasAction(args: Record<string, unknown>, context: { ses
|
|
|
42
49
|
}
|
|
43
50
|
|
|
44
51
|
if (action === 'snapshot') {
|
|
45
|
-
const current = (session as Record<string, unknown>).canvasContent
|
|
46
|
-
return JSON.stringify({
|
|
47
|
-
ok: true,
|
|
48
|
-
action: 'snapshot',
|
|
49
|
-
hasContent: !!current,
|
|
50
|
-
contentLength: typeof current === 'string' ? current.length : 0,
|
|
51
|
-
preview: typeof current === 'string' ? current.slice(0, 500) : null,
|
|
52
|
-
})
|
|
52
|
+
const current = normalizeCanvasContent((session as Record<string, unknown>).canvasContent)
|
|
53
|
+
return JSON.stringify({ ok: true, action: 'snapshot', ...summarizeCanvasContent(current) })
|
|
53
54
|
}
|
|
54
55
|
|
|
55
56
|
return `Unknown canvas action "${action}".`
|
|
@@ -73,7 +74,8 @@ const CanvasPlugin: Plugin = {
|
|
|
73
74
|
type: 'object',
|
|
74
75
|
properties: {
|
|
75
76
|
action: { type: 'string', enum: ['present', 'hide', 'snapshot'] },
|
|
76
|
-
content: { type: 'string' }
|
|
77
|
+
content: { type: 'string' },
|
|
78
|
+
document: { type: 'object', additionalProperties: true },
|
|
77
79
|
},
|
|
78
80
|
required: ['action']
|
|
79
81
|
},
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import assert from 'node:assert/strict'
|
|
2
|
+
import { describe, it } from 'node:test'
|
|
3
|
+
|
|
4
|
+
import {
|
|
5
|
+
CONNECTOR_MESSAGE_TOOL_ACTIONS,
|
|
6
|
+
CONNECTOR_MESSAGE_TOOL_PARAMETERS,
|
|
7
|
+
inferConnectorActionName,
|
|
8
|
+
normalizeConnectorActionInputAliases,
|
|
9
|
+
normalizeConnectorActionName,
|
|
10
|
+
} from './connector'
|
|
11
|
+
import { getPluginManager } from '../plugins'
|
|
12
|
+
import { buildSessionTools } from './index'
|
|
13
|
+
|
|
14
|
+
describe('connector_message_tool contract', () => {
|
|
15
|
+
it('exposes the connector actions and voice-note fields through the plugin schema', () => {
|
|
16
|
+
const entry = getPluginManager()
|
|
17
|
+
.getTools(['manage_connectors'])
|
|
18
|
+
.find((tool) => tool.tool.name === 'connector_message_tool')
|
|
19
|
+
|
|
20
|
+
assert.ok(entry, 'connector_message_tool should be registered for manage_connectors')
|
|
21
|
+
|
|
22
|
+
const props = (entry!.tool.parameters?.properties ?? {}) as Record<string, { type?: string; enum?: string[] }>
|
|
23
|
+
assert.deepEqual(props.action?.enum, [...CONNECTOR_MESSAGE_TOOL_ACTIONS])
|
|
24
|
+
assert.equal(props.approved?.type, 'boolean')
|
|
25
|
+
assert.equal(props.ptt?.type, 'boolean')
|
|
26
|
+
assert.equal(props.voiceText?.type, 'string')
|
|
27
|
+
assert.equal(props.recipientId?.type, 'string')
|
|
28
|
+
assert.equal(props.channel?.type, 'string')
|
|
29
|
+
assert.equal(Array.isArray(entry!.tool.parameters?.required), false)
|
|
30
|
+
assert.equal(Array.isArray((CONNECTOR_MESSAGE_TOOL_PARAMETERS as { required?: unknown }).required), false)
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
it('normalizes legacy rich-message aliases to the current connector actions', () => {
|
|
34
|
+
assert.equal(normalizeConnectorActionName('message_react'), 'react')
|
|
35
|
+
assert.equal(normalizeConnectorActionName('message_edit'), 'edit')
|
|
36
|
+
assert.equal(normalizeConnectorActionName('message_delete'), 'delete')
|
|
37
|
+
assert.equal(normalizeConnectorActionName('message_pin'), 'pin')
|
|
38
|
+
assert.equal(normalizeConnectorActionName('send_voice_note'), 'send_voice_note')
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
it('infers send-style actions from partial connector payloads', () => {
|
|
42
|
+
assert.equal(inferConnectorActionName({ voiceText: 'hello there' }), 'send_voice_note')
|
|
43
|
+
assert.equal(inferConnectorActionName({ followUpMessage: 'check back later', delaySec: 60 }), 'schedule_followup')
|
|
44
|
+
assert.equal(inferConnectorActionName({ message: 'plain text message' }), 'send')
|
|
45
|
+
assert.equal(inferConnectorActionName({}), null)
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
it('normalizes connector and target aliases from model-generated delivery calls', () => {
|
|
49
|
+
const running = [{ id: 'd81cd63b', name: 'Main Whatsapp connection' }]
|
|
50
|
+
|
|
51
|
+
assert.deepEqual(
|
|
52
|
+
normalizeConnectorActionInputAliases({
|
|
53
|
+
action: 'send_voice_note',
|
|
54
|
+
channel: 'Main Whatsapp connection',
|
|
55
|
+
recipientId: '07958148127',
|
|
56
|
+
}, running),
|
|
57
|
+
{
|
|
58
|
+
action: 'send_voice_note',
|
|
59
|
+
channel: 'Main Whatsapp connection',
|
|
60
|
+
recipientId: '07958148127',
|
|
61
|
+
connectorId: 'd81cd63b',
|
|
62
|
+
to: '07958148127',
|
|
63
|
+
},
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
assert.deepEqual(
|
|
67
|
+
normalizeConnectorActionInputAliases({
|
|
68
|
+
action: 'send_voice_note',
|
|
69
|
+
id: 'd81cd63b',
|
|
70
|
+
target: '199900000001@lid',
|
|
71
|
+
}, running),
|
|
72
|
+
{
|
|
73
|
+
action: 'send_voice_note',
|
|
74
|
+
id: 'd81cd63b',
|
|
75
|
+
target: '199900000001@lid',
|
|
76
|
+
connectorId: 'd81cd63b',
|
|
77
|
+
to: '199900000001@lid',
|
|
78
|
+
},
|
|
79
|
+
)
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
it('treats raw id as messageId for message actions instead of as a target alias', () => {
|
|
83
|
+
assert.deepEqual(
|
|
84
|
+
normalizeConnectorActionInputAliases({
|
|
85
|
+
action: 'react',
|
|
86
|
+
id: 'msg-123',
|
|
87
|
+
emoji: '👍',
|
|
88
|
+
}, [{ id: 'conn-1', name: 'Primary connector' }]),
|
|
89
|
+
{
|
|
90
|
+
action: 'react',
|
|
91
|
+
id: 'msg-123',
|
|
92
|
+
emoji: '👍',
|
|
93
|
+
messageId: 'msg-123',
|
|
94
|
+
},
|
|
95
|
+
)
|
|
96
|
+
})
|
|
97
|
+
|
|
98
|
+
it('buildSessionTools exposes the native connector schema instead of the legacy passthrough bridge', async () => {
|
|
99
|
+
const built = await buildSessionTools(process.cwd(), ['manage_connectors'], {
|
|
100
|
+
sessionId: 'connector-native-schema-test',
|
|
101
|
+
agentId: 'default',
|
|
102
|
+
platformAssignScope: 'self',
|
|
103
|
+
})
|
|
104
|
+
|
|
105
|
+
try {
|
|
106
|
+
const connectorTool = built.tools.find((tool) => tool.name === 'connector_message_tool')
|
|
107
|
+
assert.ok(connectorTool, 'connector_message_tool should be available when manage_connectors is enabled')
|
|
108
|
+
|
|
109
|
+
const schema = (connectorTool as { schema?: { safeParse: (value: unknown) => { success: boolean } } }).schema
|
|
110
|
+
assert.ok(schema, 'connector_message_tool should expose a validation schema')
|
|
111
|
+
assert.equal(schema.safeParse({ action: 'send_voice_note', approved: true, ptt: true }).success, true)
|
|
112
|
+
assert.equal(schema.safeParse({ voiceText: 'hello', recipientId: '07958148127', channel: 'Main Whatsapp connection' }).success, true)
|
|
113
|
+
assert.equal(schema.safeParse({ action: 'message_react' }).success, true)
|
|
114
|
+
assert.equal(schema.safeParse({}).success, true)
|
|
115
|
+
assert.equal(schema.safeParse({ action: 'bogus_action' }).success, false)
|
|
116
|
+
} finally {
|
|
117
|
+
await built.cleanup()
|
|
118
|
+
}
|
|
119
|
+
})
|
|
120
|
+
|
|
121
|
+
it('loads connector_message_tool when a session only has the tool-level grant alias', async () => {
|
|
122
|
+
const built = await buildSessionTools(process.cwd(), ['connector_message_tool'], {
|
|
123
|
+
sessionId: 'connector-tool-alias-test',
|
|
124
|
+
agentId: 'default',
|
|
125
|
+
platformAssignScope: 'self',
|
|
126
|
+
})
|
|
127
|
+
|
|
128
|
+
try {
|
|
129
|
+
assert.equal(
|
|
130
|
+
built.tools.some((tool) => tool.name === 'connector_message_tool'),
|
|
131
|
+
true,
|
|
132
|
+
'connector_message_tool should load from its persisted approval alias',
|
|
133
|
+
)
|
|
134
|
+
} finally {
|
|
135
|
+
await built.cleanup()
|
|
136
|
+
}
|
|
137
|
+
})
|
|
138
|
+
})
|