@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
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import assert from 'node:assert/strict'
|
|
2
|
+
import { afterEach, test } from 'node:test'
|
|
3
|
+
|
|
4
|
+
import type { Agent, GatewayProfile } from '@/types'
|
|
5
|
+
import {
|
|
6
|
+
encryptKey,
|
|
7
|
+
loadAgents,
|
|
8
|
+
loadCredentials,
|
|
9
|
+
loadGatewayProfiles,
|
|
10
|
+
saveAgents,
|
|
11
|
+
saveCredentials,
|
|
12
|
+
saveGatewayProfiles,
|
|
13
|
+
} from './storage'
|
|
14
|
+
import { resolveGatewayConfig } from './openclaw-gateway'
|
|
15
|
+
|
|
16
|
+
const originalCredentials = loadCredentials()
|
|
17
|
+
const originalGateways = loadGatewayProfiles()
|
|
18
|
+
const originalAgents = loadAgents({ includeTrashed: true })
|
|
19
|
+
|
|
20
|
+
afterEach(() => {
|
|
21
|
+
saveCredentials(originalCredentials)
|
|
22
|
+
saveGatewayProfiles(originalGateways)
|
|
23
|
+
saveAgents(originalAgents)
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
function saveGatewayCredential(id: string, token: string) {
|
|
27
|
+
const credentials = loadCredentials()
|
|
28
|
+
credentials[id] = {
|
|
29
|
+
id,
|
|
30
|
+
provider: 'openclaw',
|
|
31
|
+
name: `Credential ${id}`,
|
|
32
|
+
encryptedKey: encryptKey(token),
|
|
33
|
+
createdAt: Date.now(),
|
|
34
|
+
}
|
|
35
|
+
saveCredentials(credentials)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function saveGatewayProfile(profile: GatewayProfile) {
|
|
39
|
+
const gateways = loadGatewayProfiles()
|
|
40
|
+
gateways[profile.id] = profile
|
|
41
|
+
saveGatewayProfiles(gateways)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
test('resolveGatewayConfig uses the gateway profile wsUrl and decrypted token', () => {
|
|
45
|
+
saveGatewayCredential('openclaw-cred-1', 'gateway-token-1')
|
|
46
|
+
saveGatewayProfile({
|
|
47
|
+
id: 'gateway-profile-1',
|
|
48
|
+
name: 'Gateway 1',
|
|
49
|
+
provider: 'openclaw',
|
|
50
|
+
endpoint: 'http://127.0.0.1:19161/v1',
|
|
51
|
+
wsUrl: 'ws://127.0.0.1:19161',
|
|
52
|
+
credentialId: 'openclaw-cred-1',
|
|
53
|
+
status: 'healthy',
|
|
54
|
+
notes: null,
|
|
55
|
+
tags: ['smoke'],
|
|
56
|
+
lastError: null,
|
|
57
|
+
lastCheckedAt: null,
|
|
58
|
+
lastModelCount: null,
|
|
59
|
+
discoveredHost: '127.0.0.1',
|
|
60
|
+
discoveredPort: 19161,
|
|
61
|
+
deployment: null,
|
|
62
|
+
stats: null,
|
|
63
|
+
isDefault: true,
|
|
64
|
+
createdAt: Date.now(),
|
|
65
|
+
updatedAt: Date.now(),
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
const resolved = resolveGatewayConfig({ profileId: 'gateway-profile-1' })
|
|
69
|
+
assert.deepEqual(resolved, {
|
|
70
|
+
key: 'profile:gateway-profile-1',
|
|
71
|
+
profileId: 'gateway-profile-1',
|
|
72
|
+
wsUrl: 'ws://127.0.0.1:19161',
|
|
73
|
+
token: 'gateway-token-1',
|
|
74
|
+
})
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
test('resolveGatewayConfig follows an OpenClaw agent route back to its gateway profile credential', () => {
|
|
78
|
+
saveGatewayCredential('openclaw-cred-2', 'gateway-token-2')
|
|
79
|
+
saveGatewayProfile({
|
|
80
|
+
id: 'gateway-profile-2',
|
|
81
|
+
name: 'Gateway 2',
|
|
82
|
+
provider: 'openclaw',
|
|
83
|
+
endpoint: 'http://127.0.0.1:19181/v1',
|
|
84
|
+
wsUrl: 'ws://127.0.0.1:19181',
|
|
85
|
+
credentialId: 'openclaw-cred-2',
|
|
86
|
+
status: 'healthy',
|
|
87
|
+
notes: null,
|
|
88
|
+
tags: ['smoke'],
|
|
89
|
+
lastError: null,
|
|
90
|
+
lastCheckedAt: null,
|
|
91
|
+
lastModelCount: null,
|
|
92
|
+
discoveredHost: '127.0.0.1',
|
|
93
|
+
discoveredPort: 19181,
|
|
94
|
+
deployment: {
|
|
95
|
+
method: 'local',
|
|
96
|
+
managedBy: 'swarmclaw',
|
|
97
|
+
useCase: 'local-dev',
|
|
98
|
+
localInstanceId: 'smoke-app-b',
|
|
99
|
+
localPort: 19181,
|
|
100
|
+
},
|
|
101
|
+
stats: null,
|
|
102
|
+
isDefault: true,
|
|
103
|
+
createdAt: Date.now(),
|
|
104
|
+
updatedAt: Date.now(),
|
|
105
|
+
})
|
|
106
|
+
|
|
107
|
+
const agents = loadAgents({ includeTrashed: true })
|
|
108
|
+
agents['openclaw-agent-1'] = {
|
|
109
|
+
id: 'openclaw-agent-1',
|
|
110
|
+
name: 'Gateway Agent',
|
|
111
|
+
description: '',
|
|
112
|
+
systemPrompt: '',
|
|
113
|
+
provider: 'openclaw',
|
|
114
|
+
model: 'default',
|
|
115
|
+
credentialId: null,
|
|
116
|
+
fallbackCredentialIds: [],
|
|
117
|
+
apiEndpoint: null,
|
|
118
|
+
gatewayProfileId: 'gateway-profile-2',
|
|
119
|
+
createdAt: Date.now(),
|
|
120
|
+
updatedAt: Date.now(),
|
|
121
|
+
} as Agent
|
|
122
|
+
saveAgents(agents)
|
|
123
|
+
|
|
124
|
+
const resolved = resolveGatewayConfig({ agentId: 'openclaw-agent-1' })
|
|
125
|
+
assert.deepEqual(resolved, {
|
|
126
|
+
key: 'profile:gateway-profile-2',
|
|
127
|
+
profileId: 'gateway-profile-2',
|
|
128
|
+
wsUrl: 'ws://127.0.0.1:19181',
|
|
129
|
+
token: 'gateway-token-2',
|
|
130
|
+
})
|
|
131
|
+
})
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { WebSocket } from 'ws'
|
|
2
2
|
import { randomUUID } from 'crypto'
|
|
3
3
|
import { wsConnect, buildOpenClawConnectParams } from '../providers/openclaw'
|
|
4
|
+
import { deriveOpenClawWsUrl } from '@/lib/openclaw-endpoint'
|
|
4
5
|
import { loadAgents, loadCredentials, decryptKey } from './storage'
|
|
5
6
|
import { notify, notifyWithPayload } from './ws-hub'
|
|
6
7
|
import { getGatewayProfile, getGatewayProfiles, resolvePrimaryAgentRoute } from './agent-runtime-config'
|
|
@@ -71,7 +72,7 @@ export function resolveGatewayConfig(target?: {
|
|
|
71
72
|
return {
|
|
72
73
|
key: `profile:${profile.id}`,
|
|
73
74
|
profileId: profile.id,
|
|
74
|
-
wsUrl: profile.wsUrl ? normalizeWsUrl(profile.wsUrl) :
|
|
75
|
+
wsUrl: profile.wsUrl ? normalizeWsUrl(profile.wsUrl) : deriveOpenClawWsUrl(profile.endpoint),
|
|
75
76
|
token: resolveTokenForCredential(profile.credentialId),
|
|
76
77
|
}
|
|
77
78
|
}
|
|
@@ -82,10 +83,15 @@ export function resolveGatewayConfig(target?: {
|
|
|
82
83
|
const agent = agents[agentId]
|
|
83
84
|
const route = resolvePrimaryAgentRoute(agent)
|
|
84
85
|
if (route?.provider === 'openclaw') {
|
|
86
|
+
const routeProfile = route.gatewayProfileId ? getGatewayProfile(route.gatewayProfileId) : null
|
|
85
87
|
return {
|
|
86
88
|
key: route.gatewayProfileId ? `profile:${route.gatewayProfileId}` : `agent:${agentId}`,
|
|
87
89
|
profileId: route.gatewayProfileId ?? null,
|
|
88
|
-
wsUrl:
|
|
90
|
+
wsUrl: routeProfile?.wsUrl
|
|
91
|
+
? normalizeWsUrl(routeProfile.wsUrl)
|
|
92
|
+
: route.apiEndpoint
|
|
93
|
+
? deriveOpenClawWsUrl(route.apiEndpoint)
|
|
94
|
+
: 'ws://127.0.0.1:18789',
|
|
89
95
|
token: resolveTokenForCredential(route.credentialId),
|
|
90
96
|
}
|
|
91
97
|
}
|
|
@@ -97,7 +103,7 @@ export function resolveGatewayConfig(target?: {
|
|
|
97
103
|
return {
|
|
98
104
|
key: `profile:${profile.id}`,
|
|
99
105
|
profileId: profile.id,
|
|
100
|
-
wsUrl: profile.wsUrl ? normalizeWsUrl(profile.wsUrl) :
|
|
106
|
+
wsUrl: profile.wsUrl ? normalizeWsUrl(profile.wsUrl) : deriveOpenClawWsUrl(profile.endpoint),
|
|
101
107
|
token: resolveTokenForCredential(profile.credentialId),
|
|
102
108
|
}
|
|
103
109
|
}
|
|
@@ -106,7 +112,7 @@ export function resolveGatewayConfig(target?: {
|
|
|
106
112
|
for (const agent of Object.values(agents)) {
|
|
107
113
|
if (agent?.provider !== 'openclaw') continue
|
|
108
114
|
const wsUrl = agent.apiEndpoint
|
|
109
|
-
?
|
|
115
|
+
? deriveOpenClawWsUrl(agent.apiEndpoint)
|
|
110
116
|
: 'ws://127.0.0.1:18789'
|
|
111
117
|
return {
|
|
112
118
|
key: `agent:${agent.id}`,
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import assert from 'node:assert/strict'
|
|
2
|
+
import { test } from 'node:test'
|
|
3
|
+
|
|
4
|
+
import { resolveOpenClawHttpProbeStatus } from './openclaw-health'
|
|
5
|
+
|
|
6
|
+
test('treats missing /v1/models as optional when chat completions works', () => {
|
|
7
|
+
const result = resolveOpenClawHttpProbeStatus({
|
|
8
|
+
modelsStatus: 404,
|
|
9
|
+
chatStatus: 200,
|
|
10
|
+
warnings: ['OpenAI-compatible models endpoint failed: OpenClaw endpoint path is invalid (404).'],
|
|
11
|
+
warningHint: 'Point to the gateway root/ws URL and let SwarmClaw normalize it, or use an explicit /v1 endpoint.',
|
|
12
|
+
})
|
|
13
|
+
|
|
14
|
+
assert.equal(result.httpCompatible, true)
|
|
15
|
+
assert.equal(result.modelsEndpointOptional, true)
|
|
16
|
+
assert.equal(result.warning, undefined)
|
|
17
|
+
assert.equal(result.hint, undefined)
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
test('keeps chat failures as real warnings even when /v1/models is missing', () => {
|
|
21
|
+
const result = resolveOpenClawHttpProbeStatus({
|
|
22
|
+
modelsStatus: 404,
|
|
23
|
+
chatStatus: 500,
|
|
24
|
+
warnings: [
|
|
25
|
+
'OpenAI-compatible models endpoint failed: OpenClaw endpoint path is invalid (404).',
|
|
26
|
+
'OpenAI-compatible chat endpoint failed: OpenClaw endpoint returned HTTP 500.',
|
|
27
|
+
],
|
|
28
|
+
warningHint: 'Ensure this is an OpenAI-compatible chat endpoint exposed by the OpenClaw gateway.',
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
assert.equal(result.httpCompatible, false)
|
|
32
|
+
assert.equal(result.modelsEndpointOptional, false)
|
|
33
|
+
assert.match(result.warning || '', /chat endpoint failed/i)
|
|
34
|
+
assert.equal(result.hint, 'Ensure this is an OpenAI-compatible chat endpoint exposed by the OpenClaw gateway.')
|
|
35
|
+
})
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { deriveOpenClawWsUrl, normalizeOpenClawEndpoint } from '@/lib/openclaw-endpoint'
|
|
2
|
+
import { wsConnect } from '@/lib/providers/openclaw'
|
|
2
3
|
import { decryptKey, loadCredentials } from './storage'
|
|
3
4
|
|
|
4
5
|
export interface OpenClawHealthInput {
|
|
@@ -13,20 +14,49 @@ export interface OpenClawHealthResult {
|
|
|
13
14
|
ok: boolean
|
|
14
15
|
endpoint: string
|
|
15
16
|
wsUrl: string
|
|
17
|
+
wsConnected: boolean
|
|
18
|
+
httpCompatible: boolean | null
|
|
16
19
|
authProvided: boolean
|
|
17
20
|
model: string | null
|
|
18
21
|
models: string[]
|
|
19
22
|
modelsStatus: number | null
|
|
20
23
|
chatStatus: number | null
|
|
24
|
+
message: string
|
|
21
25
|
completionSample?: string
|
|
26
|
+
warning?: string
|
|
22
27
|
error?: string
|
|
23
28
|
hint?: string
|
|
24
29
|
}
|
|
25
30
|
|
|
31
|
+
export interface OpenClawHttpProbeStatus {
|
|
32
|
+
httpCompatible: boolean
|
|
33
|
+
warning?: string
|
|
34
|
+
hint?: string
|
|
35
|
+
modelsEndpointOptional: boolean
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
type JsonRecord = Record<string, unknown>
|
|
39
|
+
|
|
26
40
|
function normalizeToken(value: unknown): string | null {
|
|
27
41
|
return typeof value === 'string' && value.trim() ? value.trim() : null
|
|
28
42
|
}
|
|
29
43
|
|
|
44
|
+
function asRecord(value: unknown): JsonRecord | null {
|
|
45
|
+
return value && typeof value === 'object' && !Array.isArray(value) ? value as JsonRecord : null
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function getErrorName(err: unknown): string | undefined {
|
|
49
|
+
if (err instanceof Error) return err.name
|
|
50
|
+
const record = asRecord(err)
|
|
51
|
+
return typeof record?.name === 'string' ? record.name : undefined
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function getErrorMessage(err: unknown): string | undefined {
|
|
55
|
+
if (err instanceof Error) return err.message
|
|
56
|
+
const record = asRecord(err)
|
|
57
|
+
return typeof record?.message === 'string' ? record.message : undefined
|
|
58
|
+
}
|
|
59
|
+
|
|
30
60
|
function resolveCredentialToken(credentialId?: string | null): string | null {
|
|
31
61
|
const id = normalizeToken(credentialId)
|
|
32
62
|
if (!id) return null
|
|
@@ -40,21 +70,30 @@ function resolveCredentialToken(credentialId?: string | null): string | null {
|
|
|
40
70
|
}
|
|
41
71
|
}
|
|
42
72
|
|
|
43
|
-
function extractModels(payload:
|
|
44
|
-
const
|
|
73
|
+
function extractModels(payload: unknown): string[] {
|
|
74
|
+
const payloadRecord = asRecord(payload)
|
|
75
|
+
const models = Array.isArray(payloadRecord?.data) ? payloadRecord.data : []
|
|
45
76
|
return models
|
|
46
|
-
.map((item
|
|
77
|
+
.map((item) => {
|
|
78
|
+
const record = asRecord(item)
|
|
79
|
+
return typeof record?.id === 'string' ? record.id.trim() : ''
|
|
80
|
+
})
|
|
47
81
|
.filter(Boolean)
|
|
48
82
|
}
|
|
49
83
|
|
|
50
|
-
function extractChatText(payload:
|
|
51
|
-
const
|
|
84
|
+
function extractChatText(payload: unknown): string {
|
|
85
|
+
const payloadRecord = asRecord(payload)
|
|
86
|
+
const choices = Array.isArray(payloadRecord?.choices) ? payloadRecord.choices : []
|
|
87
|
+
const firstChoice = asRecord(choices[0])
|
|
88
|
+
const message = asRecord(firstChoice?.message)
|
|
89
|
+
const content = message?.content
|
|
52
90
|
if (typeof content === 'string') return content.trim()
|
|
53
91
|
if (Array.isArray(content)) {
|
|
54
92
|
return content
|
|
55
|
-
.map((block
|
|
56
|
-
|
|
57
|
-
if (typeof
|
|
93
|
+
.map((block) => {
|
|
94
|
+
const record = asRecord(block)
|
|
95
|
+
if (typeof record?.text === 'string') return record.text
|
|
96
|
+
if (typeof record?.content === 'string') return record.content
|
|
58
97
|
return ''
|
|
59
98
|
})
|
|
60
99
|
.join(' ')
|
|
@@ -87,9 +126,105 @@ function describeHttpError(status: number): { error: string; hint?: string } {
|
|
|
87
126
|
}
|
|
88
127
|
}
|
|
89
128
|
|
|
129
|
+
function describeGatewayError(errorCode: string | undefined, message: string): { error: string; hint?: string } {
|
|
130
|
+
if (errorCode === 'AUTH_TOKEN_MISSING') {
|
|
131
|
+
return {
|
|
132
|
+
error: message || 'OpenClaw gateway requires a token.',
|
|
133
|
+
hint: 'Attach an OpenClaw credential or token before running the gateway health check.',
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
if (errorCode === 'AUTH_TOKEN_INVALID') {
|
|
137
|
+
return {
|
|
138
|
+
error: message || 'OpenClaw gateway rejected the supplied token.',
|
|
139
|
+
hint: 'Update the saved OpenClaw token or re-pair this gateway with a valid operator token.',
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
if (errorCode === 'PAIRING_REQUIRED') {
|
|
143
|
+
return {
|
|
144
|
+
error: message || 'OpenClaw gateway requires device pairing.',
|
|
145
|
+
hint: 'Approve this SwarmClaw device in the OpenClaw gateway before using it from the app.',
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
if (errorCode === 'DEVICE_AUTH_INVALID') {
|
|
149
|
+
return {
|
|
150
|
+
error: message || 'OpenClaw gateway rejected the saved device identity.',
|
|
151
|
+
hint: 'Re-pair this SwarmClaw device with the gateway or reset the saved device identity and try again.',
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
return {
|
|
155
|
+
error: message || 'Failed to connect to OpenClaw gateway.',
|
|
156
|
+
hint: 'Verify the OpenClaw gateway is running and reachable at this host/port.',
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function pushIssue(issues: string[], next: string | undefined): void {
|
|
161
|
+
if (typeof next !== 'string') return
|
|
162
|
+
const value = next.trim()
|
|
163
|
+
if (!value) return
|
|
164
|
+
issues.push(value)
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
function isModelsEndpointWarning(issue: string): boolean {
|
|
168
|
+
return issue.startsWith('OpenAI-compatible models endpoint failed:')
|
|
169
|
+
|| issue.startsWith('OpenAI-compatible models probe timed out')
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
export function resolveOpenClawHttpProbeStatus(input: {
|
|
173
|
+
modelsStatus: number | null
|
|
174
|
+
chatStatus: number | null
|
|
175
|
+
warnings: string[]
|
|
176
|
+
warningHint?: string
|
|
177
|
+
}): OpenClawHttpProbeStatus {
|
|
178
|
+
const modelsOk = !!input.modelsStatus && input.modelsStatus >= 200 && input.modelsStatus < 300
|
|
179
|
+
const chatOk = !!input.chatStatus && input.chatStatus >= 200 && input.chatStatus < 300
|
|
180
|
+
const modelsEndpointOptional = chatOk && input.modelsStatus === 404
|
|
181
|
+
const filteredWarnings = modelsEndpointOptional
|
|
182
|
+
? input.warnings.filter((issue) => !isModelsEndpointWarning(issue))
|
|
183
|
+
: input.warnings
|
|
184
|
+
const warning = filteredWarnings.join(' ') || undefined
|
|
185
|
+
|
|
186
|
+
return {
|
|
187
|
+
httpCompatible: chatOk && (modelsOk || modelsEndpointOptional),
|
|
188
|
+
warning,
|
|
189
|
+
hint: warning ? input.warningHint : undefined,
|
|
190
|
+
modelsEndpointOptional,
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
function summarizeOpenClawHealth(input: {
|
|
195
|
+
ok: boolean
|
|
196
|
+
models: string[]
|
|
197
|
+
modelsStatus: number | null
|
|
198
|
+
httpCompatible: boolean | null
|
|
199
|
+
modelsEndpointOptional?: boolean
|
|
200
|
+
warning?: string
|
|
201
|
+
error?: string
|
|
202
|
+
}): string {
|
|
203
|
+
if (!input.ok) return input.error || 'OpenClaw gateway health check failed.'
|
|
204
|
+
const parts = ['Connected to OpenClaw gateway via WebSocket.']
|
|
205
|
+
if (input.modelsStatus && input.modelsStatus >= 200 && input.modelsStatus < 300) {
|
|
206
|
+
parts.push(
|
|
207
|
+
input.models.length > 0
|
|
208
|
+
? `${input.models.length} model${input.models.length === 1 ? '' : 's'} visible.`
|
|
209
|
+
: 'HTTP models endpoint responded with no models.',
|
|
210
|
+
)
|
|
211
|
+
}
|
|
212
|
+
if (input.httpCompatible === true) {
|
|
213
|
+
if (input.modelsEndpointOptional) {
|
|
214
|
+
parts.push('OpenAI-compatible chat checks passed. This gateway does not advertise `/v1/models`, which is acceptable for OpenClaw.')
|
|
215
|
+
} else {
|
|
216
|
+
parts.push('OpenAI-compatible HTTP checks passed.')
|
|
217
|
+
}
|
|
218
|
+
} else if (input.warning) {
|
|
219
|
+
parts.push(input.warning)
|
|
220
|
+
parts.push('SwarmClaw can still use this gateway over WebSocket.')
|
|
221
|
+
}
|
|
222
|
+
return parts.join(' ')
|
|
223
|
+
}
|
|
224
|
+
|
|
90
225
|
function createTimeoutError(message: string): Error {
|
|
91
|
-
const timeoutErr = new Error(message)
|
|
92
|
-
|
|
226
|
+
const timeoutErr = new Error(message) as Error & { name: string }
|
|
227
|
+
timeoutErr.name = 'TimeoutError'
|
|
93
228
|
return timeoutErr
|
|
94
229
|
}
|
|
95
230
|
|
|
@@ -116,7 +251,7 @@ async function withTimeout<T>(promise: Promise<T>, timeoutMs: number, onTimeout?
|
|
|
116
251
|
}
|
|
117
252
|
}
|
|
118
253
|
|
|
119
|
-
async function fetchJsonWithTimeout(url: string, init: RequestInit, timeoutMs: number): Promise<{ response: Response; body:
|
|
254
|
+
async function fetchJsonWithTimeout(url: string, init: RequestInit, timeoutMs: number): Promise<{ response: Response; body: unknown }> {
|
|
120
255
|
const controller = new AbortController()
|
|
121
256
|
try {
|
|
122
257
|
const response = await withTimeout(
|
|
@@ -131,7 +266,7 @@ async function fetchJsonWithTimeout(url: string, init: RequestInit, timeoutMs: n
|
|
|
131
266
|
() => controller.abort(),
|
|
132
267
|
`Response read timed out after ${timeoutMs}ms`,
|
|
133
268
|
)
|
|
134
|
-
let body:
|
|
269
|
+
let body: unknown = {}
|
|
135
270
|
if (text) {
|
|
136
271
|
try {
|
|
137
272
|
body = JSON.parse(text)
|
|
@@ -140,8 +275,8 @@ async function fetchJsonWithTimeout(url: string, init: RequestInit, timeoutMs: n
|
|
|
140
275
|
}
|
|
141
276
|
}
|
|
142
277
|
return { response, body }
|
|
143
|
-
} catch (err:
|
|
144
|
-
if (err
|
|
278
|
+
} catch (err: unknown) {
|
|
279
|
+
if (getErrorName(err) === 'AbortError') throw createTimeoutError(`Request timed out after ${timeoutMs}ms`)
|
|
145
280
|
throw err
|
|
146
281
|
}
|
|
147
282
|
}
|
|
@@ -163,8 +298,31 @@ export async function probeOpenClawHealth(input: OpenClawHealthInput): Promise<O
|
|
|
163
298
|
let modelsStatus: number | null = null
|
|
164
299
|
let chatStatus: number | null = null
|
|
165
300
|
let completionSample = ''
|
|
166
|
-
let
|
|
167
|
-
|
|
301
|
+
let warningHint: string | undefined
|
|
302
|
+
const warnings: string[] = []
|
|
303
|
+
|
|
304
|
+
const wsResult = await wsConnect(wsUrl, token || undefined, true, timeoutMs)
|
|
305
|
+
if (wsResult.ws) {
|
|
306
|
+
try { wsResult.ws.close() } catch { /* noop */ }
|
|
307
|
+
}
|
|
308
|
+
if (!wsResult.ok) {
|
|
309
|
+
const gatewayError = describeGatewayError(wsResult.errorCode, wsResult.message)
|
|
310
|
+
return {
|
|
311
|
+
ok: false,
|
|
312
|
+
endpoint,
|
|
313
|
+
wsUrl,
|
|
314
|
+
wsConnected: false,
|
|
315
|
+
httpCompatible: null,
|
|
316
|
+
authProvided,
|
|
317
|
+
model: null,
|
|
318
|
+
models: [],
|
|
319
|
+
modelsStatus: null,
|
|
320
|
+
chatStatus: null,
|
|
321
|
+
message: gatewayError.error,
|
|
322
|
+
error: gatewayError.error,
|
|
323
|
+
hint: gatewayError.hint,
|
|
324
|
+
}
|
|
325
|
+
}
|
|
168
326
|
|
|
169
327
|
try {
|
|
170
328
|
const { response: modelsRes, body } = await fetchJsonWithTimeout(`${endpoint}/models`, {
|
|
@@ -176,27 +334,17 @@ export async function probeOpenClawHealth(input: OpenClawHealthInput): Promise<O
|
|
|
176
334
|
models = extractModels(body)
|
|
177
335
|
} else {
|
|
178
336
|
const err = describeHttpError(modelsRes.status)
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
}
|
|
182
|
-
} catch (err: any) {
|
|
183
|
-
if (err?.name === 'TimeoutError') {
|
|
184
|
-
lastError = `OpenClaw models probe timed out after ${timeoutMs}ms.`
|
|
185
|
-
} else {
|
|
186
|
-
lastError = err?.message || 'Failed to connect to OpenClaw endpoint.'
|
|
187
|
-
}
|
|
188
|
-
return {
|
|
189
|
-
ok: false,
|
|
190
|
-
endpoint,
|
|
191
|
-
wsUrl,
|
|
192
|
-
authProvided,
|
|
193
|
-
model: null,
|
|
194
|
-
models: [],
|
|
195
|
-
modelsStatus: null,
|
|
196
|
-
chatStatus: null,
|
|
197
|
-
error: lastError,
|
|
198
|
-
hint: 'Verify the OpenClaw gateway is running and reachable at this host/port.',
|
|
337
|
+
pushIssue(warnings, `OpenAI-compatible models endpoint failed: ${err.error}`)
|
|
338
|
+
warningHint = err.hint || warningHint
|
|
199
339
|
}
|
|
340
|
+
} catch (err: unknown) {
|
|
341
|
+
pushIssue(
|
|
342
|
+
warnings,
|
|
343
|
+
getErrorName(err) === 'TimeoutError'
|
|
344
|
+
? `OpenAI-compatible models probe timed out after ${timeoutMs}ms.`
|
|
345
|
+
: (getErrorMessage(err) || 'Failed to connect to the OpenAI-compatible models endpoint.'),
|
|
346
|
+
)
|
|
347
|
+
warningHint = 'The gateway is reachable, but the optional HTTP `/v1/models` endpoint did not respond normally.'
|
|
200
348
|
}
|
|
201
349
|
|
|
202
350
|
const model = normalizeToken(input.model) || models[0] || 'default'
|
|
@@ -216,30 +364,50 @@ export async function probeOpenClawHealth(input: OpenClawHealthInput): Promise<O
|
|
|
216
364
|
chatStatus = chatRes.status
|
|
217
365
|
if (!chatRes.ok) {
|
|
218
366
|
const err = describeHttpError(chatRes.status)
|
|
219
|
-
|
|
220
|
-
|
|
367
|
+
pushIssue(warnings, `OpenAI-compatible chat endpoint failed: ${err.error}`)
|
|
368
|
+
warningHint = err.hint || warningHint
|
|
221
369
|
} else {
|
|
222
370
|
completionSample = extractChatText(body).slice(0, 240)
|
|
223
371
|
}
|
|
224
|
-
} catch (err:
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
372
|
+
} catch (err: unknown) {
|
|
373
|
+
pushIssue(
|
|
374
|
+
warnings,
|
|
375
|
+
getErrorName(err) === 'TimeoutError'
|
|
376
|
+
? `OpenAI-compatible chat probe timed out after ${timeoutMs}ms.`
|
|
377
|
+
: (getErrorMessage(err) || 'OpenAI-compatible chat probe failed.'),
|
|
378
|
+
)
|
|
379
|
+
warningHint = warningHint || 'The gateway is reachable, but the optional HTTP `/v1/chat/completions` endpoint did not respond normally.'
|
|
230
380
|
}
|
|
231
381
|
|
|
382
|
+
const http = resolveOpenClawHttpProbeStatus({
|
|
383
|
+
modelsStatus,
|
|
384
|
+
chatStatus,
|
|
385
|
+
warnings,
|
|
386
|
+
warningHint,
|
|
387
|
+
})
|
|
388
|
+
const message = summarizeOpenClawHealth({
|
|
389
|
+
ok: true,
|
|
390
|
+
models,
|
|
391
|
+
modelsStatus,
|
|
392
|
+
httpCompatible: http.httpCompatible,
|
|
393
|
+
modelsEndpointOptional: http.modelsEndpointOptional,
|
|
394
|
+
warning: http.warning,
|
|
395
|
+
})
|
|
396
|
+
|
|
232
397
|
return {
|
|
233
|
-
ok:
|
|
398
|
+
ok: true,
|
|
234
399
|
endpoint,
|
|
235
400
|
wsUrl,
|
|
401
|
+
wsConnected: true,
|
|
402
|
+
httpCompatible: http.httpCompatible,
|
|
236
403
|
authProvided,
|
|
237
404
|
model,
|
|
238
405
|
models,
|
|
239
406
|
modelsStatus,
|
|
240
407
|
chatStatus,
|
|
408
|
+
message,
|
|
241
409
|
completionSample: completionSample || undefined,
|
|
242
|
-
|
|
243
|
-
hint:
|
|
410
|
+
warning: http.warning,
|
|
411
|
+
hint: http.hint,
|
|
244
412
|
}
|
|
245
413
|
}
|
|
@@ -115,6 +115,7 @@ async function executeSubTaskViaCli(agent: Agent, task: string, parentSessionId:
|
|
|
115
115
|
claudeCode: null,
|
|
116
116
|
codex: null,
|
|
117
117
|
opencode: null,
|
|
118
|
+
gemini: null,
|
|
118
119
|
},
|
|
119
120
|
messages: [],
|
|
120
121
|
createdAt: Date.now(),
|
|
@@ -398,7 +399,7 @@ export async function executeLangGraphOrchestrator(
|
|
|
398
399
|
|
|
399
400
|
const checkpointSaver = getCheckpointSaver()
|
|
400
401
|
const isStrictMode = settings.capabilityPolicyMode === 'strict'
|
|
401
|
-
const approvalInterruptsEnabled = isStrictMode && settings.approvalsEnabled
|
|
402
|
+
const approvalInterruptsEnabled = isStrictMode && settings.approvalsEnabled === true
|
|
402
403
|
const allTools = [delegateTool, storeMemoryTool, searchMemoryTool, getSecretTool, commentOnTaskTool, createTaskTool, markCompleteTool]
|
|
403
404
|
const llmWithTools = llm.bindTools(allTools)
|
|
404
405
|
const toolNode = new ToolNode(allTools)
|
|
@@ -762,7 +763,7 @@ export async function resumeLangGraphOrchestrator(
|
|
|
762
763
|
const checkpointSaver = getCheckpointSaver()
|
|
763
764
|
const settings = loadSettings()
|
|
764
765
|
const isStrictMode = settings.capabilityPolicyMode === 'strict'
|
|
765
|
-
const approvalInterruptsEnabled = isStrictMode && settings.approvalsEnabled
|
|
766
|
+
const approvalInterruptsEnabled = isStrictMode && settings.approvalsEnabled === true
|
|
766
767
|
|
|
767
768
|
const allTools = [delegateTool, storeMemoryTool, searchMemoryTool, getSecretTool, commentOnTaskTool, createTaskTool, markCompleteTool]
|
|
768
769
|
const llmWithTools = llm.bindTools(allTools)
|
|
@@ -47,6 +47,7 @@ export function createOrchestratorSession(
|
|
|
47
47
|
claudeCode: null,
|
|
48
48
|
codex: null,
|
|
49
49
|
opencode: null,
|
|
50
|
+
gemini: null,
|
|
50
51
|
},
|
|
51
52
|
messages: [] as any[],
|
|
52
53
|
createdAt: Date.now(),
|
|
@@ -294,6 +295,7 @@ async function executeSubTask(
|
|
|
294
295
|
claudeCode: null,
|
|
295
296
|
codex: null,
|
|
296
297
|
opencode: null,
|
|
298
|
+
gemini: null,
|
|
297
299
|
},
|
|
298
300
|
messages: [] as any[],
|
|
299
301
|
createdAt: Date.now(),
|