@swarmclawai/swarmclaw 0.7.2 → 0.7.4
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 +116 -50
- package/bin/package-manager.js +157 -0
- package/bin/package-manager.test.js +90 -0
- package/bin/server-cmd.js +38 -7
- package/bin/swarmclaw.js +54 -4
- package/bin/update-cmd.js +48 -10
- package/bin/update-cmd.test.js +55 -0
- package/package.json +8 -3
- package/scripts/postinstall.mjs +26 -0
- package/src/app/api/agents/[id]/route.ts +43 -0
- package/src/app/api/agents/[id]/thread/route.ts +39 -8
- package/src/app/api/agents/route.ts +35 -2
- package/src/app/api/auth/route.ts +77 -8
- package/src/app/api/chatrooms/[id]/chat/route.ts +22 -6
- package/src/app/api/chatrooms/[id]/pins/route.ts +2 -1
- package/src/app/api/chatrooms/[id]/reactions/route.ts +2 -1
- package/src/app/api/chatrooms/[id]/route.ts +6 -0
- package/src/app/api/chats/[id]/browser/route.ts +5 -1
- package/src/app/api/chats/[id]/chat/route.ts +7 -3
- package/src/app/api/chats/[id]/messages/route.ts +19 -13
- package/src/app/api/chats/[id]/route.ts +30 -0
- package/src/app/api/chats/[id]/stop/route.ts +6 -1
- package/src/app/api/chats/heartbeat/route.ts +2 -1
- package/src/app/api/chats/route.ts +23 -1
- 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/external-agents/[id]/heartbeat/route.ts +33 -0
- package/src/app/api/external-agents/[id]/route.ts +31 -0
- package/src/app/api/external-agents/register/route.ts +3 -0
- package/src/app/api/external-agents/route.ts +66 -0
- package/src/app/api/files/open/route.ts +16 -14
- package/src/app/api/gateways/[id]/health/route.ts +28 -0
- package/src/app/api/gateways/[id]/route.ts +79 -0
- package/src/app/api/gateways/route.ts +57 -0
- 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/gateway/route.ts +10 -7
- package/src/app/api/openclaw/skills/route.ts +12 -4
- 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 +3 -26
- package/src/app/api/plugins/settings/route.ts +17 -12
- package/src/app/api/plugins/ui/route.ts +1 -0
- package/src/app/api/providers/[id]/discover-models/route.ts +27 -0
- package/src/app/api/schedules/[id]/route.ts +38 -9
- package/src/app/api/schedules/route.ts +51 -28
- package/src/app/api/settings/route.ts +55 -17
- package/src/app/api/setup/doctor/route.ts +6 -4
- package/src/app/api/tasks/[id]/route.ts +16 -6
- package/src/app/api/tasks/bulk/route.ts +3 -3
- package/src/app/api/tasks/route.ts +9 -4
- package/src/app/api/webhooks/[id]/route.ts +8 -1
- package/src/app/page.tsx +135 -17
- package/src/cli/binary.test.js +142 -0
- package/src/cli/index.js +38 -11
- package/src/cli/index.test.js +195 -0
- package/src/cli/index.ts +21 -12
- package/src/cli/server-cmd.test.js +59 -0
- package/src/cli/spec.js +20 -2
- package/src/components/agents/agent-card.tsx +15 -12
- package/src/components/agents/agent-chat-list.tsx +101 -1
- package/src/components/agents/agent-list.tsx +46 -9
- package/src/components/agents/agent-sheet.tsx +456 -23
- package/src/components/agents/inspector-panel.tsx +110 -49
- package/src/components/agents/sandbox-env-panel.tsx +4 -1
- package/src/components/auth/access-key-gate.tsx +36 -97
- package/src/components/auth/setup-wizard.tsx +970 -275
- package/src/components/chat/chat-area.tsx +70 -27
- package/src/components/chat/chat-card.tsx +6 -21
- package/src/components/chat/chat-header.tsx +263 -366
- package/src/components/chat/chat-list.tsx +62 -26
- package/src/components/chat/checkpoint-timeline.tsx +1 -1
- package/src/components/chat/message-list.tsx +145 -19
- package/src/components/chatrooms/chatroom-input.tsx +96 -33
- package/src/components/chatrooms/chatroom-list.tsx +141 -72
- package/src/components/chatrooms/chatroom-message.tsx +7 -6
- package/src/components/chatrooms/chatroom-sheet.tsx +13 -1
- package/src/components/chatrooms/chatroom-tool-request-banner.tsx +5 -2
- package/src/components/chatrooms/chatroom-view.tsx +422 -209
- package/src/components/chatrooms/reaction-picker.tsx +38 -33
- package/src/components/connectors/connector-list.tsx +265 -127
- package/src/components/connectors/connector-sheet.tsx +217 -0
- package/src/components/gateways/gateway-sheet.tsx +567 -0
- package/src/components/home/home-view.tsx +128 -4
- package/src/components/input/chat-input.tsx +135 -86
- package/src/components/layout/app-layout.tsx +385 -194
- package/src/components/layout/mobile-header.tsx +26 -8
- package/src/components/memory/memory-browser.tsx +71 -6
- package/src/components/memory/memory-card.tsx +18 -0
- package/src/components/memory/memory-detail.tsx +58 -31
- package/src/components/memory/memory-sheet.tsx +32 -4
- package/src/components/plugins/plugin-list.tsx +15 -3
- package/src/components/plugins/plugin-sheet.tsx +118 -9
- package/src/components/projects/project-detail.tsx +189 -1
- package/src/components/providers/provider-list.tsx +158 -2
- package/src/components/providers/provider-sheet.tsx +81 -70
- package/src/components/shared/agent-picker-list.tsx +2 -2
- package/src/components/shared/bottom-sheet.tsx +31 -15
- package/src/components/shared/command-palette.tsx +111 -24
- package/src/components/shared/confirm-dialog.tsx +45 -30
- package/src/components/shared/model-combobox.tsx +90 -8
- 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 +88 -6
- package/src/components/shared/settings/section-orchestrator.tsx +6 -3
- 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 +248 -47
- package/src/components/tasks/approvals-panel.tsx +211 -18
- package/src/components/tasks/task-board.tsx +242 -46
- package/src/components/ui/dialog.tsx +2 -2
- package/src/components/usage/metrics-dashboard.tsx +74 -1
- package/src/components/wallets/wallet-approval-dialog.tsx +59 -54
- package/src/components/wallets/wallet-panel.tsx +17 -5
- package/src/components/webhooks/webhook-sheet.tsx +7 -7
- 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/heartbeat-defaults.ts +48 -0
- package/src/lib/memory-presentation.ts +59 -0
- package/src/lib/openclaw-agent-id.test.ts +14 -0
- package/src/lib/openclaw-agent-id.ts +31 -0
- package/src/lib/provider-model-discovery-client.ts +29 -0
- package/src/lib/providers/index.ts +12 -5
- package/src/lib/runtime-loop.ts +105 -3
- package/src/lib/safe-storage.ts +6 -1
- package/src/lib/server/agent-assignment.test.ts +112 -0
- package/src/lib/server/agent-assignment.ts +169 -0
- package/src/lib/server/agent-runtime-config.test.ts +141 -0
- package/src/lib/server/agent-runtime-config.ts +277 -0
- package/src/lib/server/approval-connector-notify.test.ts +253 -0
- package/src/lib/server/approvals-auto-approve.test.ts +264 -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 +44 -0
- package/src/lib/server/build-llm.ts +11 -4
- package/src/lib/server/builtin-plugins.ts +34 -0
- package/src/lib/server/chat-execution-heartbeat.test.ts +40 -0
- package/src/lib/server/chat-execution-tool-events.test.ts +219 -0
- package/src/lib/server/chat-execution.ts +402 -125
- 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 +74 -2
- package/src/lib/server/chatroom-helpers.ts +144 -11
- package/src/lib/server/chatroom-session-persistence.test.ts +87 -0
- 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 +994 -130
- 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 +189 -10
- 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/daemon-state.ts +62 -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/eval/agent-regression.test.ts +47 -0
- package/src/lib/server/eval/agent-regression.ts +1742 -0
- package/src/lib/server/eval/runner.ts +11 -1
- package/src/lib/server/eval/store.ts +2 -1
- package/src/lib/server/heartbeat-service.ts +23 -43
- 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 +31 -964
- 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 +6 -5
- package/src/lib/server/openclaw-gateway.ts +123 -36
- 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 +18 -8
- package/src/lib/server/orchestrator.ts +5 -4
- package/src/lib/server/playwright-proxy.mjs +27 -3
- package/src/lib/server/plugins.test.ts +215 -0
- package/src/lib/server/plugins.ts +832 -69
- package/src/lib/server/provider-health.ts +33 -3
- package/src/lib/server/provider-model-discovery.ts +481 -0
- package/src/lib/server/queue.ts +4 -21
- package/src/lib/server/runtime-settings.test.ts +119 -0
- package/src/lib/server/runtime-settings.ts +12 -92
- package/src/lib/server/schedule-normalization.ts +187 -0
- 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 -80
- package/src/lib/server/session-tools/autonomy-tools.test.ts +128 -0
- package/src/lib/server/session-tools/calendar.ts +2 -12
- package/src/lib/server/session-tools/connector.ts +109 -8
- package/src/lib/server/session-tools/context.ts +14 -2
- package/src/lib/server/session-tools/crawl.ts +447 -0
- package/src/lib/server/session-tools/crud.ts +96 -34
- package/src/lib/server/session-tools/delegate-fallback.test.ts +219 -0
- package/src/lib/server/session-tools/delegate.ts +406 -20
- package/src/lib/server/session-tools/discovery-approvals.test.ts +170 -0
- package/src/lib/server/session-tools/discovery.ts +40 -12
- package/src/lib/server/session-tools/document.ts +283 -0
- package/src/lib/server/session-tools/email.ts +1 -3
- package/src/lib/server/session-tools/extract.ts +137 -0
- package/src/lib/server/session-tools/file-normalize.test.ts +98 -0
- package/src/lib/server/session-tools/file-send.test.ts +84 -1
- package/src/lib/server/session-tools/file.ts +243 -24
- package/src/lib/server/session-tools/http.ts +9 -3
- package/src/lib/server/session-tools/human-loop.ts +227 -0
- package/src/lib/server/session-tools/image-gen.ts +1 -3
- package/src/lib/server/session-tools/index.ts +87 -2
- package/src/lib/server/session-tools/mailbox.ts +276 -0
- package/src/lib/server/session-tools/manage-schedules.test.ts +137 -0
- package/src/lib/server/session-tools/memory.ts +35 -3
- package/src/lib/server/session-tools/monitor.ts +162 -12
- package/src/lib/server/session-tools/normalize-tool-args.ts +17 -14
- package/src/lib/server/session-tools/openclaw-nodes.test.ts +111 -0
- package/src/lib/server/session-tools/openclaw-nodes.ts +86 -20
- package/src/lib/server/session-tools/platform-normalize.test.ts +142 -0
- package/src/lib/server/session-tools/platform.ts +142 -4
- package/src/lib/server/session-tools/plugin-creator.ts +95 -25
- package/src/lib/server/session-tools/primitive-tools.test.ts +257 -0
- package/src/lib/server/session-tools/replicate.ts +1 -3
- package/src/lib/server/session-tools/sandbox.ts +51 -92
- package/src/lib/server/session-tools/schedule.ts +20 -10
- package/src/lib/server/session-tools/session-info.ts +58 -4
- package/src/lib/server/session-tools/session-tools-wiring.test.ts +54 -17
- package/src/lib/server/session-tools/shell.ts +2 -2
- package/src/lib/server/session-tools/subagent.ts +195 -27
- package/src/lib/server/session-tools/table.ts +587 -0
- package/src/lib/server/session-tools/wallet.ts +13 -10
- package/src/lib/server/session-tools/web-browser-config.test.ts +39 -0
- package/src/lib/server/session-tools/web.ts +947 -108
- package/src/lib/server/storage.ts +255 -10
- package/src/lib/server/stream-agent-chat.test.ts +61 -0
- package/src/lib/server/stream-agent-chat.ts +185 -25
- 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 -11
- package/src/lib/server/tool-aliases.ts +80 -12
- package/src/lib/server/tool-capability-policy.ts +7 -1
- 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/setup-defaults.ts +352 -11
- package/src/lib/tool-definitions.ts +3 -4
- package/src/lib/validation/schemas.test.ts +26 -0
- package/src/lib/validation/schemas.ts +62 -1
- package/src/lib/ws-client.ts +14 -12
- package/src/proxy.ts +5 -5
- package/src/stores/use-app-store.ts +43 -7
- package/src/stores/use-chat-store.ts +31 -2
- package/src/stores/use-chatroom-store.ts +153 -26
- package/src/types/index.ts +470 -44
- package/src/app/api/chats/[id]/main-loop/route.ts +0 -94
- package/src/components/chat/new-chat-sheet.tsx +0 -253
- package/src/lib/server/main-session.ts +0 -17
- package/src/lib/server/session-run-manager.test.ts +0 -26
|
@@ -1,27 +1,96 @@
|
|
|
1
1
|
import { NextResponse } from 'next/server'
|
|
2
|
-
import { validateAccessKey,
|
|
2
|
+
import { validateAccessKey, isFirstTimeSetup, markSetupComplete } from '@/lib/server/storage'
|
|
3
3
|
import { ensureDaemonStarted } from '@/lib/server/daemon-state'
|
|
4
|
+
import { AUTH_COOKIE_NAME, getCookieValue } from '@/lib/auth'
|
|
4
5
|
export const dynamic = 'force-dynamic'
|
|
5
6
|
|
|
7
|
+
interface AuthAttemptEntry {
|
|
8
|
+
count: number
|
|
9
|
+
lockedUntil: number
|
|
10
|
+
}
|
|
6
11
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
12
|
+
const authRateLimitMap = (
|
|
13
|
+
(globalThis as Record<string, unknown>).__swarmclaw_auth_rate_limit__ ??= new Map()
|
|
14
|
+
) as Map<string, AuthAttemptEntry>
|
|
15
|
+
|
|
16
|
+
const MAX_ATTEMPTS = 5
|
|
17
|
+
const LOCKOUT_MS = 15 * 60 * 1000
|
|
18
|
+
|
|
19
|
+
function getClientIp(req: Request): string {
|
|
20
|
+
const forwarded = req.headers.get('x-forwarded-for')
|
|
21
|
+
if (forwarded) {
|
|
22
|
+
const first = forwarded.split(',')[0]?.trim()
|
|
23
|
+
if (first) return first
|
|
11
24
|
}
|
|
12
|
-
|
|
25
|
+
const realIp = req.headers.get('x-real-ip')?.trim()
|
|
26
|
+
return realIp || 'unknown'
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function clearAuthCookie(response: NextResponse): NextResponse {
|
|
30
|
+
response.cookies.set(AUTH_COOKIE_NAME, '', {
|
|
31
|
+
httpOnly: true,
|
|
32
|
+
sameSite: 'lax',
|
|
33
|
+
secure: false,
|
|
34
|
+
path: '/',
|
|
35
|
+
maxAge: 0,
|
|
36
|
+
})
|
|
37
|
+
return response
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function setAuthCookie(response: NextResponse, req: Request, key: string): NextResponse {
|
|
41
|
+
response.cookies.set(AUTH_COOKIE_NAME, key, {
|
|
42
|
+
httpOnly: true,
|
|
43
|
+
sameSite: 'lax',
|
|
44
|
+
secure: new URL(req.url).protocol === 'https:',
|
|
45
|
+
path: '/',
|
|
46
|
+
maxAge: 60 * 60 * 24 * 30,
|
|
47
|
+
})
|
|
48
|
+
return response
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** GET /api/auth — returns setup state and whether the auth cookie is currently valid */
|
|
52
|
+
export async function GET(req: Request) {
|
|
53
|
+
const cookieKey = getCookieValue(req.headers.get('cookie'), AUTH_COOKIE_NAME)
|
|
54
|
+
return NextResponse.json({
|
|
55
|
+
firstTime: isFirstTimeSetup(),
|
|
56
|
+
authenticated: !!cookieKey && validateAccessKey(cookieKey),
|
|
57
|
+
})
|
|
13
58
|
}
|
|
14
59
|
|
|
15
60
|
/** POST /api/auth — validate an access key */
|
|
16
61
|
export async function POST(req: Request) {
|
|
62
|
+
const clientIp = getClientIp(req)
|
|
63
|
+
const entry = authRateLimitMap.get(clientIp)
|
|
64
|
+
if (entry && entry.lockedUntil > Date.now()) {
|
|
65
|
+
const retryAfter = Math.ceil((entry.lockedUntil - Date.now()) / 1000)
|
|
66
|
+
return clearAuthCookie(NextResponse.json(
|
|
67
|
+
{ error: 'Too many failed attempts. Try again later.', retryAfter },
|
|
68
|
+
{ status: 429, headers: { 'Retry-After': String(retryAfter) } },
|
|
69
|
+
))
|
|
70
|
+
}
|
|
71
|
+
|
|
17
72
|
const { key } = await req.json()
|
|
18
73
|
if (!key || !validateAccessKey(key)) {
|
|
19
|
-
|
|
74
|
+
const current = authRateLimitMap.get(clientIp) ?? { count: 0, lockedUntil: 0 }
|
|
75
|
+
current.count += 1
|
|
76
|
+
if (current.count >= MAX_ATTEMPTS) {
|
|
77
|
+
current.lockedUntil = Date.now() + LOCKOUT_MS
|
|
78
|
+
}
|
|
79
|
+
authRateLimitMap.set(clientIp, current)
|
|
80
|
+
return clearAuthCookie(NextResponse.json(
|
|
81
|
+
{ error: 'Invalid access key' },
|
|
82
|
+
{
|
|
83
|
+
status: 401,
|
|
84
|
+
headers: { 'X-RateLimit-Remaining': String(Math.max(0, MAX_ATTEMPTS - current.count)) },
|
|
85
|
+
},
|
|
86
|
+
))
|
|
20
87
|
}
|
|
88
|
+
|
|
89
|
+
authRateLimitMap.delete(clientIp)
|
|
21
90
|
// If this was first-time setup, mark it as claimed
|
|
22
91
|
if (isFirstTimeSetup()) {
|
|
23
92
|
markSetupComplete()
|
|
24
93
|
}
|
|
25
94
|
ensureDaemonStarted('api/auth:post')
|
|
26
|
-
return NextResponse.json({ ok: true })
|
|
95
|
+
return setAuthCookie(NextResponse.json({ ok: true }), req, key)
|
|
27
96
|
}
|
|
@@ -8,9 +8,12 @@ import { getProvider } from '@/lib/providers'
|
|
|
8
8
|
import {
|
|
9
9
|
resolveApiKey,
|
|
10
10
|
parseMentions,
|
|
11
|
+
resolveReplyTargetAgentId,
|
|
12
|
+
resolveAgentApiEndpoint,
|
|
11
13
|
compactChatroomMessages,
|
|
12
14
|
buildChatroomSystemPrompt,
|
|
13
|
-
|
|
15
|
+
ensureSyntheticSession,
|
|
16
|
+
appendSyntheticSessionMessage,
|
|
14
17
|
buildAgentSystemPromptForChatroom,
|
|
15
18
|
buildHistoryForAgent,
|
|
16
19
|
isMuted,
|
|
@@ -19,6 +22,7 @@ import { filterHealthyChatroomAgents } from '@/lib/server/chatroom-health'
|
|
|
19
22
|
import { evaluateRoutingRules } from '@/lib/server/chatroom-routing'
|
|
20
23
|
import { markProviderFailure, markProviderSuccess } from '@/lib/server/provider-health'
|
|
21
24
|
import { applyAgentReactionsFromText } from '@/lib/server/chatroom-orchestration'
|
|
25
|
+
import { resolvePrimaryAgentRoute } from '@/lib/server/agent-runtime-config'
|
|
22
26
|
import type { Chatroom, ChatroomMessage, Agent } from '@/types'
|
|
23
27
|
|
|
24
28
|
export const dynamic = 'force-dynamic'
|
|
@@ -50,7 +54,8 @@ export async function POST(req: Request, { params }: { params: Promise<{ id: str
|
|
|
50
54
|
|
|
51
55
|
// Persist incoming message
|
|
52
56
|
const senderName = senderId === 'user' ? 'You' : (agents[senderId]?.name || senderId)
|
|
53
|
-
|
|
57
|
+
const replyTargetAgentId = resolveReplyTargetAgentId(replyToId, chatroom.messages, chatroom.agentIds)
|
|
58
|
+
let mentions = parseMentions(text, agents, chatroom.agentIds, { replyTargetAgentId })
|
|
54
59
|
// Routing rules: if no explicit mentions, evaluate keyword/capability rules
|
|
55
60
|
if (mentions.length === 0 && chatroom.routingRules?.length) {
|
|
56
61
|
const agentList = chatroom.agentIds.map((aid) => agents[aid]).filter(Boolean)
|
|
@@ -147,15 +152,17 @@ export async function POST(req: Request, { params }: { params: Promise<{ id: str
|
|
|
147
152
|
}
|
|
148
153
|
|
|
149
154
|
// Pre-flight: check if the agent's provider is usable before attempting to stream
|
|
150
|
-
const
|
|
151
|
-
const
|
|
155
|
+
const route = resolvePrimaryAgentRoute(agent)
|
|
156
|
+
const providerInfo = getProvider(route?.provider || agent.provider)
|
|
157
|
+
const apiKey = resolveApiKey(route?.credentialId || agent.credentialId)
|
|
158
|
+
const resolvedEndpoint = route?.apiEndpoint || resolveAgentApiEndpoint(agent)
|
|
152
159
|
if (providerInfo?.requiresApiKey && !apiKey) {
|
|
153
160
|
writeEvent({ t: 'cr_agent_start', agentId: agent.id, agentName: agent.name })
|
|
154
161
|
writeEvent({ t: 'err', text: `${agent.name} has no API credentials configured`, agentId: agent.id, agentName: agent.name })
|
|
155
162
|
writeEvent({ t: 'cr_agent_done', agentId: agent.id, agentName: agent.name })
|
|
156
163
|
return []
|
|
157
164
|
}
|
|
158
|
-
if (providerInfo?.requiresEndpoint && !
|
|
165
|
+
if (providerInfo?.requiresEndpoint && !resolvedEndpoint) {
|
|
159
166
|
writeEvent({ t: 'cr_agent_start', agentId: agent.id, agentName: agent.name })
|
|
160
167
|
writeEvent({ t: 'err', text: `${agent.name} has no endpoint configured`, agentId: agent.id, agentName: agent.name })
|
|
161
168
|
writeEvent({ t: 'cr_agent_done', agentId: agent.id, agentName: agent.name })
|
|
@@ -173,7 +180,13 @@ export async function POST(req: Request, { params }: { params: Promise<{ id: str
|
|
|
173
180
|
notify(`chatroom:${id}`)
|
|
174
181
|
}
|
|
175
182
|
|
|
176
|
-
const syntheticSession =
|
|
183
|
+
const syntheticSession = ensureSyntheticSession(agent, id)
|
|
184
|
+
syntheticSession.provider = route?.provider || syntheticSession.provider
|
|
185
|
+
syntheticSession.model = route?.model || syntheticSession.model
|
|
186
|
+
syntheticSession.credentialId = route?.credentialId ?? syntheticSession.credentialId ?? null
|
|
187
|
+
syntheticSession.fallbackCredentialIds = route?.fallbackCredentialIds || syntheticSession.fallbackCredentialIds || []
|
|
188
|
+
syntheticSession.gatewayProfileId = route?.gatewayProfileId ?? syntheticSession.gatewayProfileId ?? null
|
|
189
|
+
syntheticSession.apiEndpoint = resolvedEndpoint
|
|
177
190
|
const agentSystemPrompt = buildAgentSystemPromptForChatroom(agent)
|
|
178
191
|
const chatroomContext = buildChatroomSystemPrompt(freshChatroom, agents, agent.id)
|
|
179
192
|
const fullSystemPrompt = [agentSystemPrompt, chatroomContext].filter(Boolean).join('\n\n')
|
|
@@ -181,6 +194,7 @@ export async function POST(req: Request, { params }: { params: Promise<{ id: str
|
|
|
181
194
|
|
|
182
195
|
// Use enriched context message for chained agents, or reply context + original text
|
|
183
196
|
const messageForAgent = item.contextMessage || (replyContext + text)
|
|
197
|
+
appendSyntheticSessionMessage(syntheticSession.id, 'user', messageForAgent)
|
|
184
198
|
|
|
185
199
|
let fullText = ''
|
|
186
200
|
let agentError = ''
|
|
@@ -218,12 +232,14 @@ export async function POST(req: Request, { params }: { params: Promise<{ id: str
|
|
|
218
232
|
|
|
219
233
|
// Don't persist empty or error-only messages — they pollute chat history
|
|
220
234
|
if (!responseText.trim() && agentError) {
|
|
235
|
+
appendSyntheticSessionMessage(syntheticSession.id, 'assistant', agentError)
|
|
221
236
|
markProviderFailure(agent.provider, agentError)
|
|
222
237
|
writeEvent({ t: 'cr_agent_done', agentId: agent.id, agentName: agent.name })
|
|
223
238
|
return []
|
|
224
239
|
}
|
|
225
240
|
|
|
226
241
|
if (responseText.trim()) {
|
|
242
|
+
appendSyntheticSessionMessage(syntheticSession.id, 'assistant', responseText)
|
|
227
243
|
const parsedMentions = parseMentions(responseText, agents, freshChatroom.agentIds)
|
|
228
244
|
const chainedHealth = filterHealthyChatroomAgents(parsedMentions, agents)
|
|
229
245
|
const newMentions = chainedHealth.healthyAgentIds
|
|
@@ -33,7 +33,8 @@ export async function POST(req: Request, { params }: { params: Promise<{ id: str
|
|
|
33
33
|
chatroom.updatedAt = Date.now()
|
|
34
34
|
chatrooms[id] = chatroom
|
|
35
35
|
saveChatrooms(chatrooms)
|
|
36
|
+
notify('chatrooms')
|
|
36
37
|
notify(`chatroom:${id}`)
|
|
37
38
|
|
|
38
|
-
return NextResponse.json(
|
|
39
|
+
return NextResponse.json(chatroom)
|
|
39
40
|
}
|
|
@@ -36,7 +36,8 @@ export async function POST(req: Request, { params }: { params: Promise<{ id: str
|
|
|
36
36
|
chatroom.updatedAt = Date.now()
|
|
37
37
|
chatrooms[id] = chatroom
|
|
38
38
|
saveChatrooms(chatrooms)
|
|
39
|
+
notify('chatrooms')
|
|
39
40
|
notify(`chatroom:${id}`)
|
|
40
41
|
|
|
41
|
-
return NextResponse.json(
|
|
42
|
+
return NextResponse.json(chatroom)
|
|
42
43
|
}
|
|
@@ -33,6 +33,12 @@ export async function PUT(req: Request, { params }: { params: Promise<{ id: stri
|
|
|
33
33
|
|
|
34
34
|
// Diff agentIds and inject join/leave system messages
|
|
35
35
|
if (Array.isArray(body.agentIds)) {
|
|
36
|
+
if (body.agentIds.length === 0) {
|
|
37
|
+
return NextResponse.json(
|
|
38
|
+
{ error: 'Select at least one chatroom member.' },
|
|
39
|
+
{ status: 400 },
|
|
40
|
+
)
|
|
41
|
+
}
|
|
36
42
|
const agents = loadAgents()
|
|
37
43
|
const invalidAgentIds = (body.agentIds as string[]).filter((agentId) => !agents[agentId])
|
|
38
44
|
if (invalidAgentIds.length > 0) {
|
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
import { NextResponse } from 'next/server'
|
|
2
2
|
import { hasActiveBrowser, cleanupSessionBrowser } from '@/lib/server/session-tools'
|
|
3
|
+
import { loadBrowserSessionRecord } from '@/lib/server/browser-state'
|
|
3
4
|
|
|
4
5
|
export async function GET(_req: Request, { params }: { params: Promise<{ id: string }> }) {
|
|
5
6
|
const { id } = await params
|
|
6
|
-
return NextResponse.json({
|
|
7
|
+
return NextResponse.json({
|
|
8
|
+
active: hasActiveBrowser(id),
|
|
9
|
+
state: loadBrowserSessionRecord(id),
|
|
10
|
+
})
|
|
7
11
|
}
|
|
8
12
|
|
|
9
13
|
export async function DELETE(_req: Request, { params }: { params: Promise<{ id: string }> }) {
|
|
@@ -49,7 +49,9 @@ export async function POST(req: Request, { params }: { params: Promise<{ id: str
|
|
|
49
49
|
mode: queueMode,
|
|
50
50
|
onEvent: (ev) => writeEvent(ev as unknown as Record<string, unknown>),
|
|
51
51
|
replyToId,
|
|
52
|
-
|
|
52
|
+
// Keep user-initiated runs alive even if the SSE transport drops so
|
|
53
|
+
// long-lived tasks can finish and be observed later via polling/history.
|
|
54
|
+
callerSignal: internal ? req.signal : undefined,
|
|
53
55
|
})
|
|
54
56
|
abortRun = run.abort
|
|
55
57
|
|
|
@@ -89,8 +91,10 @@ export async function POST(req: Request, { params }: { params: Promise<{ id: str
|
|
|
89
91
|
})
|
|
90
92
|
},
|
|
91
93
|
cancel() {
|
|
92
|
-
// Client disconnected
|
|
93
|
-
|
|
94
|
+
// Client disconnected. User-facing runs continue in the background so
|
|
95
|
+
// they can persist results even when the transport drops. Explicit stop
|
|
96
|
+
// controls still cancel the run through the session run manager.
|
|
97
|
+
if (internal) abortRun?.()
|
|
94
98
|
},
|
|
95
99
|
})
|
|
96
100
|
|
|
@@ -1,17 +1,26 @@
|
|
|
1
1
|
import { NextResponse } from 'next/server'
|
|
2
|
-
import {
|
|
2
|
+
import { active, loadStoredItem, upsertStoredItem } from '@/lib/server/storage'
|
|
3
3
|
import { notFound } from '@/lib/server/collection-helpers'
|
|
4
|
+
import { getSessionRunState } from '@/lib/server/session-run-manager'
|
|
5
|
+
import { pruneStreamingAssistantArtifacts } from '@/lib/chat-streaming-state'
|
|
4
6
|
|
|
5
7
|
export async function GET(req: Request, { params }: { params: Promise<{ id: string }> }) {
|
|
6
8
|
const { id } = await params
|
|
7
|
-
const
|
|
8
|
-
if (!
|
|
9
|
+
const session = loadStoredItem('sessions', id)
|
|
10
|
+
if (!session) return notFound()
|
|
11
|
+
session.messages = Array.isArray(session.messages) ? session.messages : []
|
|
12
|
+
|
|
13
|
+
const run = getSessionRunState(id)
|
|
14
|
+
const hasLiveRun = active.has(id) || !!run.runningRunId
|
|
15
|
+
if (!hasLiveRun && pruneStreamingAssistantArtifacts(session.messages)) {
|
|
16
|
+
upsertStoredItem('sessions', id, session)
|
|
17
|
+
}
|
|
9
18
|
|
|
10
19
|
const url = new URL(req.url)
|
|
11
20
|
const limitParam = url.searchParams.get('limit')
|
|
12
21
|
const beforeParam = url.searchParams.get('before')
|
|
13
22
|
|
|
14
|
-
const allMessages =
|
|
23
|
+
const allMessages = Array.isArray(session.messages) ? session.messages : []
|
|
15
24
|
const total = allMessages.length
|
|
16
25
|
|
|
17
26
|
// If no limit param, return all messages (backward compatible)
|
|
@@ -41,8 +50,7 @@ export async function POST(req: Request, { params }: { params: Promise<{ id: str
|
|
|
41
50
|
if (body.kind !== 'context-clear') {
|
|
42
51
|
return NextResponse.json({ error: 'Only context-clear kind is supported' }, { status: 400 })
|
|
43
52
|
}
|
|
44
|
-
const
|
|
45
|
-
const session = sessions[id]
|
|
53
|
+
const session = loadStoredItem('sessions', id)
|
|
46
54
|
if (!session) return notFound()
|
|
47
55
|
|
|
48
56
|
session.messages.push({
|
|
@@ -51,15 +59,14 @@ export async function POST(req: Request, { params }: { params: Promise<{ id: str
|
|
|
51
59
|
kind: 'context-clear',
|
|
52
60
|
time: Date.now(),
|
|
53
61
|
})
|
|
54
|
-
|
|
62
|
+
upsertStoredItem('sessions', id, session)
|
|
55
63
|
return NextResponse.json({ ok: true })
|
|
56
64
|
}
|
|
57
65
|
|
|
58
66
|
export async function PUT(req: Request, { params }: { params: Promise<{ id: string }> }) {
|
|
59
67
|
const { id } = await params
|
|
60
68
|
const body = await req.json() as { messageIndex: number; bookmarked: boolean }
|
|
61
|
-
const
|
|
62
|
-
const session = sessions[id]
|
|
69
|
+
const session = loadStoredItem('sessions', id)
|
|
63
70
|
if (!session) return notFound()
|
|
64
71
|
|
|
65
72
|
const { messageIndex, bookmarked } = body
|
|
@@ -68,15 +75,14 @@ export async function PUT(req: Request, { params }: { params: Promise<{ id: stri
|
|
|
68
75
|
}
|
|
69
76
|
|
|
70
77
|
session.messages[messageIndex].bookmarked = bookmarked
|
|
71
|
-
|
|
78
|
+
upsertStoredItem('sessions', id, session)
|
|
72
79
|
return NextResponse.json(session.messages[messageIndex])
|
|
73
80
|
}
|
|
74
81
|
|
|
75
82
|
export async function DELETE(req: Request, { params }: { params: Promise<{ id: string }> }) {
|
|
76
83
|
const { id } = await params
|
|
77
84
|
const body = await req.json() as { messageIndex: number }
|
|
78
|
-
const
|
|
79
|
-
const session = sessions[id]
|
|
85
|
+
const session = loadStoredItem('sessions', id)
|
|
80
86
|
if (!session) return notFound()
|
|
81
87
|
|
|
82
88
|
const { messageIndex } = body
|
|
@@ -90,6 +96,6 @@ export async function DELETE(req: Request, { params }: { params: Promise<{ id: s
|
|
|
90
96
|
}
|
|
91
97
|
|
|
92
98
|
session.messages.splice(messageIndex, 1)
|
|
93
|
-
|
|
99
|
+
upsertStoredItem('sessions', id, session)
|
|
94
100
|
return NextResponse.json({ ok: true })
|
|
95
101
|
}
|
|
@@ -2,6 +2,7 @@ import { NextResponse } from 'next/server'
|
|
|
2
2
|
import { loadSessions, saveSessions, deleteSession, active, loadAgents } from '@/lib/server/storage'
|
|
3
3
|
import { notFound } from '@/lib/server/collection-helpers'
|
|
4
4
|
import { normalizeProviderEndpoint } from '@/lib/openclaw-endpoint'
|
|
5
|
+
import { resolvePrimaryAgentRoute } from '@/lib/server/agent-runtime-config'
|
|
5
6
|
|
|
6
7
|
export async function PUT(req: Request, { params }: { params: Promise<{ id: string }> }) {
|
|
7
8
|
const { id } = await params
|
|
@@ -17,6 +18,7 @@ export async function PUT(req: Request, { params }: { params: Promise<{ id: stri
|
|
|
17
18
|
}
|
|
18
19
|
|
|
19
20
|
const linkedAgent = nextAgentId ? loadAgents()[nextAgentId] : null
|
|
21
|
+
const linkedRoute = linkedAgent ? resolvePrimaryAgentRoute(linkedAgent) : null
|
|
20
22
|
|
|
21
23
|
if (updates.name !== undefined) sessions[id].name = updates.name
|
|
22
24
|
if (updates.cwd !== undefined) sessions[id].cwd = updates.cwd
|
|
@@ -24,11 +26,19 @@ export async function PUT(req: Request, { params }: { params: Promise<{ id: stri
|
|
|
24
26
|
else if (agentIdUpdateProvided && linkedAgent?.provider) sessions[id].provider = linkedAgent.provider
|
|
25
27
|
|
|
26
28
|
if (updates.model !== undefined) sessions[id].model = updates.model
|
|
29
|
+
else if (agentIdUpdateProvided && linkedRoute?.model) sessions[id].model = linkedRoute.model
|
|
27
30
|
else if (agentIdUpdateProvided && linkedAgent?.model !== undefined) sessions[id].model = linkedAgent.model
|
|
28
31
|
|
|
29
32
|
if (updates.credentialId !== undefined) sessions[id].credentialId = updates.credentialId
|
|
33
|
+
else if (agentIdUpdateProvided && linkedRoute) sessions[id].credentialId = linkedRoute.credentialId ?? null
|
|
30
34
|
else if (agentIdUpdateProvided && linkedAgent) sessions[id].credentialId = linkedAgent.credentialId ?? null
|
|
31
35
|
|
|
36
|
+
if (updates.fallbackCredentialIds !== undefined) sessions[id].fallbackCredentialIds = updates.fallbackCredentialIds
|
|
37
|
+
else if (agentIdUpdateProvided && linkedRoute) sessions[id].fallbackCredentialIds = [...linkedRoute.fallbackCredentialIds]
|
|
38
|
+
|
|
39
|
+
if (updates.gatewayProfileId !== undefined) sessions[id].gatewayProfileId = updates.gatewayProfileId
|
|
40
|
+
else if (agentIdUpdateProvided && linkedRoute) sessions[id].gatewayProfileId = linkedRoute.gatewayProfileId ?? null
|
|
41
|
+
|
|
32
42
|
if (updates.plugins !== undefined) sessions[id].plugins = updates.plugins
|
|
33
43
|
else if (agentIdUpdateProvided && linkedAgent) sessions[id].plugins = Array.isArray(linkedAgent.plugins) ? linkedAgent.plugins : []
|
|
34
44
|
|
|
@@ -37,6 +47,8 @@ export async function PUT(req: Request, { params }: { params: Promise<{ id: stri
|
|
|
37
47
|
updates.provider || sessions[id].provider,
|
|
38
48
|
updates.apiEndpoint,
|
|
39
49
|
)
|
|
50
|
+
} else if (agentIdUpdateProvided && linkedRoute) {
|
|
51
|
+
sessions[id].apiEndpoint = linkedRoute.apiEndpoint ?? null
|
|
40
52
|
} else if (agentIdUpdateProvided && linkedAgent) {
|
|
41
53
|
sessions[id].apiEndpoint = normalizeProviderEndpoint(
|
|
42
54
|
linkedAgent.provider,
|
|
@@ -45,6 +57,24 @@ export async function PUT(req: Request, { params }: { params: Promise<{ id: stri
|
|
|
45
57
|
}
|
|
46
58
|
if (updates.heartbeatEnabled !== undefined) sessions[id].heartbeatEnabled = updates.heartbeatEnabled
|
|
47
59
|
if (updates.heartbeatIntervalSec !== undefined) sessions[id].heartbeatIntervalSec = updates.heartbeatIntervalSec
|
|
60
|
+
if (updates.sessionResetMode !== undefined) sessions[id].sessionResetMode = updates.sessionResetMode
|
|
61
|
+
if (updates.sessionIdleTimeoutSec !== undefined) sessions[id].sessionIdleTimeoutSec = updates.sessionIdleTimeoutSec
|
|
62
|
+
if (updates.sessionMaxAgeSec !== undefined) sessions[id].sessionMaxAgeSec = updates.sessionMaxAgeSec
|
|
63
|
+
if (updates.sessionDailyResetAt !== undefined) sessions[id].sessionDailyResetAt = updates.sessionDailyResetAt
|
|
64
|
+
if (updates.sessionResetTimezone !== undefined) sessions[id].sessionResetTimezone = updates.sessionResetTimezone
|
|
65
|
+
if (updates.thinkingLevel !== undefined) sessions[id].thinkingLevel = updates.thinkingLevel
|
|
66
|
+
if (updates.connectorThinkLevel !== undefined) sessions[id].connectorThinkLevel = updates.connectorThinkLevel
|
|
67
|
+
if (updates.connectorSessionScope !== undefined) sessions[id].connectorSessionScope = updates.connectorSessionScope
|
|
68
|
+
if (updates.connectorReplyMode !== undefined) sessions[id].connectorReplyMode = updates.connectorReplyMode
|
|
69
|
+
if (updates.connectorThreadBinding !== undefined) sessions[id].connectorThreadBinding = updates.connectorThreadBinding
|
|
70
|
+
if (updates.connectorGroupPolicy !== undefined) sessions[id].connectorGroupPolicy = updates.connectorGroupPolicy
|
|
71
|
+
if (updates.connectorIdleTimeoutSec !== undefined) sessions[id].connectorIdleTimeoutSec = updates.connectorIdleTimeoutSec
|
|
72
|
+
if (updates.connectorMaxAgeSec !== undefined) sessions[id].connectorMaxAgeSec = updates.connectorMaxAgeSec
|
|
73
|
+
if (updates.connectorContext !== undefined) sessions[id].connectorContext = updates.connectorContext
|
|
74
|
+
if (updates.identityState !== undefined) sessions[id].identityState = updates.identityState
|
|
75
|
+
if (updates.sessionArchiveState !== undefined) sessions[id].sessionArchiveState = updates.sessionArchiveState
|
|
76
|
+
if (updates.lastSessionResetAt !== undefined) sessions[id].lastSessionResetAt = updates.lastSessionResetAt
|
|
77
|
+
if (updates.lastSessionResetReason !== undefined) sessions[id].lastSessionResetReason = updates.lastSessionResetReason
|
|
48
78
|
if (updates.pinned !== undefined) sessions[id].pinned = !!updates.pinned
|
|
49
79
|
if (updates.claudeSessionId !== undefined) sessions[id].claudeSessionId = updates.claudeSessionId
|
|
50
80
|
if (updates.codexThreadId !== undefined) sessions[id].codexThreadId = updates.codexThreadId
|
|
@@ -1,10 +1,15 @@
|
|
|
1
1
|
import { NextResponse } from 'next/server'
|
|
2
|
-
import {
|
|
2
|
+
import { pruneStreamingAssistantArtifacts } from '@/lib/chat-streaming-state'
|
|
3
|
+
import { active, loadStoredItem, upsertStoredItem } from '@/lib/server/storage'
|
|
3
4
|
import { cancelSessionRuns } from '@/lib/server/session-run-manager'
|
|
4
5
|
|
|
5
6
|
export async function POST(_req: Request, { params }: { params: Promise<{ id: string }> }) {
|
|
6
7
|
const { id } = await params
|
|
7
8
|
const cancel = cancelSessionRuns(id, 'Stopped by user')
|
|
9
|
+
const session = loadStoredItem('sessions', id)
|
|
10
|
+
if (session && Array.isArray(session.messages) && pruneStreamingAssistantArtifacts(session.messages)) {
|
|
11
|
+
upsertStoredItem('sessions', id, session)
|
|
12
|
+
}
|
|
8
13
|
if (active.has(id)) {
|
|
9
14
|
try { active.get(id).kill() } catch {}
|
|
10
15
|
active.delete(id)
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { NextResponse } from 'next/server'
|
|
2
|
+
import { DEFAULT_HEARTBEAT_INTERVAL_SEC } from '@/lib/heartbeat-defaults'
|
|
2
3
|
import { disableAllSessionHeartbeats, loadSettings, saveSettings } from '@/lib/server/storage'
|
|
3
4
|
import { cancelAllHeartbeatRuns } from '@/lib/server/session-run-manager'
|
|
4
5
|
|
|
@@ -11,7 +12,7 @@ export async function POST(req: Request) {
|
|
|
11
12
|
|
|
12
13
|
const updatedSessions = disableAllSessionHeartbeats()
|
|
13
14
|
const settings = loadSettings()
|
|
14
|
-
if ((settings.heartbeatIntervalSec ??
|
|
15
|
+
if ((settings.heartbeatIntervalSec ?? DEFAULT_HEARTBEAT_INTERVAL_SEC) !== 0) {
|
|
15
16
|
settings.heartbeatIntervalSec = 0
|
|
16
17
|
saveSettings(settings)
|
|
17
18
|
}
|
|
@@ -7,6 +7,7 @@ import { WORKSPACE_DIR } from '@/lib/server/data-dir'
|
|
|
7
7
|
import { notify } from '@/lib/server/ws-hub'
|
|
8
8
|
import { getSessionRunState } from '@/lib/server/session-run-manager'
|
|
9
9
|
import { normalizeProviderEndpoint } from '@/lib/openclaw-endpoint'
|
|
10
|
+
import { applyResolvedRoute, resolvePrimaryAgentRoute } from '@/lib/server/agent-runtime-config'
|
|
10
11
|
export const dynamic = 'force-dynamic'
|
|
11
12
|
|
|
12
13
|
|
|
@@ -60,6 +61,7 @@ export async function POST(req: Request) {
|
|
|
60
61
|
const id = body.id || genId()
|
|
61
62
|
const sessions = loadSessions()
|
|
62
63
|
const agent = body.agentId ? loadAgents()[body.agentId] : null
|
|
64
|
+
const resolvedRoute = agent ? resolvePrimaryAgentRoute(agent) : null
|
|
63
65
|
const requestedPlugins = Array.isArray(body.plugins) ? body.plugins : (Array.isArray(body.tools) ? body.tools : null)
|
|
64
66
|
const resolvedPlugins = requestedPlugins ?? (Array.isArray(agent?.plugins) ? agent.plugins : (Array.isArray(agent?.tools) ? agent.tools : []))
|
|
65
67
|
|
|
@@ -70,12 +72,13 @@ export async function POST(req: Request) {
|
|
|
70
72
|
|
|
71
73
|
const sessionName = body.name || 'New Chat'
|
|
72
74
|
|
|
73
|
-
|
|
75
|
+
const nextSession = {
|
|
74
76
|
id, name: sessionName, cwd,
|
|
75
77
|
user: body.user || 'user',
|
|
76
78
|
provider: body.provider || agent?.provider || 'claude-cli',
|
|
77
79
|
model: body.model || agent?.model || '',
|
|
78
80
|
credentialId: body.credentialId || agent?.credentialId || null,
|
|
81
|
+
fallbackCredentialIds: body.fallbackCredentialIds || agent?.fallbackCredentialIds || [],
|
|
79
82
|
apiEndpoint: normalizeProviderEndpoint(
|
|
80
83
|
body.provider || agent?.provider || 'claude-cli',
|
|
81
84
|
body.apiEndpoint || agent?.apiEndpoint || null,
|
|
@@ -96,7 +99,26 @@ export async function POST(req: Request) {
|
|
|
96
99
|
plugins: resolvedPlugins,
|
|
97
100
|
heartbeatEnabled: body.heartbeatEnabled ?? null,
|
|
98
101
|
heartbeatIntervalSec: body.heartbeatIntervalSec ?? null,
|
|
102
|
+
sessionResetMode: body.sessionResetMode ?? agent?.sessionResetMode ?? null,
|
|
103
|
+
sessionIdleTimeoutSec: body.sessionIdleTimeoutSec ?? agent?.sessionIdleTimeoutSec ?? null,
|
|
104
|
+
sessionMaxAgeSec: body.sessionMaxAgeSec ?? agent?.sessionMaxAgeSec ?? null,
|
|
105
|
+
sessionDailyResetAt: body.sessionDailyResetAt ?? agent?.sessionDailyResetAt ?? null,
|
|
106
|
+
sessionResetTimezone: body.sessionResetTimezone ?? agent?.sessionResetTimezone ?? null,
|
|
107
|
+
thinkingLevel: body.thinkingLevel ?? null,
|
|
108
|
+
connectorThinkLevel: body.connectorThinkLevel ?? null,
|
|
109
|
+
connectorSessionScope: body.connectorSessionScope ?? null,
|
|
110
|
+
connectorReplyMode: body.connectorReplyMode ?? null,
|
|
111
|
+
connectorThreadBinding: body.connectorThreadBinding ?? null,
|
|
112
|
+
connectorGroupPolicy: body.connectorGroupPolicy ?? null,
|
|
113
|
+
connectorIdleTimeoutSec: body.connectorIdleTimeoutSec ?? null,
|
|
114
|
+
connectorMaxAgeSec: body.connectorMaxAgeSec ?? null,
|
|
115
|
+
connectorContext: body.connectorContext ?? null,
|
|
116
|
+
identityState: body.identityState ?? agent?.identityState ?? null,
|
|
117
|
+
sessionArchiveState: body.sessionArchiveState ?? null,
|
|
99
118
|
}
|
|
119
|
+
sessions[id] = (body.provider || body.model || body.credentialId || body.apiEndpoint)
|
|
120
|
+
? nextSession
|
|
121
|
+
: applyResolvedRoute(nextSession, resolvedRoute)
|
|
100
122
|
saveSessions(sessions)
|
|
101
123
|
notify('sessions')
|
|
102
124
|
return NextResponse.json(sessions[id])
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { NextResponse } from 'next/server'
|
|
2
|
+
import { loadConnectors } from '@/lib/server/storage'
|
|
3
|
+
import { notFound } from '@/lib/server/collection-helpers'
|
|
4
|
+
import { buildConnectorDoctorPreview, buildConnectorDoctorReport, type ConnectorDoctorPreviewInput } from '@/lib/server/connectors/doctor'
|
|
5
|
+
|
|
6
|
+
export const dynamic = 'force-dynamic'
|
|
7
|
+
|
|
8
|
+
export async function GET(_req: Request, { params }: { params: Promise<{ id: string }> }) {
|
|
9
|
+
const { id } = await params
|
|
10
|
+
const connectors = loadConnectors()
|
|
11
|
+
const connector = connectors[id]
|
|
12
|
+
if (!connector) return notFound()
|
|
13
|
+
|
|
14
|
+
return NextResponse.json(buildConnectorDoctorReport(connector, null, { baseConnector: connector }))
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export async function POST(req: Request, { params }: { params: Promise<{ id: string }> }) {
|
|
18
|
+
const { id } = await params
|
|
19
|
+
const connectors = loadConnectors()
|
|
20
|
+
const baseConnector = connectors[id]
|
|
21
|
+
if (!baseConnector) return notFound()
|
|
22
|
+
|
|
23
|
+
const body = await req.json().catch(() => ({})) as ConnectorDoctorPreviewInput
|
|
24
|
+
const connector = buildConnectorDoctorPreview({ baseConnector, input: body, fallbackId: id })
|
|
25
|
+
return NextResponse.json(buildConnectorDoctorReport(connector, body.sampleMsg, { baseConnector }))
|
|
26
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { NextResponse } from 'next/server'
|
|
2
|
+
import { buildConnectorDoctorPreview, buildConnectorDoctorReport, type ConnectorDoctorPreviewInput } from '@/lib/server/connectors/doctor'
|
|
3
|
+
import { loadConnectors } from '@/lib/server/storage'
|
|
4
|
+
|
|
5
|
+
export const dynamic = 'force-dynamic'
|
|
6
|
+
|
|
7
|
+
export async function POST(req: Request) {
|
|
8
|
+
const body = await req.json().catch(() => ({})) as ConnectorDoctorPreviewInput
|
|
9
|
+
const connectors = loadConnectors()
|
|
10
|
+
const baseConnector = typeof body.id === 'string' ? connectors[body.id] : null
|
|
11
|
+
const connector = buildConnectorDoctorPreview({ baseConnector, input: body })
|
|
12
|
+
return NextResponse.json(buildConnectorDoctorReport(connector, body.sampleMsg, { baseConnector }))
|
|
13
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { NextResponse } from 'next/server'
|
|
2
|
+
import { loadExternalAgents, saveExternalAgents } from '@/lib/server/storage'
|
|
3
|
+
import { notFound } from '@/lib/server/collection-helpers'
|
|
4
|
+
import { notify } from '@/lib/server/ws-hub'
|
|
5
|
+
export const dynamic = 'force-dynamic'
|
|
6
|
+
|
|
7
|
+
export async function POST(req: Request, { params }: { params: Promise<{ id: string }> }) {
|
|
8
|
+
const { id } = await params
|
|
9
|
+
const body = await req.json().catch(() => ({}))
|
|
10
|
+
const items = loadExternalAgents()
|
|
11
|
+
const runtime = items[id]
|
|
12
|
+
if (!runtime) return notFound()
|
|
13
|
+
const now = Date.now()
|
|
14
|
+
runtime.lastHeartbeatAt = now
|
|
15
|
+
runtime.lastSeenAt = now
|
|
16
|
+
runtime.updatedAt = now
|
|
17
|
+
runtime.status = body.status || 'online'
|
|
18
|
+
if (body.tokenStats && typeof body.tokenStats === 'object') {
|
|
19
|
+
runtime.tokenStats = {
|
|
20
|
+
...(runtime.tokenStats || {}),
|
|
21
|
+
...body.tokenStats,
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
if (body.metadata && typeof body.metadata === 'object') {
|
|
25
|
+
runtime.metadata = {
|
|
26
|
+
...(runtime.metadata || {}),
|
|
27
|
+
...body.metadata,
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
saveExternalAgents(items)
|
|
31
|
+
notify('external_agents')
|
|
32
|
+
return NextResponse.json({ ok: true, id, lastHeartbeatAt: now })
|
|
33
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { NextResponse } from 'next/server'
|
|
2
|
+
import { loadExternalAgents, saveExternalAgents } from '@/lib/server/storage'
|
|
3
|
+
import { mutateItem, notFound, type CollectionOps } from '@/lib/server/collection-helpers'
|
|
4
|
+
import { notify } from '@/lib/server/ws-hub'
|
|
5
|
+
export const dynamic = 'force-dynamic'
|
|
6
|
+
|
|
7
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
8
|
+
const ops: CollectionOps<any> = { load: loadExternalAgents, save: saveExternalAgents, topic: 'external_agents' }
|
|
9
|
+
|
|
10
|
+
export async function PUT(req: Request, { params }: { params: Promise<{ id: string }> }) {
|
|
11
|
+
const { id } = await params
|
|
12
|
+
const body = await req.json().catch(() => ({}))
|
|
13
|
+
const result = mutateItem(ops, id, (runtime) => ({
|
|
14
|
+
...runtime,
|
|
15
|
+
...body,
|
|
16
|
+
id,
|
|
17
|
+
updatedAt: Date.now(),
|
|
18
|
+
}))
|
|
19
|
+
if (!result) return notFound()
|
|
20
|
+
return NextResponse.json(result)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export async function DELETE(_req: Request, { params }: { params: Promise<{ id: string }> }) {
|
|
24
|
+
const { id } = await params
|
|
25
|
+
const items = loadExternalAgents()
|
|
26
|
+
if (!items[id]) return notFound()
|
|
27
|
+
delete items[id]
|
|
28
|
+
saveExternalAgents(items)
|
|
29
|
+
notify('external_agents')
|
|
30
|
+
return NextResponse.json({ ok: true })
|
|
31
|
+
}
|