@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,32 +1,7 @@
|
|
|
1
1
|
import { NextResponse } from 'next/server'
|
|
2
2
|
import { getPluginManager } from '@/lib/server/plugins'
|
|
3
3
|
import { notify } from '@/lib/server/ws-hub'
|
|
4
|
-
|
|
5
|
-
// Ensure all builtin plugins are registered by importing their modules
|
|
6
|
-
import '@/lib/server/session-tools/shell'
|
|
7
|
-
import '@/lib/server/session-tools/file'
|
|
8
|
-
import '@/lib/server/session-tools/edit_file'
|
|
9
|
-
import '@/lib/server/session-tools/web'
|
|
10
|
-
import '@/lib/server/session-tools/memory'
|
|
11
|
-
import '@/lib/server/session-tools/platform'
|
|
12
|
-
import '@/lib/server/session-tools/monitor'
|
|
13
|
-
import '@/lib/server/session-tools/discovery'
|
|
14
|
-
import '@/lib/server/session-tools/sample-ui'
|
|
15
|
-
import '@/lib/server/session-tools/git'
|
|
16
|
-
import '@/lib/server/session-tools/wallet'
|
|
17
|
-
import '@/lib/server/session-tools/connector'
|
|
18
|
-
import '@/lib/server/session-tools/http'
|
|
19
|
-
import '@/lib/server/session-tools/sandbox'
|
|
20
|
-
import '@/lib/server/session-tools/canvas'
|
|
21
|
-
import '@/lib/server/session-tools/chatroom'
|
|
22
|
-
import '@/lib/server/session-tools/delegate'
|
|
23
|
-
import '@/lib/server/session-tools/schedule'
|
|
24
|
-
import '@/lib/server/session-tools/session-info'
|
|
25
|
-
import '@/lib/server/session-tools/openclaw-nodes'
|
|
26
|
-
import '@/lib/server/session-tools/openclaw-workspace'
|
|
27
|
-
import '@/lib/server/session-tools/context-mgmt'
|
|
28
|
-
import '@/lib/server/session-tools/subagent'
|
|
29
|
-
import '@/lib/server/session-tools/plugin-creator'
|
|
4
|
+
import '@/lib/server/builtin-plugins'
|
|
30
5
|
|
|
31
6
|
export const dynamic = 'force-dynamic'
|
|
32
7
|
|
|
@@ -74,11 +49,13 @@ export async function PATCH(req: Request) {
|
|
|
74
49
|
|
|
75
50
|
if (all) {
|
|
76
51
|
await manager.updateAllPlugins()
|
|
52
|
+
notify('plugins')
|
|
77
53
|
return NextResponse.json({ ok: true, message: 'All plugins updated' })
|
|
78
54
|
}
|
|
79
55
|
|
|
80
56
|
if (id) {
|
|
81
57
|
await manager.updatePlugin(id)
|
|
58
|
+
notify('plugins')
|
|
82
59
|
return NextResponse.json({ ok: true, message: `Plugin ${id} updated` })
|
|
83
60
|
}
|
|
84
61
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { NextResponse } from 'next/server'
|
|
2
|
-
import {
|
|
2
|
+
import { getPluginManager } from '@/lib/server/plugins'
|
|
3
|
+
import '@/lib/server/builtin-plugins'
|
|
3
4
|
|
|
4
5
|
export const dynamic = 'force-dynamic'
|
|
5
6
|
|
|
@@ -11,9 +12,7 @@ export async function GET(req: Request) {
|
|
|
11
12
|
return NextResponse.json({ error: 'pluginId required' }, { status: 400 })
|
|
12
13
|
}
|
|
13
14
|
|
|
14
|
-
|
|
15
|
-
const pluginSettings = (settings.pluginSettings as Record<string, Record<string, unknown>> | undefined) ?? {}
|
|
16
|
-
return NextResponse.json(pluginSettings[pluginId] ?? {})
|
|
15
|
+
return NextResponse.json(getPluginManager().getPublicPluginSettings(pluginId))
|
|
17
16
|
}
|
|
18
17
|
|
|
19
18
|
/** PUT /api/plugins/settings?pluginId=X — write per-plugin settings */
|
|
@@ -24,12 +23,18 @@ export async function PUT(req: Request) {
|
|
|
24
23
|
return NextResponse.json({ error: 'pluginId required' }, { status: 400 })
|
|
25
24
|
}
|
|
26
25
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
26
|
+
try {
|
|
27
|
+
const body = await req.json() as Record<string, unknown>
|
|
28
|
+
const saved = getPluginManager().setPluginSettings(pluginId, body)
|
|
29
|
+
return NextResponse.json({
|
|
30
|
+
ok: true,
|
|
31
|
+
values: saved,
|
|
32
|
+
configuredSecretFields: getPluginManager().getPublicPluginSettings(pluginId).configuredSecretFields,
|
|
33
|
+
})
|
|
34
|
+
} catch (err: unknown) {
|
|
35
|
+
return NextResponse.json(
|
|
36
|
+
{ error: err instanceof Error ? err.message : String(err) },
|
|
37
|
+
{ status: 400 },
|
|
38
|
+
)
|
|
39
|
+
}
|
|
35
40
|
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { NextResponse } from 'next/server'
|
|
2
|
+
import { discoverProviderModels } from '@/lib/server/provider-model-discovery'
|
|
3
|
+
|
|
4
|
+
export const dynamic = 'force-dynamic'
|
|
5
|
+
|
|
6
|
+
export async function GET(
|
|
7
|
+
req: Request,
|
|
8
|
+
{ params }: { params: Promise<{ id: string }> },
|
|
9
|
+
) {
|
|
10
|
+
const { id } = await params
|
|
11
|
+
const { searchParams } = new URL(req.url)
|
|
12
|
+
const result = await discoverProviderModels({
|
|
13
|
+
providerId: id,
|
|
14
|
+
credentialId: searchParams.get('credentialId'),
|
|
15
|
+
endpoint: searchParams.get('endpoint'),
|
|
16
|
+
force: searchParams.get('force') === '1',
|
|
17
|
+
requiresApiKey: searchParams.has('requiresApiKey')
|
|
18
|
+
? searchParams.get('requiresApiKey') !== '0'
|
|
19
|
+
: undefined,
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
return NextResponse.json(result, {
|
|
23
|
+
headers: {
|
|
24
|
+
'Cache-Control': 'private, no-store',
|
|
25
|
+
},
|
|
26
|
+
})
|
|
27
|
+
}
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { NextResponse } from 'next/server'
|
|
2
|
-
import { loadSchedules, saveSchedules, deleteSchedule } from '@/lib/server/storage'
|
|
2
|
+
import { loadAgents, loadSchedules, loadSessions, saveSchedules, deleteSchedule } from '@/lib/server/storage'
|
|
3
|
+
import { WORKSPACE_DIR } from '@/lib/server/data-dir'
|
|
3
4
|
import { resolveScheduleName } from '@/lib/schedule-name'
|
|
4
5
|
import { mutateItem, deleteItem, notFound, type CollectionOps } from '@/lib/server/collection-helpers'
|
|
6
|
+
import { normalizeSchedulePayload } from '@/lib/server/schedule-normalization'
|
|
5
7
|
|
|
6
8
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
7
9
|
const ops: CollectionOps<any> = { load: loadSchedules, save: saveSchedules, deleteFn: deleteSchedule, topic: 'schedules' }
|
|
@@ -9,15 +11,42 @@ const ops: CollectionOps<any> = { load: loadSchedules, save: saveSchedules, dele
|
|
|
9
11
|
export async function PUT(req: Request, { params }: { params: Promise<{ id: string }> }) {
|
|
10
12
|
const { id } = await params
|
|
11
13
|
const body = await req.json()
|
|
12
|
-
const
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
14
|
+
const sessions = loadSessions()
|
|
15
|
+
const agents = loadAgents()
|
|
16
|
+
let result = null
|
|
17
|
+
try {
|
|
18
|
+
result = mutateItem(ops, id, (schedule) => {
|
|
19
|
+
const sessionCwd = typeof schedule.createdInSessionId === 'string'
|
|
20
|
+
? sessions[schedule.createdInSessionId]?.cwd
|
|
21
|
+
: null
|
|
22
|
+
const normalized = normalizeSchedulePayload({
|
|
23
|
+
...schedule,
|
|
24
|
+
...(body as Record<string, unknown>),
|
|
25
|
+
id,
|
|
26
|
+
}, {
|
|
27
|
+
cwd: sessionCwd || WORKSPACE_DIR,
|
|
28
|
+
now: Date.now(),
|
|
29
|
+
})
|
|
30
|
+
if (!normalized.ok) throw new Error(normalized.error)
|
|
31
|
+
const nextSchedule = {
|
|
32
|
+
...schedule,
|
|
33
|
+
...normalized.value,
|
|
34
|
+
id,
|
|
35
|
+
updatedAt: Date.now(),
|
|
36
|
+
}
|
|
37
|
+
if (!agents[String(nextSchedule.agentId)]) {
|
|
38
|
+
throw new Error(`Agent not found: ${String(nextSchedule.agentId)}`)
|
|
39
|
+
}
|
|
40
|
+
nextSchedule.name = resolveScheduleName({
|
|
41
|
+
name: nextSchedule.name,
|
|
42
|
+
taskPrompt: nextSchedule.taskPrompt,
|
|
43
|
+
})
|
|
44
|
+
return nextSchedule
|
|
18
45
|
})
|
|
19
|
-
|
|
20
|
-
|
|
46
|
+
} catch (error: unknown) {
|
|
47
|
+
const message = error instanceof Error ? error.message : String(error)
|
|
48
|
+
return NextResponse.json({ error: message }, { status: 400 })
|
|
49
|
+
}
|
|
21
50
|
if (!result) return notFound()
|
|
22
51
|
return NextResponse.json(result)
|
|
23
52
|
}
|
|
@@ -1,11 +1,31 @@
|
|
|
1
1
|
import { NextResponse } from 'next/server'
|
|
2
2
|
import { genId } from '@/lib/id'
|
|
3
|
-
import { loadSchedules, saveSchedules } from '@/lib/server/storage'
|
|
3
|
+
import { loadAgents, loadSchedules, saveSchedules } from '@/lib/server/storage'
|
|
4
|
+
import { WORKSPACE_DIR } from '@/lib/server/data-dir'
|
|
5
|
+
import { normalizeSchedulePayload } from '@/lib/server/schedule-normalization'
|
|
4
6
|
import { resolveScheduleName } from '@/lib/schedule-name'
|
|
5
7
|
import { findDuplicateSchedule } from '@/lib/schedule-dedupe'
|
|
6
8
|
import { notify } from '@/lib/server/ws-hub'
|
|
7
9
|
export const dynamic = 'force-dynamic'
|
|
8
10
|
|
|
11
|
+
function asString(value: unknown): string {
|
|
12
|
+
return typeof value === 'string' ? value.trim() : ''
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function asPositiveInt(value: unknown): number | null {
|
|
16
|
+
const parsed = typeof value === 'number'
|
|
17
|
+
? value
|
|
18
|
+
: typeof value === 'string'
|
|
19
|
+
? Number.parseInt(value, 10)
|
|
20
|
+
: Number.NaN
|
|
21
|
+
if (!Number.isFinite(parsed)) return null
|
|
22
|
+
const intValue = Math.trunc(parsed)
|
|
23
|
+
return intValue > 0 ? intValue : null
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function asScheduleType(value: unknown): 'cron' | 'interval' | 'once' {
|
|
27
|
+
return value === 'cron' || value === 'interval' || value === 'once' ? value : 'cron'
|
|
28
|
+
}
|
|
9
29
|
|
|
10
30
|
export async function GET(_req: Request) {
|
|
11
31
|
return NextResponse.json(loadSchedules())
|
|
@@ -15,28 +35,46 @@ export async function POST(req: Request) {
|
|
|
15
35
|
const body = await req.json()
|
|
16
36
|
const now = Date.now()
|
|
17
37
|
const schedules = loadSchedules()
|
|
18
|
-
const
|
|
38
|
+
const normalizedSchedule = normalizeSchedulePayload(body as Record<string, unknown>, {
|
|
39
|
+
cwd: WORKSPACE_DIR,
|
|
40
|
+
now,
|
|
41
|
+
})
|
|
42
|
+
if (!normalizedSchedule.ok) {
|
|
43
|
+
return NextResponse.json({ error: normalizedSchedule.error }, { status: 400 })
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const candidate = normalizedSchedule.value
|
|
47
|
+
const agents = loadAgents()
|
|
48
|
+
if (!agents[String(candidate.agentId)]) {
|
|
49
|
+
return NextResponse.json({ error: `Agent not found: ${String(candidate.agentId)}` }, { status: 400 })
|
|
50
|
+
}
|
|
51
|
+
const scheduleType = asScheduleType(candidate.scheduleType)
|
|
52
|
+
const candidateAgentId = asString(candidate.agentId) || null
|
|
53
|
+
const candidateTaskPrompt = asString(candidate.taskPrompt)
|
|
54
|
+
const candidateCron = asString(candidate.cron) || null
|
|
55
|
+
const candidateIntervalMs = asPositiveInt(candidate.intervalMs)
|
|
56
|
+
const candidateRunAt = asPositiveInt(candidate.runAt)
|
|
19
57
|
|
|
20
58
|
const duplicate = findDuplicateSchedule(schedules, {
|
|
21
|
-
agentId:
|
|
22
|
-
taskPrompt:
|
|
59
|
+
agentId: candidateAgentId,
|
|
60
|
+
taskPrompt: candidateTaskPrompt,
|
|
23
61
|
scheduleType,
|
|
24
|
-
cron:
|
|
25
|
-
intervalMs:
|
|
26
|
-
runAt:
|
|
62
|
+
cron: candidateCron,
|
|
63
|
+
intervalMs: candidateIntervalMs,
|
|
64
|
+
runAt: candidateRunAt,
|
|
27
65
|
})
|
|
28
66
|
if (duplicate) {
|
|
29
67
|
const duplicateId = duplicate.id || ''
|
|
30
68
|
let changed = false
|
|
31
69
|
const nextName = resolveScheduleName({
|
|
32
|
-
name:
|
|
33
|
-
taskPrompt:
|
|
70
|
+
name: candidate.name ?? duplicate.name,
|
|
71
|
+
taskPrompt: candidate.taskPrompt ?? duplicate.taskPrompt,
|
|
34
72
|
})
|
|
35
73
|
if (nextName && nextName !== duplicate.name) {
|
|
36
74
|
duplicate.name = nextName
|
|
37
75
|
changed = true
|
|
38
76
|
}
|
|
39
|
-
const normalizedStatus = typeof
|
|
77
|
+
const normalizedStatus = typeof candidate.status === 'string' ? candidate.status.trim().toLowerCase() : ''
|
|
40
78
|
if ((normalizedStatus === 'active' || normalizedStatus === 'paused') && duplicate.status !== normalizedStatus) {
|
|
41
79
|
duplicate.status = normalizedStatus as 'active' | 'paused'
|
|
42
80
|
changed = true
|
|
@@ -53,29 +91,14 @@ export async function POST(req: Request) {
|
|
|
53
91
|
|
|
54
92
|
const id = genId()
|
|
55
93
|
|
|
56
|
-
let nextRunAt: number | undefined
|
|
57
|
-
if (scheduleType === 'once' && body.runAt) {
|
|
58
|
-
nextRunAt = body.runAt
|
|
59
|
-
} else if (scheduleType === 'interval' && body.intervalMs) {
|
|
60
|
-
nextRunAt = now + body.intervalMs
|
|
61
|
-
} else if (scheduleType === 'cron') {
|
|
62
|
-
// nextRunAt will be computed by the scheduler engine
|
|
63
|
-
nextRunAt = undefined
|
|
64
|
-
}
|
|
65
|
-
|
|
66
94
|
schedules[id] = {
|
|
67
95
|
id,
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
taskPrompt: body.taskPrompt || '',
|
|
96
|
+
...candidate,
|
|
97
|
+
name: resolveScheduleName({ name: candidate.name, taskPrompt: candidate.taskPrompt }),
|
|
71
98
|
scheduleType,
|
|
72
|
-
cron: body.cron,
|
|
73
|
-
intervalMs: body.intervalMs,
|
|
74
|
-
runAt: body.runAt,
|
|
75
99
|
lastRunAt: undefined,
|
|
76
|
-
nextRunAt,
|
|
77
|
-
status: body.status || 'active',
|
|
78
100
|
createdAt: now,
|
|
101
|
+
updatedAt: now,
|
|
79
102
|
}
|
|
80
103
|
saveSchedules(schedules)
|
|
81
104
|
notify('schedules')
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { NextResponse } from 'next/server'
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import { normalizeHeartbeatSettingFields } from '@/lib/heartbeat-defaults'
|
|
3
|
+
import { loadPublicSettings, loadSettings, saveSettings } from '@/lib/server/storage'
|
|
4
|
+
import { normalizeRuntimeSettingFields } from '@/lib/runtime-loop'
|
|
4
5
|
export const dynamic = 'force-dynamic'
|
|
5
6
|
|
|
6
7
|
|
|
@@ -10,8 +11,6 @@ const MEMORY_PER_LOOKUP_MIN = 1
|
|
|
10
11
|
const MEMORY_PER_LOOKUP_MAX = 200
|
|
11
12
|
const MEMORY_LINKED_MIN = 0
|
|
12
13
|
const MEMORY_LINKED_MAX = 1000
|
|
13
|
-
const DELEGATION_DEPTH_MIN = 1
|
|
14
|
-
const DELEGATION_DEPTH_MAX = 12
|
|
15
14
|
const RESPONSE_CACHE_TTL_MIN_SEC = 5
|
|
16
15
|
const RESPONSE_CACHE_TTL_MAX_SEC = 7 * 24 * 3600
|
|
17
16
|
const RESPONSE_CACHE_MAX_ENTRIES_MIN = 1
|
|
@@ -20,6 +19,9 @@ const TASK_QG_MIN_RESULT_MIN = 10
|
|
|
20
19
|
const TASK_QG_MIN_RESULT_MAX = 2000
|
|
21
20
|
const TASK_QG_MIN_EVIDENCE_MIN = 0
|
|
22
21
|
const TASK_QG_MIN_EVIDENCE_MAX = 8
|
|
22
|
+
const SESSION_RESET_TIMEOUT_MIN = 0
|
|
23
|
+
const SESSION_RESET_TIMEOUT_MAX = 365 * 24 * 60 * 60
|
|
24
|
+
const SECRET_SETTING_KEYS = ['elevenLabsApiKey', 'tavilyApiKey', 'braveApiKey'] as const
|
|
23
25
|
|
|
24
26
|
function parseIntSetting(value: unknown, fallback: number, min: number, max: number): number {
|
|
25
27
|
const parsed = typeof value === 'number'
|
|
@@ -42,13 +44,25 @@ function parseBoolSetting(value: unknown, fallback: boolean): boolean {
|
|
|
42
44
|
}
|
|
43
45
|
|
|
44
46
|
export async function GET(_req: Request) {
|
|
45
|
-
return NextResponse.json(
|
|
47
|
+
return NextResponse.json(loadPublicSettings())
|
|
46
48
|
}
|
|
47
49
|
|
|
48
50
|
export async function PUT(req: Request) {
|
|
49
|
-
const body = await req.json()
|
|
51
|
+
const body = await req.json() as Record<string, unknown>
|
|
52
|
+
const sanitizedBody: Record<string, unknown> = { ...body }
|
|
53
|
+
|
|
54
|
+
delete sanitizedBody.__encryptedAppSettings
|
|
55
|
+
|
|
56
|
+
for (const key of SECRET_SETTING_KEYS) {
|
|
57
|
+
const configuredKey = `${key}Configured`
|
|
58
|
+
if (sanitizedBody[key] === null && sanitizedBody[configuredKey] === true) {
|
|
59
|
+
delete sanitizedBody[key]
|
|
60
|
+
}
|
|
61
|
+
delete sanitizedBody[configuredKey]
|
|
62
|
+
}
|
|
63
|
+
|
|
50
64
|
const settings = loadSettings()
|
|
51
|
-
Object.assign(settings,
|
|
65
|
+
Object.assign(settings, sanitizedBody)
|
|
52
66
|
|
|
53
67
|
const nextDepth = parseIntSetting(
|
|
54
68
|
settings.memoryReferenceDepth ?? settings.memoryMaxDepth,
|
|
@@ -68,12 +82,8 @@ export async function PUT(req: Request) {
|
|
|
68
82
|
MEMORY_LINKED_MIN,
|
|
69
83
|
MEMORY_LINKED_MAX,
|
|
70
84
|
)
|
|
71
|
-
const
|
|
72
|
-
|
|
73
|
-
DEFAULT_DELEGATION_MAX_DEPTH,
|
|
74
|
-
DELEGATION_DEPTH_MIN,
|
|
75
|
-
DELEGATION_DEPTH_MAX,
|
|
76
|
-
)
|
|
85
|
+
const normalizedRuntime = normalizeRuntimeSettingFields(settings)
|
|
86
|
+
const normalizedHeartbeat = normalizeHeartbeatSettingFields(settings)
|
|
77
87
|
const nextResponseCacheTtlSec = parseIntSetting(
|
|
78
88
|
settings.responseCacheTtlSec,
|
|
79
89
|
15 * 60,
|
|
@@ -105,7 +115,8 @@ export async function PUT(req: Request) {
|
|
|
105
115
|
settings.maxMemoriesPerLookup = nextPerLookup
|
|
106
116
|
settings.memoryMaxPerLookup = nextPerLookup
|
|
107
117
|
settings.maxLinkedMemoriesExpanded = nextLinked
|
|
108
|
-
settings
|
|
118
|
+
Object.assign(settings, normalizedRuntime)
|
|
119
|
+
Object.assign(settings, normalizedHeartbeat)
|
|
109
120
|
settings.responseCacheTtlSec = nextResponseCacheTtlSec
|
|
110
121
|
settings.responseCacheMaxEntries = nextResponseCacheMaxEntries
|
|
111
122
|
settings.responseCacheEnabled = parseBoolSetting(settings.responseCacheEnabled, true)
|
|
@@ -116,16 +127,43 @@ export async function PUT(req: Request) {
|
|
|
116
127
|
settings.taskQualityGateRequireArtifact = parseBoolSetting(settings.taskQualityGateRequireArtifact, false)
|
|
117
128
|
settings.taskQualityGateRequireReport = parseBoolSetting(settings.taskQualityGateRequireReport, false)
|
|
118
129
|
settings.integrityMonitorEnabled = parseBoolSetting(settings.integrityMonitorEnabled, true)
|
|
130
|
+
settings.sessionResetMode = settings.sessionResetMode === 'daily' ? 'daily' : settings.sessionResetMode === 'idle' ? 'idle' : null
|
|
131
|
+
settings.sessionIdleTimeoutSec = parseIntSetting(
|
|
132
|
+
settings.sessionIdleTimeoutSec,
|
|
133
|
+
12 * 60 * 60,
|
|
134
|
+
SESSION_RESET_TIMEOUT_MIN,
|
|
135
|
+
SESSION_RESET_TIMEOUT_MAX,
|
|
136
|
+
)
|
|
137
|
+
settings.sessionMaxAgeSec = parseIntSetting(
|
|
138
|
+
settings.sessionMaxAgeSec,
|
|
139
|
+
7 * 24 * 60 * 60,
|
|
140
|
+
SESSION_RESET_TIMEOUT_MIN,
|
|
141
|
+
SESSION_RESET_TIMEOUT_MAX,
|
|
142
|
+
)
|
|
143
|
+
if (typeof settings.sessionDailyResetAt === 'string') settings.sessionDailyResetAt = settings.sessionDailyResetAt.trim() || null
|
|
144
|
+
if (typeof settings.sessionResetTimezone === 'string') settings.sessionResetTimezone = settings.sessionResetTimezone.trim() || null
|
|
119
145
|
|
|
120
146
|
saveSettings(settings)
|
|
121
147
|
|
|
122
148
|
// Restart heartbeat service when heartbeat-related settings change
|
|
123
|
-
const heartbeatKeys = [
|
|
124
|
-
|
|
149
|
+
const heartbeatKeys = [
|
|
150
|
+
'heartbeatIntervalSec',
|
|
151
|
+
'heartbeatInterval',
|
|
152
|
+
'heartbeatPrompt',
|
|
153
|
+
'heartbeatEnabled',
|
|
154
|
+
'heartbeatActiveStart',
|
|
155
|
+
'heartbeatActiveEnd',
|
|
156
|
+
'sessionResetMode',
|
|
157
|
+
'sessionIdleTimeoutSec',
|
|
158
|
+
'sessionMaxAgeSec',
|
|
159
|
+
'sessionDailyResetAt',
|
|
160
|
+
'sessionResetTimezone',
|
|
161
|
+
]
|
|
162
|
+
if (heartbeatKeys.some((k) => k in sanitizedBody)) {
|
|
125
163
|
import('@/lib/server/heartbeat-service').then(({ restartHeartbeatService }) => {
|
|
126
164
|
restartHeartbeatService()
|
|
127
165
|
}).catch(() => { /* heartbeat service may not be initialized yet */ })
|
|
128
166
|
}
|
|
129
167
|
|
|
130
|
-
return NextResponse.json(
|
|
168
|
+
return NextResponse.json(loadPublicSettings())
|
|
131
169
|
}
|
|
@@ -90,12 +90,14 @@ export async function GET(req: Request) {
|
|
|
90
90
|
const checkedAt = Date.now()
|
|
91
91
|
|
|
92
92
|
const nodeVersion = process.versions.node
|
|
93
|
-
const
|
|
94
|
-
|
|
93
|
+
const [nodeMajorRaw, nodeMinorRaw] = String(nodeVersion).split('.')
|
|
94
|
+
const nodeMajor = Number.parseInt(nodeMajorRaw || '0', 10)
|
|
95
|
+
const nodeMinor = Number.parseInt(nodeMinorRaw || '0', 10)
|
|
96
|
+
if (nodeMajor > 22 || (nodeMajor === 22 && nodeMinor >= 6)) {
|
|
95
97
|
pushCheck(checks, 'node-version', 'Node.js version', 'pass', `Detected Node ${nodeVersion}.`, true)
|
|
96
98
|
} else {
|
|
97
|
-
pushCheck(checks, 'node-version', 'Node.js version', 'fail', `Detected Node ${nodeVersion}. Node
|
|
98
|
-
actions.push('Install Node.js
|
|
99
|
+
pushCheck(checks, 'node-version', 'Node.js version', 'fail', `Detected Node ${nodeVersion}. Node 22.6+ is required.`, true)
|
|
100
|
+
actions.push('Install Node.js 22.6 or newer from https://nodejs.org and rerun setup.')
|
|
99
101
|
}
|
|
100
102
|
|
|
101
103
|
const npmCheck = run('npm', ['--version'], 5_000)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { genId } from '@/lib/id'
|
|
2
2
|
import { NextResponse } from 'next/server'
|
|
3
|
-
import {
|
|
3
|
+
import { loadAgents, loadSettings, loadTasks, logActivity, upsertStoredItems, upsertTask } from '@/lib/server/storage'
|
|
4
4
|
import { notFound } from '@/lib/server/collection-helpers'
|
|
5
5
|
import { disableSessionHeartbeat, enqueueTask, recoverStalledRunningTasks, validateCompletedTasksQueue } from '@/lib/server/queue'
|
|
6
6
|
import { ensureTaskCompletionReport } from '@/lib/server/task-reports'
|
|
@@ -13,6 +13,8 @@ import { requestHeartbeatNow } from '@/lib/server/heartbeat-wake'
|
|
|
13
13
|
import { validateDag, cascadeUnblock } from '@/lib/server/dag-validation'
|
|
14
14
|
import { getPluginManager } from '@/lib/server/plugins'
|
|
15
15
|
import { normalizeTaskQualityGate } from '@/lib/server/task-quality-gate'
|
|
16
|
+
import type { BoardTask } from '@/types'
|
|
17
|
+
import '@/lib/server/builtin-plugins'
|
|
16
18
|
|
|
17
19
|
export async function GET(_req: Request, { params }: { params: Promise<{ id: string }> }) {
|
|
18
20
|
// Keep completed queue integrity even if daemon is not running.
|
|
@@ -90,7 +92,7 @@ export async function PUT(req: Request, { params }: { params: Promise<{ id: stri
|
|
|
90
92
|
}
|
|
91
93
|
}
|
|
92
94
|
|
|
93
|
-
|
|
95
|
+
upsertTask(id, tasks[id])
|
|
94
96
|
logActivity({ entityType: 'task', entityId: id, action: 'updated', actor: 'user', summary: `Task updated: "${tasks[id].title}" (${prevStatus} → ${tasks[id].status})` })
|
|
95
97
|
if (prevStatus !== tasks[id].status) {
|
|
96
98
|
pushMainLoopEventToMainSessions({
|
|
@@ -111,7 +113,12 @@ export async function PUT(req: Request, { params }: { params: Promise<{ id: stri
|
|
|
111
113
|
})
|
|
112
114
|
|
|
113
115
|
if (tasks[id].status === 'completed') {
|
|
114
|
-
|
|
116
|
+
const agentPlugins = tasks[id].agentId ? (loadAgents()[tasks[id].agentId]?.plugins || []) : []
|
|
117
|
+
getPluginManager().runHook(
|
|
118
|
+
'onTaskComplete',
|
|
119
|
+
{ taskId: id, result: tasks[id].result },
|
|
120
|
+
{ enabledIds: agentPlugins },
|
|
121
|
+
)
|
|
115
122
|
}
|
|
116
123
|
|
|
117
124
|
// Enqueue system event + heartbeat wake
|
|
@@ -131,7 +138,7 @@ export async function PUT(req: Request, { params }: { params: Promise<{ id: stri
|
|
|
131
138
|
// Revert status change and reject
|
|
132
139
|
tasks[id].status = prevStatus
|
|
133
140
|
tasks[id].updatedAt = Date.now()
|
|
134
|
-
|
|
141
|
+
upsertTask(id, tasks[id])
|
|
135
142
|
return NextResponse.json(
|
|
136
143
|
{ error: 'Cannot queue: blocked by incomplete tasks', blockedBy: incompleteBlocker },
|
|
137
144
|
{ status: 409 },
|
|
@@ -143,7 +150,10 @@ export async function PUT(req: Request, { params }: { params: Promise<{ id: stri
|
|
|
143
150
|
if (tasks[id].status === 'completed') {
|
|
144
151
|
const unblockedIds = cascadeUnblock(tasks, id)
|
|
145
152
|
if (unblockedIds.length > 0) {
|
|
146
|
-
|
|
153
|
+
upsertStoredItems('tasks', [
|
|
154
|
+
[id, tasks[id]],
|
|
155
|
+
...unblockedIds.map((uid) => [uid, tasks[uid]] as [string, BoardTask]),
|
|
156
|
+
])
|
|
147
157
|
for (const uid of unblockedIds) {
|
|
148
158
|
enqueueTask(uid)
|
|
149
159
|
}
|
|
@@ -168,7 +178,7 @@ export async function DELETE(_req: Request, { params }: { params: Promise<{ id:
|
|
|
168
178
|
tasks[id].status = 'archived'
|
|
169
179
|
tasks[id].archivedAt = Date.now()
|
|
170
180
|
tasks[id].updatedAt = Date.now()
|
|
171
|
-
|
|
181
|
+
upsertTask(id, tasks[id])
|
|
172
182
|
logActivity({ entityType: 'task', entityId: id, action: 'deleted', actor: 'user', summary: `Task archived: "${tasks[id].title}"` })
|
|
173
183
|
pushMainLoopEventToMainSessions({
|
|
174
184
|
type: 'task_archived',
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { NextResponse } from 'next/server'
|
|
2
|
-
import { loadTasks,
|
|
2
|
+
import { loadTasks, logActivity, upsertStoredItems } from '@/lib/server/storage'
|
|
3
3
|
import { enqueueTask, disableSessionHeartbeat } from '@/lib/server/queue'
|
|
4
4
|
import { pushMainLoopEventToMainSessions } from '@/lib/server/main-agent-loop'
|
|
5
5
|
import { notify } from '@/lib/server/ws-hub'
|
|
6
6
|
import { createNotification } from '@/lib/server/create-notification'
|
|
7
|
-
import type { BoardTaskStatus } from '@/types'
|
|
7
|
+
import type { BoardTask, BoardTaskStatus } from '@/types'
|
|
8
8
|
|
|
9
9
|
const VALID_STATUSES: BoardTaskStatus[] = ['backlog', 'queued', 'running', 'completed', 'failed', 'archived']
|
|
10
10
|
|
|
@@ -82,7 +82,7 @@ export async function POST(req: Request) {
|
|
|
82
82
|
}
|
|
83
83
|
}
|
|
84
84
|
|
|
85
|
-
|
|
85
|
+
upsertStoredItems('tasks', results.map((id) => [id, tasks[id]] as [string, BoardTask]))
|
|
86
86
|
|
|
87
87
|
if (updated > 0) {
|
|
88
88
|
const action = body.status
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { NextResponse } from 'next/server'
|
|
2
2
|
import { genId } from '@/lib/id'
|
|
3
|
-
import {
|
|
3
|
+
import { deleteTask, loadAgents, loadSettings, loadTasks, logActivity, upsertTask } from '@/lib/server/storage'
|
|
4
4
|
import { TaskCreateSchema, formatZodError } from '@/lib/validation/schemas'
|
|
5
5
|
import { z } from 'zod'
|
|
6
6
|
import { enqueueTask, recoverStalledRunningTasks, validateCompletedTasksQueue } from '@/lib/server/queue'
|
|
@@ -13,6 +13,7 @@ import { resolveTaskAgentFromDescription } from '@/lib/server/task-mention'
|
|
|
13
13
|
import { validateDag } from '@/lib/server/dag-validation'
|
|
14
14
|
import { getPluginManager } from '@/lib/server/plugins'
|
|
15
15
|
import { normalizeTaskQualityGate } from '@/lib/server/task-quality-gate'
|
|
16
|
+
import '@/lib/server/builtin-plugins'
|
|
16
17
|
|
|
17
18
|
export async function GET(req: Request) {
|
|
18
19
|
// Keep completed queue integrity even if daemon is not running.
|
|
@@ -49,7 +50,6 @@ export async function DELETE(req: Request) {
|
|
|
49
50
|
(filter === 'done' && (task.status === 'completed' || task.status === 'failed')) ||
|
|
50
51
|
(!filter && task.status === 'archived')
|
|
51
52
|
|
|
52
|
-
const { deleteTask } = await import('@/lib/server/storage')
|
|
53
53
|
for (const [id, task] of Object.entries(tasks)) {
|
|
54
54
|
if (shouldRemove(task as { status: string; sourceType?: string })) {
|
|
55
55
|
deleteTask(id)
|
|
@@ -169,7 +169,12 @@ export async function POST(req: Request) {
|
|
|
169
169
|
if (validation.ok) {
|
|
170
170
|
tasks[id].completedAt = Date.now()
|
|
171
171
|
tasks[id].error = null
|
|
172
|
-
|
|
172
|
+
const agentPlugins = resolvedAgentId ? (loadAgents()[resolvedAgentId]?.plugins || []) : []
|
|
173
|
+
getPluginManager().runHook(
|
|
174
|
+
'onTaskComplete',
|
|
175
|
+
{ taskId: id, result: tasks[id].result },
|
|
176
|
+
{ enabledIds: agentPlugins },
|
|
177
|
+
)
|
|
173
178
|
} else {
|
|
174
179
|
tasks[id].status = 'failed'
|
|
175
180
|
tasks[id].completedAt = null
|
|
@@ -177,7 +182,7 @@ export async function POST(req: Request) {
|
|
|
177
182
|
}
|
|
178
183
|
}
|
|
179
184
|
|
|
180
|
-
|
|
185
|
+
upsertTask(id, tasks[id])
|
|
181
186
|
logActivity({ entityType: 'task', entityId: id, action: 'created', actor: 'user', summary: `Task created: "${tasks[id].title}"` })
|
|
182
187
|
pushMainLoopEventToMainSessions({
|
|
183
188
|
type: 'task_created',
|
|
@@ -8,6 +8,7 @@ import { enqueueSystemEvent } from '@/lib/server/system-events'
|
|
|
8
8
|
import { requestHeartbeatNow } from '@/lib/server/heartbeat-wake'
|
|
9
9
|
import { mutateItem, deleteItem, notFound, type CollectionOps } from '@/lib/server/collection-helpers'
|
|
10
10
|
import type { WebhookRetryEntry } from '@/types'
|
|
11
|
+
import { triggerWebhookWatchJobs } from '@/lib/server/watch-jobs'
|
|
11
12
|
|
|
12
13
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
13
14
|
const ops: CollectionOps<any> = { load: loadWebhooks, save: saveWebhooks }
|
|
@@ -126,6 +127,12 @@ export async function POST(req: Request, { params }: { params: Promise<{ id: str
|
|
|
126
127
|
})
|
|
127
128
|
}
|
|
128
129
|
|
|
130
|
+
triggerWebhookWatchJobs({
|
|
131
|
+
webhookId: id,
|
|
132
|
+
event: incomingEvent,
|
|
133
|
+
payloadPreview: rawBody,
|
|
134
|
+
})
|
|
135
|
+
|
|
129
136
|
const agents = loadAgents()
|
|
130
137
|
const agent = webhook.agentId ? agents[webhook.agentId] : null
|
|
131
138
|
if (!agent) {
|
|
@@ -165,7 +172,7 @@ export async function POST(req: Request, { params }: { params: Promise<{ id: str
|
|
|
165
172
|
messages: [],
|
|
166
173
|
createdAt: now,
|
|
167
174
|
lastActiveAt: now,
|
|
168
|
-
sessionType: '
|
|
175
|
+
sessionType: 'human',
|
|
169
176
|
agentId: agent.id,
|
|
170
177
|
parentSessionId: null,
|
|
171
178
|
tools: agent.tools || [],
|