@swarmclawai/swarmclaw 0.7.1 → 0.7.3
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 +155 -150
- package/package.json +1 -1
- package/src/app/api/agents/[id]/route.ts +26 -0
- package/src/app/api/agents/[id]/thread/route.ts +37 -9
- package/src/app/api/agents/route.ts +13 -2
- package/src/app/api/auth/route.ts +76 -7
- package/src/app/api/chatrooms/[id]/chat/route.ts +7 -2
- package/src/app/api/{sessions → chats}/[id]/browser/route.ts +5 -1
- package/src/app/api/{sessions → chats}/[id]/chat/route.ts +7 -3
- package/src/app/api/{sessions → chats}/[id]/checkpoints/route.ts +1 -1
- package/src/app/api/chats/[id]/main-loop/route.ts +13 -0
- package/src/app/api/{sessions → chats}/[id]/messages/route.ts +19 -13
- package/src/app/api/{sessions → chats}/[id]/restore/route.ts +1 -1
- package/src/app/api/{sessions → chats}/[id]/route.ts +22 -52
- package/src/app/api/{sessions → chats}/[id]/stop/route.ts +6 -1
- package/src/app/api/{sessions → chats}/route.ts +21 -7
- package/src/app/api/connectors/[id]/doctor/route.ts +26 -0
- package/src/app/api/connectors/doctor/route.ts +13 -0
- package/src/app/api/files/open/route.ts +16 -14
- package/src/app/api/memory/maintenance/route.ts +11 -1
- package/src/app/api/openclaw/agent-files/route.ts +27 -4
- package/src/app/api/openclaw/skills/route.ts +11 -3
- package/src/app/api/plugins/dependencies/route.ts +24 -0
- package/src/app/api/plugins/install/route.ts +15 -92
- package/src/app/api/plugins/route.ts +6 -26
- package/src/app/api/plugins/settings/route.ts +40 -0
- package/src/app/api/plugins/ui/route.ts +1 -0
- package/src/app/api/settings/route.ts +49 -7
- package/src/app/api/tasks/[id]/route.ts +15 -6
- package/src/app/api/tasks/bulk/route.ts +2 -2
- package/src/app/api/tasks/route.ts +9 -4
- package/src/app/api/usage/route.ts +30 -0
- package/src/app/api/webhooks/[id]/route.ts +8 -1
- package/src/app/page.tsx +9 -2
- package/src/cli/index.js +39 -33
- package/src/cli/index.ts +43 -49
- package/src/cli/spec.js +29 -27
- package/src/components/agents/agent-card.tsx +16 -13
- package/src/components/agents/agent-chat-list.tsx +104 -4
- package/src/components/agents/agent-list.tsx +54 -22
- package/src/components/agents/agent-sheet.tsx +209 -18
- package/src/components/agents/cron-job-form.tsx +3 -3
- package/src/components/agents/inspector-panel.tsx +110 -50
- package/src/components/auth/access-key-gate.tsx +36 -97
- package/src/components/auth/setup-wizard.tsx +5 -38
- package/src/components/chat/chat-area.tsx +39 -27
- package/src/components/{sessions/session-card.tsx → chat/chat-card.tsx} +7 -23
- package/src/components/chat/chat-header.tsx +299 -314
- package/src/components/{sessions/session-list.tsx → chat/chat-list.tsx} +11 -14
- package/src/components/chat/chat-tool-toggles.tsx +26 -17
- package/src/components/chat/checkpoint-timeline.tsx +4 -4
- package/src/components/chat/message-bubble.tsx +4 -1
- package/src/components/chat/message-list.tsx +5 -3
- package/src/components/chat/session-debug-panel.tsx +1 -1
- package/src/components/chat/tool-request-banner.tsx +3 -3
- package/src/components/chatrooms/agent-hover-card.tsx +3 -3
- package/src/components/chatrooms/chatroom-tool-request-banner.tsx +2 -2
- package/src/components/chatrooms/chatroom-view.tsx +347 -205
- package/src/components/connectors/connector-list.tsx +265 -127
- package/src/components/connectors/connector-sheet.tsx +218 -1
- package/src/components/home/home-view.tsx +129 -5
- package/src/components/layout/app-layout.tsx +392 -182
- package/src/components/layout/mobile-header.tsx +26 -8
- package/src/components/plugins/plugin-list.tsx +487 -254
- package/src/components/plugins/plugin-sheet.tsx +236 -13
- package/src/components/projects/project-detail.tsx +183 -0
- package/src/components/settings/gateway-connection-panel.tsx +1 -1
- package/src/components/shared/agent-picker-list.tsx +2 -2
- package/src/components/shared/command-palette.tsx +111 -25
- package/src/components/shared/settings/plugin-manager.tsx +20 -4
- package/src/components/shared/settings/section-capability-policy.tsx +105 -0
- package/src/components/shared/settings/section-heartbeat.tsx +78 -1
- package/src/components/shared/settings/section-orchestrator.tsx +3 -3
- package/src/components/shared/settings/section-providers.tsx +1 -1
- package/src/components/shared/settings/section-runtime-loop.tsx +5 -5
- package/src/components/shared/settings/section-secrets.tsx +6 -6
- package/src/components/shared/settings/section-user-preferences.tsx +1 -1
- package/src/components/shared/settings/section-voice.tsx +5 -1
- package/src/components/shared/settings/section-web-search.tsx +10 -2
- package/src/components/shared/settings/settings-page.tsx +244 -56
- package/src/components/tasks/approvals-panel.tsx +205 -18
- package/src/components/tasks/task-board.tsx +242 -46
- package/src/components/usage/metrics-dashboard.tsx +147 -1
- package/src/components/wallets/wallet-panel.tsx +17 -5
- package/src/components/webhooks/webhook-sheet.tsx +8 -8
- package/src/lib/auth.ts +17 -0
- package/src/lib/chat-streaming-state.test.ts +108 -0
- package/src/lib/chat-streaming-state.ts +108 -0
- package/src/lib/chat.ts +1 -1
- package/src/lib/{sessions.ts → chats.ts} +28 -18
- package/src/lib/openclaw-agent-id.test.ts +14 -0
- package/src/lib/openclaw-agent-id.ts +31 -0
- package/src/lib/providers/claude-cli.ts +1 -1
- package/src/lib/server/agent-assignment.test.ts +112 -0
- package/src/lib/server/agent-assignment.ts +169 -0
- package/src/lib/server/approval-connector-notify.test.ts +253 -0
- package/src/lib/server/approvals-auto-approve.test.ts +205 -0
- package/src/lib/server/approvals.ts +483 -75
- package/src/lib/server/autonomy-runtime.test.ts +341 -0
- package/src/lib/server/browser-state.test.ts +118 -0
- package/src/lib/server/browser-state.ts +123 -0
- package/src/lib/server/build-llm.test.ts +36 -0
- package/src/lib/server/build-llm.ts +11 -4
- package/src/lib/server/builtin-plugins.ts +34 -0
- package/src/lib/server/capability-router.ts +10 -8
- package/src/lib/server/chat-execution-heartbeat.test.ts +40 -0
- package/src/lib/server/chat-execution-tool-events.test.ts +134 -0
- package/src/lib/server/chat-execution.ts +285 -165
- package/src/lib/server/chatroom-health.test.ts +26 -0
- package/src/lib/server/chatroom-health.ts +2 -3
- package/src/lib/server/chatroom-helpers.test.ts +67 -2
- package/src/lib/server/chatroom-helpers.ts +48 -8
- package/src/lib/server/connectors/discord.ts +175 -11
- package/src/lib/server/connectors/doctor.test.ts +80 -0
- package/src/lib/server/connectors/doctor.ts +116 -0
- package/src/lib/server/connectors/manager.ts +948 -112
- package/src/lib/server/connectors/policy.test.ts +222 -0
- package/src/lib/server/connectors/policy.ts +452 -0
- package/src/lib/server/connectors/slack.ts +188 -9
- package/src/lib/server/connectors/telegram.ts +65 -15
- package/src/lib/server/connectors/thread-context.test.ts +44 -0
- package/src/lib/server/connectors/thread-context.ts +72 -0
- package/src/lib/server/connectors/types.ts +41 -11
- package/src/lib/server/cost.ts +34 -1
- package/src/lib/server/daemon-state.ts +61 -3
- package/src/lib/server/data-dir.ts +13 -0
- package/src/lib/server/delegation-jobs.test.ts +140 -0
- package/src/lib/server/delegation-jobs.ts +248 -0
- package/src/lib/server/document-utils.test.ts +47 -0
- package/src/lib/server/document-utils.ts +397 -0
- package/src/lib/server/heartbeat-service.ts +14 -40
- package/src/lib/server/heartbeat-source.test.ts +22 -0
- package/src/lib/server/heartbeat-source.ts +7 -0
- package/src/lib/server/identity-continuity.test.ts +77 -0
- package/src/lib/server/identity-continuity.ts +127 -0
- package/src/lib/server/mailbox-utils.ts +347 -0
- package/src/lib/server/main-agent-loop.ts +28 -1103
- package/src/lib/server/memory-db.ts +4 -6
- package/src/lib/server/memory-tiers.ts +40 -0
- package/src/lib/server/openclaw-agent-resolver.test.ts +70 -0
- package/src/lib/server/openclaw-agent-resolver.ts +128 -0
- package/src/lib/server/openclaw-exec-config.ts +5 -6
- package/src/lib/server/openclaw-skills-normalize.test.ts +56 -0
- package/src/lib/server/openclaw-skills-normalize.ts +136 -0
- package/src/lib/server/openclaw-sync.ts +3 -2
- package/src/lib/server/orchestrator-lg.ts +20 -9
- package/src/lib/server/orchestrator.ts +7 -7
- package/src/lib/server/playwright-proxy.mjs +27 -3
- package/src/lib/server/plugins.test.ts +207 -0
- package/src/lib/server/plugins.ts +927 -66
- package/src/lib/server/provider-health.ts +38 -6
- package/src/lib/server/queue.ts +13 -28
- package/src/lib/server/scheduler.ts +2 -0
- package/src/lib/server/session-archive-memory.test.ts +85 -0
- package/src/lib/server/session-archive-memory.ts +230 -0
- package/src/lib/server/session-mailbox.ts +8 -18
- package/src/lib/server/session-reset-policy.test.ts +99 -0
- package/src/lib/server/session-reset-policy.ts +311 -0
- package/src/lib/server/session-run-manager.ts +33 -82
- package/src/lib/server/session-tools/autonomy-tools.test.ts +105 -0
- package/src/lib/server/session-tools/calendar.ts +366 -0
- package/src/lib/server/session-tools/canvas.ts +1 -1
- package/src/lib/server/session-tools/chatroom.ts +4 -2
- package/src/lib/server/session-tools/connector.ts +114 -10
- package/src/lib/server/session-tools/context.ts +21 -5
- package/src/lib/server/session-tools/crawl.ts +447 -0
- package/src/lib/server/session-tools/crud.ts +74 -28
- package/src/lib/server/session-tools/delegate-fallback.test.ts +219 -0
- package/src/lib/server/session-tools/delegate.ts +497 -24
- package/src/lib/server/session-tools/discovery.ts +24 -6
- package/src/lib/server/session-tools/document.ts +283 -0
- package/src/lib/server/session-tools/edit_file.ts +4 -2
- package/src/lib/server/session-tools/email.ts +320 -0
- package/src/lib/server/session-tools/extract.ts +137 -0
- package/src/lib/server/session-tools/file-normalize.test.ts +93 -0
- package/src/lib/server/session-tools/file-send.test.ts +84 -1
- package/src/lib/server/session-tools/file.ts +241 -25
- package/src/lib/server/session-tools/git.ts +1 -1
- package/src/lib/server/session-tools/http.ts +1 -1
- package/src/lib/server/session-tools/human-loop.ts +227 -0
- package/src/lib/server/session-tools/image-gen.ts +380 -0
- package/src/lib/server/session-tools/index.ts +130 -50
- package/src/lib/server/session-tools/mailbox.ts +276 -0
- package/src/lib/server/session-tools/memory.ts +172 -3
- package/src/lib/server/session-tools/monitor.ts +151 -8
- package/src/lib/server/session-tools/normalize-tool-args.ts +17 -14
- package/src/lib/server/session-tools/openclaw-nodes.ts +1 -1
- package/src/lib/server/session-tools/openclaw-workspace.ts +1 -1
- package/src/lib/server/session-tools/platform-normalize.test.ts +142 -0
- package/src/lib/server/session-tools/platform.ts +148 -7
- package/src/lib/server/session-tools/plugin-creator.ts +89 -26
- package/src/lib/server/session-tools/primitive-tools.test.ts +257 -0
- package/src/lib/server/session-tools/replicate.ts +301 -0
- package/src/lib/server/session-tools/sample-ui.ts +1 -1
- package/src/lib/server/session-tools/sandbox.ts +4 -2
- package/src/lib/server/session-tools/schedule.ts +24 -12
- package/src/lib/server/session-tools/session-info.ts +43 -7
- package/src/lib/server/session-tools/session-tools-wiring.test.ts +31 -17
- package/src/lib/server/session-tools/shell.ts +5 -2
- package/src/lib/server/session-tools/subagent.ts +194 -28
- package/src/lib/server/session-tools/table.ts +587 -0
- package/src/lib/server/session-tools/wallet.ts +42 -12
- package/src/lib/server/session-tools/web-browser-config.test.ts +39 -0
- package/src/lib/server/session-tools/web.ts +926 -91
- package/src/lib/server/storage.ts +255 -16
- package/src/lib/server/stream-agent-chat.ts +116 -268
- package/src/lib/server/structured-extract.test.ts +72 -0
- package/src/lib/server/structured-extract.ts +373 -0
- package/src/lib/server/task-mention.test.ts +16 -2
- package/src/lib/server/task-mention.ts +61 -10
- package/src/lib/server/tool-aliases.ts +66 -18
- package/src/lib/server/tool-capability-policy.test.ts +9 -9
- package/src/lib/server/tool-capability-policy.ts +38 -27
- package/src/lib/server/tool-retry.ts +2 -0
- package/src/lib/server/watch-jobs.test.ts +173 -0
- package/src/lib/server/watch-jobs.ts +532 -0
- package/src/lib/server/ws-hub.ts +5 -3
- package/src/lib/tool-definitions.ts +4 -0
- package/src/lib/validation/schemas.test.ts +26 -0
- package/src/lib/validation/schemas.ts +10 -1
- package/src/lib/ws-client.ts +14 -12
- package/src/proxy.ts +5 -5
- package/src/stores/use-app-store.ts +5 -11
- package/src/stores/use-chat-store.ts +38 -9
- package/src/types/index.ts +352 -47
- package/src/app/api/sessions/[id]/main-loop/route.ts +0 -94
- package/src/components/sessions/new-session-sheet.tsx +0 -253
- package/src/lib/server/main-session.ts +0 -24
- package/src/lib/server/session-run-manager.test.ts +0 -23
- /package/src/app/api/{sessions → chats}/[id]/clear/route.ts +0 -0
- /package/src/app/api/{sessions → chats}/[id]/deploy/route.ts +0 -0
- /package/src/app/api/{sessions → chats}/[id]/devserver/route.ts +0 -0
- /package/src/app/api/{sessions → chats}/[id]/edit-resend/route.ts +0 -0
- /package/src/app/api/{sessions → chats}/[id]/fork/route.ts +0 -0
- /package/src/app/api/{sessions → chats}/[id]/mailbox/route.ts +0 -0
- /package/src/app/api/{sessions → chats}/[id]/retry/route.ts +0 -0
- /package/src/app/api/{sessions → chats}/heartbeat/route.ts +0 -0
|
@@ -30,6 +30,29 @@ function linkify(text: string) {
|
|
|
30
30
|
})
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
+
interface ConnectorDoctorPolicyPreview {
|
|
34
|
+
scope?: string
|
|
35
|
+
replyMode?: string
|
|
36
|
+
threadBinding?: string
|
|
37
|
+
groupPolicy?: string
|
|
38
|
+
resetMode?: string
|
|
39
|
+
idleTimeoutSec?: number | null
|
|
40
|
+
maxAgeSec?: number | null
|
|
41
|
+
dailyResetAt?: string | null
|
|
42
|
+
resetTimezone?: string | null
|
|
43
|
+
thinkingLevel?: string | null
|
|
44
|
+
providerOverride?: string | null
|
|
45
|
+
modelOverride?: string | null
|
|
46
|
+
inboundDebounceMs?: number
|
|
47
|
+
statusReactions?: boolean
|
|
48
|
+
typingIndicators?: boolean
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
interface ConnectorDoctorResponse {
|
|
52
|
+
warnings?: string[]
|
|
53
|
+
policy?: ConnectorDoctorPolicyPreview | null
|
|
54
|
+
}
|
|
55
|
+
|
|
33
56
|
const PLATFORMS: {
|
|
34
57
|
id: ConnectorPlatform
|
|
35
58
|
label: string
|
|
@@ -121,7 +144,7 @@ const PLATFORMS: {
|
|
|
121
144
|
tokenHelp: 'Required when your OpenClaw gateway is auth-protected',
|
|
122
145
|
configFields: [
|
|
123
146
|
{ key: 'wsUrl', label: 'WebSocket URL', placeholder: 'ws://localhost:18789', help: 'OpenClaw gateway WebSocket endpoint (root URL, not /ws)' },
|
|
124
|
-
{ key: 'sessionKey', label: 'Chat Key Filter', placeholder: 'main', help: 'Optional. If set, only inbound events for this OpenClaw
|
|
147
|
+
{ key: 'sessionKey', label: 'Chat Key Filter', placeholder: 'main', help: 'Optional. If set, only inbound events for this OpenClaw chat are processed.' },
|
|
125
148
|
{ key: 'nodeId', label: 'Client Label', placeholder: 'swarmclaw', help: 'Optional display label shown in OpenClaw presence metadata.' },
|
|
126
149
|
{ key: 'role', label: 'Gateway Role', placeholder: 'operator', help: 'Optional role claim for connect handshake. Default is operator.' },
|
|
127
150
|
{ key: 'scopes', label: 'Scopes (CSV)', placeholder: 'operator.read,operator.write', help: 'Optional comma-separated scopes for OpenClaw connect.' },
|
|
@@ -225,6 +248,96 @@ const PLATFORMS: {
|
|
|
225
248
|
]
|
|
226
249
|
|
|
227
250
|
const COMMON_CONFIG_FIELDS: { key: string; label: string; placeholder: string; help?: string }[] = [
|
|
251
|
+
{
|
|
252
|
+
key: 'thinkingLevel',
|
|
253
|
+
label: 'Thinking Level',
|
|
254
|
+
placeholder: 'minimal | low | medium | high',
|
|
255
|
+
help: 'Default reasoning depth for new/reset direct connector sessions.',
|
|
256
|
+
},
|
|
257
|
+
{
|
|
258
|
+
key: 'providerOverride',
|
|
259
|
+
label: 'Provider Override',
|
|
260
|
+
placeholder: 'openai | anthropic | openclaw | ...',
|
|
261
|
+
help: 'Optional direct-session provider override. Useful for connector-specific routing or cheaper autonomy lanes.',
|
|
262
|
+
},
|
|
263
|
+
{
|
|
264
|
+
key: 'modelOverride',
|
|
265
|
+
label: 'Model Override',
|
|
266
|
+
placeholder: 'gpt-4.1-mini',
|
|
267
|
+
help: 'Optional direct-session model override. Defaults to the assigned agent model when empty.',
|
|
268
|
+
},
|
|
269
|
+
{
|
|
270
|
+
key: 'sessionScope',
|
|
271
|
+
label: 'Session Scope',
|
|
272
|
+
placeholder: 'main | channel | peer | channel-peer | thread',
|
|
273
|
+
help: 'Conversation identity policy. Defaults to channel-peer for DMs and channel for groups.',
|
|
274
|
+
},
|
|
275
|
+
{
|
|
276
|
+
key: 'replyMode',
|
|
277
|
+
label: 'Reply Mode',
|
|
278
|
+
placeholder: 'off | first | all',
|
|
279
|
+
help: 'Whether outbound replies should attach to the triggering inbound message.',
|
|
280
|
+
},
|
|
281
|
+
{
|
|
282
|
+
key: 'threadBinding',
|
|
283
|
+
label: 'Thread Binding',
|
|
284
|
+
placeholder: 'off | prefer | strict',
|
|
285
|
+
help: 'Prefer or require thread/topic-specific sessions when the platform exposes thread IDs.',
|
|
286
|
+
},
|
|
287
|
+
{
|
|
288
|
+
key: 'groupPolicy',
|
|
289
|
+
label: 'Group Policy',
|
|
290
|
+
placeholder: 'open | mention | reply-or-mention | disabled',
|
|
291
|
+
help: 'Controls whether the agent speaks in group chats without being mentioned or replied to.',
|
|
292
|
+
},
|
|
293
|
+
{
|
|
294
|
+
key: 'idleTimeoutSec',
|
|
295
|
+
label: 'Idle Timeout (sec)',
|
|
296
|
+
placeholder: '43200',
|
|
297
|
+
help: 'If exceeded, the connector session is reset before the next inbound turn.',
|
|
298
|
+
},
|
|
299
|
+
{
|
|
300
|
+
key: 'maxAgeSec',
|
|
301
|
+
label: 'Max Age (sec)',
|
|
302
|
+
placeholder: '604800',
|
|
303
|
+
help: 'Absolute maximum age of a connector session before it is reset.',
|
|
304
|
+
},
|
|
305
|
+
{
|
|
306
|
+
key: 'sessionResetMode',
|
|
307
|
+
label: 'Reset Mode',
|
|
308
|
+
placeholder: 'idle | daily',
|
|
309
|
+
help: 'Freshness policy for connector sessions. Daily resets use the fields below.',
|
|
310
|
+
},
|
|
311
|
+
{
|
|
312
|
+
key: 'sessionDailyResetAt',
|
|
313
|
+
label: 'Daily Reset Time',
|
|
314
|
+
placeholder: '04:00',
|
|
315
|
+
help: 'Used only when Reset Mode is daily. Format: HH:MM.',
|
|
316
|
+
},
|
|
317
|
+
{
|
|
318
|
+
key: 'sessionResetTimezone',
|
|
319
|
+
label: 'Reset Timezone',
|
|
320
|
+
placeholder: 'UTC or Europe/Isle_of_Man',
|
|
321
|
+
help: 'Optional timezone for daily reset boundaries. Defaults to the server timezone.',
|
|
322
|
+
},
|
|
323
|
+
{
|
|
324
|
+
key: 'inboundDebounceMs',
|
|
325
|
+
label: 'Inbound Debounce (ms)',
|
|
326
|
+
placeholder: '700',
|
|
327
|
+
help: 'Coalesces rapid inbound bursts from the same sender before starting a run.',
|
|
328
|
+
},
|
|
329
|
+
{
|
|
330
|
+
key: 'statusReactions',
|
|
331
|
+
label: 'Status Reactions',
|
|
332
|
+
placeholder: 'true | false',
|
|
333
|
+
help: 'When supported, add lightweight platform-native reactions for processing/sent/silent states.',
|
|
334
|
+
},
|
|
335
|
+
{
|
|
336
|
+
key: 'typingIndicators',
|
|
337
|
+
label: 'Typing Indicators',
|
|
338
|
+
placeholder: 'true | false',
|
|
339
|
+
help: 'When supported, keep a native typing/working indicator alive while the agent is running.',
|
|
340
|
+
},
|
|
228
341
|
{
|
|
229
342
|
key: 'taskFollowups',
|
|
230
343
|
label: 'Task Follow-ups',
|
|
@@ -295,6 +408,9 @@ export function ConnectorSheet() {
|
|
|
295
408
|
const [newCredName, setNewCredName] = useState('')
|
|
296
409
|
const [newCredValue, setNewCredValue] = useState('')
|
|
297
410
|
const [savingCred, setSavingCred] = useState(false)
|
|
411
|
+
const [doctorWarnings, setDoctorWarnings] = useState<string[]>([])
|
|
412
|
+
const [doctorPolicy, setDoctorPolicy] = useState<ConnectorDoctorPolicyPreview | null>(null)
|
|
413
|
+
const [doctorLoading, setDoctorLoading] = useState(false)
|
|
298
414
|
|
|
299
415
|
const editing = editingId ? connectors[editingId] as Connector | undefined : null
|
|
300
416
|
|
|
@@ -360,6 +476,41 @@ export function ConnectorSheet() {
|
|
|
360
476
|
|
|
361
477
|
useWs('connectors', pollWaStatus, isWaRunning ? 2000 : undefined)
|
|
362
478
|
|
|
479
|
+
const loadDoctorPreview = useCallback(async () => {
|
|
480
|
+
setDoctorLoading(true)
|
|
481
|
+
try {
|
|
482
|
+
const data = await api<ConnectorDoctorResponse>('POST', '/connectors/doctor', {
|
|
483
|
+
id: editing?.id || null,
|
|
484
|
+
name,
|
|
485
|
+
platform,
|
|
486
|
+
agentId: routeMode === 'agent' ? (agentId || null) : null,
|
|
487
|
+
chatroomId: routeMode === 'chatroom' ? (chatroomId || null) : null,
|
|
488
|
+
credentialId: credentialId || null,
|
|
489
|
+
config,
|
|
490
|
+
})
|
|
491
|
+
setDoctorWarnings(Array.isArray(data.warnings) ? data.warnings : [])
|
|
492
|
+
setDoctorPolicy(data.policy || null)
|
|
493
|
+
} catch {
|
|
494
|
+
setDoctorWarnings([])
|
|
495
|
+
setDoctorPolicy(null)
|
|
496
|
+
} finally {
|
|
497
|
+
setDoctorLoading(false)
|
|
498
|
+
}
|
|
499
|
+
}, [editing?.id, name, platform, routeMode, agentId, chatroomId, credentialId, config])
|
|
500
|
+
|
|
501
|
+
useEffect(() => {
|
|
502
|
+
if (!open) {
|
|
503
|
+
setDoctorWarnings([])
|
|
504
|
+
setDoctorPolicy(null)
|
|
505
|
+
setDoctorLoading(false)
|
|
506
|
+
return
|
|
507
|
+
}
|
|
508
|
+
const timer = window.setTimeout(() => {
|
|
509
|
+
void loadDoctorPreview()
|
|
510
|
+
}, 250)
|
|
511
|
+
return () => window.clearTimeout(timer)
|
|
512
|
+
}, [open, loadDoctorPreview])
|
|
513
|
+
|
|
363
514
|
const handleSave = async () => {
|
|
364
515
|
const hasTarget = routeMode === 'agent' ? !!agentId : !!chatroomId
|
|
365
516
|
if (!hasTarget) return
|
|
@@ -796,6 +947,72 @@ export function ConnectorSheet() {
|
|
|
796
947
|
)
|
|
797
948
|
})()}
|
|
798
949
|
|
|
950
|
+
<div className="mb-6 p-4 rounded-[14px] border border-white/[0.06] bg-white/[0.01]">
|
|
951
|
+
<div className="flex items-center justify-between gap-3 mb-2">
|
|
952
|
+
<div>
|
|
953
|
+
<div className="text-[13px] font-600 text-text-2">Connector Doctor</div>
|
|
954
|
+
<div className="text-[12px] text-text-3/70">
|
|
955
|
+
Live autonomy and safety preview for the current connector settings.
|
|
956
|
+
</div>
|
|
957
|
+
</div>
|
|
958
|
+
<button
|
|
959
|
+
type="button"
|
|
960
|
+
onClick={() => void loadDoctorPreview()}
|
|
961
|
+
disabled={doctorLoading}
|
|
962
|
+
className="px-3 py-1.5 rounded-[9px] border border-white/[0.08] bg-transparent text-[12px] font-600 text-text-3 hover:text-text-2 hover:bg-white/[0.04] transition-all cursor-pointer disabled:opacity-50"
|
|
963
|
+
style={{ fontFamily: 'inherit' }}
|
|
964
|
+
>
|
|
965
|
+
{doctorLoading ? 'Checking...' : 'Refresh'}
|
|
966
|
+
</button>
|
|
967
|
+
</div>
|
|
968
|
+
{doctorPolicy && (
|
|
969
|
+
<div className="grid grid-cols-1 md:grid-cols-2 gap-2 mb-3">
|
|
970
|
+
<div className="rounded-[10px] border border-white/[0.06] bg-white/[0.02] px-3 py-2 text-[12px] text-text-3/80">
|
|
971
|
+
Scope: <span className="text-text-2">{doctorPolicy.scope || 'channel-peer'}</span>{' '}
|
|
972
|
+
· Reply: <span className="text-text-2">{doctorPolicy.replyMode || 'first'}</span>{' '}
|
|
973
|
+
· Thread: <span className="text-text-2">{doctorPolicy.threadBinding || 'prefer'}</span>
|
|
974
|
+
</div>
|
|
975
|
+
<div className="rounded-[10px] border border-white/[0.06] bg-white/[0.02] px-3 py-2 text-[12px] text-text-3/80">
|
|
976
|
+
Group: <span className="text-text-2">{doctorPolicy.groupPolicy || 'reply-or-mention'}</span>{' '}
|
|
977
|
+
· Debounce: <span className="text-text-2">{doctorPolicy.inboundDebounceMs ?? 700}ms</span>
|
|
978
|
+
</div>
|
|
979
|
+
<div className="rounded-[10px] border border-white/[0.06] bg-white/[0.02] px-3 py-2 text-[12px] text-text-3/80">
|
|
980
|
+
Reset: <span className="text-text-2">{doctorPolicy.resetMode || 'idle'}</span>{' '}
|
|
981
|
+
{doctorPolicy.resetMode === 'daily'
|
|
982
|
+
? `at ${doctorPolicy.dailyResetAt || 'unset'} (${doctorPolicy.resetTimezone || 'server timezone'})`
|
|
983
|
+
: `idle ${doctorPolicy.idleTimeoutSec ?? 0}s / max ${doctorPolicy.maxAgeSec ?? 0}s`}
|
|
984
|
+
</div>
|
|
985
|
+
<div className="rounded-[10px] border border-white/[0.06] bg-white/[0.02] px-3 py-2 text-[12px] text-text-3/80">
|
|
986
|
+
Runtime: <span className="text-text-2">{doctorPolicy.thinkingLevel || 'inherit'}</span>{' '}
|
|
987
|
+
· Provider: <span className="text-text-2">{doctorPolicy.providerOverride || 'agent default'}</span>{' '}
|
|
988
|
+
· Model: <span className="text-text-2">{doctorPolicy.modelOverride || 'agent default'}</span>
|
|
989
|
+
</div>
|
|
990
|
+
</div>
|
|
991
|
+
)}
|
|
992
|
+
{doctorWarnings.length > 0 ? (
|
|
993
|
+
<div className="space-y-2">
|
|
994
|
+
{doctorWarnings.map((warning, index) => (
|
|
995
|
+
<div key={`${index}:${warning}`} className="rounded-[10px] border border-amber-400/15 bg-amber-500/8 px-3 py-2 text-[12px] text-amber-200/85 leading-[1.5]">
|
|
996
|
+
{warning}
|
|
997
|
+
</div>
|
|
998
|
+
))}
|
|
999
|
+
</div>
|
|
1000
|
+
) : (
|
|
1001
|
+
<div className="text-[12px] text-emerald-300/85">
|
|
1002
|
+
{doctorLoading ? 'Running checks…' : 'No autonomy or safety warnings detected for the current form values.'}
|
|
1003
|
+
</div>
|
|
1004
|
+
)}
|
|
1005
|
+
<p className="text-[11px] text-text-3/55 mt-2">
|
|
1006
|
+
This preview updates from the form directly, so you can catch risky connector policy changes before saving.
|
|
1007
|
+
</p>
|
|
1008
|
+
</div>
|
|
1009
|
+
|
|
1010
|
+
{editing && (
|
|
1011
|
+
<div className="mb-6 p-4 rounded-[14px] border border-white/[0.06] bg-white/[0.01]">
|
|
1012
|
+
<ConnectorHealth connectorId={editing.id} />
|
|
1013
|
+
</div>
|
|
1014
|
+
)}
|
|
1015
|
+
|
|
799
1016
|
{/* WhatsApp QR code */}
|
|
800
1017
|
{editing && editing.platform === 'whatsapp' && (editing.status === 'running' || waConnecting) && qrDataUrl && (
|
|
801
1018
|
<div className="mb-6 p-5 rounded-[14px] border border-white/[0.06] bg-white/[0.01] text-center"
|
|
@@ -197,12 +197,120 @@ export function HomeView() {
|
|
|
197
197
|
SwarmClaw
|
|
198
198
|
</h1>
|
|
199
199
|
<p className="text-[14px] text-text-3 mt-1">
|
|
200
|
-
|
|
200
|
+
Workspace overview for your agent chats, tasks, and automations
|
|
201
201
|
</p>
|
|
202
202
|
</div>
|
|
203
203
|
|
|
204
|
+
{/* Quick actions / triage */}
|
|
205
|
+
<section className="mb-8" style={{ animation: 'fade-up 0.6s var(--ease-spring) 0.15s both' }}>
|
|
206
|
+
<SectionHeader
|
|
207
|
+
label="Needs Attention"
|
|
208
|
+
actionLabel="Open Tasks"
|
|
209
|
+
onViewAll={activeTaskCount > 0 ? () => setActiveView('tasks') : undefined}
|
|
210
|
+
/>
|
|
211
|
+
<div className="rounded-[18px] border border-white/[0.06] bg-white/[0.025] p-4">
|
|
212
|
+
<div className="flex flex-wrap items-center gap-2 mb-4">
|
|
213
|
+
<StatusPill label={`${allTasks.filter((task) => task.status === 'failed').length} failed task${allTasks.filter((task) => task.status === 'failed').length === 1 ? '' : 's'}`} tone={allTasks.some((task) => task.status === 'failed') ? 'danger' : 'neutral'} />
|
|
214
|
+
<StatusPill label={`${allTasks.filter((task) => task.pendingApproval).length} awaiting approval`} tone={allTasks.some((task) => task.pendingApproval) ? 'warning' : 'neutral'} />
|
|
215
|
+
<StatusPill label={`${allConnectors.filter((connector) => connector.status === 'error').length} connector issue${allConnectors.filter((connector) => connector.status === 'error').length === 1 ? '' : 's'}`} tone={allConnectors.some((connector) => connector.status === 'error') ? 'danger' : 'neutral'} />
|
|
216
|
+
<StatusPill label={`${runningAgentIds.size} active agent${runningAgentIds.size === 1 ? '' : 's'}`} tone={runningAgentIds.size > 0 ? 'success' : 'neutral'} />
|
|
217
|
+
</div>
|
|
218
|
+
|
|
219
|
+
{(() => {
|
|
220
|
+
const now = Date.now()
|
|
221
|
+
const items = [
|
|
222
|
+
...allTasks
|
|
223
|
+
.filter((task) => task.pendingApproval)
|
|
224
|
+
.slice(0, 2)
|
|
225
|
+
.map((task) => ({
|
|
226
|
+
id: `approval:${task.id}`,
|
|
227
|
+
tone: 'warning' as const,
|
|
228
|
+
label: task.title,
|
|
229
|
+
meta: `${task.agentId && agents[task.agentId] ? agents[task.agentId]!.name : 'Task'} is waiting for approval`,
|
|
230
|
+
onClick: () => handleTaskClick(task),
|
|
231
|
+
})),
|
|
232
|
+
...allTasks
|
|
233
|
+
.filter((task) => task.status === 'failed')
|
|
234
|
+
.slice(0, 2)
|
|
235
|
+
.map((task) => ({
|
|
236
|
+
id: `failed:${task.id}`,
|
|
237
|
+
tone: 'danger' as const,
|
|
238
|
+
label: task.title,
|
|
239
|
+
meta: `Failed ${timeAgo(task.updatedAt || task.createdAt)}`,
|
|
240
|
+
onClick: () => handleTaskClick(task),
|
|
241
|
+
})),
|
|
242
|
+
...allConnectors
|
|
243
|
+
.filter((connector) => connector.status === 'error')
|
|
244
|
+
.slice(0, 2)
|
|
245
|
+
.map((connector) => ({
|
|
246
|
+
id: `connector:${connector.id}`,
|
|
247
|
+
tone: 'danger' as const,
|
|
248
|
+
label: connector.name,
|
|
249
|
+
meta: `${PLATFORM_LABELS[connector.platform] || connector.platform} connector needs attention`,
|
|
250
|
+
onClick: () => setActiveView('connectors'),
|
|
251
|
+
})),
|
|
252
|
+
...Object.values(schedules)
|
|
253
|
+
.filter((schedule) => schedule.status === 'active' && schedule.nextRunAt && schedule.nextRunAt < now)
|
|
254
|
+
.slice(0, 2)
|
|
255
|
+
.map((schedule) => ({
|
|
256
|
+
id: `schedule:${schedule.id}`,
|
|
257
|
+
tone: 'warning' as const,
|
|
258
|
+
label: schedule.name,
|
|
259
|
+
meta: 'Schedule missed its expected run window',
|
|
260
|
+
onClick: () => setActiveView('schedules'),
|
|
261
|
+
})),
|
|
262
|
+
...unreadNotifications
|
|
263
|
+
.slice(0, 2)
|
|
264
|
+
.map((notification) => ({
|
|
265
|
+
id: `notification:${notification.id}`,
|
|
266
|
+
tone: 'info' as const,
|
|
267
|
+
label: notification.title,
|
|
268
|
+
meta: notification.message || 'Unread notification',
|
|
269
|
+
onClick: () => handleNotificationClick(notification),
|
|
270
|
+
})),
|
|
271
|
+
].slice(0, 6)
|
|
272
|
+
|
|
273
|
+
if (items.length === 0) {
|
|
274
|
+
return (
|
|
275
|
+
<div className="rounded-[14px] border border-dashed border-white/[0.06] bg-white/[0.02] px-4 py-5">
|
|
276
|
+
<p className="text-[13px] font-600 text-text">Everything looks stable.</p>
|
|
277
|
+
<p className="text-[12px] text-text-3/60 mt-1">
|
|
278
|
+
No failed tasks, no waiting approvals, and no connector issues right now.
|
|
279
|
+
</p>
|
|
280
|
+
</div>
|
|
281
|
+
)
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
return (
|
|
285
|
+
<div className="grid grid-cols-1 md:grid-cols-2 gap-2">
|
|
286
|
+
{items.map((item) => (
|
|
287
|
+
<button
|
|
288
|
+
key={item.id}
|
|
289
|
+
onClick={item.onClick}
|
|
290
|
+
className="flex items-start gap-3 rounded-[14px] border border-white/[0.06] bg-transparent px-4 py-3 text-left hover:bg-white/[0.04] transition-colors cursor-pointer"
|
|
291
|
+
style={{ fontFamily: 'inherit' }}
|
|
292
|
+
>
|
|
293
|
+
<div className={`mt-0.5 h-2.5 w-2.5 rounded-full shrink-0 ${
|
|
294
|
+
item.tone === 'danger'
|
|
295
|
+
? 'bg-red-400'
|
|
296
|
+
: item.tone === 'warning'
|
|
297
|
+
? 'bg-amber-400'
|
|
298
|
+
: 'bg-sky-400'
|
|
299
|
+
}`} />
|
|
300
|
+
<div className="min-w-0">
|
|
301
|
+
<div className="text-[13px] font-600 text-text truncate">{item.label}</div>
|
|
302
|
+
<div className="text-[11px] text-text-3/65 mt-1">{item.meta}</div>
|
|
303
|
+
</div>
|
|
304
|
+
</button>
|
|
305
|
+
))}
|
|
306
|
+
</div>
|
|
307
|
+
)
|
|
308
|
+
})()}
|
|
309
|
+
</div>
|
|
310
|
+
</section>
|
|
311
|
+
|
|
204
312
|
{/* Quick Stats */}
|
|
205
|
-
<div className="grid grid-cols-2 md:grid-cols-4 gap-3 mb-
|
|
313
|
+
<div className="grid grid-cols-2 md:grid-cols-4 gap-3 mb-8">
|
|
206
314
|
<StatCard label="Agents" value={String(agentCount)} hint="Total active agents configured in your dashboard" index={0} />
|
|
207
315
|
<StatCard label="Active Tasks" value={String(activeTaskCount)} accent={activeTaskCount > 0} hint="Tasks currently running or queued for execution" index={1} />
|
|
208
316
|
<StatCard label="Today's Cost" value={`$${todayCost.toFixed(2)}`} hint="Estimated API cost for today across all providers" index={2} />
|
|
@@ -379,7 +487,7 @@ export function HomeView() {
|
|
|
379
487
|
<div className="flex gap-3 overflow-x-auto pb-2">
|
|
380
488
|
{pinnedAgents.map((agent) => {
|
|
381
489
|
const threadSession = agent.threadSessionId ? sessions[agent.threadSessionId] as Session | undefined : undefined
|
|
382
|
-
const heartbeatOn = agent.heartbeatEnabled === true && (agent.
|
|
490
|
+
const heartbeatOn = agent.heartbeatEnabled === true && (agent.plugins?.length ?? 0) > 0
|
|
383
491
|
const recentlyActive = (threadSession?.lastActiveAt ?? 0) > Date.now() - 30 * 60 * 1000
|
|
384
492
|
const isOnline = runningAgentIds.has(agent.id) || (threadSession?.active ?? false) || heartbeatOn || recentlyActive
|
|
385
493
|
const isTyping = streamingSessionId === agent.threadSessionId
|
|
@@ -507,7 +615,7 @@ export function HomeView() {
|
|
|
507
615
|
)
|
|
508
616
|
}
|
|
509
617
|
|
|
510
|
-
function SectionHeader({ label, onViewAll }: { label: string; onViewAll?: () => void }) {
|
|
618
|
+
function SectionHeader({ label, onViewAll, actionLabel = 'View all →' }: { label: string; onViewAll?: () => void; actionLabel?: string }) {
|
|
511
619
|
return (
|
|
512
620
|
<div className="flex items-center justify-between mb-3">
|
|
513
621
|
<h2 className="font-display text-[13px] font-600 text-text-2 uppercase tracking-[0.08em]">
|
|
@@ -519,13 +627,29 @@ function SectionHeader({ label, onViewAll }: { label: string; onViewAll?: () =>
|
|
|
519
627
|
className="text-[11px] text-text-3/50 hover:text-text-3 transition-colors bg-transparent border-none cursor-pointer"
|
|
520
628
|
style={{ fontFamily: 'inherit' }}
|
|
521
629
|
>
|
|
522
|
-
|
|
630
|
+
{actionLabel}
|
|
523
631
|
</button>
|
|
524
632
|
)}
|
|
525
633
|
</div>
|
|
526
634
|
)
|
|
527
635
|
}
|
|
528
636
|
|
|
637
|
+
function StatusPill({ label, tone }: { label: string; tone: 'neutral' | 'warning' | 'danger' | 'success' }) {
|
|
638
|
+
const toneClasses = tone === 'danger'
|
|
639
|
+
? 'border-red-400/20 bg-red-400/[0.05] text-red-300/85'
|
|
640
|
+
: tone === 'warning'
|
|
641
|
+
? 'border-amber-400/20 bg-amber-400/[0.05] text-amber-300/85'
|
|
642
|
+
: tone === 'success'
|
|
643
|
+
? 'border-emerald-400/20 bg-emerald-400/[0.05] text-emerald-300/85'
|
|
644
|
+
: 'border-white/[0.06] bg-white/[0.03] text-text-3/75'
|
|
645
|
+
|
|
646
|
+
return (
|
|
647
|
+
<div className={`rounded-[999px] border px-3 py-1.5 text-[11px] font-600 ${toneClasses}`}>
|
|
648
|
+
{label}
|
|
649
|
+
</div>
|
|
650
|
+
)
|
|
651
|
+
}
|
|
652
|
+
|
|
529
653
|
function StatCard({ label, value, accent, hint, index = 0 }: { label: string; value: string; accent?: boolean; hint?: string; index?: number }) {
|
|
530
654
|
return (
|
|
531
655
|
<div
|