@swarmclawai/swarmclaw 1.2.0 → 1.2.2
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 +19 -0
- package/package.json +5 -2
- package/skills/coding-agent/SKILL.md +111 -0
- package/skills/github/SKILL.md +140 -0
- package/skills/nano-banana-pro/SKILL.md +62 -0
- package/skills/nano-banana-pro/scripts/generate_image.py +235 -0
- package/skills/nano-pdf/SKILL.md +53 -0
- package/skills/openai-image-gen/SKILL.md +78 -0
- package/skills/openai-image-gen/scripts/gen.py +328 -0
- package/skills/resourceful-problem-solving/SKILL.md +49 -0
- package/skills/skill-creator/SKILL.md +147 -0
- package/skills/skill-creator/scripts/init_skill.py +378 -0
- package/skills/skill-creator/scripts/quick_validate.py +159 -0
- package/skills/summarize/SKILL.md +77 -0
- package/src/app/api/auth/route.ts +20 -5
- package/src/app/api/chats/[id]/deploy/route.ts +11 -6
- package/src/app/api/chats/[id]/devserver/route.ts +17 -20
- package/src/app/api/chats/[id]/messages/route.ts +15 -11
- package/src/app/api/chats/[id]/route.ts +9 -10
- package/src/app/api/chats/[id]/stop/route.ts +5 -7
- package/src/app/api/chats/messages-route.test.ts +8 -6
- package/src/app/api/chats/route.ts +9 -10
- package/src/app/api/credentials/[id]/route.ts +4 -1
- package/src/app/api/extensions/marketplace/route.ts +5 -2
- package/src/app/api/ip/route.ts +2 -2
- package/src/app/api/memory/maintenance/route.ts +5 -2
- package/src/app/api/preview-server/route.ts +15 -12
- package/src/app/api/projects/[id]/route.ts +7 -46
- package/src/app/api/system/status/route.ts +11 -0
- package/src/app/api/upload/route.ts +4 -1
- package/src/cli/index.js +7 -0
- package/src/cli/spec.js +1 -0
- package/src/components/agents/agent-files-editor.tsx +44 -32
- package/src/components/agents/personality-builder.tsx +13 -7
- package/src/components/agents/trash-list.tsx +1 -1
- package/src/components/chat/chat-area.tsx +45 -23
- package/src/components/chat/message-bubble.test.ts +35 -0
- package/src/components/chat/message-bubble.tsx +20 -9
- package/src/components/chat/message-list.tsx +62 -42
- package/src/components/chat/swarm-status-card.tsx +10 -3
- package/src/components/input/chat-input.tsx +34 -14
- package/src/components/layout/daemon-indicator.tsx +7 -8
- package/src/components/layout/update-banner.tsx +8 -13
- package/src/components/logs/log-list.tsx +1 -1
- package/src/components/memory/memory-card.tsx +3 -1
- package/src/components/org-chart/org-chart-view.tsx +4 -0
- package/src/components/projects/project-list.tsx +4 -2
- package/src/components/projects/tabs/overview-tab.tsx +3 -2
- package/src/components/secrets/secret-sheet.tsx +1 -1
- package/src/components/secrets/secrets-list.tsx +1 -1
- package/src/components/shared/agent-switch-dialog.tsx +12 -6
- package/src/components/shared/dir-browser.tsx +22 -18
- package/src/components/skills/skill-sheet.tsx +2 -3
- package/src/components/tasks/task-list.tsx +1 -1
- package/src/components/tasks/task-sheet.tsx +1 -1
- package/src/hooks/use-openclaw-gateway.ts +46 -27
- package/src/instrumentation.ts +10 -7
- package/src/lib/chat/assistant-render-id.ts +3 -0
- package/src/lib/chat/chat-streaming-state.test.ts +42 -3
- package/src/lib/chat/chat-streaming-state.ts +20 -8
- package/src/lib/chat/chat.ts +18 -2
- package/src/lib/chat/queued-message-queue.test.ts +23 -1
- package/src/lib/chat/queued-message-queue.ts +11 -2
- package/src/lib/providers/anthropic.ts +6 -3
- package/src/lib/providers/claude-cli.ts +9 -3
- package/src/lib/providers/cli-utils.test.ts +124 -0
- package/src/lib/providers/cli-utils.ts +15 -0
- package/src/lib/providers/codex-cli.ts +9 -3
- package/src/lib/providers/gemini-cli.ts +6 -2
- package/src/lib/providers/index.ts +4 -1
- package/src/lib/providers/ollama.ts +5 -2
- package/src/lib/providers/openai.ts +8 -5
- package/src/lib/providers/opencode-cli.ts +6 -2
- package/src/lib/server/activity/activity-log.ts +21 -0
- package/src/lib/server/agents/agent-availability.test.ts +10 -5
- package/src/lib/server/agents/agent-cascade.ts +79 -59
- package/src/lib/server/agents/agent-registry.ts +23 -4
- package/src/lib/server/agents/agent-repository.ts +90 -0
- package/src/lib/server/agents/delegation-job-repository.ts +53 -0
- package/src/lib/server/agents/delegation-jobs.ts +11 -4
- package/src/lib/server/agents/guardian-checkpoint-repository.ts +35 -0
- package/src/lib/server/agents/guardian.ts +2 -2
- package/src/lib/server/agents/main-agent-loop.ts +14 -6
- package/src/lib/server/agents/main-loop-state-repository.ts +38 -0
- package/src/lib/server/agents/subagent-runtime.ts +9 -6
- package/src/lib/server/agents/subagent-swarm.ts +3 -2
- package/src/lib/server/agents/task-session.ts +3 -4
- package/src/lib/server/approvals/approval-repository.ts +30 -0
- package/src/lib/server/autonomy/supervisor-incident-repository.ts +42 -0
- package/src/lib/server/autonomy/supervisor-reflection.ts +14 -1
- package/src/lib/server/chat-execution/chat-execution-types.ts +38 -0
- package/src/lib/server/chat-execution/chat-execution-utils.ts +1 -1
- package/src/lib/server/chat-execution/chat-execution.ts +84 -1914
- package/src/lib/server/chat-execution/chat-turn-finalization.ts +620 -0
- package/src/lib/server/chat-execution/chat-turn-partial-persistence.ts +221 -0
- package/src/lib/server/chat-execution/chat-turn-preflight.ts +133 -0
- package/src/lib/server/chat-execution/chat-turn-preparation.ts +817 -0
- package/src/lib/server/chat-execution/chat-turn-stream-execution.ts +296 -0
- package/src/lib/server/chat-execution/chat-turn-tool-routing.ts +5 -5
- package/src/lib/server/chat-execution/continuation-evaluator.ts +4 -3
- package/src/lib/server/chat-execution/continuation-limits.ts +6 -3
- package/src/lib/server/chat-execution/message-classifier.test.ts +329 -0
- package/src/lib/server/chat-execution/message-classifier.ts +5 -2
- package/src/lib/server/chat-execution/post-stream-finalization.ts +5 -2
- package/src/lib/server/chat-execution/prompt-builder.ts +22 -1
- package/src/lib/server/chat-execution/prompt-sections.ts +55 -13
- package/src/lib/server/chat-execution/response-completeness.ts +5 -2
- package/src/lib/server/chat-execution/situational-awareness.ts +12 -7
- package/src/lib/server/chat-execution/stream-agent-chat.ts +58 -25
- package/src/lib/server/chatrooms/chatroom-memory-bridge.ts +6 -3
- package/src/lib/server/chatrooms/chatroom-repository.ts +32 -0
- package/src/lib/server/connectors/bluebubbles.ts +7 -4
- package/src/lib/server/connectors/connector-inbound.ts +16 -13
- package/src/lib/server/connectors/connector-lifecycle.ts +11 -8
- package/src/lib/server/connectors/connector-outbound.ts +6 -3
- package/src/lib/server/connectors/connector-repository.ts +58 -0
- package/src/lib/server/connectors/discord.ts +10 -7
- package/src/lib/server/connectors/email.ts +17 -14
- package/src/lib/server/connectors/googlechat.ts +7 -4
- package/src/lib/server/connectors/inbound-audio-transcription.ts +5 -2
- package/src/lib/server/connectors/matrix.ts +6 -3
- package/src/lib/server/connectors/openclaw.ts +20 -17
- package/src/lib/server/connectors/outbox.ts +4 -1
- package/src/lib/server/connectors/runtime-state.test.ts +117 -0
- package/src/lib/server/connectors/runtime-state.ts +19 -0
- package/src/lib/server/connectors/session-consolidation.ts +5 -2
- package/src/lib/server/connectors/signal.ts +9 -6
- package/src/lib/server/connectors/slack.ts +13 -10
- package/src/lib/server/connectors/teams.ts +8 -5
- package/src/lib/server/connectors/telegram.ts +15 -12
- package/src/lib/server/connectors/whatsapp.ts +32 -29
- package/src/lib/server/credentials/credential-repository.ts +7 -0
- package/src/lib/server/embeddings.ts +4 -1
- package/src/lib/server/gateways/gateway-profile-repository.ts +4 -0
- package/src/lib/server/link-understanding.ts +4 -1
- package/src/lib/server/memory/memory-abstract.test.ts +59 -0
- package/src/lib/server/memory/memory-abstract.ts +59 -0
- package/src/lib/server/memory/memory-db.ts +40 -14
- package/src/lib/server/missions/mission-repository.ts +74 -0
- package/src/lib/server/missions/mission-service/actions.ts +6 -0
- package/src/lib/server/missions/mission-service/bindings.ts +9 -0
- package/src/lib/server/missions/mission-service/context.ts +4 -0
- package/src/lib/server/missions/mission-service/core.ts +2269 -0
- package/src/lib/server/missions/mission-service/queries.ts +12 -0
- package/src/lib/server/missions/mission-service/recovery.ts +5 -0
- package/src/lib/server/missions/mission-service/ticks.ts +9 -0
- package/src/lib/server/missions/mission-service.test.ts +9 -2
- package/src/lib/server/missions/mission-service.ts +6 -2263
- package/src/lib/server/openclaw/gateway.ts +8 -5
- package/src/lib/server/persistence/repository-utils.ts +154 -0
- package/src/lib/server/persistence/storage-context.ts +51 -0
- package/src/lib/server/persistence/transaction.ts +1 -0
- package/src/lib/server/project-utils.ts +13 -0
- package/src/lib/server/projects/project-repository.ts +36 -0
- package/src/lib/server/projects/project-service.ts +79 -0
- package/src/lib/server/protocols/protocol-agent-turn.ts +5 -2
- package/src/lib/server/protocols/protocol-normalization.test.ts +6 -4
- package/src/lib/server/protocols/protocol-run-lifecycle.ts +5 -2
- package/src/lib/server/protocols/protocol-step-helpers.ts +4 -1
- package/src/lib/server/provider-health.ts +18 -0
- package/src/lib/server/query-expansion.ts +4 -1
- package/src/lib/server/runtime/alert-dispatch.ts +8 -7
- package/src/lib/server/runtime/daemon-policy.ts +1 -1
- package/src/lib/server/runtime/daemon-state/core.ts +1570 -0
- package/src/lib/server/runtime/daemon-state/health.ts +6 -0
- package/src/lib/server/runtime/daemon-state/policy.ts +7 -0
- package/src/lib/server/runtime/daemon-state/supervisor.ts +6 -0
- package/src/lib/server/runtime/daemon-state.test.ts +48 -0
- package/src/lib/server/runtime/daemon-state.ts +3 -1331
- package/src/lib/server/runtime/estop-repository.ts +4 -0
- package/src/lib/server/runtime/estop.ts +3 -1
- package/src/lib/server/runtime/heartbeat-service.test.ts +2 -2
- package/src/lib/server/runtime/heartbeat-service.ts +78 -34
- package/src/lib/server/runtime/heartbeat-wake.ts +6 -4
- package/src/lib/server/runtime/idle-window.ts +6 -3
- package/src/lib/server/runtime/network.ts +11 -0
- package/src/lib/server/runtime/orchestrator-events.ts +2 -2
- package/src/lib/server/runtime/perf.ts +4 -1
- package/src/lib/server/runtime/process-manager.ts +7 -4
- package/src/lib/server/runtime/queue/claims.ts +4 -0
- package/src/lib/server/runtime/queue/core.ts +2079 -0
- package/src/lib/server/runtime/queue/execution.ts +7 -0
- package/src/lib/server/runtime/queue/followups.ts +4 -0
- package/src/lib/server/runtime/queue/queries.ts +12 -0
- package/src/lib/server/runtime/queue/recovery.ts +7 -0
- package/src/lib/server/runtime/queue-recovery.test.ts +48 -13
- package/src/lib/server/runtime/queue-repository.ts +17 -0
- package/src/lib/server/runtime/queue.ts +5 -2058
- package/src/lib/server/runtime/run-ledger.ts +6 -5
- package/src/lib/server/runtime/run-repository.ts +73 -0
- package/src/lib/server/runtime/runtime-lock-repository.ts +8 -0
- package/src/lib/server/runtime/runtime-settings.ts +1 -1
- package/src/lib/server/runtime/runtime-state.ts +99 -0
- package/src/lib/server/runtime/scheduler.ts +13 -8
- package/src/lib/server/runtime/session-run-manager/cancellation.ts +157 -0
- package/src/lib/server/runtime/session-run-manager/drain.ts +246 -0
- package/src/lib/server/runtime/session-run-manager/enqueue.ts +287 -0
- package/src/lib/server/runtime/session-run-manager/queries.ts +117 -0
- package/src/lib/server/runtime/session-run-manager/recovery.ts +238 -0
- package/src/lib/server/runtime/session-run-manager/state.ts +441 -0
- package/src/lib/server/runtime/session-run-manager/types.ts +74 -0
- package/src/lib/server/runtime/session-run-manager.ts +72 -1374
- package/src/lib/server/runtime/watch-job-repository.ts +35 -0
- package/src/lib/server/runtime/watch-jobs.ts +3 -1
- package/src/lib/server/sandbox/bridge-auth-registry.ts +6 -0
- package/src/lib/server/sandbox/novnc-auth.ts +10 -0
- package/src/lib/server/schedules/schedule-repository.ts +42 -0
- package/src/lib/server/session-tools/context.ts +14 -0
- package/src/lib/server/session-tools/discovery.ts +9 -6
- package/src/lib/server/session-tools/index.ts +3 -1
- package/src/lib/server/session-tools/platform.ts +1 -1
- package/src/lib/server/session-tools/subagent.ts +23 -2
- package/src/lib/server/session-tools/wallet.ts +4 -1
- package/src/lib/server/sessions/session-repository.ts +85 -0
- package/src/lib/server/settings/settings-repository.ts +25 -0
- package/src/lib/server/skills/clawhub-client.ts +4 -1
- package/src/lib/server/skills/runtime-skill-resolver.ts +8 -2
- package/src/lib/server/skills/skill-discovery.test.ts +2 -2
- package/src/lib/server/skills/skill-discovery.ts +2 -2
- package/src/lib/server/skills/skill-eligibility.ts +6 -0
- package/src/lib/server/skills/skill-repository.ts +14 -0
- package/src/lib/server/solana.ts +6 -0
- package/src/lib/server/storage-auth.ts +5 -5
- package/src/lib/server/storage-normalization.ts +4 -0
- package/src/lib/server/storage.ts +32 -32
- package/src/lib/server/tasks/task-followups.ts +4 -1
- package/src/lib/server/tasks/task-repository.ts +54 -0
- package/src/lib/server/tool-loop-detection.ts +8 -3
- package/src/lib/server/tool-planning.ts +226 -0
- package/src/lib/server/tool-retry.ts +4 -3
- package/src/lib/server/usage/usage-repository.ts +30 -0
- package/src/lib/server/wallet/wallet-portfolio.ts +29 -0
- package/src/lib/server/webhooks/webhook-repository.ts +10 -0
- package/src/lib/server/ws-hub.ts +5 -2
- package/src/lib/strip-internal-metadata.test.ts +78 -37
- package/src/lib/strip-internal-metadata.ts +20 -6
- package/src/stores/use-approval-store.ts +7 -1
- package/src/stores/use-chat-store.test.ts +54 -0
- package/src/stores/use-chat-store.ts +26 -6
- package/src/types/index.ts +6 -0
- /package/{bundled-skills → skills}/google-workspace/SKILL.md +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use client'
|
|
2
2
|
|
|
3
|
-
import { memo,
|
|
3
|
+
import { memo, useEffect, useState } from 'react'
|
|
4
4
|
import { AgentAvatar } from '@/components/agents/agent-avatar'
|
|
5
5
|
import { useAppStore } from '@/stores/use-app-store'
|
|
6
6
|
|
|
@@ -115,7 +115,7 @@ const SwarmMemberCard = memo(function SwarmMemberCard({
|
|
|
115
115
|
agents,
|
|
116
116
|
}: {
|
|
117
117
|
member: SwarmMemberData
|
|
118
|
-
agents: Record<string,
|
|
118
|
+
agents: Record<string, { avatarSeed?: string; avatarUrl?: string | null; name?: string }>
|
|
119
119
|
}) {
|
|
120
120
|
const [expanded, setExpanded] = useState(false)
|
|
121
121
|
const cfg = MEMBER_STATUS_CONFIG[member.status]
|
|
@@ -206,6 +206,13 @@ const SwarmMemberCard = memo(function SwarmMemberCard({
|
|
|
206
206
|
function SwarmSummaryBar({ data }: { data: SwarmStatusData }) {
|
|
207
207
|
const cfg = SWARM_STATUS_CONFIG[data.status]
|
|
208
208
|
const isTerminal = data.status === 'completed' || data.status === 'partial' || data.status === 'failed'
|
|
209
|
+
const [now, setNow] = useState(data.completedAt ?? data.createdAt)
|
|
210
|
+
|
|
211
|
+
useEffect(() => {
|
|
212
|
+
if (isTerminal) return
|
|
213
|
+
const id = setInterval(() => setNow(Date.now()), 1000)
|
|
214
|
+
return () => clearInterval(id)
|
|
215
|
+
}, [isTerminal])
|
|
209
216
|
|
|
210
217
|
const formatDuration = (ms: number) => {
|
|
211
218
|
if (ms < 1000) return `${ms}ms`
|
|
@@ -215,7 +222,7 @@ function SwarmSummaryBar({ data }: { data: SwarmStatusData }) {
|
|
|
215
222
|
|
|
216
223
|
const durationMs = data.completedAt
|
|
217
224
|
? data.completedAt - data.createdAt
|
|
218
|
-
:
|
|
225
|
+
: now - data.createdAt
|
|
219
226
|
|
|
220
227
|
return (
|
|
221
228
|
<div
|
|
@@ -50,6 +50,10 @@ export function ChatInput({ streaming, busy, onSend, onStop, extensionChatAction
|
|
|
50
50
|
const streamPhase = useChatStore((s) => s.streamPhase)
|
|
51
51
|
const streamToolName = useChatStore((s) => s.streamToolName)
|
|
52
52
|
const visibleQueuedMessages = listQueuedMessagesForSession(queuedMessages, sessionId)
|
|
53
|
+
const sendingQueuedMessages = visibleQueuedMessages.filter((item) => item.sending)
|
|
54
|
+
const pendingQueuedMessages = visibleQueuedMessages.filter((item) => !item.sending)
|
|
55
|
+
const displayedQueuedMessages = [...sendingQueuedMessages, ...pendingQueuedMessages]
|
|
56
|
+
const nextPendingRunId = pendingQueuedMessages[0]?.runId ?? null
|
|
53
57
|
const shouldQueue = !!sessionId && (busy || visibleQueuedMessages.length > 0)
|
|
54
58
|
|
|
55
59
|
useEffect(() => {
|
|
@@ -178,6 +182,8 @@ export function ChatInput({ streaming, busy, onSend, onStop, extensionChatAction
|
|
|
178
182
|
const hasContent = value.trim().length > 0 || pendingFiles.length > 0
|
|
179
183
|
const queueStatusLabel = !busy
|
|
180
184
|
? 'Queue ready'
|
|
185
|
+
: sendingQueuedMessages.length > 0 && pendingQueuedMessages.length === 0
|
|
186
|
+
? 'Sending now'
|
|
181
187
|
: streamPhase === 'queued'
|
|
182
188
|
? 'Queued'
|
|
183
189
|
: streamPhase === 'tool' && streamToolName
|
|
@@ -191,6 +197,8 @@ export function ChatInput({ streaming, busy, onSend, onStop, extensionChatAction
|
|
|
191
197
|
: 'Working'
|
|
192
198
|
const queueStatusDetail = !busy
|
|
193
199
|
? 'Queued messages are ready and will dispatch automatically.'
|
|
200
|
+
: sendingQueuedMessages.length > 0 && pendingQueuedMessages.length === 0
|
|
201
|
+
? 'The queued message has been accepted and should appear in the transcript shortly.'
|
|
194
202
|
: 'Queued messages will send automatically when the current turn finishes.'
|
|
195
203
|
|
|
196
204
|
return (
|
|
@@ -229,9 +237,16 @@ export function ChatInput({ streaming, busy, onSend, onStop, extensionChatAction
|
|
|
229
237
|
<span className={`relative inline-flex h-2.5 w-2.5 rounded-full ${busy ? 'bg-amber-300' : 'bg-white/[0.45]'}`} />
|
|
230
238
|
</span>
|
|
231
239
|
<span className="label-mono text-amber-300/80">Message queue</span>
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
240
|
+
{pendingQueuedMessages.length > 0 && (
|
|
241
|
+
<span className="rounded-pill border border-amber-400/15 bg-amber-400/10 px-2 py-0.5 text-[10px] font-600 text-amber-200">
|
|
242
|
+
{pendingQueuedMessages.length}
|
|
243
|
+
</span>
|
|
244
|
+
)}
|
|
245
|
+
{sendingQueuedMessages.length > 0 && (
|
|
246
|
+
<span className="rounded-pill border border-sky-300/15 bg-sky-300/10 px-2 py-0.5 text-[10px] font-600 text-sky-200">
|
|
247
|
+
{sendingQueuedMessages.length} sending
|
|
248
|
+
</span>
|
|
249
|
+
)}
|
|
235
250
|
<span className={`rounded-pill border px-2 py-0.5 text-[10px] font-700 uppercase tracking-[0.12em] ${
|
|
236
251
|
busy
|
|
237
252
|
? 'border-amber-300/20 bg-amber-300/10 text-amber-100'
|
|
@@ -256,7 +271,7 @@ export function ChatInput({ streaming, busy, onSend, onStop, extensionChatAction
|
|
|
256
271
|
Stop
|
|
257
272
|
</button>
|
|
258
273
|
)}
|
|
259
|
-
{sessionId &&
|
|
274
|
+
{sessionId && pendingQueuedMessages.length > 0 && (
|
|
260
275
|
<button
|
|
261
276
|
type="button"
|
|
262
277
|
onClick={() => { void clearQueuedMessagesForSession(sessionId) }}
|
|
@@ -268,21 +283,26 @@ export function ChatInput({ streaming, busy, onSend, onStop, extensionChatAction
|
|
|
268
283
|
</div>
|
|
269
284
|
</div>
|
|
270
285
|
<div className="max-h-[184px] space-y-1.5 overflow-y-auto px-2.5 py-2.5">
|
|
271
|
-
{
|
|
286
|
+
{displayedQueuedMessages.map((item, index) => (
|
|
272
287
|
<div
|
|
273
288
|
key={item.runId}
|
|
274
289
|
className={`group flex items-start gap-3 rounded-[12px] border px-3 py-2.5 transition-all ${
|
|
275
|
-
|
|
276
|
-
? 'border-
|
|
277
|
-
:
|
|
290
|
+
item.sending
|
|
291
|
+
? 'border-sky-300/15 bg-sky-300/[0.06]'
|
|
292
|
+
: item.runId === nextPendingRunId
|
|
293
|
+
? 'border-amber-300/20 bg-amber-300/[0.07]'
|
|
294
|
+
: 'border-white/[0.05] bg-white/[0.02]'
|
|
278
295
|
}`}
|
|
279
296
|
>
|
|
280
297
|
<div className={`mt-0.5 flex h-6 min-w-6 items-center justify-center rounded-[8px] px-2 text-[10px] font-700 ${
|
|
281
|
-
|
|
282
|
-
? 'bg-
|
|
283
|
-
:
|
|
284
|
-
|
|
285
|
-
|
|
298
|
+
item.sending
|
|
299
|
+
? 'bg-sky-300/15 text-sky-100'
|
|
300
|
+
: item.runId === nextPendingRunId
|
|
301
|
+
? 'bg-amber-300/15 text-amber-100'
|
|
302
|
+
: 'border-white/[0.05] bg-white/[0.02]'
|
|
303
|
+
}`}
|
|
304
|
+
>
|
|
305
|
+
{item.sending ? '>' : pendingQueuedMessages.findIndex((candidate) => candidate.runId === item.runId) + 1}
|
|
286
306
|
</div>
|
|
287
307
|
<div className="min-w-0 flex-1">
|
|
288
308
|
<div className="flex flex-wrap items-center gap-2">
|
|
@@ -290,7 +310,7 @@ export function ChatInput({ streaming, busy, onSend, onStop, extensionChatAction
|
|
|
290
310
|
<span className="rounded-pill border border-sky-300/15 bg-sky-300/10 px-2 py-0.5 text-[10px] font-700 uppercase tracking-[0.12em] text-sky-200 animate-pulse">
|
|
291
311
|
Sending
|
|
292
312
|
</span>
|
|
293
|
-
) :
|
|
313
|
+
) : item.runId === nextPendingRunId && (
|
|
294
314
|
<span className="rounded-pill border border-amber-300/15 bg-amber-300/10 px-2 py-0.5 text-[10px] font-700 uppercase tracking-[0.12em] text-amber-100">
|
|
295
315
|
Next
|
|
296
316
|
</span>
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use client'
|
|
2
2
|
|
|
3
|
-
import { useEffect, useState } from 'react'
|
|
3
|
+
import { useCallback, useEffect, useState } from 'react'
|
|
4
4
|
import { api } from '@/lib/app/api-client'
|
|
5
5
|
import { useWs } from '@/hooks/use-ws'
|
|
6
6
|
import { StatusDot } from '@/components/ui/status-dot'
|
|
@@ -16,14 +16,13 @@ interface DaemonStatus {
|
|
|
16
16
|
export function DaemonIndicator() {
|
|
17
17
|
const [status, setStatus] = useState<DaemonStatus | null>(null)
|
|
18
18
|
|
|
19
|
-
const fetchStatus =
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
}
|
|
19
|
+
const fetchStatus = useCallback(() => {
|
|
20
|
+
api<DaemonStatus>('GET', '/daemon')
|
|
21
|
+
.then(setStatus)
|
|
22
|
+
.catch(() => {})
|
|
23
|
+
}, [])
|
|
25
24
|
|
|
26
|
-
useEffect(() => { fetchStatus() }, [])
|
|
25
|
+
useEffect(() => { fetchStatus() }, [fetchStatus])
|
|
27
26
|
useWs('daemon', fetchStatus, 60_000)
|
|
28
27
|
|
|
29
28
|
const toggle = async () => {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use client'
|
|
2
2
|
|
|
3
|
-
import { useEffect, useState
|
|
3
|
+
import { useEffect, useState } from 'react'
|
|
4
4
|
|
|
5
5
|
const CHECK_INTERVAL = 5 * 60_000 // 5 minutes
|
|
6
6
|
|
|
@@ -19,22 +19,17 @@ export function UpdateBanner() {
|
|
|
19
19
|
const [dismissed, setDismissed] = useState<string | null>(null)
|
|
20
20
|
const [errorMsg, setErrorMsg] = useState('')
|
|
21
21
|
|
|
22
|
-
const checkVersion = useCallback(async () => {
|
|
23
|
-
try {
|
|
24
|
-
const res = await fetch('/api/version')
|
|
25
|
-
if (!res.ok) return
|
|
26
|
-
const data: VersionInfo = await res.json()
|
|
27
|
-
setVersion(data)
|
|
28
|
-
} catch {
|
|
29
|
-
// silently fail — no network or server issue
|
|
30
|
-
}
|
|
31
|
-
}, [])
|
|
32
|
-
|
|
33
22
|
useEffect(() => {
|
|
23
|
+
const checkVersion = () => {
|
|
24
|
+
fetch('/api/version')
|
|
25
|
+
.then((res) => res.ok ? res.json() as Promise<VersionInfo> : null)
|
|
26
|
+
.then((data) => { if (data) setVersion(data) })
|
|
27
|
+
.catch(() => {})
|
|
28
|
+
}
|
|
34
29
|
checkVersion()
|
|
35
30
|
const id = setInterval(checkVersion, CHECK_INTERVAL)
|
|
36
31
|
return () => clearInterval(id)
|
|
37
|
-
}, [
|
|
32
|
+
}, [])
|
|
38
33
|
|
|
39
34
|
const handleUpdate = async () => {
|
|
40
35
|
setUpdateState('updating')
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
'use client'
|
|
2
2
|
|
|
3
|
+
import { useState } from 'react'
|
|
3
4
|
import type { MemoryEntry } from '@/types'
|
|
4
5
|
import { AgentAvatar } from '@/components/agents/agent-avatar'
|
|
5
6
|
import { deriveMemoryScope, getMemoryScopeLabel, getMemoryTier } from '@/lib/memory-presentation'
|
|
@@ -15,6 +16,7 @@ interface Props {
|
|
|
15
16
|
}
|
|
16
17
|
|
|
17
18
|
export function MemoryCard({ entry, active, agentName, agentAvatarSeed, agentAvatarUrl, onClick }: Props) {
|
|
19
|
+
const [now] = useState(() => Date.now())
|
|
18
20
|
const scope = deriveMemoryScope(entry)
|
|
19
21
|
const tier = getMemoryTier(entry)
|
|
20
22
|
|
|
@@ -41,7 +43,7 @@ export function MemoryCard({ entry, active, agentName, agentAvatarSeed, agentAva
|
|
|
41
43
|
)}
|
|
42
44
|
<span className="font-display text-[13px] font-600 truncate flex-1 tracking-[-0.01em]">{entry.title}</span>
|
|
43
45
|
<span className="text-[10px] text-text-3/60 shrink-0 tabular-nums font-mono">
|
|
44
|
-
{timeAgoShort(entry.updatedAt || entry.createdAt,
|
|
46
|
+
{timeAgoShort(entry.updatedAt || entry.createdAt, now)}
|
|
45
47
|
</span>
|
|
46
48
|
</div>
|
|
47
49
|
<div className="text-[12px] text-text-2/40 mt-1 line-clamp-3 leading-relaxed">
|
|
@@ -21,6 +21,7 @@ import type { ContextAction } from './org-chart-context-menu'
|
|
|
21
21
|
import { useOrgChartPanZoom } from './use-org-chart-pan-zoom'
|
|
22
22
|
import { useOrgChartDrag } from './use-org-chart-drag'
|
|
23
23
|
import { useNavigate } from '@/lib/app/navigation'
|
|
24
|
+
import { useWs } from '@/hooks/use-ws'
|
|
24
25
|
import { ConfirmDialog } from '@/components/shared/confirm-dialog'
|
|
25
26
|
|
|
26
27
|
const NODE_W = 200
|
|
@@ -57,6 +58,9 @@ export function OrgChartView() {
|
|
|
57
58
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
58
59
|
useEffect(() => { loadAgents(); loadSessions() }, [])
|
|
59
60
|
|
|
61
|
+
useWs('agents', loadAgents, 60_000)
|
|
62
|
+
useWs('sessions', loadSessions, 30_000)
|
|
63
|
+
|
|
60
64
|
// Running agents — derived from sessions
|
|
61
65
|
const runningAgentIds = useMemo(() => {
|
|
62
66
|
const ids = new Set<string>()
|
|
@@ -58,8 +58,10 @@ export function ProjectList() {
|
|
|
58
58
|
}
|
|
59
59
|
for (const t of Object.values(tasks)) {
|
|
60
60
|
if (t.projectId && map[t.projectId]) {
|
|
61
|
-
|
|
62
|
-
|
|
61
|
+
if (t.status !== 'cancelled' && t.status !== 'archived') {
|
|
62
|
+
map[t.projectId].tasks++
|
|
63
|
+
if (t.status === 'completed') map[t.projectId].completedTasks++
|
|
64
|
+
}
|
|
63
65
|
if (t.updatedAt && t.updatedAt > map[t.projectId].lastActivity) {
|
|
64
66
|
map[t.projectId].lastActivity = t.updatedAt
|
|
65
67
|
}
|
|
@@ -46,8 +46,9 @@ export function OverviewTab({ project, missions }: OverviewTabProps) {
|
|
|
46
46
|
[now, projectTasks],
|
|
47
47
|
)
|
|
48
48
|
|
|
49
|
-
const
|
|
50
|
-
const
|
|
49
|
+
const actionableTasks = projectTasks.filter((t) => t.status !== 'cancelled' && t.status !== 'archived')
|
|
50
|
+
const completedTasks = actionableTasks.filter((t) => t.status === 'completed').length
|
|
51
|
+
const totalTasks = actionableTasks.length
|
|
51
52
|
const progressPct = totalTasks > 0 ? Math.round((completedTasks / totalTasks) * 100) : 0
|
|
52
53
|
|
|
53
54
|
const tasksByStatus = useMemo(() => {
|
|
@@ -31,13 +31,19 @@ export function AgentSwitchDialog() {
|
|
|
31
31
|
return () => window.removeEventListener('keydown', handler)
|
|
32
32
|
}, [])
|
|
33
33
|
|
|
34
|
-
// Reset on open
|
|
34
|
+
// Reset on open (render-time state adjustment)
|
|
35
|
+
const [prevOpen, setPrevOpen] = useState(false)
|
|
36
|
+
if (open && !prevOpen) {
|
|
37
|
+
setQuery('')
|
|
38
|
+
setSelectedIdx(0)
|
|
39
|
+
}
|
|
40
|
+
if (open !== prevOpen) {
|
|
41
|
+
setPrevOpen(open)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Focus input after opening
|
|
35
45
|
useEffect(() => {
|
|
36
|
-
if (open)
|
|
37
|
-
setQuery('')
|
|
38
|
-
setSelectedIdx(0)
|
|
39
|
-
setTimeout(() => inputRef.current?.focus(), 50)
|
|
40
|
-
}
|
|
46
|
+
if (open) setTimeout(() => inputRef.current?.focus(), 50)
|
|
41
47
|
}, [open])
|
|
42
48
|
|
|
43
49
|
const filtered = useMemo(() => {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use client'
|
|
2
2
|
|
|
3
|
-
import { useEffect, useState,
|
|
3
|
+
import { useEffect, useState, useMemo } from 'react'
|
|
4
4
|
import { api } from '@/lib/app/api-client'
|
|
5
5
|
import { SearchInput } from '@/components/ui/search-input'
|
|
6
6
|
|
|
@@ -37,26 +37,30 @@ export function DirBrowser({ value, file, onChange, onClear }: DirBrowserProps)
|
|
|
37
37
|
const [pathInput, setPathInput] = useState('')
|
|
38
38
|
const [search, setSearch] = useState('')
|
|
39
39
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
setParentPath(data.parentPath || null)
|
|
47
|
-
setPathInput(data.currentPath || dirPath)
|
|
48
|
-
} catch {
|
|
49
|
-
setDirs([])
|
|
50
|
-
}
|
|
51
|
-
setLoading(false)
|
|
52
|
-
}, [])
|
|
53
|
-
|
|
54
|
-
useEffect(() => {
|
|
40
|
+
// Reset search and mark loading when navigating in browse mode
|
|
41
|
+
const [prevBrowsePath, setPrevBrowsePath] = useState(browsePath)
|
|
42
|
+
const [prevMode, setPrevMode] = useState(mode)
|
|
43
|
+
if (browsePath !== prevBrowsePath || mode !== prevMode) {
|
|
44
|
+
setPrevBrowsePath(browsePath)
|
|
45
|
+
setPrevMode(mode)
|
|
55
46
|
if (mode === 'browse') {
|
|
56
|
-
fetchDirs(browsePath)
|
|
57
47
|
setSearch('')
|
|
48
|
+
setLoading(true)
|
|
58
49
|
}
|
|
59
|
-
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
useEffect(() => {
|
|
53
|
+
if (mode !== 'browse') return
|
|
54
|
+
api<DirApiResponse>('GET', `/dirs?path=${encodeURIComponent(browsePath)}`)
|
|
55
|
+
.then((data) => {
|
|
56
|
+
setDirs(data.dirs || [])
|
|
57
|
+
setCurrentPath(data.currentPath || browsePath)
|
|
58
|
+
setParentPath(data.parentPath || null)
|
|
59
|
+
setPathInput(data.currentPath || browsePath)
|
|
60
|
+
})
|
|
61
|
+
.catch(() => { setDirs([]) })
|
|
62
|
+
.finally(() => setLoading(false))
|
|
63
|
+
}, [browsePath, mode])
|
|
60
64
|
|
|
61
65
|
const filteredDirs = useMemo(() => {
|
|
62
66
|
if (!search) return dirs
|
|
@@ -64,8 +64,7 @@ export function SkillSheet() {
|
|
|
64
64
|
|
|
65
65
|
useEffect(() => {
|
|
66
66
|
if (open) loadAgents()
|
|
67
|
-
|
|
68
|
-
}, [open])
|
|
67
|
+
}, [open, loadAgents])
|
|
69
68
|
|
|
70
69
|
useEffect(() => {
|
|
71
70
|
if (open) {
|
|
@@ -91,7 +90,7 @@ export function SkillSheet() {
|
|
|
91
90
|
setMetadataPreview(null)
|
|
92
91
|
}
|
|
93
92
|
}
|
|
94
|
-
}, [open, editingId])
|
|
93
|
+
}, [open, editingId, editing])
|
|
95
94
|
|
|
96
95
|
const onClose = () => {
|
|
97
96
|
setConfirmDelete(false)
|
|
@@ -29,7 +29,7 @@ export function TaskList({ inSidebar }: { inSidebar?: boolean }) {
|
|
|
29
29
|
const [search, setSearch] = useState('')
|
|
30
30
|
const [clearing, setClearing] = useState(false)
|
|
31
31
|
|
|
32
|
-
useEffect(() => { loadTasks() }, [])
|
|
32
|
+
useEffect(() => { loadTasks() }, [loadTasks])
|
|
33
33
|
useWs('tasks', loadTasks, 5000)
|
|
34
34
|
|
|
35
35
|
const sorted = useMemo(() =>
|
|
@@ -151,7 +151,7 @@ export function TaskSheet() {
|
|
|
151
151
|
if (open && !editing && !agentId && agentList.length) {
|
|
152
152
|
setAgentId(agentList[0].id)
|
|
153
153
|
}
|
|
154
|
-
}, [open, editing, agentId, agentList
|
|
154
|
+
}, [open, editing, agentId, agentList])
|
|
155
155
|
|
|
156
156
|
useEffect(() => {
|
|
157
157
|
if (!editing?.id || !open) {
|
|
@@ -8,35 +8,57 @@ import { useWs } from './use-ws'
|
|
|
8
8
|
/** Call an OpenClaw gateway RPC method via the proxy route. */
|
|
9
9
|
export function useOpenClawRpc<T = unknown>(method: string | null, params?: unknown) {
|
|
10
10
|
const [data, setData] = useState<T | null>(null)
|
|
11
|
-
const [loading, setLoading] = useState(
|
|
11
|
+
const [loading, setLoading] = useState(!!method)
|
|
12
12
|
const [error, setError] = useState<string | null>(null)
|
|
13
13
|
const paramsRef = useRef(params)
|
|
14
|
-
paramsRef.current = params
|
|
14
|
+
useEffect(() => { paramsRef.current = params })
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
// doFetch only uses async callbacks for setState (no synchronous setState)
|
|
17
|
+
const doFetch = useCallback(() => {
|
|
17
18
|
if (!method) return
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
19
|
+
api<{ ok: boolean; result: T; error?: string }>('POST', '/openclaw/gateway', {
|
|
20
|
+
method,
|
|
21
|
+
params: paramsRef.current,
|
|
22
|
+
})
|
|
23
|
+
.then((res) => {
|
|
24
|
+
if (res.error) {
|
|
25
|
+
setError(res.error)
|
|
26
|
+
} else {
|
|
27
|
+
setData(res.result)
|
|
28
|
+
}
|
|
29
|
+
})
|
|
30
|
+
.catch((err: unknown) => {
|
|
31
|
+
setError(errorMessage(err))
|
|
24
32
|
})
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
+
.finally(() => {
|
|
34
|
+
setLoading(false)
|
|
35
|
+
})
|
|
36
|
+
}, [method])
|
|
37
|
+
|
|
38
|
+
// Reset loading/error when method changes (render-time state adjustment)
|
|
39
|
+
const [prevMethod, setPrevMethod] = useState(method)
|
|
40
|
+
if (method !== prevMethod) {
|
|
41
|
+
setPrevMethod(method)
|
|
42
|
+
if (method) {
|
|
43
|
+
setLoading(true)
|
|
44
|
+
setError(null)
|
|
45
|
+
} else {
|
|
33
46
|
setLoading(false)
|
|
47
|
+
setError(null)
|
|
48
|
+
setData(null)
|
|
34
49
|
}
|
|
35
|
-
}
|
|
50
|
+
}
|
|
36
51
|
|
|
37
|
-
useEffect(() => {
|
|
52
|
+
useEffect(() => { doFetch() }, [doFetch])
|
|
38
53
|
|
|
39
|
-
|
|
54
|
+
// refetch wraps doFetch with loading/error reset (called from event handlers)
|
|
55
|
+
const refetch = useCallback(() => {
|
|
56
|
+
setLoading(true)
|
|
57
|
+
setError(null)
|
|
58
|
+
doFetch()
|
|
59
|
+
}, [doFetch])
|
|
60
|
+
|
|
61
|
+
return { data, loading, error, refetch }
|
|
40
62
|
}
|
|
41
63
|
|
|
42
64
|
/** Subscribe to an OpenClaw event topic via the WS hub. */
|
|
@@ -48,13 +70,10 @@ export function useOpenClawEvent(topic: string, handler: () => void) {
|
|
|
48
70
|
export function useOpenClawConnected() {
|
|
49
71
|
const [connected, setConnected] = useState(false)
|
|
50
72
|
|
|
51
|
-
const check = useCallback(
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
setConnected(
|
|
55
|
-
} catch {
|
|
56
|
-
setConnected(false)
|
|
57
|
-
}
|
|
73
|
+
const check = useCallback(() => {
|
|
74
|
+
api<{ connected: boolean }>('GET', '/openclaw/gateway')
|
|
75
|
+
.then((res) => setConnected(res.connected))
|
|
76
|
+
.catch(() => setConnected(false))
|
|
58
77
|
}, [])
|
|
59
78
|
|
|
60
79
|
useEffect(() => { check() }, [check])
|
package/src/instrumentation.ts
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import { hmrSingleton } from '@/lib/shared-utils'
|
|
2
2
|
|
|
3
|
+
const TAG = 'instrumentation'
|
|
4
|
+
|
|
3
5
|
export async function register() {
|
|
4
6
|
if (process.env.NEXT_RUNTIME === 'nodejs') {
|
|
7
|
+
const { log } = await import('@/lib/server/logger')
|
|
5
8
|
const isWorkerOnly = process.env.SWARMCLAW_WORKER_ONLY === '1'
|
|
6
9
|
const { initWsServer, closeWsServer } = await import('./lib/server/ws-hub')
|
|
7
10
|
const { ensureDaemonStarted } = await import('@/lib/server/runtime/daemon-state')
|
|
@@ -12,12 +15,12 @@ export async function register() {
|
|
|
12
15
|
backfillAllKnownPeerIds()
|
|
13
16
|
pruneThreadConnectorMirrors()
|
|
14
17
|
} catch (err) {
|
|
15
|
-
|
|
18
|
+
log.error(TAG, 'connector session consolidation failed:', err)
|
|
16
19
|
}
|
|
17
20
|
|
|
18
21
|
// In worker-only mode, we FORCE the daemon to start, but skip the WebSocket listener
|
|
19
22
|
if (isWorkerOnly) {
|
|
20
|
-
|
|
23
|
+
log.info(TAG, 'Booting in WORKER ONLY mode')
|
|
21
24
|
ensureDaemonStarted('worker-boot')
|
|
22
25
|
} else {
|
|
23
26
|
// In normal mode, we start the WS server, and conditionally start the daemon if autostart allows
|
|
@@ -34,12 +37,12 @@ export async function register() {
|
|
|
34
37
|
const shutdown = async (signal: string) => {
|
|
35
38
|
if (shutdownState.shuttingDown) return
|
|
36
39
|
shutdownState.shuttingDown = true
|
|
37
|
-
|
|
40
|
+
log.info(TAG, `${signal} received, shutting down gracefully...`)
|
|
38
41
|
try {
|
|
39
42
|
const { stopDaemon } = await import('@/lib/server/runtime/daemon-state')
|
|
40
43
|
await stopDaemon({ source: signal })
|
|
41
44
|
} catch (err) {
|
|
42
|
-
|
|
45
|
+
log.error(TAG, 'Failed to stop daemon during shutdown:', err)
|
|
43
46
|
}
|
|
44
47
|
if (!isWorkerOnly) {
|
|
45
48
|
await closeWsServer()
|
|
@@ -54,10 +57,10 @@ export async function register() {
|
|
|
54
57
|
// that occur during dev server restarts when stdio pipes break
|
|
55
58
|
process.on('uncaughtException', (err: NodeJS.ErrnoException) => {
|
|
56
59
|
if (err.code === 'EPIPE') {
|
|
57
|
-
|
|
60
|
+
log.warn(TAG, 'Ignoring EPIPE (expected during dev server restart)')
|
|
58
61
|
return
|
|
59
62
|
}
|
|
60
|
-
|
|
63
|
+
log.error(TAG, 'Uncaught exception:', err)
|
|
61
64
|
process.exit(1)
|
|
62
65
|
})
|
|
63
66
|
|
|
@@ -73,7 +76,7 @@ export async function register() {
|
|
|
73
76
|
) {
|
|
74
77
|
return
|
|
75
78
|
}
|
|
76
|
-
|
|
79
|
+
log.error(TAG, 'Unhandled rejection:', err)
|
|
77
80
|
})
|
|
78
81
|
|
|
79
82
|
shutdownState.registered = true
|