@swarmclawai/swarmclaw 1.0.9 → 1.1.1
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 +16 -1
- package/package.json +1 -1
- package/src/app/api/activity/route.ts +1 -1
- package/src/app/api/autonomy/estop/route.ts +92 -0
- package/src/app/api/autonomy/guardian/restore/route.ts +18 -0
- package/src/app/api/chatrooms/route.ts +6 -1
- package/src/app/api/chats/[id]/queue/route.ts +84 -0
- package/src/app/api/chats/[id]/route.ts +6 -4
- package/src/app/api/chats/route.ts +5 -3
- package/src/app/api/clawhub/install/route.ts +22 -7
- package/src/app/api/missions/[id]/actions/route.ts +31 -0
- package/src/app/api/missions/[id]/events/route.ts +14 -0
- package/src/app/api/missions/[id]/route.ts +10 -0
- package/src/app/api/missions/route.test.ts +244 -0
- package/src/app/api/missions/route.ts +57 -0
- package/src/app/api/openclaw/skills/install/route.ts +12 -1
- package/src/app/api/projects/[id]/route.ts +4 -4
- package/src/app/api/protocols/runs/[id]/actions/route.ts +26 -0
- package/src/app/api/protocols/runs/[id]/events/route.ts +16 -0
- package/src/app/api/protocols/runs/[id]/route.ts +10 -0
- package/src/app/api/protocols/runs/route.test.ts +173 -0
- package/src/app/api/protocols/runs/route.ts +51 -0
- package/src/app/api/protocols/templates/[id]/route.ts +50 -0
- package/src/app/api/protocols/templates/route.test.ts +109 -0
- package/src/app/api/protocols/templates/route.ts +30 -0
- package/src/app/api/runs/[id]/events/route.ts +22 -0
- package/src/app/api/runs/route.ts +4 -1
- package/src/app/api/schedules/[id]/route.ts +1 -1
- package/src/app/api/search/route.ts +4 -4
- package/src/app/api/settings/route.ts +10 -1
- package/src/app/api/skills/import/route.ts +14 -0
- package/src/app/api/tasks/[id]/route.ts +14 -3
- package/src/app/api/tasks/claim/route.test.ts +58 -0
- package/src/app/api/tasks/claim/route.ts +18 -0
- package/src/app/api/tasks/route.ts +13 -4
- package/src/app/api/webhooks/[id]/helpers.ts +9 -2
- package/src/app/api/webhooks/route.test.ts +39 -0
- package/src/app/autonomy/page.tsx +822 -0
- package/src/app/chatrooms/[id]/page.tsx +49 -0
- package/src/app/globals.css +22 -1
- package/src/app/memory/page.tsx +24 -2
- package/src/app/missions/[id]/page.tsx +3 -0
- package/src/app/missions/page.tsx +684 -0
- package/src/app/protocols/page.tsx +1117 -0
- package/src/cli/index.js +34 -0
- package/src/cli/spec.js +23 -0
- package/src/components/agents/agent-chat-list.tsx +2 -0
- package/src/components/chat/chat-area.tsx +29 -4
- package/src/components/chat/chat-card.tsx +55 -2
- package/src/components/chat/chat-header.tsx +66 -1
- package/src/components/chat/message-bubble.test.ts +131 -0
- package/src/components/chat/message-bubble.tsx +394 -190
- package/src/components/chat/message-list.tsx +51 -42
- package/src/components/chatrooms/breakout-command.test.ts +86 -0
- package/src/components/chatrooms/breakout-command.ts +119 -0
- package/src/components/chatrooms/chatroom-input.tsx +304 -90
- package/src/components/chatrooms/chatroom-message.tsx +3 -3
- package/src/components/chatrooms/chatroom-view.tsx +247 -25
- package/src/components/input/chat-input.tsx +226 -94
- package/src/components/input/composer-shell.tsx +44 -0
- package/src/components/layout/dashboard-shell.tsx +1 -10
- package/src/components/layout/sidebar-rail.tsx +65 -65
- package/src/components/projects/project-detail.tsx +103 -2
- package/src/components/protocols/structured-session-launcher.tsx +490 -0
- package/src/components/runs/run-list.tsx +59 -3
- package/src/components/schedules/schedule-sheet.tsx +57 -8
- package/src/components/shared/command-palette.tsx +1 -0
- package/src/components/tasks/task-card.tsx +38 -1
- package/src/components/tasks/task-sheet.tsx +115 -0
- package/src/lib/app/navigation.ts +8 -0
- package/src/lib/app/view-constants.ts +26 -2
- package/src/lib/chat/chat-streaming-state.test.ts +74 -0
- package/src/lib/chat/chat-streaming-state.ts +57 -0
- package/src/lib/chat/chats.ts +21 -1
- package/src/lib/chat/queued-message-queue.test.ts +86 -11
- package/src/lib/chat/queued-message-queue.ts +49 -22
- package/src/lib/runtime/heartbeat-defaults.ts +5 -1
- package/src/lib/runtime/runtime-loop.ts +4 -2
- package/src/lib/server/agents/agent-cascade.ts +13 -12
- package/src/lib/server/agents/delegation-jobs.test.ts +70 -0
- package/src/lib/server/agents/delegation-jobs.ts +28 -3
- package/src/lib/server/agents/guardian.ts +202 -24
- package/src/lib/server/agents/main-agent-loop.test.ts +86 -0
- package/src/lib/server/agents/main-agent-loop.ts +48 -1
- package/src/lib/server/agents/subagent-runtime.ts +2 -1
- package/src/lib/server/approvals.ts +6 -0
- package/src/lib/server/autonomy/supervisor-reflection.test.ts +113 -0
- package/src/lib/server/autonomy/supervisor-reflection.ts +152 -16
- package/src/lib/server/chat-execution/chat-execution-advanced.test.ts +68 -6
- package/src/lib/server/chat-execution/chat-execution-utils.ts +11 -0
- package/src/lib/server/chat-execution/chat-execution.ts +379 -153
- package/src/lib/server/chat-execution/chat-streaming-utils.ts +13 -3
- package/src/lib/server/chat-execution/chat-turn-tool-routing.test.ts +474 -0
- package/src/lib/server/chat-execution/chat-turn-tool-routing.ts +371 -19
- package/src/lib/server/chat-execution/direct-memory-intent.test.ts +100 -0
- package/src/lib/server/chat-execution/direct-memory-intent.ts +233 -0
- package/src/lib/server/chat-execution/exact-output-contract.test.ts +91 -0
- package/src/lib/server/chat-execution/exact-output-contract.ts +220 -0
- package/src/lib/server/chat-execution/memory-mutation-tools.ts +81 -0
- package/src/lib/server/chat-execution/situational-awareness.test.ts +318 -0
- package/src/lib/server/chat-execution/situational-awareness.ts +221 -0
- package/src/lib/server/chat-execution/stream-agent-chat.test.ts +243 -10
- package/src/lib/server/chat-execution/stream-agent-chat.ts +159 -13
- package/src/lib/server/chat-execution/stream-continuation.ts +65 -2
- package/src/lib/server/chatrooms/chatroom-helpers.ts +1 -1
- package/src/lib/server/chatrooms/session-mailbox.ts +10 -0
- package/src/lib/server/connectors/connector-routing.test.ts +59 -1
- package/src/lib/server/connectors/manager.test.ts +195 -0
- package/src/lib/server/connectors/manager.ts +280 -6
- package/src/lib/server/connectors/response-media.ts +3 -0
- package/src/lib/server/connectors/session-consolidation.ts +4 -4
- package/src/lib/server/context-manager.test.ts +19 -0
- package/src/lib/server/context-manager.ts +10 -3
- package/src/lib/server/dag-validation.ts +10 -0
- package/src/lib/server/elevenlabs.ts +1 -1
- package/src/lib/server/eval/agent-regression.ts +9 -9
- package/src/lib/server/integrity-monitor.ts +4 -1
- package/src/lib/server/memory/memory-consolidation.test.ts +89 -0
- package/src/lib/server/memory/memory-consolidation.ts +21 -0
- package/src/lib/server/memory/memory-db.ts +46 -8
- package/src/lib/server/memory/memory-graph.ts +5 -1
- package/src/lib/server/memory/temporal-decay.ts +7 -1
- package/src/lib/server/missions/mission-intent.test.ts +63 -0
- package/src/lib/server/missions/mission-intent.ts +569 -0
- package/src/lib/server/missions/mission-service.test.ts +881 -0
- package/src/lib/server/missions/mission-service.ts +2254 -0
- package/src/lib/server/openclaw/gateway.test.ts +157 -1
- package/src/lib/server/openclaw/gateway.ts +55 -34
- package/src/lib/server/protocols/protocol-service.test.ts +585 -0
- package/src/lib/server/protocols/protocol-service.ts +2765 -0
- package/src/lib/server/provider-health.test.ts +38 -0
- package/src/lib/server/provider-health.ts +33 -4
- package/src/lib/server/query-expansion.ts +2 -1
- package/src/lib/server/resolve-image.ts +41 -9
- package/src/lib/server/runtime/daemon-guards.test.ts +74 -0
- package/src/lib/server/runtime/daemon-state.ts +168 -19
- package/src/lib/server/runtime/estop.test.ts +98 -0
- package/src/lib/server/runtime/estop.ts +199 -0
- package/src/lib/server/runtime/process-manager.ts +45 -2
- package/src/lib/server/runtime/queue.test.ts +25 -0
- package/src/lib/server/runtime/queue.ts +114 -14
- package/src/lib/server/runtime/run-ledger.ts +108 -0
- package/src/lib/server/runtime/scheduler.test.ts +161 -0
- package/src/lib/server/runtime/scheduler.ts +35 -35
- package/src/lib/server/runtime/session-run-manager.test.ts +178 -0
- package/src/lib/server/runtime/session-run-manager.ts +439 -52
- package/src/lib/server/schedules/schedule-normalization.ts +23 -3
- package/src/lib/server/schedules/schedule-service.ts +1 -1
- package/src/lib/server/session-tools/context-mgmt.ts +3 -1
- package/src/lib/server/session-tools/crud.ts +41 -6
- package/src/lib/server/session-tools/delegate.ts +3 -43
- package/src/lib/server/session-tools/file-normalize.test.ts +18 -0
- package/src/lib/server/session-tools/file.ts +6 -0
- package/src/lib/server/session-tools/memory.ts +23 -0
- package/src/lib/server/skills/skill-audit.test.ts +33 -0
- package/src/lib/server/skills/skill-audit.ts +145 -0
- package/src/lib/server/storage.ts +325 -22
- package/src/lib/server/tasks/task-lifecycle.ts +2 -1
- package/src/lib/server/tasks/task-service.ts +10 -3
- package/src/lib/server/tasks/task-validation.ts +2 -2
- package/src/lib/server/tool-capability-policy-advanced.test.ts +6 -0
- package/src/lib/server/tool-capability-policy.ts +18 -10
- package/src/lib/server/tool-loop-detection.ts +9 -9
- package/src/lib/server/untrusted-content.ts +113 -0
- package/src/lib/validation/schemas.ts +174 -0
- package/src/stores/use-chat-store.test.ts +346 -22
- package/src/stores/use-chat-store.ts +213 -55
- package/src/stores/use-chatroom-store.ts +19 -1
- package/src/types/index.ts +620 -3
- package/src/views/settings/section-runtime-loop.tsx +28 -0
- package/src/components/chat/streaming-bubble.tsx +0 -97
- package/src/components/layout/mobile-drawer.tsx +0 -227
package/README.md
CHANGED
|
@@ -17,6 +17,19 @@ Extension tutorial: https://swarmclaw.ai/docs/extension-tutorial
|
|
|
17
17
|
|
|
18
18
|
## Release Notes
|
|
19
19
|
|
|
20
|
+
### v1.1.1 Highlights
|
|
21
|
+
|
|
22
|
+
- **Structured Sessions are now contextual**: start bounded structured runs from direct chats, chatrooms, tasks, missions, or schedules, including a new chatroom `/breakout` command that spins up a focused session from the current room with auto-filled participants and kickoff context.
|
|
23
|
+
- **ProtocolRun orchestration matured**: structured sessions now run on the same durable engine for step-based branching, repeat loops, parallel branches, and explicit joins instead of growing a separate orchestration subsystem.
|
|
24
|
+
- **Live-agent runtime hardening**: exact-output contracts, memory preflight behavior, same-channel delivery rendering, inline media, and grounded runtime inspection were all tightened through live-agent validation before release.
|
|
25
|
+
|
|
26
|
+
### v1.1.0 Highlights
|
|
27
|
+
|
|
28
|
+
- **Mission controller and Missions UI**: SwarmClaw now tracks durable multi-step objectives as missions with status, phase, linked tasks, queued turns, recent runs, event history, and operator actions from the new **Missions** surface.
|
|
29
|
+
- **Autonomy safety desk and run replay**: the new **Autonomy Control** page adds estop visibility, resume policy controls, incident review, and run replay backed by durable run history rather than transient in-memory state.
|
|
30
|
+
- **Durable queued follow-ups**: direct chat and connector follow-up turns now use a backend queue so queued work survives reloads, drains in order, and stays attached to the right mission/session context.
|
|
31
|
+
- **Chat execution and UX hardening**: streamed handoff, memory writes, inline media, queue state, and tool-policy fallback behavior were cleaned up so agents are less noisy, less brittle, and easier to follow in real chats.
|
|
32
|
+
|
|
20
33
|
### v1.0.9 Highlights
|
|
21
34
|
|
|
22
35
|
- **Quieter chat and inbox replies**: chat-origin and connector turns now suppress more hidden control text, stop replaying connector-tool output as normal assistant prose, and avoid extra empty follow-up chatter after successful tool work.
|
|
@@ -27,6 +40,7 @@ Extension tutorial: https://swarmclaw.ai/docs/extension-tutorial
|
|
|
27
40
|
## What SwarmClaw Focuses On
|
|
28
41
|
|
|
29
42
|
- **Delegation and background execution**: delegated work, subagents, durable jobs, checkpointing, and background task execution.
|
|
43
|
+
- **Structured Sessions**: temporary bounded runs for one agent or many, launched from context and backed by durable templates, transcripts, outputs, operator controls, and chatroom breakout flows.
|
|
30
44
|
- **Autonomy and memory**: heartbeats, schedules, long-running execution, durable memory, reflection memory, human-context learning, document recall, and project-aware context.
|
|
31
45
|
- **OpenClaw integration**: named gateway profiles, external runtimes, deploy helpers, config sync, approval handling, and OpenClaw agent file editing.
|
|
32
46
|
- **Runtime skills**: pinned skills, OpenClaw-compatible `SKILL.md` import, on-demand skill execution, and configurable keyword or embedding-based recommendation.
|
|
@@ -108,7 +122,8 @@ Then open `http://localhost:3456`.
|
|
|
108
122
|
|
|
109
123
|
- **Providers**: OpenClaw, OpenAI, Anthropic, Ollama, Google, DeepSeek, Groq, Together, Mistral, xAI, Fireworks, plus compatible custom endpoints.
|
|
110
124
|
- **Delegation**: built-in delegation to Claude Code, Codex CLI, OpenCode CLI, Gemini CLI, and native SwarmClaw subagents.
|
|
111
|
-
- **Autonomy**: heartbeat loops, schedules, background jobs, task execution, supervisor recovery, and agent wakeups.
|
|
125
|
+
- **Autonomy**: heartbeat loops, schedules, background jobs, task execution, supervisor recovery, mission control, and agent wakeups.
|
|
126
|
+
- **Structured Sessions**: contextual launch from chats, chatrooms, tasks, missions, and schedules, plus reusable templates, chatroom `/breakout`, and durable run state.
|
|
112
127
|
- **Memory**: hybrid recall, graph traversal, journaling, durable documents, project-scoped context, automatic reflection memory, communication preferences, profile and boundary memory, significant events, and open follow-up loops.
|
|
113
128
|
- **Wallets**: balances, transfers, signatures, EVM call/quote/swap flows, and approval-gated execution.
|
|
114
129
|
- **Connectors**: Discord, Slack, Telegram, WhatsApp, Teams, Matrix, OpenClaw, and more.
|
package/package.json
CHANGED
|
@@ -11,7 +11,7 @@ export async function GET(req: Request) {
|
|
|
11
11
|
const limit = Math.min(200, Math.max(1, Number(searchParams.get('limit')) || 50))
|
|
12
12
|
|
|
13
13
|
const all = loadActivity()
|
|
14
|
-
let entries = Object.values(all) as Array<Record<string, unknown>>
|
|
14
|
+
let entries = Object.values(all) as unknown as Array<Record<string, unknown>>
|
|
15
15
|
|
|
16
16
|
if (entityType) entries = entries.filter((e) => e.entityType === entityType)
|
|
17
17
|
if (entityId) entries = entries.filter((e) => e.entityId === entityId)
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { NextResponse } from 'next/server'
|
|
2
|
+
import { cancelAllRuns } from '@/lib/server/runtime/session-run-manager'
|
|
3
|
+
import { startDaemon, stopDaemon } from '@/lib/server/runtime/daemon-state'
|
|
4
|
+
import {
|
|
5
|
+
areEstopResumeApprovalsEnabled,
|
|
6
|
+
engageEstop,
|
|
7
|
+
findEstopResumeApproval,
|
|
8
|
+
loadEstopState,
|
|
9
|
+
requestEstopResumeApproval,
|
|
10
|
+
resumeEstop,
|
|
11
|
+
} from '@/lib/server/runtime/estop'
|
|
12
|
+
|
|
13
|
+
export const dynamic = 'force-dynamic'
|
|
14
|
+
|
|
15
|
+
function buildStateResponse(state = loadEstopState()) {
|
|
16
|
+
const approval = state.resumeApprovalId ? findEstopResumeApproval(state.resumeApprovalId) : null
|
|
17
|
+
return {
|
|
18
|
+
...state,
|
|
19
|
+
resumeRequiresApproval: areEstopResumeApprovalsEnabled(),
|
|
20
|
+
approval,
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export async function GET() {
|
|
25
|
+
return NextResponse.json(buildStateResponse())
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export async function POST(req: Request) {
|
|
29
|
+
try {
|
|
30
|
+
const body = await req.json().catch(() => ({})) as Record<string, unknown>
|
|
31
|
+
const action = typeof body.action === 'string' ? body.action.trim().toLowerCase() : 'status'
|
|
32
|
+
|
|
33
|
+
if (action === 'engage') {
|
|
34
|
+
const level = body.level === 'autonomy' ? 'autonomy' : 'all'
|
|
35
|
+
const state = engageEstop({
|
|
36
|
+
level,
|
|
37
|
+
reason: typeof body.reason === 'string' ? body.reason : null,
|
|
38
|
+
engagedBy: typeof body.engagedBy === 'string' ? body.engagedBy : 'user',
|
|
39
|
+
})
|
|
40
|
+
stopDaemon({ source: `api/autonomy/estop:${level}` })
|
|
41
|
+
const cancelled = level === 'all'
|
|
42
|
+
? cancelAllRuns('Cancelled because all estop is engaged.')
|
|
43
|
+
: { cancelledQueued: 0, abortedRunning: 0 }
|
|
44
|
+
return NextResponse.json({ ok: true, state: buildStateResponse(state), cancelled })
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if (action === 'resume') {
|
|
48
|
+
const approvalId = typeof body.approvalId === 'string' ? body.approvalId : null
|
|
49
|
+
const requiresApproval = areEstopResumeApprovalsEnabled()
|
|
50
|
+
const state = loadEstopState()
|
|
51
|
+
|
|
52
|
+
if (state.level === 'none') {
|
|
53
|
+
return NextResponse.json({ ok: true, state: buildStateResponse(state) })
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (!requiresApproval) {
|
|
57
|
+
const resumed = resumeEstop({ bypassApproval: true })
|
|
58
|
+
startDaemon({ source: 'api/autonomy/estop:resume', manualStart: true })
|
|
59
|
+
return NextResponse.json({ ok: true, state: buildStateResponse(resumed) })
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (!approvalId) {
|
|
63
|
+
const existingApproval = state.resumeApprovalId ? findEstopResumeApproval(state.resumeApprovalId) : null
|
|
64
|
+
if (existingApproval?.status === 'approved') {
|
|
65
|
+
const resumed = resumeEstop({ approvalId: existingApproval.id })
|
|
66
|
+
startDaemon({ source: 'api/autonomy/estop:resume', manualStart: true })
|
|
67
|
+
return NextResponse.json({ ok: true, state: buildStateResponse(resumed) })
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const result = requestEstopResumeApproval({
|
|
71
|
+
requester: typeof body.requester === 'string' ? body.requester : 'user',
|
|
72
|
+
})
|
|
73
|
+
return NextResponse.json({
|
|
74
|
+
ok: false,
|
|
75
|
+
requiresApproval: true,
|
|
76
|
+
state: buildStateResponse(result.state),
|
|
77
|
+
approval: result.approval,
|
|
78
|
+
}, { status: 202 })
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const resumed = resumeEstop({ approvalId })
|
|
82
|
+
startDaemon({ source: 'api/autonomy/estop:resume', manualStart: true })
|
|
83
|
+
return NextResponse.json({ ok: true, state: buildStateResponse(resumed) })
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return NextResponse.json(buildStateResponse())
|
|
87
|
+
} catch (err: unknown) {
|
|
88
|
+
return NextResponse.json({
|
|
89
|
+
error: err instanceof Error ? err.message : 'Failed to update estop state',
|
|
90
|
+
}, { status: 400 })
|
|
91
|
+
}
|
|
92
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { NextResponse } from 'next/server'
|
|
2
|
+
import { restoreGuardianCheckpoint } from '@/lib/server/agents/guardian'
|
|
3
|
+
|
|
4
|
+
export const dynamic = 'force-dynamic'
|
|
5
|
+
|
|
6
|
+
export async function POST(req: Request) {
|
|
7
|
+
const body = await req.json().catch(() => ({})) as Record<string, unknown>
|
|
8
|
+
const approvalId = typeof body.approvalId === 'string' ? body.approvalId.trim() : ''
|
|
9
|
+
if (!approvalId) {
|
|
10
|
+
return NextResponse.json({ error: 'approvalId is required' }, { status: 400 })
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const result = restoreGuardianCheckpoint(approvalId)
|
|
14
|
+
if (!result.ok) {
|
|
15
|
+
return NextResponse.json({ error: result.reason || 'Restore failed' }, { status: 400 })
|
|
16
|
+
}
|
|
17
|
+
return NextResponse.json({ ok: true, checkpoint: result.checkpoint })
|
|
18
|
+
}
|
|
@@ -10,7 +10,12 @@ export const dynamic = 'force-dynamic'
|
|
|
10
10
|
|
|
11
11
|
export async function GET() {
|
|
12
12
|
const chatrooms = loadChatrooms()
|
|
13
|
-
|
|
13
|
+
const filtered: typeof chatrooms = {}
|
|
14
|
+
for (const [id, chatroom] of Object.entries(chatrooms)) {
|
|
15
|
+
if (chatroom.hidden === true || chatroom.archivedAt) continue
|
|
16
|
+
filtered[id] = chatroom
|
|
17
|
+
}
|
|
18
|
+
return NextResponse.json(filtered)
|
|
14
19
|
}
|
|
15
20
|
|
|
16
21
|
export async function POST(req: Request) {
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { NextResponse } from 'next/server'
|
|
2
|
+
import { notFound } from '@/lib/server/collection-helpers'
|
|
3
|
+
import { loadSession } from '@/lib/server/storage'
|
|
4
|
+
import {
|
|
5
|
+
cancelQueuedRunById,
|
|
6
|
+
cancelQueuedRunsForSession,
|
|
7
|
+
enqueueSessionRun,
|
|
8
|
+
getSessionQueueSnapshot,
|
|
9
|
+
} from '@/lib/server/runtime/session-run-manager'
|
|
10
|
+
|
|
11
|
+
export const dynamic = 'force-dynamic'
|
|
12
|
+
|
|
13
|
+
export async function GET(_req: Request, { params }: { params: Promise<{ id: string }> }) {
|
|
14
|
+
const { id } = await params
|
|
15
|
+
const session = loadSession(id)
|
|
16
|
+
if (!session) return notFound()
|
|
17
|
+
return NextResponse.json(getSessionQueueSnapshot(id))
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export async function POST(req: Request, { params }: { params: Promise<{ id: string }> }) {
|
|
21
|
+
const { id } = await params
|
|
22
|
+
const session = loadSession(id)
|
|
23
|
+
if (!session) return notFound()
|
|
24
|
+
|
|
25
|
+
const body = await req.json().catch(() => ({}))
|
|
26
|
+
const message = typeof body.message === 'string' ? body.message : ''
|
|
27
|
+
const imagePath = typeof body.imagePath === 'string' ? body.imagePath : undefined
|
|
28
|
+
const imageUrl = typeof body.imageUrl === 'string' ? body.imageUrl : undefined
|
|
29
|
+
const attachedFiles = Array.isArray(body.attachedFiles)
|
|
30
|
+
? body.attachedFiles.filter((file: unknown): file is string => typeof file === 'string' && file.trim().length > 0)
|
|
31
|
+
: undefined
|
|
32
|
+
const replyToId = typeof body.replyToId === 'string' ? body.replyToId : undefined
|
|
33
|
+
const hasFiles = !!(imagePath || imageUrl || attachedFiles?.length)
|
|
34
|
+
|
|
35
|
+
if (!message.trim() && !hasFiles) {
|
|
36
|
+
return NextResponse.json({ error: 'message or file is required' }, { status: 400 })
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const queued = enqueueSessionRun({
|
|
40
|
+
sessionId: id,
|
|
41
|
+
missionId: session.missionId || null,
|
|
42
|
+
message,
|
|
43
|
+
imagePath,
|
|
44
|
+
imageUrl,
|
|
45
|
+
attachedFiles,
|
|
46
|
+
source: 'chat',
|
|
47
|
+
mode: 'followup',
|
|
48
|
+
replyToId,
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
return NextResponse.json({
|
|
52
|
+
queued: {
|
|
53
|
+
runId: queued.runId,
|
|
54
|
+
position: queued.position,
|
|
55
|
+
},
|
|
56
|
+
snapshot: getSessionQueueSnapshot(id),
|
|
57
|
+
}, { status: 202 })
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export async function DELETE(req: Request, { params }: { params: Promise<{ id: string }> }) {
|
|
61
|
+
const { id } = await params
|
|
62
|
+
const session = loadSession(id)
|
|
63
|
+
if (!session) return notFound()
|
|
64
|
+
|
|
65
|
+
const body = await req.json().catch(() => ({}))
|
|
66
|
+
const runId = typeof body.runId === 'string' ? body.runId.trim() : ''
|
|
67
|
+
if (runId) {
|
|
68
|
+
const snapshot = getSessionQueueSnapshot(id)
|
|
69
|
+
if (!snapshot.items.some((item) => item.runId === runId)) {
|
|
70
|
+
return NextResponse.json({ error: 'Queued run not found' }, { status: 404 })
|
|
71
|
+
}
|
|
72
|
+
cancelQueuedRunById(runId, 'Removed from queue')
|
|
73
|
+
return NextResponse.json({
|
|
74
|
+
cancelled: 1,
|
|
75
|
+
snapshot: getSessionQueueSnapshot(id),
|
|
76
|
+
})
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const cancelled = cancelQueuedRunsForSession(id, 'Cleared queued messages')
|
|
80
|
+
return NextResponse.json({
|
|
81
|
+
cancelled,
|
|
82
|
+
snapshot: getSessionQueueSnapshot(id),
|
|
83
|
+
})
|
|
84
|
+
}
|
|
@@ -4,8 +4,9 @@ import { notFound } from '@/lib/server/collection-helpers'
|
|
|
4
4
|
import { normalizeProviderEndpoint } from '@/lib/openclaw/openclaw-endpoint'
|
|
5
5
|
import { resolvePrimaryAgentRoute } from '@/lib/server/agents/agent-runtime-config'
|
|
6
6
|
import { clearMainLoopStateForSession } from '@/lib/server/agents/main-agent-loop'
|
|
7
|
-
import { getSessionRunState } from '@/lib/server/runtime/session-run-manager'
|
|
7
|
+
import { getSessionQueueSnapshot, getSessionRunState } from '@/lib/server/runtime/session-run-manager'
|
|
8
8
|
import { normalizeCapabilitySelection } from '@/lib/capability-selection'
|
|
9
|
+
import { enrichSessionWithMissionSummary } from '@/lib/server/missions/mission-service'
|
|
9
10
|
|
|
10
11
|
export async function GET(_req: Request, { params }: { params: Promise<{ id: string }> }) {
|
|
11
12
|
const { id } = await params
|
|
@@ -13,11 +14,12 @@ export async function GET(_req: Request, { params }: { params: Promise<{ id: str
|
|
|
13
14
|
if (!session) return notFound()
|
|
14
15
|
|
|
15
16
|
const run = getSessionRunState(id)
|
|
17
|
+
const queue = getSessionQueueSnapshot(id)
|
|
16
18
|
session.active = active.has(id) || !!run.runningRunId
|
|
17
|
-
session.queuedCount =
|
|
19
|
+
session.queuedCount = queue.queueLength
|
|
18
20
|
session.currentRunId = run.runningRunId || null
|
|
19
21
|
|
|
20
|
-
return NextResponse.json(session)
|
|
22
|
+
return NextResponse.json(enrichSessionWithMissionSummary(session))
|
|
21
23
|
}
|
|
22
24
|
|
|
23
25
|
export async function PUT(req: Request, { params }: { params: Promise<{ id: string }> }) {
|
|
@@ -133,7 +135,7 @@ export async function PUT(req: Request, { params }: { params: Promise<{ id: stri
|
|
|
133
135
|
if (!Array.isArray(session.messages)) session.messages = []
|
|
134
136
|
|
|
135
137
|
upsertSession(id, session)
|
|
136
|
-
return NextResponse.json(session)
|
|
138
|
+
return NextResponse.json(enrichSessionWithMissionSummary(session as never))
|
|
137
139
|
}
|
|
138
140
|
|
|
139
141
|
export async function DELETE(_req: Request, { params }: { params: Promise<{ id: string }> }) {
|
|
@@ -6,13 +6,14 @@ import { perf } from '@/lib/server/runtime/perf'
|
|
|
6
6
|
import { loadSessions, saveSessions, deleteSession, active, loadAgents, upsertStoredItem } from '@/lib/server/storage'
|
|
7
7
|
import { WORKSPACE_DIR } from '@/lib/server/data-dir'
|
|
8
8
|
import { notify } from '@/lib/server/ws-hub'
|
|
9
|
-
import { getSessionRunState } from '@/lib/server/runtime/session-run-manager'
|
|
9
|
+
import { getSessionQueueSnapshot, getSessionRunState } from '@/lib/server/runtime/session-run-manager'
|
|
10
10
|
import { normalizeProviderEndpoint } from '@/lib/openclaw/openclaw-endpoint'
|
|
11
11
|
import { applyResolvedRoute, resolvePrimaryAgentRoute } from '@/lib/server/agents/agent-runtime-config'
|
|
12
12
|
import { buildAgentDisabledMessage, isAgentDisabled } from '@/lib/server/agents/agent-availability'
|
|
13
13
|
import { materializeStreamingAssistantArtifacts } from '@/lib/chat/chat-streaming-state'
|
|
14
14
|
import { buildSessionListSummary } from '@/lib/chat/session-summary'
|
|
15
15
|
import { normalizeCapabilitySelection } from '@/lib/capability-selection'
|
|
16
|
+
import { enrichSessionWithMissionSummary } from '@/lib/server/missions/mission-service'
|
|
16
17
|
export const dynamic = 'force-dynamic'
|
|
17
18
|
|
|
18
19
|
async function ensureDaemonIfNeeded(source: string) {
|
|
@@ -33,8 +34,9 @@ export async function GET(req: Request) {
|
|
|
33
34
|
const changedSessionIds: string[] = []
|
|
34
35
|
for (const id of Object.keys(sessions)) {
|
|
35
36
|
const run = getSessionRunState(id)
|
|
37
|
+
const queue = getSessionQueueSnapshot(id)
|
|
36
38
|
sessions[id].active = active.has(id) || !!run.runningRunId
|
|
37
|
-
sessions[id].queuedCount =
|
|
39
|
+
sessions[id].queuedCount = queue.queueLength
|
|
38
40
|
sessions[id].currentRunId = run.runningRunId || null
|
|
39
41
|
if (!sessions[id].active && Array.isArray(sessions[id].messages)) {
|
|
40
42
|
if (materializeStreamingAssistantArtifacts(sessions[id].messages)) changedSessionIds.push(id)
|
|
@@ -49,7 +51,7 @@ export async function GET(req: Request) {
|
|
|
49
51
|
}
|
|
50
52
|
|
|
51
53
|
const summarized = Object.fromEntries(
|
|
52
|
-
Object.entries(sessions).map(([id, session]) => [id, buildSessionListSummary(session)]),
|
|
54
|
+
Object.entries(sessions).map(([id, session]) => [id, buildSessionListSummary(enrichSessionWithMissionSummary(session))]),
|
|
53
55
|
)
|
|
54
56
|
|
|
55
57
|
const { searchParams } = new URL(req.url)
|
|
@@ -6,6 +6,7 @@ import { loadSkills, saveSkills } from '@/lib/server/storage'
|
|
|
6
6
|
import type { ClawHubSkillBundle } from '@/lib/server/skills/clawhub-client'
|
|
7
7
|
import { fetchClawHubSkillBundle, fetchSkillContent } from '@/lib/server/skills/clawhub-client'
|
|
8
8
|
import { clearDiscoveredSkillsCache, resolveWorkspaceSkillsDir } from '@/lib/server/skills/skill-discovery'
|
|
9
|
+
import { auditSkillBundleFiles, auditSkillContent, mergeSkillAuditResults } from '@/lib/server/skills/skill-audit'
|
|
9
10
|
import { normalizeSkillPayload } from '@/lib/server/skills/skills-normalize'
|
|
10
11
|
|
|
11
12
|
function sanitizeSkillDirName(value: string): string {
|
|
@@ -33,11 +34,8 @@ function stripSharedTopLevelDir(paths: string[]): string[] {
|
|
|
33
34
|
: paths
|
|
34
35
|
}
|
|
35
36
|
|
|
36
|
-
async function materializeClawHubBundle(url: string): Promise<
|
|
37
|
-
|
|
38
|
-
if (!bundle) return null
|
|
39
|
-
await writeClawHubBundleToWorkspace(bundle)
|
|
40
|
-
return bundle.content
|
|
37
|
+
async function materializeClawHubBundle(url: string): Promise<ClawHubSkillBundle | null> {
|
|
38
|
+
return fetchClawHubSkillBundle(url)
|
|
41
39
|
}
|
|
42
40
|
|
|
43
41
|
async function writeClawHubBundleToWorkspace(bundle: ClawHubSkillBundle): Promise<void> {
|
|
@@ -73,10 +71,12 @@ export async function POST(req: Request) {
|
|
|
73
71
|
const body = await req.json()
|
|
74
72
|
const { name, description, url, author, tags } = body
|
|
75
73
|
let { content } = body
|
|
74
|
+
let bundle: ClawHubSkillBundle | null = null
|
|
76
75
|
|
|
77
76
|
if (!content) {
|
|
78
77
|
try {
|
|
79
|
-
|
|
78
|
+
bundle = await materializeClawHubBundle(url)
|
|
79
|
+
content = bundle?.content || await fetchSkillContent(url)
|
|
80
80
|
} catch (err: unknown) {
|
|
81
81
|
return NextResponse.json(
|
|
82
82
|
{ error: err instanceof Error ? err.message : 'Failed to fetch skill content' },
|
|
@@ -93,6 +93,21 @@ export async function POST(req: Request) {
|
|
|
93
93
|
author,
|
|
94
94
|
tags,
|
|
95
95
|
})
|
|
96
|
+
const audit = mergeSkillAuditResults(
|
|
97
|
+
bundle ? auditSkillBundleFiles(bundle.files) : { status: 'pass', findings: [] },
|
|
98
|
+
auditSkillContent({
|
|
99
|
+
content,
|
|
100
|
+
requirements: normalized.skillRequirements,
|
|
101
|
+
installOptions: normalized.installOptions,
|
|
102
|
+
primaryEnv: normalized.primaryEnv,
|
|
103
|
+
}),
|
|
104
|
+
)
|
|
105
|
+
if (audit.status === 'block') {
|
|
106
|
+
return NextResponse.json({ error: 'Skill blocked by static audit', audit }, { status: 400 })
|
|
107
|
+
}
|
|
108
|
+
if (bundle) {
|
|
109
|
+
await writeClawHubBundleToWorkspace(bundle)
|
|
110
|
+
}
|
|
96
111
|
|
|
97
112
|
const skills = loadSkills()
|
|
98
113
|
const duplicate = Object.values(skills).find((skill) => {
|
|
@@ -131,5 +146,5 @@ export async function POST(req: Request) {
|
|
|
131
146
|
}
|
|
132
147
|
saveSkills(skills)
|
|
133
148
|
clearDiscoveredSkillsCache()
|
|
134
|
-
return NextResponse.json(skills[id])
|
|
149
|
+
return NextResponse.json({ ...skills[id], audit })
|
|
135
150
|
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { NextResponse } from 'next/server'
|
|
2
|
+
import { notFound } from '@/lib/server/collection-helpers'
|
|
3
|
+
import { loadMissionById, performMissionAction } from '@/lib/server/missions/mission-service'
|
|
4
|
+
|
|
5
|
+
export async function POST(req: Request, { params }: { params: Promise<{ id: string }> }) {
|
|
6
|
+
const { id } = await params
|
|
7
|
+
const mission = loadMissionById(id)
|
|
8
|
+
if (!mission) return notFound()
|
|
9
|
+
|
|
10
|
+
const body = await req.json().catch(() => ({}))
|
|
11
|
+
const action = body?.action
|
|
12
|
+
if (action !== 'resume' && action !== 'replan' && action !== 'cancel' && action !== 'retry_verification' && action !== 'wait') {
|
|
13
|
+
return NextResponse.json({ error: 'Invalid mission action.' }, { status: 400 })
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const result = performMissionAction({
|
|
17
|
+
missionId: id,
|
|
18
|
+
action,
|
|
19
|
+
reason: typeof body.reason === 'string' ? body.reason : null,
|
|
20
|
+
waitKind: typeof body.waitKind === 'string' ? body.waitKind : undefined,
|
|
21
|
+
untilAt: typeof body.untilAt === 'number' ? body.untilAt : null,
|
|
22
|
+
})
|
|
23
|
+
if (!result) {
|
|
24
|
+
return NextResponse.json({ error: 'Unable to update mission.' }, { status: 409 })
|
|
25
|
+
}
|
|
26
|
+
return NextResponse.json({
|
|
27
|
+
ok: true,
|
|
28
|
+
mission: result.mission,
|
|
29
|
+
appendedEvent: result.event,
|
|
30
|
+
})
|
|
31
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { NextResponse } from 'next/server'
|
|
2
|
+
import { notFound } from '@/lib/server/collection-helpers'
|
|
3
|
+
import { listMissionEventsForMission, loadMissionById } from '@/lib/server/missions/mission-service'
|
|
4
|
+
|
|
5
|
+
export async function GET(req: Request, { params }: { params: Promise<{ id: string }> }) {
|
|
6
|
+
const { id } = await params
|
|
7
|
+
const mission = loadMissionById(id)
|
|
8
|
+
if (!mission) return notFound()
|
|
9
|
+
|
|
10
|
+
const { searchParams } = new URL(req.url)
|
|
11
|
+
const limitParam = searchParams.get('limit')
|
|
12
|
+
const limit = limitParam ? Number.parseInt(limitParam, 10) : undefined
|
|
13
|
+
return NextResponse.json(listMissionEventsForMission(id, Number.isFinite(limit) ? limit : undefined))
|
|
14
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { NextResponse } from 'next/server'
|
|
2
|
+
import { notFound } from '@/lib/server/collection-helpers'
|
|
3
|
+
import { getMissionDetail } from '@/lib/server/missions/mission-service'
|
|
4
|
+
|
|
5
|
+
export async function GET(_req: Request, { params }: { params: Promise<{ id: string }> }) {
|
|
6
|
+
const { id } = await params
|
|
7
|
+
const mission = getMissionDetail(id)
|
|
8
|
+
if (!mission) return notFound()
|
|
9
|
+
return NextResponse.json(mission)
|
|
10
|
+
}
|