@swarmclawai/swarmclaw 1.2.4 → 1.2.6
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 +14 -0
- package/bin/daemon-cmd.js +169 -0
- package/bin/server-cmd.js +3 -0
- package/bin/swarmclaw.js +11 -0
- package/package.json +17 -16
- package/src/app/api/agents/[id]/clone/route.ts +3 -32
- package/src/app/api/agents/[id]/route.ts +6 -158
- package/src/app/api/agents/[id]/status/route.ts +2 -3
- package/src/app/api/agents/[id]/thread/route.ts +4 -17
- package/src/app/api/agents/bulk/route.ts +5 -47
- package/src/app/api/agents/route.ts +5 -119
- package/src/app/api/agents/trash/route.ts +13 -24
- package/src/app/api/auth/route.ts +3 -9
- package/src/app/api/autonomy/estop/route.ts +5 -5
- package/src/app/api/chatrooms/[id]/chat/route.ts +11 -5
- package/src/app/api/chatrooms/[id]/route.ts +23 -2
- package/src/app/api/chatrooms/route.ts +13 -2
- package/src/app/api/chats/[id]/clear/route.ts +2 -13
- package/src/app/api/chats/[id]/deploy/route.ts +2 -3
- package/src/app/api/chats/[id]/edit-resend/route.ts +7 -13
- package/src/app/api/chats/[id]/mailbox/route.ts +6 -8
- package/src/app/api/chats/[id]/queue/route.ts +17 -64
- package/src/app/api/chats/[id]/retry/route.ts +4 -22
- package/src/app/api/chats/[id]/route.ts +10 -138
- package/src/app/api/chats/heartbeat/route.ts +2 -1
- package/src/app/api/chats/migrate-messages/route.ts +7 -0
- package/src/app/api/chats/route.ts +13 -134
- package/src/app/api/connectors/[id]/access/route.ts +12 -229
- package/src/app/api/connectors/[id]/doctor/route.ts +1 -1
- package/src/app/api/connectors/[id]/health/route.ts +12 -39
- package/src/app/api/connectors/[id]/route.ts +14 -122
- package/src/app/api/connectors/[id]/webhook/route.ts +1 -1
- package/src/app/api/connectors/doctor/route.ts +1 -1
- package/src/app/api/connectors/route.ts +12 -70
- package/src/app/api/credentials/[id]/route.ts +2 -4
- package/src/app/api/credentials/route.ts +10 -19
- package/src/app/api/daemon/health-check/route.ts +3 -4
- package/src/app/api/daemon/route.ts +10 -8
- package/src/app/api/documents/route.ts +11 -10
- package/src/app/api/external-agents/route.ts +3 -3
- package/src/app/api/gateways/[id]/health/route.ts +2 -3
- package/src/app/api/gateways/[id]/route.ts +7 -122
- package/src/app/api/gateways/route.ts +3 -103
- package/src/app/api/mcp-servers/[id]/tools/route.ts +5 -5
- package/src/app/api/openclaw/dashboard-url/route.ts +8 -16
- package/src/app/api/openclaw/directory/route.ts +2 -2
- package/src/app/api/openclaw/history/route.ts +3 -5
- package/src/app/api/providers/[id]/route.test.ts +49 -0
- package/src/app/api/providers/ollama/route.ts +6 -5
- package/src/app/api/schedules/[id]/route.ts +14 -108
- package/src/app/api/schedules/[id]/run/route.ts +6 -67
- package/src/app/api/schedules/route.ts +9 -51
- package/src/app/api/settings/route.ts +4 -3
- package/src/app/api/setup/check-provider/route.ts +23 -1
- package/src/app/api/setup/openclaw-device/route.ts +2 -2
- package/src/app/api/system/status/route.ts +2 -2
- package/src/app/api/tasks/[id]/route.ts +16 -202
- package/src/app/api/tasks/bulk/route.ts +5 -86
- package/src/app/api/tasks/metrics/route.ts +2 -1
- package/src/app/api/tasks/route.ts +11 -171
- package/src/app/api/upload/route.ts +1 -1
- package/src/app/api/uploads/[filename]/route.ts +1 -1
- package/src/app/api/uploads/route.ts +1 -1
- package/src/app/api/webhooks/[id]/history/route.ts +2 -2
- package/src/app/layout.tsx +9 -6
- package/src/app/protocols/page.tsx +71 -89
- package/src/app/tasks/page.tsx +32 -32
- package/src/cli/index.js +1 -0
- package/src/cli/spec.js +1 -0
- package/src/components/agents/agent-sheet.tsx +5 -5
- package/src/components/auth/setup-wizard/index.tsx +4 -4
- package/src/components/auth/setup-wizard/step-agents.tsx +1 -1
- package/src/components/auth/setup-wizard/step-connect.tsx +1 -1
- package/src/components/auth/setup-wizard/utils.ts +1 -1
- package/src/components/chatrooms/chatroom-sheet.tsx +16 -276
- package/src/components/connectors/connector-list.tsx +26 -40
- package/src/components/connectors/connector-sheet.tsx +95 -149
- package/src/components/gateways/gateway-sheet.tsx +61 -110
- package/src/components/layout/live-query-sync.tsx +121 -0
- package/src/components/protocols/structured-session-launcher.tsx +24 -45
- package/src/components/providers/app-query-provider.tsx +17 -0
- package/src/components/providers/provider-list.tsx +60 -61
- package/src/components/providers/provider-sheet.tsx +74 -56
- package/src/components/skills/skill-list.tsx +5 -18
- package/src/components/skills/skill-sheet.tsx +21 -20
- package/src/components/skills/skills-workspace.tsx +48 -87
- package/src/components/tasks/task-card.tsx +20 -13
- package/src/components/tasks/task-column.tsx +22 -7
- package/src/components/tasks/task-list.tsx +8 -11
- package/src/components/tasks/task-sheet.tsx +111 -103
- package/src/features/agents/queries.ts +20 -0
- package/src/features/chatrooms/queries.ts +20 -0
- package/src/features/chats/queries.ts +27 -0
- package/src/features/connectors/queries.ts +145 -0
- package/src/features/credentials/queries.ts +37 -0
- package/src/features/extensions/queries.ts +26 -0
- package/src/features/external-agents/queries.ts +36 -0
- package/src/features/gateways/queries.ts +274 -0
- package/src/features/missions/queries.ts +23 -0
- package/src/features/projects/queries.ts +20 -0
- package/src/features/protocols/queries.ts +149 -0
- package/src/features/providers/queries.ts +142 -0
- package/src/features/settings/queries.ts +20 -0
- package/src/features/skills/queries.ts +182 -0
- package/src/features/tasks/queries.ts +189 -0
- package/src/hooks/use-ws.ts +3 -2
- package/src/lib/app/api-client.ts +2 -2
- package/src/lib/providers/index.test.ts +108 -0
- package/src/lib/providers/index.ts +38 -15
- package/src/lib/query/client.ts +17 -0
- package/src/lib/server/agents/agent-runtime-config.ts +1 -1
- package/src/lib/server/agents/agent-service.ts +429 -0
- package/src/lib/server/agents/agent-thread-session.ts +6 -5
- package/src/lib/server/agents/autonomy-contract.ts +1 -4
- package/src/lib/server/agents/delegation-advisory.test.ts +206 -0
- package/src/lib/server/agents/delegation-advisory.ts +251 -0
- package/src/lib/server/agents/main-agent-loop.ts +98 -40
- package/src/lib/server/agents/subagent-runtime.ts +12 -0
- package/src/lib/server/autonomy/supervisor-reflection.test.ts +20 -1
- package/src/lib/server/autonomy/supervisor-reflection.ts +39 -19
- package/src/lib/server/build-llm.ts +7 -15
- package/src/lib/server/capability-router.test.ts +70 -1
- package/src/lib/server/capability-router.ts +24 -99
- package/src/lib/server/chat-execution/chat-execution-utils.ts +0 -15
- package/src/lib/server/chat-execution/chat-streaming-utils.ts +2 -4
- package/src/lib/server/chat-execution/chat-turn-finalization.ts +77 -12
- package/src/lib/server/chat-execution/chat-turn-partial-persistence.ts +4 -4
- package/src/lib/server/chat-execution/chat-turn-preflight.ts +2 -2
- package/src/lib/server/chat-execution/chat-turn-preparation.ts +41 -17
- package/src/lib/server/chat-execution/chat-turn-stream-execution.ts +4 -2
- package/src/lib/server/chat-execution/chat-turn-tool-routing.test.ts +45 -0
- package/src/lib/server/chat-execution/chat-turn-tool-routing.ts +48 -17
- package/src/lib/server/chat-execution/continuation-evaluator.ts +4 -1
- package/src/lib/server/chat-execution/direct-memory-intent.test.ts +9 -0
- package/src/lib/server/chat-execution/direct-memory-intent.ts +12 -2
- package/src/lib/server/chat-execution/message-classifier.test.ts +35 -23
- package/src/lib/server/chat-execution/message-classifier.ts +74 -32
- package/src/lib/server/chat-execution/prompt-builder.test.ts +29 -0
- package/src/lib/server/chat-execution/prompt-builder.ts +37 -2
- package/src/lib/server/chat-execution/prompt-sections.test.ts +56 -0
- package/src/lib/server/chat-execution/prompt-sections.ts +193 -0
- package/src/lib/server/chat-execution/stream-agent-chat.ts +63 -7
- package/src/lib/server/chat-execution/stream-continuation.test.ts +36 -0
- package/src/lib/server/chat-execution/stream-continuation.ts +28 -13
- package/src/lib/server/chatrooms/chatroom-agent-signals.ts +26 -18
- package/src/lib/server/chatrooms/chatroom-helpers.ts +19 -18
- package/src/lib/server/chatrooms/chatroom-repository.ts +16 -0
- package/src/lib/server/chatrooms/chatroom-routing.test.ts +96 -0
- package/src/lib/server/chatrooms/chatroom-routing.ts +207 -53
- package/src/lib/server/chatrooms/mailbox-utils.ts +4 -2
- package/src/lib/server/chatrooms/session-mailbox.ts +50 -40
- package/src/lib/server/chats/chat-session-service.ts +410 -0
- package/src/lib/server/connectors/access.ts +1 -1
- package/src/lib/server/connectors/commands.ts +7 -6
- package/src/lib/server/connectors/connector-inbound.ts +14 -7
- package/src/lib/server/connectors/connector-outbound.ts +16 -11
- package/src/lib/server/connectors/connector-service.ts +453 -0
- package/src/lib/server/connectors/delivery.ts +17 -12
- package/src/lib/server/connectors/inbound-audio-transcription.ts +5 -14
- package/src/lib/server/connectors/media.ts +1 -1
- package/src/lib/server/connectors/response-media.ts +1 -1
- package/src/lib/server/connectors/session-consolidation.ts +11 -7
- package/src/lib/server/connectors/session.ts +9 -7
- package/src/lib/server/connectors/voice-note.ts +2 -1
- package/src/lib/server/context-manager.ts +20 -1
- package/src/lib/server/cost.ts +2 -3
- package/src/lib/server/credentials/credential-repository.ts +43 -4
- package/src/lib/server/credentials/credential-service.ts +112 -0
- package/src/lib/server/daemon/admin-metadata.ts +64 -0
- package/src/lib/server/daemon/controller.ts +577 -0
- package/src/lib/server/daemon/daemon-runtime.ts +352 -0
- package/src/lib/server/daemon/daemon-status-repository.ts +63 -0
- package/src/lib/server/daemon/types.ts +101 -0
- package/src/lib/server/embeddings.ts +3 -9
- package/src/lib/server/eval/agent-regression.ts +3 -2
- package/src/lib/server/eval/runner.ts +2 -2
- package/src/lib/server/execution-brief.test.ts +167 -0
- package/src/lib/server/execution-brief.ts +295 -0
- package/src/lib/server/execution-engine/chat-turn.ts +9 -0
- package/src/lib/server/execution-engine/import-boundary.test.ts +44 -0
- package/src/lib/server/execution-engine/index.ts +35 -0
- package/src/lib/server/execution-engine/task-attempt.ts +303 -0
- package/src/lib/server/execution-engine/types.ts +33 -0
- package/src/lib/server/gateways/gateway-profile-repository.ts +47 -3
- package/src/lib/server/gateways/gateway-profile-service.ts +200 -0
- package/src/lib/server/memory/session-archive-memory.ts +12 -10
- package/src/lib/server/messages/message-repository.ts +330 -0
- package/src/lib/server/missions/mission-service/core.ts +8 -6
- package/src/lib/server/openclaw/agent-resolver.ts +2 -3
- package/src/lib/server/openclaw/doctor.ts +1 -1
- package/src/lib/server/openclaw/gateway.test.ts +10 -1
- package/src/lib/server/openclaw/gateway.ts +5 -14
- package/src/lib/server/openclaw/health.ts +3 -11
- package/src/lib/server/openclaw/sync.ts +8 -6
- package/src/lib/server/persistence/storage-context.ts +3 -0
- package/src/lib/server/protocols/protocol-agent-turn.ts +25 -17
- package/src/lib/server/protocols/protocol-normalization.ts +1 -1
- package/src/lib/server/protocols/protocol-queries.ts +13 -7
- package/src/lib/server/protocols/protocol-run-lifecycle.ts +16 -20
- package/src/lib/server/protocols/protocol-run-repository.ts +81 -0
- package/src/lib/server/protocols/protocol-step-processors.ts +23 -31
- package/src/lib/server/protocols/protocol-swarm.ts +8 -8
- package/src/lib/server/protocols/protocol-template-repository.ts +42 -0
- package/src/lib/server/protocols/protocol-templates.ts +4 -2
- package/src/lib/server/protocols/protocol-types.ts +10 -7
- package/src/lib/server/provider-endpoint.ts +7 -12
- package/src/lib/server/provider-model-discovery.ts +2 -11
- package/src/lib/server/query-expansion.ts +5 -6
- package/src/lib/server/run-context.test.ts +365 -0
- package/src/lib/server/run-context.ts +367 -0
- package/src/lib/server/runtime/heartbeat-service.ts +7 -5
- package/src/lib/server/runtime/queue/core.ts +61 -190
- package/src/lib/server/runtime/run-ledger.ts +8 -0
- package/src/lib/server/runtime/session-run-manager/drain.ts +2 -2
- package/src/lib/server/runtime/session-run-manager/enqueue.ts +6 -0
- package/src/lib/server/runtime/session-run-manager/state.ts +4 -0
- package/src/lib/server/schedules/schedule-route-service.ts +230 -0
- package/src/lib/server/service-result.ts +16 -0
- package/src/lib/server/session-note.ts +2 -3
- package/src/lib/server/session-reset-policy.ts +4 -3
- package/src/lib/server/session-tools/connector.ts +9 -6
- package/src/lib/server/session-tools/context-mgmt.ts +58 -9
- package/src/lib/server/session-tools/crud.ts +162 -10
- package/src/lib/server/session-tools/delegate.ts +1 -1
- package/src/lib/server/session-tools/manage-tasks.test.ts +152 -0
- package/src/lib/server/session-tools/memory.ts +6 -4
- package/src/lib/server/session-tools/session-info.test.ts +56 -0
- package/src/lib/server/session-tools/session-info.ts +119 -12
- package/src/lib/server/session-tools/skill-runtime.ts +3 -1
- package/src/lib/server/session-tools/skills.ts +15 -15
- package/src/lib/server/session-tools/subagent.test.ts +115 -1
- package/src/lib/server/session-tools/subagent.ts +125 -7
- package/src/lib/server/session-tools/team-context.ts +4 -3
- package/src/lib/server/session-tools/wallet.ts +0 -58
- package/src/lib/server/sessions/session-lineage.ts +55 -0
- package/src/lib/server/sessions/session-repository.ts +2 -2
- package/src/lib/server/skills/learned-skills.ts +24 -23
- package/src/lib/server/skills/runtime-skill-resolver.ts +2 -1
- package/src/lib/server/skills/skill-repository.ts +136 -13
- package/src/lib/server/skills/skill-suggestions.ts +25 -28
- package/src/lib/server/storage-normalization.test.ts +44 -267
- package/src/lib/server/storage-normalization.ts +75 -0
- package/src/lib/server/storage.ts +19 -0
- package/src/lib/server/structured-extract.ts +3 -14
- package/src/lib/server/tasks/task-followups.ts +16 -11
- package/src/lib/server/tasks/task-result.test.ts +25 -29
- package/src/lib/server/tasks/task-result.ts +5 -9
- package/src/lib/server/tasks/task-route-service.ts +449 -0
- package/src/lib/server/text-normalization.ts +41 -0
- package/src/lib/server/tool-planning.ts +6 -42
- package/src/lib/server/upload-path.ts +5 -0
- package/src/lib/server/working-state/extraction.ts +614 -0
- package/src/lib/server/working-state/normalization.ts +866 -0
- package/src/lib/server/working-state/prompt.ts +60 -0
- package/src/lib/server/working-state/repository.ts +38 -0
- package/src/lib/server/working-state/service.test.ts +253 -0
- package/src/lib/server/working-state/service.ts +293 -0
- package/src/lib/validation/schemas.ts +1 -0
- package/src/lib/ws-client.ts +3 -3
- package/src/stores/slices/task-slice.ts +1 -4
- package/src/stores/use-chatroom-store.ts +2 -2
- package/src/types/index.ts +277 -12
package/README.md
CHANGED
|
@@ -190,6 +190,20 @@ The building blocks are the same: **agents, tools, memory, delegation, schedules
|
|
|
190
190
|
|
|
191
191
|
## Release Notes
|
|
192
192
|
|
|
193
|
+
### v1.2.5 Highlights
|
|
194
|
+
|
|
195
|
+
- **Working memory hierarchy**: agents maintain structured working state (facts, plans, decisions, blockers, evidence) that persists across turns and survives context compaction.
|
|
196
|
+
- **Execution brief**: each chat turn receives a concise briefing document synthesized from working state for faster, more focused agent reasoning.
|
|
197
|
+
- **RunContext**: persistent structured context tracks session lineage, delegation chain, and accumulated working memory across run lifecycle.
|
|
198
|
+
- **Unified message flow**: consolidated message handling and execution routing into a single coherent pipeline.
|
|
199
|
+
- **Delegation advisory**: new advisory layer for structured capability analysis during delegation decisions.
|
|
200
|
+
- **Real-time session sync**: session create, update, and delete events now push over WebSocket, replacing poll-only refresh for the chat list.
|
|
201
|
+
- **HMR resilience**: module-level state in the WebSocket client, fallback polling, and API request dedup now survives Next.js hot-module reloads.
|
|
202
|
+
- **Type safety sweep**: eliminated 16 `any` types across 7 API routes with proper narrowing and error guards.
|
|
203
|
+
- **Bug fix — setup wizard crash**: replaced client-side `crypto.randomUUID()` with browser-safe alternative, fixing fresh-install failures with Ollama and other providers.
|
|
204
|
+
- **Bug fix — custom provider validation**: connection-test endpoint now recognizes custom providers from storage instead of rejecting them as "Unsupported provider."
|
|
205
|
+
- **Bug fix — session cwd normalization**: `updateChatSession` now expands `~` paths consistently with session creation.
|
|
206
|
+
|
|
193
207
|
### v1.2.4 Highlights
|
|
194
208
|
|
|
195
209
|
- **Custom providers in agent config**: agent setup and inline model switching now merge saved custom provider configs into the selectable provider list, so custom providers show up reliably even when the built-in provider feed is stale or incomplete.
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict'
|
|
3
|
+
/* eslint-disable @typescript-eslint/no-require-imports */
|
|
4
|
+
|
|
5
|
+
const crypto = require('node:crypto')
|
|
6
|
+
const fs = require('node:fs')
|
|
7
|
+
const net = require('node:net')
|
|
8
|
+
const path = require('node:path')
|
|
9
|
+
const { spawn } = require('node:child_process')
|
|
10
|
+
|
|
11
|
+
const {
|
|
12
|
+
BROWSER_PROFILES_DIR,
|
|
13
|
+
DATA_DIR,
|
|
14
|
+
PKG_ROOT,
|
|
15
|
+
SWARMCLAW_HOME,
|
|
16
|
+
WORKSPACE_DIR,
|
|
17
|
+
resolvePackageBuildRoot,
|
|
18
|
+
} = require('./server-cmd.js')
|
|
19
|
+
|
|
20
|
+
function printHelp() {
|
|
21
|
+
const help = `
|
|
22
|
+
Usage: swarmclaw daemon run [options]
|
|
23
|
+
|
|
24
|
+
Run the detached SwarmClaw runtime daemon outside the public web process.
|
|
25
|
+
|
|
26
|
+
Options:
|
|
27
|
+
-d, --detach Start daemon in background
|
|
28
|
+
--port <port> Admin port to bind on localhost (default: random)
|
|
29
|
+
--token <token> Admin bearer token (default: random)
|
|
30
|
+
-h, --help Show this help message
|
|
31
|
+
|
|
32
|
+
Other daemon controls remain available through the API-backed CLI:
|
|
33
|
+
swarmclaw daemon status
|
|
34
|
+
swarmclaw daemon start
|
|
35
|
+
swarmclaw daemon stop
|
|
36
|
+
swarmclaw daemon health-check
|
|
37
|
+
`.trim()
|
|
38
|
+
console.log(help)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function resolveRoot() {
|
|
42
|
+
const buildRoot = process.env.SWARMCLAW_BUILD_ROOT || resolvePackageBuildRoot(PKG_ROOT)
|
|
43
|
+
if (fs.existsSync(path.join(buildRoot, 'src', 'lib', 'server', 'daemon', 'daemon-runtime.ts'))) return buildRoot
|
|
44
|
+
return PKG_ROOT
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function resolveEntry(root) {
|
|
48
|
+
const entry = path.join(root, 'src', 'lib', 'server', 'daemon', 'daemon-runtime.ts')
|
|
49
|
+
if (!fs.existsSync(entry)) {
|
|
50
|
+
throw new Error(`Daemon runtime entry not found at ${entry}`)
|
|
51
|
+
}
|
|
52
|
+
return entry
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function reservePort() {
|
|
56
|
+
return new Promise((resolve, reject) => {
|
|
57
|
+
const server = net.createServer()
|
|
58
|
+
server.once('error', reject)
|
|
59
|
+
server.listen(0, '127.0.0.1', () => {
|
|
60
|
+
const address = server.address()
|
|
61
|
+
if (!address || typeof address === 'string') {
|
|
62
|
+
server.close(() => reject(new Error('Failed to reserve daemon port.')))
|
|
63
|
+
return
|
|
64
|
+
}
|
|
65
|
+
const port = address.port
|
|
66
|
+
server.close((err) => {
|
|
67
|
+
if (err) reject(err)
|
|
68
|
+
else resolve(port)
|
|
69
|
+
})
|
|
70
|
+
})
|
|
71
|
+
})
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function buildEnv(root, port, token) {
|
|
75
|
+
return {
|
|
76
|
+
...process.env,
|
|
77
|
+
SWARMCLAW_HOME,
|
|
78
|
+
DATA_DIR,
|
|
79
|
+
WORKSPACE_DIR,
|
|
80
|
+
BROWSER_PROFILES_DIR,
|
|
81
|
+
SWARMCLAW_PACKAGE_ROOT: PKG_ROOT,
|
|
82
|
+
SWARMCLAW_BUILD_ROOT: root,
|
|
83
|
+
SWARMCLAW_RUNTIME_ROLE: 'daemon',
|
|
84
|
+
SWARMCLAW_DAEMON_BACKGROUND_SERVICES: '1',
|
|
85
|
+
SWARMCLAW_DAEMON_ADMIN_PORT: String(port),
|
|
86
|
+
SWARMCLAW_DAEMON_ADMIN_TOKEN: token,
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
async function runDaemon(options) {
|
|
91
|
+
const root = resolveRoot()
|
|
92
|
+
const entry = resolveEntry(root)
|
|
93
|
+
const port = options.port || await reservePort()
|
|
94
|
+
const token = options.token || crypto.randomBytes(24).toString('hex')
|
|
95
|
+
const env = buildEnv(root, port, token)
|
|
96
|
+
const args = ['--no-warnings', '--import', 'tsx', entry, '--port', String(port), '--token', token]
|
|
97
|
+
|
|
98
|
+
if (options.detach) {
|
|
99
|
+
const logPath = path.join(SWARMCLAW_HOME, 'daemon.log')
|
|
100
|
+
fs.mkdirSync(path.dirname(logPath), { recursive: true })
|
|
101
|
+
const logStream = fs.openSync(logPath, 'a')
|
|
102
|
+
const child = spawn(process.execPath, args, {
|
|
103
|
+
cwd: root,
|
|
104
|
+
detached: true,
|
|
105
|
+
env,
|
|
106
|
+
stdio: ['ignore', logStream, logStream],
|
|
107
|
+
})
|
|
108
|
+
child.unref()
|
|
109
|
+
console.log(`[swarmclaw] Daemon started in background (PID: ${child.pid})`)
|
|
110
|
+
console.log(`[swarmclaw] Admin port: ${port}`)
|
|
111
|
+
console.log(`[swarmclaw] Logs: ${logPath}`)
|
|
112
|
+
return
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
console.log(`[swarmclaw] Starting daemon runtime on 127.0.0.1:${port}`)
|
|
116
|
+
const child = spawn(process.execPath, args, {
|
|
117
|
+
cwd: root,
|
|
118
|
+
env,
|
|
119
|
+
stdio: 'inherit',
|
|
120
|
+
})
|
|
121
|
+
child.on('exit', (code) => {
|
|
122
|
+
process.exit(code || 0)
|
|
123
|
+
})
|
|
124
|
+
for (const signal of ['SIGINT', 'SIGTERM']) {
|
|
125
|
+
process.on(signal, () => child.kill(signal))
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
async function main(args = process.argv.slice(3)) {
|
|
130
|
+
let detach = false
|
|
131
|
+
let port = null
|
|
132
|
+
let token = ''
|
|
133
|
+
let command = 'run'
|
|
134
|
+
|
|
135
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
136
|
+
const arg = args[index]
|
|
137
|
+
if (arg === 'run') {
|
|
138
|
+
command = 'run'
|
|
139
|
+
} else if (arg === '-d' || arg === '--detach') {
|
|
140
|
+
detach = true
|
|
141
|
+
} else if (arg === '--port' && index + 1 < args.length) {
|
|
142
|
+
port = Number.parseInt(args[index + 1], 10)
|
|
143
|
+
index += 1
|
|
144
|
+
} else if (arg === '--token' && index + 1 < args.length) {
|
|
145
|
+
token = args[index + 1] || ''
|
|
146
|
+
index += 1
|
|
147
|
+
} else if (arg === '-h' || arg === '--help' || arg === 'help') {
|
|
148
|
+
printHelp()
|
|
149
|
+
return
|
|
150
|
+
} else {
|
|
151
|
+
throw new Error(`Unknown daemon argument: ${arg}`)
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
if (command !== 'run') {
|
|
156
|
+
throw new Error(`Unsupported daemon command: ${command}`)
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
await runDaemon({ detach, port, token })
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
if (require.main === module) {
|
|
163
|
+
void main().catch((err) => {
|
|
164
|
+
console.error(`[swarmclaw] ${err?.message || String(err)}`)
|
|
165
|
+
process.exit(1)
|
|
166
|
+
})
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
module.exports = { main }
|
package/bin/server-cmd.js
CHANGED
|
@@ -381,6 +381,9 @@ async function startServer(opts, { pkgRoot = PKG_ROOT } = {}) {
|
|
|
381
381
|
DATA_DIR,
|
|
382
382
|
WORKSPACE_DIR,
|
|
383
383
|
BROWSER_PROFILES_DIR,
|
|
384
|
+
SWARMCLAW_PACKAGE_ROOT: pkgRoot,
|
|
385
|
+
SWARMCLAW_BUILD_ROOT: buildRoot,
|
|
386
|
+
SWARMCLAW_RUNTIME_ROLE: 'web',
|
|
384
387
|
HOSTNAME: host,
|
|
385
388
|
PORT: port,
|
|
386
389
|
WS_PORT: wsPort,
|
package/bin/swarmclaw.js
CHANGED
|
@@ -131,6 +131,10 @@ async function runHelp(argv) {
|
|
|
131
131
|
await require('./server-cmd.js').main(['--help'])
|
|
132
132
|
return
|
|
133
133
|
}
|
|
134
|
+
if (target === 'daemon') {
|
|
135
|
+
await require('./daemon-cmd.js').main(['--help'])
|
|
136
|
+
return
|
|
137
|
+
}
|
|
134
138
|
if (target === 'worker') {
|
|
135
139
|
require('./worker-cmd.js').main(['--help'])
|
|
136
140
|
return
|
|
@@ -188,6 +192,13 @@ async function main() {
|
|
|
188
192
|
await require('./server-cmd.js').main(argv.slice(1))
|
|
189
193
|
return
|
|
190
194
|
}
|
|
195
|
+
if (top === 'daemon') {
|
|
196
|
+
const subcommand = argv[1]
|
|
197
|
+
if (!subcommand || subcommand === 'run' || subcommand === 'help' || subcommand === '--help' || subcommand === '-h') {
|
|
198
|
+
await require('./daemon-cmd.js').main(argv.slice(1))
|
|
199
|
+
return
|
|
200
|
+
}
|
|
201
|
+
}
|
|
191
202
|
if (top === 'run' || top === 'start') {
|
|
192
203
|
await require('./server-cmd.js').main(argv.slice(1))
|
|
193
204
|
return
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@swarmclawai/swarmclaw",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.6",
|
|
4
4
|
"description": "Self-hosted AI runtime for OpenClaw, delegation, autonomy, runtime skills, crypto wallets, and chat platform connectors.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"publishConfig": {
|
|
@@ -81,14 +81,23 @@
|
|
|
81
81
|
"@huggingface/transformers": "^3.8.1",
|
|
82
82
|
"@langchain/anthropic": "^1.3.18",
|
|
83
83
|
"@langchain/core": "^1.1.31",
|
|
84
|
-
"@modelcontextprotocol/sdk": "^1.27.1",
|
|
85
|
-
"@tailwindcss/postcss": "^4",
|
|
86
84
|
"@langchain/langgraph": "^1.2.2",
|
|
87
85
|
"@langchain/openai": "^1.2.8",
|
|
86
|
+
"@modelcontextprotocol/sdk": "^1.27.1",
|
|
88
87
|
"@multiavatar/multiavatar": "^1.0.7",
|
|
89
88
|
"@playwright/mcp": "^0.0.68",
|
|
90
89
|
"@slack/bolt": "^4.6.0",
|
|
91
90
|
"@solana/web3.js": "^1.98.4",
|
|
91
|
+
"@tailwindcss/postcss": "^4",
|
|
92
|
+
"@tanstack/react-query": "^5.91.0",
|
|
93
|
+
"@types/better-sqlite3": "^7.6.13",
|
|
94
|
+
"@types/mailparser": "^3.4.6",
|
|
95
|
+
"@types/node": "^20",
|
|
96
|
+
"@types/nodemailer": "^7.0.11",
|
|
97
|
+
"@types/qrcode": "^1.5.6",
|
|
98
|
+
"@types/react": "^19",
|
|
99
|
+
"@types/react-dom": "^19",
|
|
100
|
+
"@types/ws": "^8.18.1",
|
|
92
101
|
"@whiskeysockets/baileys": "^7.0.0-rc.9",
|
|
93
102
|
"better-sqlite3": "^12.6.2",
|
|
94
103
|
"bs58": "^5.0.0",
|
|
@@ -125,26 +134,18 @@
|
|
|
125
134
|
"remove-markdown": "^0.6.3",
|
|
126
135
|
"shadcn": "^3.8.5",
|
|
127
136
|
"sonner": "^2.0.7",
|
|
128
|
-
"tailwindcss": "^4",
|
|
129
137
|
"tailwind-merge": "^3.4.1",
|
|
130
|
-
"
|
|
138
|
+
"tailwindcss": "^4",
|
|
131
139
|
"tw-animate-css": "^1.4.0",
|
|
140
|
+
"tsx": "^4.20.6",
|
|
141
|
+
"typescript": "^5",
|
|
132
142
|
"ws": "^8.19.0",
|
|
133
143
|
"zod": "^4.3.6",
|
|
134
|
-
"zustand": "^5.0.11"
|
|
135
|
-
"@types/better-sqlite3": "^7.6.13",
|
|
136
|
-
"@types/mailparser": "^3.4.6",
|
|
137
|
-
"@types/node": "^20",
|
|
138
|
-
"@types/nodemailer": "^7.0.11",
|
|
139
|
-
"@types/qrcode": "^1.5.6",
|
|
140
|
-
"@types/react": "^19",
|
|
141
|
-
"@types/react-dom": "^19",
|
|
142
|
-
"@types/ws": "^8.18.1"
|
|
144
|
+
"zustand": "^5.0.11"
|
|
143
145
|
},
|
|
144
146
|
"devDependencies": {
|
|
145
147
|
"eslint": "^9",
|
|
146
|
-
"eslint-config-next": "16.1.7"
|
|
147
|
-
"tsx": "^4.20.6"
|
|
148
|
+
"eslint-config-next": "16.1.7"
|
|
148
149
|
},
|
|
149
150
|
"optionalDependencies": {
|
|
150
151
|
"botbuilder": "^4.23.3",
|
|
@@ -1,40 +1,11 @@
|
|
|
1
1
|
import { NextResponse } from 'next/server'
|
|
2
|
-
import {
|
|
3
|
-
import { notify } from '@/lib/server/ws-hub'
|
|
2
|
+
import { cloneAgent } from '@/lib/server/agents/agent-service'
|
|
4
3
|
|
|
5
4
|
export async function POST(_req: Request, { params }: { params: Promise<{ id: string }> }) {
|
|
6
5
|
const { id } = await params
|
|
7
|
-
const
|
|
8
|
-
|
|
9
|
-
if (!source) {
|
|
6
|
+
const cloned = cloneAgent(id)
|
|
7
|
+
if (!cloned) {
|
|
10
8
|
return NextResponse.json({ error: 'Agent not found' }, { status: 404 })
|
|
11
9
|
}
|
|
12
|
-
|
|
13
|
-
const newId = crypto.randomUUID()
|
|
14
|
-
const now = Date.now()
|
|
15
|
-
|
|
16
|
-
// Deep-copy the source agent, then override clone-specific fields
|
|
17
|
-
const cloned = JSON.parse(JSON.stringify(source)) as typeof source
|
|
18
|
-
cloned.id = newId
|
|
19
|
-
cloned.name = `${source.name} (Copy)`
|
|
20
|
-
cloned.createdAt = now
|
|
21
|
-
cloned.updatedAt = now
|
|
22
|
-
cloned.totalCost = 0
|
|
23
|
-
cloned.lastUsedAt = undefined
|
|
24
|
-
cloned.threadSessionId = null
|
|
25
|
-
cloned.pinned = false
|
|
26
|
-
cloned.trashedAt = undefined
|
|
27
|
-
|
|
28
|
-
agents[newId] = cloned
|
|
29
|
-
saveAgents(agents)
|
|
30
|
-
logActivity({
|
|
31
|
-
entityType: 'agent',
|
|
32
|
-
entityId: newId,
|
|
33
|
-
action: 'created',
|
|
34
|
-
actor: 'user',
|
|
35
|
-
summary: `Agent cloned from "${source.name}": "${cloned.name}"`,
|
|
36
|
-
})
|
|
37
|
-
notify('agents')
|
|
38
|
-
|
|
39
10
|
return NextResponse.json(cloned)
|
|
40
11
|
}
|
|
@@ -1,175 +1,23 @@
|
|
|
1
1
|
import { NextResponse } from 'next/server'
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import { mutateItem, notFound, type CollectionOps } from '@/lib/server/collection-helpers'
|
|
5
|
-
import { ensureAgentThreadSession } from '@/lib/server/agents/agent-thread-session'
|
|
6
|
-
import { suspendAgentReferences } from '@/lib/server/agents/agent-cascade'
|
|
2
|
+
import { notFound } from '@/lib/server/collection-helpers'
|
|
3
|
+
import { trashAgent, updateAgent } from '@/lib/server/agents/agent-service'
|
|
7
4
|
import { notify } from '@/lib/server/ws-hub'
|
|
8
|
-
import { normalizeAgentSandboxConfig } from '@/lib/agent-sandbox-defaults'
|
|
9
|
-
import { normalizeCapabilitySelection } from '@/lib/capability-selection'
|
|
10
|
-
import { normalizeOrchestratorConfig } from '@/lib/orchestrator-config'
|
|
11
5
|
import { safeParseBody } from '@/lib/server/safe-parse-body'
|
|
12
6
|
|
|
13
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
14
|
-
const ops: CollectionOps<any> = { load: () => loadAgents({ includeTrashed: true }), save: saveAgents, topic: 'agents', table: 'agents' }
|
|
15
|
-
|
|
16
7
|
export async function PUT(req: Request, { params }: { params: Promise<{ id: string }> }) {
|
|
17
8
|
const { id } = await params
|
|
18
9
|
const { data: body, error } = await safeParseBody(req)
|
|
19
10
|
if (error) return error
|
|
20
|
-
const result =
|
|
21
|
-
Object.assign(agent, body, { updatedAt: Date.now() })
|
|
22
|
-
if (body.tools !== undefined || body.extensions !== undefined) {
|
|
23
|
-
const nextSelection = normalizeCapabilitySelection({
|
|
24
|
-
tools: Array.isArray(body.tools) ? body.tools : agent.tools,
|
|
25
|
-
extensions: Array.isArray(body.extensions) ? body.extensions : agent.extensions,
|
|
26
|
-
})
|
|
27
|
-
agent.tools = nextSelection.tools
|
|
28
|
-
agent.extensions = nextSelection.extensions
|
|
29
|
-
}
|
|
30
|
-
if (body.delegationEnabled !== undefined) {
|
|
31
|
-
agent.delegationEnabled = body.delegationEnabled === true
|
|
32
|
-
}
|
|
33
|
-
if (body.delegationTargetMode === 'all' || body.delegationTargetMode === 'selected') {
|
|
34
|
-
agent.delegationTargetMode = body.delegationTargetMode
|
|
35
|
-
}
|
|
36
|
-
if (body.delegationTargetAgentIds !== undefined) {
|
|
37
|
-
agent.delegationTargetAgentIds = Array.isArray(body.delegationTargetAgentIds)
|
|
38
|
-
? body.delegationTargetAgentIds.filter((entry: unknown): entry is string => typeof entry === 'string' && entry.trim().length > 0)
|
|
39
|
-
: []
|
|
40
|
-
}
|
|
41
|
-
if (agent.delegationTargetMode !== 'selected') {
|
|
42
|
-
agent.delegationTargetAgentIds = []
|
|
43
|
-
}
|
|
44
|
-
if (body.apiEndpoint !== undefined) {
|
|
45
|
-
agent.apiEndpoint = normalizeProviderEndpoint(
|
|
46
|
-
body.provider || agent.provider,
|
|
47
|
-
body.apiEndpoint as string | null | undefined,
|
|
48
|
-
)
|
|
49
|
-
}
|
|
50
|
-
if (body.provider !== undefined && body.provider !== 'ollama' && body.ollamaMode === undefined) {
|
|
51
|
-
agent.ollamaMode = null
|
|
52
|
-
}
|
|
53
|
-
if (body.sandboxConfig !== undefined) {
|
|
54
|
-
agent.sandboxConfig = normalizeAgentSandboxConfig(body.sandboxConfig)
|
|
55
|
-
}
|
|
56
|
-
if (
|
|
57
|
-
body.provider !== undefined
|
|
58
|
-
|| body.orchestratorEnabled !== undefined
|
|
59
|
-
|| body.orchestratorMission !== undefined
|
|
60
|
-
|| body.orchestratorWakeInterval !== undefined
|
|
61
|
-
|| body.orchestratorGovernance !== undefined
|
|
62
|
-
|| body.orchestratorMaxCyclesPerDay !== undefined
|
|
63
|
-
) {
|
|
64
|
-
const orchestratorConfig = normalizeOrchestratorConfig({
|
|
65
|
-
provider: typeof body.provider === 'string' ? body.provider : agent.provider,
|
|
66
|
-
orchestratorEnabled: body.orchestratorEnabled ?? agent.orchestratorEnabled,
|
|
67
|
-
orchestratorMission: body.orchestratorMission ?? agent.orchestratorMission,
|
|
68
|
-
orchestratorWakeInterval: body.orchestratorWakeInterval ?? agent.orchestratorWakeInterval,
|
|
69
|
-
orchestratorGovernance: body.orchestratorGovernance ?? agent.orchestratorGovernance,
|
|
70
|
-
orchestratorMaxCyclesPerDay: body.orchestratorMaxCyclesPerDay ?? agent.orchestratorMaxCyclesPerDay,
|
|
71
|
-
})
|
|
72
|
-
agent.orchestratorEnabled = orchestratorConfig.orchestratorEnabled
|
|
73
|
-
agent.orchestratorMission = orchestratorConfig.orchestratorMission
|
|
74
|
-
agent.orchestratorWakeInterval = orchestratorConfig.orchestratorWakeInterval
|
|
75
|
-
agent.orchestratorGovernance = orchestratorConfig.orchestratorGovernance
|
|
76
|
-
agent.orchestratorMaxCyclesPerDay = orchestratorConfig.orchestratorMaxCyclesPerDay
|
|
77
|
-
}
|
|
78
|
-
if (body.preferredGatewayTags !== undefined) {
|
|
79
|
-
agent.preferredGatewayTags = Array.isArray(body.preferredGatewayTags)
|
|
80
|
-
? body.preferredGatewayTags.filter((tag: unknown): tag is string => typeof tag === 'string' && tag.trim().length > 0)
|
|
81
|
-
: []
|
|
82
|
-
}
|
|
83
|
-
if (body.preferredGatewayUseCase !== undefined) {
|
|
84
|
-
agent.preferredGatewayUseCase = typeof body.preferredGatewayUseCase === 'string' && body.preferredGatewayUseCase.trim()
|
|
85
|
-
? body.preferredGatewayUseCase.trim()
|
|
86
|
-
: null
|
|
87
|
-
}
|
|
88
|
-
if (body.routingTargets !== undefined && Array.isArray(body.routingTargets)) {
|
|
89
|
-
agent.routingTargets = body.routingTargets.map((target: Record<string, unknown>, index: number) => ({
|
|
90
|
-
id: typeof target.id === 'string' && target.id.trim() ? target.id.trim() : `route-${index + 1}`,
|
|
91
|
-
label: typeof target.label === 'string' ? target.label : undefined,
|
|
92
|
-
role: target.role,
|
|
93
|
-
provider: (typeof target.provider === 'string' && target.provider.trim() ? target.provider : agent.provider),
|
|
94
|
-
model: typeof target.model === 'string' ? target.model : '',
|
|
95
|
-
ollamaMode: (typeof target.provider === 'string' ? target.provider : agent.provider) === 'ollama'
|
|
96
|
-
? (target.ollamaMode === 'cloud' ? 'cloud' : 'local')
|
|
97
|
-
: null,
|
|
98
|
-
credentialId: target.credentialId ?? null,
|
|
99
|
-
fallbackCredentialIds: Array.isArray(target.fallbackCredentialIds) ? target.fallbackCredentialIds : [],
|
|
100
|
-
apiEndpoint: normalizeProviderEndpoint(
|
|
101
|
-
typeof target.provider === 'string' ? target.provider : agent.provider,
|
|
102
|
-
typeof target.apiEndpoint === 'string' ? target.apiEndpoint : null,
|
|
103
|
-
),
|
|
104
|
-
gatewayProfileId: target.gatewayProfileId ?? null,
|
|
105
|
-
preferredGatewayTags: Array.isArray(target.preferredGatewayTags)
|
|
106
|
-
? target.preferredGatewayTags.filter((tag: unknown): tag is string => typeof tag === 'string' && tag.trim().length > 0)
|
|
107
|
-
: [],
|
|
108
|
-
preferredGatewayUseCase: typeof target.preferredGatewayUseCase === 'string' && target.preferredGatewayUseCase.trim()
|
|
109
|
-
? target.preferredGatewayUseCase.trim()
|
|
110
|
-
: null,
|
|
111
|
-
priority: typeof target.priority === 'number' ? target.priority : index + 1,
|
|
112
|
-
}))
|
|
113
|
-
}
|
|
114
|
-
delete (agent as Record<string, unknown>).platformAssignScope
|
|
115
|
-
delete (agent as Record<string, unknown>).subAgentIds
|
|
116
|
-
delete (agent as Record<string, unknown>).id
|
|
117
|
-
agent.id = id
|
|
118
|
-
return agent
|
|
119
|
-
})
|
|
11
|
+
const result = updateAgent(id, body as Record<string, unknown>)
|
|
120
12
|
if (!result) return notFound()
|
|
121
|
-
|
|
122
|
-
if (result.threadSessionId) {
|
|
123
|
-
ensureAgentThreadSession(id)
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
if (result.threadSessionId) {
|
|
127
|
-
const sessions = loadSessions()
|
|
128
|
-
const shortcut = sessions[result.threadSessionId]
|
|
129
|
-
if (shortcut) {
|
|
130
|
-
let changed = false
|
|
131
|
-
if (shortcut.name !== result.name) {
|
|
132
|
-
shortcut.name = result.name
|
|
133
|
-
changed = true
|
|
134
|
-
}
|
|
135
|
-
if (shortcut.shortcutForAgentId !== id) {
|
|
136
|
-
shortcut.shortcutForAgentId = id
|
|
137
|
-
changed = true
|
|
138
|
-
}
|
|
139
|
-
if (changed) upsertStoredItem('sessions', shortcut.id, shortcut)
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
logActivity({ entityType: 'agent', entityId: id, action: 'updated', actor: 'user', summary: `Agent updated: "${result.name}"` })
|
|
144
13
|
return NextResponse.json(result)
|
|
145
14
|
}
|
|
146
15
|
|
|
147
16
|
export async function DELETE(_req: Request, { params }: { params: Promise<{ id: string }> }) {
|
|
148
17
|
const { id } = await params
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
return agent
|
|
153
|
-
})
|
|
154
|
-
if (!result) return notFound()
|
|
155
|
-
logActivity({ entityType: 'agent', entityId: id, action: 'deleted', actor: 'user', summary: `Agent trashed: "${result.name}"` })
|
|
156
|
-
|
|
157
|
-
// Detach sessions from the trashed agent
|
|
158
|
-
const sessions = loadSessions()
|
|
159
|
-
const detached: Array<[string, unknown]> = []
|
|
160
|
-
for (const session of Object.values(sessions)) {
|
|
161
|
-
if (!session || session.agentId !== id) continue
|
|
162
|
-
session.agentId = null
|
|
163
|
-
session.heartbeatEnabled = false
|
|
164
|
-
detached.push([session.id, session])
|
|
165
|
-
}
|
|
166
|
-
if (detached.length > 0) {
|
|
167
|
-
upsertStoredItems('sessions', detached)
|
|
168
|
-
}
|
|
169
|
-
const detachedSessions = detached.length
|
|
170
|
-
|
|
171
|
-
// Cascade: suspend tasks, schedules, watch jobs, connectors, webhooks, chatrooms
|
|
172
|
-
const cascade = suspendAgentReferences(id)
|
|
18
|
+
const result = trashAgent(id)
|
|
19
|
+
if (!result.ok) return notFound()
|
|
20
|
+
const { detachedSessions, cascade } = result
|
|
173
21
|
if (cascade.tasks) notify('tasks')
|
|
174
22
|
if (cascade.schedules) notify('schedules')
|
|
175
23
|
if (cascade.connectors) notify('connectors')
|
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
import { NextResponse } from 'next/server'
|
|
2
|
-
import {
|
|
2
|
+
import { getAgentStatus } from '@/lib/server/agents/agent-service'
|
|
3
3
|
import { getMainLoopStateForSession } from '@/lib/server/agents/main-agent-loop'
|
|
4
4
|
|
|
5
5
|
export const dynamic = 'force-dynamic'
|
|
6
6
|
|
|
7
7
|
export async function GET(_req: Request, { params }: { params: Promise<{ id: string }> }) {
|
|
8
8
|
const { id } = await params
|
|
9
|
-
const
|
|
10
|
-
const agent = agents[id]
|
|
9
|
+
const agent = getAgentStatus(id)
|
|
11
10
|
if (!agent) return NextResponse.json(null, { status: 404 })
|
|
12
11
|
|
|
13
12
|
const sessionId = agent.threadSessionId
|
|
@@ -1,23 +1,10 @@
|
|
|
1
1
|
import { NextResponse } from 'next/server'
|
|
2
|
-
import {
|
|
3
|
-
import { ensureAgentThreadSession } from '@/lib/server/agents/agent-thread-session'
|
|
4
|
-
import type { Agent } from '@/types'
|
|
5
|
-
import { loadAgents } from '@/lib/server/storage'
|
|
2
|
+
import { getAgentThreadSession } from '@/lib/server/agents/agent-service'
|
|
6
3
|
|
|
7
4
|
export async function POST(req: Request, { params }: { params: Promise<{ id: string }> }) {
|
|
8
5
|
const { id: agentId } = await params
|
|
9
6
|
const body = await req.json().catch(() => ({}))
|
|
10
|
-
const
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
return NextResponse.json({ error: 'Agent not found' }, { status: 404 })
|
|
14
|
-
}
|
|
15
|
-
const session = ensureAgentThreadSession(agentId, user, agent as Agent)
|
|
16
|
-
if (!session) {
|
|
17
|
-
if (isAgentDisabled(agent)) {
|
|
18
|
-
return NextResponse.json({ error: buildAgentDisabledMessage(agent, 'start new chats') }, { status: 409 })
|
|
19
|
-
}
|
|
20
|
-
return NextResponse.json({ error: 'Agent not found' }, { status: 404 })
|
|
21
|
-
}
|
|
22
|
-
return NextResponse.json(session)
|
|
7
|
+
const result = getAgentThreadSession(agentId, typeof body.user === 'string' ? body.user : 'default')
|
|
8
|
+
if (!result.ok) return NextResponse.json(result.payload, { status: result.status })
|
|
9
|
+
return NextResponse.json(result.payload)
|
|
23
10
|
}
|
|
@@ -1,55 +1,13 @@
|
|
|
1
1
|
import { NextResponse } from 'next/server'
|
|
2
2
|
import { safeParseBody } from '@/lib/server/safe-parse-body'
|
|
3
|
-
import {
|
|
4
|
-
import { logActivity } from '@/lib/server/storage'
|
|
5
|
-
import { notify } from '@/lib/server/ws-hub'
|
|
3
|
+
import { bulkPatchAgents } from '@/lib/server/agents/agent-service'
|
|
6
4
|
|
|
7
5
|
export async function PATCH(req: Request) {
|
|
8
6
|
const { data: body, error } = await safeParseBody<Record<string, unknown>>(req)
|
|
9
7
|
if (error) return error
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
return NextResponse.json({ error: 'patches must be a non-empty array' }, { status: 400 })
|
|
8
|
+
const result = bulkPatchAgents(body.patches)
|
|
9
|
+
if (result.updated === 0 && result.errors.length === 1 && result.errors[0] === 'patches must be a non-empty array') {
|
|
10
|
+
return NextResponse.json({ error: result.errors[0] }, { status: 400 })
|
|
14
11
|
}
|
|
15
|
-
|
|
16
|
-
let updated = 0
|
|
17
|
-
const errors: string[] = []
|
|
18
|
-
|
|
19
|
-
for (const entry of patches) {
|
|
20
|
-
if (!entry || typeof entry !== 'object' || Array.isArray(entry)) {
|
|
21
|
-
errors.push('Invalid patch entry (not an object)')
|
|
22
|
-
continue
|
|
23
|
-
}
|
|
24
|
-
const { id, patch } = entry as { id?: unknown; patch?: unknown }
|
|
25
|
-
if (typeof id !== 'string' || !id.trim()) {
|
|
26
|
-
errors.push('Patch entry missing valid id')
|
|
27
|
-
continue
|
|
28
|
-
}
|
|
29
|
-
if (!patch || typeof patch !== 'object' || Array.isArray(patch)) {
|
|
30
|
-
errors.push(`Patch for ${id} is not a valid object`)
|
|
31
|
-
continue
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
const result = patchAgent(id, (current) => {
|
|
35
|
-
if (!current) return null
|
|
36
|
-
return { ...current, ...(patch as Record<string, unknown>), updatedAt: Date.now() }
|
|
37
|
-
})
|
|
38
|
-
|
|
39
|
-
if (result) {
|
|
40
|
-
updated++
|
|
41
|
-
logActivity({
|
|
42
|
-
entityType: 'agent',
|
|
43
|
-
entityId: id,
|
|
44
|
-
action: 'updated',
|
|
45
|
-
actor: 'user',
|
|
46
|
-
summary: `Bulk patch: updated agent "${result.name || id}"`,
|
|
47
|
-
})
|
|
48
|
-
} else {
|
|
49
|
-
errors.push(`Agent ${id} not found`)
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
if (updated > 0) notify('agents')
|
|
54
|
-
return NextResponse.json({ updated, errors })
|
|
12
|
+
return NextResponse.json(result)
|
|
55
13
|
}
|