@swarmclawai/swarmclaw 0.4.0 → 0.5.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 +21 -4
- package/bin/server-cmd.js +28 -19
- package/next.config.ts +13 -0
- package/package.json +3 -1
- package/src/app/api/agents/[id]/route.ts +39 -22
- package/src/app/api/agents/[id]/thread/route.ts +2 -2
- package/src/app/api/agents/route.ts +3 -2
- package/src/app/api/agents/trash/route.ts +44 -0
- package/src/app/api/clawhub/install/route.ts +2 -2
- package/src/app/api/connectors/[id]/route.ts +17 -7
- package/src/app/api/connectors/[id]/webhook/route.ts +103 -0
- package/src/app/api/connectors/route.ts +6 -3
- package/src/app/api/credentials/[id]/route.ts +2 -1
- package/src/app/api/credentials/route.ts +2 -2
- package/src/app/api/documents/route.ts +2 -2
- package/src/app/api/files/serve/route.ts +8 -0
- package/src/app/api/knowledge/[id]/route.ts +5 -4
- package/src/app/api/knowledge/upload/route.ts +2 -2
- package/src/app/api/mcp-servers/[id]/route.ts +11 -14
- package/src/app/api/mcp-servers/[id]/test/route.ts +2 -1
- package/src/app/api/mcp-servers/[id]/tools/route.ts +2 -1
- package/src/app/api/mcp-servers/route.ts +2 -2
- package/src/app/api/memory/[id]/route.ts +9 -8
- package/src/app/api/memory/route.ts +2 -2
- package/src/app/api/memory-images/[filename]/route.ts +2 -1
- package/src/app/api/openclaw/agent-files/route.ts +57 -0
- package/src/app/api/openclaw/approvals/route.ts +46 -0
- package/src/app/api/openclaw/config-sync/route.ts +33 -0
- package/src/app/api/openclaw/cron/route.ts +52 -0
- package/src/app/api/openclaw/directory/route.ts +27 -0
- package/src/app/api/openclaw/discover/route.ts +62 -0
- package/src/app/api/openclaw/dotenv-keys/route.ts +18 -0
- package/src/app/api/openclaw/exec-config/route.ts +41 -0
- package/src/app/api/openclaw/gateway/route.ts +72 -0
- package/src/app/api/openclaw/history/route.ts +109 -0
- package/src/app/api/openclaw/media/route.ts +53 -0
- package/src/app/api/openclaw/models/route.ts +12 -0
- package/src/app/api/openclaw/permissions/route.ts +39 -0
- package/src/app/api/openclaw/sandbox-env/route.ts +69 -0
- package/src/app/api/openclaw/skills/install/route.ts +32 -0
- package/src/app/api/openclaw/skills/remove/route.ts +24 -0
- package/src/app/api/openclaw/skills/route.ts +82 -0
- package/src/app/api/openclaw/sync/route.ts +31 -0
- package/src/app/api/orchestrator/run/route.ts +2 -2
- package/src/app/api/projects/[id]/route.ts +55 -0
- package/src/app/api/projects/route.ts +27 -0
- package/src/app/api/providers/[id]/models/route.ts +2 -1
- package/src/app/api/providers/[id]/route.ts +13 -15
- package/src/app/api/providers/route.ts +2 -2
- package/src/app/api/schedules/[id]/route.ts +16 -18
- package/src/app/api/schedules/[id]/run/route.ts +4 -3
- package/src/app/api/schedules/route.ts +2 -2
- package/src/app/api/secrets/[id]/route.ts +16 -17
- package/src/app/api/secrets/route.ts +2 -2
- package/src/app/api/sessions/[id]/clear/route.ts +2 -1
- package/src/app/api/sessions/[id]/deploy/route.ts +2 -1
- package/src/app/api/sessions/[id]/devserver/route.ts +2 -1
- package/src/app/api/sessions/[id]/edit-resend/route.ts +22 -0
- package/src/app/api/sessions/[id]/fork/route.ts +44 -0
- package/src/app/api/sessions/[id]/messages/route.ts +20 -2
- package/src/app/api/sessions/[id]/retry/route.ts +2 -1
- package/src/app/api/sessions/[id]/route.ts +14 -4
- package/src/app/api/sessions/route.ts +8 -4
- package/src/app/api/skills/[id]/route.ts +23 -21
- package/src/app/api/skills/import/route.ts +2 -2
- package/src/app/api/skills/route.ts +2 -2
- package/src/app/api/tasks/[id]/approve/route.ts +2 -1
- package/src/app/api/tasks/[id]/route.ts +6 -5
- package/src/app/api/tasks/route.ts +2 -2
- package/src/app/api/tts/stream/route.ts +48 -0
- package/src/app/api/upload/route.ts +2 -2
- package/src/app/api/uploads/[filename]/route.ts +4 -1
- package/src/app/api/webhooks/[id]/route.ts +29 -31
- package/src/app/api/webhooks/route.ts +2 -2
- package/src/app/globals.css +14 -0
- package/src/app/layout.tsx +5 -20
- package/src/app/page.tsx +3 -24
- package/src/cli/index.js +60 -0
- package/src/cli/index.ts +1 -1
- package/src/cli/spec.js +42 -0
- package/src/components/agents/agent-avatar.tsx +45 -0
- package/src/components/agents/agent-card.tsx +19 -5
- package/src/components/agents/agent-chat-list.tsx +31 -24
- package/src/components/agents/agent-files-editor.tsx +185 -0
- package/src/components/agents/agent-list.tsx +84 -3
- package/src/components/agents/agent-sheet.tsx +147 -14
- package/src/components/agents/cron-job-form.tsx +137 -0
- package/src/components/agents/exec-config-panel.tsx +147 -0
- package/src/components/agents/inspector-panel.tsx +310 -0
- package/src/components/agents/openclaw-skills-panel.tsx +230 -0
- package/src/components/agents/permission-preset-selector.tsx +79 -0
- package/src/components/agents/personality-builder.tsx +111 -0
- package/src/components/agents/sandbox-env-panel.tsx +72 -0
- package/src/components/agents/skill-install-dialog.tsx +102 -0
- package/src/components/agents/trash-list.tsx +109 -0
- package/src/components/chat/chat-area.tsx +41 -6
- package/src/components/chat/chat-header.tsx +305 -29
- package/src/components/chat/chat-preview-panel.tsx +113 -0
- package/src/components/chat/exec-approval-card.tsx +89 -0
- package/src/components/chat/message-bubble.tsx +218 -36
- package/src/components/chat/message-list.tsx +135 -31
- package/src/components/chat/streaming-bubble.tsx +59 -10
- package/src/components/chat/suggestions-bar.tsx +74 -0
- package/src/components/chat/thinking-indicator.tsx +20 -6
- package/src/components/chat/tool-call-bubble.tsx +98 -19
- package/src/components/chat/tool-request-banner.tsx +20 -2
- package/src/components/chat/trace-block.tsx +103 -0
- package/src/components/chat/voice-overlay.tsx +80 -0
- package/src/components/connectors/connector-list.tsx +6 -2
- package/src/components/connectors/connector-sheet.tsx +31 -7
- package/src/components/layout/app-layout.tsx +47 -25
- package/src/components/projects/project-list.tsx +123 -0
- package/src/components/projects/project-sheet.tsx +135 -0
- package/src/components/schedules/schedule-list.tsx +3 -1
- package/src/components/sessions/new-session-sheet.tsx +6 -6
- package/src/components/sessions/session-card.tsx +1 -1
- package/src/components/sessions/session-list.tsx +7 -7
- package/src/components/settings/gateway-connection-panel.tsx +278 -0
- package/src/components/shared/avatar.tsx +13 -2
- package/src/components/shared/connector-platform-icon.tsx +4 -0
- package/src/components/shared/settings/section-heartbeat.tsx +1 -1
- package/src/components/shared/settings/section-orchestrator.tsx +1 -2
- package/src/components/shared/settings/section-web-search.tsx +56 -0
- package/src/components/shared/settings/settings-page.tsx +74 -0
- package/src/components/skills/skill-list.tsx +2 -1
- package/src/components/tasks/task-board.tsx +1 -1
- package/src/components/tasks/task-list.tsx +5 -2
- package/src/components/tasks/task-sheet.tsx +12 -12
- package/src/hooks/use-continuous-speech.ts +181 -0
- package/src/hooks/use-openclaw-gateway.ts +63 -0
- package/src/hooks/use-view-router.ts +52 -0
- package/src/hooks/use-voice-conversation.ts +80 -0
- package/src/lib/id.ts +6 -0
- package/src/lib/notification-sounds.ts +58 -0
- package/src/lib/personality-parser.ts +97 -0
- package/src/lib/projects.ts +13 -0
- package/src/lib/provider-sets.ts +5 -0
- package/src/lib/providers/anthropic.ts +14 -1
- package/src/lib/providers/index.ts +6 -0
- package/src/lib/providers/ollama.ts +9 -1
- package/src/lib/providers/openai.ts +9 -1
- package/src/lib/providers/openclaw.ts +28 -2
- package/src/lib/runtime-loop.ts +2 -2
- package/src/lib/server/api-routes.test.ts +5 -6
- package/src/lib/server/build-llm.ts +17 -4
- package/src/lib/server/chat-execution.ts +82 -6
- package/src/lib/server/collection-helpers.ts +54 -0
- package/src/lib/server/connectors/bluebubbles.test.ts +217 -0
- package/src/lib/server/connectors/bluebubbles.ts +360 -0
- package/src/lib/server/connectors/connector-routing.test.ts +1 -1
- package/src/lib/server/connectors/googlechat.ts +51 -8
- package/src/lib/server/connectors/manager.ts +424 -13
- package/src/lib/server/connectors/media.ts +2 -2
- package/src/lib/server/connectors/openclaw.ts +65 -0
- package/src/lib/server/connectors/pairing.test.ts +99 -0
- package/src/lib/server/connectors/pairing.ts +256 -0
- package/src/lib/server/connectors/signal.ts +1 -0
- package/src/lib/server/connectors/teams.ts +5 -5
- package/src/lib/server/connectors/types.ts +10 -0
- package/src/lib/server/daemon-state.ts +11 -0
- package/src/lib/server/execution-log.ts +3 -3
- package/src/lib/server/heartbeat-service.ts +1 -1
- package/src/lib/server/knowledge-db.test.ts +2 -33
- package/src/lib/server/main-agent-loop.ts +8 -9
- package/src/lib/server/main-session.ts +21 -0
- package/src/lib/server/memory-db.ts +6 -6
- package/src/lib/server/openclaw-approvals.ts +105 -0
- package/src/lib/server/openclaw-config-sync.ts +107 -0
- package/src/lib/server/openclaw-exec-config.ts +52 -0
- package/src/lib/server/openclaw-gateway.ts +291 -0
- package/src/lib/server/openclaw-history-merge.ts +36 -0
- package/src/lib/server/openclaw-models.ts +56 -0
- package/src/lib/server/openclaw-permission-presets.ts +64 -0
- package/src/lib/server/openclaw-sync.ts +497 -0
- package/src/lib/server/orchestrator-lg.ts +30 -9
- package/src/lib/server/orchestrator.ts +4 -4
- package/src/lib/server/process-manager.ts +2 -2
- package/src/lib/server/queue.ts +24 -11
- package/src/lib/server/scheduler.ts +2 -2
- package/src/lib/server/session-mailbox.ts +2 -2
- package/src/lib/server/session-run-manager.ts +2 -2
- package/src/lib/server/session-tools/connector.ts +53 -6
- package/src/lib/server/session-tools/crud.ts +3 -3
- package/src/lib/server/session-tools/delegate.ts +22 -6
- package/src/lib/server/session-tools/file.ts +192 -19
- package/src/lib/server/session-tools/index.ts +4 -2
- package/src/lib/server/session-tools/memory.ts +2 -2
- package/src/lib/server/session-tools/openclaw-nodes.ts +112 -0
- package/src/lib/server/session-tools/sandbox.ts +33 -0
- package/src/lib/server/session-tools/search-providers.ts +277 -0
- package/src/lib/server/session-tools/session-info.ts +2 -2
- package/src/lib/server/session-tools/session-tools-wiring.test.ts +2 -2
- package/src/lib/server/session-tools/shell.ts +1 -1
- package/src/lib/server/session-tools/web.ts +53 -72
- package/src/lib/server/storage.ts +74 -11
- package/src/lib/server/stream-agent-chat.ts +53 -4
- package/src/lib/server/suggestions.ts +20 -0
- package/src/lib/server/task-result.test.ts +44 -0
- package/src/lib/server/task-result.ts +14 -0
- package/src/lib/server/ws-hub.ts +14 -0
- package/src/lib/tool-definitions.ts +5 -3
- package/src/lib/tts-stream.ts +130 -0
- package/src/lib/view-routes.ts +28 -0
- package/src/proxy.ts +3 -0
- package/src/stores/use-app-store.ts +80 -1
- package/src/stores/use-approval-store.ts +78 -0
- package/src/stores/use-chat-store.ts +162 -6
- package/src/types/index.ts +154 -3
- package/tsconfig.json +13 -4
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { NextResponse } from 'next/server'
|
|
2
2
|
import fs from 'fs'
|
|
3
3
|
import path from 'path'
|
|
4
|
-
import
|
|
4
|
+
import { genId } from '@/lib/id'
|
|
5
5
|
import { UPLOAD_DIR } from '@/lib/server/storage'
|
|
6
6
|
|
|
7
7
|
export async function POST(req: Request) {
|
|
8
8
|
const filename = req.headers.get('x-filename') || 'image.png'
|
|
9
9
|
const buf = Buffer.from(await req.arrayBuffer())
|
|
10
|
-
const name =
|
|
10
|
+
const name = genId() + '-' + filename.replace(/[^a-zA-Z0-9._-]/g, '_')
|
|
11
11
|
const filePath = path.join(UPLOAD_DIR, name)
|
|
12
12
|
|
|
13
13
|
if (!fs.existsSync(UPLOAD_DIR)) fs.mkdirSync(UPLOAD_DIR, { recursive: true })
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { NextResponse } from 'next/server'
|
|
2
|
+
import { notFound } from '@/lib/server/collection-helpers'
|
|
2
3
|
import fs from 'fs'
|
|
3
4
|
import path from 'path'
|
|
4
5
|
import { UPLOAD_DIR } from '@/lib/server/storage'
|
|
@@ -43,16 +44,18 @@ export async function GET(_req: Request, { params }: { params: Promise<{ filenam
|
|
|
43
44
|
const filePath = path.join(UPLOAD_DIR, safeName)
|
|
44
45
|
|
|
45
46
|
if (!fs.existsSync(filePath)) {
|
|
46
|
-
return
|
|
47
|
+
return notFound()
|
|
47
48
|
}
|
|
48
49
|
|
|
49
50
|
const ext = path.extname(safeName).toLowerCase()
|
|
50
51
|
const contentType = MIME_TYPES[ext] || 'application/octet-stream'
|
|
51
52
|
const data = fs.readFileSync(filePath)
|
|
52
53
|
|
|
54
|
+
const inline = contentType.startsWith('image/') || contentType.startsWith('video/') || contentType.startsWith('text/') || contentType === 'application/pdf'
|
|
53
55
|
return new NextResponse(data, {
|
|
54
56
|
headers: {
|
|
55
57
|
'Content-Type': contentType,
|
|
58
|
+
'Content-Disposition': inline ? 'inline' : `attachment; filename="${path.basename(safeName)}"`,
|
|
56
59
|
'Cache-Control': 'public, max-age=86400',
|
|
57
60
|
},
|
|
58
61
|
})
|
|
@@ -1,8 +1,12 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { genId } from '@/lib/id'
|
|
2
2
|
import { NextResponse } from 'next/server'
|
|
3
3
|
import { loadAgents, loadSessions, loadWebhooks, saveSessions, saveWebhooks, appendWebhookLog } from '@/lib/server/storage'
|
|
4
4
|
import { WORKSPACE_DIR } from '@/lib/server/data-dir'
|
|
5
5
|
import { enqueueSessionRun } from '@/lib/server/session-run-manager'
|
|
6
|
+
import { mutateItem, deleteItem, notFound, type CollectionOps } from '@/lib/server/collection-helpers'
|
|
7
|
+
|
|
8
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
9
|
+
const ops: CollectionOps<any> = { load: loadWebhooks, save: saveWebhooks }
|
|
6
10
|
|
|
7
11
|
function normalizeEvents(value: unknown): string[] {
|
|
8
12
|
if (!Array.isArray(value)) return []
|
|
@@ -22,36 +26,30 @@ export async function GET(_req: Request, { params }: { params: Promise<{ id: str
|
|
|
22
26
|
const { id } = await params
|
|
23
27
|
const webhooks = loadWebhooks()
|
|
24
28
|
const webhook = webhooks[id]
|
|
25
|
-
if (!webhook) return
|
|
29
|
+
if (!webhook) return notFound('Webhook not found')
|
|
26
30
|
return NextResponse.json(webhook)
|
|
27
31
|
}
|
|
28
32
|
|
|
29
33
|
export async function PUT(req: Request, { params }: { params: Promise<{ id: string }> }) {
|
|
30
34
|
const { id } = await params
|
|
31
35
|
const body = await req.json().catch(() => ({}))
|
|
32
|
-
const
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
webhooks[id] = webhook
|
|
45
|
-
saveWebhooks(webhooks)
|
|
46
|
-
return NextResponse.json(webhook)
|
|
36
|
+
const result = mutateItem(ops, id, (webhook) => {
|
|
37
|
+
if (body.name !== undefined) webhook.name = body.name
|
|
38
|
+
if (body.source !== undefined) webhook.source = body.source
|
|
39
|
+
if (body.events !== undefined) webhook.events = normalizeEvents(body.events)
|
|
40
|
+
if (body.agentId !== undefined) webhook.agentId = body.agentId
|
|
41
|
+
if (body.secret !== undefined) webhook.secret = body.secret
|
|
42
|
+
if (body.isEnabled !== undefined) webhook.isEnabled = !!body.isEnabled
|
|
43
|
+
webhook.updatedAt = Date.now()
|
|
44
|
+
return webhook
|
|
45
|
+
})
|
|
46
|
+
if (!result) return notFound('Webhook not found')
|
|
47
|
+
return NextResponse.json(result)
|
|
47
48
|
}
|
|
48
49
|
|
|
49
50
|
export async function DELETE(_req: Request, { params }: { params: Promise<{ id: string }> }) {
|
|
50
51
|
const { id } = await params
|
|
51
|
-
|
|
52
|
-
if (!webhooks[id]) return NextResponse.json({ error: 'Webhook not found' }, { status: 404 })
|
|
53
|
-
delete webhooks[id]
|
|
54
|
-
saveWebhooks(webhooks)
|
|
52
|
+
if (!deleteItem(ops, id)) return notFound('Webhook not found')
|
|
55
53
|
return NextResponse.json({ ok: true })
|
|
56
54
|
}
|
|
57
55
|
|
|
@@ -59,10 +57,10 @@ export async function POST(req: Request, { params }: { params: Promise<{ id: str
|
|
|
59
57
|
const { id } = await params
|
|
60
58
|
const webhooks = loadWebhooks()
|
|
61
59
|
const webhook = webhooks[id]
|
|
62
|
-
if (!webhook) return
|
|
60
|
+
if (!webhook) return notFound('Webhook not found')
|
|
63
61
|
if (webhook.isEnabled === false) {
|
|
64
|
-
appendWebhookLog(
|
|
65
|
-
id:
|
|
62
|
+
appendWebhookLog(genId(8), {
|
|
63
|
+
id: genId(8), webhookId: id, event: 'unknown',
|
|
66
64
|
payload: '', status: 'error', error: 'Webhook is disabled', timestamp: Date.now(),
|
|
67
65
|
})
|
|
68
66
|
return NextResponse.json({ error: 'Webhook is disabled' }, { status: 409 })
|
|
@@ -73,8 +71,8 @@ export async function POST(req: Request, { params }: { params: Promise<{ id: str
|
|
|
73
71
|
const url = new URL(req.url)
|
|
74
72
|
const provided = req.headers.get('x-webhook-secret') || url.searchParams.get('secret') || ''
|
|
75
73
|
if (provided !== secret) {
|
|
76
|
-
appendWebhookLog(
|
|
77
|
-
id:
|
|
74
|
+
appendWebhookLog(genId(8), {
|
|
75
|
+
id: genId(8), webhookId: id, event: 'unknown',
|
|
78
76
|
payload: '', status: 'error', error: 'Invalid webhook secret', timestamp: Date.now(),
|
|
79
77
|
})
|
|
80
78
|
return NextResponse.json({ error: 'Invalid webhook secret' }, { status: 401 })
|
|
@@ -122,8 +120,8 @@ export async function POST(req: Request, { params }: { params: Promise<{ id: str
|
|
|
122
120
|
const agents = loadAgents()
|
|
123
121
|
const agent = webhook.agentId ? agents[webhook.agentId] : null
|
|
124
122
|
if (!agent) {
|
|
125
|
-
appendWebhookLog(
|
|
126
|
-
id:
|
|
123
|
+
appendWebhookLog(genId(8), {
|
|
124
|
+
id: genId(8), webhookId: id, event: incomingEvent,
|
|
127
125
|
payload: (rawBody || '').slice(0, 2000), status: 'error', error: 'Webhook agent is not configured or missing', timestamp: Date.now(),
|
|
128
126
|
})
|
|
129
127
|
return NextResponse.json({ error: 'Webhook agent is not configured or missing' }, { status: 400 })
|
|
@@ -133,7 +131,7 @@ export async function POST(req: Request, { params }: { params: Promise<{ id: str
|
|
|
133
131
|
const sessionName = `webhook:${id}`
|
|
134
132
|
let session = Object.values(sessions).find((s: any) => s.name === sessionName && s.agentId === agent.id) as any
|
|
135
133
|
if (!session) {
|
|
136
|
-
const sessionId =
|
|
134
|
+
const sessionId = genId()
|
|
137
135
|
const now = Date.now()
|
|
138
136
|
session = {
|
|
139
137
|
id: sessionId,
|
|
@@ -189,8 +187,8 @@ export async function POST(req: Request, { params }: { params: Promise<{ id: str
|
|
|
189
187
|
mode: 'followup',
|
|
190
188
|
})
|
|
191
189
|
|
|
192
|
-
appendWebhookLog(
|
|
193
|
-
id:
|
|
190
|
+
appendWebhookLog(genId(8), {
|
|
191
|
+
id: genId(8), webhookId: id, event: incomingEvent,
|
|
194
192
|
payload: (rawBody || '').slice(0, 2000), status: 'success',
|
|
195
193
|
sessionId: session.id, runId: run.runId, timestamp: Date.now(),
|
|
196
194
|
})
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { genId } from '@/lib/id'
|
|
2
2
|
import { NextResponse } from 'next/server'
|
|
3
3
|
import { loadWebhooks, saveWebhooks } from '@/lib/server/storage'
|
|
4
4
|
export const dynamic = 'force-dynamic'
|
|
@@ -19,7 +19,7 @@ export async function GET(_req: Request) {
|
|
|
19
19
|
export async function POST(req: Request) {
|
|
20
20
|
const body = await req.json().catch(() => ({}))
|
|
21
21
|
const webhooks = loadWebhooks()
|
|
22
|
-
const id =
|
|
22
|
+
const id = genId()
|
|
23
23
|
const now = Date.now()
|
|
24
24
|
|
|
25
25
|
webhooks[id] = {
|
package/src/app/globals.css
CHANGED
|
@@ -79,6 +79,9 @@
|
|
|
79
79
|
}
|
|
80
80
|
|
|
81
81
|
:root {
|
|
82
|
+
--font-dm-sans: 'Segoe UI';
|
|
83
|
+
--font-sora: 'Segoe UI';
|
|
84
|
+
--font-jetbrains-mono: 'SF Mono';
|
|
82
85
|
--radius: 0.625rem;
|
|
83
86
|
--background: #08080d;
|
|
84
87
|
--foreground: #e2e2ec;
|
|
@@ -203,6 +206,17 @@ body {
|
|
|
203
206
|
100% { background-position: 0% 50%; }
|
|
204
207
|
}
|
|
205
208
|
|
|
209
|
+
/* AI avatar mood animations */
|
|
210
|
+
@keyframes ai-pulse { 0%,100% { transform: scale(1); } 50% { transform: scale(1.15); } }
|
|
211
|
+
@keyframes ai-glow { 0%,100% { box-shadow: 0 0 0 0 rgba(99,102,241,0); } 50% { box-shadow: 0 0 12px 4px rgba(99,102,241,0.35); } }
|
|
212
|
+
@keyframes ai-shake { 0%,100% { transform: translateX(0); } 20% { transform: translateX(-3px); } 40% { transform: translateX(3px); } 60% { transform: translateX(-2px); } 80% { transform: translateX(2px); } }
|
|
213
|
+
@keyframes ai-bounce { 0%,100% { transform: scale(1); } 40% { transform: scale(1.25); } 60% { transform: scale(0.95); } }
|
|
214
|
+
|
|
215
|
+
.ai-mood-pulse { animation: ai-pulse 2s ease-in-out infinite; }
|
|
216
|
+
.ai-mood-glow { animation: ai-glow 1.8s ease-in-out infinite; }
|
|
217
|
+
.ai-mood-shake { animation: ai-shake 0.5s ease-in-out; }
|
|
218
|
+
.ai-mood-bounce { animation: ai-bounce 0.6s ease-out; }
|
|
219
|
+
|
|
206
220
|
/* ===== Markdown content ===== */
|
|
207
221
|
.msg-content pre {
|
|
208
222
|
background: #0a0a12 !important;
|
package/src/app/layout.tsx
CHANGED
|
@@ -1,27 +1,8 @@
|
|
|
1
1
|
import type { Metadata, Viewport } from "next"
|
|
2
|
-
import { Sora, DM_Sans, JetBrains_Mono } from "next/font/google"
|
|
3
2
|
import { TooltipProvider } from "@/components/ui/tooltip"
|
|
4
3
|
import { Toaster } from "@/components/ui/sonner"
|
|
5
4
|
import "./globals.css"
|
|
6
5
|
|
|
7
|
-
const sora = Sora({
|
|
8
|
-
variable: "--font-sora",
|
|
9
|
-
subsets: ["latin"],
|
|
10
|
-
weight: ["400", "500", "600", "700", "800"],
|
|
11
|
-
})
|
|
12
|
-
|
|
13
|
-
const dmSans = DM_Sans({
|
|
14
|
-
variable: "--font-dm-sans",
|
|
15
|
-
subsets: ["latin"],
|
|
16
|
-
weight: ["400", "500", "600", "700"],
|
|
17
|
-
})
|
|
18
|
-
|
|
19
|
-
const jetbrainsMono = JetBrains_Mono({
|
|
20
|
-
variable: "--font-jetbrains-mono",
|
|
21
|
-
subsets: ["latin"],
|
|
22
|
-
weight: ["400", "500"],
|
|
23
|
-
})
|
|
24
|
-
|
|
25
6
|
export const metadata: Metadata = {
|
|
26
7
|
title: "SwarmClaw",
|
|
27
8
|
description: "AI agent orchestration dashboard with multi-provider support",
|
|
@@ -34,6 +15,10 @@ export const viewport: Viewport = {
|
|
|
34
15
|
viewportFit: "cover",
|
|
35
16
|
}
|
|
36
17
|
|
|
18
|
+
// Avoid static prerendering for the app shell. This prevents flaky
|
|
19
|
+
// Turbopack prerender failures seen in detached fresh-install builds.
|
|
20
|
+
export const dynamic = "force-dynamic"
|
|
21
|
+
|
|
37
22
|
export default function RootLayout({
|
|
38
23
|
children,
|
|
39
24
|
}: Readonly<{
|
|
@@ -41,7 +26,7 @@ export default function RootLayout({
|
|
|
41
26
|
}>) {
|
|
42
27
|
return (
|
|
43
28
|
<html lang="en" className="dark">
|
|
44
|
-
<body className=
|
|
29
|
+
<body className="antialiased" cz-shortcut-listen="true">
|
|
45
30
|
<TooltipProvider>
|
|
46
31
|
{children}
|
|
47
32
|
<Toaster />
|
package/src/app/page.tsx
CHANGED
|
@@ -10,6 +10,7 @@ import { AccessKeyGate } from '@/components/auth/access-key-gate'
|
|
|
10
10
|
import { UserPicker } from '@/components/auth/user-picker'
|
|
11
11
|
import { SetupWizard } from '@/components/auth/setup-wizard'
|
|
12
12
|
import { AppLayout } from '@/components/layout/app-layout'
|
|
13
|
+
import { useViewRouter } from '@/hooks/use-view-router'
|
|
13
14
|
|
|
14
15
|
export default function Home() {
|
|
15
16
|
const currentUser = useAppStore((s) => s.currentUser)
|
|
@@ -17,7 +18,6 @@ export default function Home() {
|
|
|
17
18
|
const hydrated = useAppStore((s) => s._hydrated)
|
|
18
19
|
const hydrate = useAppStore((s) => s.hydrate)
|
|
19
20
|
const loadNetworkInfo = useAppStore((s) => s.loadNetworkInfo)
|
|
20
|
-
const sessions = useAppStore((s) => s.sessions)
|
|
21
21
|
const loadSessions = useAppStore((s) => s.loadSessions)
|
|
22
22
|
const loadSettings = useAppStore((s) => s.loadSettings)
|
|
23
23
|
|
|
@@ -107,29 +107,6 @@ export default function Home() {
|
|
|
107
107
|
return () => { cancelled = true }
|
|
108
108
|
}, [authenticated, currentUser])
|
|
109
109
|
|
|
110
|
-
// Keep __main__ session for backward compat — create if missing
|
|
111
|
-
useEffect(() => {
|
|
112
|
-
if (!authenticated || !currentUser) return
|
|
113
|
-
const sessionList = Object.values(sessions)
|
|
114
|
-
const mainSession = sessionList.find((s: any) => s.name === '__main__' && s.user === currentUser)
|
|
115
|
-
if (mainSession) return
|
|
116
|
-
let cancelled = false
|
|
117
|
-
;(async () => {
|
|
118
|
-
try {
|
|
119
|
-
const mainId = `main-${currentUser}`
|
|
120
|
-
await api<any>('POST', '/sessions', {
|
|
121
|
-
id: mainId,
|
|
122
|
-
name: '__main__',
|
|
123
|
-
user: currentUser,
|
|
124
|
-
agentId: 'default',
|
|
125
|
-
heartbeatEnabled: true,
|
|
126
|
-
})
|
|
127
|
-
if (!cancelled) await loadSessions()
|
|
128
|
-
} catch { /* ignore */ }
|
|
129
|
-
})()
|
|
130
|
-
return () => { cancelled = true }
|
|
131
|
-
}, [authenticated, currentUser, sessions, loadSessions])
|
|
132
|
-
|
|
133
110
|
// Check if first-run setup is needed
|
|
134
111
|
useEffect(() => {
|
|
135
112
|
if (!authenticated || !currentUser) return
|
|
@@ -169,6 +146,8 @@ export default function Home() {
|
|
|
169
146
|
return () => window.removeEventListener('sc_auth_required', handler)
|
|
170
147
|
}, [])
|
|
171
148
|
|
|
149
|
+
useViewRouter()
|
|
150
|
+
|
|
172
151
|
if (!hydrated || !authChecked) return null
|
|
173
152
|
if (!authenticated) return <AccessKeyGate onAuthenticated={() => setAuthenticated(true)} />
|
|
174
153
|
if (!currentUser) return <UserPicker />
|
package/src/cli/index.js
CHANGED
|
@@ -17,6 +17,9 @@ const COMMAND_GROUPS = [
|
|
|
17
17
|
cmd('create', 'POST', '/agents', 'Create an agent', { expectsJsonBody: true }),
|
|
18
18
|
cmd('update', 'PUT', '/agents/:id', 'Update an agent', { expectsJsonBody: true }),
|
|
19
19
|
cmd('delete', 'DELETE', '/agents/:id', 'Delete an agent'),
|
|
20
|
+
cmd('trash', 'GET', '/agents/trash', 'List trashed agents'),
|
|
21
|
+
cmd('restore', 'POST', '/agents/trash', 'Restore a trashed agent', { expectsJsonBody: true }),
|
|
22
|
+
cmd('purge', 'DELETE', '/agents/trash', 'Permanently delete a trashed agent', { expectsJsonBody: true }),
|
|
20
23
|
cmd('thread', 'POST', '/agents/:id/thread', 'Get or create agent thread session'),
|
|
21
24
|
],
|
|
22
25
|
},
|
|
@@ -55,6 +58,7 @@ const COMMAND_GROUPS = [
|
|
|
55
58
|
cmd('create', 'POST', '/connectors', 'Create connector', { expectsJsonBody: true }),
|
|
56
59
|
cmd('update', 'PUT', '/connectors/:id', 'Update connector', { expectsJsonBody: true }),
|
|
57
60
|
cmd('delete', 'DELETE', '/connectors/:id', 'Delete connector'),
|
|
61
|
+
cmd('webhook', 'POST', '/connectors/:id/webhook', 'Trigger connector webhook ingress', { expectsJsonBody: true }),
|
|
58
62
|
cmd('start', 'PUT', '/connectors/:id', 'Start connector', {
|
|
59
63
|
expectsJsonBody: true,
|
|
60
64
|
defaultBody: { action: 'start' },
|
|
@@ -199,6 +203,42 @@ const COMMAND_GROUPS = [
|
|
|
199
203
|
expectsJsonBody: true,
|
|
200
204
|
waitEntityFrom: 'taskId',
|
|
201
205
|
}),
|
|
206
|
+
cmd('graph', 'GET', '/orchestrator/graph', 'Get orchestrator graph structure'),
|
|
207
|
+
],
|
|
208
|
+
},
|
|
209
|
+
{
|
|
210
|
+
name: 'openclaw',
|
|
211
|
+
description: 'OpenClaw discovery, gateway control, and runtime APIs',
|
|
212
|
+
commands: [
|
|
213
|
+
cmd('discover', 'GET', '/openclaw/discover', 'Discover OpenClaw gateways'),
|
|
214
|
+
cmd('directory', 'GET', '/openclaw/directory', 'List directory entries from running OpenClaw connectors'),
|
|
215
|
+
cmd('gateway-status', 'GET', '/openclaw/gateway', 'Check OpenClaw gateway connection status'),
|
|
216
|
+
cmd('gateway', 'POST', '/openclaw/gateway', 'Call OpenClaw gateway RPC/control action', { expectsJsonBody: true }),
|
|
217
|
+
cmd('config-sync', 'GET', '/openclaw/config-sync', 'Detect OpenClaw gateway config issues'),
|
|
218
|
+
cmd('config-sync-repair', 'POST', '/openclaw/config-sync', 'Repair a detected OpenClaw config issue', { expectsJsonBody: true }),
|
|
219
|
+
cmd('approvals', 'GET', '/openclaw/approvals', 'List pending OpenClaw execution approvals'),
|
|
220
|
+
cmd('approvals-resolve', 'POST', '/openclaw/approvals', 'Resolve an OpenClaw execution approval', { expectsJsonBody: true }),
|
|
221
|
+
cmd('cron', 'GET', '/openclaw/cron', 'List OpenClaw cron jobs'),
|
|
222
|
+
cmd('cron-action', 'POST', '/openclaw/cron', 'Create/run/remove OpenClaw cron jobs', { expectsJsonBody: true }),
|
|
223
|
+
cmd('agent-files', 'GET', '/openclaw/agent-files', 'Fetch OpenClaw agent files'),
|
|
224
|
+
cmd('agent-files-set', 'PUT', '/openclaw/agent-files', 'Save an OpenClaw agent file', { expectsJsonBody: true }),
|
|
225
|
+
cmd('dotenv-keys', 'GET', '/openclaw/dotenv-keys', 'List gateway .env keys'),
|
|
226
|
+
cmd('exec-config', 'GET', '/openclaw/exec-config', 'Fetch OpenClaw exec approval config'),
|
|
227
|
+
cmd('exec-config-set', 'PUT', '/openclaw/exec-config', 'Save OpenClaw exec approval config', { expectsJsonBody: true }),
|
|
228
|
+
cmd('history-preview', 'GET', '/openclaw/history', 'Preview OpenClaw session history'),
|
|
229
|
+
cmd('history-merge', 'POST', '/openclaw/history', 'Merge OpenClaw session history into local session', { expectsJsonBody: true }),
|
|
230
|
+
cmd('media', 'GET', '/openclaw/media', 'Proxy OpenClaw media/file content'),
|
|
231
|
+
cmd('models', 'GET', '/openclaw/models', 'List allowed OpenClaw models'),
|
|
232
|
+
cmd('permissions', 'GET', '/openclaw/permissions', 'Get OpenClaw permission preset/config'),
|
|
233
|
+
cmd('permissions-set', 'PUT', '/openclaw/permissions', 'Apply OpenClaw permission preset', { expectsJsonBody: true }),
|
|
234
|
+
cmd('sandbox-env', 'GET', '/openclaw/sandbox-env', 'List OpenClaw sandbox env allowlist'),
|
|
235
|
+
cmd('sandbox-env-set', 'PUT', '/openclaw/sandbox-env', 'Update OpenClaw sandbox env allowlist', { expectsJsonBody: true }),
|
|
236
|
+
cmd('skills', 'GET', '/openclaw/skills', 'List OpenClaw skills and eligibility'),
|
|
237
|
+
cmd('skills-update', 'PATCH', '/openclaw/skills', 'Update OpenClaw skill state/config', { expectsJsonBody: true }),
|
|
238
|
+
cmd('skills-save', 'PUT', '/openclaw/skills', 'Save OpenClaw skill allowlist mode/config', { expectsJsonBody: true }),
|
|
239
|
+
cmd('skills-install', 'POST', '/openclaw/skills/install', 'Install OpenClaw skill dependencies', { expectsJsonBody: true }),
|
|
240
|
+
cmd('skills-remove', 'POST', '/openclaw/skills/remove', 'Remove OpenClaw skill', { expectsJsonBody: true }),
|
|
241
|
+
cmd('sync', 'POST', '/openclaw/sync', 'Run OpenClaw sync action', { expectsJsonBody: true }),
|
|
202
242
|
],
|
|
203
243
|
},
|
|
204
244
|
{
|
|
@@ -208,6 +248,17 @@ const COMMAND_GROUPS = [
|
|
|
208
248
|
cmd('manage', 'POST', '/preview-server', 'Start/stop/status/detect preview server', { expectsJsonBody: true }),
|
|
209
249
|
],
|
|
210
250
|
},
|
|
251
|
+
{
|
|
252
|
+
name: 'projects',
|
|
253
|
+
description: 'Manage projects',
|
|
254
|
+
commands: [
|
|
255
|
+
cmd('list', 'GET', '/projects', 'List projects'),
|
|
256
|
+
cmd('get', 'GET', '/projects/:id', 'Get project by id'),
|
|
257
|
+
cmd('create', 'POST', '/projects', 'Create project', { expectsJsonBody: true }),
|
|
258
|
+
cmd('update', 'PUT', '/projects/:id', 'Update project', { expectsJsonBody: true }),
|
|
259
|
+
cmd('delete', 'DELETE', '/projects/:id', 'Delete project'),
|
|
260
|
+
],
|
|
261
|
+
},
|
|
211
262
|
{
|
|
212
263
|
name: 'plugins',
|
|
213
264
|
description: 'Manage plugins and marketplace',
|
|
@@ -281,6 +332,9 @@ const COMMAND_GROUPS = [
|
|
|
281
332
|
defaultBody: { action: 'disable_all' },
|
|
282
333
|
}),
|
|
283
334
|
cmd('messages', 'GET', '/sessions/:id/messages', 'Get session messages'),
|
|
335
|
+
cmd('messages-update', 'PUT', '/sessions/:id/messages', 'Update session message metadata (e.g. bookmark)', { expectsJsonBody: true }),
|
|
336
|
+
cmd('fork', 'POST', '/sessions/:id/fork', 'Fork session from a specific message index', { expectsJsonBody: true }),
|
|
337
|
+
cmd('edit-resend', 'POST', '/sessions/:id/edit-resend', 'Edit and resend from a specific message index', { expectsJsonBody: true }),
|
|
284
338
|
cmd('main-loop', 'GET', '/sessions/:id/main-loop', 'Get main mission loop state'),
|
|
285
339
|
cmd('main-loop-action', 'POST', '/sessions/:id/main-loop', 'Control main mission loop (pause/resume/set_goal/set_mode/clear_events/nudge)', {
|
|
286
340
|
expectsJsonBody: true,
|
|
@@ -351,6 +405,7 @@ const COMMAND_GROUPS = [
|
|
|
351
405
|
cmd('update', 'PUT', '/tasks/:id', 'Update task', { expectsJsonBody: true }),
|
|
352
406
|
cmd('delete', 'DELETE', '/tasks/:id', 'Delete task'),
|
|
353
407
|
cmd('purge', 'DELETE', '/tasks', 'Bulk delete tasks', { expectsJsonBody: true }),
|
|
408
|
+
cmd('approve', 'POST', '/tasks/:id/approve', 'Approve or reject a pending tool execution', { expectsJsonBody: true }),
|
|
354
409
|
],
|
|
355
410
|
},
|
|
356
411
|
{
|
|
@@ -362,6 +417,11 @@ const COMMAND_GROUPS = [
|
|
|
362
417
|
responseType: 'binary',
|
|
363
418
|
bodyFlagMap: { text: 'text' },
|
|
364
419
|
}),
|
|
420
|
+
cmd('stream', 'POST', '/tts/stream', 'Generate streaming TTS audio', {
|
|
421
|
+
expectsJsonBody: true,
|
|
422
|
+
responseType: 'binary',
|
|
423
|
+
bodyFlagMap: { text: 'text' },
|
|
424
|
+
}),
|
|
365
425
|
],
|
|
366
426
|
},
|
|
367
427
|
{
|
package/src/cli/index.ts
CHANGED
|
@@ -928,7 +928,7 @@ export function buildProgram(): Command {
|
|
|
928
928
|
connectors
|
|
929
929
|
.command('create')
|
|
930
930
|
.description('Create connector')
|
|
931
|
-
.requiredOption('--platform <platform>', 'Connector platform (discord|telegram|slack|whatsapp|openclaw)')
|
|
931
|
+
.requiredOption('--platform <platform>', 'Connector platform (discord|telegram|slack|whatsapp|openclaw|bluebubbles|signal|teams|googlechat|matrix)')
|
|
932
932
|
.requiredOption('--agent-id <agentId>', 'Agent id')
|
|
933
933
|
.option('--name <name>', 'Connector name')
|
|
934
934
|
.option('--credential-id <credentialId>', 'Credential id')
|
package/src/cli/spec.js
CHANGED
|
@@ -7,6 +7,9 @@ const COMMAND_GROUPS = {
|
|
|
7
7
|
create: { description: 'Create an agent', method: 'POST', path: '/agents' },
|
|
8
8
|
update: { description: 'Update an agent', method: 'PUT', path: '/agents/:id', params: ['id'] },
|
|
9
9
|
delete: { description: 'Delete an agent', method: 'DELETE', path: '/agents/:id', params: ['id'] },
|
|
10
|
+
trash: { description: 'List trashed agents', method: 'GET', path: '/agents/trash' },
|
|
11
|
+
restore: { description: 'Restore a trashed agent', method: 'POST', path: '/agents/trash' },
|
|
12
|
+
purge: { description: 'Permanently delete a trashed agent', method: 'DELETE', path: '/agents/trash' },
|
|
10
13
|
},
|
|
11
14
|
},
|
|
12
15
|
auth: {
|
|
@@ -127,6 +130,41 @@ const COMMAND_GROUPS = {
|
|
|
127
130
|
run: { description: 'Run orchestrator task now', method: 'POST', path: '/orchestrator/run', waitable: true },
|
|
128
131
|
runs: { description: 'List queued/running/completed runs', method: 'GET', path: '/runs' },
|
|
129
132
|
'run-get': { description: 'Get run by id', method: 'GET', path: '/runs/:id', params: ['id'] },
|
|
133
|
+
graph: { description: 'Get orchestrator graph structure', method: 'GET', path: '/orchestrator/graph' },
|
|
134
|
+
},
|
|
135
|
+
},
|
|
136
|
+
openclaw: {
|
|
137
|
+
description: 'OpenClaw discovery, gateway control, and runtime APIs',
|
|
138
|
+
commands: {
|
|
139
|
+
discover: { description: 'Discover OpenClaw gateways', method: 'GET', path: '/openclaw/discover' },
|
|
140
|
+
directory: { description: 'List directory entries from running OpenClaw connectors', method: 'GET', path: '/openclaw/directory' },
|
|
141
|
+
'gateway-status': { description: 'Check OpenClaw gateway connection status', method: 'GET', path: '/openclaw/gateway' },
|
|
142
|
+
gateway: { description: 'Call OpenClaw gateway RPC/control action', method: 'POST', path: '/openclaw/gateway' },
|
|
143
|
+
'config-sync': { description: 'Detect OpenClaw gateway config issues', method: 'GET', path: '/openclaw/config-sync' },
|
|
144
|
+
'config-sync-repair': { description: 'Repair a detected OpenClaw config issue', method: 'POST', path: '/openclaw/config-sync' },
|
|
145
|
+
approvals: { description: 'List pending OpenClaw execution approvals', method: 'GET', path: '/openclaw/approvals' },
|
|
146
|
+
'approvals-resolve': { description: 'Resolve an OpenClaw execution approval', method: 'POST', path: '/openclaw/approvals' },
|
|
147
|
+
cron: { description: 'List OpenClaw cron jobs', method: 'GET', path: '/openclaw/cron' },
|
|
148
|
+
'cron-action': { description: 'Create/run/remove OpenClaw cron jobs', method: 'POST', path: '/openclaw/cron' },
|
|
149
|
+
'agent-files': { description: 'Fetch OpenClaw agent files', method: 'GET', path: '/openclaw/agent-files' },
|
|
150
|
+
'agent-files-set': { description: 'Save an OpenClaw agent file', method: 'PUT', path: '/openclaw/agent-files' },
|
|
151
|
+
'dotenv-keys': { description: 'List gateway .env keys', method: 'GET', path: '/openclaw/dotenv-keys' },
|
|
152
|
+
'exec-config': { description: 'Fetch OpenClaw exec approval config', method: 'GET', path: '/openclaw/exec-config' },
|
|
153
|
+
'exec-config-set': { description: 'Save OpenClaw exec approval config', method: 'PUT', path: '/openclaw/exec-config' },
|
|
154
|
+
'history-preview': { description: 'Preview OpenClaw session history', method: 'GET', path: '/openclaw/history' },
|
|
155
|
+
'history-merge': { description: 'Merge OpenClaw session history into local session', method: 'POST', path: '/openclaw/history' },
|
|
156
|
+
media: { description: 'Proxy OpenClaw media/file content', method: 'GET', path: '/openclaw/media' },
|
|
157
|
+
models: { description: 'List allowed OpenClaw models', method: 'GET', path: '/openclaw/models' },
|
|
158
|
+
permissions: { description: 'Get OpenClaw permission preset/config', method: 'GET', path: '/openclaw/permissions' },
|
|
159
|
+
'permissions-set': { description: 'Apply OpenClaw permission preset', method: 'PUT', path: '/openclaw/permissions' },
|
|
160
|
+
'sandbox-env': { description: 'List OpenClaw sandbox env allowlist', method: 'GET', path: '/openclaw/sandbox-env' },
|
|
161
|
+
'sandbox-env-set': { description: 'Update OpenClaw sandbox env allowlist', method: 'PUT', path: '/openclaw/sandbox-env' },
|
|
162
|
+
skills: { description: 'List OpenClaw skills and eligibility', method: 'GET', path: '/openclaw/skills' },
|
|
163
|
+
'skills-update': { description: 'Update OpenClaw skill state/config', method: 'PATCH', path: '/openclaw/skills' },
|
|
164
|
+
'skills-save': { description: 'Save OpenClaw skill allowlist mode/config', method: 'PUT', path: '/openclaw/skills' },
|
|
165
|
+
'skills-install': { description: 'Install OpenClaw skill dependencies', method: 'POST', path: '/openclaw/skills/install' },
|
|
166
|
+
'skills-remove': { description: 'Remove OpenClaw skill', method: 'POST', path: '/openclaw/skills/remove' },
|
|
167
|
+
sync: { description: 'Run OpenClaw sync action', method: 'POST', path: '/openclaw/sync' },
|
|
130
168
|
},
|
|
131
169
|
},
|
|
132
170
|
plugins: {
|
|
@@ -186,6 +224,9 @@ const COMMAND_GROUPS = {
|
|
|
186
224
|
'delete-many': { description: 'Delete multiple sessions (body: {"ids":[...]})', method: 'DELETE', path: '/sessions' },
|
|
187
225
|
'heartbeat-disable-all': { description: 'Disable all session heartbeats and cancel queued heartbeat runs', method: 'POST', path: '/sessions/heartbeat' },
|
|
188
226
|
messages: { description: 'Get session message history', method: 'GET', path: '/sessions/:id/messages', params: ['id'] },
|
|
227
|
+
'messages-update': { description: 'Update session message metadata (e.g. bookmark)', method: 'PUT', path: '/sessions/:id/messages', params: ['id'] },
|
|
228
|
+
fork: { description: 'Fork session from a specific message index', method: 'POST', path: '/sessions/:id/fork', params: ['id'] },
|
|
229
|
+
'edit-resend': { description: 'Edit and resend from a specific message index', method: 'POST', path: '/sessions/:id/edit-resend', params: ['id'] },
|
|
189
230
|
'main-loop': { description: 'Get main mission loop state for a session', method: 'GET', path: '/sessions/:id/main-loop', params: ['id'] },
|
|
190
231
|
'main-loop-action': { description: 'Control main mission loop (pause/resume/set_goal/set_mode/clear_events/nudge)', method: 'POST', path: '/sessions/:id/main-loop', params: ['id'] },
|
|
191
232
|
chat: { description: 'Send chat message (SSE stream)', method: 'POST', path: '/sessions/:id/chat', params: ['id'], stream: true, waitable: true },
|
|
@@ -243,6 +284,7 @@ const COMMAND_GROUPS = {
|
|
|
243
284
|
update: { description: 'Update task', method: 'PUT', path: '/tasks/:id', params: ['id'] },
|
|
244
285
|
delete: { description: 'Archive task', method: 'DELETE', path: '/tasks/:id', params: ['id'] },
|
|
245
286
|
archive: { description: 'Archive task', method: 'DELETE', path: '/tasks/:id', params: ['id'] },
|
|
287
|
+
approve: { description: 'Approve or reject a pending tool execution', method: 'POST', path: '/tasks/:id/approve', params: ['id'] },
|
|
246
288
|
},
|
|
247
289
|
},
|
|
248
290
|
webhooks: {
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { useMemo } from 'react'
|
|
4
|
+
import multiavatar from '@multiavatar/multiavatar'
|
|
5
|
+
|
|
6
|
+
interface Props {
|
|
7
|
+
seed?: string | null
|
|
8
|
+
name: string
|
|
9
|
+
size?: number
|
|
10
|
+
className?: string
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function AgentAvatar({ seed, name, size = 32, className = '' }: Props) {
|
|
14
|
+
const svgHtml = useMemo(() => {
|
|
15
|
+
if (!seed) return null
|
|
16
|
+
return multiavatar(seed)
|
|
17
|
+
}, [seed])
|
|
18
|
+
|
|
19
|
+
if (svgHtml) {
|
|
20
|
+
return (
|
|
21
|
+
<div
|
|
22
|
+
className={`shrink-0 rounded-full overflow-hidden ${className}`}
|
|
23
|
+
style={{ width: size, height: size }}
|
|
24
|
+
dangerouslySetInnerHTML={{ __html: svgHtml }}
|
|
25
|
+
/>
|
|
26
|
+
)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Fallback: initials
|
|
30
|
+
const initials = name
|
|
31
|
+
.split(/\s+/)
|
|
32
|
+
.slice(0, 2)
|
|
33
|
+
.map((w) => w[0] || '')
|
|
34
|
+
.join('')
|
|
35
|
+
.toUpperCase()
|
|
36
|
+
|
|
37
|
+
return (
|
|
38
|
+
<div
|
|
39
|
+
className={`shrink-0 rounded-full flex items-center justify-center bg-accent-soft text-accent-bright font-600 ${className}`}
|
|
40
|
+
style={{ width: size, height: size, fontSize: size * 0.38 }}
|
|
41
|
+
>
|
|
42
|
+
{initials || '?'}
|
|
43
|
+
</div>
|
|
44
|
+
)
|
|
45
|
+
}
|
|
@@ -15,14 +15,17 @@ import {
|
|
|
15
15
|
DropdownMenuTrigger,
|
|
16
16
|
} from '@/components/ui/dropdown-menu'
|
|
17
17
|
import { ConfirmDialog } from '@/components/shared/confirm-dialog'
|
|
18
|
+
import { useApprovalStore } from '@/stores/use-approval-store'
|
|
19
|
+
import { AgentAvatar } from './agent-avatar'
|
|
18
20
|
|
|
19
21
|
interface Props {
|
|
20
22
|
agent: Agent
|
|
21
23
|
isDefault?: boolean
|
|
24
|
+
isRunning?: boolean
|
|
22
25
|
onSetDefault?: (id: string) => void
|
|
23
26
|
}
|
|
24
27
|
|
|
25
|
-
export function AgentCard({ agent, isDefault, onSetDefault }: Props) {
|
|
28
|
+
export function AgentCard({ agent, isDefault, isRunning, onSetDefault }: Props) {
|
|
26
29
|
const setEditingAgentId = useAppStore((s) => s.setEditingAgentId)
|
|
27
30
|
const setAgentSheetOpen = useAppStore((s) => s.setAgentSheetOpen)
|
|
28
31
|
const loadSessions = useAppStore((s) => s.loadSessions)
|
|
@@ -34,6 +37,8 @@ export function AgentCard({ agent, isDefault, onSetDefault }: Props) {
|
|
|
34
37
|
const [dialogOpen, setDialogOpen] = useState(false)
|
|
35
38
|
const [taskInput, setTaskInput] = useState('')
|
|
36
39
|
const [confirmDelete, setConfirmDelete] = useState(false)
|
|
40
|
+
const approvals = useApprovalStore((s) => s.approvals)
|
|
41
|
+
const pendingApprovalCount = Object.values(approvals).filter((a) => a.agentId === agent.id).length
|
|
37
42
|
|
|
38
43
|
const handleClick = () => {
|
|
39
44
|
setEditingAgentId(agent.id)
|
|
@@ -111,13 +116,22 @@ export function AgentCard({ agent, isDefault, onSetDefault }: Props) {
|
|
|
111
116
|
onClick={() => setConfirmDelete(true)}
|
|
112
117
|
className="text-red-400 focus:text-red-400"
|
|
113
118
|
>
|
|
114
|
-
|
|
119
|
+
Move to Trash
|
|
115
120
|
</DropdownMenuItem>
|
|
116
121
|
</DropdownMenuContent>
|
|
117
122
|
</DropdownMenu>
|
|
118
123
|
|
|
119
124
|
<div className="flex items-center gap-2.5">
|
|
125
|
+
<AgentAvatar seed={agent.avatarSeed} name={agent.name} size={28} />
|
|
126
|
+
{isRunning && (
|
|
127
|
+
<span className="shrink-0 w-2 h-2 rounded-full bg-emerald-400" style={{ animation: 'pulse 2s ease infinite' }} title="Running" />
|
|
128
|
+
)}
|
|
120
129
|
<span className="font-display text-[14px] font-600 truncate flex-1 tracking-[-0.01em]">{agent.name}</span>
|
|
130
|
+
{pendingApprovalCount > 0 && (
|
|
131
|
+
<span className="shrink-0 inline-flex items-center justify-center min-w-[18px] h-[18px] px-1 rounded-full bg-red-500 text-white text-[10px] font-700">
|
|
132
|
+
{pendingApprovalCount}
|
|
133
|
+
</span>
|
|
134
|
+
)}
|
|
121
135
|
{isDefault && (
|
|
122
136
|
<span className="shrink-0 text-[10px] font-600 uppercase tracking-wider text-accent-bright bg-accent-soft px-2 py-0.5 rounded-[6px]">
|
|
123
137
|
default
|
|
@@ -207,9 +221,9 @@ export function AgentCard({ agent, isDefault, onSetDefault }: Props) {
|
|
|
207
221
|
|
|
208
222
|
<ConfirmDialog
|
|
209
223
|
open={confirmDelete}
|
|
210
|
-
title="
|
|
211
|
-
message={`
|
|
212
|
-
confirmLabel="
|
|
224
|
+
title="Move to Trash"
|
|
225
|
+
message={`Move "${agent.name}" to trash? You can restore it later from the trash.`}
|
|
226
|
+
confirmLabel="Move to Trash"
|
|
213
227
|
danger
|
|
214
228
|
onConfirm={handleDelete}
|
|
215
229
|
onCancel={() => setConfirmDelete(false)}
|